]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/ruleutils.c
Support SQL-compliant triggers on columns, ie fire only if certain columns
[postgresql] / src / backend / utils / adt / ruleutils.c
1 /*-------------------------------------------------------------------------
2  *
3  * ruleutils.c
4  *        Functions to convert stored expressions/querytrees back to
5  *        source text
6  *
7  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.310 2009/10/14 22:14:23 tgl Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
18 #include <unistd.h>
19 #include <fcntl.h>
20
21 #include "access/genam.h"
22 #include "access/sysattr.h"
23 #include "catalog/dependency.h"
24 #include "catalog/indexing.h"
25 #include "catalog/pg_authid.h"
26 #include "catalog/pg_constraint.h"
27 #include "catalog/pg_depend.h"
28 #include "catalog/pg_language.h"
29 #include "catalog/pg_opclass.h"
30 #include "catalog/pg_operator.h"
31 #include "catalog/pg_proc.h"
32 #include "catalog/pg_trigger.h"
33 #include "catalog/pg_type.h"
34 #include "commands/defrem.h"
35 #include "commands/tablespace.h"
36 #include "executor/spi.h"
37 #include "funcapi.h"
38 #include "nodes/makefuncs.h"
39 #include "nodes/nodeFuncs.h"
40 #include "optimizer/clauses.h"
41 #include "optimizer/tlist.h"
42 #include "parser/keywords.h"
43 #include "parser/parse_func.h"
44 #include "parser/parse_oper.h"
45 #include "parser/parser.h"
46 #include "parser/parsetree.h"
47 #include "rewrite/rewriteHandler.h"
48 #include "rewrite/rewriteManip.h"
49 #include "rewrite/rewriteSupport.h"
50 #include "utils/array.h"
51 #include "utils/builtins.h"
52 #include "utils/fmgroids.h"
53 #include "utils/lsyscache.h"
54 #include "utils/tqual.h"
55 #include "utils/syscache.h"
56 #include "utils/typcache.h"
57 #include "utils/xml.h"
58
59
60 /* ----------
61  * Pretty formatting constants
62  * ----------
63  */
64
65 /* Indent counts */
66 #define PRETTYINDENT_STD                8
67 #define PRETTYINDENT_JOIN          13
68 #define PRETTYINDENT_JOIN_ON    (PRETTYINDENT_JOIN-PRETTYINDENT_STD)
69 #define PRETTYINDENT_VAR                4
70
71 /* Pretty flags */
72 #define PRETTYFLAG_PAREN                1
73 #define PRETTYFLAG_INDENT               2
74
75 /* macro to test if pretty action needed */
76 #define PRETTY_PAREN(context)   ((context)->prettyFlags & PRETTYFLAG_PAREN)
77 #define PRETTY_INDENT(context)  ((context)->prettyFlags & PRETTYFLAG_INDENT)
78
79
80 /* ----------
81  * Local data types
82  * ----------
83  */
84
85 /* Context info needed for invoking a recursive querytree display routine */
86 typedef struct
87 {
88         StringInfo      buf;                    /* output buffer to append to */
89         List       *namespaces;         /* List of deparse_namespace nodes */
90         List       *windowClause;       /* Current query level's WINDOW clause */
91         List       *windowTList;        /* targetlist for resolving WINDOW clause */
92         int                     prettyFlags;    /* enabling of pretty-print functions */
93         int                     indentLevel;    /* current indent level for prettyprint */
94         bool            varprefix;              /* TRUE to print prefixes on Vars */
95 } deparse_context;
96
97 /*
98  * Each level of query context around a subtree needs a level of Var namespace.
99  * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
100  * the current context's namespaces list.
101  *
102  * The rangetable is the list of actual RTEs from the query tree, and the
103  * cte list is the list of actual CTEs.
104  *
105  * For deparsing plan trees, we provide for outer and inner subplan nodes.
106  * The tlists of these nodes are used to resolve OUTER and INNER varnos.
107  * Also, in the plan-tree case we don't have access to the parse-time CTE
108  * list, so we need a list of subplans instead.
109  */
110 typedef struct
111 {
112         List       *rtable;                     /* List of RangeTblEntry nodes */
113         List       *ctes;                       /* List of CommonTableExpr nodes */
114         List       *subplans;           /* List of subplans, in plan-tree case */
115         Plan       *outer_plan;         /* OUTER subplan, or NULL if none */
116         Plan       *inner_plan;         /* INNER subplan, or NULL if none */
117 } deparse_namespace;
118
119
120 /* ----------
121  * Global data
122  * ----------
123  */
124 static SPIPlanPtr plan_getrulebyoid = NULL;
125 static const char *query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
126 static SPIPlanPtr plan_getviewrule = NULL;
127 static const char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
128
129
130 /* ----------
131  * Local functions
132  *
133  * Most of these functions used to use fixed-size buffers to build their
134  * results.  Now, they take an (already initialized) StringInfo object
135  * as a parameter, and append their text output to its contents.
136  * ----------
137  */
138 static char *deparse_expression_pretty(Node *expr, List *dpcontext,
139                                                   bool forceprefix, bool showimplicit,
140                                                   int prettyFlags, int startIndent);
141 static char *pg_get_viewdef_worker(Oid viewoid, int prettyFlags);
142 static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
143 static void decompile_column_index_array(Datum column_index_array, Oid relId,
144                                                          StringInfo buf);
145 static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
146 static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
147                                            bool attrsOnly, bool showTblSpc,
148                                            int prettyFlags);
149 static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
150                                                         int prettyFlags);
151 static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname,
152                                    int prettyFlags);
153 static int print_function_arguments(StringInfo buf, HeapTuple proctup,
154                                                  bool print_table_args, bool print_defaults);
155 static void print_function_rettype(StringInfo buf, HeapTuple proctup);
156 static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
157                          int prettyFlags);
158 static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
159                          int prettyFlags);
160 static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
161                           TupleDesc resultDesc, int prettyFlags, int startIndent);
162 static void get_values_def(List *values_lists, deparse_context *context);
163 static void get_with_clause(Query *query, deparse_context *context);
164 static void get_select_query_def(Query *query, deparse_context *context,
165                                          TupleDesc resultDesc);
166 static void get_insert_query_def(Query *query, deparse_context *context);
167 static void get_update_query_def(Query *query, deparse_context *context);
168 static void get_delete_query_def(Query *query, deparse_context *context);
169 static void get_utility_query_def(Query *query, deparse_context *context);
170 static void get_basic_select_query(Query *query, deparse_context *context,
171                                            TupleDesc resultDesc);
172 static void get_target_list(List *targetList, deparse_context *context,
173                                 TupleDesc resultDesc);
174 static void get_setop_query(Node *setOp, Query *query,
175                                 deparse_context *context,
176                                 TupleDesc resultDesc);
177 static Node *get_rule_sortgroupclause(SortGroupClause *srt, List *tlist,
178                                                  bool force_colno,
179                                                  deparse_context *context);
180 static void get_rule_orderby(List *orderList, List *targetList,
181                                  bool force_colno, deparse_context *context);
182 static void get_rule_windowclause(Query *query, deparse_context *context);
183 static void get_rule_windowspec(WindowClause *wc, List *targetList,
184                                         deparse_context *context);
185 static void push_plan(deparse_namespace *dpns, Plan *subplan);
186 static char *get_variable(Var *var, int levelsup, bool showstar,
187                          deparse_context *context);
188 static RangeTblEntry *find_rte_by_refname(const char *refname,
189                                         deparse_context *context);
190 static const char *get_simple_binary_op_name(OpExpr *expr);
191 static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
192 static void appendContextKeyword(deparse_context *context, const char *str,
193                                          int indentBefore, int indentAfter, int indentPlus);
194 static void get_rule_expr(Node *node, deparse_context *context,
195                           bool showimplicit);
196 static void get_oper_expr(OpExpr *expr, deparse_context *context);
197 static void get_func_expr(FuncExpr *expr, deparse_context *context,
198                           bool showimplicit);
199 static void get_agg_expr(Aggref *aggref, deparse_context *context);
200 static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
201 static void get_coercion_expr(Node *arg, deparse_context *context,
202                                   Oid resulttype, int32 resulttypmod,
203                                   Node *parentNode);
204 static void get_const_expr(Const *constval, deparse_context *context,
205                            int showtype);
206 static void simple_quote_literal(StringInfo buf, const char *val);
207 static void get_sublink_expr(SubLink *sublink, deparse_context *context);
208 static void get_from_clause(Query *query, const char *prefix,
209                                 deparse_context *context);
210 static void get_from_clause_item(Node *jtnode, Query *query,
211                                          deparse_context *context);
212 static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
213                                           deparse_context *context);
214 static void get_from_clause_coldeflist(List *names, List *types, List *typmods,
215                                                    deparse_context *context);
216 static void get_opclass_name(Oid opclass, Oid actual_datatype,
217                                  StringInfo buf);
218 static Node *processIndirection(Node *node, deparse_context *context,
219                                    bool printit);
220 static void printSubscripts(ArrayRef *aref, deparse_context *context);
221 static char *generate_relation_name(Oid relid, List *namespaces);
222 static char *generate_function_name(Oid funcid, int nargs, List *argnames,
223                                                                         Oid *argtypes, bool *is_variadic);
224 static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
225 static text *string_to_text(char *str);
226 static char *flatten_reloptions(Oid relid);
227
228 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
229
230
231 /* ----------
232  * get_ruledef                  - Do it all and return a text
233  *                                that could be used as a statement
234  *                                to recreate the rule
235  * ----------
236  */
237 Datum
238 pg_get_ruledef(PG_FUNCTION_ARGS)
239 {
240         Oid                     ruleoid = PG_GETARG_OID(0);
241
242         PG_RETURN_TEXT_P(string_to_text(pg_get_ruledef_worker(ruleoid, 0)));
243 }
244
245
246 Datum
247 pg_get_ruledef_ext(PG_FUNCTION_ARGS)
248 {
249         Oid                     ruleoid = PG_GETARG_OID(0);
250         bool            pretty = PG_GETARG_BOOL(1);
251         int                     prettyFlags;
252
253         prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
254         PG_RETURN_TEXT_P(string_to_text(pg_get_ruledef_worker(ruleoid, prettyFlags)));
255 }
256
257
258 static char *
259 pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
260 {
261         Datum           args[1];
262         char            nulls[1];
263         int                     spirc;
264         HeapTuple       ruletup;
265         TupleDesc       rulettc;
266         StringInfoData buf;
267
268         /*
269          * Do this first so that string is alloc'd in outer context not SPI's.
270          */
271         initStringInfo(&buf);
272
273         /*
274          * Connect to SPI manager
275          */
276         if (SPI_connect() != SPI_OK_CONNECT)
277                 elog(ERROR, "SPI_connect failed");
278
279         /*
280          * On the first call prepare the plan to lookup pg_rewrite. We read
281          * pg_rewrite over the SPI manager instead of using the syscache to be
282          * checked for read access on pg_rewrite.
283          */
284         if (plan_getrulebyoid == NULL)
285         {
286                 Oid                     argtypes[1];
287                 SPIPlanPtr      plan;
288
289                 argtypes[0] = OIDOID;
290                 plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
291                 if (plan == NULL)
292                         elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
293                 plan_getrulebyoid = SPI_saveplan(plan);
294         }
295
296         /*
297          * Get the pg_rewrite tuple for this rule
298          */
299         args[0] = ObjectIdGetDatum(ruleoid);
300         nulls[0] = ' ';
301         spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 1);
302         if (spirc != SPI_OK_SELECT)
303                 elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
304         if (SPI_processed != 1)
305                 appendStringInfo(&buf, "-");
306         else
307         {
308                 /*
309                  * Get the rule's definition and put it into executor's memory
310                  */
311                 ruletup = SPI_tuptable->vals[0];
312                 rulettc = SPI_tuptable->tupdesc;
313                 make_ruledef(&buf, ruletup, rulettc, prettyFlags);
314         }
315
316         /*
317          * Disconnect from SPI manager
318          */
319         if (SPI_finish() != SPI_OK_FINISH)
320                 elog(ERROR, "SPI_finish failed");
321
322         return buf.data;
323 }
324
325
326 /* ----------
327  * get_viewdef                  - Mainly the same thing, but we
328  *                                only return the SELECT part of a view
329  * ----------
330  */
331 Datum
332 pg_get_viewdef(PG_FUNCTION_ARGS)
333 {
334         /* By OID */
335         Oid                     viewoid = PG_GETARG_OID(0);
336
337         PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, 0)));
338 }
339
340
341 Datum
342 pg_get_viewdef_ext(PG_FUNCTION_ARGS)
343 {
344         /* By OID */
345         Oid                     viewoid = PG_GETARG_OID(0);
346         bool            pretty = PG_GETARG_BOOL(1);
347         int                     prettyFlags;
348
349         prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
350         PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags)));
351 }
352
353 Datum
354 pg_get_viewdef_name(PG_FUNCTION_ARGS)
355 {
356         /* By qualified name */
357         text       *viewname = PG_GETARG_TEXT_P(0);
358         RangeVar   *viewrel;
359         Oid                     viewoid;
360
361         viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
362         viewoid = RangeVarGetRelid(viewrel, false);
363
364         PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, 0)));
365 }
366
367
368 Datum
369 pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
370 {
371         /* By qualified name */
372         text       *viewname = PG_GETARG_TEXT_P(0);
373         bool            pretty = PG_GETARG_BOOL(1);
374         int                     prettyFlags;
375         RangeVar   *viewrel;
376         Oid                     viewoid;
377
378         prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
379         viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
380         viewoid = RangeVarGetRelid(viewrel, false);
381
382         PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags)));
383 }
384
385 /*
386  * Common code for by-OID and by-name variants of pg_get_viewdef
387  */
388 static char *
389 pg_get_viewdef_worker(Oid viewoid, int prettyFlags)
390 {
391         Datum           args[2];
392         char            nulls[2];
393         int                     spirc;
394         HeapTuple       ruletup;
395         TupleDesc       rulettc;
396         StringInfoData buf;
397
398         /*
399          * Do this first so that string is alloc'd in outer context not SPI's.
400          */
401         initStringInfo(&buf);
402
403         /*
404          * Connect to SPI manager
405          */
406         if (SPI_connect() != SPI_OK_CONNECT)
407                 elog(ERROR, "SPI_connect failed");
408
409         /*
410          * On the first call prepare the plan to lookup pg_rewrite. We read
411          * pg_rewrite over the SPI manager instead of using the syscache to be
412          * checked for read access on pg_rewrite.
413          */
414         if (plan_getviewrule == NULL)
415         {
416                 Oid                     argtypes[2];
417                 SPIPlanPtr      plan;
418
419                 argtypes[0] = OIDOID;
420                 argtypes[1] = NAMEOID;
421                 plan = SPI_prepare(query_getviewrule, 2, argtypes);
422                 if (plan == NULL)
423                         elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
424                 plan_getviewrule = SPI_saveplan(plan);
425         }
426
427         /*
428          * Get the pg_rewrite tuple for the view's SELECT rule
429          */
430         args[0] = ObjectIdGetDatum(viewoid);
431         args[1] = PointerGetDatum(ViewSelectRuleName);
432         nulls[0] = ' ';
433         nulls[1] = ' ';
434         spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 2);
435         if (spirc != SPI_OK_SELECT)
436                 elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
437         if (SPI_processed != 1)
438                 appendStringInfo(&buf, "Not a view");
439         else
440         {
441                 /*
442                  * Get the rule's definition and put it into executor's memory
443                  */
444                 ruletup = SPI_tuptable->vals[0];
445                 rulettc = SPI_tuptable->tupdesc;
446                 make_viewdef(&buf, ruletup, rulettc, prettyFlags);
447         }
448
449         /*
450          * Disconnect from SPI manager
451          */
452         if (SPI_finish() != SPI_OK_FINISH)
453                 elog(ERROR, "SPI_finish failed");
454
455         return buf.data;
456 }
457
458 /* ----------
459  * get_triggerdef                       - Get the definition of a trigger
460  * ----------
461  */
462 Datum
463 pg_get_triggerdef(PG_FUNCTION_ARGS)
464 {
465         Oid                     trigid = PG_GETARG_OID(0);
466
467         PG_RETURN_TEXT_P(string_to_text(pg_get_triggerdef_worker(trigid, false)));
468 }
469
470 Datum
471 pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
472 {
473         Oid                     trigid = PG_GETARG_OID(0);
474         bool            pretty = PG_GETARG_BOOL(1);
475
476         PG_RETURN_TEXT_P(string_to_text(pg_get_triggerdef_worker(trigid, pretty)));
477 }
478
479 static char *
480 pg_get_triggerdef_worker(Oid trigid, bool pretty)
481 {
482         HeapTuple       ht_trig;
483         Form_pg_trigger trigrec;
484         StringInfoData buf;
485         Relation        tgrel;
486         ScanKeyData skey[1];
487         SysScanDesc tgscan;
488         int                     findx = 0;
489         char       *tgname;
490
491         /*
492          * Fetch the pg_trigger tuple by the Oid of the trigger
493          */
494         tgrel = heap_open(TriggerRelationId, AccessShareLock);
495
496         ScanKeyInit(&skey[0],
497                                 ObjectIdAttributeNumber,
498                                 BTEqualStrategyNumber, F_OIDEQ,
499                                 ObjectIdGetDatum(trigid));
500
501         tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
502                                                                 SnapshotNow, 1, skey);
503
504         ht_trig = systable_getnext(tgscan);
505
506         if (!HeapTupleIsValid(ht_trig))
507                 elog(ERROR, "could not find tuple for trigger %u", trigid);
508
509         trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
510
511         /*
512          * Start the trigger definition. Note that the trigger's name should never
513          * be schema-qualified, but the trigger rel's name may be.
514          */
515         initStringInfo(&buf);
516
517         tgname = NameStr(trigrec->tgname);
518         appendStringInfo(&buf, "CREATE %sTRIGGER %s",
519                                          trigrec->tgisconstraint ? "CONSTRAINT " : "",
520                                          quote_identifier(tgname));
521         appendStringInfoString(&buf, pretty ? "\n    " : " ");
522
523         if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
524                 appendStringInfo(&buf, "BEFORE");
525         else
526                 appendStringInfo(&buf, "AFTER");
527         if (TRIGGER_FOR_INSERT(trigrec->tgtype))
528         {
529                 appendStringInfo(&buf, " INSERT");
530                 findx++;
531         }
532         if (TRIGGER_FOR_DELETE(trigrec->tgtype))
533         {
534                 if (findx > 0)
535                         appendStringInfo(&buf, " OR DELETE");
536                 else
537                         appendStringInfo(&buf, " DELETE");
538                 findx++;
539         }
540         if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
541         {
542                 if (findx > 0)
543                         appendStringInfo(&buf, " OR UPDATE");
544                 else
545                         appendStringInfo(&buf, " UPDATE");
546                 /* tgattr is first var-width field, so OK to access directly */
547                 if (trigrec->tgattr.dim1 > 0)
548                 {
549                         int             i;
550
551                         appendStringInfoString(&buf, " OF ");
552                         for (i = 0; i < trigrec->tgattr.dim1; i++)
553                         {
554                                 char   *attname;
555
556                                 if (i > 0)
557                                         appendStringInfoString(&buf, ", ");
558                                 attname = get_relid_attribute_name(trigrec->tgrelid,
559                                                                                                    trigrec->tgattr.values[i]);
560                                 appendStringInfoString(&buf, quote_identifier(attname));
561                         }
562                 }
563         }
564         if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
565         {
566                 if (findx > 0)
567                         appendStringInfo(&buf, " OR TRUNCATE");
568                 else
569                         appendStringInfo(&buf, " TRUNCATE");
570         }
571         appendStringInfo(&buf, " ON %s",
572                                          generate_relation_name(trigrec->tgrelid, NIL));
573         appendStringInfoString(&buf, pretty ? "\n    " : " ");
574
575         if (trigrec->tgisconstraint)
576         {
577                 if (trigrec->tgconstrrelid != InvalidOid)
578                 {
579                         appendStringInfo(&buf, "FROM %s",
580                                                          generate_relation_name(trigrec->tgconstrrelid, NIL));
581                         appendStringInfoString(&buf, pretty ? "\n    " : " ");
582                 }
583                 if (!trigrec->tgdeferrable)
584                         appendStringInfo(&buf, "NOT ");
585                 appendStringInfo(&buf, "DEFERRABLE INITIALLY ");
586                 if (trigrec->tginitdeferred)
587                         appendStringInfo(&buf, "DEFERRED");
588                 else
589                         appendStringInfo(&buf, "IMMEDIATE");
590                 appendStringInfoString(&buf, pretty ? "\n    " : " ");
591         }
592
593         if (TRIGGER_FOR_ROW(trigrec->tgtype))
594                 appendStringInfo(&buf, "FOR EACH ROW");
595         else
596                 appendStringInfo(&buf, "FOR EACH STATEMENT");
597         appendStringInfoString(&buf, pretty ? "\n    " : " ");
598
599         appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
600                                          generate_function_name(trigrec->tgfoid, 0,
601                                                                                         NIL, NULL, NULL));
602
603         if (trigrec->tgnargs > 0)
604         {
605                 bytea      *val;
606                 bool            isnull;
607                 char       *p;
608                 int                     i;
609
610                 val = DatumGetByteaP(fastgetattr(ht_trig,
611                                                                                  Anum_pg_trigger_tgargs,
612                                                                                  tgrel->rd_att, &isnull));
613                 if (isnull)
614                         elog(ERROR, "tgargs is null for trigger %u", trigid);
615                 p = (char *) VARDATA(val);
616                 for (i = 0; i < trigrec->tgnargs; i++)
617                 {
618                         if (i > 0)
619                                 appendStringInfo(&buf, ", ");
620                         simple_quote_literal(&buf, p);
621                         /* advance p to next string embedded in tgargs */
622                         while (*p)
623                                 p++;
624                         p++;
625                 }
626         }
627
628         /* We deliberately do not put semi-colon at end */
629         appendStringInfo(&buf, ")");
630
631         /* Clean up */
632         systable_endscan(tgscan);
633
634         heap_close(tgrel, AccessShareLock);
635
636         return buf.data;
637 }
638
639 /* ----------
640  * get_indexdef                 - Get the definition of an index
641  *
642  * In the extended version, there is a colno argument as well as pretty bool.
643  *      if colno == 0, we want a complete index definition.
644  *      if colno > 0, we only want the Nth index key's variable or expression.
645  *
646  * Note that the SQL-function versions of this omit any info about the
647  * index tablespace; this is intentional because pg_dump wants it that way.
648  * However pg_get_indexdef_string() includes index tablespace if not default.
649  * ----------
650  */
651 Datum
652 pg_get_indexdef(PG_FUNCTION_ARGS)
653 {
654         Oid                     indexrelid = PG_GETARG_OID(0);
655
656         PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, 0,
657                                                                                                                    false, false, 0)));
658 }
659
660 Datum
661 pg_get_indexdef_ext(PG_FUNCTION_ARGS)
662 {
663         Oid                     indexrelid = PG_GETARG_OID(0);
664         int32           colno = PG_GETARG_INT32(1);
665         bool            pretty = PG_GETARG_BOOL(2);
666         int                     prettyFlags;
667
668         prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
669         PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno,
670                                                                                                                    colno != 0,
671                                                                                                                    false,
672                                                                                                                    prettyFlags)));
673 }
674
675 /* Internal version that returns a palloc'd C string */
676 char *
677 pg_get_indexdef_string(Oid indexrelid)
678 {
679         return pg_get_indexdef_worker(indexrelid, 0, false, true, 0);
680 }
681
682 /* Internal version that just reports the column definitions */
683 char *
684 pg_get_indexdef_columns(Oid indexrelid, bool pretty)
685 {
686         int                     prettyFlags;
687
688         prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
689         return pg_get_indexdef_worker(indexrelid, 0, true, false, prettyFlags);
690 }
691
692 static char *
693 pg_get_indexdef_worker(Oid indexrelid, int colno,
694                                            bool attrsOnly, bool showTblSpc,
695                                            int prettyFlags)
696 {
697         HeapTuple       ht_idx;
698         HeapTuple       ht_idxrel;
699         HeapTuple       ht_am;
700         Form_pg_index idxrec;
701         Form_pg_class idxrelrec;
702         Form_pg_am      amrec;
703         List       *indexprs;
704         ListCell   *indexpr_item;
705         List       *context;
706         Oid                     indrelid;
707         int                     keyno;
708         Oid                     keycoltype;
709         Datum           indclassDatum;
710         Datum           indoptionDatum;
711         bool            isnull;
712         oidvector  *indclass;
713         int2vector *indoption;
714         StringInfoData buf;
715         char       *str;
716         char       *sep;
717
718         /*
719          * Fetch the pg_index tuple by the Oid of the index
720          */
721         ht_idx = SearchSysCache(INDEXRELID,
722                                                         ObjectIdGetDatum(indexrelid),
723                                                         0, 0, 0);
724         if (!HeapTupleIsValid(ht_idx))
725                 elog(ERROR, "cache lookup failed for index %u", indexrelid);
726         idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
727
728         indrelid = idxrec->indrelid;
729         Assert(indexrelid == idxrec->indexrelid);
730
731         /* Must get indclass and indoption the hard way */
732         indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
733                                                                         Anum_pg_index_indclass, &isnull);
734         Assert(!isnull);
735         indclass = (oidvector *) DatumGetPointer(indclassDatum);
736         indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
737                                                                          Anum_pg_index_indoption, &isnull);
738         Assert(!isnull);
739         indoption = (int2vector *) DatumGetPointer(indoptionDatum);
740
741         /*
742          * Fetch the pg_class tuple of the index relation
743          */
744         ht_idxrel = SearchSysCache(RELOID,
745                                                            ObjectIdGetDatum(indexrelid),
746                                                            0, 0, 0);
747         if (!HeapTupleIsValid(ht_idxrel))
748                 elog(ERROR, "cache lookup failed for relation %u", indexrelid);
749         idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
750
751         /*
752          * Fetch the pg_am tuple of the index' access method
753          */
754         ht_am = SearchSysCache(AMOID,
755                                                    ObjectIdGetDatum(idxrelrec->relam),
756                                                    0, 0, 0);
757         if (!HeapTupleIsValid(ht_am))
758                 elog(ERROR, "cache lookup failed for access method %u",
759                          idxrelrec->relam);
760         amrec = (Form_pg_am) GETSTRUCT(ht_am);
761
762         /*
763          * Get the index expressions, if any.  (NOTE: we do not use the relcache
764          * versions of the expressions and predicate, because we want to display
765          * non-const-folded expressions.)
766          */
767         if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
768         {
769                 Datum           exprsDatum;
770                 bool            isnull;
771                 char       *exprsString;
772
773                 exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
774                                                                          Anum_pg_index_indexprs, &isnull);
775                 Assert(!isnull);
776                 exprsString = TextDatumGetCString(exprsDatum);
777                 indexprs = (List *) stringToNode(exprsString);
778                 pfree(exprsString);
779         }
780         else
781                 indexprs = NIL;
782
783         indexpr_item = list_head(indexprs);
784
785         context = deparse_context_for(get_rel_name(indrelid), indrelid);
786
787         /*
788          * Start the index definition.  Note that the index's name should never be
789          * schema-qualified, but the indexed rel's name may be.
790          */
791         initStringInfo(&buf);
792
793         if (!attrsOnly)
794                 appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
795                                                  idxrec->indisunique ? "UNIQUE " : "",
796                                                  quote_identifier(NameStr(idxrelrec->relname)),
797                                                  generate_relation_name(indrelid, NIL),
798                                                  quote_identifier(NameStr(amrec->amname)));
799
800         /*
801          * Report the indexed attributes
802          */
803         sep = "";
804         for (keyno = 0; keyno < idxrec->indnatts; keyno++)
805         {
806                 AttrNumber      attnum = idxrec->indkey.values[keyno];
807                 int16           opt = indoption->values[keyno];
808
809                 if (!colno)
810                         appendStringInfoString(&buf, sep);
811                 sep = ", ";
812
813                 if (attnum != 0)
814                 {
815                         /* Simple index column */
816                         char       *attname;
817
818                         attname = get_relid_attribute_name(indrelid, attnum);
819                         if (!colno || colno == keyno + 1)
820                                 appendStringInfoString(&buf, quote_identifier(attname));
821                         keycoltype = get_atttype(indrelid, attnum);
822                 }
823                 else
824                 {
825                         /* expressional index */
826                         Node       *indexkey;
827
828                         if (indexpr_item == NULL)
829                                 elog(ERROR, "too few entries in indexprs list");
830                         indexkey = (Node *) lfirst(indexpr_item);
831                         indexpr_item = lnext(indexpr_item);
832                         /* Deparse */
833                         str = deparse_expression_pretty(indexkey, context, false, false,
834                                                                                         prettyFlags, 0);
835                         if (!colno || colno == keyno + 1)
836                         {
837                                 /* Need parens if it's not a bare function call */
838                                 if (indexkey && IsA(indexkey, FuncExpr) &&
839                                  ((FuncExpr *) indexkey)->funcformat == COERCE_EXPLICIT_CALL)
840                                         appendStringInfoString(&buf, str);
841                                 else
842                                         appendStringInfo(&buf, "(%s)", str);
843                         }
844                         keycoltype = exprType(indexkey);
845                 }
846
847                 if (!attrsOnly && (!colno || colno == keyno + 1))
848                 {
849                         /* Add the operator class name, if not default */
850                         get_opclass_name(indclass->values[keyno], keycoltype, &buf);
851
852                         /* Add options if relevant */
853                         if (amrec->amcanorder)
854                         {
855                                 /* if it supports sort ordering, report DESC and NULLS opts */
856                                 if (opt & INDOPTION_DESC)
857                                 {
858                                         appendStringInfo(&buf, " DESC");
859                                         /* NULLS FIRST is the default in this case */
860                                         if (!(opt & INDOPTION_NULLS_FIRST))
861                                                 appendStringInfo(&buf, " NULLS LAST");
862                                 }
863                                 else
864                                 {
865                                         if (opt & INDOPTION_NULLS_FIRST)
866                                                 appendStringInfo(&buf, " NULLS FIRST");
867                                 }
868                         }
869                 }
870         }
871
872         if (!attrsOnly)
873         {
874                 appendStringInfoChar(&buf, ')');
875
876                 /*
877                  * If it has options, append "WITH (options)"
878                  */
879                 str = flatten_reloptions(indexrelid);
880                 if (str)
881                 {
882                         appendStringInfo(&buf, " WITH (%s)", str);
883                         pfree(str);
884                 }
885
886                 /*
887                  * If it's in a nondefault tablespace, say so, but only if requested
888                  */
889                 if (showTblSpc)
890                 {
891                         Oid                     tblspc;
892
893                         tblspc = get_rel_tablespace(indexrelid);
894                         if (OidIsValid(tblspc))
895                                 appendStringInfo(&buf, " TABLESPACE %s",
896                                                           quote_identifier(get_tablespace_name(tblspc)));
897                 }
898
899                 /*
900                  * If it's a partial index, decompile and append the predicate
901                  */
902                 if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
903                 {
904                         Node       *node;
905                         Datum           predDatum;
906                         bool            isnull;
907                         char       *predString;
908
909                         /* Convert text string to node tree */
910                         predDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
911                                                                                 Anum_pg_index_indpred, &isnull);
912                         Assert(!isnull);
913                         predString = TextDatumGetCString(predDatum);
914                         node = (Node *) stringToNode(predString);
915                         pfree(predString);
916
917                         /* Deparse */
918                         str = deparse_expression_pretty(node, context, false, false,
919                                                                                         prettyFlags, 0);
920                         appendStringInfo(&buf, " WHERE %s", str);
921                 }
922         }
923
924         /* Clean up */
925         ReleaseSysCache(ht_idx);
926         ReleaseSysCache(ht_idxrel);
927         ReleaseSysCache(ht_am);
928
929         return buf.data;
930 }
931
932
933 /*
934  * pg_get_constraintdef
935  *
936  * Returns the definition for the constraint, ie, everything that needs to
937  * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
938  */
939 Datum
940 pg_get_constraintdef(PG_FUNCTION_ARGS)
941 {
942         Oid                     constraintId = PG_GETARG_OID(0);
943
944         PG_RETURN_TEXT_P(string_to_text(pg_get_constraintdef_worker(constraintId,
945                                                                                                                                 false, 0)));
946 }
947
948 Datum
949 pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
950 {
951         Oid                     constraintId = PG_GETARG_OID(0);
952         bool            pretty = PG_GETARG_BOOL(1);
953         int                     prettyFlags;
954
955         prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
956         PG_RETURN_TEXT_P(string_to_text(pg_get_constraintdef_worker(constraintId,
957                                                                                                            false, prettyFlags)));
958 }
959
960 /* Internal version that returns a palloc'd C string */
961 char *
962 pg_get_constraintdef_string(Oid constraintId)
963 {
964         return pg_get_constraintdef_worker(constraintId, true, 0);
965 }
966
967 static char *
968 pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
969                                                         int prettyFlags)
970 {
971         HeapTuple       tup;
972         Form_pg_constraint conForm;
973         StringInfoData buf;
974
975         tup = SearchSysCache(CONSTROID,
976                                                  ObjectIdGetDatum(constraintId),
977                                                  0, 0, 0);
978         if (!HeapTupleIsValid(tup)) /* should not happen */
979                 elog(ERROR, "cache lookup failed for constraint %u", constraintId);
980         conForm = (Form_pg_constraint) GETSTRUCT(tup);
981
982         initStringInfo(&buf);
983
984         if (fullCommand && OidIsValid(conForm->conrelid))
985         {
986                 appendStringInfo(&buf, "ALTER TABLE ONLY %s ADD CONSTRAINT %s ",
987                                                  generate_relation_name(conForm->conrelid, NIL),
988                                                  quote_identifier(NameStr(conForm->conname)));
989         }
990
991         switch (conForm->contype)
992         {
993                 case CONSTRAINT_FOREIGN:
994                         {
995                                 Datum           val;
996                                 bool            isnull;
997                                 const char *string;
998
999                                 /* Start off the constraint definition */
1000                                 appendStringInfo(&buf, "FOREIGN KEY (");
1001
1002                                 /* Fetch and build referencing-column list */
1003                                 val = SysCacheGetAttr(CONSTROID, tup,
1004                                                                           Anum_pg_constraint_conkey, &isnull);
1005                                 if (isnull)
1006                                         elog(ERROR, "null conkey for constraint %u",
1007                                                  constraintId);
1008
1009                                 decompile_column_index_array(val, conForm->conrelid, &buf);
1010
1011                                 /* add foreign relation name */
1012                                 appendStringInfo(&buf, ") REFERENCES %s(",
1013                                                                  generate_relation_name(conForm->confrelid,
1014                                                                                                                 NIL));
1015
1016                                 /* Fetch and build referenced-column list */
1017                                 val = SysCacheGetAttr(CONSTROID, tup,
1018                                                                           Anum_pg_constraint_confkey, &isnull);
1019                                 if (isnull)
1020                                         elog(ERROR, "null confkey for constraint %u",
1021                                                  constraintId);
1022
1023                                 decompile_column_index_array(val, conForm->confrelid, &buf);
1024
1025                                 appendStringInfo(&buf, ")");
1026
1027                                 /* Add match type */
1028                                 switch (conForm->confmatchtype)
1029                                 {
1030                                         case FKCONSTR_MATCH_FULL:
1031                                                 string = " MATCH FULL";
1032                                                 break;
1033                                         case FKCONSTR_MATCH_PARTIAL:
1034                                                 string = " MATCH PARTIAL";
1035                                                 break;
1036                                         case FKCONSTR_MATCH_UNSPECIFIED:
1037                                                 string = "";
1038                                                 break;
1039                                         default:
1040                                                 elog(ERROR, "unrecognized confmatchtype: %d",
1041                                                          conForm->confmatchtype);
1042                                                 string = "";    /* keep compiler quiet */
1043                                                 break;
1044                                 }
1045                                 appendStringInfoString(&buf, string);
1046
1047                                 /* Add ON UPDATE and ON DELETE clauses, if needed */
1048                                 switch (conForm->confupdtype)
1049                                 {
1050                                         case FKCONSTR_ACTION_NOACTION:
1051                                                 string = NULL;  /* suppress default */
1052                                                 break;
1053                                         case FKCONSTR_ACTION_RESTRICT:
1054                                                 string = "RESTRICT";
1055                                                 break;
1056                                         case FKCONSTR_ACTION_CASCADE:
1057                                                 string = "CASCADE";
1058                                                 break;
1059                                         case FKCONSTR_ACTION_SETNULL:
1060                                                 string = "SET NULL";
1061                                                 break;
1062                                         case FKCONSTR_ACTION_SETDEFAULT:
1063                                                 string = "SET DEFAULT";
1064                                                 break;
1065                                         default:
1066                                                 elog(ERROR, "unrecognized confupdtype: %d",
1067                                                          conForm->confupdtype);
1068                                                 string = NULL;  /* keep compiler quiet */
1069                                                 break;
1070                                 }
1071                                 if (string)
1072                                         appendStringInfo(&buf, " ON UPDATE %s", string);
1073
1074                                 switch (conForm->confdeltype)
1075                                 {
1076                                         case FKCONSTR_ACTION_NOACTION:
1077                                                 string = NULL;  /* suppress default */
1078                                                 break;
1079                                         case FKCONSTR_ACTION_RESTRICT:
1080                                                 string = "RESTRICT";
1081                                                 break;
1082                                         case FKCONSTR_ACTION_CASCADE:
1083                                                 string = "CASCADE";
1084                                                 break;
1085                                         case FKCONSTR_ACTION_SETNULL:
1086                                                 string = "SET NULL";
1087                                                 break;
1088                                         case FKCONSTR_ACTION_SETDEFAULT:
1089                                                 string = "SET DEFAULT";
1090                                                 break;
1091                                         default:
1092                                                 elog(ERROR, "unrecognized confdeltype: %d",
1093                                                          conForm->confdeltype);
1094                                                 string = NULL;  /* keep compiler quiet */
1095                                                 break;
1096                                 }
1097                                 if (string)
1098                                         appendStringInfo(&buf, " ON DELETE %s", string);
1099
1100                                 break;
1101                         }
1102                 case CONSTRAINT_PRIMARY:
1103                 case CONSTRAINT_UNIQUE:
1104                         {
1105                                 Datum           val;
1106                                 bool            isnull;
1107                                 Oid                     indexId;
1108
1109                                 /* Start off the constraint definition */
1110                                 if (conForm->contype == CONSTRAINT_PRIMARY)
1111                                         appendStringInfo(&buf, "PRIMARY KEY (");
1112                                 else
1113                                         appendStringInfo(&buf, "UNIQUE (");
1114
1115                                 /* Fetch and build target column list */
1116                                 val = SysCacheGetAttr(CONSTROID, tup,
1117                                                                           Anum_pg_constraint_conkey, &isnull);
1118                                 if (isnull)
1119                                         elog(ERROR, "null conkey for constraint %u",
1120                                                  constraintId);
1121
1122                                 decompile_column_index_array(val, conForm->conrelid, &buf);
1123
1124                                 appendStringInfo(&buf, ")");
1125
1126                                 indexId = get_constraint_index(constraintId);
1127
1128                                 /* XXX why do we only print these bits if fullCommand? */
1129                                 if (fullCommand && OidIsValid(indexId))
1130                                 {
1131                                         char       *options = flatten_reloptions(indexId);
1132                                         Oid                     tblspc;
1133
1134                                         if (options)
1135                                         {
1136                                                 appendStringInfo(&buf, " WITH (%s)", options);
1137                                                 pfree(options);
1138                                         }
1139
1140                                         tblspc = get_rel_tablespace(indexId);
1141                                         if (OidIsValid(tblspc))
1142                                                 appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
1143                                                           quote_identifier(get_tablespace_name(tblspc)));
1144                                 }
1145
1146                                 break;
1147                         }
1148                 case CONSTRAINT_CHECK:
1149                         {
1150                                 Datum           val;
1151                                 bool            isnull;
1152                                 char       *conbin;
1153                                 char       *consrc;
1154                                 Node       *expr;
1155                                 List       *context;
1156
1157                                 /* Fetch constraint expression in parsetree form */
1158                                 val = SysCacheGetAttr(CONSTROID, tup,
1159                                                                           Anum_pg_constraint_conbin, &isnull);
1160                                 if (isnull)
1161                                         elog(ERROR, "null conbin for constraint %u",
1162                                                  constraintId);
1163
1164                                 conbin = TextDatumGetCString(val);
1165                                 expr = stringToNode(conbin);
1166
1167                                 /* Set up deparsing context for Var nodes in constraint */
1168                                 if (conForm->conrelid != InvalidOid)
1169                                 {
1170                                         /* relation constraint */
1171                                         context = deparse_context_for(get_rel_name(conForm->conrelid),
1172                                                                                                   conForm->conrelid);
1173                                 }
1174                                 else
1175                                 {
1176                                         /* domain constraint --- can't have Vars */
1177                                         context = NIL;
1178                                 }
1179
1180                                 consrc = deparse_expression_pretty(expr, context, false, false,
1181                                                                                                    prettyFlags, 0);
1182
1183                                 /*
1184                                  * Now emit the constraint definition.  There are cases where
1185                                  * the constraint expression will be fully parenthesized and
1186                                  * we don't need the outer parens ... but there are other
1187                                  * cases where we do need 'em.  Be conservative for now.
1188                                  *
1189                                  * Note that simply checking for leading '(' and trailing ')'
1190                                  * would NOT be good enough, consider "(x > 0) AND (y > 0)".
1191                                  */
1192                                 appendStringInfo(&buf, "CHECK (%s)", consrc);
1193
1194                                 break;
1195                         }
1196                 default:
1197                         elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
1198                         break;
1199         }
1200
1201         if (conForm->condeferrable)
1202                 appendStringInfo(&buf, " DEFERRABLE");
1203         if (conForm->condeferred)
1204                 appendStringInfo(&buf, " INITIALLY DEFERRED");
1205
1206         /* Cleanup */
1207         ReleaseSysCache(tup);
1208
1209         return buf.data;
1210 }
1211
1212
1213 /*
1214  * Convert an int16[] Datum into a comma-separated list of column names
1215  * for the indicated relation; append the list to buf.
1216  */
1217 static void
1218 decompile_column_index_array(Datum column_index_array, Oid relId,
1219                                                          StringInfo buf)
1220 {
1221         Datum      *keys;
1222         int                     nKeys;
1223         int                     j;
1224
1225         /* Extract data from array of int16 */
1226         deconstruct_array(DatumGetArrayTypeP(column_index_array),
1227                                           INT2OID, 2, true, 's',
1228                                           &keys, NULL, &nKeys);
1229
1230         for (j = 0; j < nKeys; j++)
1231         {
1232                 char       *colName;
1233
1234                 colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
1235
1236                 if (j == 0)
1237                         appendStringInfoString(buf, quote_identifier(colName));
1238                 else
1239                         appendStringInfo(buf, ", %s", quote_identifier(colName));
1240         }
1241 }
1242
1243
1244 /* ----------
1245  * get_expr                     - Decompile an expression tree
1246  *
1247  * Input: an expression tree in nodeToString form, and a relation OID
1248  *
1249  * Output: reverse-listed expression
1250  *
1251  * Currently, the expression can only refer to a single relation, namely
1252  * the one specified by the second parameter.  This is sufficient for
1253  * partial indexes, column default expressions, etc.  We also support
1254  * Var-free expressions, for which the OID can be InvalidOid.
1255  * ----------
1256  */
1257 Datum
1258 pg_get_expr(PG_FUNCTION_ARGS)
1259 {
1260         text       *expr = PG_GETARG_TEXT_P(0);
1261         Oid                     relid = PG_GETARG_OID(1);
1262         char       *relname;
1263
1264         if (OidIsValid(relid))
1265         {
1266                 /* Get the name for the relation */
1267                 relname = get_rel_name(relid);
1268
1269                 /*
1270                  * If the OID isn't actually valid, don't throw an error, just return
1271                  * NULL.  This is a bit questionable, but it's what we've done
1272                  * historically, and it can help avoid unwanted failures when
1273                  * examining catalog entries for just-deleted relations.
1274                  */
1275                 if (relname == NULL)
1276                         PG_RETURN_NULL();
1277         }
1278         else
1279                 relname = NULL;
1280
1281         PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, 0));
1282 }
1283
1284 Datum
1285 pg_get_expr_ext(PG_FUNCTION_ARGS)
1286 {
1287         text       *expr = PG_GETARG_TEXT_P(0);
1288         Oid                     relid = PG_GETARG_OID(1);
1289         bool            pretty = PG_GETARG_BOOL(2);
1290         int                     prettyFlags;
1291         char       *relname;
1292
1293         prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
1294
1295         if (OidIsValid(relid))
1296         {
1297                 /* Get the name for the relation */
1298                 relname = get_rel_name(relid);
1299                 /* See notes above */
1300                 if (relname == NULL)
1301                         PG_RETURN_NULL();
1302         }
1303         else
1304                 relname = NULL;
1305
1306         PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, prettyFlags));
1307 }
1308
1309 static text *
1310 pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags)
1311 {
1312         Node       *node;
1313         List       *context;
1314         char       *exprstr;
1315         char       *str;
1316
1317         /* Convert input TEXT object to C string */
1318         exprstr = text_to_cstring(expr);
1319
1320         /* Convert expression to node tree */
1321         node = (Node *) stringToNode(exprstr);
1322
1323         pfree(exprstr);
1324
1325         /* Prepare deparse context if needed */
1326         if (OidIsValid(relid))
1327                 context = deparse_context_for(relname, relid);
1328         else
1329                 context = NIL;
1330
1331         /* Deparse */
1332         str = deparse_expression_pretty(node, context, false, false,
1333                                                                         prettyFlags, 0);
1334
1335         return string_to_text(str);
1336 }
1337
1338
1339 /* ----------
1340  * get_userbyid                 - Get a user name by roleid and
1341  *                                fallback to 'unknown (OID=n)'
1342  * ----------
1343  */
1344 Datum
1345 pg_get_userbyid(PG_FUNCTION_ARGS)
1346 {
1347         Oid                     roleid = PG_GETARG_OID(0);
1348         Name            result;
1349         HeapTuple       roletup;
1350         Form_pg_authid role_rec;
1351
1352         /*
1353          * Allocate space for the result
1354          */
1355         result = (Name) palloc(NAMEDATALEN);
1356         memset(NameStr(*result), 0, NAMEDATALEN);
1357
1358         /*
1359          * Get the pg_authid entry and print the result
1360          */
1361         roletup = SearchSysCache(AUTHOID,
1362                                                          ObjectIdGetDatum(roleid),
1363                                                          0, 0, 0);
1364         if (HeapTupleIsValid(roletup))
1365         {
1366                 role_rec = (Form_pg_authid) GETSTRUCT(roletup);
1367                 StrNCpy(NameStr(*result), NameStr(role_rec->rolname), NAMEDATALEN);
1368                 ReleaseSysCache(roletup);
1369         }
1370         else
1371                 sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
1372
1373         PG_RETURN_NAME(result);
1374 }
1375
1376
1377 /*
1378  * pg_get_serial_sequence
1379  *              Get the name of the sequence used by a serial column,
1380  *              formatted suitably for passing to setval, nextval or currval.
1381  *              First parameter is not treated as double-quoted, second parameter
1382  *              is --- see documentation for reason.
1383  */
1384 Datum
1385 pg_get_serial_sequence(PG_FUNCTION_ARGS)
1386 {
1387         text       *tablename = PG_GETARG_TEXT_P(0);
1388         text       *columnname = PG_GETARG_TEXT_PP(1);
1389         RangeVar   *tablerv;
1390         Oid                     tableOid;
1391         char       *column;
1392         AttrNumber      attnum;
1393         Oid                     sequenceId = InvalidOid;
1394         Relation        depRel;
1395         ScanKeyData key[3];
1396         SysScanDesc scan;
1397         HeapTuple       tup;
1398
1399         /* Get the OID of the table */
1400         tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
1401         tableOid = RangeVarGetRelid(tablerv, false);
1402
1403         /* Get the number of the column */
1404         column = text_to_cstring(columnname);
1405
1406         attnum = get_attnum(tableOid, column);
1407         if (attnum == InvalidAttrNumber)
1408                 ereport(ERROR,
1409                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
1410                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
1411                                                 column, tablerv->relname)));
1412
1413         /* Search the dependency table for the dependent sequence */
1414         depRel = heap_open(DependRelationId, AccessShareLock);
1415
1416         ScanKeyInit(&key[0],
1417                                 Anum_pg_depend_refclassid,
1418                                 BTEqualStrategyNumber, F_OIDEQ,
1419                                 ObjectIdGetDatum(RelationRelationId));
1420         ScanKeyInit(&key[1],
1421                                 Anum_pg_depend_refobjid,
1422                                 BTEqualStrategyNumber, F_OIDEQ,
1423                                 ObjectIdGetDatum(tableOid));
1424         ScanKeyInit(&key[2],
1425                                 Anum_pg_depend_refobjsubid,
1426                                 BTEqualStrategyNumber, F_INT4EQ,
1427                                 Int32GetDatum(attnum));
1428
1429         scan = systable_beginscan(depRel, DependReferenceIndexId, true,
1430                                                           SnapshotNow, 3, key);
1431
1432         while (HeapTupleIsValid(tup = systable_getnext(scan)))
1433         {
1434                 Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
1435
1436                 /*
1437                  * We assume any auto dependency of a sequence on a column must be
1438                  * what we are looking for.  (We need the relkind test because indexes
1439                  * can also have auto dependencies on columns.)
1440                  */
1441                 if (deprec->classid == RelationRelationId &&
1442                         deprec->objsubid == 0 &&
1443                         deprec->deptype == DEPENDENCY_AUTO &&
1444                         get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
1445                 {
1446                         sequenceId = deprec->objid;
1447                         break;
1448                 }
1449         }
1450
1451         systable_endscan(scan);
1452         heap_close(depRel, AccessShareLock);
1453
1454         if (OidIsValid(sequenceId))
1455         {
1456                 HeapTuple       classtup;
1457                 Form_pg_class classtuple;
1458                 char       *nspname;
1459                 char       *result;
1460
1461                 /* Get the sequence's pg_class entry */
1462                 classtup = SearchSysCache(RELOID,
1463                                                                   ObjectIdGetDatum(sequenceId),
1464                                                                   0, 0, 0);
1465                 if (!HeapTupleIsValid(classtup))
1466                         elog(ERROR, "cache lookup failed for relation %u", sequenceId);
1467                 classtuple = (Form_pg_class) GETSTRUCT(classtup);
1468
1469                 /* Get the namespace */
1470                 nspname = get_namespace_name(classtuple->relnamespace);
1471                 if (!nspname)
1472                         elog(ERROR, "cache lookup failed for namespace %u",
1473                                  classtuple->relnamespace);
1474
1475                 /* And construct the result string */
1476                 result = quote_qualified_identifier(nspname,
1477                                                                                         NameStr(classtuple->relname));
1478
1479                 ReleaseSysCache(classtup);
1480
1481                 PG_RETURN_TEXT_P(string_to_text(result));
1482         }
1483
1484         PG_RETURN_NULL();
1485 }
1486
1487
1488 /*
1489  * pg_get_functiondef
1490  *              Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
1491  *              the specified function.
1492  */
1493 Datum
1494 pg_get_functiondef(PG_FUNCTION_ARGS)
1495 {
1496         Oid                     funcid = PG_GETARG_OID(0);
1497         StringInfoData buf;
1498         StringInfoData dq;
1499         HeapTuple       proctup;
1500         HeapTuple       langtup;
1501         Form_pg_proc proc;
1502         Form_pg_language lang;
1503         Datum           tmp;
1504         bool            isnull;
1505         const char *prosrc;
1506         const char *name;
1507         const char *nsp;
1508         float4          procost;
1509         int                     oldlen;
1510
1511         initStringInfo(&buf);
1512
1513         /* Look up the function */
1514         proctup = SearchSysCache(PROCOID,
1515                                                          ObjectIdGetDatum(funcid),
1516                                                          0, 0, 0);
1517         if (!HeapTupleIsValid(proctup))
1518                 elog(ERROR, "cache lookup failed for function %u", funcid);
1519         proc = (Form_pg_proc) GETSTRUCT(proctup);
1520         name = NameStr(proc->proname);
1521
1522         if (proc->proisagg)
1523                 ereport(ERROR,
1524                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1525                                  errmsg("\"%s\" is an aggregate function", name)));
1526
1527         /* Need its pg_language tuple for the language name */
1528         langtup = SearchSysCache(LANGOID,
1529                                                          ObjectIdGetDatum(proc->prolang),
1530                                                          0, 0, 0);
1531         if (!HeapTupleIsValid(langtup))
1532                 elog(ERROR, "cache lookup failed for language %u", proc->prolang);
1533         lang = (Form_pg_language) GETSTRUCT(langtup);
1534
1535         /*
1536          * We always qualify the function name, to ensure the right function gets
1537          * replaced.
1538          */
1539         nsp = get_namespace_name(proc->pronamespace);
1540         appendStringInfo(&buf, "CREATE OR REPLACE FUNCTION %s(",
1541                                          quote_qualified_identifier(nsp, name));
1542         (void) print_function_arguments(&buf, proctup, false, true);
1543         appendStringInfoString(&buf, ")\n RETURNS ");
1544         print_function_rettype(&buf, proctup);
1545         appendStringInfo(&buf, "\n LANGUAGE %s\n",
1546                                          quote_identifier(NameStr(lang->lanname)));
1547
1548         /* Emit some miscellaneous options on one line */
1549         oldlen = buf.len;
1550
1551         if (proc->proiswindow)
1552                 appendStringInfoString(&buf, " WINDOW");
1553         switch (proc->provolatile)
1554         {
1555                 case PROVOLATILE_IMMUTABLE:
1556                         appendStringInfoString(&buf, " IMMUTABLE");
1557                         break;
1558                 case PROVOLATILE_STABLE:
1559                         appendStringInfoString(&buf, " STABLE");
1560                         break;
1561                 case PROVOLATILE_VOLATILE:
1562                         break;
1563         }
1564         if (proc->proisstrict)
1565                 appendStringInfoString(&buf, " STRICT");
1566         if (proc->prosecdef)
1567                 appendStringInfoString(&buf, " SECURITY DEFINER");
1568
1569         /* This code for the default cost and rows should match functioncmds.c */
1570         if (proc->prolang == INTERNALlanguageId ||
1571                 proc->prolang == ClanguageId)
1572                 procost = 1;
1573         else
1574                 procost = 100;
1575         if (proc->procost != procost)
1576                 appendStringInfo(&buf, " COST %g", proc->procost);
1577
1578         if (proc->prorows > 0 && proc->prorows != 1000)
1579                 appendStringInfo(&buf, " ROWS %g", proc->prorows);
1580
1581         if (oldlen != buf.len)
1582                 appendStringInfoChar(&buf, '\n');
1583
1584         /* Emit any proconfig options, one per line */
1585         tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
1586         if (!isnull)
1587         {
1588                 ArrayType  *a = DatumGetArrayTypeP(tmp);
1589                 int                     i;
1590
1591                 Assert(ARR_ELEMTYPE(a) == TEXTOID);
1592                 Assert(ARR_NDIM(a) == 1);
1593                 Assert(ARR_LBOUND(a)[0] == 1);
1594
1595                 for (i = 1; i <= ARR_DIMS(a)[0]; i++)
1596                 {
1597                         Datum           d;
1598
1599                         d = array_ref(a, 1, &i,
1600                                                   -1 /* varlenarray */ ,
1601                                                   -1 /* TEXT's typlen */ ,
1602                                                   false /* TEXT's typbyval */ ,
1603                                                   'i' /* TEXT's typalign */ ,
1604                                                   &isnull);
1605                         if (!isnull)
1606                         {
1607                                 char       *configitem = TextDatumGetCString(d);
1608                                 char       *pos;
1609
1610                                 pos = strchr(configitem, '=');
1611                                 if (pos == NULL)
1612                                         continue;
1613                                 *pos++ = '\0';
1614
1615                                 appendStringInfo(&buf, " SET %s TO ",
1616                                                                  quote_identifier(configitem));
1617
1618                                 /*
1619                                  * Some GUC variable names are 'LIST' type and hence must not
1620                                  * be quoted.
1621                                  */
1622                                 if (pg_strcasecmp(configitem, "DateStyle") == 0
1623                                         || pg_strcasecmp(configitem, "search_path") == 0)
1624                                         appendStringInfoString(&buf, pos);
1625                                 else
1626                                         simple_quote_literal(&buf, pos);
1627                                 appendStringInfoChar(&buf, '\n');
1628                         }
1629                 }
1630         }
1631
1632         /* And finally the function definition ... */
1633         appendStringInfoString(&buf, "AS ");
1634
1635         tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
1636         if (!isnull)
1637         {
1638                 simple_quote_literal(&buf, TextDatumGetCString(tmp));
1639                 appendStringInfoString(&buf, ", ");             /* assume prosrc isn't null */
1640         }
1641
1642         tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosrc, &isnull);
1643         if (isnull)
1644                 elog(ERROR, "null prosrc");
1645         prosrc = TextDatumGetCString(tmp);
1646
1647         /*
1648          * We always use dollar quoting.  Figure out a suitable delimiter.
1649          *
1650          * Since the user is likely to be editing the function body string, we
1651          * shouldn't use a short delimiter that he might easily create a conflict
1652          * with.  Hence prefer "$function$", but extend if needed.
1653          */
1654         initStringInfo(&dq);
1655         appendStringInfoString(&dq, "$function");
1656         while (strstr(prosrc, dq.data) != NULL)
1657                 appendStringInfoChar(&dq, 'x');
1658         appendStringInfoChar(&dq, '$');
1659
1660         appendStringInfoString(&buf, dq.data);
1661         appendStringInfoString(&buf, prosrc);
1662         appendStringInfoString(&buf, dq.data);
1663
1664         appendStringInfoString(&buf, "\n");
1665
1666         ReleaseSysCache(langtup);
1667         ReleaseSysCache(proctup);
1668
1669         PG_RETURN_TEXT_P(string_to_text(buf.data));
1670 }
1671
1672 /*
1673  * pg_get_function_arguments
1674  *              Get a nicely-formatted list of arguments for a function.
1675  *              This is everything that would go between the parentheses in
1676  *              CREATE FUNCTION.
1677  */
1678 Datum
1679 pg_get_function_arguments(PG_FUNCTION_ARGS)
1680 {
1681         Oid                     funcid = PG_GETARG_OID(0);
1682         StringInfoData buf;
1683         HeapTuple       proctup;
1684
1685         initStringInfo(&buf);
1686
1687         proctup = SearchSysCache(PROCOID,
1688                                                          ObjectIdGetDatum(funcid),
1689                                                          0, 0, 0);
1690         if (!HeapTupleIsValid(proctup))
1691                 elog(ERROR, "cache lookup failed for function %u", funcid);
1692
1693         (void) print_function_arguments(&buf, proctup, false, true);
1694
1695         ReleaseSysCache(proctup);
1696
1697         PG_RETURN_TEXT_P(string_to_text(buf.data));
1698 }
1699
1700 /*
1701  * pg_get_function_identity_arguments
1702  *              Get a formatted list of arguments for a function.
1703  *              This is everything that would go between the parentheses in
1704  *              ALTER FUNCTION, etc.  In particular, don't print defaults.
1705  */
1706 Datum
1707 pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
1708 {
1709         Oid                     funcid = PG_GETARG_OID(0);
1710         StringInfoData buf;
1711         HeapTuple       proctup;
1712
1713         initStringInfo(&buf);
1714
1715         proctup = SearchSysCache(PROCOID,
1716                                                          ObjectIdGetDatum(funcid),
1717                                                          0, 0, 0);
1718         if (!HeapTupleIsValid(proctup))
1719                 elog(ERROR, "cache lookup failed for function %u", funcid);
1720
1721         (void) print_function_arguments(&buf, proctup, false, false);
1722
1723         ReleaseSysCache(proctup);
1724
1725         PG_RETURN_TEXT_P(string_to_text(buf.data));
1726 }
1727
1728 /*
1729  * pg_get_function_result
1730  *              Get a nicely-formatted version of the result type of a function.
1731  *              This is what would appear after RETURNS in CREATE FUNCTION.
1732  */
1733 Datum
1734 pg_get_function_result(PG_FUNCTION_ARGS)
1735 {
1736         Oid                     funcid = PG_GETARG_OID(0);
1737         StringInfoData buf;
1738         HeapTuple       proctup;
1739
1740         initStringInfo(&buf);
1741
1742         proctup = SearchSysCache(PROCOID,
1743                                                          ObjectIdGetDatum(funcid),
1744                                                          0, 0, 0);
1745         if (!HeapTupleIsValid(proctup))
1746                 elog(ERROR, "cache lookup failed for function %u", funcid);
1747
1748         print_function_rettype(&buf, proctup);
1749
1750         ReleaseSysCache(proctup);
1751
1752         PG_RETURN_TEXT_P(string_to_text(buf.data));
1753 }
1754
1755 /*
1756  * Guts of pg_get_function_result: append the function's return type
1757  * to the specified buffer.
1758  */
1759 static void
1760 print_function_rettype(StringInfo buf, HeapTuple proctup)
1761 {
1762         Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
1763         int                     ntabargs = 0;
1764         StringInfoData rbuf;
1765
1766         initStringInfo(&rbuf);
1767
1768         if (proc->proretset)
1769         {
1770                 /* It might be a table function; try to print the arguments */
1771                 appendStringInfoString(&rbuf, "TABLE(");
1772                 ntabargs = print_function_arguments(&rbuf, proctup, true, false);
1773                 if (ntabargs > 0)
1774                         appendStringInfoString(&rbuf, ")");
1775                 else
1776                         resetStringInfo(&rbuf);
1777         }
1778
1779         if (ntabargs == 0)
1780         {
1781                 /* Not a table function, so do the normal thing */
1782                 if (proc->proretset)
1783                         appendStringInfoString(&rbuf, "SETOF ");
1784                 appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
1785         }
1786
1787         appendStringInfoString(buf, rbuf.data);
1788 }
1789
1790 /*
1791  * Common code for pg_get_function_arguments and pg_get_function_result:
1792  * append the desired subset of arguments to buf.  We print only TABLE
1793  * arguments when print_table_args is true, and all the others when it's false.
1794  * We print argument defaults only if print_defaults is true.
1795  * Function return value is the number of arguments printed.
1796  */
1797 static int
1798 print_function_arguments(StringInfo buf, HeapTuple proctup,
1799                                                  bool print_table_args, bool print_defaults)
1800 {
1801         Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
1802         int                     numargs;
1803         Oid                *argtypes;
1804         char      **argnames;
1805         char       *argmodes;
1806         int                     argsprinted;
1807         int                     inputargno;
1808         int                     nlackdefaults;
1809         ListCell   *nextargdefault = NULL;
1810         int                     i;
1811
1812         numargs = get_func_arg_info(proctup,
1813                                                                 &argtypes, &argnames, &argmodes);
1814
1815         nlackdefaults = numargs;
1816         if (print_defaults && proc->pronargdefaults > 0)
1817         {
1818                 Datum           proargdefaults;
1819                 bool            isnull;
1820
1821                 proargdefaults = SysCacheGetAttr(PROCOID, proctup,
1822                                                                                  Anum_pg_proc_proargdefaults,
1823                                                                                  &isnull);
1824                 if (!isnull)
1825                 {
1826                         char       *str;
1827                         List       *argdefaults;
1828
1829                         str = TextDatumGetCString(proargdefaults);
1830                         argdefaults = (List *) stringToNode(str);
1831                         Assert(IsA(argdefaults, List));
1832                         pfree(str);
1833                         nextargdefault = list_head(argdefaults);
1834                         /* nlackdefaults counts only *input* arguments lacking defaults */
1835                         nlackdefaults = proc->pronargs - list_length(argdefaults);
1836                 }
1837         }
1838
1839         argsprinted = 0;
1840         inputargno = 0;
1841         for (i = 0; i < numargs; i++)
1842         {
1843                 Oid                     argtype = argtypes[i];
1844                 char       *argname = argnames ? argnames[i] : NULL;
1845                 char            argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
1846                 const char *modename;
1847                 bool            isinput;
1848
1849                 switch (argmode)
1850                 {
1851                         case PROARGMODE_IN:
1852                                 modename = "";
1853                                 isinput = true;
1854                                 break;
1855                         case PROARGMODE_INOUT:
1856                                 modename = "INOUT ";
1857                                 isinput = true;
1858                                 break;
1859                         case PROARGMODE_OUT:
1860                                 modename = "OUT ";
1861                                 isinput = false;
1862                                 break;
1863                         case PROARGMODE_VARIADIC:
1864                                 modename = "VARIADIC ";
1865                                 isinput = true;
1866                                 break;
1867                         case PROARGMODE_TABLE:
1868                                 modename = "";
1869                                 isinput = false;
1870                                 break;
1871                         default:
1872                                 elog(ERROR, "invalid parameter mode '%c'", argmode);
1873                                 modename = NULL;        /* keep compiler quiet */
1874                                 isinput = false;
1875                                 break;
1876                 }
1877                 if (isinput)
1878                         inputargno++;           /* this is a 1-based counter */
1879
1880                 if (print_table_args != (argmode == PROARGMODE_TABLE))
1881                         continue;
1882
1883                 if (argsprinted)
1884                         appendStringInfoString(buf, ", ");
1885                 appendStringInfoString(buf, modename);
1886                 if (argname && argname[0])
1887                         appendStringInfo(buf, "%s ", quote_identifier(argname));
1888                 appendStringInfoString(buf, format_type_be(argtype));
1889                 if (print_defaults && isinput && inputargno > nlackdefaults)
1890                 {
1891                         Node       *expr;
1892
1893                         Assert(nextargdefault != NULL);
1894                         expr = (Node *) lfirst(nextargdefault);
1895                         nextargdefault = lnext(nextargdefault);
1896
1897                         appendStringInfo(buf, " DEFAULT %s",
1898                                                          deparse_expression(expr, NIL, false, false));
1899                 }
1900                 argsprinted++;
1901         }
1902
1903         return argsprinted;
1904 }
1905
1906
1907 /*
1908  * deparse_expression                   - General utility for deparsing expressions
1909  *
1910  * calls deparse_expression_pretty with all prettyPrinting disabled
1911  */
1912 char *
1913 deparse_expression(Node *expr, List *dpcontext,
1914                                    bool forceprefix, bool showimplicit)
1915 {
1916         return deparse_expression_pretty(expr, dpcontext, forceprefix,
1917                                                                          showimplicit, 0, 0);
1918 }
1919
1920 /* ----------
1921  * deparse_expression_pretty    - General utility for deparsing expressions
1922  *
1923  * expr is the node tree to be deparsed.  It must be a transformed expression
1924  * tree (ie, not the raw output of gram.y).
1925  *
1926  * dpcontext is a list of deparse_namespace nodes representing the context
1927  * for interpreting Vars in the node tree.
1928  *
1929  * forceprefix is TRUE to force all Vars to be prefixed with their table names.
1930  *
1931  * showimplicit is TRUE to force all implicit casts to be shown explicitly.
1932  *
1933  * tries to pretty up the output according to prettyFlags and startIndent.
1934  *
1935  * The result is a palloc'd string.
1936  * ----------
1937  */
1938 static char *
1939 deparse_expression_pretty(Node *expr, List *dpcontext,
1940                                                   bool forceprefix, bool showimplicit,
1941                                                   int prettyFlags, int startIndent)
1942 {
1943         StringInfoData buf;
1944         deparse_context context;
1945
1946         initStringInfo(&buf);
1947         context.buf = &buf;
1948         context.namespaces = dpcontext;
1949         context.windowClause = NIL;
1950         context.windowTList = NIL;
1951         context.varprefix = forceprefix;
1952         context.prettyFlags = prettyFlags;
1953         context.indentLevel = startIndent;
1954
1955         get_rule_expr(expr, &context, showimplicit);
1956
1957         return buf.data;
1958 }
1959
1960 /* ----------
1961  * deparse_context_for                  - Build deparse context for a single relation
1962  *
1963  * Given the reference name (alias) and OID of a relation, build deparsing
1964  * context for an expression referencing only that relation (as varno 1,
1965  * varlevelsup 0).      This is sufficient for many uses of deparse_expression.
1966  * ----------
1967  */
1968 List *
1969 deparse_context_for(const char *aliasname, Oid relid)
1970 {
1971         deparse_namespace *dpns;
1972         RangeTblEntry *rte;
1973
1974         dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
1975
1976         /* Build a minimal RTE for the rel */
1977         rte = makeNode(RangeTblEntry);
1978         rte->rtekind = RTE_RELATION;
1979         rte->relid = relid;
1980         rte->eref = makeAlias(aliasname, NIL);
1981         rte->inh = false;
1982         rte->inFromCl = true;
1983
1984         /* Build one-element rtable */
1985         dpns->rtable = list_make1(rte);
1986         dpns->ctes = NIL;
1987         dpns->subplans = NIL;
1988         dpns->outer_plan = dpns->inner_plan = NULL;
1989
1990         /* Return a one-deep namespace stack */
1991         return list_make1(dpns);
1992 }
1993
1994 /*
1995  * deparse_context_for_plan             - Build deparse context for a plan node
1996  *
1997  * When deparsing an expression in a Plan tree, we might have to resolve
1998  * OUTER or INNER references.  To do this, the caller must provide the
1999  * parent Plan node.  In the normal case of a join plan node, OUTER and
2000  * INNER references can be resolved by drilling down into the left and
2001  * right child plans.  A special case is that a nestloop inner indexscan
2002  * might have OUTER Vars, but the outer side of the join is not a child
2003  * plan node.  To handle such cases the outer plan node must be passed
2004  * separately.  (Pass NULL for outer_plan otherwise.)
2005  *
2006  * Note: plan and outer_plan really ought to be declared as "Plan *", but
2007  * we use "Node *" to avoid having to include plannodes.h in builtins.h.
2008  *
2009  * The plan's rangetable list must also be passed.  We actually prefer to use
2010  * the rangetable to resolve simple Vars, but the plan inputs are necessary
2011  * for Vars that reference expressions computed in subplan target lists.
2012  *
2013  * We also need the list of subplans associated with the Plan tree; this
2014  * is for resolving references to CTE subplans.
2015  */
2016 List *
2017 deparse_context_for_plan(Node *plan, Node *outer_plan,
2018                                                  List *rtable, List *subplans)
2019 {
2020         deparse_namespace *dpns;
2021
2022         dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
2023
2024         dpns->rtable = rtable;
2025         dpns->ctes = NIL;
2026         dpns->subplans = subplans;
2027
2028         /*
2029          * Set up outer_plan and inner_plan from the Plan node (this includes
2030          * various special cases for particular Plan types).
2031          */
2032         push_plan(dpns, (Plan *) plan);
2033
2034         /*
2035          * If outer_plan is given, that overrides whatever we got from the plan.
2036          */
2037         if (outer_plan)
2038                 dpns->outer_plan = (Plan *) outer_plan;
2039
2040         /* Return a one-deep namespace stack */
2041         return list_make1(dpns);
2042 }
2043
2044 /* ----------
2045  * make_ruledef                 - reconstruct the CREATE RULE command
2046  *                                for a given pg_rewrite tuple
2047  * ----------
2048  */
2049 static void
2050 make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
2051                          int prettyFlags)
2052 {
2053         char       *rulename;
2054         char            ev_type;
2055         Oid                     ev_class;
2056         int2            ev_attr;
2057         bool            is_instead;
2058         char       *ev_qual;
2059         char       *ev_action;
2060         List       *actions = NIL;
2061         int                     fno;
2062         Datum           dat;
2063         bool            isnull;
2064
2065         /*
2066          * Get the attribute values from the rules tuple
2067          */
2068         fno = SPI_fnumber(rulettc, "rulename");
2069         dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
2070         Assert(!isnull);
2071         rulename = NameStr(*(DatumGetName(dat)));
2072
2073         fno = SPI_fnumber(rulettc, "ev_type");
2074         dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
2075         Assert(!isnull);
2076         ev_type = DatumGetChar(dat);
2077
2078         fno = SPI_fnumber(rulettc, "ev_class");
2079         dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
2080         Assert(!isnull);
2081         ev_class = DatumGetObjectId(dat);
2082
2083         fno = SPI_fnumber(rulettc, "ev_attr");
2084         dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
2085         Assert(!isnull);
2086         ev_attr = DatumGetInt16(dat);
2087
2088         fno = SPI_fnumber(rulettc, "is_instead");
2089         dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
2090         Assert(!isnull);
2091         is_instead = DatumGetBool(dat);
2092
2093         /* these could be nulls */
2094         fno = SPI_fnumber(rulettc, "ev_qual");
2095         ev_qual = SPI_getvalue(ruletup, rulettc, fno);
2096
2097         fno = SPI_fnumber(rulettc, "ev_action");
2098         ev_action = SPI_getvalue(ruletup, rulettc, fno);
2099         if (ev_action != NULL)
2100                 actions = (List *) stringToNode(ev_action);
2101
2102         /*
2103          * Build the rules definition text
2104          */
2105         appendStringInfo(buf, "CREATE RULE %s AS",
2106                                          quote_identifier(rulename));
2107
2108         if (prettyFlags & PRETTYFLAG_INDENT)
2109                 appendStringInfoString(buf, "\n    ON ");
2110         else
2111                 appendStringInfoString(buf, " ON ");
2112
2113         /* The event the rule is fired for */
2114         switch (ev_type)
2115         {
2116                 case '1':
2117                         appendStringInfo(buf, "SELECT");
2118                         break;
2119
2120                 case '2':
2121                         appendStringInfo(buf, "UPDATE");
2122                         break;
2123
2124                 case '3':
2125                         appendStringInfo(buf, "INSERT");
2126                         break;
2127
2128                 case '4':
2129                         appendStringInfo(buf, "DELETE");
2130                         break;
2131
2132                 default:
2133                         ereport(ERROR,
2134                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2135                                          errmsg("rule \"%s\" has unsupported event type %d",
2136                                                         rulename, ev_type)));
2137                         break;
2138         }
2139
2140         /* The relation the rule is fired on */
2141         appendStringInfo(buf, " TO %s", generate_relation_name(ev_class, NIL));
2142         if (ev_attr > 0)
2143                 appendStringInfo(buf, ".%s",
2144                                                  quote_identifier(get_relid_attribute_name(ev_class,
2145                                                                                                                                    ev_attr)));
2146
2147         /* If the rule has an event qualification, add it */
2148         if (ev_qual == NULL)
2149                 ev_qual = "";
2150         if (strlen(ev_qual) > 0 && strcmp(ev_qual, "<>") != 0)
2151         {
2152                 Node       *qual;
2153                 Query      *query;
2154                 deparse_context context;
2155                 deparse_namespace dpns;
2156
2157                 if (prettyFlags & PRETTYFLAG_INDENT)
2158                         appendStringInfoString(buf, "\n  ");
2159                 appendStringInfo(buf, " WHERE ");
2160
2161                 qual = stringToNode(ev_qual);
2162
2163                 /*
2164                  * We need to make a context for recognizing any Vars in the qual
2165                  * (which can only be references to OLD and NEW).  Use the rtable of
2166                  * the first query in the action list for this purpose.
2167                  */
2168                 query = (Query *) linitial(actions);
2169
2170                 /*
2171                  * If the action is INSERT...SELECT, OLD/NEW have been pushed down
2172                  * into the SELECT, and that's what we need to look at. (Ugly kluge
2173                  * ... try to fix this when we redesign querytrees.)
2174                  */
2175                 query = getInsertSelectQuery(query, NULL);
2176
2177                 /* Must acquire locks right away; see notes in get_query_def() */
2178                 AcquireRewriteLocks(query);
2179
2180                 context.buf = buf;
2181                 context.namespaces = list_make1(&dpns);
2182                 context.windowClause = NIL;
2183                 context.windowTList = NIL;
2184                 context.varprefix = (list_length(query->rtable) != 1);
2185                 context.prettyFlags = prettyFlags;
2186                 context.indentLevel = PRETTYINDENT_STD;
2187                 dpns.rtable = query->rtable;
2188                 dpns.ctes = query->cteList;
2189                 dpns.subplans = NIL;
2190                 dpns.outer_plan = dpns.inner_plan = NULL;
2191
2192                 get_rule_expr(qual, &context, false);
2193         }
2194
2195         appendStringInfo(buf, " DO ");
2196
2197         /* The INSTEAD keyword (if so) */
2198         if (is_instead)
2199                 appendStringInfo(buf, "INSTEAD ");
2200
2201         /* Finally the rules actions */
2202         if (list_length(actions) > 1)
2203         {
2204                 ListCell   *action;
2205                 Query      *query;
2206
2207                 appendStringInfo(buf, "(");
2208                 foreach(action, actions)
2209                 {
2210                         query = (Query *) lfirst(action);
2211                         get_query_def(query, buf, NIL, NULL, prettyFlags, 0);
2212                         if (prettyFlags)
2213                                 appendStringInfo(buf, ";\n");
2214                         else
2215                                 appendStringInfo(buf, "; ");
2216                 }
2217                 appendStringInfo(buf, ");");
2218         }
2219         else if (list_length(actions) == 0)
2220         {
2221                 appendStringInfo(buf, "NOTHING;");
2222         }
2223         else
2224         {
2225                 Query      *query;
2226
2227                 query = (Query *) linitial(actions);
2228                 get_query_def(query, buf, NIL, NULL, prettyFlags, 0);
2229                 appendStringInfo(buf, ";");
2230         }
2231 }
2232
2233
2234 /* ----------
2235  * make_viewdef                 - reconstruct the SELECT part of a
2236  *                                view rewrite rule
2237  * ----------
2238  */
2239 static void
2240 make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
2241                          int prettyFlags)
2242 {
2243         Query      *query;
2244         char            ev_type;
2245         Oid                     ev_class;
2246         int2            ev_attr;
2247         bool            is_instead;
2248         char       *ev_qual;
2249         char       *ev_action;
2250         List       *actions = NIL;
2251         Relation        ev_relation;
2252         int                     fno;
2253         bool            isnull;
2254
2255         /*
2256          * Get the attribute values from the rules tuple
2257          */
2258         fno = SPI_fnumber(rulettc, "ev_type");
2259         ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull);
2260
2261         fno = SPI_fnumber(rulettc, "ev_class");
2262         ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull);
2263
2264         fno = SPI_fnumber(rulettc, "ev_attr");
2265         ev_attr = (int2) SPI_getbinval(ruletup, rulettc, fno, &isnull);
2266
2267         fno = SPI_fnumber(rulettc, "is_instead");
2268         is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull);
2269
2270         fno = SPI_fnumber(rulettc, "ev_qual");
2271         ev_qual = SPI_getvalue(ruletup, rulettc, fno);
2272
2273         fno = SPI_fnumber(rulettc, "ev_action");
2274         ev_action = SPI_getvalue(ruletup, rulettc, fno);
2275         if (ev_action != NULL)
2276                 actions = (List *) stringToNode(ev_action);
2277
2278         if (list_length(actions) != 1)
2279         {
2280                 appendStringInfo(buf, "Not a view");
2281                 return;
2282         }
2283
2284         query = (Query *) linitial(actions);
2285
2286         if (ev_type != '1' || ev_attr >= 0 || !is_instead ||
2287                 strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
2288         {
2289                 appendStringInfo(buf, "Not a view");
2290                 return;
2291         }
2292
2293         ev_relation = heap_open(ev_class, AccessShareLock);
2294
2295         get_query_def(query, buf, NIL, RelationGetDescr(ev_relation),
2296                                   prettyFlags, 0);
2297         appendStringInfo(buf, ";");
2298
2299         heap_close(ev_relation, AccessShareLock);
2300 }
2301
2302
2303 /* ----------
2304  * get_query_def                        - Parse back one query parsetree
2305  *
2306  * If resultDesc is not NULL, then it is the output tuple descriptor for
2307  * the view represented by a SELECT query.
2308  * ----------
2309  */
2310 static void
2311 get_query_def(Query *query, StringInfo buf, List *parentnamespace,
2312                           TupleDesc resultDesc, int prettyFlags, int startIndent)
2313 {
2314         deparse_context context;
2315         deparse_namespace dpns;
2316
2317         /*
2318          * Before we begin to examine the query, acquire locks on referenced
2319          * relations, and fix up deleted columns in JOIN RTEs.  This ensures
2320          * consistent results.  Note we assume it's OK to scribble on the passed
2321          * querytree!
2322          */
2323         AcquireRewriteLocks(query);
2324
2325         context.buf = buf;
2326         context.namespaces = lcons(&dpns, list_copy(parentnamespace));
2327         context.windowClause = NIL;
2328         context.windowTList = NIL;
2329         context.varprefix = (parentnamespace != NIL ||
2330                                                  list_length(query->rtable) != 1);
2331         context.prettyFlags = prettyFlags;
2332         context.indentLevel = startIndent;
2333
2334         dpns.rtable = query->rtable;
2335         dpns.ctes = query->cteList;
2336         dpns.subplans = NIL;
2337         dpns.outer_plan = dpns.inner_plan = NULL;
2338
2339         switch (query->commandType)
2340         {
2341                 case CMD_SELECT:
2342                         get_select_query_def(query, &context, resultDesc);
2343                         break;
2344
2345                 case CMD_UPDATE:
2346                         get_update_query_def(query, &context);
2347                         break;
2348
2349                 case CMD_INSERT:
2350                         get_insert_query_def(query, &context);
2351                         break;
2352
2353                 case CMD_DELETE:
2354                         get_delete_query_def(query, &context);
2355                         break;
2356
2357                 case CMD_NOTHING:
2358                         appendStringInfo(buf, "NOTHING");
2359                         break;
2360
2361                 case CMD_UTILITY:
2362                         get_utility_query_def(query, &context);
2363                         break;
2364
2365                 default:
2366                         elog(ERROR, "unrecognized query command type: %d",
2367                                  query->commandType);
2368                         break;
2369         }
2370 }
2371
2372 /* ----------
2373  * get_values_def                       - Parse back a VALUES list
2374  * ----------
2375  */
2376 static void
2377 get_values_def(List *values_lists, deparse_context *context)
2378 {
2379         StringInfo      buf = context->buf;
2380         bool            first_list = true;
2381         ListCell   *vtl;
2382
2383         appendStringInfoString(buf, "VALUES ");
2384
2385         foreach(vtl, values_lists)
2386         {
2387                 List       *sublist = (List *) lfirst(vtl);
2388                 bool            first_col = true;
2389                 ListCell   *lc;
2390
2391                 if (first_list)
2392                         first_list = false;
2393                 else
2394                         appendStringInfoString(buf, ", ");
2395
2396                 appendStringInfoChar(buf, '(');
2397                 foreach(lc, sublist)
2398                 {
2399                         Node       *col = (Node *) lfirst(lc);
2400
2401                         if (first_col)
2402                                 first_col = false;
2403                         else
2404                                 appendStringInfoChar(buf, ',');
2405
2406                         /*
2407                          * Strip any top-level nodes representing indirection assignments,
2408                          * then print the result.
2409                          */
2410                         get_rule_expr(processIndirection(col, context, false),
2411                                                   context, false);
2412                 }
2413                 appendStringInfoChar(buf, ')');
2414         }
2415 }
2416
2417 /* ----------
2418  * get_with_clause                      - Parse back a WITH clause
2419  * ----------
2420  */
2421 static void
2422 get_with_clause(Query *query, deparse_context *context)
2423 {
2424         StringInfo      buf = context->buf;
2425         const char *sep;
2426         ListCell   *l;
2427
2428         if (query->cteList == NIL)
2429                 return;
2430
2431         if (PRETTY_INDENT(context))
2432         {
2433                 context->indentLevel += PRETTYINDENT_STD;
2434                 appendStringInfoChar(buf, ' ');
2435         }
2436
2437         if (query->hasRecursive)
2438                 sep = "WITH RECURSIVE ";
2439         else
2440                 sep = "WITH ";
2441         foreach(l, query->cteList)
2442         {
2443                 CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
2444
2445                 appendStringInfoString(buf, sep);
2446                 appendStringInfoString(buf, quote_identifier(cte->ctename));
2447                 if (cte->aliascolnames)
2448                 {
2449                         bool            first = true;
2450                         ListCell   *col;
2451
2452                         appendStringInfoChar(buf, '(');
2453                         foreach(col, cte->aliascolnames)
2454                         {
2455                                 if (first)
2456                                         first = false;
2457                                 else
2458                                         appendStringInfoString(buf, ", ");
2459                                 appendStringInfoString(buf,
2460                                                                            quote_identifier(strVal(lfirst(col))));
2461                         }
2462                         appendStringInfoChar(buf, ')');
2463                 }
2464                 appendStringInfoString(buf, " AS (");
2465                 if (PRETTY_INDENT(context))
2466                         appendContextKeyword(context, "", 0, 0, 0);
2467                 get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
2468                                           context->prettyFlags, context->indentLevel);
2469                 if (PRETTY_INDENT(context))
2470                         appendContextKeyword(context, "", 0, 0, 0);
2471                 appendStringInfoChar(buf, ')');
2472                 sep = ", ";
2473         }
2474
2475         if (PRETTY_INDENT(context))
2476         {
2477                 context->indentLevel -= PRETTYINDENT_STD;
2478                 appendContextKeyword(context, "", 0, 0, 0);
2479         }
2480         else
2481                 appendStringInfoChar(buf, ' ');
2482 }
2483
2484 /* ----------
2485  * get_select_query_def                 - Parse back a SELECT parsetree
2486  * ----------
2487  */
2488 static void
2489 get_select_query_def(Query *query, deparse_context *context,
2490                                          TupleDesc resultDesc)
2491 {
2492         StringInfo      buf = context->buf;
2493         List       *save_windowclause;
2494         List       *save_windowtlist;
2495         bool            force_colno;
2496         ListCell   *l;
2497
2498         /* Insert the WITH clause if given */
2499         get_with_clause(query, context);
2500
2501         /* Set up context for possible window functions */
2502         save_windowclause = context->windowClause;
2503         context->windowClause = query->windowClause;
2504         save_windowtlist = context->windowTList;
2505         context->windowTList = query->targetList;
2506
2507         /*
2508          * If the Query node has a setOperations tree, then it's the top level of
2509          * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
2510          * fields are interesting in the top query itself.
2511          */
2512         if (query->setOperations)
2513         {
2514                 get_setop_query(query->setOperations, query, context, resultDesc);
2515                 /* ORDER BY clauses must be simple in this case */
2516                 force_colno = true;
2517         }
2518         else
2519         {
2520                 get_basic_select_query(query, context, resultDesc);
2521                 force_colno = false;
2522         }
2523
2524         /* Add the ORDER BY clause if given */
2525         if (query->sortClause != NIL)
2526         {
2527                 appendContextKeyword(context, " ORDER BY ",
2528                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
2529                 get_rule_orderby(query->sortClause, query->targetList,
2530                                                  force_colno, context);
2531         }
2532
2533         /* Add the LIMIT clause if given */
2534         if (query->limitOffset != NULL)
2535         {
2536                 appendContextKeyword(context, " OFFSET ",
2537                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
2538                 get_rule_expr(query->limitOffset, context, false);
2539         }
2540         if (query->limitCount != NULL)
2541         {
2542                 appendContextKeyword(context, " LIMIT ",
2543                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
2544                 if (IsA(query->limitCount, Const) &&
2545                         ((Const *) query->limitCount)->constisnull)
2546                         appendStringInfo(buf, "ALL");
2547                 else
2548                         get_rule_expr(query->limitCount, context, false);
2549         }
2550
2551         /* Add FOR UPDATE/SHARE clauses if present */
2552         foreach(l, query->rowMarks)
2553         {
2554                 RowMarkClause *rc = (RowMarkClause *) lfirst(l);
2555                 RangeTblEntry *rte = rt_fetch(rc->rti, query->rtable);
2556
2557                 if (rc->forUpdate)
2558                         appendContextKeyword(context, " FOR UPDATE",
2559                                                                  -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
2560                 else
2561                         appendContextKeyword(context, " FOR SHARE",
2562                                                                  -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
2563                 appendStringInfo(buf, " OF %s",
2564                                                  quote_identifier(rte->eref->aliasname));
2565                 if (rc->noWait)
2566                         appendStringInfo(buf, " NOWAIT");
2567         }
2568
2569         context->windowClause = save_windowclause;
2570         context->windowTList = save_windowtlist;
2571 }
2572
2573 static void
2574 get_basic_select_query(Query *query, deparse_context *context,
2575                                            TupleDesc resultDesc)
2576 {
2577         StringInfo      buf = context->buf;
2578         char       *sep;
2579         ListCell   *l;
2580
2581         if (PRETTY_INDENT(context))
2582         {
2583                 context->indentLevel += PRETTYINDENT_STD;
2584                 appendStringInfoChar(buf, ' ');
2585         }
2586
2587         /*
2588          * If the query looks like SELECT * FROM (VALUES ...), then print just the
2589          * VALUES part.  This reverses what transformValuesClause() did at parse
2590          * time.  If the jointree contains just a single VALUES RTE, we assume
2591          * this case applies (without looking at the targetlist...)
2592          */
2593         if (list_length(query->jointree->fromlist) == 1)
2594         {
2595                 RangeTblRef *rtr = (RangeTblRef *) linitial(query->jointree->fromlist);
2596
2597                 if (IsA(rtr, RangeTblRef))
2598                 {
2599                         RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
2600
2601                         if (rte->rtekind == RTE_VALUES)
2602                         {
2603                                 get_values_def(rte->values_lists, context);
2604                                 return;
2605                         }
2606                 }
2607         }
2608
2609         /*
2610          * Build up the query string - first we say SELECT
2611          */
2612         appendStringInfo(buf, "SELECT");
2613
2614         /* Add the DISTINCT clause if given */
2615         if (query->distinctClause != NIL)
2616         {
2617                 if (query->hasDistinctOn)
2618                 {
2619                         appendStringInfo(buf, " DISTINCT ON (");
2620                         sep = "";
2621                         foreach(l, query->distinctClause)
2622                         {
2623                                 SortGroupClause *srt = (SortGroupClause *) lfirst(l);
2624
2625                                 appendStringInfoString(buf, sep);
2626                                 get_rule_sortgroupclause(srt, query->targetList,
2627                                                                                  false, context);
2628                                 sep = ", ";
2629                         }
2630                         appendStringInfo(buf, ")");
2631                 }
2632                 else
2633                         appendStringInfo(buf, " DISTINCT");
2634         }
2635
2636         /* Then we tell what to select (the targetlist) */
2637         get_target_list(query->targetList, context, resultDesc);
2638
2639         /* Add the FROM clause if needed */
2640         get_from_clause(query, " FROM ", context);
2641
2642         /* Add the WHERE clause if given */
2643         if (query->jointree->quals != NULL)
2644         {
2645                 appendContextKeyword(context, " WHERE ",
2646                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
2647                 get_rule_expr(query->jointree->quals, context, false);
2648         }
2649
2650         /* Add the GROUP BY clause if given */
2651         if (query->groupClause != NULL)
2652         {
2653                 appendContextKeyword(context, " GROUP BY ",
2654                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
2655                 sep = "";
2656                 foreach(l, query->groupClause)
2657                 {
2658                         SortGroupClause *grp = (SortGroupClause *) lfirst(l);
2659
2660                         appendStringInfoString(buf, sep);
2661                         get_rule_sortgroupclause(grp, query->targetList,
2662                                                                          false, context);
2663                         sep = ", ";
2664                 }
2665         }
2666
2667         /* Add the HAVING clause if given */
2668         if (query->havingQual != NULL)
2669         {
2670                 appendContextKeyword(context, " HAVING ",
2671                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
2672                 get_rule_expr(query->havingQual, context, false);
2673         }
2674
2675         /* Add the WINDOW clause if needed */
2676         if (query->windowClause != NIL)
2677                 get_rule_windowclause(query, context);
2678 }
2679
2680 /* ----------
2681  * get_target_list                      - Parse back a SELECT target list
2682  *
2683  * This is also used for RETURNING lists in INSERT/UPDATE/DELETE.
2684  * ----------
2685  */
2686 static void
2687 get_target_list(List *targetList, deparse_context *context,
2688                                 TupleDesc resultDesc)
2689 {
2690         StringInfo      buf = context->buf;
2691         char       *sep;
2692         int                     colno;
2693         ListCell   *l;
2694
2695         sep = " ";
2696         colno = 0;
2697         foreach(l, targetList)
2698         {
2699                 TargetEntry *tle = (TargetEntry *) lfirst(l);
2700                 char       *colname;
2701                 char       *attname;
2702
2703                 if (tle->resjunk)
2704                         continue;                       /* ignore junk entries */
2705
2706                 appendStringInfoString(buf, sep);
2707                 sep = ", ";
2708                 colno++;
2709
2710                 /*
2711                  * We special-case Var nodes rather than using get_rule_expr. This is
2712                  * needed because get_rule_expr will display a whole-row Var as
2713                  * "foo.*", which is the preferred notation in most contexts, but at
2714                  * the top level of a SELECT list it's not right (the parser will
2715                  * expand that notation into multiple columns, yielding behavior
2716                  * different from a whole-row Var).  We want just "foo", instead.
2717                  */
2718                 if (tle->expr && IsA(tle->expr, Var))
2719                 {
2720                         attname = get_variable((Var *) tle->expr, 0, false, context);
2721                 }
2722                 else
2723                 {
2724                         get_rule_expr((Node *) tle->expr, context, true);
2725                         /* We'll show the AS name unless it's this: */
2726                         attname = "?column?";
2727                 }
2728
2729                 /*
2730                  * Figure out what the result column should be called.  In the context
2731                  * of a view, use the view's tuple descriptor (so as to pick up the
2732                  * effects of any column RENAME that's been done on the view).
2733                  * Otherwise, just use what we can find in the TLE.
2734                  */
2735                 if (resultDesc && colno <= resultDesc->natts)
2736                         colname = NameStr(resultDesc->attrs[colno - 1]->attname);
2737                 else
2738                         colname = tle->resname;
2739
2740                 /* Show AS unless the column's name is correct as-is */
2741                 if (colname)                    /* resname could be NULL */
2742                 {
2743                         if (attname == NULL || strcmp(attname, colname) != 0)
2744                                 appendStringInfo(buf, " AS %s", quote_identifier(colname));
2745                 }
2746         }
2747 }
2748
2749 static void
2750 get_setop_query(Node *setOp, Query *query, deparse_context *context,
2751                                 TupleDesc resultDesc)
2752 {
2753         StringInfo      buf = context->buf;
2754         bool            need_paren;
2755
2756         if (IsA(setOp, RangeTblRef))
2757         {
2758                 RangeTblRef *rtr = (RangeTblRef *) setOp;
2759                 RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
2760                 Query      *subquery = rte->subquery;
2761
2762                 Assert(subquery != NULL);
2763                 Assert(subquery->setOperations == NULL);
2764                 /* Need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y */
2765                 need_paren = (subquery->cteList ||
2766                                           subquery->sortClause ||
2767                                           subquery->rowMarks ||
2768                                           subquery->limitOffset ||
2769                                           subquery->limitCount);
2770                 if (need_paren)
2771                         appendStringInfoChar(buf, '(');
2772                 get_query_def(subquery, buf, context->namespaces, resultDesc,
2773                                           context->prettyFlags, context->indentLevel);
2774                 if (need_paren)
2775                         appendStringInfoChar(buf, ')');
2776         }
2777         else if (IsA(setOp, SetOperationStmt))
2778         {
2779                 SetOperationStmt *op = (SetOperationStmt *) setOp;
2780
2781                 if (PRETTY_INDENT(context))
2782                 {
2783                         context->indentLevel += PRETTYINDENT_STD;
2784                         appendStringInfoSpaces(buf, PRETTYINDENT_STD);
2785                 }
2786
2787                 /*
2788                  * We force parens whenever nesting two SetOperationStmts. There are
2789                  * some cases in which parens are needed around a leaf query too, but
2790                  * those are more easily handled at the next level down (see code
2791                  * above).
2792                  */
2793                 need_paren = !IsA(op->larg, RangeTblRef);
2794
2795                 if (need_paren)
2796                         appendStringInfoChar(buf, '(');
2797                 get_setop_query(op->larg, query, context, resultDesc);
2798                 if (need_paren)
2799                         appendStringInfoChar(buf, ')');
2800
2801                 if (!PRETTY_INDENT(context))
2802                         appendStringInfoChar(buf, ' ');
2803                 switch (op->op)
2804                 {
2805                         case SETOP_UNION:
2806                                 appendContextKeyword(context, "UNION ",
2807                                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
2808                                 break;
2809                         case SETOP_INTERSECT:
2810                                 appendContextKeyword(context, "INTERSECT ",
2811                                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
2812                                 break;
2813                         case SETOP_EXCEPT:
2814                                 appendContextKeyword(context, "EXCEPT ",
2815                                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
2816                                 break;
2817                         default:
2818                                 elog(ERROR, "unrecognized set op: %d",
2819                                          (int) op->op);
2820                 }
2821                 if (op->all)
2822                         appendStringInfo(buf, "ALL ");
2823
2824                 if (PRETTY_INDENT(context))
2825                         appendContextKeyword(context, "", 0, 0, 0);
2826
2827                 need_paren = !IsA(op->rarg, RangeTblRef);
2828
2829                 if (need_paren)
2830                         appendStringInfoChar(buf, '(');
2831                 get_setop_query(op->rarg, query, context, resultDesc);
2832                 if (need_paren)
2833                         appendStringInfoChar(buf, ')');
2834
2835                 if (PRETTY_INDENT(context))
2836                         context->indentLevel -= PRETTYINDENT_STD;
2837         }
2838         else
2839         {
2840                 elog(ERROR, "unrecognized node type: %d",
2841                          (int) nodeTag(setOp));
2842         }
2843 }
2844
2845 /*
2846  * Display a sort/group clause.
2847  *
2848  * Also returns the expression tree, so caller need not find it again.
2849  */
2850 static Node *
2851 get_rule_sortgroupclause(SortGroupClause *srt, List *tlist, bool force_colno,
2852                                                  deparse_context *context)
2853 {
2854         StringInfo      buf = context->buf;
2855         TargetEntry *tle;
2856         Node       *expr;
2857
2858         tle = get_sortgroupclause_tle(srt, tlist);
2859         expr = (Node *) tle->expr;
2860
2861         /*
2862          * Use column-number form if requested by caller.  Otherwise, if
2863          * expression is a constant, force it to be dumped with an explicit cast
2864          * as decoration --- this is because a simple integer constant is
2865          * ambiguous (and will be misinterpreted by findTargetlistEntry()) if we
2866          * dump it without any decoration.      Otherwise, just dump the expression
2867          * normally.
2868          */
2869         if (force_colno)
2870         {
2871                 Assert(!tle->resjunk);
2872                 appendStringInfo(buf, "%d", tle->resno);
2873         }
2874         else if (expr && IsA(expr, Const))
2875                 get_const_expr((Const *) expr, context, 1);
2876         else
2877                 get_rule_expr(expr, context, true);
2878
2879         return expr;
2880 }
2881
2882 /*
2883  * Display an ORDER BY list.
2884  */
2885 static void
2886 get_rule_orderby(List *orderList, List *targetList,
2887                                  bool force_colno, deparse_context *context)
2888 {
2889         StringInfo      buf = context->buf;
2890         const char *sep;
2891         ListCell   *l;
2892
2893         sep = "";
2894         foreach(l, orderList)
2895         {
2896                 SortGroupClause *srt = (SortGroupClause *) lfirst(l);
2897                 Node       *sortexpr;
2898                 Oid                     sortcoltype;
2899                 TypeCacheEntry *typentry;
2900
2901                 appendStringInfoString(buf, sep);
2902                 sortexpr = get_rule_sortgroupclause(srt, targetList,
2903                                                                                         force_colno, context);
2904                 sortcoltype = exprType(sortexpr);
2905                 /* See whether operator is default < or > for datatype */
2906                 typentry = lookup_type_cache(sortcoltype,
2907                                                                          TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
2908                 if (srt->sortop == typentry->lt_opr)
2909                 {
2910                         /* ASC is default, so emit nothing for it */
2911                         if (srt->nulls_first)
2912                                 appendStringInfo(buf, " NULLS FIRST");
2913                 }
2914                 else if (srt->sortop == typentry->gt_opr)
2915                 {
2916                         appendStringInfo(buf, " DESC");
2917                         /* DESC defaults to NULLS FIRST */
2918                         if (!srt->nulls_first)
2919                                 appendStringInfo(buf, " NULLS LAST");
2920                 }
2921                 else
2922                 {
2923                         appendStringInfo(buf, " USING %s",
2924                                                          generate_operator_name(srt->sortop,
2925                                                                                                         sortcoltype,
2926                                                                                                         sortcoltype));
2927                         /* be specific to eliminate ambiguity */
2928                         if (srt->nulls_first)
2929                                 appendStringInfo(buf, " NULLS FIRST");
2930                         else
2931                                 appendStringInfo(buf, " NULLS LAST");
2932                 }
2933                 sep = ", ";
2934         }
2935 }
2936
2937 /*
2938  * Display a WINDOW clause.
2939  *
2940  * Note that the windowClause list might contain only anonymous window
2941  * specifications, in which case we should print nothing here.
2942  */
2943 static void
2944 get_rule_windowclause(Query *query, deparse_context *context)
2945 {
2946         StringInfo      buf = context->buf;
2947         const char *sep;
2948         ListCell   *l;
2949
2950         sep = NULL;
2951         foreach(l, query->windowClause)
2952         {
2953                 WindowClause *wc = (WindowClause *) lfirst(l);
2954
2955                 if (wc->name == NULL)
2956                         continue;                       /* ignore anonymous windows */
2957
2958                 if (sep == NULL)
2959                         appendContextKeyword(context, " WINDOW ",
2960                                                                  -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
2961                 else
2962                         appendStringInfoString(buf, sep);
2963
2964                 appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
2965
2966                 get_rule_windowspec(wc, query->targetList, context);
2967
2968                 sep = ", ";
2969         }
2970 }
2971
2972 /*
2973  * Display a window definition
2974  */
2975 static void
2976 get_rule_windowspec(WindowClause *wc, List *targetList,
2977                                         deparse_context *context)
2978 {
2979         StringInfo      buf = context->buf;
2980         bool            needspace = false;
2981         const char *sep;
2982         ListCell   *l;
2983
2984         appendStringInfoChar(buf, '(');
2985         if (wc->refname)
2986         {
2987                 appendStringInfoString(buf, quote_identifier(wc->refname));
2988                 needspace = true;
2989         }
2990         /* partition clauses are always inherited, so only print if no refname */
2991         if (wc->partitionClause && !wc->refname)
2992         {
2993                 if (needspace)
2994                         appendStringInfoChar(buf, ' ');
2995                 appendStringInfoString(buf, "PARTITION BY ");
2996                 sep = "";
2997                 foreach(l, wc->partitionClause)
2998                 {
2999                         SortGroupClause *grp = (SortGroupClause *) lfirst(l);
3000
3001                         appendStringInfoString(buf, sep);
3002                         get_rule_sortgroupclause(grp, targetList,
3003                                                                          false, context);
3004                         sep = ", ";
3005                 }
3006                 needspace = true;
3007         }
3008         /* print ordering clause only if not inherited */
3009         if (wc->orderClause && !wc->copiedOrder)
3010         {
3011                 if (needspace)
3012                         appendStringInfoChar(buf, ' ');
3013                 appendStringInfoString(buf, "ORDER BY ");
3014                 get_rule_orderby(wc->orderClause, targetList, false, context);
3015                 needspace = true;
3016         }
3017         /* framing clause is never inherited, so print unless it's default */
3018         if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
3019         {
3020                 if (needspace)
3021                         appendStringInfoChar(buf, ' ');
3022                 if (wc->frameOptions & FRAMEOPTION_RANGE)
3023                         appendStringInfoString(buf, "RANGE ");
3024                 else if (wc->frameOptions & FRAMEOPTION_ROWS)
3025                         appendStringInfoString(buf, "ROWS ");
3026                 else
3027                         Assert(false);
3028                 if (wc->frameOptions & FRAMEOPTION_BETWEEN)
3029                         appendStringInfoString(buf, "BETWEEN ");
3030                 if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
3031                         appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
3032                 else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW)
3033                         appendStringInfoString(buf, "CURRENT ROW ");
3034                 else
3035                         Assert(false);
3036                 if (wc->frameOptions & FRAMEOPTION_BETWEEN)
3037                 {
3038                         appendStringInfoString(buf, "AND ");
3039                         if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
3040                                 appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
3041                         else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW)
3042                                 appendStringInfoString(buf, "CURRENT ROW ");
3043                         else
3044                                 Assert(false);
3045                 }
3046                 /* we will now have a trailing space; remove it */
3047                 buf->len--;
3048         }
3049         appendStringInfoChar(buf, ')');
3050 }
3051
3052 /* ----------
3053  * get_insert_query_def                 - Parse back an INSERT parsetree
3054  * ----------
3055  */
3056 static void
3057 get_insert_query_def(Query *query, deparse_context *context)
3058 {
3059         StringInfo      buf = context->buf;
3060         RangeTblEntry *select_rte = NULL;
3061         RangeTblEntry *values_rte = NULL;
3062         RangeTblEntry *rte;
3063         char       *sep;
3064         ListCell   *values_cell;
3065         ListCell   *l;
3066         List       *strippedexprs;
3067
3068         /*
3069          * If it's an INSERT ... SELECT or VALUES (...), (...), ... there will be
3070          * a single RTE for the SELECT or VALUES.
3071          */
3072         foreach(l, query->rtable)
3073         {
3074                 rte = (RangeTblEntry *) lfirst(l);
3075
3076                 if (rte->rtekind == RTE_SUBQUERY)
3077                 {
3078                         if (select_rte)
3079                                 elog(ERROR, "too many subquery RTEs in INSERT");
3080                         select_rte = rte;
3081                 }
3082
3083                 if (rte->rtekind == RTE_VALUES)
3084                 {
3085                         if (values_rte)
3086                                 elog(ERROR, "too many values RTEs in INSERT");
3087                         values_rte = rte;
3088                 }
3089         }
3090         if (select_rte && values_rte)
3091                 elog(ERROR, "both subquery and values RTEs in INSERT");
3092
3093         /*
3094          * Start the query with INSERT INTO relname
3095          */
3096         rte = rt_fetch(query->resultRelation, query->rtable);
3097         Assert(rte->rtekind == RTE_RELATION);
3098
3099         if (PRETTY_INDENT(context))
3100         {
3101                 context->indentLevel += PRETTYINDENT_STD;
3102                 appendStringInfoChar(buf, ' ');
3103         }
3104         appendStringInfo(buf, "INSERT INTO %s (",
3105                                          generate_relation_name(rte->relid, NIL));
3106
3107         /*
3108          * Add the insert-column-names list.  To handle indirection properly, we
3109          * need to look for indirection nodes in the top targetlist (if it's
3110          * INSERT ... SELECT or INSERT ... single VALUES), or in the first
3111          * expression list of the VALUES RTE (if it's INSERT ... multi VALUES). We
3112          * assume that all the expression lists will have similar indirection in
3113          * the latter case.
3114          */
3115         if (values_rte)
3116                 values_cell = list_head((List *) linitial(values_rte->values_lists));
3117         else
3118                 values_cell = NULL;
3119         strippedexprs = NIL;
3120         sep = "";
3121         foreach(l, query->targetList)
3122         {
3123                 TargetEntry *tle = (TargetEntry *) lfirst(l);
3124
3125                 if (tle->resjunk)
3126                         continue;                       /* ignore junk entries */
3127
3128                 appendStringInfoString(buf, sep);
3129                 sep = ", ";
3130
3131                 /*
3132                  * Put out name of target column; look in the catalogs, not at
3133                  * tle->resname, since resname will fail to track RENAME.
3134                  */
3135                 appendStringInfoString(buf,
3136                                                 quote_identifier(get_relid_attribute_name(rte->relid,
3137                                                                                                                            tle->resno)));
3138
3139                 /*
3140                  * Print any indirection needed (subfields or subscripts), and strip
3141                  * off the top-level nodes representing the indirection assignments.
3142                  */
3143                 if (values_cell)
3144                 {
3145                         /* we discard the stripped expression in this case */
3146                         processIndirection((Node *) lfirst(values_cell), context, true);
3147                         values_cell = lnext(values_cell);
3148                 }
3149                 else
3150                 {
3151                         /* we keep a list of the stripped expressions in this case */
3152                         strippedexprs = lappend(strippedexprs,
3153                                                                         processIndirection((Node *) tle->expr,
3154                                                                                                            context, true));
3155                 }
3156         }
3157         appendStringInfo(buf, ") ");
3158
3159         if (select_rte)
3160         {
3161                 /* Add the SELECT */
3162                 get_query_def(select_rte->subquery, buf, NIL, NULL,
3163                                           context->prettyFlags, context->indentLevel);
3164         }
3165         else if (values_rte)
3166         {
3167                 /* A WITH clause is possible here */
3168                 get_with_clause(query, context);
3169                 /* Add the multi-VALUES expression lists */
3170                 get_values_def(values_rte->values_lists, context);
3171         }
3172         else
3173         {
3174                 /* A WITH clause is possible here */
3175                 get_with_clause(query, context);
3176                 /* Add the single-VALUES expression list */
3177                 appendContextKeyword(context, "VALUES (",
3178                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
3179                 get_rule_expr((Node *) strippedexprs, context, false);
3180                 appendStringInfoChar(buf, ')');
3181         }
3182
3183         /* Add RETURNING if present */
3184         if (query->returningList)
3185         {
3186                 appendContextKeyword(context, " RETURNING",
3187                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
3188                 get_target_list(query->returningList, context, NULL);
3189         }
3190 }
3191
3192
3193 /* ----------
3194  * get_update_query_def                 - Parse back an UPDATE parsetree
3195  * ----------
3196  */
3197 static void
3198 get_update_query_def(Query *query, deparse_context *context)
3199 {
3200         StringInfo      buf = context->buf;
3201         char       *sep;
3202         RangeTblEntry *rte;
3203         ListCell   *l;
3204
3205         /*
3206          * Start the query with UPDATE relname SET
3207          */
3208         rte = rt_fetch(query->resultRelation, query->rtable);
3209         Assert(rte->rtekind == RTE_RELATION);
3210         if (PRETTY_INDENT(context))
3211         {
3212                 appendStringInfoChar(buf, ' ');
3213                 context->indentLevel += PRETTYINDENT_STD;
3214         }
3215         appendStringInfo(buf, "UPDATE %s%s",
3216                                          only_marker(rte),
3217                                          generate_relation_name(rte->relid, NIL));
3218         if (rte->alias != NULL)
3219                 appendStringInfo(buf, " %s",
3220                                                  quote_identifier(rte->alias->aliasname));
3221         appendStringInfoString(buf, " SET ");
3222
3223         /* Add the comma separated list of 'attname = value' */
3224         sep = "";
3225         foreach(l, query->targetList)
3226         {
3227                 TargetEntry *tle = (TargetEntry *) lfirst(l);
3228                 Node       *expr;
3229
3230                 if (tle->resjunk)
3231                         continue;                       /* ignore junk entries */
3232
3233                 appendStringInfoString(buf, sep);
3234                 sep = ", ";
3235
3236                 /*
3237                  * Put out name of target column; look in the catalogs, not at
3238                  * tle->resname, since resname will fail to track RENAME.
3239                  */
3240                 appendStringInfoString(buf,
3241                                                 quote_identifier(get_relid_attribute_name(rte->relid,
3242                                                                                                                            tle->resno)));
3243
3244                 /*
3245                  * Print any indirection needed (subfields or subscripts), and strip
3246                  * off the top-level nodes representing the indirection assignments.
3247                  */
3248                 expr = processIndirection((Node *) tle->expr, context, true);
3249
3250                 appendStringInfo(buf, " = ");
3251
3252                 get_rule_expr(expr, context, false);
3253         }
3254
3255         /* Add the FROM clause if needed */
3256         get_from_clause(query, " FROM ", context);
3257
3258         /* Add a WHERE clause if given */
3259         if (query->jointree->quals != NULL)
3260         {
3261                 appendContextKeyword(context, " WHERE ",
3262                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
3263                 get_rule_expr(query->jointree->quals, context, false);
3264         }
3265
3266         /* Add RETURNING if present */
3267         if (query->returningList)
3268         {
3269                 appendContextKeyword(context, " RETURNING",
3270                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
3271                 get_target_list(query->returningList, context, NULL);
3272         }
3273 }
3274
3275
3276 /* ----------
3277  * get_delete_query_def                 - Parse back a DELETE parsetree
3278  * ----------
3279  */
3280 static void
3281 get_delete_query_def(Query *query, deparse_context *context)
3282 {
3283         StringInfo      buf = context->buf;
3284         RangeTblEntry *rte;
3285
3286         /*
3287          * Start the query with DELETE FROM relname
3288          */
3289         rte = rt_fetch(query->resultRelation, query->rtable);
3290         Assert(rte->rtekind == RTE_RELATION);
3291         if (PRETTY_INDENT(context))
3292         {
3293                 appendStringInfoChar(buf, ' ');
3294                 context->indentLevel += PRETTYINDENT_STD;
3295         }
3296         appendStringInfo(buf, "DELETE FROM %s%s",
3297                                          only_marker(rte),
3298                                          generate_relation_name(rte->relid, NIL));
3299         if (rte->alias != NULL)
3300                 appendStringInfo(buf, " %s",
3301                                                  quote_identifier(rte->alias->aliasname));
3302
3303         /* Add the USING clause if given */
3304         get_from_clause(query, " USING ", context);
3305
3306         /* Add a WHERE clause if given */
3307         if (query->jointree->quals != NULL)
3308         {
3309                 appendContextKeyword(context, " WHERE ",
3310                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
3311                 get_rule_expr(query->jointree->quals, context, false);
3312         }
3313
3314         /* Add RETURNING if present */
3315         if (query->returningList)
3316         {
3317                 appendContextKeyword(context, " RETURNING",
3318                                                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
3319                 get_target_list(query->returningList, context, NULL);
3320         }
3321 }
3322
3323
3324 /* ----------
3325  * get_utility_query_def                        - Parse back a UTILITY parsetree
3326  * ----------
3327  */
3328 static void
3329 get_utility_query_def(Query *query, deparse_context *context)
3330 {
3331         StringInfo      buf = context->buf;
3332
3333         if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
3334         {
3335                 NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
3336
3337                 appendContextKeyword(context, "",
3338                                                          0, PRETTYINDENT_STD, 1);
3339                 appendStringInfo(buf, "NOTIFY %s",
3340                                                  quote_identifier(stmt->conditionname));
3341         }
3342         else
3343         {
3344                 /* Currently only NOTIFY utility commands can appear in rules */
3345                 elog(ERROR, "unexpected utility statement type");
3346         }
3347 }
3348
3349
3350 /*
3351  * push_plan: set up deparse_namespace to recurse into the tlist of a subplan
3352  *
3353  * When expanding an OUTER or INNER reference, we must push new outer/inner
3354  * subplans in case the referenced expression itself uses OUTER/INNER.  We
3355  * modify the top stack entry in-place to avoid affecting levelsup issues
3356  * (although in a Plan tree there really shouldn't be any).
3357  *
3358  * Caller must save and restore outer_plan and inner_plan around this.
3359  *
3360  * We also use this to initialize the fields during deparse_context_for_plan.
3361  */
3362 static void
3363 push_plan(deparse_namespace *dpns, Plan *subplan)
3364 {
3365         /*
3366          * We special-case ModifyTable to pretend that the first child plan is the
3367          * OUTER referent; otherwise normal.  This is to support RETURNING lists
3368          * containing references to non-target relations.
3369          */
3370         if (IsA(subplan, ModifyTable))
3371                 dpns->outer_plan = (Plan *) linitial(((ModifyTable *) subplan)->plans);
3372         else
3373                 dpns->outer_plan = outerPlan(subplan);
3374
3375         /*
3376          * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't
3377          * use OUTER because that could someday conflict with the normal meaning.)
3378          * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
3379          */
3380         if (IsA(subplan, SubqueryScan))
3381                 dpns->inner_plan = ((SubqueryScan *) subplan)->subplan;
3382         else if (IsA(subplan, CteScan))
3383         {
3384                 int                     ctePlanId = ((CteScan *) subplan)->ctePlanId;
3385
3386                 if (ctePlanId > 0 && ctePlanId <= list_length(dpns->subplans))
3387                         dpns->inner_plan = list_nth(dpns->subplans, ctePlanId - 1);
3388                 else
3389                         dpns->inner_plan = NULL;
3390         }
3391         else
3392                 dpns->inner_plan = innerPlan(subplan);
3393 }
3394
3395
3396 /*
3397  * Display a Var appropriately.
3398  *
3399  * In some cases (currently only when recursing into an unnamed join)
3400  * the Var's varlevelsup has to be interpreted with respect to a context
3401  * above the current one; levelsup indicates the offset.
3402  *
3403  * If showstar is TRUE, whole-row Vars are displayed as "foo.*";
3404  * if FALSE, merely as "foo".
3405  *
3406  * Returns the attname of the Var, or NULL if not determinable.
3407  */
3408 static char *
3409 get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
3410 {
3411         StringInfo      buf = context->buf;
3412         RangeTblEntry *rte;
3413         AttrNumber      attnum;
3414         int                     netlevelsup;
3415         deparse_namespace *dpns;
3416         char       *schemaname;
3417         char       *refname;
3418         char       *attname;
3419
3420         /* Find appropriate nesting depth */
3421         netlevelsup = var->varlevelsup + levelsup;
3422         if (netlevelsup >= list_length(context->namespaces))
3423                 elog(ERROR, "bogus varlevelsup: %d offset %d",
3424                          var->varlevelsup, levelsup);
3425         dpns = (deparse_namespace *) list_nth(context->namespaces,
3426                                                                                   netlevelsup);
3427
3428         /*
3429          * Try to find the relevant RTE in this rtable.  In a plan tree, it's
3430          * likely that varno is OUTER or INNER, in which case we must dig down
3431          * into the subplans.
3432          */
3433         if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
3434         {
3435                 rte = rt_fetch(var->varno, dpns->rtable);
3436                 attnum = var->varattno;
3437         }
3438         else if (var->varno == OUTER && dpns->outer_plan)
3439         {
3440                 TargetEntry *tle;
3441                 Plan       *save_outer;
3442                 Plan       *save_inner;
3443
3444                 tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
3445                 if (!tle)
3446                         elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
3447
3448                 Assert(netlevelsup == 0);
3449                 save_outer = dpns->outer_plan;
3450                 save_inner = dpns->inner_plan;
3451                 push_plan(dpns, dpns->outer_plan);
3452
3453                 /*
3454                  * Force parentheses because our caller probably assumed a Var is a
3455                  * simple expression.
3456                  */
3457                 if (!IsA(tle->expr, Var))
3458                         appendStringInfoChar(buf, '(');
3459                 get_rule_expr((Node *) tle->expr, context, true);
3460                 if (!IsA(tle->expr, Var))
3461                         appendStringInfoChar(buf, ')');
3462
3463                 dpns->outer_plan = save_outer;
3464                 dpns->inner_plan = save_inner;
3465                 return NULL;
3466         }
3467         else if (var->varno == INNER && dpns->inner_plan)
3468         {
3469                 TargetEntry *tle;
3470                 Plan       *save_outer;
3471                 Plan       *save_inner;
3472
3473                 tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
3474                 if (!tle)
3475                         elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
3476
3477                 Assert(netlevelsup == 0);
3478                 save_outer = dpns->outer_plan;
3479                 save_inner = dpns->inner_plan;
3480                 push_plan(dpns, dpns->inner_plan);
3481
3482                 /*
3483                  * Force parentheses because our caller probably assumed a Var is a
3484                  * simple expression.
3485                  */
3486                 if (!IsA(tle->expr, Var))
3487                         appendStringInfoChar(buf, '(');
3488                 get_rule_expr((Node *) tle->expr, context, true);
3489                 if (!IsA(tle->expr, Var))
3490                         appendStringInfoChar(buf, ')');
3491
3492                 dpns->outer_plan = save_outer;
3493                 dpns->inner_plan = save_inner;
3494                 return NULL;
3495         }
3496         else
3497         {
3498                 elog(ERROR, "bogus varno: %d", var->varno);
3499                 return NULL;                    /* keep compiler quiet */
3500         }
3501
3502         /* Identify names to use */
3503         schemaname = NULL;                      /* default assumptions */
3504         refname = rte->eref->aliasname;
3505
3506         /* Exceptions occur only if the RTE is alias-less */
3507         if (rte->alias == NULL)
3508         {
3509                 if (rte->rtekind == RTE_RELATION)
3510                 {
3511                         /*
3512                          * It's possible that use of the bare refname would find another
3513                          * more-closely-nested RTE, or be ambiguous, in which case we need
3514                          * to specify the schemaname to avoid these errors.
3515                          */
3516                         if (find_rte_by_refname(rte->eref->aliasname, context) != rte)
3517                                 schemaname = get_namespace_name(get_rel_namespace(rte->relid));
3518                 }
3519                 else if (rte->rtekind == RTE_JOIN)
3520                 {
3521                         /*
3522                          * If it's an unnamed join, look at the expansion of the alias
3523                          * variable.  If it's a simple reference to one of the input vars
3524                          * then recursively print the name of that var, instead. (This
3525                          * allows correct decompiling of cases where there are identically
3526                          * named columns on both sides of the join.) When it's not a
3527                          * simple reference, we have to just print the unqualified
3528                          * variable name (this can only happen with columns that were
3529                          * merged by USING or NATURAL clauses).
3530                          *
3531                          * This wouldn't work in decompiling plan trees, because we don't
3532                          * store joinaliasvars lists after planning; but a plan tree
3533                          * should never contain a join alias variable.
3534                          */
3535                         if (rte->joinaliasvars == NIL)
3536                                 elog(ERROR, "cannot decompile join alias var in plan tree");
3537                         if (attnum > 0)
3538                         {
3539                                 Var                *aliasvar;
3540
3541                                 aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
3542                                 if (IsA(aliasvar, Var))
3543                                 {
3544                                         return get_variable(aliasvar, var->varlevelsup + levelsup,
3545                                                                                 showstar, context);
3546                                 }
3547                         }
3548                         /* Unnamed join has neither schemaname nor refname */
3549                         refname = NULL;
3550                 }
3551         }
3552
3553         if (attnum == InvalidAttrNumber)
3554                 attname = NULL;
3555         else
3556                 attname = get_rte_attribute_name(rte, attnum);
3557
3558         if (refname && (context->varprefix || attname == NULL))
3559         {
3560                 if (schemaname)
3561                         appendStringInfo(buf, "%s.",
3562                                                          quote_identifier(schemaname));
3563
3564                 if (strcmp(refname, "*NEW*") == 0)
3565                         appendStringInfoString(buf, "new");
3566                 else if (strcmp(refname, "*OLD*") == 0)
3567                         appendStringInfoString(buf, "old");
3568                 else
3569                         appendStringInfoString(buf, quote_identifier(refname));
3570
3571                 if (attname || showstar)
3572                         appendStringInfoChar(buf, '.');
3573         }
3574         if (attname)
3575                 appendStringInfoString(buf, quote_identifier(attname));
3576         else if (showstar)
3577                 appendStringInfoChar(buf, '*');
3578
3579         return attname;
3580 }
3581
3582
3583 /*
3584  * Get the name of a field of an expression of composite type.
3585  *
3586  * This is fairly straightforward except for the case of a Var of type RECORD.
3587  * Since no actual table or view column is allowed to have type RECORD, such
3588  * a Var must refer to a JOIN or FUNCTION RTE or to a subquery output.  We
3589  * drill down to find the ultimate defining expression and attempt to infer
3590  * the field name from it.      We ereport if we can't determine the name.
3591  *
3592  * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
3593  */
3594 static const char *
3595 get_name_for_var_field(Var *var, int fieldno,
3596                                            int levelsup, deparse_context *context)
3597 {
3598         RangeTblEntry *rte;
3599         AttrNumber      attnum;
3600         int                     netlevelsup;
3601         deparse_namespace *dpns;
3602         TupleDesc       tupleDesc;
3603         Node       *expr;
3604
3605         /*
3606          * If it's a RowExpr that was expanded from a whole-row Var, use the
3607          * column names attached to it.
3608          */
3609         if (IsA(var, RowExpr))
3610         {
3611                 RowExpr    *r = (RowExpr *) var;
3612
3613                 if (fieldno > 0 && fieldno <= list_length(r->colnames))
3614                         return strVal(list_nth(r->colnames, fieldno - 1));
3615         }
3616
3617         /*
3618          * If it's a Var of type RECORD, we have to find what the Var refers to;
3619          * if not, we can use get_expr_result_type. If that fails, we try
3620          * lookup_rowtype_tupdesc, which will probably fail too, but will ereport
3621          * an acceptable message.
3622          */
3623         if (!IsA(var, Var) ||
3624                 var->vartype != RECORDOID)
3625         {
3626                 if (get_expr_result_type((Node *) var, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
3627                         tupleDesc = lookup_rowtype_tupdesc_copy(exprType((Node *) var),
3628                                                                                                         exprTypmod((Node *) var));
3629                 Assert(tupleDesc);
3630                 /* Got the tupdesc, so we can extract the field name */
3631                 Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
3632                 return NameStr(tupleDesc->attrs[fieldno - 1]->attname);
3633         }
3634
3635         /* Find appropriate nesting depth */
3636         netlevelsup = var->varlevelsup + levelsup;
3637         if (netlevelsup >= list_length(context->namespaces))
3638                 elog(ERROR, "bogus varlevelsup: %d offset %d",
3639                          var->varlevelsup, levelsup);
3640         dpns = (deparse_namespace *) list_nth(context->namespaces,
3641                                                                                   netlevelsup);
3642
3643         /*
3644          * Try to find the relevant RTE in this rtable.  In a plan tree, it's
3645          * likely that varno is OUTER or INNER, in which case we must dig down
3646          * into the subplans.
3647          */
3648         if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
3649         {
3650                 rte = rt_fetch(var->varno, dpns->rtable);
3651                 attnum = var->varattno;
3652         }
3653         else if (var->varno == OUTER && dpns->outer_plan)
3654         {
3655                 TargetEntry *tle;
3656                 Plan       *save_outer;
3657                 Plan       *save_inner;
3658                 const char *result;
3659
3660                 tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
3661                 if (!tle)
3662                         elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
3663
3664                 Assert(netlevelsup == 0);
3665                 save_outer = dpns->outer_plan;
3666                 save_inner = dpns->inner_plan;
3667                 push_plan(dpns, dpns->outer_plan);
3668
3669                 result = get_name_for_var_field((Var *) tle->expr, fieldno,
3670                                                                                 levelsup, context);
3671
3672                 dpns->outer_plan = save_outer;
3673                 dpns->inner_plan = save_inner;
3674                 return result;
3675         }
3676         else if (var->varno == INNER && dpns->inner_plan)
3677         {
3678                 TargetEntry *tle;
3679                 Plan       *save_outer;
3680                 Plan       *save_inner;
3681                 const char *result;
3682
3683                 tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
3684                 if (!tle)
3685                         elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
3686
3687                 Assert(netlevelsup == 0);
3688                 save_outer = dpns->outer_plan;
3689                 save_inner = dpns->inner_plan;
3690                 push_plan(dpns, dpns->inner_plan);
3691
3692                 result = get_name_for_var_field((Var *) tle->expr, fieldno,
3693                                                                                 levelsup, context);
3694
3695                 dpns->outer_plan = save_outer;
3696                 dpns->inner_plan = save_inner;
3697                 return result;
3698         }
3699         else
3700         {
3701                 elog(ERROR, "bogus varno: %d", var->varno);
3702                 return NULL;                    /* keep compiler quiet */
3703         }
3704
3705         if (attnum == InvalidAttrNumber)
3706         {
3707                 /* Var is whole-row reference to RTE, so select the right field */
3708                 return get_rte_attribute_name(rte, fieldno);
3709         }
3710
3711         /*
3712          * This part has essentially the same logic as the parser's
3713          * expandRecordVariable() function, but we are dealing with a different
3714          * representation of the input context, and we only need one field name
3715          * not a TupleDesc.  Also, we need special cases for finding subquery and
3716          * CTE subplans when deparsing Plan trees.
3717          */
3718         expr = (Node *) var;            /* default if we can't drill down */
3719
3720         switch (rte->rtekind)
3721         {
3722                 case RTE_RELATION:
3723                 case RTE_SPECIAL:
3724                 case RTE_VALUES:
3725
3726                         /*
3727                          * This case should not occur: a column of a table or values list
3728                          * shouldn't have type RECORD.  Fall through and fail (most
3729                          * likely) at the bottom.
3730                          */
3731                         break;
3732                 case RTE_SUBQUERY:
3733                         /* Subselect-in-FROM: examine sub-select's output expr */
3734                         {
3735                                 if (rte->subquery)
3736                                 {
3737                                         TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
3738                                                                                                                 attnum);
3739
3740                                         if (ste == NULL || ste->resjunk)
3741                                                 elog(ERROR, "subquery %s does not have attribute %d",
3742                                                          rte->eref->aliasname, attnum);
3743                                         expr = (Node *) ste->expr;
3744                                         if (IsA(expr, Var))
3745                                         {
3746                                                 /*
3747                                                  * Recurse into the sub-select to see what its Var
3748                                                  * refers to. We have to build an additional level of
3749                                                  * namespace to keep in step with varlevelsup in the
3750                                                  * subselect.
3751                                                  */
3752                                                 deparse_namespace mydpns;
3753                                                 const char *result;
3754
3755                                                 mydpns.rtable = rte->subquery->rtable;
3756                                                 mydpns.ctes = rte->subquery->cteList;
3757                                                 mydpns.subplans = NIL;
3758                                                 mydpns.outer_plan = mydpns.inner_plan = NULL;
3759
3760                                                 context->namespaces = lcons(&mydpns,
3761                                                                                                         context->namespaces);
3762
3763                                                 result = get_name_for_var_field((Var *) expr, fieldno,
3764                                                                                                                 0, context);
3765
3766                                                 context->namespaces =
3767                                                         list_delete_first(context->namespaces);
3768
3769                                                 return result;
3770                                         }
3771                                         /* else fall through to inspect the expression */
3772                                 }
3773                                 else
3774                                 {
3775                                         /*
3776                                          * We're deparsing a Plan tree so we don't have complete
3777                                          * RTE entries (in particular, rte->subquery is NULL). But
3778                                          * the only place we'd see a Var directly referencing a
3779                                          * SUBQUERY RTE is in a SubqueryScan plan node, and we can
3780                                          * look into the child plan's tlist instead.
3781                                          */
3782                                         TargetEntry *tle;
3783                                         Plan       *save_outer;
3784                                         Plan       *save_inner;
3785                                         const char *result;
3786
3787                                         if (!dpns->inner_plan)
3788                                                 elog(ERROR, "failed to find plan for subquery %s",
3789                                                          rte->eref->aliasname);
3790                                         tle = get_tle_by_resno(dpns->inner_plan->targetlist,
3791                                                                                    attnum);
3792                                         if (!tle)
3793                                                 elog(ERROR, "bogus varattno for subquery var: %d",
3794                                                          attnum);
3795                                         Assert(netlevelsup == 0);
3796                                         save_outer = dpns->outer_plan;
3797                                         save_inner = dpns->inner_plan;
3798                                         push_plan(dpns, dpns->inner_plan);
3799
3800                                         result = get_name_for_var_field((Var *) tle->expr, fieldno,
3801                                                                                                         levelsup, context);
3802
3803                                         dpns->outer_plan = save_outer;
3804                                         dpns->inner_plan = save_inner;
3805                                         return result;
3806                                 }
3807                         }
3808                         break;
3809                 case RTE_JOIN:
3810                         /* Join RTE --- recursively inspect the alias variable */
3811                         if (rte->joinaliasvars == NIL)
3812                                 elog(ERROR, "cannot decompile join alias var in plan tree");
3813                         Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
3814                         expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
3815                         if (IsA(expr, Var))
3816                                 return get_name_for_var_field((Var *) expr, fieldno,
3817                                                                                           var->varlevelsup + levelsup,
3818                                                                                           context);
3819                         /* else fall through to inspect the expression */
3820                         break;
3821                 case RTE_FUNCTION:
3822
3823                         /*
3824                          * We couldn't get here unless a function is declared with one of
3825                          * its result columns as RECORD, which is not allowed.
3826                          */
3827                         break;
3828                 case RTE_CTE:
3829                         /* CTE reference: examine subquery's output expr */
3830                         {
3831                                 CommonTableExpr *cte = NULL;
3832                                 Index           ctelevelsup;
3833                                 ListCell   *lc;
3834
3835                                 /*
3836                                  * Try to find the referenced CTE using the namespace stack.
3837                                  */
3838                                 ctelevelsup = rte->ctelevelsup + netlevelsup;
3839                                 if (ctelevelsup >= list_length(context->namespaces))
3840                                         lc = NULL;
3841                                 else
3842                                 {
3843                                         deparse_namespace *ctedpns;
3844
3845                                         ctedpns = (deparse_namespace *)
3846                                                 list_nth(context->namespaces, ctelevelsup);
3847                                         foreach(lc, ctedpns->ctes)
3848                                         {
3849                                                 cte = (CommonTableExpr *) lfirst(lc);
3850                                                 if (strcmp(cte->ctename, rte->ctename) == 0)
3851                                                         break;
3852                                         }
3853                                 }
3854                                 if (lc != NULL)
3855                                 {
3856                                         Query      *ctequery = (Query *) cte->ctequery;
3857                                         TargetEntry *ste = get_tle_by_resno(ctequery->targetList,
3858                                                                                                                 attnum);
3859
3860                                         if (ste == NULL || ste->resjunk)
3861                                                 elog(ERROR, "subquery %s does not have attribute %d",
3862                                                          rte->eref->aliasname, attnum);
3863                                         expr = (Node *) ste->expr;
3864                                         if (IsA(expr, Var))
3865                                         {
3866                                                 /*
3867                                                  * Recurse into the CTE to see what its Var refers to.
3868                                                  * We have to build an additional level of namespace
3869                                                  * to keep in step with varlevelsup in the CTE.
3870                                                  * Furthermore it could be an outer CTE, so we may
3871                                                  * have to delete some levels of namespace.
3872                                                  */
3873                                                 List       *save_nslist = context->namespaces;
3874                                                 List       *new_nslist;
3875                                                 deparse_namespace mydpns;
3876                                                 const char *result;
3877
3878                                                 mydpns.rtable = ctequery->rtable;
3879                                                 mydpns.ctes = ctequery->cteList;
3880                                                 mydpns.subplans = NIL;
3881                                                 mydpns.outer_plan = mydpns.inner_plan = NULL;
3882
3883                                                 new_nslist = list_copy_tail(context->namespaces,
3884                                                                                                         ctelevelsup);
3885                                                 context->namespaces = lcons(&mydpns, new_nslist);
3886
3887                                                 result = get_name_for_var_field((Var *) expr, fieldno,
3888                                                                                                                 0, context);
3889
3890                                                 context->namespaces = save_nslist;
3891
3892                                                 return result;
3893                                         }
3894                                         /* else fall through to inspect the expression */
3895                                 }
3896                                 else
3897                                 {
3898                                         /*
3899                                          * We're deparsing a Plan tree so we don't have a CTE
3900                                          * list.  But the only place we'd see a Var directly
3901                                          * referencing a CTE RTE is in a CteScan plan node, and we
3902                                          * can look into the subplan's tlist instead.
3903                                          */
3904                                         TargetEntry *tle;
3905                                         Plan       *save_outer;
3906                                         Plan       *save_inner;
3907                                         const char *result;
3908
3909                                         if (!dpns->inner_plan)
3910                                                 elog(ERROR, "failed to find plan for CTE %s",
3911                                                          rte->eref->aliasname);
3912                                         tle = get_tle_by_resno(dpns->inner_plan->targetlist,
3913                                                                                    attnum);
3914                                         if (!tle)
3915                                                 elog(ERROR, "bogus varattno for subquery var: %d",
3916                                                          attnum);
3917                                         Assert(netlevelsup == 0);
3918                                         save_outer = dpns->outer_plan;
3919                                         save_inner = dpns->inner_plan;
3920                                         push_plan(dpns, dpns->inner_plan);
3921
3922                                         result = get_name_for_var_field((Var *) tle->expr, fieldno,
3923                                                                                                         levelsup, context);
3924
3925                                         dpns->outer_plan = save_outer;
3926                                         dpns->inner_plan = save_inner;
3927                                         return result;
3928                                 }
3929                         }
3930                         break;
3931         }
3932
3933         /*
3934          * We now have an expression we can't expand any more, so see if
3935          * get_expr_result_type() can do anything with it.      If not, pass to
3936          * lookup_rowtype_tupdesc() which will probably fail, but will give an
3937          * appropriate error message while failing.
3938          */
3939         if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
3940                 tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
3941                                                                                                 exprTypmod(expr));
3942         Assert(tupleDesc);
3943         /* Got the tupdesc, so we can extract the field name */
3944         Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
3945         return NameStr(tupleDesc->attrs[fieldno - 1]->attname);
3946 }
3947
3948
3949 /*
3950  * find_rte_by_refname          - look up an RTE by refname in a deparse context
3951  *
3952  * Returns NULL if there is no matching RTE or the refname is ambiguous.
3953  *
3954  * NOTE: this code is not really correct since it does not take account of
3955  * the fact that not all the RTEs in a rangetable may be visible from the
3956  * point where a Var reference appears.  For the purposes we need, however,
3957  * the only consequence of a false match is that we might stick a schema
3958  * qualifier on a Var that doesn't really need it.  So it seems close
3959  * enough.
3960  */
3961 static RangeTblEntry *
3962 find_rte_by_refname(const char *refname, deparse_context *context)
3963 {
3964         RangeTblEntry *result = NULL;
3965         ListCell   *nslist;
3966
3967         foreach(nslist, context->namespaces)
3968         {
3969                 deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
3970                 ListCell   *rtlist;
3971
3972                 foreach(rtlist, dpns->rtable)
3973                 {
3974                         RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtlist);
3975
3976                         if (strcmp(rte->eref->aliasname, refname) == 0)
3977                         {
3978                                 if (result)
3979                                         return NULL;    /* it's ambiguous */
3980                                 result = rte;
3981                         }
3982                 }
3983                 if (result)
3984                         break;
3985         }
3986         return result;
3987 }
3988
3989
3990 /*
3991  * get_simple_binary_op_name
3992  *
3993  * helper function for isSimpleNode
3994  * will return single char binary operator name, or NULL if it's not
3995  */
3996 static const char *
3997 get_simple_binary_op_name(OpExpr *expr)
3998 {
3999         List       *args = expr->args;
4000
4001         if (list_length(args) == 2)
4002         {
4003                 /* binary operator */
4004                 Node       *arg1 = (Node *) linitial(args);
4005                 Node       *arg2 = (Node *) lsecond(args);
4006                 const char *op;
4007
4008                 op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
4009                 if (strlen(op) == 1)
4010                         return op;
4011         }
4012         return NULL;
4013 }
4014
4015
4016 /*
4017  * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
4018  *
4019  *      true   : simple in the context of parent node's type
4020  *      false  : not simple
4021  */
4022 static bool
4023 isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
4024 {
4025         if (!node)
4026                 return false;
4027
4028         switch (nodeTag(node))
4029         {
4030                 case T_Var:
4031                 case T_Const:
4032                 case T_Param:
4033                 case T_CoerceToDomainValue:
4034                 case T_SetToDefault:
4035                 case T_CurrentOfExpr:
4036                         /* single words: always simple */
4037                         return true;
4038
4039                 case T_ArrayRef:
4040                 case T_ArrayExpr:
4041                 case T_RowExpr:
4042                 case T_CoalesceExpr:
4043                 case T_MinMaxExpr:
4044                 case T_XmlExpr:
4045                 case T_NullIfExpr:
4046                 case T_Aggref:
4047                 case T_WindowFunc:
4048                 case T_FuncExpr:
4049                         /* function-like: name(..) or name[..] */
4050                         return true;
4051
4052                         /* CASE keywords act as parentheses */
4053                 case T_CaseExpr:
4054                         return true;
4055
4056                 case T_FieldSelect:
4057
4058                         /*
4059                          * appears simple since . has top precedence, unless parent is
4060                          * T_FieldSelect itself!
4061                          */
4062                         return (IsA(parentNode, FieldSelect) ? false : true);
4063
4064                 case T_FieldStore:
4065
4066                         /*
4067                          * treat like FieldSelect (probably doesn't matter)
4068                          */
4069                         return (IsA(parentNode, FieldStore) ? false : true);
4070
4071                 case T_CoerceToDomain:
4072                         /* maybe simple, check args */
4073                         return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
4074                                                                 node, prettyFlags);
4075                 case T_RelabelType:
4076                         return isSimpleNode((Node *) ((RelabelType *) node)->arg,
4077                                                                 node, prettyFlags);
4078                 case T_CoerceViaIO:
4079                         return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
4080                                                                 node, prettyFlags);
4081                 case T_ArrayCoerceExpr:
4082                         return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
4083                                                                 node, prettyFlags);
4084                 case T_ConvertRowtypeExpr:
4085                         return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
4086                                                                 node, prettyFlags);
4087
4088                 case T_OpExpr:
4089                         {
4090                                 /* depends on parent node type; needs further checking */
4091                                 if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
4092                                 {
4093                                         const char *op;
4094                                         const char *parentOp;
4095                                         bool            is_lopriop;
4096                                         bool            is_hipriop;
4097                                         bool            is_lopriparent;
4098                                         bool            is_hipriparent;
4099
4100                                         op = get_simple_binary_op_name((OpExpr *) node);
4101                                         if (!op)
4102                                                 return false;
4103
4104                                         /* We know only the basic operators + - and * / % */
4105                                         is_lopriop = (strchr("+-", *op) != NULL);
4106                                         is_hipriop = (strchr("*/%", *op) != NULL);
4107                                         if (!(is_lopriop || is_hipriop))
4108                                                 return false;
4109
4110                                         parentOp = get_simple_binary_op_name((OpExpr *) parentNode);
4111                                         if (!parentOp)
4112                                                 return false;
4113
4114                                         is_lopriparent = (strchr("+-", *parentOp) != NULL);
4115                                         is_hipriparent = (strchr("*/%", *parentOp) != NULL);
4116                                         if (!(is_lopriparent || is_hipriparent))
4117                                                 return false;
4118
4119                                         if (is_hipriop && is_lopriparent)
4120                                                 return true;    /* op binds tighter than parent */
4121
4122                                         if (is_lopriop && is_hipriparent)
4123                                                 return false;
4124
4125                                         /*
4126                                          * Operators are same priority --- can skip parens only if
4127                                          * we have (a - b) - c, not a - (b - c).
4128                                          */
4129                                         if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
4130                                                 return true;
4131
4132                                         return false;
4133                                 }
4134                                 /* else do the same stuff as for T_SubLink et al. */
4135                                 /* FALL THROUGH */
4136                         }
4137
4138                 case T_SubLink:
4139                 case T_NullTest:
4140                 case T_BooleanTest:
4141                 case T_DistinctExpr:
4142                         switch (nodeTag(parentNode))
4143                         {
4144                                 case T_FuncExpr:
4145                                         {
4146                                                 /* special handling for casts */
4147                                                 CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
4148
4149                                                 if (type == COERCE_EXPLICIT_CAST ||
4150                                                         type == COERCE_IMPLICIT_CAST)
4151                                                         return false;
4152                                                 return true;    /* own parentheses */
4153                                         }
4154                                 case T_BoolExpr:                /* lower precedence */
4155                                 case T_ArrayRef:                /* other separators */
4156                                 case T_ArrayExpr:               /* other separators */
4157                                 case T_RowExpr: /* other separators */
4158                                 case T_CoalesceExpr:    /* own parentheses */
4159                                 case T_MinMaxExpr:              /* own parentheses */
4160                                 case T_XmlExpr: /* own parentheses */
4161                                 case T_NullIfExpr:              /* other separators */
4162                                 case T_Aggref:  /* own parentheses */
4163                                 case T_WindowFunc:              /* own parentheses */
4164                                 case T_CaseExpr:                /* other separators */
4165                                         return true;
4166                                 default:
4167                                         return false;
4168                         }
4169
4170                 case T_BoolExpr:
4171                         switch (nodeTag(parentNode))
4172                         {
4173                                 case T_BoolExpr:
4174                                         if (prettyFlags & PRETTYFLAG_PAREN)
4175                                         {
4176                                                 BoolExprType type;
4177                                                 BoolExprType parentType;
4178
4179                                                 type = ((BoolExpr *) node)->boolop;
4180                                                 parentType = ((BoolExpr *) parentNode)->boolop;
4181                                                 switch (type)
4182                                                 {
4183                                                         case NOT_EXPR:
4184                                                         case AND_EXPR:
4185                                                                 if (parentType == AND_EXPR || parentType == OR_EXPR)
4186                                                                         return true;
4187                                                                 break;
4188                                                         case OR_EXPR:
4189                                                                 if (parentType == OR_EXPR)
4190                                                                         return true;
4191                                                                 break;
4192                                                 }
4193                                         }
4194                                         return false;
4195                                 case T_FuncExpr:
4196                                         {
4197                                                 /* special handling for casts */
4198                                                 CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
4199
4200                                                 if (type == COERCE_EXPLICIT_CAST ||
4201                                                         type == COERCE_IMPLICIT_CAST)
4202                                                         return false;
4203                                                 return true;    /* own parentheses */
4204                                         }
4205                                 case T_ArrayRef:                /* other separators */
4206                                 case T_ArrayExpr:               /* other separators */
4207                                 case T_RowExpr: /* other separators */
4208                                 case T_CoalesceExpr:    /* own parentheses */
4209                                 case T_MinMaxExpr:              /* own parentheses */
4210                                 case T_XmlExpr: /* own parentheses */
4211                                 case T_NullIfExpr:              /* other separators */
4212                                 case T_Aggref:  /* own parentheses */
4213                                 case T_WindowFunc:              /* own parentheses */
4214                                 case T_CaseExpr:                /* other separators */
4215                                         return true;
4216                                 default:
4217                                         return false;
4218                         }
4219
4220                 default:
4221                         break;
4222         }
4223         /* those we don't know: in dubio complexo */
4224         return false;
4225 }
4226
4227
4228 /*
4229  * appendContextKeyword - append a keyword to buffer
4230  *
4231  * If prettyPrint is enabled, perform a line break, and adjust indentation.
4232  * Otherwise, just append the keyword.
4233  */
4234 static void
4235 appendContextKeyword(deparse_context *context, const char *str,
4236                                          int indentBefore, int indentAfter, int indentPlus)
4237 {
4238         if (PRETTY_INDENT(context))
4239         {
4240                 context->indentLevel += indentBefore;
4241
4242                 appendStringInfoChar(context->buf, '\n');
4243                 appendStringInfoSpaces(context->buf,
4244                                                            Max(context->indentLevel, 0) + indentPlus);
4245                 appendStringInfoString(context->buf, str);
4246
4247                 context->indentLevel += indentAfter;
4248                 if (context->indentLevel < 0)
4249                         context->indentLevel = 0;
4250         }
4251         else
4252                 appendStringInfoString(context->buf, str);
4253 }
4254
4255 /*
4256  * get_rule_expr_paren  - deparse expr using get_rule_expr,
4257  * embracing the string with parentheses if necessary for prettyPrint.
4258  *
4259  * Never embrace if prettyFlags=0, because it's done in the calling node.
4260  *
4261  * Any node that does *not* embrace its argument node by sql syntax (with
4262  * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
4263  * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
4264  * added.
4265  */
4266 static void
4267 get_rule_expr_paren(Node *node, deparse_context *context,
4268                                         bool showimplicit, Node *parentNode)
4269 {
4270         bool            need_paren;
4271
4272         need_paren = PRETTY_PAREN(context) &&
4273                 !isSimpleNode(node, parentNode, context->prettyFlags);
4274
4275         if (need_paren)
4276                 appendStringInfoChar(context->buf, '(');
4277
4278         get_rule_expr(node, context, showimplicit);
4279
4280         if (need_paren)
4281                 appendStringInfoChar(context->buf, ')');
4282 }
4283
4284
4285 /* ----------
4286  * get_rule_expr                        - Parse back an expression
4287  *
4288  * Note: showimplicit determines whether we display any implicit cast that
4289  * is present at the top of the expression tree.  It is a passed argument,
4290  * not a field of the context struct, because we change the value as we
4291  * recurse down into the expression.  In general we suppress implicit casts
4292  * when the result type is known with certainty (eg, the arguments of an
4293  * OR must be boolean).  We display implicit casts for arguments of functions
4294  * and operators, since this is needed to be certain that the same function
4295  * or operator will be chosen when the expression is re-parsed.
4296  * ----------
4297  */
4298 static void
4299 get_rule_expr(Node *node, deparse_context *context,
4300                           bool showimplicit)
4301 {
4302         StringInfo      buf = context->buf;
4303
4304         if (node == NULL)
4305                 return;
4306
4307         /*
4308          * Each level of get_rule_expr must emit an indivisible term
4309          * (parenthesized if necessary) to ensure result is reparsed into the same
4310          * expression tree.  The only exception is that when the input is a List,
4311          * we emit the component items comma-separated with no surrounding
4312          * decoration; this is convenient for most callers.
4313          */
4314         switch (nodeTag(node))
4315         {
4316                 case T_Var:
4317                         (void) get_variable((Var *) node, 0, true, context);
4318                         break;
4319
4320                 case T_Const:
4321                         get_const_expr((Const *) node, context, 0);
4322                         break;
4323
4324                 case T_Param:
4325                         appendStringInfo(buf, "$%d", ((Param *) node)->paramid);
4326                         break;
4327
4328                 case T_Aggref:
4329                         get_agg_expr((Aggref *) node, context);
4330                         break;
4331
4332                 case T_WindowFunc:
4333                         get_windowfunc_expr((WindowFunc *) node, context);
4334                         break;
4335
4336                 case T_ArrayRef:
4337                         {
4338                                 ArrayRef   *aref = (ArrayRef *) node;
4339                                 bool            need_parens;
4340
4341                                 /*
4342                                  * Parenthesize the argument unless it's a simple Var or a
4343                                  * FieldSelect.  (In particular, if it's another ArrayRef, we
4344                                  * *must* parenthesize to avoid confusion.)
4345                                  */
4346                                 need_parens = !IsA(aref->refexpr, Var) &&
4347                                         !IsA(aref->refexpr, FieldSelect);
4348                                 if (need_parens)
4349                                         appendStringInfoChar(buf, '(');
4350                                 get_rule_expr((Node *) aref->refexpr, context, showimplicit);
4351                                 if (need_parens)
4352                                         appendStringInfoChar(buf, ')');
4353                                 printSubscripts(aref, context);
4354
4355                                 /*
4356                                  * Array assignment nodes should have been handled in
4357                                  * processIndirection().
4358                                  */
4359                                 if (aref->refassgnexpr)
4360                                         elog(ERROR, "unexpected refassgnexpr");
4361                         }
4362                         break;
4363
4364                 case T_FuncExpr:
4365                         get_func_expr((FuncExpr *) node, context, showimplicit);
4366                         break;
4367
4368                 case T_NamedArgExpr:
4369                         {
4370                                 NamedArgExpr *na = (NamedArgExpr *) node;
4371
4372                                 get_rule_expr((Node *) na->arg, context, showimplicit);
4373                                 appendStringInfo(buf, " AS %s", quote_identifier(na->name));
4374                         }
4375                         break;
4376
4377                 case T_OpExpr:
4378                         get_oper_expr((OpExpr *) node, context);
4379                         break;
4380
4381                 case T_DistinctExpr:
4382                         {
4383                                 DistinctExpr *expr = (DistinctExpr *) node;
4384                                 List       *args = expr->args;
4385                                 Node       *arg1 = (Node *) linitial(args);
4386                                 Node       *arg2 = (Node *) lsecond(args);
4387
4388                                 if (!PRETTY_PAREN(context))
4389                                         appendStringInfoChar(buf, '(');
4390                                 get_rule_expr_paren(arg1, context, true, node);
4391                                 appendStringInfo(buf, " IS DISTINCT FROM ");
4392                                 get_rule_expr_paren(arg2, context, true, node);
4393                                 if (!PRETTY_PAREN(context))
4394                                         appendStringInfoChar(buf, ')');
4395                         }
4396                         break;
4397
4398                 case T_ScalarArrayOpExpr:
4399                         {
4400                                 ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
4401                                 List       *args = expr->args;
4402                                 Node       *arg1 = (Node *) linitial(args);
4403                                 Node       *arg2 = (Node *) lsecond(args);
4404
4405                                 if (!PRETTY_PAREN(context))
4406                                         appendStringInfoChar(buf, '(');
4407                                 get_rule_expr_paren(arg1, context, true, node);
4408                                 appendStringInfo(buf, " %s %s (",
4409                                                                  generate_operator_name(expr->opno,
4410                                                                                                                 exprType(arg1),
4411                                                                                    get_element_type(exprType(arg2))),
4412                                                                  expr->useOr ? "ANY" : "ALL");
4413                                 get_rule_expr_paren(arg2, context, true, node);
4414                                 appendStringInfoChar(buf, ')');
4415                                 if (!PRETTY_PAREN(context))
4416                                         appendStringInfoChar(buf, ')');
4417                         }
4418                         break;
4419
4420                 case T_BoolExpr:
4421                         {
4422                                 BoolExpr   *expr = (BoolExpr *) node;
4423                                 Node       *first_arg = linitial(expr->args);
4424                                 ListCell   *arg = lnext(list_head(expr->args));
4425
4426                                 switch (expr->boolop)
4427                                 {
4428                                         case AND_EXPR:
4429                                                 if (!PRETTY_PAREN(context))
4430                                                         appendStringInfoChar(buf, '(');
4431                                                 get_rule_expr_paren(first_arg, context,
4432                                                                                         false, node);
4433                                                 while (arg)
4434                                                 {
4435                                                         appendStringInfo(buf, " AND ");
4436                                                         get_rule_expr_paren((Node *) lfirst(arg), context,
4437                                                                                                 false, node);
4438                                                         arg = lnext(arg);
4439                                                 }
4440                                                 if (!PRETTY_PAREN(context))
4441                                                         appendStringInfoChar(buf, ')');
4442                                                 break;
4443
4444                                         case OR_EXPR:
4445                                                 if (!PRETTY_PAREN(context))
4446                                                         appendStringInfoChar(buf, '(');
4447                                                 get_rule_expr_paren(first_arg, context,
4448                                                                                         false, node);
4449                                                 while (arg)
4450                                                 {
4451                                                         appendStringInfo(buf, " OR ");
4452                                                         get_rule_expr_paren((Node *) lfirst(arg), context,
4453                                                                                                 false, node);
4454                                                         arg = lnext(arg);
4455                                                 }
4456                                                 if (!PRETTY_PAREN(context))
4457                                                         appendStringInfoChar(buf, ')');
4458                                                 break;
4459
4460                                         case NOT_EXPR:
4461                                                 if (!PRETTY_PAREN(context))
4462                                                         appendStringInfoChar(buf, '(');
4463                                                 appendStringInfo(buf, "NOT ");
4464                                                 get_rule_expr_paren(first_arg, context,
4465                                                                                         false, node);
4466                                                 if (!PRETTY_PAREN(context))
4467                                                         appendStringInfoChar(buf, ')');
4468                                                 break;
4469
4470                                         default:
4471                                                 elog(ERROR, "unrecognized boolop: %d",
4472                                                          (int) expr->boolop);
4473                                 }
4474                         }
4475                         break;
4476
4477                 case T_SubLink:
4478                         get_sublink_expr((SubLink *) node, context);
4479                         break;
4480
4481                 case T_SubPlan:
4482                         {
4483                                 SubPlan    *subplan = (SubPlan *) node;
4484
4485                                 /*
4486                                  * We cannot see an already-planned subplan in rule deparsing,
4487                                  * only while EXPLAINing a query plan.  We don't try to
4488                                  * reconstruct the original SQL, just reference the subplan
4489                                  * that appears elsewhere in EXPLAIN's result.
4490                                  */
4491                                 if (subplan->useHashTable)
4492                                         appendStringInfo(buf, "(hashed %s)", subplan->plan_name);
4493                                 else
4494                                         appendStringInfo(buf, "(%s)", subplan->plan_name);
4495                         }
4496                         break;
4497
4498                 case T_AlternativeSubPlan:
4499                         {
4500                                 AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
4501                                 ListCell   *lc;
4502
4503                                 /* As above, this can only happen during EXPLAIN */
4504                                 appendStringInfo(buf, "(alternatives: ");
4505                                 foreach(lc, asplan->subplans)
4506                                 {
4507                                         SubPlan    *splan = (SubPlan *) lfirst(lc);
4508
4509                                         Assert(IsA(splan, SubPlan));
4510                                         if (splan->useHashTable)
4511                                                 appendStringInfo(buf, "hashed %s", splan->plan_name);
4512                                         else
4513                                                 appendStringInfo(buf, "%s", splan->plan_name);
4514                                         if (lnext(lc))
4515                                                 appendStringInfo(buf, " or ");
4516                                 }
4517                                 appendStringInfo(buf, ")");
4518                         }
4519                         break;
4520
4521                 case T_FieldSelect:
4522                         {
4523                                 FieldSelect *fselect = (FieldSelect *) node;
4524                                 Node       *arg = (Node *) fselect->arg;
4525                                 int                     fno = fselect->fieldnum;
4526                                 const char *fieldname;
4527                                 bool            need_parens;
4528
4529                                 /*
4530                                  * Parenthesize the argument unless it's an ArrayRef or
4531                                  * another FieldSelect.  Note in particular that it would be
4532                                  * WRONG to not parenthesize a Var argument; simplicity is not
4533                                  * the issue here, having the right number of names is.
4534                                  */
4535                                 need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect);
4536                                 if (need_parens)
4537                                         appendStringInfoChar(buf, '(');
4538                                 get_rule_expr(arg, context, true);
4539                                 if (need_parens)
4540                                         appendStringInfoChar(buf, ')');
4541
4542                                 /*
4543                                  * Get and print the field name.
4544                                  */
4545                                 fieldname = get_name_for_var_field((Var *) arg, fno,
4546                                                                                                    0, context);
4547                                 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
4548                         }
4549                         break;
4550
4551                 case T_FieldStore:
4552
4553                         /*
4554                          * We shouldn't see FieldStore here; it should have been stripped
4555                          * off by processIndirection().
4556                          */
4557                         elog(ERROR, "unexpected FieldStore");
4558                         break;
4559
4560                 case T_RelabelType:
4561                         {
4562                                 RelabelType *relabel = (RelabelType *) node;
4563                                 Node       *arg = (Node *) relabel->arg;
4564
4565                                 if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
4566                                         !showimplicit)
4567                                 {
4568                                         /* don't show the implicit cast */
4569                                         get_rule_expr_paren(arg, context, false, node);
4570                                 }
4571                                 else
4572                                 {
4573                                         get_coercion_expr(arg, context,
4574                                                                           relabel->resulttype,
4575                                                                           relabel->resulttypmod,
4576                                                                           node);
4577                                 }
4578                         }
4579                         break;
4580
4581                 case T_CoerceViaIO:
4582                         {
4583                                 CoerceViaIO *iocoerce = (CoerceViaIO *) node;
4584                                 Node       *arg = (Node *) iocoerce->arg;
4585
4586                                 if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
4587                                         !showimplicit)
4588                                 {
4589                                         /* don't show the implicit cast */
4590                                         get_rule_expr_paren(arg, context, false, node);
4591                                 }
4592                                 else
4593                                 {
4594                                         get_coercion_expr(arg, context,
4595                                                                           iocoerce->resulttype,
4596                                                                           -1,
4597                                                                           node);
4598                                 }
4599                         }
4600                         break;
4601
4602                 case T_ArrayCoerceExpr:
4603                         {
4604                                 ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
4605                                 Node       *arg = (Node *) acoerce->arg;
4606
4607                                 if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
4608                                         !showimplicit)
4609                                 {
4610                                         /* don't show the implicit cast */
4611                                         get_rule_expr_paren(arg, context, false, node);
4612                                 }
4613                                 else
4614                                 {
4615                                         get_coercion_expr(arg, context,
4616                                                                           acoerce->resulttype,
4617                                                                           acoerce->resulttypmod,
4618                                                                           node);
4619                                 }
4620                         }
4621                         break;
4622
4623                 case T_ConvertRowtypeExpr:
4624                         {
4625                                 ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
4626                                 Node       *arg = (Node *) convert->arg;
4627
4628                                 if (convert->convertformat == COERCE_IMPLICIT_CAST &&
4629                                         !showimplicit)
4630                                 {
4631                                         /* don't show the implicit cast */
4632                                         get_rule_expr_paren(arg, context, false, node);
4633                                 }
4634                                 else
4635                                 {
4636                                         get_coercion_expr(arg, context,
4637                                                                           convert->resulttype, -1,
4638                                                                           node);
4639                                 }
4640                         }
4641                         break;
4642
4643                 case T_CaseExpr:
4644                         {
4645                                 CaseExpr   *caseexpr = (CaseExpr *) node;
4646                                 ListCell   *temp;
4647
4648                                 appendContextKeyword(context, "CASE",
4649                                                                          0, PRETTYINDENT_VAR, 0);
4650                                 if (caseexpr->arg)
4651                                 {
4652                                         appendStringInfoChar(buf, ' ');
4653                                         get_rule_expr((Node *) caseexpr->arg, context, true);
4654                                 }
4655                                 foreach(temp, caseexpr->args)
4656                                 {
4657                                         CaseWhen   *when = (CaseWhen *) lfirst(temp);
4658                                         Node       *w = (Node *) when->expr;
4659
4660                                         if (!PRETTY_INDENT(context))
4661                                                 appendStringInfoChar(buf, ' ');
4662                                         appendContextKeyword(context, "WHEN ",
4663                                                                                  0, 0, 0);
4664                                         if (caseexpr->arg)
4665                                         {
4666                                                 /*
4667                                                  * The parser should have produced WHEN clauses of the
4668                                                  * form "CaseTestExpr = RHS"; we want to show just the
4669                                                  * RHS.  If the user wrote something silly like "CASE
4670                                                  * boolexpr WHEN TRUE THEN ...", then the optimizer's
4671                                                  * simplify_boolean_equality() may have reduced this
4672                                                  * to just "CaseTestExpr" or "NOT CaseTestExpr", for
4673                                                  * which we have to show "TRUE" or "FALSE".  Also,
4674                                                  * depending on context the original CaseTestExpr
4675                                                  * might have been reduced to a Const (but we won't
4676                                                  * see "WHEN Const").  We have also to consider the
4677                                                  * possibility that an implicit coercion was inserted
4678                                                  * between the CaseTestExpr and the operator.
4679                                                  */
4680                                                 if (IsA(w, OpExpr))
4681                                                 {
4682                                                         List       *args = ((OpExpr *) w)->args;
4683                                                         Node       *lhs;
4684                                                         Node       *rhs;
4685
4686                                                         Assert(list_length(args) == 2);
4687                                                         lhs = strip_implicit_coercions(linitial(args));
4688                                                         Assert(IsA(lhs, CaseTestExpr) ||
4689                                                                    IsA(lhs, Const));
4690                                                         rhs = (Node *) lsecond(args);
4691                                                         get_rule_expr(rhs, context, false);
4692                                                 }
4693                                                 else if (IsA(strip_implicit_coercions(w),
4694                                                                          CaseTestExpr))
4695                                                         appendStringInfo(buf, "TRUE");
4696                                                 else if (not_clause(w))
4697                                                 {
4698                                                         Assert(IsA(strip_implicit_coercions((Node *) get_notclausearg((Expr *) w)),
4699                                                                            CaseTestExpr));
4700                                                         appendStringInfo(buf, "FALSE");
4701                                                 }
4702                                                 else
4703                                                         elog(ERROR, "unexpected CASE WHEN clause: %d",
4704                                                                  (int) nodeTag(w));
4705                                         }
4706                                         else
4707                                                 get_rule_expr(w, context, false);
4708                                         appendStringInfo(buf, " THEN ");
4709                                         get_rule_expr((Node *) when->result, context, true);
4710                                 }
4711                                 if (!PRETTY_INDENT(context))
4712                                         appendStringInfoChar(buf, ' ');
4713                                 appendContextKeyword(context, "ELSE ",
4714                                                                          0, 0, 0);
4715                                 get_rule_expr((Node *) caseexpr->defresult, context, true);
4716                                 if (!PRETTY_INDENT(context))
4717                                         appendStringInfoChar(buf, ' ');
4718                                 appendContextKeyword(context, "END",
4719                                                                          -PRETTYINDENT_VAR, 0, 0);
4720                         }
4721                         break;
4722
4723                 case T_ArrayExpr:
4724                         {
4725                                 ArrayExpr  *arrayexpr = (ArrayExpr *) node;
4726
4727                                 appendStringInfo(buf, "ARRAY[");
4728                                 get_rule_expr((Node *) arrayexpr->elements, context, true);
4729                                 appendStringInfoChar(buf, ']');
4730
4731                                 /*
4732                                  * If the array isn't empty, we assume its elements are
4733                                  * coerced to the desired type.  If it's empty, though, we
4734                                  * need an explicit coercion to the array type.
4735                                  */
4736                                 if (arrayexpr->elements == NIL)
4737                                         appendStringInfo(buf, "::%s",
4738                                           format_type_with_typemod(arrayexpr->array_typeid, -1));
4739                         }
4740                         break;
4741
4742                 case T_RowExpr:
4743                         {
4744                                 RowExpr    *rowexpr = (RowExpr *) node;
4745                                 TupleDesc       tupdesc = NULL;
4746                                 ListCell   *arg;
4747                                 int                     i;
4748                                 char       *sep;
4749
4750                                 /*
4751                                  * If it's a named type and not RECORD, we may have to skip
4752                                  * dropped columns and/or claim there are NULLs for added
4753                                  * columns.
4754                                  */
4755                                 if (rowexpr->row_typeid != RECORDOID)
4756                                 {
4757                                         tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
4758                                         Assert(list_length(rowexpr->args) <= tupdesc->natts);
4759                                 }
4760
4761                                 /*
4762                                  * SQL99 allows "ROW" to be omitted when there is more than
4763                                  * one column, but for simplicity we always print it.
4764                                  */
4765                                 appendStringInfo(buf, "ROW(");
4766                                 sep = "";
4767                                 i = 0;
4768                                 foreach(arg, rowexpr->args)
4769                                 {
4770                                         Node       *e = (Node *) lfirst(arg);
4771
4772                                         if (tupdesc == NULL ||
4773                                                 !tupdesc->attrs[i]->attisdropped)
4774                                         {
4775                                                 appendStringInfoString(buf, sep);
4776                                                 get_rule_expr(e, context, true);
4777                                                 sep = ", ";
4778                                         }
4779                                         i++;
4780                                 }
4781                                 if (tupdesc != NULL)
4782                                 {
4783                                         while (i < tupdesc->natts)
4784                                         {
4785                                                 if (!tupdesc->attrs[i]->attisdropped)
4786                                                 {
4787                                                         appendStringInfoString(buf, sep);
4788                                                         appendStringInfo(buf, "NULL");
4789                                                         sep = ", ";
4790                                                 }
4791                                                 i++;
4792                                         }
4793
4794                                         ReleaseTupleDesc(tupdesc);
4795                                 }
4796                                 appendStringInfo(buf, ")");
4797                                 if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
4798                                         appendStringInfo(buf, "::%s",
4799                                                   format_type_with_typemod(rowexpr->row_typeid, -1));
4800                         }
4801                         break;
4802
4803                 case T_RowCompareExpr:
4804                         {
4805                                 RowCompareExpr *rcexpr = (RowCompareExpr *) node;
4806                                 ListCell   *arg;
4807                                 char       *sep;
4808
4809                                 /*
4810                                  * SQL99 allows "ROW" to be omitted when there is more than
4811                                  * one column, but for simplicity we always print it.
4812                                  */
4813                                 appendStringInfo(buf, "(ROW(");
4814                                 sep = "";
4815                                 foreach(arg, rcexpr->largs)
4816                                 {
4817                                         Node       *e = (Node *) lfirst(arg);
4818
4819                                         appendStringInfoString(buf, sep);
4820                                         get_rule_expr(e, context, true);
4821                                         sep = ", ";
4822                                 }
4823
4824                                 /*
4825                                  * We assume that the name of the first-column operator will
4826                                  * do for all the rest too.  This is definitely open to
4827                                  * failure, eg if some but not all operators were renamed
4828                                  * since the construct was parsed, but there seems no way to
4829                                  * be perfect.
4830                                  */
4831                                 appendStringInfo(buf, ") %s ROW(",
4832                                                   generate_operator_name(linitial_oid(rcexpr->opnos),
4833                                                                                    exprType(linitial(rcexpr->largs)),
4834                                                                                  exprType(linitial(rcexpr->rargs))));
4835                                 sep = "";
4836                                 foreach(arg, rcexpr->rargs)
4837                                 {
4838                                         Node       *e = (Node *) lfirst(arg);
4839
4840                                         appendStringInfoString(buf, sep);
4841                                         get_rule_expr(e, context, true);
4842                                         sep = ", ";
4843                                 }
4844                                 appendStringInfo(buf, "))");
4845                         }
4846                         break;
4847
4848                 case T_CoalesceExpr:
4849                         {
4850                                 CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
4851
4852                                 appendStringInfo(buf, "COALESCE(");
4853                                 get_rule_expr((Node *) coalesceexpr->args, context, true);
4854                                 appendStringInfoChar(buf, ')');
4855                         }
4856                         break;
4857
4858                 case T_MinMaxExpr:
4859                         {
4860                                 MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
4861
4862                                 switch (minmaxexpr->op)
4863                                 {
4864                                         case IS_GREATEST:
4865                                                 appendStringInfo(buf, "GREATEST(");
4866                                                 break;
4867                                         case IS_LEAST:
4868                                                 appendStringInfo(buf, "LEAST(");
4869                                                 break;
4870                                 }
4871                                 get_rule_expr((Node *) minmaxexpr->args, context, true);
4872                                 appendStringInfoChar(buf, ')');
4873                         }
4874                         break;
4875
4876                 case T_XmlExpr:
4877                         {
4878                                 XmlExpr    *xexpr = (XmlExpr *) node;
4879                                 bool            needcomma = false;
4880                                 ListCell   *arg;
4881                                 ListCell   *narg;
4882                                 Const      *con;
4883
4884                                 switch (xexpr->op)
4885                                 {
4886                                         case IS_XMLCONCAT:
4887                                                 appendStringInfoString(buf, "XMLCONCAT(");
4888                                                 break;
4889                                         case IS_XMLELEMENT:
4890                                                 appendStringInfoString(buf, "XMLELEMENT(");
4891                                                 break;
4892                                         case IS_XMLFOREST:
4893                                                 appendStringInfoString(buf, "XMLFOREST(");
4894                                                 break;
4895                                         case IS_XMLPARSE:
4896                                                 appendStringInfoString(buf, "XMLPARSE(");
4897                                                 break;
4898                                         case IS_XMLPI:
4899                                                 appendStringInfoString(buf, "XMLPI(");
4900                                                 break;
4901                                         case IS_XMLROOT:
4902                                                 appendStringInfoString(buf, "XMLROOT(");
4903                                                 break;
4904                                         case IS_XMLSERIALIZE:
4905                                                 appendStringInfoString(buf, "XMLSERIALIZE(");
4906                                                 break;
4907                                         case IS_DOCUMENT:
4908                                                 break;
4909                                 }
4910                                 if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
4911                                 {
4912                                         if (xexpr->xmloption == XMLOPTION_DOCUMENT)
4913                                                 appendStringInfoString(buf, "DOCUMENT ");
4914                                         else
4915                                                 appendStringInfoString(buf, "CONTENT ");
4916                                 }
4917                                 if (xexpr->name)
4918                                 {
4919                                         appendStringInfo(buf, "NAME %s",
4920                                                                          quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));
4921                                         needcomma = true;
4922                                 }
4923                                 if (xexpr->named_args)
4924                                 {
4925                                         if (xexpr->op != IS_XMLFOREST)
4926                                         {
4927                                                 if (needcomma)
4928                                                         appendStringInfoString(buf, ", ");
4929                                                 appendStringInfoString(buf, "XMLATTRIBUTES(");
4930                                                 needcomma = false;
4931                                         }
4932                                         forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
4933                                         {
4934                                                 Node       *e = (Node *) lfirst(arg);
4935                                                 char       *argname = strVal(lfirst(narg));
4936
4937                                                 if (needcomma)
4938                                                         appendStringInfoString(buf, ", ");
4939                                                 get_rule_expr((Node *) e, context, true);
4940                                                 appendStringInfo(buf, " AS %s",
4941                                                                                  quote_identifier(map_xml_name_to_sql_identifier(argname)));
4942                                                 needcomma = true;
4943                                         }
4944                                         if (xexpr->op != IS_XMLFOREST)
4945                                                 appendStringInfoChar(buf, ')');
4946                                 }
4947                                 if (xexpr->args)
4948                                 {
4949                                         if (needcomma)
4950                                                 appendStringInfoString(buf, ", ");
4951                                         switch (xexpr->op)
4952                                         {
4953                                                 case IS_XMLCONCAT:
4954                                                 case IS_XMLELEMENT:
4955                                                 case IS_XMLFOREST:
4956                                                 case IS_XMLPI:
4957                                                 case IS_XMLSERIALIZE:
4958                                                         /* no extra decoration needed */
4959                                                         get_rule_expr((Node *) xexpr->args, context, true);
4960                                                         break;
4961                                                 case IS_XMLPARSE:
4962                                                         Assert(list_length(xexpr->args) == 2);
4963
4964                                                         get_rule_expr((Node *) linitial(xexpr->args),
4965                                                                                   context, true);
4966
4967                                                         con = (Const *) lsecond(xexpr->args);
4968                                                         Assert(IsA(con, Const));
4969                                                         Assert(!con->constisnull);
4970                                                         if (DatumGetBool(con->constvalue))
4971                                                                 appendStringInfoString(buf,
4972                                                                                                          " PRESERVE WHITESPACE");
4973                                                         else
4974                                                                 appendStringInfoString(buf,
4975                                                                                                            " STRIP WHITESPACE");
4976                                                         break;
4977                                                 case IS_XMLROOT:
4978                                                         Assert(list_length(xexpr->args) == 3);
4979
4980                                                         get_rule_expr((Node *) linitial(xexpr->args),
4981                                                                                   context, true);
4982
4983                                                         appendStringInfoString(buf, ", VERSION ");
4984                                                         con = (Const *) lsecond(xexpr->args);
4985                                                         if (IsA(con, Const) &&
4986                                                                 con->constisnull)
4987                                                                 appendStringInfoString(buf, "NO VALUE");
4988                                                         else
4989                                                                 get_rule_expr((Node *) con, context, false);
4990
4991                                                         con = (Const *) lthird(xexpr->args);
4992                                                         Assert(IsA(con, Const));
4993                                                         if (con->constisnull)
4994                                                                  /* suppress STANDALONE NO VALUE */ ;
4995                                                         else
4996                                                         {
4997                                                                 switch (DatumGetInt32(con->constvalue))
4998                                                                 {
4999                                                                         case XML_STANDALONE_YES:
5000                                                                                 appendStringInfoString(buf,
5001                                                                                                                  ", STANDALONE YES");
5002                                                                                 break;
5003                                                                         case XML_STANDALONE_NO:
5004                                                                                 appendStringInfoString(buf,
5005                                                                                                                   ", STANDALONE NO");
5006                                                                                 break;
5007                                                                         case XML_STANDALONE_NO_VALUE:
5008                                                                                 appendStringInfoString(buf,
5009                                                                                                         ", STANDALONE NO VALUE");
5010                                                                                 break;
5011                                                                         default:
5012                                                                                 break;
5013                                                                 }
5014                                                         }
5015                                                         break;
5016                                                 case IS_DOCUMENT:
5017                                                         get_rule_expr_paren((Node *) xexpr->args, context, false, node);
5018                                                         break;
5019                                         }
5020
5021                                 }
5022                                 if (xexpr->op == IS_XMLSERIALIZE)
5023                                         appendStringInfo(buf, " AS %s", format_type_with_typemod(xexpr->type,
5024                                                                                                                          xexpr->typmod));
5025                                 if (xexpr->op == IS_DOCUMENT)
5026                                         appendStringInfoString(buf, " IS DOCUMENT");
5027                                 else
5028                                         appendStringInfoChar(buf, ')');
5029                         }
5030                         break;
5031
5032                 case T_NullIfExpr:
5033                         {
5034                                 NullIfExpr *nullifexpr = (NullIfExpr *) node;
5035
5036                                 appendStringInfo(buf, "NULLIF(");
5037                                 get_rule_expr((Node *) nullifexpr->args, context, true);
5038                                 appendStringInfoChar(buf, ')');
5039                         }
5040                         break;
5041
5042                 case T_NullTest:
5043                         {
5044                                 NullTest   *ntest = (NullTest *) node;
5045
5046                                 if (!PRETTY_PAREN(context))
5047                                         appendStringInfoChar(buf, '(');
5048                                 get_rule_expr_paren((Node *) ntest->arg, context, true, node);
5049                                 switch (ntest->nulltesttype)
5050                                 {
5051                                         case IS_NULL:
5052                                                 appendStringInfo(buf, " IS NULL");
5053                                                 break;
5054                                         case IS_NOT_NULL:
5055                                                 appendStringInfo(buf, " IS NOT NULL");
5056                                                 break;
5057                                         default:
5058                                                 elog(ERROR, "unrecognized nulltesttype: %d",
5059                                                          (int) ntest->nulltesttype);
5060                                 }
5061                                 if (!PRETTY_PAREN(context))
5062                                         appendStringInfoChar(buf, ')');
5063                         }
5064                         break;
5065
5066                 case T_BooleanTest:
5067                         {
5068                                 BooleanTest *btest = (BooleanTest *) node;
5069
5070                                 if (!PRETTY_PAREN(context))
5071                                         appendStringInfoChar(buf, '(');
5072                                 get_rule_expr_paren((Node *) btest->arg, context, false, node);
5073                                 switch (btest->booltesttype)
5074                                 {
5075                                         case IS_TRUE:
5076                                                 appendStringInfo(buf, " IS TRUE");
5077                                                 break;
5078                                         case IS_NOT_TRUE:
5079                                                 appendStringInfo(buf, " IS NOT TRUE");
5080                                                 break;
5081                                         case IS_FALSE:
5082                                                 appendStringInfo(buf, " IS FALSE");
5083                                                 break;
5084                                         case IS_NOT_FALSE:
5085                                                 appendStringInfo(buf, " IS NOT FALSE");
5086                                                 break;
5087                                         case IS_UNKNOWN:
5088                                                 appendStringInfo(buf, " IS UNKNOWN");
5089                                                 break;
5090                                         case IS_NOT_UNKNOWN:
5091                                                 appendStringInfo(buf, " IS NOT UNKNOWN");
5092                                                 break;
5093                                         default:
5094                                                 elog(ERROR, "unrecognized booltesttype: %d",
5095                                                          (int) btest->booltesttype);
5096                                 }
5097                                 if (!PRETTY_PAREN(context))
5098                                         appendStringInfoChar(buf, ')');
5099                         }
5100                         break;
5101
5102                 case T_CoerceToDomain:
5103                         {
5104                                 CoerceToDomain *ctest = (CoerceToDomain *) node;
5105                                 Node       *arg = (Node *) ctest->arg;
5106
5107                                 if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
5108                                         !showimplicit)
5109                                 {
5110                                         /* don't show the implicit cast */
5111                                         get_rule_expr(arg, context, false);
5112                                 }
5113                                 else
5114                                 {
5115                                         get_coercion_expr(arg, context,
5116                                                                           ctest->resulttype,
5117                                                                           ctest->resulttypmod,
5118                                                                           node);
5119                                 }
5120                         }
5121                         break;
5122
5123                 case T_CoerceToDomainValue:
5124                         appendStringInfo(buf, "VALUE");
5125                         break;
5126
5127                 case T_SetToDefault:
5128                         appendStringInfo(buf, "DEFAULT");
5129                         break;
5130
5131                 case T_CurrentOfExpr:
5132                         {
5133                                 CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
5134
5135                                 if (cexpr->cursor_name)
5136                                         appendStringInfo(buf, "CURRENT OF %s",
5137                                                                          quote_identifier(cexpr->cursor_name));
5138                                 else
5139                                         appendStringInfo(buf, "CURRENT OF $%d",
5140                                                                          cexpr->cursor_param);
5141                         }
5142                         break;
5143
5144                 case T_List:
5145                         {
5146                                 char       *sep;
5147                                 ListCell   *l;
5148
5149                                 sep = "";
5150                                 foreach(l, (List *) node)
5151                                 {
5152                                         appendStringInfoString(buf, sep);
5153                                         get_rule_expr((Node *) lfirst(l), context, showimplicit);
5154                                         sep = ", ";
5155                                 }
5156                         }
5157                         break;
5158
5159                 default:
5160                         elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
5161                         break;
5162         }
5163 }
5164
5165
5166 /*
5167  * get_oper_expr                        - Parse back an OpExpr node
5168  */
5169 static void
5170 get_oper_expr(OpExpr *expr, deparse_context *context)
5171 {
5172         StringInfo      buf = context->buf;
5173         Oid                     opno = expr->opno;
5174         List       *args = expr->args;
5175
5176         if (!PRETTY_PAREN(context))
5177                 appendStringInfoChar(buf, '(');
5178         if (list_length(args) == 2)
5179         {
5180                 /* binary operator */
5181                 Node       *arg1 = (Node *) linitial(args);
5182                 Node       *arg2 = (Node *) lsecond(args);
5183
5184                 get_rule_expr_paren(arg1, context, true, (Node *) expr);
5185                 appendStringInfo(buf, " %s ",
5186                                                  generate_operator_name(opno,
5187                                                                                                 exprType(arg1),
5188                                                                                                 exprType(arg2)));
5189                 get_rule_expr_paren(arg2, context, true, (Node *) expr);
5190         }
5191         else
5192         {
5193                 /* unary operator --- but which side? */
5194                 Node       *arg = (Node *) linitial(args);
5195                 HeapTuple       tp;
5196                 Form_pg_operator optup;
5197
5198                 tp = SearchSysCache(OPEROID,
5199                                                         ObjectIdGetDatum(opno),
5200                                                         0, 0, 0);
5201                 if (!HeapTupleIsValid(tp))
5202                         elog(ERROR, "cache lookup failed for operator %u", opno);
5203                 optup = (Form_pg_operator) GETSTRUCT(tp);
5204                 switch (optup->oprkind)
5205                 {
5206                         case 'l':
5207                                 appendStringInfo(buf, "%s ",
5208                                                                  generate_operator_name(opno,
5209                                                                                                                 InvalidOid,
5210                                                                                                                 exprType(arg)));
5211                                 get_rule_expr_paren(arg, context, true, (Node *) expr);
5212                                 break;
5213                         case 'r':
5214                                 get_rule_expr_paren(arg, context, true, (Node *) expr);
5215                                 appendStringInfo(buf, " %s",
5216                                                                  generate_operator_name(opno,
5217                                                                                                                 exprType(arg),
5218                                                                                                                 InvalidOid));
5219                                 break;
5220                         default:
5221                                 elog(ERROR, "bogus oprkind: %d", optup->oprkind);
5222                 }
5223                 ReleaseSysCache(tp);
5224         }
5225         if (!PRETTY_PAREN(context))
5226                 appendStringInfoChar(buf, ')');
5227 }
5228
5229 /*
5230  * get_func_expr                        - Parse back a FuncExpr node
5231  */
5232 static void
5233 get_func_expr(FuncExpr *expr, deparse_context *context,
5234                           bool showimplicit)
5235 {
5236         StringInfo      buf = context->buf;
5237         Oid                     funcoid = expr->funcid;
5238         Oid                     argtypes[FUNC_MAX_ARGS];
5239         int                     nargs;
5240         List       *argnames;
5241         bool            is_variadic;
5242         ListCell   *l;
5243
5244         /*
5245          * If the function call came from an implicit coercion, then just show the
5246          * first argument --- unless caller wants to see implicit coercions.
5247          */
5248         if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
5249         {
5250                 get_rule_expr_paren((Node *) linitial(expr->args), context,
5251                                                         false, (Node *) expr);
5252                 return;
5253         }
5254
5255         /*
5256          * If the function call came from a cast, then show the first argument
5257          * plus an explicit cast operation.
5258          */
5259         if (expr->funcformat == COERCE_EXPLICIT_CAST ||
5260                 expr->funcformat == COERCE_IMPLICIT_CAST)
5261         {
5262                 Node       *arg = linitial(expr->args);
5263                 Oid                     rettype = expr->funcresulttype;
5264                 int32           coercedTypmod;
5265
5266                 /* Get the typmod if this is a length-coercion function */
5267                 (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
5268
5269                 get_coercion_expr(arg, context,
5270                                                   rettype, coercedTypmod,
5271                                                   (Node *) expr);
5272
5273                 return;
5274         }
5275
5276         /*
5277          * Normal function: display as proname(args).  First we need to extract
5278          * the argument datatypes.
5279          */
5280         if (list_length(expr->args) > FUNC_MAX_ARGS)
5281                 ereport(ERROR,
5282                                 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
5283                                  errmsg("too many arguments")));
5284         nargs = 0;
5285         argnames = NIL;
5286         foreach(l, expr->args)
5287         {
5288                 Node   *arg = (Node *) lfirst(l);
5289
5290                 if (IsA(arg, NamedArgExpr))
5291                         argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
5292                 argtypes[nargs] = exprType(arg);
5293                 nargs++;
5294         }
5295
5296         appendStringInfo(buf, "%s(",
5297                                          generate_function_name(funcoid, nargs,
5298                                                                                         argnames, argtypes,
5299                                                                                         &is_variadic));
5300         nargs = 0;
5301         foreach(l, expr->args)
5302         {
5303                 if (nargs++ > 0)
5304                         appendStringInfoString(buf, ", ");
5305                 if (is_variadic && lnext(l) == NULL)
5306                         appendStringInfoString(buf, "VARIADIC ");
5307                 get_rule_expr((Node *) lfirst(l), context, true);
5308         }
5309         appendStringInfoChar(buf, ')');
5310 }
5311
5312 /*
5313  * get_agg_expr                 - Parse back an Aggref node
5314  */
5315 static void
5316 get_agg_expr(Aggref *aggref, deparse_context *context)
5317 {
5318         StringInfo      buf = context->buf;
5319         Oid                     argtypes[FUNC_MAX_ARGS];
5320         int                     nargs;
5321         ListCell   *l;
5322
5323         if (list_length(aggref->args) > FUNC_MAX_ARGS)
5324                 ereport(ERROR,
5325                                 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
5326                                  errmsg("too many arguments")));
5327         nargs = 0;
5328         foreach(l, aggref->args)
5329         {
5330                 Node   *arg = (Node *) lfirst(l);
5331
5332                 Assert(!IsA(arg, NamedArgExpr));
5333                 argtypes[nargs] = exprType(arg);
5334                 nargs++;
5335         }
5336
5337         appendStringInfo(buf, "%s(%s",
5338                                          generate_function_name(aggref->aggfnoid, nargs,
5339                                                                                         NIL, argtypes, NULL),
5340                                          aggref->aggdistinct ? "DISTINCT " : "");
5341         /* aggstar can be set only in zero-argument aggregates */
5342         if (aggref->aggstar)
5343                 appendStringInfoChar(buf, '*');
5344         else
5345                 get_rule_expr((Node *) aggref->args, context, true);
5346         appendStringInfoChar(buf, ')');
5347 }
5348
5349 /*
5350  * get_windowfunc_expr  - Parse back a WindowFunc node
5351  */
5352 static void
5353 get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
5354 {
5355         StringInfo      buf = context->buf;
5356         Oid                     argtypes[FUNC_MAX_ARGS];
5357         int                     nargs;
5358         ListCell   *l;
5359
5360         if (list_length(wfunc->args) > FUNC_MAX_ARGS)
5361                 ereport(ERROR,
5362                                 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
5363                                  errmsg("too many arguments")));
5364         nargs = 0;
5365         foreach(l, wfunc->args)
5366         {
5367                 Node   *arg = (Node *) lfirst(l);
5368
5369                 Assert(!IsA(arg, NamedArgExpr));
5370                 argtypes[nargs] = exprType(arg);
5371                 nargs++;
5372         }
5373
5374         appendStringInfo(buf, "%s(",
5375                                          generate_function_name(wfunc->winfnoid, nargs,
5376                                                                                         NIL, argtypes, NULL));
5377         /* winstar can be set only in zero-argument aggregates */
5378         if (wfunc->winstar)
5379                 appendStringInfoChar(buf, '*');
5380         else
5381                 get_rule_expr((Node *) wfunc->args, context, true);
5382         appendStringInfoString(buf, ") OVER ");
5383
5384         foreach(l, context->windowClause)
5385         {
5386                 WindowClause *wc = (WindowClause *) lfirst(l);
5387
5388                 if (wc->winref == wfunc->winref)
5389                 {
5390                         if (wc->name)
5391                                 appendStringInfoString(buf, quote_identifier(wc->name));
5392                         else
5393                                 get_rule_windowspec(wc, context->windowTList, context);
5394                         break;
5395                 }
5396         }
5397         if (l == NULL)
5398         {
5399                 if (context->windowClause)
5400                         elog(ERROR, "could not find window clause for winref %u",
5401                                  wfunc->winref);
5402
5403                 /*
5404                  * In EXPLAIN, we don't have window context information available, so
5405                  * we have to settle for this:
5406                  */
5407                 appendStringInfoString(buf, "(?)");
5408         }
5409 }
5410
5411 /* ----------
5412  * get_coercion_expr
5413  *
5414  *      Make a string representation of a value coerced to a specific type
5415  * ----------
5416  */
5417 static void
5418 get_coercion_expr(Node *arg, deparse_context *context,
5419                                   Oid resulttype, int32 resulttypmod,
5420                                   Node *parentNode)
5421 {
5422         StringInfo      buf = context->buf;
5423
5424         /*
5425          * Since parse_coerce.c doesn't immediately collapse application of
5426          * length-coercion functions to constants, what we'll typically see in
5427          * such cases is a Const with typmod -1 and a length-coercion function
5428          * right above it.      Avoid generating redundant output. However, beware of
5429          * suppressing casts when the user actually wrote something like
5430          * 'foo'::text::char(3).
5431          */
5432         if (arg && IsA(arg, Const) &&
5433                 ((Const *) arg)->consttype == resulttype &&
5434                 ((Const *) arg)->consttypmod == -1)
5435         {
5436                 /* Show the constant without normal ::typename decoration */
5437                 get_const_expr((Const *) arg, context, -1);
5438         }
5439         else
5440         {
5441                 if (!PRETTY_PAREN(context))
5442                         appendStringInfoChar(buf, '(');
5443                 get_rule_expr_paren(arg, context, false, parentNode);
5444                 if (!PRETTY_PAREN(context))
5445                         appendStringInfoChar(buf, ')');
5446         }
5447         appendStringInfo(buf, "::%s",
5448                                          format_type_with_typemod(resulttype, resulttypmod));
5449 }
5450
5451 /* ----------
5452  * get_const_expr
5453  *
5454  *      Make a string representation of a Const
5455  *
5456  * showtype can be -1 to never show "::typename" decoration, or +1 to always
5457  * show it, or 0 to show it only if the constant wouldn't be assumed to be
5458  * the right type by default.
5459  * ----------
5460  */
5461 static void
5462 get_const_expr(Const *constval, deparse_context *context, int showtype)
5463 {
5464         StringInfo      buf = context->buf;
5465         Oid                     typoutput;
5466         bool            typIsVarlena;
5467         char       *extval;
5468         bool            isfloat = false;
5469         bool            needlabel;
5470
5471         if (constval->constisnull)
5472         {
5473                 /*
5474                  * Always label the type of a NULL constant to prevent misdecisions
5475                  * about type when reparsing.
5476                  */
5477                 appendStringInfo(buf, "NULL");
5478                 if (showtype >= 0)
5479                         appendStringInfo(buf, "::%s",
5480                                                          format_type_with_typemod(constval->consttype,
5481                                                                                                           constval->consttypmod));
5482                 return;
5483         }
5484
5485         getTypeOutputInfo(constval->consttype,
5486                                           &typoutput, &typIsVarlena);
5487
5488         extval = OidOutputFunctionCall(typoutput, constval->constvalue);
5489
5490         switch (constval->consttype)
5491         {
5492                 case INT2OID:
5493                 case INT4OID:
5494                 case INT8OID:
5495                 case OIDOID:
5496                 case FLOAT4OID:
5497                 case FLOAT8OID:
5498                 case NUMERICOID:
5499                         {
5500                                 /*
5501                                  * These types are printed without quotes unless they contain
5502                                  * values that aren't accepted by the scanner unquoted (e.g.,
5503                                  * 'NaN').      Note that strtod() and friends might accept NaN,
5504                                  * so we can't use that to test.
5505                                  *
5506                                  * In reality we only need to defend against infinity and NaN,
5507                                  * so we need not get too crazy about pattern matching here.
5508                                  *
5509                                  * There is a special-case gotcha: if the constant is signed,
5510                                  * we need to parenthesize it, else the parser might see a
5511                                  * leading plus/minus as binding less tightly than adjacent
5512                                  * operators --- particularly, the cast that we might attach
5513                                  * below.
5514                                  */
5515                                 if (strspn(extval, "0123456789+-eE.") == strlen(extval))
5516                                 {
5517                                         if (extval[0] == '+' || extval[0] == '-')
5518                                                 appendStringInfo(buf, "(%s)", extval);
5519                                         else
5520                                                 appendStringInfoString(buf, extval);
5521                                         if (strcspn(extval, "eE.") != strlen(extval))
5522                                                 isfloat = true; /* it looks like a float */
5523                                 }
5524                                 else
5525                                         appendStringInfo(buf, "'%s'", extval);
5526                         }
5527                         break;
5528
5529                 case BITOID:
5530                 case VARBITOID:
5531                         appendStringInfo(buf, "B'%s'", extval);
5532                         break;
5533
5534                 case BOOLOID:
5535                         if (strcmp(extval, "t") == 0)
5536                                 appendStringInfo(buf, "true");
5537                         else
5538                                 appendStringInfo(buf, "false");
5539                         break;
5540
5541                 default:
5542                         simple_quote_literal(buf, extval);
5543                         break;
5544         }
5545
5546         pfree(extval);
5547
5548         if (showtype < 0)
5549                 return;
5550
5551         /*
5552          * For showtype == 0, append ::typename unless the constant will be
5553          * implicitly typed as the right type when it is read in.
5554          *
5555          * XXX this code has to be kept in sync with the behavior of the parser,
5556          * especially make_const.
5557          */
5558         switch (constval->consttype)
5559         {
5560                 case BOOLOID:
5561                 case INT4OID:
5562                 case UNKNOWNOID:
5563                         /* These types can be left unlabeled */
5564                         needlabel = false;
5565                         break;
5566                 case NUMERICOID:
5567
5568                         /*
5569                          * Float-looking constants will be typed as numeric, but if
5570                          * there's a specific typmod we need to show it.
5571                          */
5572                         needlabel = !isfloat || (constval->consttypmod >= 0);
5573                         break;
5574                 default:
5575                         needlabel = true;
5576                         break;
5577         }
5578         if (needlabel || showtype > 0)
5579                 appendStringInfo(buf, "::%s",
5580                                                  format_type_with_typemod(constval->consttype,
5581                                                                                                   constval->consttypmod));
5582 }
5583
5584 /*
5585  * simple_quote_literal - Format a string as a SQL literal, append to buf
5586  */
5587 static void
5588 simple_quote_literal(StringInfo buf, const char *val)
5589 {
5590         const char *valptr;
5591
5592         /*
5593          * We form the string literal according to the prevailing setting of
5594          * standard_conforming_strings; we never use E''. User is responsible for
5595          * making sure result is used correctly.
5596          */
5597         appendStringInfoChar(buf, '\'');
5598         for (valptr = val; *valptr; valptr++)
5599         {
5600                 char            ch = *valptr;
5601
5602                 if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
5603                         appendStringInfoChar(buf, ch);
5604                 appendStringInfoChar(buf, ch);
5605         }
5606         appendStringInfoChar(buf, '\'');
5607 }
5608
5609
5610 /* ----------
5611  * get_sublink_expr                     - Parse back a sublink
5612  * ----------
5613  */
5614 static void
5615 get_sublink_expr(SubLink *sublink, deparse_context *context)
5616 {
5617         StringInfo      buf = context->buf;
5618         Query      *query = (Query *) (sublink->subselect);
5619         char       *opname = NULL;
5620         bool            need_paren;
5621
5622         if (sublink->subLinkType == ARRAY_SUBLINK)
5623                 appendStringInfo(buf, "ARRAY(");
5624         else
5625                 appendStringInfoChar(buf, '(');
5626
5627         /*
5628          * Note that we print the name of only the first operator, when there are
5629          * multiple combining operators.  This is an approximation that could go
5630          * wrong in various scenarios (operators in different schemas, renamed
5631          * operators, etc) but there is not a whole lot we can do about it, since
5632          * the syntax allows only one operator to be shown.
5633          */
5634         if (sublink->testexpr)
5635         {
5636                 if (IsA(sublink->testexpr, OpExpr))
5637                 {
5638                         /* single combining operator */
5639                         OpExpr     *opexpr = (OpExpr *) sublink->testexpr;
5640
5641                         get_rule_expr(linitial(opexpr->args), context, true);
5642                         opname = generate_operator_name(opexpr->opno,
5643                                                                                         exprType(linitial(opexpr->args)),
5644                                                                                         exprType(lsecond(opexpr->args)));
5645                 }
5646                 else if (IsA(sublink->testexpr, BoolExpr))
5647                 {
5648                         /* multiple combining operators, = or <> cases */
5649                         char       *sep;
5650                         ListCell   *l;
5651
5652                         appendStringInfoChar(buf, '(');
5653                         sep = "";
5654                         foreach(l, ((BoolExpr *) sublink->testexpr)->args)
5655                         {
5656                                 OpExpr     *opexpr = (OpExpr *) lfirst(l);
5657
5658                                 Assert(IsA(opexpr, OpExpr));
5659                                 appendStringInfoString(buf, sep);
5660                                 get_rule_expr(linitial(opexpr->args), context, true);
5661                                 if (!opname)
5662                                         opname = generate_operator_name(opexpr->opno,
5663                                                                                         exprType(linitial(opexpr->args)),
5664                                                                                         exprType(lsecond(opexpr->args)));
5665                                 sep = ", ";
5666                         }
5667                         appendStringInfoChar(buf, ')');
5668                 }
5669                 else if (IsA(sublink->testexpr, RowCompareExpr))
5670                 {
5671                         /* multiple combining operators, < <= > >= cases */
5672                         RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
5673
5674                         appendStringInfoChar(buf, '(');
5675                         get_rule_expr((Node *) rcexpr->largs, context, true);
5676                         opname = generate_operator_name(linitial_oid(rcexpr->opnos),
5677                                                                                         exprType(linitial(rcexpr->largs)),
5678                                                                                   exprType(linitial(rcexpr->rargs)));
5679                         appendStringInfoChar(buf, ')');
5680                 }
5681                 else
5682                         elog(ERROR, "unrecognized testexpr type: %d",
5683                                  (int) nodeTag(sublink->testexpr));
5684         }
5685
5686         need_paren = true;
5687
5688         switch (sublink->subLinkType)
5689         {
5690                 case EXISTS_SUBLINK:
5691                         appendStringInfo(buf, "EXISTS ");
5692                         break;
5693
5694                 case ANY_SUBLINK:
5695                         if (strcmp(opname, "=") == 0)           /* Represent = ANY as IN */
5696                                 appendStringInfo(buf, " IN ");
5697                         else
5698                                 appendStringInfo(buf, " %s ANY ", opname);
5699                         break;
5700
5701                 case ALL_SUBLINK:
5702                         appendStringInfo(buf, " %s ALL ", opname);
5703                         break;
5704
5705                 case ROWCOMPARE_SUBLINK:
5706                         appendStringInfo(buf, " %s ", opname);
5707                         break;
5708
5709                 case EXPR_SUBLINK:
5710                 case ARRAY_SUBLINK:
5711                         need_paren = false;
5712                         break;
5713
5714                 case CTE_SUBLINK:               /* shouldn't occur in a SubLink */
5715                 default:
5716                         elog(ERROR, "unrecognized sublink type: %d",
5717                                  (int) sublink->subLinkType);
5718                         break;
5719         }
5720
5721         if (need_paren)
5722                 appendStringInfoChar(buf, '(');
5723
5724         get_query_def(query, buf, context->namespaces, NULL,
5725                                   context->prettyFlags, context->indentLevel);
5726
5727         if (need_paren)
5728                 appendStringInfo(buf, "))");
5729         else
5730                 appendStringInfoChar(buf, ')');
5731 }
5732
5733
5734 /* ----------
5735  * get_from_clause                      - Parse back a FROM clause
5736  *
5737  * "prefix" is the keyword that denotes the start of the list of FROM
5738  * elements. It is FROM when used to parse back SELECT and UPDATE, but
5739  * is USING when parsing back DELETE.
5740  * ----------
5741  */
5742 static void
5743 get_from_clause(Query *query, const char *prefix, deparse_context *context)
5744 {
5745         StringInfo      buf = context->buf;
5746         bool            first = true;
5747         ListCell   *l;
5748
5749         /*
5750          * We use the query's jointree as a guide to what to print.  However, we
5751          * must ignore auto-added RTEs that are marked not inFromCl. (These can
5752          * only appear at the top level of the jointree, so it's sufficient to
5753          * check here.)  This check also ensures we ignore the rule pseudo-RTEs
5754          * for NEW and OLD.
5755          */
5756         foreach(l, query->jointree->fromlist)
5757         {
5758                 Node       *jtnode = (Node *) lfirst(l);
5759
5760                 if (IsA(jtnode, RangeTblRef))
5761                 {
5762                         int                     varno = ((RangeTblRef *) jtnode)->rtindex;
5763                         RangeTblEntry *rte = rt_fetch(varno, query->rtable);
5764
5765                         if (!rte->inFromCl)
5766                                 continue;
5767                 }
5768
5769                 if (first)
5770                 {
5771                         appendContextKeyword(context, prefix,
5772                                                                  -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
5773                         first = false;
5774                 }
5775                 else
5776                         appendStringInfoString(buf, ", ");
5777
5778                 get_from_clause_item(jtnode, query, context);
5779         }
5780 }
5781
5782 static void
5783 get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
5784 {
5785         StringInfo      buf = context->buf;
5786
5787         if (IsA(jtnode, RangeTblRef))
5788         {
5789                 int                     varno = ((RangeTblRef *) jtnode)->rtindex;
5790                 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
5791                 bool            gavealias = false;
5792
5793                 switch (rte->rtekind)
5794                 {
5795                         case RTE_RELATION:
5796                                 /* Normal relation RTE */
5797                                 appendStringInfo(buf, "%s%s",
5798                                                                  only_marker(rte),
5799                                                                  generate_relation_name(rte->relid,
5800                                                                                                                 context->namespaces));
5801                                 break;
5802                         case RTE_SUBQUERY:
5803                                 /* Subquery RTE */
5804                                 appendStringInfoChar(buf, '(');
5805                                 get_query_def(rte->subquery, buf, context->namespaces, NULL,
5806                                                           context->prettyFlags, context->indentLevel);
5807                                 appendStringInfoChar(buf, ')');
5808                                 break;
5809                         case RTE_FUNCTION:
5810                                 /* Function RTE */
5811                                 get_rule_expr(rte->funcexpr, context, true);
5812                                 break;
5813                         case RTE_VALUES:
5814                                 /* Values list RTE */
5815                                 get_values_def(rte->values_lists, context);
5816                                 break;
5817                         case RTE_CTE:
5818                                 appendStringInfoString(buf, quote_identifier(rte->ctename));
5819                                 break;
5820                         default:
5821                                 elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
5822                                 break;
5823                 }
5824
5825                 if (rte->alias != NULL)
5826                 {
5827                         appendStringInfo(buf, " %s",
5828                                                          quote_identifier(rte->alias->aliasname));
5829                         gavealias = true;
5830                 }
5831                 else if (rte->rtekind == RTE_RELATION &&
5832                                  strcmp(rte->eref->aliasname, get_rel_name(rte->relid)) != 0)
5833                 {
5834                         /*
5835                          * Apparently the rel has been renamed since the rule was made.
5836                          * Emit a fake alias clause so that variable references will still
5837                          * work.  This is not a 100% solution but should work in most
5838                          * reasonable situations.
5839                          */
5840                         appendStringInfo(buf, " %s",
5841                                                          quote_identifier(rte->eref->aliasname));
5842                         gavealias = true;
5843                 }
5844                 else if (rte->rtekind == RTE_FUNCTION)
5845                 {
5846                         /*
5847                          * For a function RTE, always give an alias. This covers possible
5848                          * renaming of the function and/or instability of the
5849                          * FigureColname rules for things that aren't simple functions.
5850                          */
5851                         appendStringInfo(buf, " %s",
5852                                                          quote_identifier(rte->eref->aliasname));
5853                         gavealias = true;
5854                 }
5855
5856                 if (rte->rtekind == RTE_FUNCTION)
5857                 {
5858                         if (rte->funccoltypes != NIL)
5859                         {
5860                                 /* Function returning RECORD, reconstruct the columndefs */
5861                                 if (!gavealias)
5862                                         appendStringInfo(buf, " AS ");
5863                                 get_from_clause_coldeflist(rte->eref->colnames,
5864                                                                                    rte->funccoltypes,
5865                                                                                    rte->funccoltypmods,
5866                                                                                    context);
5867                         }
5868                         else
5869                         {
5870                                 /*
5871                                  * For a function RTE, always emit a complete column alias
5872                                  * list; this is to protect against possible instability of
5873                                  * the default column names (eg, from altering parameter
5874                                  * names).
5875                                  */
5876                                 get_from_clause_alias(rte->eref, rte, context);
5877                         }
5878                 }
5879                 else
5880                 {
5881                         /*
5882                          * For non-function RTEs, just report whatever the user originally
5883                          * gave as column aliases.
5884                          */
5885                         get_from_clause_alias(rte->alias, rte, context);
5886                 }
5887         }
5888         else if (IsA(jtnode, JoinExpr))
5889         {
5890                 JoinExpr   *j = (JoinExpr *) jtnode;
5891                 bool            need_paren_on_right;
5892
5893                 need_paren_on_right = PRETTY_PAREN(context) &&
5894                         !IsA(j->rarg, RangeTblRef) &&
5895                         !(IsA(j->rarg, JoinExpr) &&((JoinExpr *) j->rarg)->alias != NULL);
5896
5897                 if (!PRETTY_PAREN(context) || j->alias != NULL)
5898                         appendStringInfoChar(buf, '(');
5899
5900                 get_from_clause_item(j->larg, query, context);
5901
5902                 if (j->isNatural)
5903                 {
5904                         if (!PRETTY_INDENT(context))
5905                                 appendStringInfoChar(buf, ' ');
5906                         switch (j->jointype)
5907                         {
5908                                 case JOIN_INNER:
5909                                         appendContextKeyword(context, "NATURAL JOIN ",
5910                                                                                  -PRETTYINDENT_JOIN,
5911                                                                                  PRETTYINDENT_JOIN, 0);
5912                                         break;
5913                                 case JOIN_LEFT:
5914                                         appendContextKeyword(context, "NATURAL LEFT JOIN ",
5915                                                                                  -PRETTYINDENT_JOIN,
5916                                                                                  PRETTYINDENT_JOIN, 0);
5917                                         break;
5918                                 case JOIN_FULL:
5919                                         appendContextKeyword(context, "NATURAL FULL JOIN ",
5920                                                                                  -PRETTYINDENT_JOIN,
5921                                                                                  PRETTYINDENT_JOIN, 0);
5922                                         break;
5923                                 case JOIN_RIGHT:
5924                                         appendContextKeyword(context, "NATURAL RIGHT JOIN ",
5925                                                                                  -PRETTYINDENT_JOIN,
5926                                                                                  PRETTYINDENT_JOIN, 0);
5927                                         break;
5928                                 default:
5929                                         elog(ERROR, "unrecognized join type: %d",
5930                                                  (int) j->jointype);
5931                         }
5932                 }
5933                 else
5934                 {
5935                         switch (j->jointype)
5936                         {
5937                                 case JOIN_INNER:
5938                                         if (j->quals)
5939                                                 appendContextKeyword(context, " JOIN ",
5940                                                                                          -PRETTYINDENT_JOIN,
5941                                                                                          PRETTYINDENT_JOIN, 2);
5942                                         else
5943                                                 appendContextKeyword(context, " CROSS JOIN ",
5944                                                                                          -PRETTYINDENT_JOIN,
5945                                                                                          PRETTYINDENT_JOIN, 1);
5946                                         break;
5947                                 case JOIN_LEFT:
5948                                         appendContextKeyword(context, " LEFT JOIN ",
5949                                                                                  -PRETTYINDENT_JOIN,
5950                                                                                  PRETTYINDENT_JOIN, 2);
5951                                         break;
5952                                 case JOIN_FULL:
5953                                         appendContextKeyword(context, " FULL JOIN ",
5954                                                                                  -PRETTYINDENT_JOIN,
5955                                                                                  PRETTYINDENT_JOIN, 2);
5956                                         break;
5957                                 case JOIN_RIGHT:
5958                                         appendContextKeyword(context, " RIGHT JOIN ",
5959                                                                                  -PRETTYINDENT_JOIN,
5960                                                                                  PRETTYINDENT_JOIN, 2);
5961                                         break;
5962                                 default:
5963                                         elog(ERROR, "unrecognized join type: %d",
5964                                                  (int) j->jointype);
5965                         }
5966                 }
5967
5968                 if (need_paren_on_right)
5969                         appendStringInfoChar(buf, '(');
5970                 get_from_clause_item(j->rarg, query, context);
5971                 if (need_paren_on_right)
5972                         appendStringInfoChar(buf, ')');
5973
5974                 context->indentLevel -= PRETTYINDENT_JOIN_ON;
5975
5976                 if (!j->isNatural)
5977                 {
5978                         if (j->usingClause)
5979                         {
5980                                 ListCell   *col;
5981
5982                                 appendStringInfo(buf, " USING (");
5983                                 foreach(col, j->usingClause)
5984                                 {
5985                                         if (col != list_head(j->usingClause))
5986                                                 appendStringInfo(buf, ", ");
5987                                         appendStringInfoString(buf,
5988                                                                           quote_identifier(strVal(lfirst(col))));
5989                                 }
5990                                 appendStringInfoChar(buf, ')');
5991                         }
5992                         else if (j->quals)
5993                         {
5994                                 appendStringInfo(buf, " ON ");
5995                                 if (!PRETTY_PAREN(context))
5996                                         appendStringInfoChar(buf, '(');
5997                                 get_rule_expr(j->quals, context, false);
5998                                 if (!PRETTY_PAREN(context))
5999                                         appendStringInfoChar(buf, ')');
6000                         }
6001                 }
6002                 if (!PRETTY_PAREN(context) || j->alias != NULL)
6003                         appendStringInfoChar(buf, ')');
6004
6005                 /* Yes, it's correct to put alias after the right paren ... */
6006                 if (j->alias != NULL)
6007                 {
6008                         appendStringInfo(buf, " %s",
6009                                                          quote_identifier(j->alias->aliasname));
6010                         get_from_clause_alias(j->alias,
6011                                                                   rt_fetch(j->rtindex, query->rtable),
6012                                                                   context);
6013                 }
6014         }
6015         else
6016                 elog(ERROR, "unrecognized node type: %d",
6017                          (int) nodeTag(jtnode));
6018 }
6019
6020 /*
6021  * get_from_clause_alias - reproduce column alias list
6022  *
6023  * This is tricky because we must ignore dropped columns.
6024  */
6025 static void
6026 get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
6027                                           deparse_context *context)
6028 {
6029         StringInfo      buf = context->buf;
6030         ListCell   *col;
6031         AttrNumber      attnum;
6032         bool            first = true;
6033
6034         if (alias == NULL || alias->colnames == NIL)
6035                 return;                                 /* definitely nothing to do */
6036
6037         attnum = 0;
6038         foreach(col, alias->colnames)
6039         {
6040                 attnum++;
6041                 if (get_rte_attribute_is_dropped(rte, attnum))
6042                         continue;
6043                 if (first)
6044                 {
6045                         appendStringInfoChar(buf, '(');
6046                         first = false;
6047                 }
6048                 else
6049                         appendStringInfo(buf, ", ");
6050                 appendStringInfoString(buf,
6051                                                            quote_identifier(strVal(lfirst(col))));
6052         }
6053         if (!first)
6054                 appendStringInfoChar(buf, ')');
6055 }
6056
6057 /*
6058  * get_from_clause_coldeflist - reproduce FROM clause coldeflist
6059  *
6060  * The coldeflist is appended immediately (no space) to buf.  Caller is
6061  * responsible for ensuring that an alias or AS is present before it.
6062  */
6063 static void
6064 get_from_clause_coldeflist(List *names, List *types, List *typmods,
6065                                                    deparse_context *context)
6066 {
6067         StringInfo      buf = context->buf;
6068         ListCell   *l1;
6069         ListCell   *l2;
6070         ListCell   *l3;
6071         int                     i = 0;
6072
6073         appendStringInfoChar(buf, '(');
6074
6075         l2 = list_head(types);
6076         l3 = list_head(typmods);
6077         foreach(l1, names)
6078         {
6079                 char       *attname = strVal(lfirst(l1));
6080                 Oid                     atttypid;
6081                 int32           atttypmod;
6082
6083                 atttypid = lfirst_oid(l2);
6084                 l2 = lnext(l2);
6085                 atttypmod = lfirst_int(l3);
6086                 l3 = lnext(l3);
6087
6088                 if (i > 0)
6089                         appendStringInfo(buf, ", ");
6090                 appendStringInfo(buf, "%s %s",
6091                                                  quote_identifier(attname),
6092                                                  format_type_with_typemod(atttypid, atttypmod));
6093                 i++;
6094         }
6095
6096         appendStringInfoChar(buf, ')');
6097 }
6098
6099 /*
6100  * get_opclass_name                     - fetch name of an index operator class
6101  *
6102  * The opclass name is appended (after a space) to buf.
6103  *
6104  * Output is suppressed if the opclass is the default for the given
6105  * actual_datatype.  (If you don't want this behavior, just pass
6106  * InvalidOid for actual_datatype.)
6107  */
6108 static void
6109 get_opclass_name(Oid opclass, Oid actual_datatype,
6110                                  StringInfo buf)
6111 {
6112         HeapTuple       ht_opc;
6113         Form_pg_opclass opcrec;
6114         char       *opcname;
6115         char       *nspname;
6116
6117         ht_opc = SearchSysCache(CLAOID,
6118                                                         ObjectIdGetDatum(opclass),
6119                                                         0, 0, 0);
6120         if (!HeapTupleIsValid(ht_opc))
6121                 elog(ERROR, "cache lookup failed for opclass %u", opclass);
6122         opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
6123
6124         if (!OidIsValid(actual_datatype) ||
6125                 GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
6126         {
6127                 /* Okay, we need the opclass name.      Do we need to qualify it? */
6128                 opcname = NameStr(opcrec->opcname);
6129                 if (OpclassIsVisible(opclass))
6130                         appendStringInfo(buf, " %s", quote_identifier(opcname));
6131                 else
6132                 {
6133                         nspname = get_namespace_name(opcrec->opcnamespace);
6134                         appendStringInfo(buf, " %s.%s",
6135                                                          quote_identifier(nspname),
6136                                                          quote_identifier(opcname));
6137                 }
6138         }
6139         ReleaseSysCache(ht_opc);
6140 }
6141
6142 /*
6143  * processIndirection - take care of array and subfield assignment
6144  *
6145  * We strip any top-level FieldStore or assignment ArrayRef nodes that
6146  * appear in the input, and return the subexpression that's to be assigned.
6147  * If printit is true, we also print out the appropriate decoration for the
6148  * base column name (that the caller just printed).
6149  */
6150 static Node *
6151 processIndirection(Node *node, deparse_context *context, bool printit)
6152 {
6153         StringInfo      buf = context->buf;
6154
6155         for (;;)
6156         {
6157                 if (node == NULL)
6158                         break;
6159                 if (IsA(node, FieldStore))
6160                 {
6161                         FieldStore *fstore = (FieldStore *) node;
6162                         Oid                     typrelid;
6163                         char       *fieldname;
6164
6165                         /* lookup tuple type */
6166                         typrelid = get_typ_typrelid(fstore->resulttype);
6167                         if (!OidIsValid(typrelid))
6168                                 elog(ERROR, "argument type %s of FieldStore is not a tuple type",
6169                                          format_type_be(fstore->resulttype));
6170
6171                         /*
6172                          * Print the field name.  Note we assume here that there's only
6173                          * one field being assigned to.  This is okay in stored rules but
6174                          * could be wrong in executable target lists.  Presently no
6175                          * problem since explain.c doesn't print plan targetlists, but
6176                          * someday may have to think of something ...
6177                          */
6178                         fieldname = get_relid_attribute_name(typrelid,
6179                                                                                         linitial_int(fstore->fieldnums));
6180                         if (printit)
6181                                 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
6182
6183                         /*
6184                          * We ignore arg since it should be an uninteresting reference to
6185                          * the target column or subcolumn.
6186                          */
6187                         node = (Node *) linitial(fstore->newvals);
6188                 }
6189                 else if (IsA(node, ArrayRef))
6190                 {
6191                         ArrayRef   *aref = (ArrayRef *) node;
6192
6193                         if (aref->refassgnexpr == NULL)
6194                                 break;
6195                         if (printit)
6196                                 printSubscripts(aref, context);
6197
6198                         /*
6199                          * We ignore refexpr since it should be an uninteresting reference
6200                          * to the target column or subcolumn.
6201                          */
6202                         node = (Node *) aref->refassgnexpr;
6203                 }
6204                 else
6205                         break;
6206         }
6207
6208         return node;
6209 }
6210
6211 static void
6212 printSubscripts(ArrayRef *aref, deparse_context *context)
6213 {
6214         StringInfo      buf = context->buf;
6215         ListCell   *lowlist_item;
6216         ListCell   *uplist_item;
6217
6218         lowlist_item = list_head(aref->reflowerindexpr);        /* could be NULL */
6219         foreach(uplist_item, aref->refupperindexpr)
6220         {
6221                 appendStringInfoChar(buf, '[');
6222                 if (lowlist_item)
6223                 {
6224                         get_rule_expr((Node *) lfirst(lowlist_item), context, false);
6225                         appendStringInfoChar(buf, ':');
6226                         lowlist_item = lnext(lowlist_item);
6227                 }
6228                 get_rule_expr((Node *) lfirst(uplist_item), context, false);
6229                 appendStringInfoChar(buf, ']');
6230         }
6231 }
6232
6233 /*
6234  * quote_identifier                     - Quote an identifier only if needed
6235  *
6236  * When quotes are needed, we palloc the required space; slightly
6237  * space-wasteful but well worth it for notational simplicity.
6238  */
6239 const char *
6240 quote_identifier(const char *ident)
6241 {
6242         /*
6243          * Can avoid quoting if ident starts with a lowercase letter or underscore
6244          * and contains only lowercase letters, digits, and underscores, *and* is
6245          * not any SQL keyword.  Otherwise, supply quotes.
6246          */
6247         int                     nquotes = 0;
6248         bool            safe;
6249         const char *ptr;
6250         char       *result;
6251         char       *optr;
6252
6253         /*
6254          * would like to use <ctype.h> macros here, but they might yield unwanted
6255          * locale-specific results...
6256          */
6257         safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
6258
6259         for (ptr = ident; *ptr; ptr++)
6260         {
6261                 char            ch = *ptr;
6262
6263                 if ((ch >= 'a' && ch <= 'z') ||
6264                         (ch >= '0' && ch <= '9') ||
6265                         (ch == '_'))
6266                 {
6267                         /* okay */
6268                 }
6269                 else
6270                 {
6271                         safe = false;
6272                         if (ch == '"')
6273                                 nquotes++;
6274                 }
6275         }
6276
6277         if (safe)
6278         {
6279                 /*
6280                  * Check for keyword.  We quote keywords except for unreserved ones.
6281                  * (In some cases we could avoid quoting a col_name or type_func_name
6282                  * keyword, but it seems much harder than it's worth to tell that.)
6283                  *
6284                  * Note: ScanKeywordLookup() does case-insensitive comparison, but
6285                  * that's fine, since we already know we have all-lower-case.
6286                  */
6287                 const ScanKeyword *keyword = ScanKeywordLookup(ident,
6288                                                                                                            ScanKeywords,
6289                                                                                                            NumScanKeywords);
6290
6291                 if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD)
6292                         safe = false;
6293         }
6294
6295         if (safe)
6296                 return ident;                   /* no change needed */
6297
6298         result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
6299
6300         optr = result;
6301         *optr++ = '"';
6302         for (ptr = ident; *ptr; ptr++)
6303         {
6304                 char            ch = *ptr;
6305
6306                 if (ch == '"')
6307                         *optr++ = '"';
6308                 *optr++ = ch;
6309         }
6310         *optr++ = '"';
6311         *optr = '\0';
6312
6313         return result;
6314 }
6315
6316 /*
6317  * quote_qualified_identifier   - Quote a possibly-qualified identifier
6318  *
6319  * Return a name of the form qualifier.ident, or just ident if qualifier
6320  * is NULL, quoting each component if necessary.  The result is palloc'd.
6321  */
6322 char *
6323 quote_qualified_identifier(const char *qualifier,
6324                                                    const char *ident)
6325 {
6326         StringInfoData buf;
6327
6328         initStringInfo(&buf);
6329         if (qualifier)
6330                 appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
6331         appendStringInfoString(&buf, quote_identifier(ident));
6332         return buf.data;
6333 }
6334
6335 /*
6336  * generate_relation_name
6337  *              Compute the name to display for a relation specified by OID
6338  *
6339  * The result includes all necessary quoting and schema-prefixing.
6340  *
6341  * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
6342  * We will forcibly qualify the relation name if it equals any CTE name
6343  * visible in the namespace list.
6344  */
6345 static char *
6346 generate_relation_name(Oid relid, List *namespaces)
6347 {
6348         HeapTuple       tp;
6349         Form_pg_class reltup;
6350         bool            need_qual;
6351         ListCell   *nslist;
6352         char       *relname;
6353         char       *nspname;
6354         char       *result;
6355
6356         tp = SearchSysCache(RELOID,
6357                                                 ObjectIdGetDatum(relid),
6358                                                 0, 0, 0);
6359         if (!HeapTupleIsValid(tp))
6360                 elog(ERROR, "cache lookup failed for relation %u", relid);
6361         reltup = (Form_pg_class) GETSTRUCT(tp);
6362         relname = NameStr(reltup->relname);
6363
6364         /* Check for conflicting CTE name */
6365         need_qual = false;
6366         foreach(nslist, namespaces)
6367         {
6368                 deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
6369                 ListCell   *ctlist;
6370
6371                 foreach(ctlist, dpns->ctes)
6372                 {
6373                         CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);
6374
6375                         if (strcmp(cte->ctename, relname) == 0)
6376                         {
6377                                 need_qual = true;
6378                                 break;
6379                         }
6380                 }
6381                 if (need_qual)
6382                         break;
6383         }
6384
6385         /* Otherwise, qualify the name if not visible in search path */
6386         if (!need_qual)
6387                 need_qual = !RelationIsVisible(relid);
6388
6389         if (need_qual)
6390                 nspname = get_namespace_name(reltup->relnamespace);
6391         else
6392                 nspname = NULL;
6393
6394         result = quote_qualified_identifier(nspname, relname);
6395
6396         ReleaseSysCache(tp);
6397
6398         return result;
6399 }
6400
6401 /*
6402  * generate_function_name
6403  *              Compute the name to display for a function specified by OID,
6404  *              given that it is being called with the specified actual arg names and
6405  *              types.  (Those matter because of ambiguous-function resolution rules.)
6406  *
6407  * The result includes all necessary quoting and schema-prefixing.      We can
6408  * also pass back an indication of whether the function is variadic.
6409  */
6410 static char *
6411 generate_function_name(Oid funcid, int nargs, List *argnames,
6412                                            Oid *argtypes, bool *is_variadic)
6413 {
6414         HeapTuple       proctup;
6415         Form_pg_proc procform;
6416         char       *proname;
6417         char       *nspname;
6418         char       *result;
6419         FuncDetailCode p_result;
6420         Oid                     p_funcid;
6421         Oid                     p_rettype;
6422         bool            p_retset;
6423         int                     p_nvargs;
6424         Oid                *p_true_typeids;
6425
6426         proctup = SearchSysCache(PROCOID,
6427                                                          ObjectIdGetDatum(funcid),
6428                                                          0, 0, 0);
6429         if (!HeapTupleIsValid(proctup))
6430                 elog(ERROR, "cache lookup failed for function %u", funcid);
6431         procform = (Form_pg_proc) GETSTRUCT(proctup);
6432         proname = NameStr(procform->proname);
6433
6434         /*
6435          * The idea here is to schema-qualify only if the parser would fail to
6436          * resolve the correct function given the unqualified func name with the
6437          * specified argtypes.  If the function is variadic, we should presume
6438          * that VARIADIC will be included in the call.
6439          */
6440         p_result = func_get_detail(list_make1(makeString(proname)),
6441                                                            NIL, argnames, nargs, argtypes,
6442                                                            !OidIsValid(procform->provariadic), true,
6443                                                            &p_funcid, &p_rettype,
6444                                                            &p_retset, &p_nvargs, &p_true_typeids, NULL);
6445         if ((p_result == FUNCDETAIL_NORMAL ||
6446                  p_result == FUNCDETAIL_AGGREGATE ||
6447                  p_result == FUNCDETAIL_WINDOWFUNC) &&
6448                 p_funcid == funcid)
6449                 nspname = NULL;
6450         else
6451                 nspname = get_namespace_name(procform->pronamespace);
6452
6453         result = quote_qualified_identifier(nspname, proname);
6454
6455         /* Check variadic-ness if caller cares */
6456         if (is_variadic)
6457         {
6458                 /* "any" variadics are not treated as variadics for listing */
6459                 if (OidIsValid(procform->provariadic) &&
6460                         procform->provariadic != ANYOID)
6461                         *is_variadic = true;
6462                 else
6463                         *is_variadic = false;
6464         }
6465
6466         ReleaseSysCache(proctup);
6467
6468         return result;
6469 }
6470
6471 /*
6472  * generate_operator_name
6473  *              Compute the name to display for an operator specified by OID,
6474  *              given that it is being called with the specified actual arg types.
6475  *              (Arg types matter because of ambiguous-operator resolution rules.
6476  *              Pass InvalidOid for unused arg of a unary operator.)
6477  *
6478  * The result includes all necessary quoting and schema-prefixing,
6479  * plus the OPERATOR() decoration needed to use a qualified operator name
6480  * in an expression.
6481  */
6482 static char *
6483 generate_operator_name(Oid operid, Oid arg1, Oid arg2)
6484 {
6485         StringInfoData buf;
6486         HeapTuple       opertup;
6487         Form_pg_operator operform;
6488         char       *oprname;
6489         char       *nspname;
6490         Operator        p_result;
6491
6492         initStringInfo(&buf);
6493
6494         opertup = SearchSysCache(OPEROID,
6495                                                          ObjectIdGetDatum(operid),
6496                                                          0, 0, 0);
6497         if (!HeapTupleIsValid(opertup))
6498                 elog(ERROR, "cache lookup failed for operator %u", operid);
6499         operform = (Form_pg_operator) GETSTRUCT(opertup);
6500         oprname = NameStr(operform->oprname);
6501
6502         /*
6503          * The idea here is to schema-qualify only if the parser would fail to
6504          * resolve the correct operator given the unqualified op name with the
6505          * specified argtypes.
6506          */
6507         switch (operform->oprkind)
6508         {
6509                 case 'b':
6510                         p_result = oper(NULL, list_make1(makeString(oprname)), arg1, arg2,
6511                                                         true, -1);
6512                         break;
6513                 case 'l':
6514                         p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2,
6515                                                                  true, -1);
6516                         break;
6517                 case 'r':
6518                         p_result = right_oper(NULL, list_make1(makeString(oprname)), arg1,
6519                                                                   true, -1);
6520                         break;
6521                 default:
6522                         elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
6523                         p_result = NULL;        /* keep compiler quiet */
6524                         break;
6525         }
6526
6527         if (p_result != NULL && oprid(p_result) == operid)
6528                 nspname = NULL;
6529         else
6530         {
6531                 nspname = get_namespace_name(operform->oprnamespace);
6532                 appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
6533         }
6534
6535         appendStringInfoString(&buf, oprname);
6536
6537         if (nspname)
6538                 appendStringInfoChar(&buf, ')');
6539
6540         if (p_result != NULL)
6541                 ReleaseSysCache(p_result);
6542
6543         ReleaseSysCache(opertup);
6544
6545         return buf.data;
6546 }
6547
6548 /*
6549  * Given a C string, produce a TEXT datum.
6550  *
6551  * We assume that the input was palloc'd and may be freed.
6552  */
6553 static text *
6554 string_to_text(char *str)
6555 {
6556         text       *result;
6557
6558         result = cstring_to_text(str);
6559         pfree(str);
6560         return result;
6561 }
6562
6563 /*
6564  * Generate a C string representing a relation's reloptions, or NULL if none.
6565  */
6566 static char *
6567 flatten_reloptions(Oid relid)
6568 {
6569         char       *result = NULL;
6570         HeapTuple       tuple;
6571         Datum           reloptions;
6572         bool            isnull;
6573
6574         tuple = SearchSysCache(RELOID,
6575                                                    ObjectIdGetDatum(relid),
6576                                                    0, 0, 0);
6577         if (!HeapTupleIsValid(tuple))
6578                 elog(ERROR, "cache lookup failed for relation %u", relid);
6579
6580         reloptions = SysCacheGetAttr(RELOID, tuple,
6581                                                                  Anum_pg_class_reloptions, &isnull);
6582         if (!isnull)
6583         {
6584                 Datum           sep,
6585                                         txt;
6586
6587                 /*
6588                  * We want to use array_to_text(reloptions, ', ') --- but
6589                  * DirectFunctionCall2(array_to_text) does not work, because
6590                  * array_to_text() relies on flinfo to be valid.  So use
6591                  * OidFunctionCall2.
6592                  */
6593                 sep = CStringGetTextDatum(", ");
6594                 txt = OidFunctionCall2(F_ARRAY_TO_TEXT, reloptions, sep);
6595                 result = TextDatumGetCString(txt);
6596         }
6597
6598         ReleaseSysCache(tuple);
6599
6600         return result;
6601 }