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