Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * ruleutils.c
4 : * Functions to convert stored expressions/querytrees back to
5 : * source text
6 : *
7 : * Portions Copyright (c) 1996-2017, 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 <ctype.h>
19 : #include <unistd.h>
20 : #include <fcntl.h>
21 :
22 : #include "access/amapi.h"
23 : #include "access/htup_details.h"
24 : #include "access/sysattr.h"
25 : #include "catalog/dependency.h"
26 : #include "catalog/indexing.h"
27 : #include "catalog/partition.h"
28 : #include "catalog/pg_aggregate.h"
29 : #include "catalog/pg_am.h"
30 : #include "catalog/pg_authid.h"
31 : #include "catalog/pg_collation.h"
32 : #include "catalog/pg_constraint.h"
33 : #include "catalog/pg_depend.h"
34 : #include "catalog/pg_language.h"
35 : #include "catalog/pg_opclass.h"
36 : #include "catalog/pg_operator.h"
37 : #include "catalog/pg_partitioned_table.h"
38 : #include "catalog/pg_proc.h"
39 : #include "catalog/pg_statistic_ext.h"
40 : #include "catalog/pg_trigger.h"
41 : #include "catalog/pg_type.h"
42 : #include "commands/defrem.h"
43 : #include "commands/tablespace.h"
44 : #include "common/keywords.h"
45 : #include "executor/spi.h"
46 : #include "funcapi.h"
47 : #include "mb/pg_wchar.h"
48 : #include "miscadmin.h"
49 : #include "nodes/makefuncs.h"
50 : #include "nodes/nodeFuncs.h"
51 : #include "optimizer/tlist.h"
52 : #include "parser/parse_node.h"
53 : #include "parser/parse_agg.h"
54 : #include "parser/parse_func.h"
55 : #include "parser/parse_oper.h"
56 : #include "parser/parser.h"
57 : #include "parser/parsetree.h"
58 : #include "rewrite/rewriteHandler.h"
59 : #include "rewrite/rewriteManip.h"
60 : #include "rewrite/rewriteSupport.h"
61 : #include "utils/array.h"
62 : #include "utils/builtins.h"
63 : #include "utils/fmgroids.h"
64 : #include "utils/hsearch.h"
65 : #include "utils/lsyscache.h"
66 : #include "utils/rel.h"
67 : #include "utils/ruleutils.h"
68 : #include "utils/snapmgr.h"
69 : #include "utils/syscache.h"
70 : #include "utils/tqual.h"
71 : #include "utils/typcache.h"
72 : #include "utils/varlena.h"
73 : #include "utils/xml.h"
74 :
75 :
76 : /* ----------
77 : * Pretty formatting constants
78 : * ----------
79 : */
80 :
81 : /* Indent counts */
82 : #define PRETTYINDENT_STD 8
83 : #define PRETTYINDENT_JOIN 4
84 : #define PRETTYINDENT_VAR 4
85 :
86 : #define PRETTYINDENT_LIMIT 40 /* wrap limit */
87 :
88 : /* Pretty flags */
89 : #define PRETTYFLAG_PAREN 1
90 : #define PRETTYFLAG_INDENT 2
91 :
92 : /* Default line length for pretty-print wrapping: 0 means wrap always */
93 : #define WRAP_COLUMN_DEFAULT 0
94 :
95 : /* macro to test if pretty action needed */
96 : #define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
97 : #define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
98 :
99 :
100 : /* ----------
101 : * Local data types
102 : * ----------
103 : */
104 :
105 : /* Context info needed for invoking a recursive querytree display routine */
106 : typedef struct
107 : {
108 : StringInfo buf; /* output buffer to append to */
109 : List *namespaces; /* List of deparse_namespace nodes */
110 : List *windowClause; /* Current query level's WINDOW clause */
111 : List *windowTList; /* targetlist for resolving WINDOW clause */
112 : int prettyFlags; /* enabling of pretty-print functions */
113 : int wrapColumn; /* max line length, or -1 for no limit */
114 : int indentLevel; /* current indent level for prettyprint */
115 : bool varprefix; /* TRUE to print prefixes on Vars */
116 : ParseExprKind special_exprkind; /* set only for exprkinds needing special
117 : * handling */
118 : } deparse_context;
119 :
120 : /*
121 : * Each level of query context around a subtree needs a level of Var namespace.
122 : * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
123 : * the current context's namespaces list.
124 : *
125 : * The rangetable is the list of actual RTEs from the query tree, and the
126 : * cte list is the list of actual CTEs.
127 : *
128 : * rtable_names holds the alias name to be used for each RTE (either a C
129 : * string, or NULL for nameless RTEs such as unnamed joins).
130 : * rtable_columns holds the column alias names to be used for each RTE.
131 : *
132 : * In some cases we need to make names of merged JOIN USING columns unique
133 : * across the whole query, not only per-RTE. If so, unique_using is TRUE
134 : * and using_names is a list of C strings representing names already assigned
135 : * to USING columns.
136 : *
137 : * When deparsing plan trees, there is always just a single item in the
138 : * deparse_namespace list (since a plan tree never contains Vars with
139 : * varlevelsup > 0). We store the PlanState node that is the immediate
140 : * parent of the expression to be deparsed, as well as a list of that
141 : * PlanState's ancestors. In addition, we store its outer and inner subplan
142 : * state nodes, as well as their plan nodes' targetlists, and the index tlist
143 : * if the current plan node might contain INDEX_VAR Vars. (These fields could
144 : * be derived on-the-fly from the current PlanState, but it seems notationally
145 : * clearer to set them up as separate fields.)
146 : */
147 : typedef struct
148 : {
149 : List *rtable; /* List of RangeTblEntry nodes */
150 : List *rtable_names; /* Parallel list of names for RTEs */
151 : List *rtable_columns; /* Parallel list of deparse_columns structs */
152 : List *ctes; /* List of CommonTableExpr nodes */
153 : /* Workspace for column alias assignment: */
154 : bool unique_using; /* Are we making USING names globally unique */
155 : List *using_names; /* List of assigned names for USING columns */
156 : /* Remaining fields are used only when deparsing a Plan tree: */
157 : PlanState *planstate; /* immediate parent of current expression */
158 : List *ancestors; /* ancestors of planstate */
159 : PlanState *outer_planstate; /* outer subplan state, or NULL if none */
160 : PlanState *inner_planstate; /* inner subplan state, or NULL if none */
161 : List *outer_tlist; /* referent for OUTER_VAR Vars */
162 : List *inner_tlist; /* referent for INNER_VAR Vars */
163 : List *index_tlist; /* referent for INDEX_VAR Vars */
164 : } deparse_namespace;
165 :
166 : /*
167 : * Per-relation data about column alias names.
168 : *
169 : * Selecting aliases is unreasonably complicated because of the need to dump
170 : * rules/views whose underlying tables may have had columns added, deleted, or
171 : * renamed since the query was parsed. We must nonetheless print the rule/view
172 : * in a form that can be reloaded and will produce the same results as before.
173 : *
174 : * For each RTE used in the query, we must assign column aliases that are
175 : * unique within that RTE. SQL does not require this of the original query,
176 : * but due to factors such as *-expansion we need to be able to uniquely
177 : * reference every column in a decompiled query. As long as we qualify all
178 : * column references, per-RTE uniqueness is sufficient for that.
179 : *
180 : * However, we can't ensure per-column name uniqueness for unnamed join RTEs,
181 : * since they just inherit column names from their input RTEs, and we can't
182 : * rename the columns at the join level. Most of the time this isn't an issue
183 : * because we don't need to reference the join's output columns as such; we
184 : * can reference the input columns instead. That approach can fail for merged
185 : * JOIN USING columns, however, so when we have one of those in an unnamed
186 : * join, we have to make that column's alias globally unique across the whole
187 : * query to ensure it can be referenced unambiguously.
188 : *
189 : * Another problem is that a JOIN USING clause requires the columns to be
190 : * merged to have the same aliases in both input RTEs, and that no other
191 : * columns in those RTEs or their children conflict with the USING names.
192 : * To handle that, we do USING-column alias assignment in a recursive
193 : * traversal of the query's jointree. When descending through a JOIN with
194 : * USING, we preassign the USING column names to the child columns, overriding
195 : * other rules for column alias assignment. We also mark each RTE with a list
196 : * of all USING column names selected for joins containing that RTE, so that
197 : * when we assign other columns' aliases later, we can avoid conflicts.
198 : *
199 : * Another problem is that if a JOIN's input tables have had columns added or
200 : * deleted since the query was parsed, we must generate a column alias list
201 : * for the join that matches the current set of input columns --- otherwise, a
202 : * change in the number of columns in the left input would throw off matching
203 : * of aliases to columns of the right input. Thus, positions in the printable
204 : * column alias list are not necessarily one-for-one with varattnos of the
205 : * JOIN, so we need a separate new_colnames[] array for printing purposes.
206 : */
207 : typedef struct
208 : {
209 : /*
210 : * colnames is an array containing column aliases to use for columns that
211 : * existed when the query was parsed. Dropped columns have NULL entries.
212 : * This array can be directly indexed by varattno to get a Var's name.
213 : *
214 : * Non-NULL entries are guaranteed unique within the RTE, *except* when
215 : * this is for an unnamed JOIN RTE. In that case we merely copy up names
216 : * from the two input RTEs.
217 : *
218 : * During the recursive descent in set_using_names(), forcible assignment
219 : * of a child RTE's column name is represented by pre-setting that element
220 : * of the child's colnames array. So at that stage, NULL entries in this
221 : * array just mean that no name has been preassigned, not necessarily that
222 : * the column is dropped.
223 : */
224 : int num_cols; /* length of colnames[] array */
225 : char **colnames; /* array of C strings and NULLs */
226 :
227 : /*
228 : * new_colnames is an array containing column aliases to use for columns
229 : * that would exist if the query was re-parsed against the current
230 : * definitions of its base tables. This is what to print as the column
231 : * alias list for the RTE. This array does not include dropped columns,
232 : * but it will include columns added since original parsing. Indexes in
233 : * it therefore have little to do with current varattno values. As above,
234 : * entries are unique unless this is for an unnamed JOIN RTE. (In such an
235 : * RTE, we never actually print this array, but we must compute it anyway
236 : * for possible use in computing column names of upper joins.) The
237 : * parallel array is_new_col marks which of these columns are new since
238 : * original parsing. Entries with is_new_col false must match the
239 : * non-NULL colnames entries one-for-one.
240 : */
241 : int num_new_cols; /* length of new_colnames[] array */
242 : char **new_colnames; /* array of C strings */
243 : bool *is_new_col; /* array of bool flags */
244 :
245 : /* This flag tells whether we should actually print a column alias list */
246 : bool printaliases;
247 :
248 : /* This list has all names used as USING names in joins above this RTE */
249 : List *parentUsing; /* names assigned to parent merged columns */
250 :
251 : /*
252 : * If this struct is for a JOIN RTE, we fill these fields during the
253 : * set_using_names() pass to describe its relationship to its child RTEs.
254 : *
255 : * leftattnos and rightattnos are arrays with one entry per existing
256 : * output column of the join (hence, indexable by join varattno). For a
257 : * simple reference to a column of the left child, leftattnos[i] is the
258 : * child RTE's attno and rightattnos[i] is zero; and conversely for a
259 : * column of the right child. But for merged columns produced by JOIN
260 : * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.
261 : * Also, if the column has been dropped, both are zero.
262 : *
263 : * If it's a JOIN USING, usingNames holds the alias names selected for the
264 : * merged columns (these might be different from the original USING list,
265 : * if we had to modify names to achieve uniqueness).
266 : */
267 : int leftrti; /* rangetable index of left child */
268 : int rightrti; /* rangetable index of right child */
269 : int *leftattnos; /* left-child varattnos of join cols, or 0 */
270 : int *rightattnos; /* right-child varattnos of join cols, or 0 */
271 : List *usingNames; /* names assigned to merged columns */
272 : } deparse_columns;
273 :
274 : /* This macro is analogous to rt_fetch(), but for deparse_columns structs */
275 : #define deparse_columns_fetch(rangetable_index, dpns) \
276 : ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
277 :
278 : /*
279 : * Entry in set_rtable_names' hash table
280 : */
281 : typedef struct
282 : {
283 : char name[NAMEDATALEN]; /* Hash key --- must be first */
284 : int counter; /* Largest addition used so far for name */
285 : } NameHashEntry;
286 :
287 :
288 : /* ----------
289 : * Global data
290 : * ----------
291 : */
292 : static SPIPlanPtr plan_getrulebyoid = NULL;
293 : static const char *query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
294 : static SPIPlanPtr plan_getviewrule = NULL;
295 : static const char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
296 :
297 : /* GUC parameters */
298 : bool quote_all_identifiers = false;
299 :
300 :
301 : /* ----------
302 : * Local functions
303 : *
304 : * Most of these functions used to use fixed-size buffers to build their
305 : * results. Now, they take an (already initialized) StringInfo object
306 : * as a parameter, and append their text output to its contents.
307 : * ----------
308 : */
309 : static char *deparse_expression_pretty(Node *expr, List *dpcontext,
310 : bool forceprefix, bool showimplicit,
311 : int prettyFlags, int startIndent);
312 : static char *pg_get_viewdef_worker(Oid viewoid,
313 : int prettyFlags, int wrapColumn);
314 : static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
315 : static void decompile_column_index_array(Datum column_index_array, Oid relId,
316 : StringInfo buf);
317 : static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
318 : static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
319 : const Oid *excludeOps,
320 : bool attrsOnly, bool showTblSpc,
321 : int prettyFlags, bool missing_ok);
322 : static char *pg_get_statisticsobj_worker(Oid statextid, bool missing_ok);
323 : static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
324 : bool attrsOnly, bool missing_ok);
325 : static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
326 : int prettyFlags, bool missing_ok);
327 : static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname,
328 : int prettyFlags);
329 : static int print_function_arguments(StringInfo buf, HeapTuple proctup,
330 : bool print_table_args, bool print_defaults);
331 : static void print_function_rettype(StringInfo buf, HeapTuple proctup);
332 : static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
333 : static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
334 : Bitmapset *rels_used);
335 : static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
336 : List *parent_namespaces);
337 : static void set_simple_column_names(deparse_namespace *dpns);
338 : static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
339 : static void set_using_names(deparse_namespace *dpns, Node *jtnode,
340 : List *parentUsing);
341 : static void set_relation_column_names(deparse_namespace *dpns,
342 : RangeTblEntry *rte,
343 : deparse_columns *colinfo);
344 : static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
345 : deparse_columns *colinfo);
346 : static bool colname_is_unique(char *colname, deparse_namespace *dpns,
347 : deparse_columns *colinfo);
348 : static char *make_colname_unique(char *colname, deparse_namespace *dpns,
349 : deparse_columns *colinfo);
350 : static void expand_colnames_array_to(deparse_columns *colinfo, int n);
351 : static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
352 : deparse_columns *colinfo);
353 : static void flatten_join_using_qual(Node *qual,
354 : List **leftvars, List **rightvars);
355 : static char *get_rtable_name(int rtindex, deparse_context *context);
356 : static void set_deparse_planstate(deparse_namespace *dpns, PlanState *ps);
357 : static void push_child_plan(deparse_namespace *dpns, PlanState *ps,
358 : deparse_namespace *save_dpns);
359 : static void pop_child_plan(deparse_namespace *dpns,
360 : deparse_namespace *save_dpns);
361 : static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
362 : deparse_namespace *save_dpns);
363 : static void pop_ancestor_plan(deparse_namespace *dpns,
364 : deparse_namespace *save_dpns);
365 : static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
366 : int prettyFlags);
367 : static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
368 : int prettyFlags, int wrapColumn);
369 : static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
370 : TupleDesc resultDesc,
371 : int prettyFlags, int wrapColumn, int startIndent);
372 : static void get_values_def(List *values_lists, deparse_context *context);
373 : static void get_with_clause(Query *query, deparse_context *context);
374 : static void get_select_query_def(Query *query, deparse_context *context,
375 : TupleDesc resultDesc);
376 : static void get_insert_query_def(Query *query, deparse_context *context);
377 : static void get_update_query_def(Query *query, deparse_context *context);
378 : static void get_update_query_targetlist_def(Query *query, List *targetList,
379 : deparse_context *context,
380 : RangeTblEntry *rte);
381 : static void get_delete_query_def(Query *query, deparse_context *context);
382 : static void get_utility_query_def(Query *query, deparse_context *context);
383 : static void get_basic_select_query(Query *query, deparse_context *context,
384 : TupleDesc resultDesc);
385 : static void get_target_list(List *targetList, deparse_context *context,
386 : TupleDesc resultDesc);
387 : static void get_setop_query(Node *setOp, Query *query,
388 : deparse_context *context,
389 : TupleDesc resultDesc);
390 : static Node *get_rule_sortgroupclause(Index ref, List *tlist,
391 : bool force_colno,
392 : deparse_context *context);
393 : static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
394 : bool omit_parens, deparse_context *context);
395 : static void get_rule_orderby(List *orderList, List *targetList,
396 : bool force_colno, deparse_context *context);
397 : static void get_rule_windowclause(Query *query, deparse_context *context);
398 : static void get_rule_windowspec(WindowClause *wc, List *targetList,
399 : deparse_context *context);
400 : static char *get_variable(Var *var, int levelsup, bool istoplevel,
401 : deparse_context *context);
402 : static void get_special_variable(Node *node, deparse_context *context,
403 : void *private);
404 : static void resolve_special_varno(Node *node, deparse_context *context,
405 : void *private,
406 : void (*callback) (Node *, deparse_context *, void *));
407 : static Node *find_param_referent(Param *param, deparse_context *context,
408 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
409 : static void get_parameter(Param *param, deparse_context *context);
410 : static const char *get_simple_binary_op_name(OpExpr *expr);
411 : static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
412 : static void appendContextKeyword(deparse_context *context, const char *str,
413 : int indentBefore, int indentAfter, int indentPlus);
414 : static void removeStringInfoSpaces(StringInfo str);
415 : static void get_rule_expr(Node *node, deparse_context *context,
416 : bool showimplicit);
417 : static void get_rule_expr_toplevel(Node *node, deparse_context *context,
418 : bool showimplicit);
419 : static void get_rule_expr_funccall(Node *node, deparse_context *context,
420 : bool showimplicit);
421 : static bool looks_like_function(Node *node);
422 : static void get_oper_expr(OpExpr *expr, deparse_context *context);
423 : static void get_func_expr(FuncExpr *expr, deparse_context *context,
424 : bool showimplicit);
425 : static void get_agg_expr(Aggref *aggref, deparse_context *context,
426 : Aggref *original_aggref);
427 : static void get_agg_combine_expr(Node *node, deparse_context *context,
428 : void *private);
429 : static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
430 : static void get_coercion_expr(Node *arg, deparse_context *context,
431 : Oid resulttype, int32 resulttypmod,
432 : Node *parentNode);
433 : static void get_const_expr(Const *constval, deparse_context *context,
434 : int showtype);
435 : static void get_const_collation(Const *constval, deparse_context *context);
436 : static void simple_quote_literal(StringInfo buf, const char *val);
437 : static void get_sublink_expr(SubLink *sublink, deparse_context *context);
438 : static void get_tablefunc(TableFunc *tf, deparse_context *context,
439 : bool showimplicit);
440 : static void get_from_clause(Query *query, const char *prefix,
441 : deparse_context *context);
442 : static void get_from_clause_item(Node *jtnode, Query *query,
443 : deparse_context *context);
444 : static void get_column_alias_list(deparse_columns *colinfo,
445 : deparse_context *context);
446 : static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
447 : deparse_columns *colinfo,
448 : deparse_context *context);
449 : static void get_tablesample_def(TableSampleClause *tablesample,
450 : deparse_context *context);
451 : static void get_opclass_name(Oid opclass, Oid actual_datatype,
452 : StringInfo buf);
453 : static Node *processIndirection(Node *node, deparse_context *context);
454 : static void printSubscripts(ArrayRef *aref, deparse_context *context);
455 : static char *get_relation_name(Oid relid);
456 : static char *generate_relation_name(Oid relid, List *namespaces);
457 : static char *generate_qualified_relation_name(Oid relid);
458 : static char *generate_function_name(Oid funcid, int nargs,
459 : List *argnames, Oid *argtypes,
460 : bool has_variadic, bool *use_variadic_p,
461 : ParseExprKind special_exprkind);
462 : static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
463 : static text *string_to_text(char *str);
464 : static char *flatten_reloptions(Oid relid);
465 :
466 : #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
467 :
468 :
469 : /* ----------
470 : * get_ruledef - Do it all and return a text
471 : * that could be used as a statement
472 : * to recreate the rule
473 : * ----------
474 : */
475 : Datum
476 35 : pg_get_ruledef(PG_FUNCTION_ARGS)
477 : {
478 35 : Oid ruleoid = PG_GETARG_OID(0);
479 : int prettyFlags;
480 : char *res;
481 :
482 35 : prettyFlags = PRETTYFLAG_INDENT;
483 :
484 35 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
485 :
486 35 : if (res == NULL)
487 1 : PG_RETURN_NULL();
488 :
489 34 : PG_RETURN_TEXT_P(string_to_text(res));
490 : }
491 :
492 :
493 : Datum
494 14 : pg_get_ruledef_ext(PG_FUNCTION_ARGS)
495 : {
496 14 : Oid ruleoid = PG_GETARG_OID(0);
497 14 : bool pretty = PG_GETARG_BOOL(1);
498 : int prettyFlags;
499 : char *res;
500 :
501 14 : prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
502 :
503 14 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
504 :
505 14 : if (res == NULL)
506 0 : PG_RETURN_NULL();
507 :
508 14 : PG_RETURN_TEXT_P(string_to_text(res));
509 : }
510 :
511 :
512 : static char *
513 49 : pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
514 : {
515 : Datum args[1];
516 : char nulls[1];
517 : int spirc;
518 : HeapTuple ruletup;
519 : TupleDesc rulettc;
520 : StringInfoData buf;
521 :
522 : /*
523 : * Do this first so that string is alloc'd in outer context not SPI's.
524 : */
525 49 : initStringInfo(&buf);
526 :
527 : /*
528 : * Connect to SPI manager
529 : */
530 49 : if (SPI_connect() != SPI_OK_CONNECT)
531 0 : elog(ERROR, "SPI_connect failed");
532 :
533 : /*
534 : * On the first call prepare the plan to lookup pg_rewrite. We read
535 : * pg_rewrite over the SPI manager instead of using the syscache to be
536 : * checked for read access on pg_rewrite.
537 : */
538 49 : if (plan_getrulebyoid == NULL)
539 : {
540 : Oid argtypes[1];
541 : SPIPlanPtr plan;
542 :
543 4 : argtypes[0] = OIDOID;
544 4 : plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
545 4 : if (plan == NULL)
546 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
547 4 : SPI_keepplan(plan);
548 4 : plan_getrulebyoid = plan;
549 : }
550 :
551 : /*
552 : * Get the pg_rewrite tuple for this rule
553 : */
554 49 : args[0] = ObjectIdGetDatum(ruleoid);
555 49 : nulls[0] = ' ';
556 49 : spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
557 49 : if (spirc != SPI_OK_SELECT)
558 0 : elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
559 49 : if (SPI_processed != 1)
560 : {
561 : /*
562 : * There is no tuple data available here, just keep the output buffer
563 : * empty.
564 : */
565 : }
566 : else
567 : {
568 : /*
569 : * Get the rule's definition and put it into executor's memory
570 : */
571 48 : ruletup = SPI_tuptable->vals[0];
572 48 : rulettc = SPI_tuptable->tupdesc;
573 48 : make_ruledef(&buf, ruletup, rulettc, prettyFlags);
574 : }
575 :
576 : /*
577 : * Disconnect from SPI manager
578 : */
579 49 : if (SPI_finish() != SPI_OK_FINISH)
580 0 : elog(ERROR, "SPI_finish failed");
581 :
582 49 : if (buf.len == 0)
583 1 : return NULL;
584 :
585 48 : return buf.data;
586 : }
587 :
588 :
589 : /* ----------
590 : * get_viewdef - Mainly the same thing, but we
591 : * only return the SELECT part of a view
592 : * ----------
593 : */
594 : Datum
595 106 : pg_get_viewdef(PG_FUNCTION_ARGS)
596 : {
597 : /* By OID */
598 106 : Oid viewoid = PG_GETARG_OID(0);
599 : int prettyFlags;
600 : char *res;
601 :
602 106 : prettyFlags = PRETTYFLAG_INDENT;
603 :
604 106 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
605 :
606 106 : if (res == NULL)
607 1 : PG_RETURN_NULL();
608 :
609 105 : PG_RETURN_TEXT_P(string_to_text(res));
610 : }
611 :
612 :
613 : Datum
614 53 : pg_get_viewdef_ext(PG_FUNCTION_ARGS)
615 : {
616 : /* By OID */
617 53 : Oid viewoid = PG_GETARG_OID(0);
618 53 : bool pretty = PG_GETARG_BOOL(1);
619 : int prettyFlags;
620 : char *res;
621 :
622 53 : prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
623 :
624 53 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
625 :
626 53 : if (res == NULL)
627 0 : PG_RETURN_NULL();
628 :
629 53 : PG_RETURN_TEXT_P(string_to_text(res));
630 : }
631 :
632 : Datum
633 1 : pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
634 : {
635 : /* By OID */
636 1 : Oid viewoid = PG_GETARG_OID(0);
637 1 : int wrap = PG_GETARG_INT32(1);
638 : int prettyFlags;
639 : char *res;
640 :
641 : /* calling this implies we want pretty printing */
642 1 : prettyFlags = PRETTYFLAG_PAREN | PRETTYFLAG_INDENT;
643 :
644 1 : res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
645 :
646 1 : if (res == NULL)
647 0 : PG_RETURN_NULL();
648 :
649 1 : PG_RETURN_TEXT_P(string_to_text(res));
650 : }
651 :
652 : Datum
653 3 : pg_get_viewdef_name(PG_FUNCTION_ARGS)
654 : {
655 : /* By qualified name */
656 3 : text *viewname = PG_GETARG_TEXT_PP(0);
657 : int prettyFlags;
658 : RangeVar *viewrel;
659 : Oid viewoid;
660 : char *res;
661 :
662 3 : prettyFlags = PRETTYFLAG_INDENT;
663 :
664 : /* Look up view name. Can't lock it - we might not have privileges. */
665 3 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
666 3 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
667 :
668 3 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
669 :
670 3 : if (res == NULL)
671 0 : PG_RETURN_NULL();
672 :
673 3 : PG_RETURN_TEXT_P(string_to_text(res));
674 : }
675 :
676 :
677 : Datum
678 52 : pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
679 : {
680 : /* By qualified name */
681 52 : text *viewname = PG_GETARG_TEXT_PP(0);
682 52 : bool pretty = PG_GETARG_BOOL(1);
683 : int prettyFlags;
684 : RangeVar *viewrel;
685 : Oid viewoid;
686 : char *res;
687 :
688 52 : prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
689 :
690 : /* Look up view name. Can't lock it - we might not have privileges. */
691 52 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
692 52 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
693 :
694 52 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
695 :
696 52 : if (res == NULL)
697 0 : PG_RETURN_NULL();
698 :
699 52 : PG_RETURN_TEXT_P(string_to_text(res));
700 : }
701 :
702 : /*
703 : * Common code for by-OID and by-name variants of pg_get_viewdef
704 : */
705 : static char *
706 215 : pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
707 : {
708 : Datum args[2];
709 : char nulls[2];
710 : int spirc;
711 : HeapTuple ruletup;
712 : TupleDesc rulettc;
713 : StringInfoData buf;
714 :
715 : /*
716 : * Do this first so that string is alloc'd in outer context not SPI's.
717 : */
718 215 : initStringInfo(&buf);
719 :
720 : /*
721 : * Connect to SPI manager
722 : */
723 215 : if (SPI_connect() != SPI_OK_CONNECT)
724 0 : elog(ERROR, "SPI_connect failed");
725 :
726 : /*
727 : * On the first call prepare the plan to lookup pg_rewrite. We read
728 : * pg_rewrite over the SPI manager instead of using the syscache to be
729 : * checked for read access on pg_rewrite.
730 : */
731 215 : if (plan_getviewrule == NULL)
732 : {
733 : Oid argtypes[2];
734 : SPIPlanPtr plan;
735 :
736 14 : argtypes[0] = OIDOID;
737 14 : argtypes[1] = NAMEOID;
738 14 : plan = SPI_prepare(query_getviewrule, 2, argtypes);
739 14 : if (plan == NULL)
740 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
741 14 : SPI_keepplan(plan);
742 14 : plan_getviewrule = plan;
743 : }
744 :
745 : /*
746 : * Get the pg_rewrite tuple for the view's SELECT rule
747 : */
748 215 : args[0] = ObjectIdGetDatum(viewoid);
749 215 : args[1] = DirectFunctionCall1(namein, CStringGetDatum(ViewSelectRuleName));
750 215 : nulls[0] = ' ';
751 215 : nulls[1] = ' ';
752 215 : spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
753 215 : if (spirc != SPI_OK_SELECT)
754 0 : elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
755 215 : if (SPI_processed != 1)
756 : {
757 : /*
758 : * There is no tuple data available here, just keep the output buffer
759 : * empty.
760 : */
761 : }
762 : else
763 : {
764 : /*
765 : * Get the rule's definition and put it into executor's memory
766 : */
767 214 : ruletup = SPI_tuptable->vals[0];
768 214 : rulettc = SPI_tuptable->tupdesc;
769 214 : make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
770 : }
771 :
772 : /*
773 : * Disconnect from SPI manager
774 : */
775 215 : if (SPI_finish() != SPI_OK_FINISH)
776 0 : elog(ERROR, "SPI_finish failed");
777 :
778 215 : if (buf.len == 0)
779 1 : return NULL;
780 :
781 214 : return buf.data;
782 : }
783 :
784 : /* ----------
785 : * get_triggerdef - Get the definition of a trigger
786 : * ----------
787 : */
788 : Datum
789 2 : pg_get_triggerdef(PG_FUNCTION_ARGS)
790 : {
791 2 : Oid trigid = PG_GETARG_OID(0);
792 : char *res;
793 :
794 2 : res = pg_get_triggerdef_worker(trigid, false);
795 :
796 2 : if (res == NULL)
797 1 : PG_RETURN_NULL();
798 :
799 1 : PG_RETURN_TEXT_P(string_to_text(res));
800 : }
801 :
802 : Datum
803 19 : pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
804 : {
805 19 : Oid trigid = PG_GETARG_OID(0);
806 19 : bool pretty = PG_GETARG_BOOL(1);
807 : char *res;
808 :
809 19 : res = pg_get_triggerdef_worker(trigid, pretty);
810 :
811 19 : if (res == NULL)
812 0 : PG_RETURN_NULL();
813 :
814 19 : PG_RETURN_TEXT_P(string_to_text(res));
815 : }
816 :
817 : static char *
818 21 : pg_get_triggerdef_worker(Oid trigid, bool pretty)
819 : {
820 : HeapTuple ht_trig;
821 : Form_pg_trigger trigrec;
822 : StringInfoData buf;
823 : Relation tgrel;
824 : ScanKeyData skey[1];
825 : SysScanDesc tgscan;
826 21 : int findx = 0;
827 : char *tgname;
828 : char *tgoldtable;
829 : char *tgnewtable;
830 : Oid argtypes[1]; /* dummy */
831 : Datum value;
832 : bool isnull;
833 :
834 : /*
835 : * Fetch the pg_trigger tuple by the Oid of the trigger
836 : */
837 21 : tgrel = heap_open(TriggerRelationId, AccessShareLock);
838 :
839 21 : ScanKeyInit(&skey[0],
840 : ObjectIdAttributeNumber,
841 : BTEqualStrategyNumber, F_OIDEQ,
842 : ObjectIdGetDatum(trigid));
843 :
844 21 : tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
845 : NULL, 1, skey);
846 :
847 21 : ht_trig = systable_getnext(tgscan);
848 :
849 21 : if (!HeapTupleIsValid(ht_trig))
850 : {
851 1 : systable_endscan(tgscan);
852 1 : heap_close(tgrel, AccessShareLock);
853 1 : return NULL;
854 : }
855 :
856 20 : trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
857 :
858 : /*
859 : * Start the trigger definition. Note that the trigger's name should never
860 : * be schema-qualified, but the trigger rel's name may be.
861 : */
862 20 : initStringInfo(&buf);
863 :
864 20 : tgname = NameStr(trigrec->tgname);
865 40 : appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
866 20 : OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
867 : quote_identifier(tgname));
868 :
869 20 : if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
870 9 : appendStringInfoString(&buf, "BEFORE");
871 11 : else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
872 7 : appendStringInfoString(&buf, "AFTER");
873 4 : else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
874 4 : appendStringInfoString(&buf, "INSTEAD OF");
875 : else
876 0 : elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
877 :
878 20 : if (TRIGGER_FOR_INSERT(trigrec->tgtype))
879 : {
880 5 : appendStringInfoString(&buf, " INSERT");
881 5 : findx++;
882 : }
883 20 : if (TRIGGER_FOR_DELETE(trigrec->tgtype))
884 : {
885 5 : if (findx > 0)
886 0 : appendStringInfoString(&buf, " OR DELETE");
887 : else
888 5 : appendStringInfoString(&buf, " DELETE");
889 5 : findx++;
890 : }
891 20 : if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
892 : {
893 10 : if (findx > 0)
894 0 : appendStringInfoString(&buf, " OR UPDATE");
895 : else
896 10 : appendStringInfoString(&buf, " UPDATE");
897 10 : findx++;
898 : /* tgattr is first var-width field, so OK to access directly */
899 10 : if (trigrec->tgattr.dim1 > 0)
900 : {
901 : int i;
902 :
903 4 : appendStringInfoString(&buf, " OF ");
904 9 : for (i = 0; i < trigrec->tgattr.dim1; i++)
905 : {
906 : char *attname;
907 :
908 5 : if (i > 0)
909 1 : appendStringInfoString(&buf, ", ");
910 5 : attname = get_relid_attribute_name(trigrec->tgrelid,
911 5 : trigrec->tgattr.values[i]);
912 5 : appendStringInfoString(&buf, quote_identifier(attname));
913 : }
914 : }
915 : }
916 20 : if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
917 : {
918 0 : if (findx > 0)
919 0 : appendStringInfoString(&buf, " OR TRUNCATE");
920 : else
921 0 : appendStringInfoString(&buf, " TRUNCATE");
922 0 : findx++;
923 : }
924 20 : appendStringInfo(&buf, " ON %s ",
925 : generate_relation_name(trigrec->tgrelid, NIL));
926 :
927 20 : if (OidIsValid(trigrec->tgconstraint))
928 : {
929 0 : if (OidIsValid(trigrec->tgconstrrelid))
930 0 : appendStringInfo(&buf, "FROM %s ",
931 : generate_relation_name(trigrec->tgconstrrelid, NIL));
932 0 : if (!trigrec->tgdeferrable)
933 0 : appendStringInfoString(&buf, "NOT ");
934 0 : appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
935 0 : if (trigrec->tginitdeferred)
936 0 : appendStringInfoString(&buf, "DEFERRED ");
937 : else
938 0 : appendStringInfoString(&buf, "IMMEDIATE ");
939 : }
940 :
941 20 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
942 : tgrel->rd_att, &isnull);
943 20 : if (!isnull)
944 0 : tgoldtable = NameStr(*((NameData *) DatumGetPointer(value)));
945 : else
946 20 : tgoldtable = NULL;
947 20 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
948 : tgrel->rd_att, &isnull);
949 20 : if (!isnull)
950 0 : tgnewtable = NameStr(*((NameData *) DatumGetPointer(value)));
951 : else
952 20 : tgnewtable = NULL;
953 20 : if (tgoldtable != NULL || tgnewtable != NULL)
954 : {
955 0 : appendStringInfoString(&buf, "REFERENCING ");
956 0 : if (tgoldtable != NULL)
957 0 : appendStringInfo(&buf, "OLD TABLE AS %s ", tgoldtable);
958 0 : if (tgnewtable != NULL)
959 0 : appendStringInfo(&buf, "NEW TABLE AS %s ", tgnewtable);
960 : }
961 :
962 20 : if (TRIGGER_FOR_ROW(trigrec->tgtype))
963 8 : appendStringInfoString(&buf, "FOR EACH ROW ");
964 : else
965 12 : appendStringInfoString(&buf, "FOR EACH STATEMENT ");
966 :
967 : /* If the trigger has a WHEN qualification, add that */
968 20 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
969 : tgrel->rd_att, &isnull);
970 20 : if (!isnull)
971 : {
972 : Node *qual;
973 : char relkind;
974 : deparse_context context;
975 : deparse_namespace dpns;
976 : RangeTblEntry *oldrte;
977 : RangeTblEntry *newrte;
978 :
979 3 : appendStringInfoString(&buf, "WHEN (");
980 :
981 3 : qual = stringToNode(TextDatumGetCString(value));
982 :
983 3 : relkind = get_rel_relkind(trigrec->tgrelid);
984 :
985 : /* Build minimal OLD and NEW RTEs for the rel */
986 3 : oldrte = makeNode(RangeTblEntry);
987 3 : oldrte->rtekind = RTE_RELATION;
988 3 : oldrte->relid = trigrec->tgrelid;
989 3 : oldrte->relkind = relkind;
990 3 : oldrte->alias = makeAlias("old", NIL);
991 3 : oldrte->eref = oldrte->alias;
992 3 : oldrte->lateral = false;
993 3 : oldrte->inh = false;
994 3 : oldrte->inFromCl = true;
995 :
996 3 : newrte = makeNode(RangeTblEntry);
997 3 : newrte->rtekind = RTE_RELATION;
998 3 : newrte->relid = trigrec->tgrelid;
999 3 : newrte->relkind = relkind;
1000 3 : newrte->alias = makeAlias("new", NIL);
1001 3 : newrte->eref = newrte->alias;
1002 3 : newrte->lateral = false;
1003 3 : newrte->inh = false;
1004 3 : newrte->inFromCl = true;
1005 :
1006 : /* Build two-element rtable */
1007 3 : memset(&dpns, 0, sizeof(dpns));
1008 3 : dpns.rtable = list_make2(oldrte, newrte);
1009 3 : dpns.ctes = NIL;
1010 3 : set_rtable_names(&dpns, NIL, NULL);
1011 3 : set_simple_column_names(&dpns);
1012 :
1013 : /* Set up context with one-deep namespace stack */
1014 3 : context.buf = &buf;
1015 3 : context.namespaces = list_make1(&dpns);
1016 3 : context.windowClause = NIL;
1017 3 : context.windowTList = NIL;
1018 3 : context.varprefix = true;
1019 3 : context.prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
1020 3 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
1021 3 : context.indentLevel = PRETTYINDENT_STD;
1022 3 : context.special_exprkind = EXPR_KIND_NONE;
1023 :
1024 3 : get_rule_expr(qual, &context, false);
1025 :
1026 3 : appendStringInfoString(&buf, ") ");
1027 : }
1028 :
1029 20 : appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
1030 : generate_function_name(trigrec->tgfoid, 0,
1031 : NIL, argtypes,
1032 : false, NULL, EXPR_KIND_NONE));
1033 :
1034 20 : if (trigrec->tgnargs > 0)
1035 : {
1036 : char *p;
1037 : int i;
1038 :
1039 20 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
1040 : tgrel->rd_att, &isnull);
1041 20 : if (isnull)
1042 0 : elog(ERROR, "tgargs is null for trigger %u", trigid);
1043 20 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
1044 40 : for (i = 0; i < trigrec->tgnargs; i++)
1045 : {
1046 20 : if (i > 0)
1047 0 : appendStringInfoString(&buf, ", ");
1048 20 : simple_quote_literal(&buf, p);
1049 : /* advance p to next string embedded in tgargs */
1050 379 : while (*p)
1051 339 : p++;
1052 20 : p++;
1053 : }
1054 : }
1055 :
1056 : /* We deliberately do not put semi-colon at end */
1057 20 : appendStringInfoChar(&buf, ')');
1058 :
1059 : /* Clean up */
1060 20 : systable_endscan(tgscan);
1061 :
1062 20 : heap_close(tgrel, AccessShareLock);
1063 :
1064 20 : return buf.data;
1065 : }
1066 :
1067 : /* ----------
1068 : * get_indexdef - Get the definition of an index
1069 : *
1070 : * In the extended version, there is a colno argument as well as pretty bool.
1071 : * if colno == 0, we want a complete index definition.
1072 : * if colno > 0, we only want the Nth index key's variable or expression.
1073 : *
1074 : * Note that the SQL-function versions of this omit any info about the
1075 : * index tablespace; this is intentional because pg_dump wants it that way.
1076 : * However pg_get_indexdef_string() includes the index tablespace.
1077 : * ----------
1078 : */
1079 : Datum
1080 5 : pg_get_indexdef(PG_FUNCTION_ARGS)
1081 : {
1082 5 : Oid indexrelid = PG_GETARG_OID(0);
1083 : int prettyFlags;
1084 : char *res;
1085 :
1086 5 : prettyFlags = PRETTYFLAG_INDENT;
1087 :
1088 5 : res = pg_get_indexdef_worker(indexrelid, 0, NULL, false, false,
1089 : prettyFlags, true);
1090 :
1091 5 : if (res == NULL)
1092 1 : PG_RETURN_NULL();
1093 :
1094 4 : PG_RETURN_TEXT_P(string_to_text(res));
1095 : }
1096 :
1097 : Datum
1098 86 : pg_get_indexdef_ext(PG_FUNCTION_ARGS)
1099 : {
1100 86 : Oid indexrelid = PG_GETARG_OID(0);
1101 86 : int32 colno = PG_GETARG_INT32(1);
1102 86 : bool pretty = PG_GETARG_BOOL(2);
1103 : int prettyFlags;
1104 : char *res;
1105 :
1106 86 : prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
1107 :
1108 86 : res = pg_get_indexdef_worker(indexrelid, colno, NULL, colno != 0, false,
1109 : prettyFlags, true);
1110 :
1111 86 : if (res == NULL)
1112 0 : PG_RETURN_NULL();
1113 :
1114 86 : PG_RETURN_TEXT_P(string_to_text(res));
1115 : }
1116 :
1117 : /*
1118 : * Internal version for use by ALTER TABLE.
1119 : * Includes a tablespace clause in the result.
1120 : * Returns a palloc'd C string; no pretty-printing.
1121 : */
1122 : char *
1123 12 : pg_get_indexdef_string(Oid indexrelid)
1124 : {
1125 12 : return pg_get_indexdef_worker(indexrelid, 0, NULL, false, true, 0, false);
1126 : }
1127 :
1128 : /* Internal version that just reports the column definitions */
1129 : char *
1130 55 : pg_get_indexdef_columns(Oid indexrelid, bool pretty)
1131 : {
1132 : int prettyFlags;
1133 :
1134 55 : prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
1135 55 : return pg_get_indexdef_worker(indexrelid, 0, NULL, true, false,
1136 : prettyFlags, false);
1137 : }
1138 :
1139 : /*
1140 : * Internal workhorse to decompile an index definition.
1141 : *
1142 : * This is now used for exclusion constraints as well: if excludeOps is not
1143 : * NULL then it points to an array of exclusion operator OIDs.
1144 : */
1145 : static char *
1146 159 : pg_get_indexdef_worker(Oid indexrelid, int colno,
1147 : const Oid *excludeOps,
1148 : bool attrsOnly, bool showTblSpc,
1149 : int prettyFlags, bool missing_ok)
1150 : {
1151 : /* might want a separate isConstraint parameter later */
1152 159 : bool isConstraint = (excludeOps != NULL);
1153 : HeapTuple ht_idx;
1154 : HeapTuple ht_idxrel;
1155 : HeapTuple ht_am;
1156 : Form_pg_index idxrec;
1157 : Form_pg_class idxrelrec;
1158 : Form_pg_am amrec;
1159 : IndexAmRoutine *amroutine;
1160 : List *indexprs;
1161 : ListCell *indexpr_item;
1162 : List *context;
1163 : Oid indrelid;
1164 : int keyno;
1165 : Datum indcollDatum;
1166 : Datum indclassDatum;
1167 : Datum indoptionDatum;
1168 : bool isnull;
1169 : oidvector *indcollation;
1170 : oidvector *indclass;
1171 : int2vector *indoption;
1172 : StringInfoData buf;
1173 : char *str;
1174 : char *sep;
1175 :
1176 : /*
1177 : * Fetch the pg_index tuple by the Oid of the index
1178 : */
1179 159 : ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
1180 159 : if (!HeapTupleIsValid(ht_idx))
1181 : {
1182 1 : if (missing_ok)
1183 1 : return NULL;
1184 0 : elog(ERROR, "cache lookup failed for index %u", indexrelid);
1185 : }
1186 158 : idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
1187 :
1188 158 : indrelid = idxrec->indrelid;
1189 158 : Assert(indexrelid == idxrec->indexrelid);
1190 :
1191 : /* Must get indcollation, indclass, and indoption the hard way */
1192 158 : indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
1193 : Anum_pg_index_indcollation, &isnull);
1194 158 : Assert(!isnull);
1195 158 : indcollation = (oidvector *) DatumGetPointer(indcollDatum);
1196 :
1197 158 : indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
1198 : Anum_pg_index_indclass, &isnull);
1199 158 : Assert(!isnull);
1200 158 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
1201 :
1202 158 : indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
1203 : Anum_pg_index_indoption, &isnull);
1204 158 : Assert(!isnull);
1205 158 : indoption = (int2vector *) DatumGetPointer(indoptionDatum);
1206 :
1207 : /*
1208 : * Fetch the pg_class tuple of the index relation
1209 : */
1210 158 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
1211 158 : if (!HeapTupleIsValid(ht_idxrel))
1212 0 : elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1213 158 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
1214 :
1215 : /*
1216 : * Fetch the pg_am tuple of the index' access method
1217 : */
1218 158 : ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
1219 158 : if (!HeapTupleIsValid(ht_am))
1220 0 : elog(ERROR, "cache lookup failed for access method %u",
1221 : idxrelrec->relam);
1222 158 : amrec = (Form_pg_am) GETSTRUCT(ht_am);
1223 :
1224 : /* Fetch the index AM's API struct */
1225 158 : amroutine = GetIndexAmRoutine(amrec->amhandler);
1226 :
1227 : /*
1228 : * Get the index expressions, if any. (NOTE: we do not use the relcache
1229 : * versions of the expressions and predicate, because we want to display
1230 : * non-const-folded expressions.)
1231 : */
1232 158 : if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
1233 : {
1234 : Datum exprsDatum;
1235 : bool isnull;
1236 : char *exprsString;
1237 :
1238 14 : exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
1239 : Anum_pg_index_indexprs, &isnull);
1240 14 : Assert(!isnull);
1241 14 : exprsString = TextDatumGetCString(exprsDatum);
1242 14 : indexprs = (List *) stringToNode(exprsString);
1243 14 : pfree(exprsString);
1244 : }
1245 : else
1246 144 : indexprs = NIL;
1247 :
1248 158 : indexpr_item = list_head(indexprs);
1249 :
1250 158 : context = deparse_context_for(get_relation_name(indrelid), indrelid);
1251 :
1252 : /*
1253 : * Start the index definition. Note that the index's name should never be
1254 : * schema-qualified, but the indexed rel's name may be.
1255 : */
1256 158 : initStringInfo(&buf);
1257 :
1258 158 : if (!attrsOnly)
1259 : {
1260 86 : if (!isConstraint)
1261 255 : appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
1262 85 : idxrec->indisunique ? "UNIQUE " : "",
1263 85 : quote_identifier(NameStr(idxrelrec->relname)),
1264 : generate_relation_name(indrelid, NIL),
1265 85 : quote_identifier(NameStr(amrec->amname)));
1266 : else /* currently, must be EXCLUDE constraint */
1267 1 : appendStringInfo(&buf, "EXCLUDE USING %s (",
1268 1 : quote_identifier(NameStr(amrec->amname)));
1269 : }
1270 :
1271 : /*
1272 : * Report the indexed attributes
1273 : */
1274 158 : sep = "";
1275 362 : for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1276 : {
1277 204 : AttrNumber attnum = idxrec->indkey.values[keyno];
1278 204 : int16 opt = indoption->values[keyno];
1279 : Oid keycoltype;
1280 : Oid keycolcollation;
1281 :
1282 204 : if (!colno)
1283 183 : appendStringInfoString(&buf, sep);
1284 204 : sep = ", ";
1285 :
1286 204 : if (attnum != 0)
1287 : {
1288 : /* Simple index column */
1289 : char *attname;
1290 : int32 keycoltypmod;
1291 :
1292 190 : attname = get_relid_attribute_name(indrelid, attnum);
1293 190 : if (!colno || colno == keyno + 1)
1294 186 : appendStringInfoString(&buf, quote_identifier(attname));
1295 190 : get_atttypetypmodcoll(indrelid, attnum,
1296 : &keycoltype, &keycoltypmod,
1297 : &keycolcollation);
1298 : }
1299 : else
1300 : {
1301 : /* expressional index */
1302 : Node *indexkey;
1303 :
1304 14 : if (indexpr_item == NULL)
1305 0 : elog(ERROR, "too few entries in indexprs list");
1306 14 : indexkey = (Node *) lfirst(indexpr_item);
1307 14 : indexpr_item = lnext(indexpr_item);
1308 : /* Deparse */
1309 14 : str = deparse_expression_pretty(indexkey, context, false, false,
1310 : prettyFlags, 0);
1311 14 : if (!colno || colno == keyno + 1)
1312 : {
1313 : /* Need parens if it's not a bare function call */
1314 14 : if (looks_like_function(indexkey))
1315 1 : appendStringInfoString(&buf, str);
1316 : else
1317 13 : appendStringInfo(&buf, "(%s)", str);
1318 : }
1319 14 : keycoltype = exprType(indexkey);
1320 14 : keycolcollation = exprCollation(indexkey);
1321 : }
1322 :
1323 204 : if (!attrsOnly && (!colno || colno == keyno + 1))
1324 : {
1325 : Oid indcoll;
1326 :
1327 : /* Add collation, if not default for column */
1328 117 : indcoll = indcollation->values[keyno];
1329 117 : if (OidIsValid(indcoll) && indcoll != keycolcollation)
1330 3 : appendStringInfo(&buf, " COLLATE %s",
1331 : generate_collation_name((indcoll)));
1332 :
1333 : /* Add the operator class name, if not default */
1334 117 : get_opclass_name(indclass->values[keyno], keycoltype, &buf);
1335 :
1336 : /* Add options if relevant */
1337 117 : if (amroutine->amcanorder)
1338 : {
1339 : /* if it supports sort ordering, report DESC and NULLS opts */
1340 112 : if (opt & INDOPTION_DESC)
1341 : {
1342 0 : appendStringInfoString(&buf, " DESC");
1343 : /* NULLS FIRST is the default in this case */
1344 0 : if (!(opt & INDOPTION_NULLS_FIRST))
1345 0 : appendStringInfoString(&buf, " NULLS LAST");
1346 : }
1347 : else
1348 : {
1349 112 : if (opt & INDOPTION_NULLS_FIRST)
1350 0 : appendStringInfoString(&buf, " NULLS FIRST");
1351 : }
1352 : }
1353 :
1354 : /* Add the exclusion operator if relevant */
1355 117 : if (excludeOps != NULL)
1356 2 : appendStringInfo(&buf, " WITH %s",
1357 1 : generate_operator_name(excludeOps[keyno],
1358 : keycoltype,
1359 : keycoltype));
1360 : }
1361 : }
1362 :
1363 158 : if (!attrsOnly)
1364 : {
1365 86 : appendStringInfoChar(&buf, ')');
1366 :
1367 : /*
1368 : * If it has options, append "WITH (options)"
1369 : */
1370 86 : str = flatten_reloptions(indexrelid);
1371 86 : if (str)
1372 : {
1373 0 : appendStringInfo(&buf, " WITH (%s)", str);
1374 0 : pfree(str);
1375 : }
1376 :
1377 : /*
1378 : * Print tablespace, but only if requested
1379 : */
1380 86 : if (showTblSpc)
1381 : {
1382 : Oid tblspc;
1383 :
1384 12 : tblspc = get_rel_tablespace(indexrelid);
1385 12 : if (!OidIsValid(tblspc))
1386 7 : tblspc = MyDatabaseTableSpace;
1387 12 : if (isConstraint)
1388 0 : appendStringInfoString(&buf, " USING INDEX");
1389 12 : appendStringInfo(&buf, " TABLESPACE %s",
1390 12 : quote_identifier(get_tablespace_name(tblspc)));
1391 : }
1392 :
1393 : /*
1394 : * If it's a partial index, decompile and append the predicate
1395 : */
1396 86 : if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
1397 : {
1398 : Node *node;
1399 : Datum predDatum;
1400 : bool isnull;
1401 : char *predString;
1402 :
1403 : /* Convert text string to node tree */
1404 8 : predDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
1405 : Anum_pg_index_indpred, &isnull);
1406 8 : Assert(!isnull);
1407 8 : predString = TextDatumGetCString(predDatum);
1408 8 : node = (Node *) stringToNode(predString);
1409 8 : pfree(predString);
1410 :
1411 : /* Deparse */
1412 8 : str = deparse_expression_pretty(node, context, false, false,
1413 : prettyFlags, 0);
1414 8 : if (isConstraint)
1415 0 : appendStringInfo(&buf, " WHERE (%s)", str);
1416 : else
1417 8 : appendStringInfo(&buf, " WHERE %s", str);
1418 : }
1419 : }
1420 :
1421 : /* Clean up */
1422 158 : ReleaseSysCache(ht_idx);
1423 158 : ReleaseSysCache(ht_idxrel);
1424 158 : ReleaseSysCache(ht_am);
1425 :
1426 158 : return buf.data;
1427 : }
1428 :
1429 : /*
1430 : * pg_get_statisticsobjdef
1431 : * Get the definition of an extended statistics object
1432 : */
1433 : Datum
1434 2 : pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
1435 : {
1436 2 : Oid statextid = PG_GETARG_OID(0);
1437 : char *res;
1438 :
1439 2 : res = pg_get_statisticsobj_worker(statextid, true);
1440 :
1441 2 : if (res == NULL)
1442 1 : PG_RETURN_NULL();
1443 :
1444 1 : PG_RETURN_TEXT_P(string_to_text(res));
1445 : }
1446 :
1447 : /*
1448 : * Internal workhorse to decompile an extended statistics object.
1449 : */
1450 : static char *
1451 2 : pg_get_statisticsobj_worker(Oid statextid, bool missing_ok)
1452 : {
1453 : Form_pg_statistic_ext statextrec;
1454 : HeapTuple statexttup;
1455 : StringInfoData buf;
1456 : int colno;
1457 : char *nsp;
1458 : ArrayType *arr;
1459 : char *enabled;
1460 : Datum datum;
1461 : bool isnull;
1462 : bool ndistinct_enabled;
1463 : bool dependencies_enabled;
1464 : int i;
1465 :
1466 2 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1467 :
1468 2 : if (!HeapTupleIsValid(statexttup))
1469 : {
1470 1 : if (missing_ok)
1471 1 : return NULL;
1472 0 : elog(ERROR, "cache lookup failed for statistics object %u", statextid);
1473 : }
1474 :
1475 1 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1476 :
1477 1 : initStringInfo(&buf);
1478 :
1479 1 : nsp = get_namespace_name(statextrec->stxnamespace);
1480 1 : appendStringInfo(&buf, "CREATE STATISTICS %s",
1481 : quote_qualified_identifier(nsp,
1482 1 : NameStr(statextrec->stxname)));
1483 :
1484 : /*
1485 : * Decode the stxkind column so that we know which stats types to print.
1486 : */
1487 1 : datum = SysCacheGetAttr(STATEXTOID, statexttup,
1488 : Anum_pg_statistic_ext_stxkind, &isnull);
1489 1 : Assert(!isnull);
1490 1 : arr = DatumGetArrayTypeP(datum);
1491 2 : if (ARR_NDIM(arr) != 1 ||
1492 2 : ARR_HASNULL(arr) ||
1493 1 : ARR_ELEMTYPE(arr) != CHAROID)
1494 0 : elog(ERROR, "stxkind is not a 1-D char array");
1495 1 : enabled = (char *) ARR_DATA_PTR(arr);
1496 :
1497 1 : ndistinct_enabled = false;
1498 1 : dependencies_enabled = false;
1499 :
1500 3 : for (i = 0; i < ARR_DIMS(arr)[0]; i++)
1501 : {
1502 2 : if (enabled[i] == STATS_EXT_NDISTINCT)
1503 1 : ndistinct_enabled = true;
1504 2 : if (enabled[i] == STATS_EXT_DEPENDENCIES)
1505 1 : dependencies_enabled = true;
1506 : }
1507 :
1508 : /*
1509 : * If any option is disabled, then we'll need to append the types clause
1510 : * to show which options are enabled. We omit the types clause on purpose
1511 : * when all options are enabled, so a pg_dump/pg_restore will create all
1512 : * statistics types on a newer postgres version, if the statistics had all
1513 : * options enabled on the original version.
1514 : */
1515 1 : if (!ndistinct_enabled || !dependencies_enabled)
1516 : {
1517 0 : appendStringInfoString(&buf, " (");
1518 0 : if (ndistinct_enabled)
1519 0 : appendStringInfoString(&buf, "ndistinct");
1520 0 : else if (dependencies_enabled)
1521 0 : appendStringInfoString(&buf, "dependencies");
1522 0 : appendStringInfoChar(&buf, ')');
1523 : }
1524 :
1525 1 : appendStringInfoString(&buf, " ON ");
1526 :
1527 3 : for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
1528 : {
1529 2 : AttrNumber attnum = statextrec->stxkeys.values[colno];
1530 : char *attname;
1531 :
1532 2 : if (colno > 0)
1533 1 : appendStringInfoString(&buf, ", ");
1534 :
1535 2 : attname = get_relid_attribute_name(statextrec->stxrelid, attnum);
1536 :
1537 2 : appendStringInfoString(&buf, quote_identifier(attname));
1538 : }
1539 :
1540 1 : appendStringInfo(&buf, " FROM %s",
1541 : generate_relation_name(statextrec->stxrelid, NIL));
1542 :
1543 1 : ReleaseSysCache(statexttup);
1544 :
1545 1 : return buf.data;
1546 : }
1547 :
1548 : /*
1549 : * pg_get_partkeydef
1550 : *
1551 : * Returns the partition key specification, ie, the following:
1552 : *
1553 : * PARTITION BY { RANGE | LIST } (column opt_collation opt_opclass [, ...])
1554 : */
1555 : Datum
1556 14 : pg_get_partkeydef(PG_FUNCTION_ARGS)
1557 : {
1558 14 : Oid relid = PG_GETARG_OID(0);
1559 : char *res;
1560 :
1561 14 : res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
1562 :
1563 14 : if (res == NULL)
1564 1 : PG_RETURN_NULL();
1565 :
1566 13 : PG_RETURN_TEXT_P(string_to_text(res));
1567 : }
1568 :
1569 : /* Internal version that just reports the column definitions */
1570 : char *
1571 9 : pg_get_partkeydef_columns(Oid relid, bool pretty)
1572 : {
1573 : int prettyFlags;
1574 :
1575 9 : prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
1576 9 : return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
1577 : }
1578 :
1579 : /*
1580 : * Internal workhorse to decompile a partition key definition.
1581 : */
1582 : static char *
1583 23 : pg_get_partkeydef_worker(Oid relid, int prettyFlags,
1584 : bool attrsOnly, bool missing_ok)
1585 : {
1586 : Form_pg_partitioned_table form;
1587 : HeapTuple tuple;
1588 : oidvector *partclass;
1589 : oidvector *partcollation;
1590 : List *partexprs;
1591 : ListCell *partexpr_item;
1592 : List *context;
1593 : Datum datum;
1594 : bool isnull;
1595 : StringInfoData buf;
1596 : int keyno;
1597 : char *str;
1598 : char *sep;
1599 :
1600 23 : tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
1601 23 : if (!HeapTupleIsValid(tuple))
1602 : {
1603 1 : if (missing_ok)
1604 1 : return NULL;
1605 0 : elog(ERROR, "cache lookup failed for partition key of %u", relid);
1606 : }
1607 :
1608 22 : form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
1609 :
1610 22 : Assert(form->partrelid == relid);
1611 :
1612 : /* Must get partclass and partcollation the hard way */
1613 22 : datum = SysCacheGetAttr(PARTRELID, tuple,
1614 : Anum_pg_partitioned_table_partclass, &isnull);
1615 22 : Assert(!isnull);
1616 22 : partclass = (oidvector *) DatumGetPointer(datum);
1617 :
1618 22 : datum = SysCacheGetAttr(PARTRELID, tuple,
1619 : Anum_pg_partitioned_table_partcollation, &isnull);
1620 22 : Assert(!isnull);
1621 22 : partcollation = (oidvector *) DatumGetPointer(datum);
1622 :
1623 :
1624 : /*
1625 : * Get the expressions, if any. (NOTE: we do not use the relcache
1626 : * versions of the expressions, because we want to display
1627 : * non-const-folded expressions.)
1628 : */
1629 22 : if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprs))
1630 : {
1631 : Datum exprsDatum;
1632 : bool isnull;
1633 : char *exprsString;
1634 :
1635 7 : exprsDatum = SysCacheGetAttr(PARTRELID, tuple,
1636 : Anum_pg_partitioned_table_partexprs, &isnull);
1637 7 : Assert(!isnull);
1638 7 : exprsString = TextDatumGetCString(exprsDatum);
1639 7 : partexprs = (List *) stringToNode(exprsString);
1640 :
1641 7 : if (!IsA(partexprs, List))
1642 0 : elog(ERROR, "unexpected node type found in partexprs: %d",
1643 : (int) nodeTag(partexprs));
1644 :
1645 7 : pfree(exprsString);
1646 : }
1647 : else
1648 15 : partexprs = NIL;
1649 :
1650 22 : partexpr_item = list_head(partexprs);
1651 22 : context = deparse_context_for(get_relation_name(relid), relid);
1652 :
1653 22 : initStringInfo(&buf);
1654 :
1655 22 : switch (form->partstrat)
1656 : {
1657 : case PARTITION_STRATEGY_LIST:
1658 10 : if (!attrsOnly)
1659 9 : appendStringInfoString(&buf, "LIST");
1660 10 : break;
1661 : case PARTITION_STRATEGY_RANGE:
1662 12 : if (!attrsOnly)
1663 4 : appendStringInfoString(&buf, "RANGE");
1664 12 : break;
1665 : default:
1666 0 : elog(ERROR, "unexpected partition strategy: %d",
1667 : (int) form->partstrat);
1668 : }
1669 :
1670 22 : if (!attrsOnly)
1671 13 : appendStringInfoString(&buf, " (");
1672 22 : sep = "";
1673 53 : for (keyno = 0; keyno < form->partnatts; keyno++)
1674 : {
1675 31 : AttrNumber attnum = form->partattrs.values[keyno];
1676 : Oid keycoltype;
1677 : Oid keycolcollation;
1678 : Oid partcoll;
1679 :
1680 31 : appendStringInfoString(&buf, sep);
1681 31 : sep = ", ";
1682 31 : if (attnum != 0)
1683 : {
1684 : /* Simple attribute reference */
1685 : char *attname;
1686 : int32 keycoltypmod;
1687 :
1688 24 : attname = get_relid_attribute_name(relid, attnum);
1689 24 : appendStringInfoString(&buf, quote_identifier(attname));
1690 24 : get_atttypetypmodcoll(relid, attnum,
1691 : &keycoltype, &keycoltypmod,
1692 : &keycolcollation);
1693 : }
1694 : else
1695 : {
1696 : /* Expression */
1697 : Node *partkey;
1698 :
1699 7 : if (partexpr_item == NULL)
1700 0 : elog(ERROR, "too few entries in partexprs list");
1701 7 : partkey = (Node *) lfirst(partexpr_item);
1702 7 : partexpr_item = lnext(partexpr_item);
1703 :
1704 : /* Deparse */
1705 7 : str = deparse_expression_pretty(partkey, context, false, false,
1706 : prettyFlags, 0);
1707 : /* Need parens if it's not a bare function call */
1708 7 : if (looks_like_function(partkey))
1709 2 : appendStringInfoString(&buf, str);
1710 : else
1711 5 : appendStringInfo(&buf, "(%s)", str);
1712 :
1713 7 : keycoltype = exprType(partkey);
1714 7 : keycolcollation = exprCollation(partkey);
1715 : }
1716 :
1717 : /* Add collation, if not default for column */
1718 31 : partcoll = partcollation->values[keyno];
1719 31 : if (!attrsOnly && OidIsValid(partcoll) && partcoll != keycolcollation)
1720 1 : appendStringInfo(&buf, " COLLATE %s",
1721 : generate_collation_name((partcoll)));
1722 :
1723 : /* Add the operator class name, if not default */
1724 31 : if (!attrsOnly)
1725 17 : get_opclass_name(partclass->values[keyno], keycoltype, &buf);
1726 : }
1727 :
1728 22 : if (!attrsOnly)
1729 13 : appendStringInfoChar(&buf, ')');
1730 :
1731 : /* Clean up */
1732 22 : ReleaseSysCache(tuple);
1733 :
1734 22 : return buf.data;
1735 : }
1736 :
1737 : /*
1738 : * pg_get_partition_constraintdef
1739 : *
1740 : * Returns partition constraint expression as a string for the input relation
1741 : */
1742 : Datum
1743 19 : pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
1744 : {
1745 19 : Oid relationId = PG_GETARG_OID(0);
1746 : Expr *constr_expr;
1747 : int prettyFlags;
1748 : List *context;
1749 : char *consrc;
1750 :
1751 19 : constr_expr = get_partition_qual_relid(relationId);
1752 :
1753 : /* Quick exit if not a partition */
1754 19 : if (constr_expr == NULL)
1755 0 : PG_RETURN_NULL();
1756 :
1757 : /*
1758 : * Deparse and return the constraint expression.
1759 : */
1760 19 : prettyFlags = PRETTYFLAG_INDENT;
1761 19 : context = deparse_context_for(get_relation_name(relationId), relationId);
1762 19 : consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
1763 : false, prettyFlags, 0);
1764 :
1765 19 : PG_RETURN_TEXT_P(string_to_text(consrc));
1766 : }
1767 :
1768 : /*
1769 : * pg_get_constraintdef
1770 : *
1771 : * Returns the definition for the constraint, ie, everything that needs to
1772 : * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
1773 : */
1774 : Datum
1775 1 : pg_get_constraintdef(PG_FUNCTION_ARGS)
1776 : {
1777 1 : Oid constraintId = PG_GETARG_OID(0);
1778 : int prettyFlags;
1779 : char *res;
1780 :
1781 1 : prettyFlags = PRETTYFLAG_INDENT;
1782 :
1783 1 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
1784 :
1785 1 : if (res == NULL)
1786 1 : PG_RETURN_NULL();
1787 :
1788 0 : PG_RETURN_TEXT_P(string_to_text(res));
1789 : }
1790 :
1791 : Datum
1792 114 : pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
1793 : {
1794 114 : Oid constraintId = PG_GETARG_OID(0);
1795 114 : bool pretty = PG_GETARG_BOOL(1);
1796 : int prettyFlags;
1797 : char *res;
1798 :
1799 114 : prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
1800 :
1801 114 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
1802 :
1803 114 : if (res == NULL)
1804 0 : PG_RETURN_NULL();
1805 :
1806 114 : PG_RETURN_TEXT_P(string_to_text(res));
1807 : }
1808 :
1809 : /*
1810 : * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
1811 : */
1812 : char *
1813 21 : pg_get_constraintdef_command(Oid constraintId)
1814 : {
1815 21 : return pg_get_constraintdef_worker(constraintId, true, 0, false);
1816 : }
1817 :
1818 : /*
1819 : * As of 9.4, we now use an MVCC snapshot for this.
1820 : */
1821 : static char *
1822 136 : pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
1823 : int prettyFlags, bool missing_ok)
1824 : {
1825 : HeapTuple tup;
1826 : Form_pg_constraint conForm;
1827 : StringInfoData buf;
1828 : SysScanDesc scandesc;
1829 : ScanKeyData scankey[1];
1830 136 : Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());
1831 136 : Relation relation = heap_open(ConstraintRelationId, AccessShareLock);
1832 :
1833 136 : ScanKeyInit(&scankey[0],
1834 : ObjectIdAttributeNumber,
1835 : BTEqualStrategyNumber, F_OIDEQ,
1836 : ObjectIdGetDatum(constraintId));
1837 :
1838 136 : scandesc = systable_beginscan(relation,
1839 : ConstraintOidIndexId,
1840 : true,
1841 : snapshot,
1842 : 1,
1843 : scankey);
1844 :
1845 : /*
1846 : * We later use the tuple with SysCacheGetAttr() as if we had obtained it
1847 : * via SearchSysCache, which works fine.
1848 : */
1849 136 : tup = systable_getnext(scandesc);
1850 :
1851 136 : UnregisterSnapshot(snapshot);
1852 :
1853 136 : if (!HeapTupleIsValid(tup))
1854 : {
1855 1 : if (missing_ok)
1856 : {
1857 1 : systable_endscan(scandesc);
1858 1 : heap_close(relation, AccessShareLock);
1859 1 : return NULL;
1860 : }
1861 0 : elog(ERROR, "could not find tuple for constraint %u", constraintId);
1862 : }
1863 :
1864 135 : conForm = (Form_pg_constraint) GETSTRUCT(tup);
1865 :
1866 135 : initStringInfo(&buf);
1867 :
1868 135 : if (fullCommand)
1869 : {
1870 : /*
1871 : * Currently, callers want ALTER TABLE (without ONLY) for CHECK
1872 : * constraints, and other types of constraints don't inherit anyway so
1873 : * it doesn't matter whether we say ONLY or not. Someday we might
1874 : * need to let callers specify whether to put ONLY in the command.
1875 : */
1876 21 : appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
1877 : generate_qualified_relation_name(conForm->conrelid),
1878 21 : quote_identifier(NameStr(conForm->conname)));
1879 : }
1880 :
1881 135 : switch (conForm->contype)
1882 : {
1883 : case CONSTRAINT_FOREIGN:
1884 : {
1885 : Datum val;
1886 : bool isnull;
1887 : const char *string;
1888 :
1889 : /* Start off the constraint definition */
1890 8 : appendStringInfoString(&buf, "FOREIGN KEY (");
1891 :
1892 : /* Fetch and build referencing-column list */
1893 8 : val = SysCacheGetAttr(CONSTROID, tup,
1894 : Anum_pg_constraint_conkey, &isnull);
1895 8 : if (isnull)
1896 0 : elog(ERROR, "null conkey for constraint %u",
1897 : constraintId);
1898 :
1899 8 : decompile_column_index_array(val, conForm->conrelid, &buf);
1900 :
1901 : /* add foreign relation name */
1902 8 : appendStringInfo(&buf, ") REFERENCES %s(",
1903 : generate_relation_name(conForm->confrelid,
1904 : NIL));
1905 :
1906 : /* Fetch and build referenced-column list */
1907 8 : val = SysCacheGetAttr(CONSTROID, tup,
1908 : Anum_pg_constraint_confkey, &isnull);
1909 8 : if (isnull)
1910 0 : elog(ERROR, "null confkey for constraint %u",
1911 : constraintId);
1912 :
1913 8 : decompile_column_index_array(val, conForm->confrelid, &buf);
1914 :
1915 8 : appendStringInfoChar(&buf, ')');
1916 :
1917 : /* Add match type */
1918 8 : switch (conForm->confmatchtype)
1919 : {
1920 : case FKCONSTR_MATCH_FULL:
1921 2 : string = " MATCH FULL";
1922 2 : break;
1923 : case FKCONSTR_MATCH_PARTIAL:
1924 0 : string = " MATCH PARTIAL";
1925 0 : break;
1926 : case FKCONSTR_MATCH_SIMPLE:
1927 6 : string = "";
1928 6 : break;
1929 : default:
1930 0 : elog(ERROR, "unrecognized confmatchtype: %d",
1931 : conForm->confmatchtype);
1932 : string = ""; /* keep compiler quiet */
1933 : break;
1934 : }
1935 8 : appendStringInfoString(&buf, string);
1936 :
1937 : /* Add ON UPDATE and ON DELETE clauses, if needed */
1938 8 : switch (conForm->confupdtype)
1939 : {
1940 : case FKCONSTR_ACTION_NOACTION:
1941 6 : string = NULL; /* suppress default */
1942 6 : break;
1943 : case FKCONSTR_ACTION_RESTRICT:
1944 0 : string = "RESTRICT";
1945 0 : break;
1946 : case FKCONSTR_ACTION_CASCADE:
1947 0 : string = "CASCADE";
1948 0 : break;
1949 : case FKCONSTR_ACTION_SETNULL:
1950 2 : string = "SET NULL";
1951 2 : break;
1952 : case FKCONSTR_ACTION_SETDEFAULT:
1953 0 : string = "SET DEFAULT";
1954 0 : break;
1955 : default:
1956 0 : elog(ERROR, "unrecognized confupdtype: %d",
1957 : conForm->confupdtype);
1958 : string = NULL; /* keep compiler quiet */
1959 : break;
1960 : }
1961 8 : if (string)
1962 2 : appendStringInfo(&buf, " ON UPDATE %s", string);
1963 :
1964 8 : switch (conForm->confdeltype)
1965 : {
1966 : case FKCONSTR_ACTION_NOACTION:
1967 6 : string = NULL; /* suppress default */
1968 6 : break;
1969 : case FKCONSTR_ACTION_RESTRICT:
1970 0 : string = "RESTRICT";
1971 0 : break;
1972 : case FKCONSTR_ACTION_CASCADE:
1973 0 : string = "CASCADE";
1974 0 : break;
1975 : case FKCONSTR_ACTION_SETNULL:
1976 2 : string = "SET NULL";
1977 2 : break;
1978 : case FKCONSTR_ACTION_SETDEFAULT:
1979 0 : string = "SET DEFAULT";
1980 0 : break;
1981 : default:
1982 0 : elog(ERROR, "unrecognized confdeltype: %d",
1983 : conForm->confdeltype);
1984 : string = NULL; /* keep compiler quiet */
1985 : break;
1986 : }
1987 8 : if (string)
1988 2 : appendStringInfo(&buf, " ON DELETE %s", string);
1989 :
1990 8 : break;
1991 : }
1992 : case CONSTRAINT_PRIMARY:
1993 : case CONSTRAINT_UNIQUE:
1994 : {
1995 : Datum val;
1996 : bool isnull;
1997 : Oid indexId;
1998 :
1999 : /* Start off the constraint definition */
2000 34 : if (conForm->contype == CONSTRAINT_PRIMARY)
2001 23 : appendStringInfoString(&buf, "PRIMARY KEY (");
2002 : else
2003 11 : appendStringInfoString(&buf, "UNIQUE (");
2004 :
2005 : /* Fetch and build target column list */
2006 34 : val = SysCacheGetAttr(CONSTROID, tup,
2007 : Anum_pg_constraint_conkey, &isnull);
2008 34 : if (isnull)
2009 0 : elog(ERROR, "null conkey for constraint %u",
2010 : constraintId);
2011 :
2012 34 : decompile_column_index_array(val, conForm->conrelid, &buf);
2013 :
2014 34 : appendStringInfoChar(&buf, ')');
2015 :
2016 34 : indexId = get_constraint_index(constraintId);
2017 :
2018 : /* XXX why do we only print these bits if fullCommand? */
2019 34 : if (fullCommand && OidIsValid(indexId))
2020 : {
2021 7 : char *options = flatten_reloptions(indexId);
2022 : Oid tblspc;
2023 :
2024 7 : if (options)
2025 : {
2026 0 : appendStringInfo(&buf, " WITH (%s)", options);
2027 0 : pfree(options);
2028 : }
2029 :
2030 7 : tblspc = get_rel_tablespace(indexId);
2031 7 : if (OidIsValid(tblspc))
2032 0 : appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2033 0 : quote_identifier(get_tablespace_name(tblspc)));
2034 : }
2035 :
2036 34 : break;
2037 : }
2038 : case CONSTRAINT_CHECK:
2039 : {
2040 : Datum val;
2041 : bool isnull;
2042 : char *conbin;
2043 : char *consrc;
2044 : Node *expr;
2045 : List *context;
2046 :
2047 : /* Fetch constraint expression in parsetree form */
2048 92 : val = SysCacheGetAttr(CONSTROID, tup,
2049 : Anum_pg_constraint_conbin, &isnull);
2050 92 : if (isnull)
2051 0 : elog(ERROR, "null conbin for constraint %u",
2052 : constraintId);
2053 :
2054 92 : conbin = TextDatumGetCString(val);
2055 92 : expr = stringToNode(conbin);
2056 :
2057 : /* Set up deparsing context for Var nodes in constraint */
2058 92 : if (conForm->conrelid != InvalidOid)
2059 : {
2060 : /* relation constraint */
2061 92 : context = deparse_context_for(get_relation_name(conForm->conrelid),
2062 : conForm->conrelid);
2063 : }
2064 : else
2065 : {
2066 : /* domain constraint --- can't have Vars */
2067 0 : context = NIL;
2068 : }
2069 :
2070 92 : consrc = deparse_expression_pretty(expr, context, false, false,
2071 : prettyFlags, 0);
2072 :
2073 : /*
2074 : * Now emit the constraint definition, adding NO INHERIT if
2075 : * necessary.
2076 : *
2077 : * There are cases where the constraint expression will be
2078 : * fully parenthesized and we don't need the outer parens ...
2079 : * but there are other cases where we do need 'em. Be
2080 : * conservative for now.
2081 : *
2082 : * Note that simply checking for leading '(' and trailing ')'
2083 : * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2084 : */
2085 92 : appendStringInfo(&buf, "CHECK (%s)%s",
2086 : consrc,
2087 92 : conForm->connoinherit ? " NO INHERIT" : "");
2088 92 : break;
2089 : }
2090 : case CONSTRAINT_TRIGGER:
2091 :
2092 : /*
2093 : * There isn't an ALTER TABLE syntax for creating a user-defined
2094 : * constraint trigger, but it seems better to print something than
2095 : * throw an error; if we throw error then this function couldn't
2096 : * safely be applied to all rows of pg_constraint.
2097 : */
2098 0 : appendStringInfoString(&buf, "TRIGGER");
2099 0 : break;
2100 : case CONSTRAINT_EXCLUSION:
2101 : {
2102 1 : Oid indexOid = conForm->conindid;
2103 : Datum val;
2104 : bool isnull;
2105 : Datum *elems;
2106 : int nElems;
2107 : int i;
2108 : Oid *operators;
2109 :
2110 : /* Extract operator OIDs from the pg_constraint tuple */
2111 1 : val = SysCacheGetAttr(CONSTROID, tup,
2112 : Anum_pg_constraint_conexclop,
2113 : &isnull);
2114 1 : if (isnull)
2115 0 : elog(ERROR, "null conexclop for constraint %u",
2116 : constraintId);
2117 :
2118 1 : deconstruct_array(DatumGetArrayTypeP(val),
2119 : OIDOID, sizeof(Oid), true, 'i',
2120 : &elems, NULL, &nElems);
2121 :
2122 1 : operators = (Oid *) palloc(nElems * sizeof(Oid));
2123 2 : for (i = 0; i < nElems; i++)
2124 1 : operators[i] = DatumGetObjectId(elems[i]);
2125 :
2126 : /* pg_get_indexdef_worker does the rest */
2127 : /* suppress tablespace because pg_dump wants it that way */
2128 1 : appendStringInfoString(&buf,
2129 1 : pg_get_indexdef_worker(indexOid,
2130 : 0,
2131 : operators,
2132 : false,
2133 : false,
2134 : prettyFlags,
2135 : false));
2136 1 : break;
2137 : }
2138 : default:
2139 0 : elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2140 : break;
2141 : }
2142 :
2143 135 : if (conForm->condeferrable)
2144 3 : appendStringInfoString(&buf, " DEFERRABLE");
2145 135 : if (conForm->condeferred)
2146 0 : appendStringInfoString(&buf, " INITIALLY DEFERRED");
2147 135 : if (!conForm->convalidated)
2148 3 : appendStringInfoString(&buf, " NOT VALID");
2149 :
2150 : /* Cleanup */
2151 135 : systable_endscan(scandesc);
2152 135 : heap_close(relation, AccessShareLock);
2153 :
2154 135 : return buf.data;
2155 : }
2156 :
2157 :
2158 : /*
2159 : * Convert an int16[] Datum into a comma-separated list of column names
2160 : * for the indicated relation; append the list to buf.
2161 : */
2162 : static void
2163 50 : decompile_column_index_array(Datum column_index_array, Oid relId,
2164 : StringInfo buf)
2165 : {
2166 : Datum *keys;
2167 : int nKeys;
2168 : int j;
2169 :
2170 : /* Extract data from array of int16 */
2171 50 : deconstruct_array(DatumGetArrayTypeP(column_index_array),
2172 : INT2OID, 2, true, 's',
2173 : &keys, NULL, &nKeys);
2174 :
2175 115 : for (j = 0; j < nKeys; j++)
2176 : {
2177 : char *colName;
2178 :
2179 65 : colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
2180 :
2181 65 : if (j == 0)
2182 50 : appendStringInfoString(buf, quote_identifier(colName));
2183 : else
2184 15 : appendStringInfo(buf, ", %s", quote_identifier(colName));
2185 : }
2186 50 : }
2187 :
2188 :
2189 : /* ----------
2190 : * get_expr - Decompile an expression tree
2191 : *
2192 : * Input: an expression tree in nodeToString form, and a relation OID
2193 : *
2194 : * Output: reverse-listed expression
2195 : *
2196 : * Currently, the expression can only refer to a single relation, namely
2197 : * the one specified by the second parameter. This is sufficient for
2198 : * partial indexes, column default expressions, etc. We also support
2199 : * Var-free expressions, for which the OID can be InvalidOid.
2200 : * ----------
2201 : */
2202 : Datum
2203 85 : pg_get_expr(PG_FUNCTION_ARGS)
2204 : {
2205 85 : text *expr = PG_GETARG_TEXT_PP(0);
2206 85 : Oid relid = PG_GETARG_OID(1);
2207 : int prettyFlags;
2208 : char *relname;
2209 :
2210 85 : prettyFlags = PRETTYFLAG_INDENT;
2211 :
2212 85 : if (OidIsValid(relid))
2213 : {
2214 : /* Get the name for the relation */
2215 85 : relname = get_rel_name(relid);
2216 :
2217 : /*
2218 : * If the OID isn't actually valid, don't throw an error, just return
2219 : * NULL. This is a bit questionable, but it's what we've done
2220 : * historically, and it can help avoid unwanted failures when
2221 : * examining catalog entries for just-deleted relations.
2222 : */
2223 85 : if (relname == NULL)
2224 0 : PG_RETURN_NULL();
2225 : }
2226 : else
2227 0 : relname = NULL;
2228 :
2229 85 : PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, prettyFlags));
2230 : }
2231 :
2232 : Datum
2233 0 : pg_get_expr_ext(PG_FUNCTION_ARGS)
2234 : {
2235 0 : text *expr = PG_GETARG_TEXT_PP(0);
2236 0 : Oid relid = PG_GETARG_OID(1);
2237 0 : bool pretty = PG_GETARG_BOOL(2);
2238 : int prettyFlags;
2239 : char *relname;
2240 :
2241 0 : prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
2242 :
2243 0 : if (OidIsValid(relid))
2244 : {
2245 : /* Get the name for the relation */
2246 0 : relname = get_rel_name(relid);
2247 : /* See notes above */
2248 0 : if (relname == NULL)
2249 0 : PG_RETURN_NULL();
2250 : }
2251 : else
2252 0 : relname = NULL;
2253 :
2254 0 : PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, prettyFlags));
2255 : }
2256 :
2257 : static text *
2258 85 : pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags)
2259 : {
2260 : Node *node;
2261 : List *context;
2262 : char *exprstr;
2263 : char *str;
2264 :
2265 : /* Convert input TEXT object to C string */
2266 85 : exprstr = text_to_cstring(expr);
2267 :
2268 : /* Convert expression to node tree */
2269 85 : node = (Node *) stringToNode(exprstr);
2270 :
2271 85 : pfree(exprstr);
2272 :
2273 : /* Prepare deparse context if needed */
2274 85 : if (OidIsValid(relid))
2275 85 : context = deparse_context_for(relname, relid);
2276 : else
2277 0 : context = NIL;
2278 :
2279 : /* Deparse */
2280 85 : str = deparse_expression_pretty(node, context, false, false,
2281 : prettyFlags, 0);
2282 :
2283 85 : return string_to_text(str);
2284 : }
2285 :
2286 :
2287 : /* ----------
2288 : * get_userbyid - Get a user name by roleid and
2289 : * fallback to 'unknown (OID=n)'
2290 : * ----------
2291 : */
2292 : Datum
2293 159 : pg_get_userbyid(PG_FUNCTION_ARGS)
2294 : {
2295 159 : Oid roleid = PG_GETARG_OID(0);
2296 : Name result;
2297 : HeapTuple roletup;
2298 : Form_pg_authid role_rec;
2299 :
2300 : /*
2301 : * Allocate space for the result
2302 : */
2303 159 : result = (Name) palloc(NAMEDATALEN);
2304 159 : memset(NameStr(*result), 0, NAMEDATALEN);
2305 :
2306 : /*
2307 : * Get the pg_authid entry and print the result
2308 : */
2309 159 : roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
2310 159 : if (HeapTupleIsValid(roletup))
2311 : {
2312 159 : role_rec = (Form_pg_authid) GETSTRUCT(roletup);
2313 159 : StrNCpy(NameStr(*result), NameStr(role_rec->rolname), NAMEDATALEN);
2314 159 : ReleaseSysCache(roletup);
2315 : }
2316 : else
2317 0 : sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
2318 :
2319 159 : PG_RETURN_NAME(result);
2320 : }
2321 :
2322 :
2323 : /*
2324 : * pg_get_serial_sequence
2325 : * Get the name of the sequence used by a serial column,
2326 : * formatted suitably for passing to setval, nextval or currval.
2327 : * First parameter is not treated as double-quoted, second parameter
2328 : * is --- see documentation for reason.
2329 : */
2330 : Datum
2331 0 : pg_get_serial_sequence(PG_FUNCTION_ARGS)
2332 : {
2333 0 : text *tablename = PG_GETARG_TEXT_PP(0);
2334 0 : text *columnname = PG_GETARG_TEXT_PP(1);
2335 : RangeVar *tablerv;
2336 : Oid tableOid;
2337 : char *column;
2338 : AttrNumber attnum;
2339 0 : Oid sequenceId = InvalidOid;
2340 : Relation depRel;
2341 : ScanKeyData key[3];
2342 : SysScanDesc scan;
2343 : HeapTuple tup;
2344 :
2345 : /* Look up table name. Can't lock it - we might not have privileges. */
2346 0 : tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
2347 0 : tableOid = RangeVarGetRelid(tablerv, NoLock, false);
2348 :
2349 : /* Get the number of the column */
2350 0 : column = text_to_cstring(columnname);
2351 :
2352 0 : attnum = get_attnum(tableOid, column);
2353 0 : if (attnum == InvalidAttrNumber)
2354 0 : ereport(ERROR,
2355 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2356 : errmsg("column \"%s\" of relation \"%s\" does not exist",
2357 : column, tablerv->relname)));
2358 :
2359 : /* Search the dependency table for the dependent sequence */
2360 0 : depRel = heap_open(DependRelationId, AccessShareLock);
2361 :
2362 0 : ScanKeyInit(&key[0],
2363 : Anum_pg_depend_refclassid,
2364 : BTEqualStrategyNumber, F_OIDEQ,
2365 : ObjectIdGetDatum(RelationRelationId));
2366 0 : ScanKeyInit(&key[1],
2367 : Anum_pg_depend_refobjid,
2368 : BTEqualStrategyNumber, F_OIDEQ,
2369 : ObjectIdGetDatum(tableOid));
2370 0 : ScanKeyInit(&key[2],
2371 : Anum_pg_depend_refobjsubid,
2372 : BTEqualStrategyNumber, F_INT4EQ,
2373 : Int32GetDatum(attnum));
2374 :
2375 0 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
2376 : NULL, 3, key);
2377 :
2378 0 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
2379 : {
2380 0 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
2381 :
2382 : /*
2383 : * We assume any auto dependency of a sequence on a column must be
2384 : * what we are looking for. (We need the relkind test because indexes
2385 : * can also have auto dependencies on columns.)
2386 : */
2387 0 : if (deprec->classid == RelationRelationId &&
2388 0 : deprec->objsubid == 0 &&
2389 0 : deprec->deptype == DEPENDENCY_AUTO &&
2390 0 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
2391 : {
2392 0 : sequenceId = deprec->objid;
2393 0 : break;
2394 : }
2395 : }
2396 :
2397 0 : systable_endscan(scan);
2398 0 : heap_close(depRel, AccessShareLock);
2399 :
2400 0 : if (OidIsValid(sequenceId))
2401 : {
2402 : char *result;
2403 :
2404 0 : result = generate_qualified_relation_name(sequenceId);
2405 :
2406 0 : PG_RETURN_TEXT_P(string_to_text(result));
2407 : }
2408 :
2409 0 : PG_RETURN_NULL();
2410 : }
2411 :
2412 :
2413 : /*
2414 : * pg_get_functiondef
2415 : * Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
2416 : * the specified function.
2417 : *
2418 : * Note: if you change the output format of this function, be careful not
2419 : * to break psql's rules (in \ef and \sf) for identifying the start of the
2420 : * function body. To wit: the function body starts on a line that begins
2421 : * with "AS ", and no preceding line will look like that.
2422 : */
2423 : Datum
2424 1 : pg_get_functiondef(PG_FUNCTION_ARGS)
2425 : {
2426 1 : Oid funcid = PG_GETARG_OID(0);
2427 : StringInfoData buf;
2428 : StringInfoData dq;
2429 : HeapTuple proctup;
2430 : Form_pg_proc proc;
2431 : Datum tmp;
2432 : bool isnull;
2433 : const char *prosrc;
2434 : const char *name;
2435 : const char *nsp;
2436 : float4 procost;
2437 : int oldlen;
2438 :
2439 1 : initStringInfo(&buf);
2440 :
2441 : /* Look up the function */
2442 1 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
2443 1 : if (!HeapTupleIsValid(proctup))
2444 1 : PG_RETURN_NULL();
2445 :
2446 0 : proc = (Form_pg_proc) GETSTRUCT(proctup);
2447 0 : name = NameStr(proc->proname);
2448 :
2449 0 : if (proc->proisagg)
2450 0 : ereport(ERROR,
2451 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2452 : errmsg("\"%s\" is an aggregate function", name)));
2453 :
2454 : /*
2455 : * We always qualify the function name, to ensure the right function gets
2456 : * replaced.
2457 : */
2458 0 : nsp = get_namespace_name(proc->pronamespace);
2459 0 : appendStringInfo(&buf, "CREATE OR REPLACE FUNCTION %s(",
2460 : quote_qualified_identifier(nsp, name));
2461 0 : (void) print_function_arguments(&buf, proctup, false, true);
2462 0 : appendStringInfoString(&buf, ")\n RETURNS ");
2463 0 : print_function_rettype(&buf, proctup);
2464 :
2465 0 : print_function_trftypes(&buf, proctup);
2466 :
2467 0 : appendStringInfo(&buf, "\n LANGUAGE %s\n",
2468 0 : quote_identifier(get_language_name(proc->prolang, false)));
2469 :
2470 : /* Emit some miscellaneous options on one line */
2471 0 : oldlen = buf.len;
2472 :
2473 0 : if (proc->proiswindow)
2474 0 : appendStringInfoString(&buf, " WINDOW");
2475 0 : switch (proc->provolatile)
2476 : {
2477 : case PROVOLATILE_IMMUTABLE:
2478 0 : appendStringInfoString(&buf, " IMMUTABLE");
2479 0 : break;
2480 : case PROVOLATILE_STABLE:
2481 0 : appendStringInfoString(&buf, " STABLE");
2482 0 : break;
2483 : case PROVOLATILE_VOLATILE:
2484 0 : break;
2485 : }
2486 :
2487 0 : switch (proc->proparallel)
2488 : {
2489 : case PROPARALLEL_SAFE:
2490 0 : appendStringInfoString(&buf, " PARALLEL SAFE");
2491 0 : break;
2492 : case PROPARALLEL_RESTRICTED:
2493 0 : appendStringInfoString(&buf, " PARALLEL RESTRICTED");
2494 0 : break;
2495 : case PROPARALLEL_UNSAFE:
2496 0 : break;
2497 : }
2498 :
2499 0 : if (proc->proisstrict)
2500 0 : appendStringInfoString(&buf, " STRICT");
2501 0 : if (proc->prosecdef)
2502 0 : appendStringInfoString(&buf, " SECURITY DEFINER");
2503 0 : if (proc->proleakproof)
2504 0 : appendStringInfoString(&buf, " LEAKPROOF");
2505 :
2506 : /* This code for the default cost and rows should match functioncmds.c */
2507 0 : if (proc->prolang == INTERNALlanguageId ||
2508 0 : proc->prolang == ClanguageId)
2509 0 : procost = 1;
2510 : else
2511 0 : procost = 100;
2512 0 : if (proc->procost != procost)
2513 0 : appendStringInfo(&buf, " COST %g", proc->procost);
2514 :
2515 0 : if (proc->prorows > 0 && proc->prorows != 1000)
2516 0 : appendStringInfo(&buf, " ROWS %g", proc->prorows);
2517 :
2518 0 : if (oldlen != buf.len)
2519 0 : appendStringInfoChar(&buf, '\n');
2520 :
2521 : /* Emit any proconfig options, one per line */
2522 0 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
2523 0 : if (!isnull)
2524 : {
2525 0 : ArrayType *a = DatumGetArrayTypeP(tmp);
2526 : int i;
2527 :
2528 0 : Assert(ARR_ELEMTYPE(a) == TEXTOID);
2529 0 : Assert(ARR_NDIM(a) == 1);
2530 0 : Assert(ARR_LBOUND(a)[0] == 1);
2531 :
2532 0 : for (i = 1; i <= ARR_DIMS(a)[0]; i++)
2533 : {
2534 : Datum d;
2535 :
2536 0 : d = array_ref(a, 1, &i,
2537 : -1 /* varlenarray */ ,
2538 : -1 /* TEXT's typlen */ ,
2539 : false /* TEXT's typbyval */ ,
2540 : 'i' /* TEXT's typalign */ ,
2541 : &isnull);
2542 0 : if (!isnull)
2543 : {
2544 0 : char *configitem = TextDatumGetCString(d);
2545 : char *pos;
2546 :
2547 0 : pos = strchr(configitem, '=');
2548 0 : if (pos == NULL)
2549 0 : continue;
2550 0 : *pos++ = '\0';
2551 :
2552 0 : appendStringInfo(&buf, " SET %s TO ",
2553 : quote_identifier(configitem));
2554 :
2555 : /*
2556 : * Some GUC variable names are 'LIST' type and hence must not
2557 : * be quoted.
2558 : */
2559 0 : if (pg_strcasecmp(configitem, "DateStyle") == 0
2560 0 : || pg_strcasecmp(configitem, "search_path") == 0)
2561 0 : appendStringInfoString(&buf, pos);
2562 : else
2563 0 : simple_quote_literal(&buf, pos);
2564 0 : appendStringInfoChar(&buf, '\n');
2565 : }
2566 : }
2567 : }
2568 :
2569 : /* And finally the function definition ... */
2570 0 : appendStringInfoString(&buf, "AS ");
2571 :
2572 0 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
2573 0 : if (!isnull)
2574 : {
2575 0 : simple_quote_literal(&buf, TextDatumGetCString(tmp));
2576 0 : appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
2577 : }
2578 :
2579 0 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosrc, &isnull);
2580 0 : if (isnull)
2581 0 : elog(ERROR, "null prosrc");
2582 0 : prosrc = TextDatumGetCString(tmp);
2583 :
2584 : /*
2585 : * We always use dollar quoting. Figure out a suitable delimiter.
2586 : *
2587 : * Since the user is likely to be editing the function body string, we
2588 : * shouldn't use a short delimiter that he might easily create a conflict
2589 : * with. Hence prefer "$function$", but extend if needed.
2590 : */
2591 0 : initStringInfo(&dq);
2592 0 : appendStringInfoString(&dq, "$function");
2593 0 : while (strstr(prosrc, dq.data) != NULL)
2594 0 : appendStringInfoChar(&dq, 'x');
2595 0 : appendStringInfoChar(&dq, '$');
2596 :
2597 0 : appendStringInfoString(&buf, dq.data);
2598 0 : appendStringInfoString(&buf, prosrc);
2599 0 : appendStringInfoString(&buf, dq.data);
2600 :
2601 0 : appendStringInfoChar(&buf, '\n');
2602 :
2603 0 : ReleaseSysCache(proctup);
2604 :
2605 0 : PG_RETURN_TEXT_P(string_to_text(buf.data));
2606 : }
2607 :
2608 : /*
2609 : * pg_get_function_arguments
2610 : * Get a nicely-formatted list of arguments for a function.
2611 : * This is everything that would go between the parentheses in
2612 : * CREATE FUNCTION.
2613 : */
2614 : Datum
2615 5 : pg_get_function_arguments(PG_FUNCTION_ARGS)
2616 : {
2617 5 : Oid funcid = PG_GETARG_OID(0);
2618 : StringInfoData buf;
2619 : HeapTuple proctup;
2620 :
2621 5 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
2622 5 : if (!HeapTupleIsValid(proctup))
2623 1 : PG_RETURN_NULL();
2624 :
2625 4 : initStringInfo(&buf);
2626 :
2627 4 : (void) print_function_arguments(&buf, proctup, false, true);
2628 :
2629 4 : ReleaseSysCache(proctup);
2630 :
2631 4 : PG_RETURN_TEXT_P(string_to_text(buf.data));
2632 : }
2633 :
2634 : /*
2635 : * pg_get_function_identity_arguments
2636 : * Get a formatted list of arguments for a function.
2637 : * This is everything that would go between the parentheses in
2638 : * ALTER FUNCTION, etc. In particular, don't print defaults.
2639 : */
2640 : Datum
2641 1 : pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
2642 : {
2643 1 : Oid funcid = PG_GETARG_OID(0);
2644 : StringInfoData buf;
2645 : HeapTuple proctup;
2646 :
2647 1 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
2648 1 : if (!HeapTupleIsValid(proctup))
2649 1 : PG_RETURN_NULL();
2650 :
2651 0 : initStringInfo(&buf);
2652 :
2653 0 : (void) print_function_arguments(&buf, proctup, false, false);
2654 :
2655 0 : ReleaseSysCache(proctup);
2656 :
2657 0 : PG_RETURN_TEXT_P(string_to_text(buf.data));
2658 : }
2659 :
2660 : /*
2661 : * pg_get_function_result
2662 : * Get a nicely-formatted version of the result type of a function.
2663 : * This is what would appear after RETURNS in CREATE FUNCTION.
2664 : */
2665 : Datum
2666 3 : pg_get_function_result(PG_FUNCTION_ARGS)
2667 : {
2668 3 : Oid funcid = PG_GETARG_OID(0);
2669 : StringInfoData buf;
2670 : HeapTuple proctup;
2671 :
2672 3 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
2673 3 : if (!HeapTupleIsValid(proctup))
2674 1 : PG_RETURN_NULL();
2675 :
2676 2 : initStringInfo(&buf);
2677 :
2678 2 : print_function_rettype(&buf, proctup);
2679 :
2680 2 : ReleaseSysCache(proctup);
2681 :
2682 2 : PG_RETURN_TEXT_P(string_to_text(buf.data));
2683 : }
2684 :
2685 : /*
2686 : * Guts of pg_get_function_result: append the function's return type
2687 : * to the specified buffer.
2688 : */
2689 : static void
2690 2 : print_function_rettype(StringInfo buf, HeapTuple proctup)
2691 : {
2692 2 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
2693 2 : int ntabargs = 0;
2694 : StringInfoData rbuf;
2695 :
2696 2 : initStringInfo(&rbuf);
2697 :
2698 2 : if (proc->proretset)
2699 : {
2700 : /* It might be a table function; try to print the arguments */
2701 0 : appendStringInfoString(&rbuf, "TABLE(");
2702 0 : ntabargs = print_function_arguments(&rbuf, proctup, true, false);
2703 0 : if (ntabargs > 0)
2704 0 : appendStringInfoChar(&rbuf, ')');
2705 : else
2706 0 : resetStringInfo(&rbuf);
2707 : }
2708 :
2709 2 : if (ntabargs == 0)
2710 : {
2711 : /* Not a table function, so do the normal thing */
2712 2 : if (proc->proretset)
2713 0 : appendStringInfoString(&rbuf, "SETOF ");
2714 2 : appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
2715 : }
2716 :
2717 2 : appendStringInfoString(buf, rbuf.data);
2718 2 : }
2719 :
2720 : /*
2721 : * Common code for pg_get_function_arguments and pg_get_function_result:
2722 : * append the desired subset of arguments to buf. We print only TABLE
2723 : * arguments when print_table_args is true, and all the others when it's false.
2724 : * We print argument defaults only if print_defaults is true.
2725 : * Function return value is the number of arguments printed.
2726 : */
2727 : static int
2728 4 : print_function_arguments(StringInfo buf, HeapTuple proctup,
2729 : bool print_table_args, bool print_defaults)
2730 : {
2731 4 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
2732 : int numargs;
2733 : Oid *argtypes;
2734 : char **argnames;
2735 : char *argmodes;
2736 4 : int insertorderbyat = -1;
2737 : int argsprinted;
2738 : int inputargno;
2739 : int nlackdefaults;
2740 4 : ListCell *nextargdefault = NULL;
2741 : int i;
2742 :
2743 4 : numargs = get_func_arg_info(proctup,
2744 : &argtypes, &argnames, &argmodes);
2745 :
2746 4 : nlackdefaults = numargs;
2747 4 : if (print_defaults && proc->pronargdefaults > 0)
2748 : {
2749 : Datum proargdefaults;
2750 : bool isnull;
2751 :
2752 2 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
2753 : Anum_pg_proc_proargdefaults,
2754 : &isnull);
2755 2 : if (!isnull)
2756 : {
2757 : char *str;
2758 : List *argdefaults;
2759 :
2760 2 : str = TextDatumGetCString(proargdefaults);
2761 2 : argdefaults = castNode(List, stringToNode(str));
2762 2 : pfree(str);
2763 2 : nextargdefault = list_head(argdefaults);
2764 : /* nlackdefaults counts only *input* arguments lacking defaults */
2765 2 : nlackdefaults = proc->pronargs - list_length(argdefaults);
2766 : }
2767 : }
2768 :
2769 : /* Check for special treatment of ordered-set aggregates */
2770 4 : if (proc->proisagg)
2771 : {
2772 : HeapTuple aggtup;
2773 : Form_pg_aggregate agg;
2774 :
2775 2 : aggtup = SearchSysCache1(AGGFNOID,
2776 : ObjectIdGetDatum(HeapTupleGetOid(proctup)));
2777 2 : if (!HeapTupleIsValid(aggtup))
2778 0 : elog(ERROR, "cache lookup failed for aggregate %u",
2779 : HeapTupleGetOid(proctup));
2780 2 : agg = (Form_pg_aggregate) GETSTRUCT(aggtup);
2781 2 : if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
2782 2 : insertorderbyat = agg->aggnumdirectargs;
2783 2 : ReleaseSysCache(aggtup);
2784 : }
2785 :
2786 4 : argsprinted = 0;
2787 4 : inputargno = 0;
2788 12 : for (i = 0; i < numargs; i++)
2789 : {
2790 8 : Oid argtype = argtypes[i];
2791 8 : char *argname = argnames ? argnames[i] : NULL;
2792 8 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
2793 : const char *modename;
2794 : bool isinput;
2795 :
2796 8 : switch (argmode)
2797 : {
2798 : case PROARGMODE_IN:
2799 4 : modename = "";
2800 4 : isinput = true;
2801 4 : break;
2802 : case PROARGMODE_INOUT:
2803 0 : modename = "INOUT ";
2804 0 : isinput = true;
2805 0 : break;
2806 : case PROARGMODE_OUT:
2807 1 : modename = "OUT ";
2808 1 : isinput = false;
2809 1 : break;
2810 : case PROARGMODE_VARIADIC:
2811 3 : modename = "VARIADIC ";
2812 3 : isinput = true;
2813 3 : break;
2814 : case PROARGMODE_TABLE:
2815 0 : modename = "";
2816 0 : isinput = false;
2817 0 : break;
2818 : default:
2819 0 : elog(ERROR, "invalid parameter mode '%c'", argmode);
2820 : modename = NULL; /* keep compiler quiet */
2821 : isinput = false;
2822 : break;
2823 : }
2824 8 : if (isinput)
2825 7 : inputargno++; /* this is a 1-based counter */
2826 :
2827 8 : if (print_table_args != (argmode == PROARGMODE_TABLE))
2828 0 : continue;
2829 :
2830 8 : if (argsprinted == insertorderbyat)
2831 : {
2832 2 : if (argsprinted)
2833 2 : appendStringInfoChar(buf, ' ');
2834 2 : appendStringInfoString(buf, "ORDER BY ");
2835 : }
2836 6 : else if (argsprinted)
2837 2 : appendStringInfoString(buf, ", ");
2838 :
2839 8 : appendStringInfoString(buf, modename);
2840 8 : if (argname && argname[0])
2841 4 : appendStringInfo(buf, "%s ", quote_identifier(argname));
2842 8 : appendStringInfoString(buf, format_type_be(argtype));
2843 8 : if (print_defaults && isinput && inputargno > nlackdefaults)
2844 : {
2845 : Node *expr;
2846 :
2847 3 : Assert(nextargdefault != NULL);
2848 3 : expr = (Node *) lfirst(nextargdefault);
2849 3 : nextargdefault = lnext(nextargdefault);
2850 :
2851 3 : appendStringInfo(buf, " DEFAULT %s",
2852 : deparse_expression(expr, NIL, false, false));
2853 : }
2854 8 : argsprinted++;
2855 :
2856 : /* nasty hack: print the last arg twice for variadic ordered-set agg */
2857 8 : if (argsprinted == insertorderbyat && i == numargs - 1)
2858 : {
2859 1 : i--;
2860 : /* aggs shouldn't have defaults anyway, but just to be sure ... */
2861 1 : print_defaults = false;
2862 : }
2863 : }
2864 :
2865 4 : return argsprinted;
2866 : }
2867 :
2868 : static bool
2869 16 : is_input_argument(int nth, const char *argmodes)
2870 : {
2871 16 : return (!argmodes
2872 7 : || argmodes[nth] == PROARGMODE_IN
2873 3 : || argmodes[nth] == PROARGMODE_INOUT
2874 19 : || argmodes[nth] == PROARGMODE_VARIADIC);
2875 : }
2876 :
2877 : /*
2878 : * Append used transformed types to specified buffer
2879 : */
2880 : static void
2881 0 : print_function_trftypes(StringInfo buf, HeapTuple proctup)
2882 : {
2883 : Oid *trftypes;
2884 : int ntypes;
2885 :
2886 0 : ntypes = get_func_trftypes(proctup, &trftypes);
2887 0 : if (ntypes > 0)
2888 : {
2889 : int i;
2890 :
2891 0 : appendStringInfoString(buf, "\n TRANSFORM ");
2892 0 : for (i = 0; i < ntypes; i++)
2893 : {
2894 0 : if (i != 0)
2895 0 : appendStringInfoString(buf, ", ");
2896 0 : appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
2897 : }
2898 : }
2899 0 : }
2900 :
2901 : /*
2902 : * Get textual representation of a function argument's default value. The
2903 : * second argument of this function is the argument number among all arguments
2904 : * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
2905 : * how information_schema.sql uses it.
2906 : */
2907 : Datum
2908 9 : pg_get_function_arg_default(PG_FUNCTION_ARGS)
2909 : {
2910 9 : Oid funcid = PG_GETARG_OID(0);
2911 9 : int32 nth_arg = PG_GETARG_INT32(1);
2912 : HeapTuple proctup;
2913 : Form_pg_proc proc;
2914 : int numargs;
2915 : Oid *argtypes;
2916 : char **argnames;
2917 : char *argmodes;
2918 : int i;
2919 : List *argdefaults;
2920 : Node *node;
2921 : char *str;
2922 : int nth_inputarg;
2923 : Datum proargdefaults;
2924 : bool isnull;
2925 : int nth_default;
2926 :
2927 9 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
2928 9 : if (!HeapTupleIsValid(proctup))
2929 2 : PG_RETURN_NULL();
2930 :
2931 7 : numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
2932 7 : if (nth_arg < 1 || nth_arg > numargs || !is_input_argument(nth_arg - 1, argmodes))
2933 : {
2934 2 : ReleaseSysCache(proctup);
2935 2 : PG_RETURN_NULL();
2936 : }
2937 :
2938 5 : nth_inputarg = 0;
2939 14 : for (i = 0; i < nth_arg; i++)
2940 9 : if (is_input_argument(i, argmodes))
2941 8 : nth_inputarg++;
2942 :
2943 5 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
2944 : Anum_pg_proc_proargdefaults,
2945 : &isnull);
2946 5 : if (isnull)
2947 : {
2948 0 : ReleaseSysCache(proctup);
2949 0 : PG_RETURN_NULL();
2950 : }
2951 :
2952 5 : str = TextDatumGetCString(proargdefaults);
2953 5 : argdefaults = castNode(List, stringToNode(str));
2954 5 : pfree(str);
2955 :
2956 5 : proc = (Form_pg_proc) GETSTRUCT(proctup);
2957 :
2958 : /*
2959 : * Calculate index into proargdefaults: proargdefaults corresponds to the
2960 : * last N input arguments, where N = pronargdefaults.
2961 : */
2962 5 : nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
2963 :
2964 5 : if (nth_default < 0 || nth_default >= list_length(argdefaults))
2965 : {
2966 1 : ReleaseSysCache(proctup);
2967 1 : PG_RETURN_NULL();
2968 : }
2969 4 : node = list_nth(argdefaults, nth_default);
2970 4 : str = deparse_expression(node, NIL, false, false);
2971 :
2972 4 : ReleaseSysCache(proctup);
2973 :
2974 4 : PG_RETURN_TEXT_P(string_to_text(str));
2975 : }
2976 :
2977 :
2978 : /*
2979 : * deparse_expression - General utility for deparsing expressions
2980 : *
2981 : * calls deparse_expression_pretty with all prettyPrinting disabled
2982 : */
2983 : char *
2984 3364 : deparse_expression(Node *expr, List *dpcontext,
2985 : bool forceprefix, bool showimplicit)
2986 : {
2987 3364 : return deparse_expression_pretty(expr, dpcontext, forceprefix,
2988 : showimplicit, 0, 0);
2989 : }
2990 :
2991 : /* ----------
2992 : * deparse_expression_pretty - General utility for deparsing expressions
2993 : *
2994 : * expr is the node tree to be deparsed. It must be a transformed expression
2995 : * tree (ie, not the raw output of gram.y).
2996 : *
2997 : * dpcontext is a list of deparse_namespace nodes representing the context
2998 : * for interpreting Vars in the node tree. It can be NIL if no Vars are
2999 : * expected.
3000 : *
3001 : * forceprefix is TRUE to force all Vars to be prefixed with their table names.
3002 : *
3003 : * showimplicit is TRUE to force all implicit casts to be shown explicitly.
3004 : *
3005 : * Tries to pretty up the output according to prettyFlags and startIndent.
3006 : *
3007 : * The result is a palloc'd string.
3008 : * ----------
3009 : */
3010 : static char *
3011 3589 : deparse_expression_pretty(Node *expr, List *dpcontext,
3012 : bool forceprefix, bool showimplicit,
3013 : int prettyFlags, int startIndent)
3014 : {
3015 : StringInfoData buf;
3016 : deparse_context context;
3017 :
3018 3589 : initStringInfo(&buf);
3019 3589 : context.buf = &buf;
3020 3589 : context.namespaces = dpcontext;
3021 3589 : context.windowClause = NIL;
3022 3589 : context.windowTList = NIL;
3023 3589 : context.varprefix = forceprefix;
3024 3589 : context.prettyFlags = prettyFlags;
3025 3589 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
3026 3589 : context.indentLevel = startIndent;
3027 3589 : context.special_exprkind = EXPR_KIND_NONE;
3028 :
3029 3589 : get_rule_expr(expr, &context, showimplicit);
3030 :
3031 3589 : return buf.data;
3032 : }
3033 :
3034 : /* ----------
3035 : * deparse_context_for - Build deparse context for a single relation
3036 : *
3037 : * Given the reference name (alias) and OID of a relation, build deparsing
3038 : * context for an expression referencing only that relation (as varno 1,
3039 : * varlevelsup 0). This is sufficient for many uses of deparse_expression.
3040 : * ----------
3041 : */
3042 : List *
3043 796 : deparse_context_for(const char *aliasname, Oid relid)
3044 : {
3045 : deparse_namespace *dpns;
3046 : RangeTblEntry *rte;
3047 :
3048 796 : dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
3049 :
3050 : /* Build a minimal RTE for the rel */
3051 796 : rte = makeNode(RangeTblEntry);
3052 796 : rte->rtekind = RTE_RELATION;
3053 796 : rte->relid = relid;
3054 796 : rte->relkind = RELKIND_RELATION; /* no need for exactness here */
3055 796 : rte->alias = makeAlias(aliasname, NIL);
3056 796 : rte->eref = rte->alias;
3057 796 : rte->lateral = false;
3058 796 : rte->inh = false;
3059 796 : rte->inFromCl = true;
3060 :
3061 : /* Build one-element rtable */
3062 796 : dpns->rtable = list_make1(rte);
3063 796 : dpns->ctes = NIL;
3064 796 : set_rtable_names(dpns, NIL, NULL);
3065 796 : set_simple_column_names(dpns);
3066 :
3067 : /* Return a one-deep namespace stack */
3068 796 : return list_make1(dpns);
3069 : }
3070 :
3071 : /*
3072 : * deparse_context_for_plan_rtable - Build deparse context for a plan's rtable
3073 : *
3074 : * When deparsing an expression in a Plan tree, we use the plan's rangetable
3075 : * to resolve names of simple Vars. The initialization of column names for
3076 : * this is rather expensive if the rangetable is large, and it'll be the same
3077 : * for every expression in the Plan tree; so we do it just once and re-use
3078 : * the result of this function for each expression. (Note that the result
3079 : * is not usable until set_deparse_context_planstate() is applied to it.)
3080 : *
3081 : * In addition to the plan's rangetable list, pass the per-RTE alias names
3082 : * assigned by a previous call to select_rtable_names_for_explain.
3083 : */
3084 : List *
3085 1118 : deparse_context_for_plan_rtable(List *rtable, List *rtable_names)
3086 : {
3087 : deparse_namespace *dpns;
3088 :
3089 1118 : dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
3090 :
3091 : /* Initialize fields that stay the same across the whole plan tree */
3092 1118 : dpns->rtable = rtable;
3093 1118 : dpns->rtable_names = rtable_names;
3094 1118 : dpns->ctes = NIL;
3095 :
3096 : /*
3097 : * Set up column name aliases. We will get rather bogus results for join
3098 : * RTEs, but that doesn't matter because plan trees don't contain any join
3099 : * alias Vars.
3100 : */
3101 1118 : set_simple_column_names(dpns);
3102 :
3103 : /* Return a one-deep namespace stack */
3104 1118 : return list_make1(dpns);
3105 : }
3106 :
3107 : /*
3108 : * set_deparse_context_planstate - Specify Plan node containing expression
3109 : *
3110 : * When deparsing an expression in a Plan tree, we might have to resolve
3111 : * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
3112 : * provide the parent PlanState node. Then OUTER_VAR and INNER_VAR references
3113 : * can be resolved by drilling down into the left and right child plans.
3114 : * Similarly, INDEX_VAR references can be resolved by reference to the
3115 : * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
3116 : * ForeignScan and CustomScan nodes. (Note that we don't currently support
3117 : * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
3118 : * for those, we can only deparse the indexqualorig fields, which won't
3119 : * contain INDEX_VAR Vars.)
3120 : *
3121 : * Note: planstate really ought to be declared as "PlanState *", but we use
3122 : * "Node *" to avoid having to include execnodes.h in ruleutils.h.
3123 : *
3124 : * The ancestors list is a list of the PlanState's parent PlanStates, the
3125 : * most-closely-nested first. This is needed to resolve PARAM_EXEC Params.
3126 : * Note we assume that all the PlanStates share the same rtable.
3127 : *
3128 : * Once this function has been called, deparse_expression() can be called on
3129 : * subsidiary expression(s) of the specified PlanState node. To deparse
3130 : * expressions of a different Plan node in the same Plan tree, re-call this
3131 : * function to identify the new parent Plan node.
3132 : *
3133 : * The result is the same List passed in; this is a notational convenience.
3134 : */
3135 : List *
3136 2276 : set_deparse_context_planstate(List *dpcontext,
3137 : Node *planstate, List *ancestors)
3138 : {
3139 : deparse_namespace *dpns;
3140 :
3141 : /* Should always have one-entry namespace list for Plan deparsing */
3142 2276 : Assert(list_length(dpcontext) == 1);
3143 2276 : dpns = (deparse_namespace *) linitial(dpcontext);
3144 :
3145 : /* Set our attention on the specific plan node passed in */
3146 2276 : set_deparse_planstate(dpns, (PlanState *) planstate);
3147 2276 : dpns->ancestors = ancestors;
3148 :
3149 2276 : return dpcontext;
3150 : }
3151 :
3152 : /*
3153 : * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
3154 : *
3155 : * Determine the relation aliases we'll use during an EXPLAIN operation.
3156 : * This is just a frontend to set_rtable_names. We have to expose the aliases
3157 : * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
3158 : */
3159 : List *
3160 1118 : select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
3161 : {
3162 : deparse_namespace dpns;
3163 :
3164 1118 : memset(&dpns, 0, sizeof(dpns));
3165 1118 : dpns.rtable = rtable;
3166 1118 : dpns.ctes = NIL;
3167 1118 : set_rtable_names(&dpns, NIL, rels_used);
3168 : /* We needn't bother computing column aliases yet */
3169 :
3170 1118 : return dpns.rtable_names;
3171 : }
3172 :
3173 : /*
3174 : * set_rtable_names: select RTE aliases to be used in printing a query
3175 : *
3176 : * We fill in dpns->rtable_names with a list of names that is one-for-one with
3177 : * the already-filled dpns->rtable list. Each RTE name is unique among those
3178 : * in the new namespace plus any ancestor namespaces listed in
3179 : * parent_namespaces.
3180 : *
3181 : * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
3182 : *
3183 : * Note that this function is only concerned with relation names, not column
3184 : * names.
3185 : */
3186 : static void
3187 2299 : set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
3188 : Bitmapset *rels_used)
3189 : {
3190 : HASHCTL hash_ctl;
3191 : HTAB *names_hash;
3192 : NameHashEntry *hentry;
3193 : bool found;
3194 : int rtindex;
3195 : ListCell *lc;
3196 :
3197 2299 : dpns->rtable_names = NIL;
3198 : /* nothing more to do if empty rtable */
3199 2299 : if (dpns->rtable == NIL)
3200 2315 : return;
3201 :
3202 : /*
3203 : * We use a hash table to hold known names, so that this process is O(N)
3204 : * not O(N^2) for N names.
3205 : */
3206 2283 : MemSet(&hash_ctl, 0, sizeof(hash_ctl));
3207 2283 : hash_ctl.keysize = NAMEDATALEN;
3208 2283 : hash_ctl.entrysize = sizeof(NameHashEntry);
3209 2283 : hash_ctl.hcxt = CurrentMemoryContext;
3210 2283 : names_hash = hash_create("set_rtable_names names",
3211 2283 : list_length(dpns->rtable),
3212 : &hash_ctl,
3213 : HASH_ELEM | HASH_CONTEXT);
3214 : /* Preload the hash table with names appearing in parent_namespaces */
3215 2398 : foreach(lc, parent_namespaces)
3216 : {
3217 115 : deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
3218 : ListCell *lc2;
3219 :
3220 635 : foreach(lc2, olddpns->rtable_names)
3221 : {
3222 520 : char *oldname = (char *) lfirst(lc2);
3223 :
3224 520 : if (oldname == NULL)
3225 7 : continue;
3226 513 : hentry = (NameHashEntry *) hash_search(names_hash,
3227 : oldname,
3228 : HASH_ENTER,
3229 : &found);
3230 : /* we do not complain about duplicate names in parent namespaces */
3231 513 : hentry->counter = 0;
3232 : }
3233 : }
3234 :
3235 : /* Now we can scan the rtable */
3236 2283 : rtindex = 1;
3237 6741 : foreach(lc, dpns->rtable)
3238 : {
3239 4458 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
3240 : char *refname;
3241 :
3242 : /* Just in case this takes an unreasonable amount of time ... */
3243 4458 : CHECK_FOR_INTERRUPTS();
3244 :
3245 4458 : if (rels_used && !bms_is_member(rtindex, rels_used))
3246 : {
3247 : /* Ignore unreferenced RTE */
3248 771 : refname = NULL;
3249 : }
3250 3687 : else if (rte->alias)
3251 : {
3252 : /* If RTE has a user-defined alias, prefer that */
3253 1937 : refname = rte->alias->aliasname;
3254 : }
3255 1750 : else if (rte->rtekind == RTE_RELATION)
3256 : {
3257 : /* Use the current actual name of the relation */
3258 1526 : refname = get_rel_name(rte->relid);
3259 : }
3260 224 : else if (rte->rtekind == RTE_JOIN)
3261 : {
3262 : /* Unnamed join has no refname */
3263 127 : refname = NULL;
3264 : }
3265 : else
3266 : {
3267 : /* Otherwise use whatever the parser assigned */
3268 97 : refname = rte->eref->aliasname;
3269 : }
3270 :
3271 : /*
3272 : * If the selected name isn't unique, append digits to make it so, and
3273 : * make a new hash entry for it once we've got a unique name. For a
3274 : * very long input name, we might have to truncate to stay within
3275 : * NAMEDATALEN.
3276 : */
3277 4458 : if (refname)
3278 : {
3279 3560 : hentry = (NameHashEntry *) hash_search(names_hash,
3280 : refname,
3281 : HASH_ENTER,
3282 : &found);
3283 3560 : if (found)
3284 : {
3285 : /* Name already in use, must choose a new one */
3286 86 : int refnamelen = strlen(refname);
3287 86 : char *modname = (char *) palloc(refnamelen + 16);
3288 : NameHashEntry *hentry2;
3289 :
3290 : do
3291 : {
3292 86 : hentry->counter++;
3293 : for (;;)
3294 : {
3295 : /*
3296 : * We avoid using %.*s here because it can misbehave
3297 : * if the data is not valid in what libc thinks is the
3298 : * prevailing encoding.
3299 : */
3300 88 : memcpy(modname, refname, refnamelen);
3301 88 : sprintf(modname + refnamelen, "_%d", hentry->counter);
3302 88 : if (strlen(modname) < NAMEDATALEN)
3303 86 : break;
3304 : /* drop chars from refname to keep all the digits */
3305 2 : refnamelen = pg_mbcliplen(refname, refnamelen,
3306 : refnamelen - 1);
3307 2 : }
3308 86 : hentry2 = (NameHashEntry *) hash_search(names_hash,
3309 : modname,
3310 : HASH_ENTER,
3311 : &found);
3312 86 : } while (found);
3313 86 : hentry2->counter = 0; /* init new hash entry */
3314 86 : refname = modname;
3315 : }
3316 : else
3317 : {
3318 : /* Name not previously used, need only initialize hentry */
3319 3474 : hentry->counter = 0;
3320 : }
3321 : }
3322 :
3323 4458 : dpns->rtable_names = lappend(dpns->rtable_names, refname);
3324 4458 : rtindex++;
3325 : }
3326 :
3327 2283 : hash_destroy(names_hash);
3328 : }
3329 :
3330 : /*
3331 : * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
3332 : *
3333 : * For convenience, this is defined to initialize the deparse_namespace struct
3334 : * from scratch.
3335 : */
3336 : static void
3337 382 : set_deparse_for_query(deparse_namespace *dpns, Query *query,
3338 : List *parent_namespaces)
3339 : {
3340 : ListCell *lc;
3341 : ListCell *lc2;
3342 :
3343 : /* Initialize *dpns and fill rtable/ctes links */
3344 382 : memset(dpns, 0, sizeof(deparse_namespace));
3345 382 : dpns->rtable = query->rtable;
3346 382 : dpns->ctes = query->cteList;
3347 :
3348 : /* Assign a unique relation alias to each RTE */
3349 382 : set_rtable_names(dpns, parent_namespaces, NULL);
3350 :
3351 : /* Initialize dpns->rtable_columns to contain zeroed structs */
3352 382 : dpns->rtable_columns = NIL;
3353 2007 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
3354 1243 : dpns->rtable_columns = lappend(dpns->rtable_columns,
3355 : palloc0(sizeof(deparse_columns)));
3356 :
3357 : /* If it's a utility query, it won't have a jointree */
3358 382 : if (query->jointree)
3359 : {
3360 : /* Detect whether global uniqueness of USING names is needed */
3361 380 : dpns->unique_using =
3362 380 : has_dangerous_join_using(dpns, (Node *) query->jointree);
3363 :
3364 : /*
3365 : * Select names for columns merged by USING, via a recursive pass over
3366 : * the query jointree.
3367 : */
3368 380 : set_using_names(dpns, (Node *) query->jointree, NIL);
3369 : }
3370 :
3371 : /*
3372 : * Now assign remaining column aliases for each RTE. We do this in a
3373 : * linear scan of the rtable, so as to process RTEs whether or not they
3374 : * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
3375 : * etc). JOIN RTEs must be processed after their children, but this is
3376 : * okay because they appear later in the rtable list than their children
3377 : * (cf Asserts in identify_join_columns()).
3378 : */
3379 1625 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
3380 : {
3381 1243 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
3382 1243 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
3383 :
3384 1243 : if (rte->rtekind == RTE_JOIN)
3385 136 : set_join_column_names(dpns, rte, colinfo);
3386 : else
3387 1107 : set_relation_column_names(dpns, rte, colinfo);
3388 : }
3389 382 : }
3390 :
3391 : /*
3392 : * set_simple_column_names: fill in column aliases for non-query situations
3393 : *
3394 : * This handles EXPLAIN and cases where we only have relation RTEs. Without
3395 : * a join tree, we can't do anything smart about join RTEs, but we don't
3396 : * need to (note that EXPLAIN should never see join alias Vars anyway).
3397 : * If we do hit a join RTE we'll just process it like a non-table base RTE.
3398 : */
3399 : static void
3400 1917 : set_simple_column_names(deparse_namespace *dpns)
3401 : {
3402 : ListCell *lc;
3403 : ListCell *lc2;
3404 :
3405 : /* Initialize dpns->rtable_columns to contain zeroed structs */
3406 1917 : dpns->rtable_columns = NIL;
3407 7049 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
3408 3215 : dpns->rtable_columns = lappend(dpns->rtable_columns,
3409 : palloc0(sizeof(deparse_columns)));
3410 :
3411 : /* Assign unique column aliases within each RTE */
3412 5132 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
3413 : {
3414 3215 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
3415 3215 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
3416 :
3417 3215 : set_relation_column_names(dpns, rte, colinfo);
3418 : }
3419 1917 : }
3420 :
3421 : /*
3422 : * has_dangerous_join_using: search jointree for unnamed JOIN USING
3423 : *
3424 : * Merged columns of a JOIN USING may act differently from either of the input
3425 : * columns, either because they are merged with COALESCE (in a FULL JOIN) or
3426 : * because an implicit coercion of the underlying input column is required.
3427 : * In such a case the column must be referenced as a column of the JOIN not as
3428 : * a column of either input. And this is problematic if the join is unnamed
3429 : * (alias-less): we cannot qualify the column's name with an RTE name, since
3430 : * there is none. (Forcibly assigning an alias to the join is not a solution,
3431 : * since that will prevent legal references to tables below the join.)
3432 : * To ensure that every column in the query is unambiguously referenceable,
3433 : * we must assign such merged columns names that are globally unique across
3434 : * the whole query, aliasing other columns out of the way as necessary.
3435 : *
3436 : * Because the ensuing re-aliasing is fairly damaging to the readability of
3437 : * the query, we don't do this unless we have to. So, we must pre-scan
3438 : * the join tree to see if we have to, before starting set_using_names().
3439 : */
3440 : static bool
3441 966 : has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
3442 : {
3443 966 : if (IsA(jtnode, RangeTblRef))
3444 : {
3445 : /* nothing to do here */
3446 : }
3447 505 : else if (IsA(jtnode, FromExpr))
3448 : {
3449 380 : FromExpr *f = (FromExpr *) jtnode;
3450 : ListCell *lc;
3451 :
3452 728 : foreach(lc, f->fromlist)
3453 : {
3454 360 : if (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))
3455 12 : return true;
3456 : }
3457 : }
3458 125 : else if (IsA(jtnode, JoinExpr))
3459 : {
3460 125 : JoinExpr *j = (JoinExpr *) jtnode;
3461 :
3462 : /* Is it an unnamed JOIN with USING? */
3463 125 : if (j->alias == NULL && j->usingClause)
3464 : {
3465 : /*
3466 : * Yes, so check each join alias var to see if any of them are not
3467 : * simple references to underlying columns. If so, we have a
3468 : * dangerous situation and must pick unique aliases.
3469 : */
3470 38 : RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
3471 : ListCell *lc;
3472 :
3473 150 : foreach(lc, jrte->joinaliasvars)
3474 : {
3475 124 : Var *aliasvar = (Var *) lfirst(lc);
3476 :
3477 124 : if (aliasvar != NULL && !IsA(aliasvar, Var))
3478 12 : return true;
3479 : }
3480 : }
3481 :
3482 : /* Nope, but inspect children */
3483 113 : if (has_dangerous_join_using(dpns, j->larg))
3484 0 : return true;
3485 113 : if (has_dangerous_join_using(dpns, j->rarg))
3486 0 : return true;
3487 : }
3488 : else
3489 0 : elog(ERROR, "unrecognized node type: %d",
3490 : (int) nodeTag(jtnode));
3491 942 : return false;
3492 : }
3493 :
3494 : /*
3495 : * set_using_names: select column aliases to be used for merged USING columns
3496 : *
3497 : * We do this during a recursive descent of the query jointree.
3498 : * dpns->unique_using must already be set to determine the global strategy.
3499 : *
3500 : * Column alias info is saved in the dpns->rtable_columns list, which is
3501 : * assumed to be filled with pre-zeroed deparse_columns structs.
3502 : *
3503 : * parentUsing is a list of all USING aliases assigned in parent joins of
3504 : * the current jointree node. (The passed-in list must not be modified.)
3505 : */
3506 : static void
3507 1019 : set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
3508 : {
3509 1019 : if (IsA(jtnode, RangeTblRef))
3510 : {
3511 : /* nothing to do now */
3512 : }
3513 516 : else if (IsA(jtnode, FromExpr))
3514 : {
3515 380 : FromExpr *f = (FromExpr *) jtnode;
3516 : ListCell *lc;
3517 :
3518 747 : foreach(lc, f->fromlist)
3519 367 : set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
3520 : }
3521 136 : else if (IsA(jtnode, JoinExpr))
3522 : {
3523 136 : JoinExpr *j = (JoinExpr *) jtnode;
3524 136 : RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
3525 136 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
3526 : int *leftattnos;
3527 : int *rightattnos;
3528 : deparse_columns *leftcolinfo;
3529 : deparse_columns *rightcolinfo;
3530 : int i;
3531 : ListCell *lc;
3532 :
3533 : /* Get info about the shape of the join */
3534 136 : identify_join_columns(j, rte, colinfo);
3535 136 : leftattnos = colinfo->leftattnos;
3536 136 : rightattnos = colinfo->rightattnos;
3537 :
3538 : /* Look up the not-yet-filled-in child deparse_columns structs */
3539 136 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
3540 136 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
3541 :
3542 : /*
3543 : * If this join is unnamed, then we cannot substitute new aliases at
3544 : * this level, so any name requirements pushed down to here must be
3545 : * pushed down again to the children.
3546 : */
3547 136 : if (rte->alias == NULL)
3548 : {
3549 145 : for (i = 0; i < colinfo->num_cols; i++)
3550 : {
3551 23 : char *colname = colinfo->colnames[i];
3552 :
3553 23 : if (colname == NULL)
3554 4 : continue;
3555 :
3556 : /* Push down to left column, unless it's a system column */
3557 19 : if (leftattnos[i] > 0)
3558 : {
3559 17 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
3560 17 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
3561 : }
3562 :
3563 : /* Same on the righthand side */
3564 19 : if (rightattnos[i] > 0)
3565 : {
3566 19 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
3567 19 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
3568 : }
3569 : }
3570 : }
3571 :
3572 : /*
3573 : * If there's a USING clause, select the USING column names and push
3574 : * those names down to the children. We have two strategies:
3575 : *
3576 : * If dpns->unique_using is TRUE, we force all USING names to be
3577 : * unique across the whole query level. In principle we'd only need
3578 : * the names of dangerous USING columns to be globally unique, but to
3579 : * safely assign all USING names in a single pass, we have to enforce
3580 : * the same uniqueness rule for all of them. However, if a USING
3581 : * column's name has been pushed down from the parent, we should use
3582 : * it as-is rather than making a uniqueness adjustment. This is
3583 : * necessary when we're at an unnamed join, and it creates no risk of
3584 : * ambiguity. Also, if there's a user-written output alias for a
3585 : * merged column, we prefer to use that rather than the input name;
3586 : * this simplifies the logic and seems likely to lead to less aliasing
3587 : * overall.
3588 : *
3589 : * If dpns->unique_using is FALSE, we only need USING names to be
3590 : * unique within their own join RTE. We still need to honor
3591 : * pushed-down names, though.
3592 : *
3593 : * Though significantly different in results, these two strategies are
3594 : * implemented by the same code, with only the difference of whether
3595 : * to put assigned names into dpns->using_names.
3596 : */
3597 136 : if (j->usingClause)
3598 : {
3599 : /* Copy the input parentUsing list so we don't modify it */
3600 59 : parentUsing = list_copy(parentUsing);
3601 :
3602 : /* USING names must correspond to the first join output columns */
3603 59 : expand_colnames_array_to(colinfo, list_length(j->usingClause));
3604 59 : i = 0;
3605 143 : foreach(lc, j->usingClause)
3606 : {
3607 84 : char *colname = strVal(lfirst(lc));
3608 :
3609 : /* Assert it's a merged column */
3610 84 : Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
3611 :
3612 : /* Adopt passed-down name if any, else select unique name */
3613 84 : if (colinfo->colnames[i] != NULL)
3614 17 : colname = colinfo->colnames[i];
3615 : else
3616 : {
3617 : /* Prefer user-written output alias if any */
3618 67 : if (rte->alias && i < list_length(rte->alias->colnames))
3619 0 : colname = strVal(list_nth(rte->alias->colnames, i));
3620 : /* Make it appropriately unique */
3621 67 : colname = make_colname_unique(colname, dpns, colinfo);
3622 67 : if (dpns->unique_using)
3623 21 : dpns->using_names = lappend(dpns->using_names,
3624 : colname);
3625 : /* Save it as output column name, too */
3626 67 : colinfo->colnames[i] = colname;
3627 : }
3628 :
3629 : /* Remember selected names for use later */
3630 84 : colinfo->usingNames = lappend(colinfo->usingNames, colname);
3631 84 : parentUsing = lappend(parentUsing, colname);
3632 :
3633 : /* Push down to left column, unless it's a system column */
3634 84 : if (leftattnos[i] > 0)
3635 : {
3636 84 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
3637 84 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
3638 : }
3639 :
3640 : /* Same on the righthand side */
3641 84 : if (rightattnos[i] > 0)
3642 : {
3643 84 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
3644 84 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
3645 : }
3646 :
3647 84 : i++;
3648 : }
3649 : }
3650 :
3651 : /* Mark child deparse_columns structs with correct parentUsing info */
3652 136 : leftcolinfo->parentUsing = parentUsing;
3653 136 : rightcolinfo->parentUsing = parentUsing;
3654 :
3655 : /* Now recursively assign USING column names in children */
3656 136 : set_using_names(dpns, j->larg, parentUsing);
3657 136 : set_using_names(dpns, j->rarg, parentUsing);
3658 : }
3659 : else
3660 0 : elog(ERROR, "unrecognized node type: %d",
3661 : (int) nodeTag(jtnode));
3662 1019 : }
3663 :
3664 : /*
3665 : * set_relation_column_names: select column aliases for a non-join RTE
3666 : *
3667 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
3668 : * If any colnames entries are already filled in, those override local
3669 : * choices.
3670 : */
3671 : static void
3672 4322 : set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
3673 : deparse_columns *colinfo)
3674 : {
3675 : int ncolumns;
3676 : char **real_colnames;
3677 : bool changed_any;
3678 : int noldcolumns;
3679 : int i;
3680 : int j;
3681 :
3682 : /*
3683 : * Extract the RTE's "real" column names. This is comparable to
3684 : * get_rte_attribute_name, except that it's important to disregard dropped
3685 : * columns. We put NULL into the array for a dropped column.
3686 : */
3687 4322 : if (rte->rtekind == RTE_RELATION)
3688 : {
3689 : /* Relation --- look to the system catalogs for up-to-date info */
3690 : Relation rel;
3691 : TupleDesc tupdesc;
3692 :
3693 3668 : rel = relation_open(rte->relid, AccessShareLock);
3694 3668 : tupdesc = RelationGetDescr(rel);
3695 :
3696 3668 : ncolumns = tupdesc->natts;
3697 3668 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
3698 :
3699 30588 : for (i = 0; i < ncolumns; i++)
3700 : {
3701 26920 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
3702 :
3703 26920 : if (attr->attisdropped)
3704 166 : real_colnames[i] = NULL;
3705 : else
3706 26754 : real_colnames[i] = pstrdup(NameStr(attr->attname));
3707 : }
3708 3668 : relation_close(rel, AccessShareLock);
3709 : }
3710 : else
3711 : {
3712 : /* Otherwise use the column names from eref */
3713 : ListCell *lc;
3714 :
3715 654 : ncolumns = list_length(rte->eref->colnames);
3716 654 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
3717 :
3718 654 : i = 0;
3719 3550 : foreach(lc, rte->eref->colnames)
3720 : {
3721 : /*
3722 : * If the column name shown in eref is an empty string, then it's
3723 : * a column that was dropped at the time of parsing the query, so
3724 : * treat it as dropped.
3725 : */
3726 2896 : char *cname = strVal(lfirst(lc));
3727 :
3728 2896 : if (cname[0] == '\0')
3729 3 : cname = NULL;
3730 2896 : real_colnames[i] = cname;
3731 2896 : i++;
3732 : }
3733 : }
3734 :
3735 : /*
3736 : * Ensure colinfo->colnames has a slot for each column. (It could be long
3737 : * enough already, if we pushed down a name for the last column.) Note:
3738 : * it's possible that there are now more columns than there were when the
3739 : * query was parsed, ie colnames could be longer than rte->eref->colnames.
3740 : * We must assign unique aliases to the new columns too, else there could
3741 : * be unresolved conflicts when the view/rule is reloaded.
3742 : */
3743 4322 : expand_colnames_array_to(colinfo, ncolumns);
3744 4322 : Assert(colinfo->num_cols == ncolumns);
3745 :
3746 : /*
3747 : * Make sufficiently large new_colnames and is_new_col arrays, too.
3748 : *
3749 : * Note: because we leave colinfo->num_new_cols zero until after the loop,
3750 : * colname_is_unique will not consult that array, which is fine because it
3751 : * would only be duplicate effort.
3752 : */
3753 4322 : colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
3754 4322 : colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
3755 :
3756 : /*
3757 : * Scan the columns, select a unique alias for each one, and store it in
3758 : * colinfo->colnames and colinfo->new_colnames. The former array has NULL
3759 : * entries for dropped columns, the latter omits them. Also mark
3760 : * new_colnames entries as to whether they are new since parse time; this
3761 : * is the case for entries beyond the length of rte->eref->colnames.
3762 : */
3763 4322 : noldcolumns = list_length(rte->eref->colnames);
3764 4322 : changed_any = false;
3765 4322 : j = 0;
3766 34138 : for (i = 0; i < ncolumns; i++)
3767 : {
3768 29816 : char *real_colname = real_colnames[i];
3769 29816 : char *colname = colinfo->colnames[i];
3770 :
3771 : /* Skip dropped columns */
3772 29816 : if (real_colname == NULL)
3773 : {
3774 169 : Assert(colname == NULL); /* colnames[i] is already NULL */
3775 169 : continue;
3776 : }
3777 :
3778 : /* If alias already assigned, that's what to use */
3779 29647 : if (colname == NULL)
3780 : {
3781 : /* If user wrote an alias, prefer that over real column name */
3782 29496 : if (rte->alias && i < list_length(rte->alias->colnames))
3783 126 : colname = strVal(list_nth(rte->alias->colnames, i));
3784 : else
3785 29370 : colname = real_colname;
3786 :
3787 : /* Unique-ify and insert into colinfo */
3788 29496 : colname = make_colname_unique(colname, dpns, colinfo);
3789 :
3790 29496 : colinfo->colnames[i] = colname;
3791 : }
3792 :
3793 : /* Put names of non-dropped columns in new_colnames[] too */
3794 29647 : colinfo->new_colnames[j] = colname;
3795 : /* And mark them as new or not */
3796 29647 : colinfo->is_new_col[j] = (i >= noldcolumns);
3797 29647 : j++;
3798 :
3799 : /* Remember if any assigned aliases differ from "real" name */
3800 29647 : if (!changed_any && strcmp(colname, real_colname) != 0)
3801 136 : changed_any = true;
3802 : }
3803 :
3804 : /*
3805 : * Set correct length for new_colnames[] array. (Note: if columns have
3806 : * been added, colinfo->num_cols includes them, which is not really quite
3807 : * right but is harmless, since any new columns must be at the end where
3808 : * they won't affect varattnos of pre-existing columns.)
3809 : */
3810 4322 : colinfo->num_new_cols = j;
3811 :
3812 : /*
3813 : * For a relation RTE, we need only print the alias column names if any
3814 : * are different from the underlying "real" names. For a function RTE,
3815 : * always emit a complete column alias list; this is to protect against
3816 : * possible instability of the default column names (eg, from altering
3817 : * parameter names). For tablefunc RTEs, we never print aliases, because
3818 : * the column names are part of the clause itself. For other RTE types,
3819 : * print if we changed anything OR if there were user-written column
3820 : * aliases (since the latter would be part of the underlying "reality").
3821 : */
3822 4322 : if (rte->rtekind == RTE_RELATION)
3823 3668 : colinfo->printaliases = changed_any;
3824 654 : else if (rte->rtekind == RTE_FUNCTION)
3825 65 : colinfo->printaliases = true;
3826 589 : else if (rte->rtekind == RTE_TABLEFUNC)
3827 6 : colinfo->printaliases = false;
3828 583 : else if (rte->alias && rte->alias->colnames != NIL)
3829 43 : colinfo->printaliases = true;
3830 : else
3831 540 : colinfo->printaliases = changed_any;
3832 4322 : }
3833 :
3834 : /*
3835 : * set_join_column_names: select column aliases for a join RTE
3836 : *
3837 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
3838 : * If any colnames entries are already filled in, those override local
3839 : * choices. Also, names for USING columns were already chosen by
3840 : * set_using_names(). We further expect that column alias selection has been
3841 : * completed for both input RTEs.
3842 : */
3843 : static void
3844 136 : set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
3845 : deparse_columns *colinfo)
3846 : {
3847 : deparse_columns *leftcolinfo;
3848 : deparse_columns *rightcolinfo;
3849 : bool changed_any;
3850 : int noldcolumns;
3851 : int nnewcolumns;
3852 136 : Bitmapset *leftmerged = NULL;
3853 136 : Bitmapset *rightmerged = NULL;
3854 : int i;
3855 : int j;
3856 : int ic;
3857 : int jc;
3858 :
3859 : /* Look up the previously-filled-in child deparse_columns structs */
3860 136 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
3861 136 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
3862 :
3863 : /*
3864 : * Ensure colinfo->colnames has a slot for each column. (It could be long
3865 : * enough already, if we pushed down a name for the last column.) Note:
3866 : * it's possible that one or both inputs now have more columns than there
3867 : * were when the query was parsed, but we'll deal with that below. We
3868 : * only need entries in colnames for pre-existing columns.
3869 : */
3870 136 : noldcolumns = list_length(rte->eref->colnames);
3871 136 : expand_colnames_array_to(colinfo, noldcolumns);
3872 136 : Assert(colinfo->num_cols == noldcolumns);
3873 :
3874 : /*
3875 : * Scan the join output columns, select an alias for each one, and store
3876 : * it in colinfo->colnames. If there are USING columns, set_using_names()
3877 : * already selected their names, so we can start the loop at the first
3878 : * non-merged column.
3879 : */
3880 136 : changed_any = false;
3881 3315 : for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
3882 : {
3883 3179 : char *colname = colinfo->colnames[i];
3884 : char *real_colname;
3885 :
3886 : /* Ignore dropped column (only possible for non-merged column) */
3887 3179 : if (colinfo->leftattnos[i] == 0 && colinfo->rightattnos[i] == 0)
3888 : {
3889 1 : Assert(colname == NULL);
3890 1 : continue;
3891 : }
3892 :
3893 : /* Get the child column name */
3894 3178 : if (colinfo->leftattnos[i] > 0)
3895 2226 : real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
3896 952 : else if (colinfo->rightattnos[i] > 0)
3897 952 : real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
3898 : else
3899 : {
3900 : /* We're joining system columns --- use eref name */
3901 0 : real_colname = strVal(list_nth(rte->eref->colnames, i));
3902 : }
3903 3178 : Assert(real_colname != NULL);
3904 :
3905 : /* In an unnamed join, just report child column names as-is */
3906 3178 : if (rte->alias == NULL)
3907 : {
3908 3127 : colinfo->colnames[i] = real_colname;
3909 3127 : continue;
3910 : }
3911 :
3912 : /* If alias already assigned, that's what to use */
3913 51 : if (colname == NULL)
3914 : {
3915 : /* If user wrote an alias, prefer that over real column name */
3916 51 : if (rte->alias && i < list_length(rte->alias->colnames))
3917 16 : colname = strVal(list_nth(rte->alias->colnames, i));
3918 : else
3919 35 : colname = real_colname;
3920 :
3921 : /* Unique-ify and insert into colinfo */
3922 51 : colname = make_colname_unique(colname, dpns, colinfo);
3923 :
3924 51 : colinfo->colnames[i] = colname;
3925 : }
3926 :
3927 : /* Remember if any assigned aliases differ from "real" name */
3928 51 : if (!changed_any && strcmp(colname, real_colname) != 0)
3929 4 : changed_any = true;
3930 : }
3931 :
3932 : /*
3933 : * Calculate number of columns the join would have if it were re-parsed
3934 : * now, and create storage for the new_colnames and is_new_col arrays.
3935 : *
3936 : * Note: colname_is_unique will be consulting new_colnames[] during the
3937 : * loops below, so its not-yet-filled entries must be zeroes.
3938 : */
3939 272 : nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
3940 136 : list_length(colinfo->usingNames);
3941 136 : colinfo->num_new_cols = nnewcolumns;
3942 136 : colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
3943 136 : colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
3944 :
3945 : /*
3946 : * Generating the new_colnames array is a bit tricky since any new columns
3947 : * added since parse time must be inserted in the right places. This code
3948 : * must match the parser, which will order a join's columns as merged
3949 : * columns first (in USING-clause order), then non-merged columns from the
3950 : * left input (in attnum order), then non-merged columns from the right
3951 : * input (ditto). If one of the inputs is itself a join, its columns will
3952 : * be ordered according to the same rule, which means newly-added columns
3953 : * might not be at the end. We can figure out what's what by consulting
3954 : * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
3955 : *
3956 : * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
3957 : * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
3958 : * meanings for the current child RTE.
3959 : */
3960 :
3961 : /* Handle merged columns; they are first and can't be new */
3962 136 : i = j = 0;
3963 576 : while (i < noldcolumns &&
3964 439 : colinfo->leftattnos[i] != 0 &&
3965 219 : colinfo->rightattnos[i] != 0)
3966 : {
3967 : /* column name is already determined and known unique */
3968 84 : colinfo->new_colnames[j] = colinfo->colnames[i];
3969 84 : colinfo->is_new_col[j] = false;
3970 :
3971 : /* build bitmapsets of child attnums of merged columns */
3972 84 : if (colinfo->leftattnos[i] > 0)
3973 84 : leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
3974 84 : if (colinfo->rightattnos[i] > 0)
3975 84 : rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);
3976 :
3977 84 : i++, j++;
3978 : }
3979 :
3980 : /* Handle non-merged left-child columns */
3981 136 : ic = 0;
3982 2527 : for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
3983 : {
3984 2391 : char *child_colname = leftcolinfo->new_colnames[jc];
3985 :
3986 2391 : if (!leftcolinfo->is_new_col[jc])
3987 : {
3988 : /* Advance ic to next non-dropped old column of left child */
3989 6958 : while (ic < leftcolinfo->num_cols &&
3990 2324 : leftcolinfo->colnames[ic] == NULL)
3991 14 : ic++;
3992 2310 : Assert(ic < leftcolinfo->num_cols);
3993 2310 : ic++;
3994 : /* If it is a merged column, we already processed it */
3995 2310 : if (bms_is_member(ic, leftmerged))
3996 84 : continue;
3997 : /* Else, advance i to the corresponding existing join column */
3998 6680 : while (i < colinfo->num_cols &&
3999 2227 : colinfo->colnames[i] == NULL)
4000 1 : i++;
4001 2226 : Assert(i < colinfo->num_cols);
4002 2226 : Assert(ic == colinfo->leftattnos[i]);
4003 : /* Use the already-assigned name of this column */
4004 2226 : colinfo->new_colnames[j] = colinfo->colnames[i];
4005 2226 : i++;
4006 : }
4007 : else
4008 : {
4009 : /*
4010 : * Unique-ify the new child column name and assign, unless we're
4011 : * in an unnamed join, in which case just copy
4012 : */
4013 81 : if (rte->alias != NULL)
4014 : {
4015 44 : colinfo->new_colnames[j] =
4016 22 : make_colname_unique(child_colname, dpns, colinfo);
4017 40 : if (!changed_any &&
4018 18 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4019 2 : changed_any = true;
4020 : }
4021 : else
4022 59 : colinfo->new_colnames[j] = child_colname;
4023 : }
4024 :
4025 2307 : colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
4026 2307 : j++;
4027 : }
4028 :
4029 : /* Handle non-merged right-child columns in exactly the same way */
4030 136 : ic = 0;
4031 1198 : for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
4032 : {
4033 1062 : char *child_colname = rightcolinfo->new_colnames[jc];
4034 :
4035 1062 : if (!rightcolinfo->is_new_col[jc])
4036 : {
4037 : /* Advance ic to next non-dropped old column of right child */
4038 3108 : while (ic < rightcolinfo->num_cols &&
4039 1036 : rightcolinfo->colnames[ic] == NULL)
4040 0 : ic++;
4041 1036 : Assert(ic < rightcolinfo->num_cols);
4042 1036 : ic++;
4043 : /* If it is a merged column, we already processed it */
4044 1036 : if (bms_is_member(ic, rightmerged))
4045 84 : continue;
4046 : /* Else, advance i to the corresponding existing join column */
4047 2856 : while (i < colinfo->num_cols &&
4048 952 : colinfo->colnames[i] == NULL)
4049 0 : i++;
4050 952 : Assert(i < colinfo->num_cols);
4051 952 : Assert(ic == colinfo->rightattnos[i]);
4052 : /* Use the already-assigned name of this column */
4053 952 : colinfo->new_colnames[j] = colinfo->colnames[i];
4054 952 : i++;
4055 : }
4056 : else
4057 : {
4058 : /*
4059 : * Unique-ify the new child column name and assign, unless we're
4060 : * in an unnamed join, in which case just copy
4061 : */
4062 26 : if (rte->alias != NULL)
4063 : {
4064 8 : colinfo->new_colnames[j] =
4065 4 : make_colname_unique(child_colname, dpns, colinfo);
4066 8 : if (!changed_any &&
4067 4 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4068 2 : changed_any = true;
4069 : }
4070 : else
4071 22 : colinfo->new_colnames[j] = child_colname;
4072 : }
4073 :
4074 978 : colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
4075 978 : j++;
4076 : }
4077 :
4078 : /* Assert we processed the right number of columns */
4079 : #ifdef USE_ASSERT_CHECKING
4080 272 : while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
4081 0 : i++;
4082 136 : Assert(i == colinfo->num_cols);
4083 136 : Assert(j == nnewcolumns);
4084 : #endif
4085 :
4086 : /*
4087 : * For a named join, print column aliases if we changed any from the child
4088 : * names. Unnamed joins cannot print aliases.
4089 : */
4090 136 : if (rte->alias != NULL)
4091 14 : colinfo->printaliases = changed_any;
4092 : else
4093 122 : colinfo->printaliases = false;
4094 136 : }
4095 :
4096 : /*
4097 : * colname_is_unique: is colname distinct from already-chosen column names?
4098 : *
4099 : * dpns is query-wide info, colinfo is for the column's RTE
4100 : */
4101 : static bool
4102 30447 : colname_is_unique(char *colname, deparse_namespace *dpns,
4103 : deparse_columns *colinfo)
4104 : {
4105 : int i;
4106 : ListCell *lc;
4107 :
4108 : /* Check against already-assigned column aliases within RTE */
4109 598084 : for (i = 0; i < colinfo->num_cols; i++)
4110 : {
4111 568412 : char *oldname = colinfo->colnames[i];
4112 :
4113 568412 : if (oldname && strcmp(oldname, colname) == 0)
4114 775 : return false;
4115 : }
4116 :
4117 : /*
4118 : * If we're building a new_colnames array, check that too (this will be
4119 : * partially but not completely redundant with the previous checks)
4120 : */
4121 29884 : for (i = 0; i < colinfo->num_new_cols; i++)
4122 : {
4123 216 : char *oldname = colinfo->new_colnames[i];
4124 :
4125 216 : if (oldname && strcmp(oldname, colname) == 0)
4126 4 : return false;
4127 : }
4128 :
4129 : /* Also check against USING-column names that must be globally unique */
4130 29938 : foreach(lc, dpns->using_names)
4131 : {
4132 297 : char *oldname = (char *) lfirst(lc);
4133 :
4134 297 : if (strcmp(oldname, colname) == 0)
4135 27 : return false;
4136 : }
4137 :
4138 : /* Also check against names already assigned for parent-join USING cols */
4139 30015 : foreach(lc, colinfo->parentUsing)
4140 : {
4141 375 : char *oldname = (char *) lfirst(lc);
4142 :
4143 375 : if (strcmp(oldname, colname) == 0)
4144 1 : return false;
4145 : }
4146 :
4147 29640 : return true;
4148 : }
4149 :
4150 : /*
4151 : * make_colname_unique: modify colname if necessary to make it unique
4152 : *
4153 : * dpns is query-wide info, colinfo is for the column's RTE
4154 : */
4155 : static char *
4156 29640 : make_colname_unique(char *colname, deparse_namespace *dpns,
4157 : deparse_columns *colinfo)
4158 : {
4159 : /*
4160 : * If the selected name isn't unique, append digits to make it so. For a
4161 : * very long input name, we might have to truncate to stay within
4162 : * NAMEDATALEN.
4163 : */
4164 29640 : if (!colname_is_unique(colname, dpns, colinfo))
4165 : {
4166 632 : int colnamelen = strlen(colname);
4167 632 : char *modname = (char *) palloc(colnamelen + 16);
4168 632 : int i = 0;
4169 :
4170 : do
4171 : {
4172 807 : i++;
4173 : for (;;)
4174 : {
4175 : /*
4176 : * We avoid using %.*s here because it can misbehave if the
4177 : * data is not valid in what libc thinks is the prevailing
4178 : * encoding.
4179 : */
4180 807 : memcpy(modname, colname, colnamelen);
4181 807 : sprintf(modname + colnamelen, "_%d", i);
4182 807 : if (strlen(modname) < NAMEDATALEN)
4183 807 : break;
4184 : /* drop chars from colname to keep all the digits */
4185 0 : colnamelen = pg_mbcliplen(colname, colnamelen,
4186 : colnamelen - 1);
4187 0 : }
4188 807 : } while (!colname_is_unique(modname, dpns, colinfo));
4189 632 : colname = modname;
4190 : }
4191 29640 : return colname;
4192 : }
4193 :
4194 : /*
4195 : * expand_colnames_array_to: make colinfo->colnames at least n items long
4196 : *
4197 : * Any added array entries are initialized to zero.
4198 : */
4199 : static void
4200 4721 : expand_colnames_array_to(deparse_columns *colinfo, int n)
4201 : {
4202 4721 : if (n > colinfo->num_cols)
4203 : {
4204 4657 : if (colinfo->colnames == NULL)
4205 4458 : colinfo->colnames = (char **) palloc0(n * sizeof(char *));
4206 : else
4207 : {
4208 199 : colinfo->colnames = (char **) repalloc(colinfo->colnames,
4209 : n * sizeof(char *));
4210 199 : memset(colinfo->colnames + colinfo->num_cols, 0,
4211 199 : (n - colinfo->num_cols) * sizeof(char *));
4212 : }
4213 4657 : colinfo->num_cols = n;
4214 : }
4215 4721 : }
4216 :
4217 : /*
4218 : * identify_join_columns: figure out where columns of a join come from
4219 : *
4220 : * Fills the join-specific fields of the colinfo struct, except for
4221 : * usingNames which is filled later.
4222 : */
4223 : static void
4224 136 : identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
4225 : deparse_columns *colinfo)
4226 : {
4227 : int numjoincols;
4228 : int i;
4229 : ListCell *lc;
4230 :
4231 : /* Extract left/right child RT indexes */
4232 136 : if (IsA(j->larg, RangeTblRef))
4233 89 : colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
4234 47 : else if (IsA(j->larg, JoinExpr))
4235 47 : colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
4236 : else
4237 0 : elog(ERROR, "unrecognized node type in jointree: %d",
4238 : (int) nodeTag(j->larg));
4239 136 : if (IsA(j->rarg, RangeTblRef))
4240 136 : colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
4241 0 : else if (IsA(j->rarg, JoinExpr))
4242 0 : colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
4243 : else
4244 0 : elog(ERROR, "unrecognized node type in jointree: %d",
4245 : (int) nodeTag(j->rarg));
4246 :
4247 : /* Assert children will be processed earlier than join in second pass */
4248 136 : Assert(colinfo->leftrti < j->rtindex);
4249 136 : Assert(colinfo->rightrti < j->rtindex);
4250 :
4251 : /* Initialize result arrays with zeroes */
4252 136 : numjoincols = list_length(jrte->joinaliasvars);
4253 136 : Assert(numjoincols == list_length(jrte->eref->colnames));
4254 136 : colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
4255 136 : colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
4256 :
4257 : /* Scan the joinaliasvars list to identify simple column references */
4258 136 : i = 0;
4259 3399 : foreach(lc, jrte->joinaliasvars)
4260 : {
4261 3263 : Var *aliasvar = (Var *) lfirst(lc);
4262 :
4263 : /* get rid of any implicit coercion above the Var */
4264 3263 : aliasvar = (Var *) strip_implicit_coercions((Node *) aliasvar);
4265 :
4266 3263 : if (aliasvar == NULL)
4267 : {
4268 : /* It's a dropped column; nothing to do here */
4269 : }
4270 3262 : else if (IsA(aliasvar, Var))
4271 : {
4272 3245 : Assert(aliasvar->varlevelsup == 0);
4273 3245 : Assert(aliasvar->varattno != 0);
4274 3245 : if (aliasvar->varno == colinfo->leftrti)
4275 2268 : colinfo->leftattnos[i] = aliasvar->varattno;
4276 977 : else if (aliasvar->varno == colinfo->rightrti)
4277 977 : colinfo->rightattnos[i] = aliasvar->varattno;
4278 : else
4279 0 : elog(ERROR, "unexpected varno %d in JOIN RTE",
4280 : aliasvar->varno);
4281 : }
4282 17 : else if (IsA(aliasvar, CoalesceExpr))
4283 : {
4284 : /*
4285 : * It's a merged column in FULL JOIN USING. Ignore it for now and
4286 : * let the code below identify the merged columns.
4287 : */
4288 : }
4289 : else
4290 0 : elog(ERROR, "unrecognized node type in join alias vars: %d",
4291 : (int) nodeTag(aliasvar));
4292 :
4293 3263 : i++;
4294 : }
4295 :
4296 : /*
4297 : * If there's a USING clause, deconstruct the join quals to identify the
4298 : * merged columns. This is a tad painful but if we cannot rely on the
4299 : * column names, there is no other representation of which columns were
4300 : * joined by USING. (Unless the join type is FULL, we can't tell from the
4301 : * joinaliasvars list which columns are merged.) Note: we assume that the
4302 : * merged columns are the first output column(s) of the join.
4303 : */
4304 136 : if (j->usingClause)
4305 : {
4306 59 : List *leftvars = NIL;
4307 59 : List *rightvars = NIL;
4308 : ListCell *lc2;
4309 :
4310 : /* Extract left- and right-side Vars from the qual expression */
4311 59 : flatten_join_using_qual(j->quals, &leftvars, &rightvars);
4312 59 : Assert(list_length(leftvars) == list_length(j->usingClause));
4313 59 : Assert(list_length(rightvars) == list_length(j->usingClause));
4314 :
4315 : /* Mark the output columns accordingly */
4316 59 : i = 0;
4317 143 : forboth(lc, leftvars, lc2, rightvars)
4318 : {
4319 84 : Var *leftvar = (Var *) lfirst(lc);
4320 84 : Var *rightvar = (Var *) lfirst(lc2);
4321 :
4322 84 : Assert(leftvar->varlevelsup == 0);
4323 84 : Assert(leftvar->varattno != 0);
4324 84 : if (leftvar->varno != colinfo->leftrti)
4325 0 : elog(ERROR, "unexpected varno %d in JOIN USING qual",
4326 : leftvar->varno);
4327 84 : colinfo->leftattnos[i] = leftvar->varattno;
4328 :
4329 84 : Assert(rightvar->varlevelsup == 0);
4330 84 : Assert(rightvar->varattno != 0);
4331 84 : if (rightvar->varno != colinfo->rightrti)
4332 0 : elog(ERROR, "unexpected varno %d in JOIN USING qual",
4333 : rightvar->varno);
4334 84 : colinfo->rightattnos[i] = rightvar->varattno;
4335 :
4336 84 : i++;
4337 : }
4338 : }
4339 136 : }
4340 :
4341 : /*
4342 : * flatten_join_using_qual: extract Vars being joined from a JOIN/USING qual
4343 : *
4344 : * We assume that transformJoinUsingClause won't have produced anything except
4345 : * AND nodes, equality operator nodes, and possibly implicit coercions, and
4346 : * that the AND node inputs match left-to-right with the original USING list.
4347 : *
4348 : * Caller must initialize the result lists to NIL.
4349 : */
4350 : static void
4351 109 : flatten_join_using_qual(Node *qual, List **leftvars, List **rightvars)
4352 : {
4353 109 : if (IsA(qual, BoolExpr))
4354 : {
4355 : /* Handle AND nodes by recursion */
4356 25 : BoolExpr *b = (BoolExpr *) qual;
4357 : ListCell *lc;
4358 :
4359 25 : Assert(b->boolop == AND_EXPR);
4360 75 : foreach(lc, b->args)
4361 : {
4362 50 : flatten_join_using_qual((Node *) lfirst(lc),
4363 : leftvars, rightvars);
4364 : }
4365 : }
4366 84 : else if (IsA(qual, OpExpr))
4367 : {
4368 : /* Otherwise we should have an equality operator */
4369 84 : OpExpr *op = (OpExpr *) qual;
4370 : Var *var;
4371 :
4372 84 : if (list_length(op->args) != 2)
4373 0 : elog(ERROR, "unexpected unary operator in JOIN/USING qual");
4374 : /* Arguments should be Vars with perhaps implicit coercions */
4375 84 : var = (Var *) strip_implicit_coercions((Node *) linitial(op->args));
4376 84 : if (!IsA(var, Var))
4377 0 : elog(ERROR, "unexpected node type in JOIN/USING qual: %d",
4378 : (int) nodeTag(var));
4379 84 : *leftvars = lappend(*leftvars, var);
4380 84 : var = (Var *) strip_implicit_coercions((Node *) lsecond(op->args));
4381 84 : if (!IsA(var, Var))
4382 0 : elog(ERROR, "unexpected node type in JOIN/USING qual: %d",
4383 : (int) nodeTag(var));
4384 84 : *rightvars = lappend(*rightvars, var);
4385 : }
4386 : else
4387 : {
4388 : /* Perhaps we have an implicit coercion to boolean? */
4389 0 : Node *q = strip_implicit_coercions(qual);
4390 :
4391 0 : if (q != qual)
4392 0 : flatten_join_using_qual(q, leftvars, rightvars);
4393 : else
4394 0 : elog(ERROR, "unexpected node type in JOIN/USING qual: %d",
4395 : (int) nodeTag(qual));
4396 : }
4397 109 : }
4398 :
4399 : /*
4400 : * get_rtable_name: convenience function to get a previously assigned RTE alias
4401 : *
4402 : * The RTE must belong to the topmost namespace level in "context".
4403 : */
4404 : static char *
4405 438 : get_rtable_name(int rtindex, deparse_context *context)
4406 : {
4407 438 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
4408 :
4409 438 : Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
4410 438 : return (char *) list_nth(dpns->rtable_names, rtindex - 1);
4411 : }
4412 :
4413 : /*
4414 : * set_deparse_planstate: set up deparse_namespace to parse subexpressions
4415 : * of a given PlanState node
4416 : *
4417 : * This sets the planstate, outer_planstate, inner_planstate, outer_tlist,
4418 : * inner_tlist, and index_tlist fields. Caller is responsible for adjusting
4419 : * the ancestors list if necessary. Note that the rtable and ctes fields do
4420 : * not need to change when shifting attention to different plan nodes in a
4421 : * single plan tree.
4422 : */
4423 : static void
4424 4310 : set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
4425 : {
4426 4310 : dpns->planstate = ps;
4427 :
4428 : /*
4429 : * We special-case Append and MergeAppend to pretend that the first child
4430 : * plan is the OUTER referent; we have to interpret OUTER Vars in their
4431 : * tlists according to one of the children, and the first one is the most
4432 : * natural choice. Likewise special-case ModifyTable to pretend that the
4433 : * first child plan is the OUTER referent; this is to support RETURNING
4434 : * lists containing references to non-target relations.
4435 : */
4436 4310 : if (IsA(ps, AppendState))
4437 24 : dpns->outer_planstate = ((AppendState *) ps)->appendplans[0];
4438 4286 : else if (IsA(ps, MergeAppendState))
4439 30 : dpns->outer_planstate = ((MergeAppendState *) ps)->mergeplans[0];
4440 4256 : else if (IsA(ps, ModifyTableState))
4441 17 : dpns->outer_planstate = ((ModifyTableState *) ps)->mt_plans[0];
4442 : else
4443 4239 : dpns->outer_planstate = outerPlanState(ps);
4444 :
4445 4310 : if (dpns->outer_planstate)
4446 1742 : dpns->outer_tlist = dpns->outer_planstate->plan->targetlist;
4447 : else
4448 2568 : dpns->outer_tlist = NIL;
4449 :
4450 : /*
4451 : * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
4452 : * use OUTER because that could someday conflict with the normal meaning.)
4453 : * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
4454 : * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the
4455 : * excluded expression's tlist. (Similar to the SubqueryScan we don't want
4456 : * to reuse OUTER, it's used for RETURNING in some modify table cases,
4457 : * although not INSERT .. CONFLICT).
4458 : */
4459 4310 : if (IsA(ps, SubqueryScanState))
4460 40 : dpns->inner_planstate = ((SubqueryScanState *) ps)->subplan;
4461 4270 : else if (IsA(ps, CteScanState))
4462 13 : dpns->inner_planstate = ((CteScanState *) ps)->cteplanstate;
4463 4257 : else if (IsA(ps, ModifyTableState))
4464 17 : dpns->inner_planstate = ps;
4465 : else
4466 4240 : dpns->inner_planstate = innerPlanState(ps);
4467 :
4468 4310 : if (IsA(ps, ModifyTableState))
4469 17 : dpns->inner_tlist = ((ModifyTableState *) ps)->mt_excludedtlist;
4470 4293 : else if (dpns->inner_planstate)
4471 773 : dpns->inner_tlist = dpns->inner_planstate->plan->targetlist;
4472 : else
4473 3520 : dpns->inner_tlist = NIL;
4474 :
4475 : /* Set up referent for INDEX_VAR Vars, if needed */
4476 4310 : if (IsA(ps->plan, IndexOnlyScan))
4477 206 : dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist;
4478 4104 : else if (IsA(ps->plan, ForeignScan))
4479 0 : dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_scan_tlist;
4480 4104 : else if (IsA(ps->plan, CustomScan))
4481 0 : dpns->index_tlist = ((CustomScan *) ps->plan)->custom_scan_tlist;
4482 : else
4483 4104 : dpns->index_tlist = NIL;
4484 4310 : }
4485 :
4486 : /*
4487 : * push_child_plan: temporarily transfer deparsing attention to a child plan
4488 : *
4489 : * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
4490 : * deparse context in case the referenced expression itself uses
4491 : * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
4492 : * affecting levelsup issues (although in a Plan tree there really shouldn't
4493 : * be any).
4494 : *
4495 : * Caller must provide a local deparse_namespace variable to save the
4496 : * previous state for pop_child_plan.
4497 : */
4498 : static void
4499 1840 : push_child_plan(deparse_namespace *dpns, PlanState *ps,
4500 : deparse_namespace *save_dpns)
4501 : {
4502 : /* Save state for restoration later */
4503 1840 : *save_dpns = *dpns;
4504 :
4505 : /* Link current plan node into ancestors list */
4506 1840 : dpns->ancestors = lcons(dpns->planstate, dpns->ancestors);
4507 :
4508 : /* Set attention on selected child */
4509 1840 : set_deparse_planstate(dpns, ps);
4510 1840 : }
4511 :
4512 : /*
4513 : * pop_child_plan: undo the effects of push_child_plan
4514 : */
4515 : static void
4516 1840 : pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
4517 : {
4518 : List *ancestors;
4519 :
4520 : /* Get rid of ancestors list cell added by push_child_plan */
4521 1840 : ancestors = list_delete_first(dpns->ancestors);
4522 :
4523 : /* Restore fields changed by push_child_plan */
4524 1840 : *dpns = *save_dpns;
4525 :
4526 : /* Make sure dpns->ancestors is right (may be unnecessary) */
4527 1840 : dpns->ancestors = ancestors;
4528 1840 : }
4529 :
4530 : /*
4531 : * push_ancestor_plan: temporarily transfer deparsing attention to an
4532 : * ancestor plan
4533 : *
4534 : * When expanding a Param reference, we must adjust the deparse context
4535 : * to match the plan node that contains the expression being printed;
4536 : * otherwise we'd fail if that expression itself contains a Param or
4537 : * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
4538 : *
4539 : * The target ancestor is conveniently identified by the ListCell holding it
4540 : * in dpns->ancestors.
4541 : *
4542 : * Caller must provide a local deparse_namespace variable to save the
4543 : * previous state for pop_ancestor_plan.
4544 : */
4545 : static void
4546 194 : push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
4547 : deparse_namespace *save_dpns)
4548 : {
4549 194 : PlanState *ps = (PlanState *) lfirst(ancestor_cell);
4550 : List *ancestors;
4551 :
4552 : /* Save state for restoration later */
4553 194 : *save_dpns = *dpns;
4554 :
4555 : /* Build a new ancestor list with just this node's ancestors */
4556 194 : ancestors = NIL;
4557 484 : while ((ancestor_cell = lnext(ancestor_cell)) != NULL)
4558 96 : ancestors = lappend(ancestors, lfirst(ancestor_cell));
4559 194 : dpns->ancestors = ancestors;
4560 :
4561 : /* Set attention on selected ancestor */
4562 194 : set_deparse_planstate(dpns, ps);
4563 194 : }
4564 :
4565 : /*
4566 : * pop_ancestor_plan: undo the effects of push_ancestor_plan
4567 : */
4568 : static void
4569 194 : pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
4570 : {
4571 : /* Free the ancestor list made in push_ancestor_plan */
4572 194 : list_free(dpns->ancestors);
4573 :
4574 : /* Restore fields changed by push_ancestor_plan */
4575 194 : *dpns = *save_dpns;
4576 194 : }
4577 :
4578 :
4579 : /* ----------
4580 : * make_ruledef - reconstruct the CREATE RULE command
4581 : * for a given pg_rewrite tuple
4582 : * ----------
4583 : */
4584 : static void
4585 48 : make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
4586 : int prettyFlags)
4587 : {
4588 : char *rulename;
4589 : char ev_type;
4590 : Oid ev_class;
4591 : bool is_instead;
4592 : char *ev_qual;
4593 : char *ev_action;
4594 48 : List *actions = NIL;
4595 : Relation ev_relation;
4596 48 : TupleDesc viewResultDesc = NULL;
4597 : int fno;
4598 : Datum dat;
4599 : bool isnull;
4600 :
4601 : /*
4602 : * Get the attribute values from the rules tuple
4603 : */
4604 48 : fno = SPI_fnumber(rulettc, "rulename");
4605 48 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
4606 48 : Assert(!isnull);
4607 48 : rulename = NameStr(*(DatumGetName(dat)));
4608 :
4609 48 : fno = SPI_fnumber(rulettc, "ev_type");
4610 48 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
4611 48 : Assert(!isnull);
4612 48 : ev_type = DatumGetChar(dat);
4613 :
4614 48 : fno = SPI_fnumber(rulettc, "ev_class");
4615 48 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
4616 48 : Assert(!isnull);
4617 48 : ev_class = DatumGetObjectId(dat);
4618 :
4619 48 : fno = SPI_fnumber(rulettc, "is_instead");
4620 48 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
4621 48 : Assert(!isnull);
4622 48 : is_instead = DatumGetBool(dat);
4623 :
4624 : /* these could be nulls */
4625 48 : fno = SPI_fnumber(rulettc, "ev_qual");
4626 48 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
4627 :
4628 48 : fno = SPI_fnumber(rulettc, "ev_action");
4629 48 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
4630 48 : if (ev_action != NULL)
4631 48 : actions = (List *) stringToNode(ev_action);
4632 :
4633 48 : ev_relation = heap_open(ev_class, AccessShareLock);
4634 :
4635 : /*
4636 : * Build the rules definition text
4637 : */
4638 48 : appendStringInfo(buf, "CREATE RULE %s AS",
4639 : quote_identifier(rulename));
4640 :
4641 48 : if (prettyFlags & PRETTYFLAG_INDENT)
4642 48 : appendStringInfoString(buf, "\n ON ");
4643 : else
4644 0 : appendStringInfoString(buf, " ON ");
4645 :
4646 : /* The event the rule is fired for */
4647 48 : switch (ev_type)
4648 : {
4649 : case '1':
4650 1 : appendStringInfoString(buf, "SELECT");
4651 1 : viewResultDesc = RelationGetDescr(ev_relation);
4652 1 : break;
4653 :
4654 : case '2':
4655 13 : appendStringInfoString(buf, "UPDATE");
4656 13 : break;
4657 :
4658 : case '3':
4659 26 : appendStringInfoString(buf, "INSERT");
4660 26 : break;
4661 :
4662 : case '4':
4663 8 : appendStringInfoString(buf, "DELETE");
4664 8 : break;
4665 :
4666 : default:
4667 0 : ereport(ERROR,
4668 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4669 : errmsg("rule \"%s\" has unsupported event type %d",
4670 : rulename, ev_type)));
4671 : break;
4672 : }
4673 :
4674 : /* The relation the rule is fired on */
4675 48 : appendStringInfo(buf, " TO %s", generate_relation_name(ev_class, NIL));
4676 :
4677 : /* If the rule has an event qualification, add it */
4678 48 : if (ev_qual == NULL)
4679 0 : ev_qual = "";
4680 48 : if (strlen(ev_qual) > 0 && strcmp(ev_qual, "<>") != 0)
4681 : {
4682 : Node *qual;
4683 : Query *query;
4684 : deparse_context context;
4685 : deparse_namespace dpns;
4686 :
4687 11 : if (prettyFlags & PRETTYFLAG_INDENT)
4688 11 : appendStringInfoString(buf, "\n ");
4689 11 : appendStringInfoString(buf, " WHERE ");
4690 :
4691 11 : qual = stringToNode(ev_qual);
4692 :
4693 : /*
4694 : * We need to make a context for recognizing any Vars in the qual
4695 : * (which can only be references to OLD and NEW). Use the rtable of
4696 : * the first query in the action list for this purpose.
4697 : */
4698 11 : query = (Query *) linitial(actions);
4699 :
4700 : /*
4701 : * If the action is INSERT...SELECT, OLD/NEW have been pushed down
4702 : * into the SELECT, and that's what we need to look at. (Ugly kluge
4703 : * ... try to fix this when we redesign querytrees.)
4704 : */
4705 11 : query = getInsertSelectQuery(query, NULL);
4706 :
4707 : /* Must acquire locks right away; see notes in get_query_def() */
4708 11 : AcquireRewriteLocks(query, false, false);
4709 :
4710 11 : context.buf = buf;
4711 11 : context.namespaces = list_make1(&dpns);
4712 11 : context.windowClause = NIL;
4713 11 : context.windowTList = NIL;
4714 11 : context.varprefix = (list_length(query->rtable) != 1);
4715 11 : context.prettyFlags = prettyFlags;
4716 11 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
4717 11 : context.indentLevel = PRETTYINDENT_STD;
4718 11 : context.special_exprkind = EXPR_KIND_NONE;
4719 :
4720 11 : set_deparse_for_query(&dpns, query, NIL);
4721 :
4722 11 : get_rule_expr(qual, &context, false);
4723 : }
4724 :
4725 48 : appendStringInfoString(buf, " DO ");
4726 :
4727 : /* The INSTEAD keyword (if so) */
4728 48 : if (is_instead)
4729 27 : appendStringInfoString(buf, "INSTEAD ");
4730 :
4731 : /* Finally the rules actions */
4732 48 : if (list_length(actions) > 1)
4733 : {
4734 : ListCell *action;
4735 : Query *query;
4736 :
4737 2 : appendStringInfoChar(buf, '(');
4738 6 : foreach(action, actions)
4739 : {
4740 4 : query = (Query *) lfirst(action);
4741 4 : get_query_def(query, buf, NIL, viewResultDesc,
4742 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
4743 4 : if (prettyFlags)
4744 4 : appendStringInfoString(buf, ";\n");
4745 : else
4746 0 : appendStringInfoString(buf, "; ");
4747 : }
4748 2 : appendStringInfoString(buf, ");");
4749 : }
4750 46 : else if (list_length(actions) == 0)
4751 : {
4752 0 : appendStringInfoString(buf, "NOTHING;");
4753 : }
4754 : else
4755 : {
4756 : Query *query;
4757 :
4758 46 : query = (Query *) linitial(actions);
4759 46 : get_query_def(query, buf, NIL, viewResultDesc,
4760 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
4761 46 : appendStringInfoChar(buf, ';');
4762 : }
4763 :
4764 48 : heap_close(ev_relation, AccessShareLock);
4765 48 : }
4766 :
4767 :
4768 : /* ----------
4769 : * make_viewdef - reconstruct the SELECT part of a
4770 : * view rewrite rule
4771 : * ----------
4772 : */
4773 : static void
4774 214 : make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
4775 : int prettyFlags, int wrapColumn)
4776 : {
4777 : Query *query;
4778 : char ev_type;
4779 : Oid ev_class;
4780 : bool is_instead;
4781 : char *ev_qual;
4782 : char *ev_action;
4783 214 : List *actions = NIL;
4784 : Relation ev_relation;
4785 : int fno;
4786 : Datum dat;
4787 : bool isnull;
4788 :
4789 : /*
4790 : * Get the attribute values from the rules tuple
4791 : */
4792 214 : fno = SPI_fnumber(rulettc, "ev_type");
4793 214 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
4794 214 : Assert(!isnull);
4795 214 : ev_type = DatumGetChar(dat);
4796 :
4797 214 : fno = SPI_fnumber(rulettc, "ev_class");
4798 214 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
4799 214 : Assert(!isnull);
4800 214 : ev_class = DatumGetObjectId(dat);
4801 :
4802 214 : fno = SPI_fnumber(rulettc, "is_instead");
4803 214 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
4804 214 : Assert(!isnull);
4805 214 : is_instead = DatumGetBool(dat);
4806 :
4807 : /* these could be nulls */
4808 214 : fno = SPI_fnumber(rulettc, "ev_qual");
4809 214 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
4810 :
4811 214 : fno = SPI_fnumber(rulettc, "ev_action");
4812 214 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
4813 214 : if (ev_action != NULL)
4814 214 : actions = (List *) stringToNode(ev_action);
4815 :
4816 214 : if (list_length(actions) != 1)
4817 : {
4818 : /* keep output buffer empty and leave */
4819 0 : return;
4820 : }
4821 :
4822 214 : query = (Query *) linitial(actions);
4823 :
4824 428 : if (ev_type != '1' || !is_instead ||
4825 428 : strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
4826 : {
4827 : /* keep output buffer empty and leave */
4828 0 : return;
4829 : }
4830 :
4831 214 : ev_relation = heap_open(ev_class, AccessShareLock);
4832 :
4833 214 : get_query_def(query, buf, NIL, RelationGetDescr(ev_relation),
4834 : prettyFlags, wrapColumn, 0);
4835 214 : appendStringInfoChar(buf, ';');
4836 :
4837 214 : heap_close(ev_relation, AccessShareLock);
4838 : }
4839 :
4840 :
4841 : /* ----------
4842 : * get_query_def - Parse back one query parsetree
4843 : *
4844 : * If resultDesc is not NULL, then it is the output tuple descriptor for
4845 : * the view represented by a SELECT query.
4846 : * ----------
4847 : */
4848 : static void
4849 371 : get_query_def(Query *query, StringInfo buf, List *parentnamespace,
4850 : TupleDesc resultDesc,
4851 : int prettyFlags, int wrapColumn, int startIndent)
4852 : {
4853 : deparse_context context;
4854 : deparse_namespace dpns;
4855 :
4856 : /* Guard against excessively long or deeply-nested queries */
4857 371 : CHECK_FOR_INTERRUPTS();
4858 371 : check_stack_depth();
4859 :
4860 : /*
4861 : * Before we begin to examine the query, acquire locks on referenced
4862 : * relations, and fix up deleted columns in JOIN RTEs. This ensures
4863 : * consistent results. Note we assume it's OK to scribble on the passed
4864 : * querytree!
4865 : *
4866 : * We are only deparsing the query (we are not about to execute it), so we
4867 : * only need AccessShareLock on the relations it mentions.
4868 : */
4869 371 : AcquireRewriteLocks(query, false, false);
4870 :
4871 371 : context.buf = buf;
4872 371 : context.namespaces = lcons(&dpns, list_copy(parentnamespace));
4873 371 : context.windowClause = NIL;
4874 371 : context.windowTList = NIL;
4875 637 : context.varprefix = (parentnamespace != NIL ||
4876 266 : list_length(query->rtable) != 1);
4877 371 : context.prettyFlags = prettyFlags;
4878 371 : context.wrapColumn = wrapColumn;
4879 371 : context.indentLevel = startIndent;
4880 371 : context.special_exprkind = EXPR_KIND_NONE;
4881 :
4882 371 : set_deparse_for_query(&dpns, query, parentnamespace);
4883 :
4884 371 : switch (query->commandType)
4885 : {
4886 : case CMD_SELECT:
4887 325 : get_select_query_def(query, &context, resultDesc);
4888 325 : break;
4889 :
4890 : case CMD_UPDATE:
4891 8 : get_update_query_def(query, &context);
4892 8 : break;
4893 :
4894 : case CMD_INSERT:
4895 27 : get_insert_query_def(query, &context);
4896 27 : break;
4897 :
4898 : case CMD_DELETE:
4899 5 : get_delete_query_def(query, &context);
4900 5 : break;
4901 :
4902 : case CMD_NOTHING:
4903 4 : appendStringInfoString(buf, "NOTHING");
4904 4 : break;
4905 :
4906 : case CMD_UTILITY:
4907 2 : get_utility_query_def(query, &context);
4908 2 : break;
4909 :
4910 : default:
4911 0 : elog(ERROR, "unrecognized query command type: %d",
4912 : query->commandType);
4913 : break;
4914 : }
4915 371 : }
4916 :
4917 : /* ----------
4918 : * get_values_def - Parse back a VALUES list
4919 : * ----------
4920 : */
4921 : static void
4922 29 : get_values_def(List *values_lists, deparse_context *context)
4923 : {
4924 29 : StringInfo buf = context->buf;
4925 29 : bool first_list = true;
4926 : ListCell *vtl;
4927 :
4928 29 : appendStringInfoString(buf, "VALUES ");
4929 :
4930 85 : foreach(vtl, values_lists)
4931 : {
4932 56 : List *sublist = (List *) lfirst(vtl);
4933 56 : bool first_col = true;
4934 : ListCell *lc;
4935 :
4936 56 : if (first_list)
4937 29 : first_list = false;
4938 : else
4939 27 : appendStringInfoString(buf, ", ");
4940 :
4941 56 : appendStringInfoChar(buf, '(');
4942 230 : foreach(lc, sublist)
4943 : {
4944 174 : Node *col = (Node *) lfirst(lc);
4945 :
4946 174 : if (first_col)
4947 56 : first_col = false;
4948 : else
4949 118 : appendStringInfoChar(buf, ',');
4950 :
4951 : /*
4952 : * Print the value. Whole-row Vars need special treatment.
4953 : */
4954 174 : get_rule_expr_toplevel(col, context, false);
4955 : }
4956 56 : appendStringInfoChar(buf, ')');
4957 : }
4958 29 : }
4959 :
4960 : /* ----------
4961 : * get_with_clause - Parse back a WITH clause
4962 : * ----------
4963 : */
4964 : static void
4965 365 : get_with_clause(Query *query, deparse_context *context)
4966 : {
4967 365 : StringInfo buf = context->buf;
4968 : const char *sep;
4969 : ListCell *l;
4970 :
4971 365 : if (query->cteList == NIL)
4972 727 : return;
4973 :
4974 3 : if (PRETTY_INDENT(context))
4975 : {
4976 3 : context->indentLevel += PRETTYINDENT_STD;
4977 3 : appendStringInfoChar(buf, ' ');
4978 : }
4979 :
4980 3 : if (query->hasRecursive)
4981 3 : sep = "WITH RECURSIVE ";
4982 : else
4983 0 : sep = "WITH ";
4984 6 : foreach(l, query->cteList)
4985 : {
4986 3 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
4987 :
4988 3 : appendStringInfoString(buf, sep);
4989 3 : appendStringInfoString(buf, quote_identifier(cte->ctename));
4990 3 : if (cte->aliascolnames)
4991 : {
4992 1 : bool first = true;
4993 : ListCell *col;
4994 :
4995 1 : appendStringInfoChar(buf, '(');
4996 2 : foreach(col, cte->aliascolnames)
4997 : {
4998 1 : if (first)
4999 1 : first = false;
5000 : else
5001 0 : appendStringInfoString(buf, ", ");
5002 1 : appendStringInfoString(buf,
5003 1 : quote_identifier(strVal(lfirst(col))));
5004 : }
5005 1 : appendStringInfoChar(buf, ')');
5006 : }
5007 3 : appendStringInfoString(buf, " AS (");
5008 3 : if (PRETTY_INDENT(context))
5009 3 : appendContextKeyword(context, "", 0, 0, 0);
5010 3 : get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
5011 : context->prettyFlags, context->wrapColumn,
5012 : context->indentLevel);
5013 3 : if (PRETTY_INDENT(context))
5014 3 : appendContextKeyword(context, "", 0, 0, 0);
5015 3 : appendStringInfoChar(buf, ')');
5016 3 : sep = ", ";
5017 : }
5018 :
5019 3 : if (PRETTY_INDENT(context))
5020 : {
5021 3 : context->indentLevel -= PRETTYINDENT_STD;
5022 3 : appendContextKeyword(context, "", 0, 0, 0);
5023 : }
5024 : else
5025 0 : appendStringInfoChar(buf, ' ');
5026 : }
5027 :
5028 : /* ----------
5029 : * get_select_query_def - Parse back a SELECT parsetree
5030 : * ----------
5031 : */
5032 : static void
5033 325 : get_select_query_def(Query *query, deparse_context *context,
5034 : TupleDesc resultDesc)
5035 : {
5036 325 : StringInfo buf = context->buf;
5037 : List *save_windowclause;
5038 : List *save_windowtlist;
5039 : bool force_colno;
5040 : ListCell *l;
5041 :
5042 : /* Insert the WITH clause if given */
5043 325 : get_with_clause(query, context);
5044 :
5045 : /* Set up context for possible window functions */
5046 325 : save_windowclause = context->windowClause;
5047 325 : context->windowClause = query->windowClause;
5048 325 : save_windowtlist = context->windowTList;
5049 325 : context->windowTList = query->targetList;
5050 :
5051 : /*
5052 : * If the Query node has a setOperations tree, then it's the top level of
5053 : * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
5054 : * fields are interesting in the top query itself.
5055 : */
5056 325 : if (query->setOperations)
5057 : {
5058 16 : get_setop_query(query->setOperations, query, context, resultDesc);
5059 : /* ORDER BY clauses must be simple in this case */
5060 16 : force_colno = true;
5061 : }
5062 : else
5063 : {
5064 309 : get_basic_select_query(query, context, resultDesc);
5065 309 : force_colno = false;
5066 : }
5067 :
5068 : /* Add the ORDER BY clause if given */
5069 325 : if (query->sortClause != NIL)
5070 : {
5071 6 : appendContextKeyword(context, " ORDER BY ",
5072 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5073 6 : get_rule_orderby(query->sortClause, query->targetList,
5074 : force_colno, context);
5075 : }
5076 :
5077 : /* Add the LIMIT clause if given */
5078 325 : if (query->limitOffset != NULL)
5079 : {
5080 0 : appendContextKeyword(context, " OFFSET ",
5081 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5082 0 : get_rule_expr(query->limitOffset, context, false);
5083 : }
5084 325 : if (query->limitCount != NULL)
5085 : {
5086 0 : appendContextKeyword(context, " LIMIT ",
5087 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5088 0 : if (IsA(query->limitCount, Const) &&
5089 0 : ((Const *) query->limitCount)->constisnull)
5090 0 : appendStringInfoString(buf, "ALL");
5091 : else
5092 0 : get_rule_expr(query->limitCount, context, false);
5093 : }
5094 :
5095 : /* Add FOR [KEY] UPDATE/SHARE clauses if present */
5096 325 : if (query->hasForUpdate)
5097 : {
5098 0 : foreach(l, query->rowMarks)
5099 : {
5100 0 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
5101 :
5102 : /* don't print implicit clauses */
5103 0 : if (rc->pushedDown)
5104 0 : continue;
5105 :
5106 0 : switch (rc->strength)
5107 : {
5108 : case LCS_NONE:
5109 : /* we intentionally throw an error for LCS_NONE */
5110 0 : elog(ERROR, "unrecognized LockClauseStrength %d",
5111 : (int) rc->strength);
5112 : break;
5113 : case LCS_FORKEYSHARE:
5114 0 : appendContextKeyword(context, " FOR KEY SHARE",
5115 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5116 0 : break;
5117 : case LCS_FORSHARE:
5118 0 : appendContextKeyword(context, " FOR SHARE",
5119 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5120 0 : break;
5121 : case LCS_FORNOKEYUPDATE:
5122 0 : appendContextKeyword(context, " FOR NO KEY UPDATE",
5123 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5124 0 : break;
5125 : case LCS_FORUPDATE:
5126 0 : appendContextKeyword(context, " FOR UPDATE",
5127 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5128 0 : break;
5129 : }
5130 :
5131 0 : appendStringInfo(buf, " OF %s",
5132 0 : quote_identifier(get_rtable_name(rc->rti,
5133 : context)));
5134 0 : if (rc->waitPolicy == LockWaitError)
5135 0 : appendStringInfoString(buf, " NOWAIT");
5136 0 : else if (rc->waitPolicy == LockWaitSkip)
5137 0 : appendStringInfoString(buf, " SKIP LOCKED");
5138 : }
5139 : }
5140 :
5141 325 : context->windowClause = save_windowclause;
5142 325 : context->windowTList = save_windowtlist;
5143 325 : }
5144 :
5145 : /*
5146 : * Detect whether query looks like SELECT ... FROM VALUES();
5147 : * if so, return the VALUES RTE. Otherwise return NULL.
5148 : */
5149 : static RangeTblEntry *
5150 309 : get_simple_values_rte(Query *query)
5151 : {
5152 309 : RangeTblEntry *result = NULL;
5153 : ListCell *lc;
5154 :
5155 : /*
5156 : * We want to return TRUE even if the Query also contains OLD or NEW rule
5157 : * RTEs. So the idea is to scan the rtable and see if there is only one
5158 : * inFromCl RTE that is a VALUES RTE.
5159 : */
5160 749 : foreach(lc, query->rtable)
5161 : {
5162 711 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
5163 :
5164 711 : if (rte->rtekind == RTE_VALUES && rte->inFromCl)
5165 : {
5166 26 : if (result)
5167 0 : return NULL; /* multiple VALUES (probably not possible) */
5168 26 : result = rte;
5169 : }
5170 685 : else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
5171 414 : continue; /* ignore rule entries */
5172 : else
5173 271 : return NULL; /* something else -> not simple VALUES */
5174 : }
5175 :
5176 : /*
5177 : * We don't need to check the targetlist in any great detail, because
5178 : * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
5179 : * appear inside auto-generated sub-queries with very restricted
5180 : * structure. However, DefineView might have modified the tlist by
5181 : * injecting new column aliases; so compare tlist resnames against the
5182 : * RTE's names to detect that.
5183 : */
5184 38 : if (result)
5185 : {
5186 : ListCell *lcn;
5187 :
5188 26 : if (list_length(query->targetList) != list_length(result->eref->colnames))
5189 0 : return NULL; /* this probably cannot happen */
5190 109 : forboth(lc, query->targetList, lcn, result->eref->colnames)
5191 : {
5192 84 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
5193 84 : char *cname = strVal(lfirst(lcn));
5194 :
5195 84 : if (tle->resjunk)
5196 0 : return NULL; /* this probably cannot happen */
5197 84 : if (tle->resname == NULL || strcmp(tle->resname, cname) != 0)
5198 1 : return NULL; /* column name has been changed */
5199 : }
5200 : }
5201 :
5202 37 : return result;
5203 : }
5204 :
5205 : static void
5206 309 : get_basic_select_query(Query *query, deparse_context *context,
5207 : TupleDesc resultDesc)
5208 : {
5209 309 : StringInfo buf = context->buf;
5210 : RangeTblEntry *values_rte;
5211 : char *sep;
5212 : ListCell *l;
5213 :
5214 309 : if (PRETTY_INDENT(context))
5215 : {
5216 309 : context->indentLevel += PRETTYINDENT_STD;
5217 309 : appendStringInfoChar(buf, ' ');
5218 : }
5219 :
5220 : /*
5221 : * If the query looks like SELECT * FROM (VALUES ...), then print just the
5222 : * VALUES part. This reverses what transformValuesClause() did at parse
5223 : * time.
5224 : */
5225 309 : values_rte = get_simple_values_rte(query);
5226 309 : if (values_rte)
5227 : {
5228 25 : get_values_def(values_rte->values_lists, context);
5229 334 : return;
5230 : }
5231 :
5232 : /*
5233 : * Build up the query string - first we say SELECT
5234 : */
5235 284 : appendStringInfoString(buf, "SELECT");
5236 :
5237 : /* Add the DISTINCT clause if given */
5238 284 : if (query->distinctClause != NIL)
5239 : {
5240 0 : if (query->hasDistinctOn)
5241 : {
5242 0 : appendStringInfoString(buf, " DISTINCT ON (");
5243 0 : sep = "";
5244 0 : foreach(l, query->distinctClause)
5245 : {
5246 0 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
5247 :
5248 0 : appendStringInfoString(buf, sep);
5249 0 : get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
5250 : false, context);
5251 0 : sep = ", ";
5252 : }
5253 0 : appendStringInfoChar(buf, ')');
5254 : }
5255 : else
5256 0 : appendStringInfoString(buf, " DISTINCT");
5257 : }
5258 :
5259 : /* Then we tell what to select (the targetlist) */
5260 284 : get_target_list(query->targetList, context, resultDesc);
5261 :
5262 : /* Add the FROM clause if needed */
5263 284 : get_from_clause(query, " FROM ", context);
5264 :
5265 : /* Add the WHERE clause if given */
5266 284 : if (query->jointree->quals != NULL)
5267 : {
5268 130 : appendContextKeyword(context, " WHERE ",
5269 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5270 130 : get_rule_expr(query->jointree->quals, context, false);
5271 : }
5272 :
5273 : /* Add the GROUP BY clause if given */
5274 284 : if (query->groupClause != NULL || query->groupingSets != NULL)
5275 : {
5276 : ParseExprKind save_exprkind;
5277 :
5278 7 : appendContextKeyword(context, " GROUP BY ",
5279 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5280 :
5281 7 : save_exprkind = context->special_exprkind;
5282 7 : context->special_exprkind = EXPR_KIND_GROUP_BY;
5283 :
5284 7 : if (query->groupingSets == NIL)
5285 : {
5286 6 : sep = "";
5287 21 : foreach(l, query->groupClause)
5288 : {
5289 15 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
5290 :
5291 15 : appendStringInfoString(buf, sep);
5292 15 : get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
5293 : false, context);
5294 15 : sep = ", ";
5295 : }
5296 : }
5297 : else
5298 : {
5299 1 : sep = "";
5300 2 : foreach(l, query->groupingSets)
5301 : {
5302 1 : GroupingSet *grp = lfirst(l);
5303 :
5304 1 : appendStringInfoString(buf, sep);
5305 1 : get_rule_groupingset(grp, query->targetList, true, context);
5306 1 : sep = ", ";
5307 : }
5308 : }
5309 :
5310 7 : context->special_exprkind = save_exprkind;
5311 : }
5312 :
5313 : /* Add the HAVING clause if given */
5314 284 : if (query->havingQual != NULL)
5315 : {
5316 0 : appendContextKeyword(context, " HAVING ",
5317 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5318 0 : get_rule_expr(query->havingQual, context, false);
5319 : }
5320 :
5321 : /* Add the WINDOW clause if needed */
5322 284 : if (query->windowClause != NIL)
5323 1 : get_rule_windowclause(query, context);
5324 : }
5325 :
5326 : /* ----------
5327 : * get_target_list - Parse back a SELECT target list
5328 : *
5329 : * This is also used for RETURNING lists in INSERT/UPDATE/DELETE.
5330 : * ----------
5331 : */
5332 : static void
5333 290 : get_target_list(List *targetList, deparse_context *context,
5334 : TupleDesc resultDesc)
5335 : {
5336 290 : StringInfo buf = context->buf;
5337 : StringInfoData targetbuf;
5338 290 : bool last_was_multiline = false;
5339 : char *sep;
5340 : int colno;
5341 : ListCell *l;
5342 :
5343 : /* we use targetbuf to hold each TLE's text temporarily */
5344 290 : initStringInfo(&targetbuf);
5345 :
5346 290 : sep = " ";
5347 290 : colno = 0;
5348 1572 : foreach(l, targetList)
5349 : {
5350 1282 : TargetEntry *tle = (TargetEntry *) lfirst(l);
5351 : char *colname;
5352 : char *attname;
5353 :
5354 1282 : if (tle->resjunk)
5355 5 : continue; /* ignore junk entries */
5356 :
5357 1277 : appendStringInfoString(buf, sep);
5358 1277 : sep = ", ";
5359 1277 : colno++;
5360 :
5361 : /*
5362 : * Put the new field text into targetbuf so we can decide after we've
5363 : * got it whether or not it needs to go on a new line.
5364 : */
5365 1277 : resetStringInfo(&targetbuf);
5366 1277 : context->buf = &targetbuf;
5367 :
5368 : /*
5369 : * We special-case Var nodes rather than using get_rule_expr. This is
5370 : * needed because get_rule_expr will display a whole-row Var as
5371 : * "foo.*", which is the preferred notation in most contexts, but at
5372 : * the top level of a SELECT list it's not right (the parser will
5373 : * expand that notation into multiple columns, yielding behavior
5374 : * different from a whole-row Var). We need to call get_variable
5375 : * directly so that we can tell it to do the right thing, and so that
5376 : * we can get the attribute name which is the default AS label.
5377 : */
5378 1277 : if (tle->expr && (IsA(tle->expr, Var)))
5379 : {
5380 1039 : attname = get_variable((Var *) tle->expr, 0, true, context);
5381 : }
5382 : else
5383 : {
5384 238 : get_rule_expr((Node *) tle->expr, context, true);
5385 : /* We'll show the AS name unless it's this: */
5386 238 : attname = "?column?";
5387 : }
5388 :
5389 : /*
5390 : * Figure out what the result column should be called. In the context
5391 : * of a view, use the view's tuple descriptor (so as to pick up the
5392 : * effects of any column RENAME that's been done on the view).
5393 : * Otherwise, just use what we can find in the TLE.
5394 : */
5395 1277 : if (resultDesc && colno <= resultDesc->natts)
5396 1209 : colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
5397 : else
5398 68 : colname = tle->resname;
5399 :
5400 : /* Show AS unless the column's name is correct as-is */
5401 1277 : if (colname) /* resname could be NULL */
5402 : {
5403 1277 : if (attname == NULL || strcmp(attname, colname) != 0)
5404 356 : appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
5405 : }
5406 :
5407 : /* Restore context's output buffer */
5408 1277 : context->buf = buf;
5409 :
5410 : /* Consider line-wrapping if enabled */
5411 1277 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
5412 : {
5413 : int leading_nl_pos;
5414 :
5415 : /* Does the new field start with a new line? */
5416 1277 : if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
5417 19 : leading_nl_pos = 0;
5418 : else
5419 1258 : leading_nl_pos = -1;
5420 :
5421 : /* If so, we shouldn't add anything */
5422 1277 : if (leading_nl_pos >= 0)
5423 : {
5424 : /* instead, remove any trailing spaces currently in buf */
5425 19 : removeStringInfoSpaces(buf);
5426 : }
5427 : else
5428 : {
5429 : char *trailing_nl;
5430 :
5431 : /* Locate the start of the current line in the output buffer */
5432 1258 : trailing_nl = strrchr(buf->data, '\n');
5433 1258 : if (trailing_nl == NULL)
5434 398 : trailing_nl = buf->data;
5435 : else
5436 860 : trailing_nl++;
5437 :
5438 : /*
5439 : * Add a newline, plus some indentation, if the new field is
5440 : * not the first and either the new field would cause an
5441 : * overflow or the last field used more than one line.
5442 : */
5443 2226 : if (colno > 1 &&
5444 968 : ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
5445 : last_was_multiline))
5446 968 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
5447 : PRETTYINDENT_STD, PRETTYINDENT_VAR);
5448 : }
5449 :
5450 : /* Remember this field's multiline status for next iteration */
5451 1277 : last_was_multiline =
5452 1277 : (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
5453 : }
5454 :
5455 : /* Add the new field */
5456 1277 : appendStringInfoString(buf, targetbuf.data);
5457 : }
5458 :
5459 : /* clean up */
5460 290 : pfree(targetbuf.data);
5461 290 : }
5462 :
5463 : static void
5464 70 : get_setop_query(Node *setOp, Query *query, deparse_context *context,
5465 : TupleDesc resultDesc)
5466 : {
5467 70 : StringInfo buf = context->buf;
5468 : bool need_paren;
5469 :
5470 : /* Guard against excessively long or deeply-nested queries */
5471 70 : CHECK_FOR_INTERRUPTS();
5472 70 : check_stack_depth();
5473 :
5474 70 : if (IsA(setOp, RangeTblRef))
5475 : {
5476 43 : RangeTblRef *rtr = (RangeTblRef *) setOp;
5477 43 : RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
5478 43 : Query *subquery = rte->subquery;
5479 :
5480 43 : Assert(subquery != NULL);
5481 43 : Assert(subquery->setOperations == NULL);
5482 : /* Need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y */
5483 129 : need_paren = (subquery->cteList ||
5484 86 : subquery->sortClause ||
5485 86 : subquery->rowMarks ||
5486 129 : subquery->limitOffset ||
5487 43 : subquery->limitCount);
5488 43 : if (need_paren)
5489 0 : appendStringInfoChar(buf, '(');
5490 43 : get_query_def(subquery, buf, context->namespaces, resultDesc,
5491 : context->prettyFlags, context->wrapColumn,
5492 : context->indentLevel);
5493 43 : if (need_paren)
5494 0 : appendStringInfoChar(buf, ')');
5495 : }
5496 27 : else if (IsA(setOp, SetOperationStmt))
5497 : {
5498 27 : SetOperationStmt *op = (SetOperationStmt *) setOp;
5499 : int subindent;
5500 :
5501 : /*
5502 : * We force parens when nesting two SetOperationStmts, except when the
5503 : * lefthand input is another setop of the same kind. Syntactically,
5504 : * we could omit parens in rather more cases, but it seems best to use
5505 : * parens to flag cases where the setop operator changes. If we use
5506 : * parens, we also increase the indentation level for the child query.
5507 : *
5508 : * There are some cases in which parens are needed around a leaf query
5509 : * too, but those are more easily handled at the next level down (see
5510 : * code above).
5511 : */
5512 27 : if (IsA(op->larg, SetOperationStmt))
5513 : {
5514 11 : SetOperationStmt *lop = (SetOperationStmt *) op->larg;
5515 :
5516 11 : if (op->op == lop->op && op->all == lop->all)
5517 11 : need_paren = false;
5518 : else
5519 0 : need_paren = true;
5520 : }
5521 : else
5522 16 : need_paren = false;
5523 :
5524 27 : if (need_paren)
5525 : {
5526 0 : appendStringInfoChar(buf, '(');
5527 0 : subindent = PRETTYINDENT_STD;
5528 0 : appendContextKeyword(context, "", subindent, 0, 0);
5529 : }
5530 : else
5531 27 : subindent = 0;
5532 :
5533 27 : get_setop_query(op->larg, query, context, resultDesc);
5534 :
5535 27 : if (need_paren)
5536 0 : appendContextKeyword(context, ") ", -subindent, 0, 0);
5537 27 : else if (PRETTY_INDENT(context))
5538 27 : appendContextKeyword(context, "", -subindent, 0, 0);
5539 : else
5540 0 : appendStringInfoChar(buf, ' ');
5541 :
5542 27 : switch (op->op)
5543 : {
5544 : case SETOP_UNION:
5545 27 : appendStringInfoString(buf, "UNION ");
5546 27 : break;
5547 : case SETOP_INTERSECT:
5548 0 : appendStringInfoString(buf, "INTERSECT ");
5549 0 : break;
5550 : case SETOP_EXCEPT:
5551 0 : appendStringInfoString(buf, "EXCEPT ");
5552 0 : break;
5553 : default:
5554 0 : elog(ERROR, "unrecognized set op: %d",
5555 : (int) op->op);
5556 : }
5557 27 : if (op->all)
5558 25 : appendStringInfoString(buf, "ALL ");
5559 :
5560 : /* Always parenthesize if RHS is another setop */
5561 27 : need_paren = IsA(op->rarg, SetOperationStmt);
5562 :
5563 : /*
5564 : * The indentation code here is deliberately a bit different from that
5565 : * for the lefthand input, because we want the line breaks in
5566 : * different places.
5567 : */
5568 27 : if (need_paren)
5569 : {
5570 0 : appendStringInfoChar(buf, '(');
5571 0 : subindent = PRETTYINDENT_STD;
5572 : }
5573 : else
5574 27 : subindent = 0;
5575 27 : appendContextKeyword(context, "", subindent, 0, 0);
5576 :
5577 27 : get_setop_query(op->rarg, query, context, resultDesc);
5578 :
5579 27 : if (PRETTY_INDENT(context))
5580 27 : context->indentLevel -= subindent;
5581 27 : if (need_paren)
5582 0 : appendContextKeyword(context, ")", 0, 0, 0);
5583 : }
5584 : else
5585 : {
5586 0 : elog(ERROR, "unrecognized node type: %d",
5587 : (int) nodeTag(setOp));
5588 : }
5589 70 : }
5590 :
5591 : /*
5592 : * Display a sort/group clause.
5593 : *
5594 : * Also returns the expression tree, so caller need not find it again.
5595 : */
5596 : static Node *
5597 39 : get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
5598 : deparse_context *context)
5599 : {
5600 39 : StringInfo buf = context->buf;
5601 : TargetEntry *tle;
5602 : Node *expr;
5603 :
5604 39 : tle = get_sortgroupref_tle(ref, tlist);
5605 39 : expr = (Node *) tle->expr;
5606 :
5607 : /*
5608 : * Use column-number form if requested by caller. Otherwise, if
5609 : * expression is a constant, force it to be dumped with an explicit cast
5610 : * as decoration --- this is because a simple integer constant is
5611 : * ambiguous (and will be misinterpreted by findTargetlistEntry()) if we
5612 : * dump it without any decoration. If it's anything more complex than a
5613 : * simple Var, then force extra parens around it, to ensure it can't be
5614 : * misinterpreted as a cube() or rollup() construct.
5615 : */
5616 39 : if (force_colno)
5617 : {
5618 0 : Assert(!tle->resjunk);
5619 0 : appendStringInfo(buf, "%d", tle->resno);
5620 : }
5621 39 : else if (expr && IsA(expr, Const))
5622 0 : get_const_expr((Const *) expr, context, 1);
5623 39 : else if (!expr || IsA(expr, Var))
5624 37 : get_rule_expr(expr, context, true);
5625 : else
5626 : {
5627 : /*
5628 : * We must force parens for function-like expressions even if
5629 : * PRETTY_PAREN is off, since those are the ones in danger of
5630 : * misparsing. For other expressions we need to force them only if
5631 : * PRETTY_PAREN is on, since otherwise the expression will output them
5632 : * itself. (We can't skip the parens.)
5633 : */
5634 4 : bool need_paren = (PRETTY_PAREN(context)
5635 2 : || IsA(expr, FuncExpr)
5636 2 : ||IsA(expr, Aggref)
5637 4 : ||IsA(expr, WindowFunc));
5638 :
5639 2 : if (need_paren)
5640 0 : appendStringInfoChar(context->buf, '(');
5641 2 : get_rule_expr(expr, context, true);
5642 2 : if (need_paren)
5643 0 : appendStringInfoChar(context->buf, ')');
5644 : }
5645 :
5646 39 : return expr;
5647 : }
5648 :
5649 : /*
5650 : * Display a GroupingSet
5651 : */
5652 : static void
5653 3 : get_rule_groupingset(GroupingSet *gset, List *targetlist,
5654 : bool omit_parens, deparse_context *context)
5655 : {
5656 : ListCell *l;
5657 3 : StringInfo buf = context->buf;
5658 3 : bool omit_child_parens = true;
5659 3 : char *sep = "";
5660 :
5661 3 : switch (gset->kind)
5662 : {
5663 : case GROUPING_SET_EMPTY:
5664 0 : appendStringInfoString(buf, "()");
5665 0 : return;
5666 :
5667 : case GROUPING_SET_SIMPLE:
5668 : {
5669 2 : if (!omit_parens || list_length(gset->content) != 1)
5670 2 : appendStringInfoChar(buf, '(');
5671 :
5672 7 : foreach(l, gset->content)
5673 : {
5674 5 : Index ref = lfirst_int(l);
5675 :
5676 5 : appendStringInfoString(buf, sep);
5677 5 : get_rule_sortgroupclause(ref, targetlist,
5678 : false, context);
5679 5 : sep = ", ";
5680 : }
5681 :
5682 2 : if (!omit_parens || list_length(gset->content) != 1)
5683 2 : appendStringInfoChar(buf, ')');
5684 : }
5685 2 : return;
5686 :
5687 : case GROUPING_SET_ROLLUP:
5688 1 : appendStringInfoString(buf, "ROLLUP(");
5689 1 : break;
5690 : case GROUPING_SET_CUBE:
5691 0 : appendStringInfoString(buf, "CUBE(");
5692 0 : break;
5693 : case GROUPING_SET_SETS:
5694 0 : appendStringInfoString(buf, "GROUPING SETS (");
5695 0 : omit_child_parens = false;
5696 0 : break;
5697 : }
5698 :
5699 3 : foreach(l, gset->content)
5700 : {
5701 2 : appendStringInfoString(buf, sep);
5702 2 : get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
5703 2 : sep = ", ";
5704 : }
5705 :
5706 1 : appendStringInfoChar(buf, ')');
5707 : }
5708 :
5709 : /*
5710 : * Display an ORDER BY list.
5711 : */
5712 : static void
5713 15 : get_rule_orderby(List *orderList, List *targetList,
5714 : bool force_colno, deparse_context *context)
5715 : {
5716 15 : StringInfo buf = context->buf;
5717 : const char *sep;
5718 : ListCell *l;
5719 :
5720 15 : sep = "";
5721 34 : foreach(l, orderList)
5722 : {
5723 19 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
5724 : Node *sortexpr;
5725 : Oid sortcoltype;
5726 : TypeCacheEntry *typentry;
5727 :
5728 19 : appendStringInfoString(buf, sep);
5729 19 : sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
5730 : force_colno, context);
5731 19 : sortcoltype = exprType(sortexpr);
5732 : /* See whether operator is default < or > for datatype */
5733 19 : typentry = lookup_type_cache(sortcoltype,
5734 : TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
5735 19 : if (srt->sortop == typentry->lt_opr)
5736 : {
5737 : /* ASC is default, so emit nothing for it */
5738 16 : if (srt->nulls_first)
5739 0 : appendStringInfoString(buf, " NULLS FIRST");
5740 : }
5741 3 : else if (srt->sortop == typentry->gt_opr)
5742 : {
5743 1 : appendStringInfoString(buf, " DESC");
5744 : /* DESC defaults to NULLS FIRST */
5745 1 : if (!srt->nulls_first)
5746 0 : appendStringInfoString(buf, " NULLS LAST");
5747 : }
5748 : else
5749 : {
5750 2 : appendStringInfo(buf, " USING %s",
5751 : generate_operator_name(srt->sortop,
5752 : sortcoltype,
5753 : sortcoltype));
5754 : /* be specific to eliminate ambiguity */
5755 2 : if (srt->nulls_first)
5756 0 : appendStringInfoString(buf, " NULLS FIRST");
5757 : else
5758 2 : appendStringInfoString(buf, " NULLS LAST");
5759 : }
5760 19 : sep = ", ";
5761 : }
5762 15 : }
5763 :
5764 : /*
5765 : * Display a WINDOW clause.
5766 : *
5767 : * Note that the windowClause list might contain only anonymous window
5768 : * specifications, in which case we should print nothing here.
5769 : */
5770 : static void
5771 1 : get_rule_windowclause(Query *query, deparse_context *context)
5772 : {
5773 1 : StringInfo buf = context->buf;
5774 : const char *sep;
5775 : ListCell *l;
5776 :
5777 1 : sep = NULL;
5778 2 : foreach(l, query->windowClause)
5779 : {
5780 1 : WindowClause *wc = (WindowClause *) lfirst(l);
5781 :
5782 1 : if (wc->name == NULL)
5783 1 : continue; /* ignore anonymous windows */
5784 :
5785 0 : if (sep == NULL)
5786 0 : appendContextKeyword(context, " WINDOW ",
5787 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5788 : else
5789 0 : appendStringInfoString(buf, sep);
5790 :
5791 0 : appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
5792 :
5793 0 : get_rule_windowspec(wc, query->targetList, context);
5794 :
5795 0 : sep = ", ";
5796 : }
5797 1 : }
5798 :
5799 : /*
5800 : * Display a window definition
5801 : */
5802 : static void
5803 1 : get_rule_windowspec(WindowClause *wc, List *targetList,
5804 : deparse_context *context)
5805 : {
5806 1 : StringInfo buf = context->buf;
5807 1 : bool needspace = false;
5808 : const char *sep;
5809 : ListCell *l;
5810 :
5811 1 : appendStringInfoChar(buf, '(');
5812 1 : if (wc->refname)
5813 : {
5814 0 : appendStringInfoString(buf, quote_identifier(wc->refname));
5815 0 : needspace = true;
5816 : }
5817 : /* partition clauses are always inherited, so only print if no refname */
5818 1 : if (wc->partitionClause && !wc->refname)
5819 : {
5820 0 : if (needspace)
5821 0 : appendStringInfoChar(buf, ' ');
5822 0 : appendStringInfoString(buf, "PARTITION BY ");
5823 0 : sep = "";
5824 0 : foreach(l, wc->partitionClause)
5825 : {
5826 0 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
5827 :
5828 0 : appendStringInfoString(buf, sep);
5829 0 : get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
5830 : false, context);
5831 0 : sep = ", ";
5832 : }
5833 0 : needspace = true;
5834 : }
5835 : /* print ordering clause only if not inherited */
5836 1 : if (wc->orderClause && !wc->copiedOrder)
5837 : {
5838 1 : if (needspace)
5839 0 : appendStringInfoChar(buf, ' ');
5840 1 : appendStringInfoString(buf, "ORDER BY ");
5841 1 : get_rule_orderby(wc->orderClause, targetList, false, context);
5842 1 : needspace = true;
5843 : }
5844 : /* framing clause is never inherited, so print unless it's default */
5845 1 : if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
5846 : {
5847 1 : if (needspace)
5848 1 : appendStringInfoChar(buf, ' ');
5849 1 : if (wc->frameOptions & FRAMEOPTION_RANGE)
5850 0 : appendStringInfoString(buf, "RANGE ");
5851 1 : else if (wc->frameOptions & FRAMEOPTION_ROWS)
5852 1 : appendStringInfoString(buf, "ROWS ");
5853 : else
5854 0 : Assert(false);
5855 1 : if (wc->frameOptions & FRAMEOPTION_BETWEEN)
5856 1 : appendStringInfoString(buf, "BETWEEN ");
5857 1 : if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
5858 0 : appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
5859 1 : else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW)
5860 0 : appendStringInfoString(buf, "CURRENT ROW ");
5861 1 : else if (wc->frameOptions & FRAMEOPTION_START_VALUE)
5862 : {
5863 1 : get_rule_expr(wc->startOffset, context, false);
5864 1 : if (wc->frameOptions & FRAMEOPTION_START_VALUE_PRECEDING)
5865 1 : appendStringInfoString(buf, " PRECEDING ");
5866 0 : else if (wc->frameOptions & FRAMEOPTION_START_VALUE_FOLLOWING)
5867 0 : appendStringInfoString(buf, " FOLLOWING ");
5868 : else
5869 0 : Assert(false);
5870 : }
5871 : else
5872 0 : Assert(false);
5873 1 : if (wc->frameOptions & FRAMEOPTION_BETWEEN)
5874 : {
5875 1 : appendStringInfoString(buf, "AND ");
5876 1 : if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
5877 0 : appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
5878 1 : else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW)
5879 0 : appendStringInfoString(buf, "CURRENT ROW ");
5880 1 : else if (wc->frameOptions & FRAMEOPTION_END_VALUE)
5881 : {
5882 1 : get_rule_expr(wc->endOffset, context, false);
5883 1 : if (wc->frameOptions & FRAMEOPTION_END_VALUE_PRECEDING)
5884 0 : appendStringInfoString(buf, " PRECEDING ");
5885 1 : else if (wc->frameOptions & FRAMEOPTION_END_VALUE_FOLLOWING)
5886 1 : appendStringInfoString(buf, " FOLLOWING ");
5887 : else
5888 0 : Assert(false);
5889 : }
5890 : else
5891 0 : Assert(false);
5892 : }
5893 : /* we will now have a trailing space; remove it */
5894 1 : buf->len--;
5895 : }
5896 1 : appendStringInfoChar(buf, ')');
5897 1 : }
5898 :
5899 : /* ----------
5900 : * get_insert_query_def - Parse back an INSERT parsetree
5901 : * ----------
5902 : */
5903 : static void
5904 27 : get_insert_query_def(Query *query, deparse_context *context)
5905 : {
5906 27 : StringInfo buf = context->buf;
5907 27 : RangeTblEntry *select_rte = NULL;
5908 27 : RangeTblEntry *values_rte = NULL;
5909 : RangeTblEntry *rte;
5910 : char *sep;
5911 : ListCell *l;
5912 : List *strippedexprs;
5913 :
5914 : /* Insert the WITH clause if given */
5915 27 : get_with_clause(query, context);
5916 :
5917 : /*
5918 : * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
5919 : * single RTE for the SELECT or VALUES. Plain VALUES has neither.
5920 : */
5921 111 : foreach(l, query->rtable)
5922 : {
5923 84 : rte = (RangeTblEntry *) lfirst(l);
5924 :
5925 84 : if (rte->rtekind == RTE_SUBQUERY)
5926 : {
5927 2 : if (select_rte)
5928 0 : elog(ERROR, "too many subquery RTEs in INSERT");
5929 2 : select_rte = rte;
5930 : }
5931 :
5932 84 : if (rte->rtekind == RTE_VALUES)
5933 : {
5934 3 : if (values_rte)
5935 0 : elog(ERROR, "too many values RTEs in INSERT");
5936 3 : values_rte = rte;
5937 : }
5938 : }
5939 27 : if (select_rte && values_rte)
5940 0 : elog(ERROR, "both subquery and values RTEs in INSERT");
5941 :
5942 : /*
5943 : * Start the query with INSERT INTO relname
5944 : */
5945 27 : rte = rt_fetch(query->resultRelation, query->rtable);
5946 27 : Assert(rte->rtekind == RTE_RELATION);
5947 :
5948 27 : if (PRETTY_INDENT(context))
5949 : {
5950 27 : context->indentLevel += PRETTYINDENT_STD;
5951 27 : appendStringInfoChar(buf, ' ');
5952 : }
5953 27 : appendStringInfo(buf, "INSERT INTO %s ",
5954 : generate_relation_name(rte->relid, NIL));
5955 : /* INSERT requires AS keyword for target alias */
5956 27 : if (rte->alias != NULL)
5957 1 : appendStringInfo(buf, "AS %s ",
5958 1 : quote_identifier(rte->alias->aliasname));
5959 :
5960 : /*
5961 : * Add the insert-column-names list. Any indirection decoration needed on
5962 : * the column names can be inferred from the top targetlist.
5963 : */
5964 27 : strippedexprs = NIL;
5965 27 : sep = "";
5966 27 : if (query->targetList)
5967 27 : appendStringInfoChar(buf, '(');
5968 100 : foreach(l, query->targetList)
5969 : {
5970 73 : TargetEntry *tle = (TargetEntry *) lfirst(l);
5971 :
5972 73 : if (tle->resjunk)
5973 0 : continue; /* ignore junk entries */
5974 :
5975 73 : appendStringInfoString(buf, sep);
5976 73 : sep = ", ";
5977 :
5978 : /*
5979 : * Put out name of target column; look in the catalogs, not at
5980 : * tle->resname, since resname will fail to track RENAME.
5981 : */
5982 73 : appendStringInfoString(buf,
5983 73 : quote_identifier(get_relid_attribute_name(rte->relid,
5984 73 : tle->resno)));
5985 :
5986 : /*
5987 : * Print any indirection needed (subfields or subscripts), and strip
5988 : * off the top-level nodes representing the indirection assignments.
5989 : * Add the stripped expressions to strippedexprs. (If it's a
5990 : * single-VALUES statement, the stripped expressions are the VALUES to
5991 : * print below. Otherwise they're just Vars and not really
5992 : * interesting.)
5993 : */
5994 73 : strippedexprs = lappend(strippedexprs,
5995 73 : processIndirection((Node *) tle->expr,
5996 : context));
5997 : }
5998 27 : if (query->targetList)
5999 27 : appendStringInfoString(buf, ") ");
6000 :
6001 27 : if (query->override)
6002 : {
6003 0 : if (query->override == OVERRIDING_SYSTEM_VALUE)
6004 0 : appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
6005 0 : else if (query->override == OVERRIDING_USER_VALUE)
6006 0 : appendStringInfoString(buf, "OVERRIDING USER VALUE ");
6007 : }
6008 :
6009 27 : if (select_rte)
6010 : {
6011 : /* Add the SELECT */
6012 2 : get_query_def(select_rte->subquery, buf, NIL, NULL,
6013 : context->prettyFlags, context->wrapColumn,
6014 : context->indentLevel);
6015 : }
6016 25 : else if (values_rte)
6017 : {
6018 : /* Add the multi-VALUES expression lists */
6019 3 : get_values_def(values_rte->values_lists, context);
6020 : }
6021 22 : else if (strippedexprs)
6022 : {
6023 : /* Add the single-VALUES expression list */
6024 22 : appendContextKeyword(context, "VALUES (",
6025 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
6026 22 : get_rule_expr((Node *) strippedexprs, context, false);
6027 22 : appendStringInfoChar(buf, ')');
6028 : }
6029 : else
6030 : {
6031 : /* No expressions, so it must be DEFAULT VALUES */
6032 0 : appendStringInfoString(buf, "DEFAULT VALUES");
6033 : }
6034 :
6035 : /* Add ON CONFLICT if present */
6036 27 : if (query->onConflict)
6037 : {
6038 5 : OnConflictExpr *confl = query->onConflict;
6039 :
6040 5 : appendStringInfoString(buf, " ON CONFLICT");
6041 :
6042 5 : if (confl->arbiterElems)
6043 : {
6044 : /* Add the single-VALUES expression list */
6045 4 : appendStringInfoChar(buf, '(');
6046 4 : get_rule_expr((Node *) confl->arbiterElems, context, false);
6047 4 : appendStringInfoChar(buf, ')');
6048 :
6049 : /* Add a WHERE clause (for partial indexes) if given */
6050 4 : if (confl->arbiterWhere != NULL)
6051 : {
6052 : bool save_varprefix;
6053 :
6054 : /*
6055 : * Force non-prefixing of Vars, since parser assumes that they
6056 : * belong to target relation. WHERE clause does not use
6057 : * InferenceElem, so this is separately required.
6058 : */
6059 2 : save_varprefix = context->varprefix;
6060 2 : context->varprefix = false;
6061 :
6062 2 : appendContextKeyword(context, " WHERE ",
6063 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6064 2 : get_rule_expr(confl->arbiterWhere, context, false);
6065 :
6066 2 : context->varprefix = save_varprefix;
6067 : }
6068 : }
6069 1 : else if (OidIsValid(confl->constraint))
6070 : {
6071 0 : char *constraint = get_constraint_name(confl->constraint);
6072 :
6073 0 : if (!constraint)
6074 0 : elog(ERROR, "cache lookup failed for constraint %u",
6075 : confl->constraint);
6076 0 : appendStringInfo(buf, " ON CONSTRAINT %s",
6077 : quote_identifier(constraint));
6078 : }
6079 :
6080 5 : if (confl->action == ONCONFLICT_NOTHING)
6081 : {
6082 3 : appendStringInfoString(buf, " DO NOTHING");
6083 : }
6084 : else
6085 : {
6086 2 : appendStringInfoString(buf, " DO UPDATE SET ");
6087 : /* Deparse targetlist */
6088 2 : get_update_query_targetlist_def(query, confl->onConflictSet,
6089 : context, rte);
6090 :
6091 : /* Add a WHERE clause if given */
6092 2 : if (confl->onConflictWhere != NULL)
6093 : {
6094 2 : appendContextKeyword(context, " WHERE ",
6095 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6096 2 : get_rule_expr(confl->onConflictWhere, context, false);
6097 : }
6098 : }
6099 : }
6100 :
6101 : /* Add RETURNING if present */
6102 27 : if (query->returningList)
6103 : {
6104 6 : appendContextKeyword(context, " RETURNING",
6105 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6106 6 : get_target_list(query->returningList, context, NULL);
6107 : }
6108 27 : }
6109 :
6110 :
6111 : /* ----------
6112 : * get_update_query_def - Parse back an UPDATE parsetree
6113 : * ----------
6114 : */
6115 : static void
6116 8 : get_update_query_def(Query *query, deparse_context *context)
6117 : {
6118 8 : StringInfo buf = context->buf;
6119 : RangeTblEntry *rte;
6120 :
6121 : /* Insert the WITH clause if given */
6122 8 : get_with_clause(query, context);
6123 :
6124 : /*
6125 : * Start the query with UPDATE relname SET
6126 : */
6127 8 : rte = rt_fetch(query->resultRelation, query->rtable);
6128 8 : Assert(rte->rtekind == RTE_RELATION);
6129 8 : if (PRETTY_INDENT(context))
6130 : {
6131 8 : appendStringInfoChar(buf, ' ');
6132 8 : context->indentLevel += PRETTYINDENT_STD;
6133 : }
6134 16 : appendStringInfo(buf, "UPDATE %s%s",
6135 8 : only_marker(rte),
6136 : generate_relation_name(rte->relid, NIL));
6137 8 : if (rte->alias != NULL)
6138 1 : appendStringInfo(buf, " %s",
6139 1 : quote_identifier(rte->alias->aliasname));
6140 8 : appendStringInfoString(buf, " SET ");
6141 :
6142 : /* Deparse targetlist */
6143 8 : get_update_query_targetlist_def(query, query->targetList, context, rte);
6144 :
6145 : /* Add the FROM clause if needed */
6146 8 : get_from_clause(query, " FROM ", context);
6147 :
6148 : /* Add a WHERE clause if given */
6149 8 : if (query->jointree->quals != NULL)
6150 : {
6151 8 : appendContextKeyword(context, " WHERE ",
6152 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6153 8 : get_rule_expr(query->jointree->quals, context, false);
6154 : }
6155 :
6156 : /* Add RETURNING if present */
6157 8 : if (query->returningList)
6158 : {
6159 0 : appendContextKeyword(context, " RETURNING",
6160 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6161 0 : get_target_list(query->returningList, context, NULL);
6162 : }
6163 8 : }
6164 :
6165 :
6166 : /* ----------
6167 : * get_update_query_targetlist_def - Parse back an UPDATE targetlist
6168 : * ----------
6169 : */
6170 : static void
6171 10 : get_update_query_targetlist_def(Query *query, List *targetList,
6172 : deparse_context *context, RangeTblEntry *rte)
6173 : {
6174 10 : StringInfo buf = context->buf;
6175 : ListCell *l;
6176 : ListCell *next_ma_cell;
6177 : int remaining_ma_columns;
6178 : const char *sep;
6179 : SubLink *cur_ma_sublink;
6180 : List *ma_sublinks;
6181 :
6182 : /*
6183 : * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
6184 : * into a list. We expect them to appear, in ID order, in resjunk tlist
6185 : * entries.
6186 : */
6187 10 : ma_sublinks = NIL;
6188 10 : if (query->hasSubLinks) /* else there can't be any */
6189 : {
6190 0 : foreach(l, targetList)
6191 : {
6192 0 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6193 :
6194 0 : if (tle->resjunk && IsA(tle->expr, SubLink))
6195 : {
6196 0 : SubLink *sl = (SubLink *) tle->expr;
6197 :
6198 0 : if (sl->subLinkType == MULTIEXPR_SUBLINK)
6199 : {
6200 0 : ma_sublinks = lappend(ma_sublinks, sl);
6201 0 : Assert(sl->subLinkId == list_length(ma_sublinks));
6202 : }
6203 : }
6204 : }
6205 : }
6206 10 : next_ma_cell = list_head(ma_sublinks);
6207 10 : cur_ma_sublink = NULL;
6208 10 : remaining_ma_columns = 0;
6209 :
6210 : /* Add the comma separated list of 'attname = value' */
6211 10 : sep = "";
6212 28 : foreach(l, targetList)
6213 : {
6214 18 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6215 : Node *expr;
6216 :
6217 18 : if (tle->resjunk)
6218 0 : continue; /* ignore junk entries */
6219 :
6220 : /* Emit separator (OK whether we're in multiassignment or not) */
6221 18 : appendStringInfoString(buf, sep);
6222 18 : sep = ", ";
6223 :
6224 : /*
6225 : * Check to see if we're starting a multiassignment group: if so,
6226 : * output a left paren.
6227 : */
6228 18 : if (next_ma_cell != NULL && cur_ma_sublink == NULL)
6229 : {
6230 : /*
6231 : * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
6232 : * Param. That could be buried under FieldStores and ArrayRefs
6233 : * and CoerceToDomains (cf processIndirection()), and underneath
6234 : * those there could be an implicit type coercion. Because we
6235 : * would ignore implicit type coercions anyway, we don't need to
6236 : * be as careful as processIndirection() is about descending past
6237 : * implicit CoerceToDomains.
6238 : */
6239 0 : expr = (Node *) tle->expr;
6240 0 : while (expr)
6241 : {
6242 0 : if (IsA(expr, FieldStore))
6243 : {
6244 0 : FieldStore *fstore = (FieldStore *) expr;
6245 :
6246 0 : expr = (Node *) linitial(fstore->newvals);
6247 : }
6248 0 : else if (IsA(expr, ArrayRef))
6249 : {
6250 0 : ArrayRef *aref = (ArrayRef *) expr;
6251 :
6252 0 : if (aref->refassgnexpr == NULL)
6253 0 : break;
6254 0 : expr = (Node *) aref->refassgnexpr;
6255 : }
6256 0 : else if (IsA(expr, CoerceToDomain))
6257 : {
6258 0 : CoerceToDomain *cdomain = (CoerceToDomain *) expr;
6259 :
6260 0 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
6261 0 : break;
6262 0 : expr = (Node *) cdomain->arg;
6263 : }
6264 : else
6265 0 : break;
6266 : }
6267 0 : expr = strip_implicit_coercions(expr);
6268 :
6269 0 : if (expr && IsA(expr, Param) &&
6270 0 : ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
6271 : {
6272 0 : cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
6273 0 : next_ma_cell = lnext(next_ma_cell);
6274 0 : remaining_ma_columns = count_nonjunk_tlist_entries(
6275 0 : ((Query *) cur_ma_sublink->subselect)->targetList);
6276 0 : Assert(((Param *) expr)->paramid ==
6277 : ((cur_ma_sublink->subLinkId << 16) | 1));
6278 0 : appendStringInfoChar(buf, '(');
6279 : }
6280 : }
6281 :
6282 : /*
6283 : * Put out name of target column; look in the catalogs, not at
6284 : * tle->resname, since resname will fail to track RENAME.
6285 : */
6286 18 : appendStringInfoString(buf,
6287 18 : quote_identifier(get_relid_attribute_name(rte->relid,
6288 18 : tle->resno)));
6289 :
6290 : /*
6291 : * Print any indirection needed (subfields or subscripts), and strip
6292 : * off the top-level nodes representing the indirection assignments.
6293 : */
6294 18 : expr = processIndirection((Node *) tle->expr, context);
6295 :
6296 : /*
6297 : * If we're in a multiassignment, skip printing anything more, unless
6298 : * this is the last column; in which case, what we print should be the
6299 : * sublink, not the Param.
6300 : */
6301 18 : if (cur_ma_sublink != NULL)
6302 : {
6303 0 : if (--remaining_ma_columns > 0)
6304 0 : continue; /* not the last column of multiassignment */
6305 0 : appendStringInfoChar(buf, ')');
6306 0 : expr = (Node *) cur_ma_sublink;
6307 0 : cur_ma_sublink = NULL;
6308 : }
6309 :
6310 18 : appendStringInfoString(buf, " = ");
6311 :
6312 18 : get_rule_expr(expr, context, false);
6313 : }
6314 10 : }
6315 :
6316 :
6317 : /* ----------
6318 : * get_delete_query_def - Parse back a DELETE parsetree
6319 : * ----------
6320 : */
6321 : static void
6322 5 : get_delete_query_def(Query *query, deparse_context *context)
6323 : {
6324 5 : StringInfo buf = context->buf;
6325 : RangeTblEntry *rte;
6326 :
6327 : /* Insert the WITH clause if given */
6328 5 : get_with_clause(query, context);
6329 :
6330 : /*
6331 : * Start the query with DELETE FROM relname
6332 : */
6333 5 : rte = rt_fetch(query->resultRelation, query->rtable);
6334 5 : Assert(rte->rtekind == RTE_RELATION);
6335 5 : if (PRETTY_INDENT(context))
6336 : {
6337 5 : appendStringInfoChar(buf, ' ');
6338 5 : context->indentLevel += PRETTYINDENT_STD;
6339 : }
6340 10 : appendStringInfo(buf, "DELETE FROM %s%s",
6341 5 : only_marker(rte),
6342 : generate_relation_name(rte->relid, NIL));
6343 5 : if (rte->alias != NULL)
6344 0 : appendStringInfo(buf, " %s",
6345 0 : quote_identifier(rte->alias->aliasname));
6346 :
6347 : /* Add the USING clause if given */
6348 5 : get_from_clause(query, " USING ", context);
6349 :
6350 : /* Add a WHERE clause if given */
6351 5 : if (query->jointree->quals != NULL)
6352 : {
6353 5 : appendContextKeyword(context, " WHERE ",
6354 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6355 5 : get_rule_expr(query->jointree->quals, context, false);
6356 : }
6357 :
6358 : /* Add RETURNING if present */
6359 5 : if (query->returningList)
6360 : {
6361 0 : appendContextKeyword(context, " RETURNING",
6362 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6363 0 : get_target_list(query->returningList, context, NULL);
6364 : }
6365 5 : }
6366 :
6367 :
6368 : /* ----------
6369 : * get_utility_query_def - Parse back a UTILITY parsetree
6370 : * ----------
6371 : */
6372 : static void
6373 2 : get_utility_query_def(Query *query, deparse_context *context)
6374 : {
6375 2 : StringInfo buf = context->buf;
6376 :
6377 2 : if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
6378 2 : {
6379 2 : NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
6380 :
6381 2 : appendContextKeyword(context, "",
6382 : 0, PRETTYINDENT_STD, 1);
6383 2 : appendStringInfo(buf, "NOTIFY %s",
6384 2 : quote_identifier(stmt->conditionname));
6385 2 : if (stmt->payload)
6386 : {
6387 0 : appendStringInfoString(buf, ", ");
6388 0 : simple_quote_literal(buf, stmt->payload);
6389 : }
6390 : }
6391 : else
6392 : {
6393 : /* Currently only NOTIFY utility commands can appear in rules */
6394 0 : elog(ERROR, "unexpected utility statement type");
6395 : }
6396 2 : }
6397 :
6398 : /*
6399 : * Display a Var appropriately.
6400 : *
6401 : * In some cases (currently only when recursing into an unnamed join)
6402 : * the Var's varlevelsup has to be interpreted with respect to a context
6403 : * above the current one; levelsup indicates the offset.
6404 : *
6405 : * If istoplevel is TRUE, the Var is at the top level of a SELECT's
6406 : * targetlist, which means we need special treatment of whole-row Vars.
6407 : * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
6408 : * dirty hack to prevent "tab.*" from being expanded into multiple columns.
6409 : * (The parser will strip the useless coercion, so no inefficiency is added in
6410 : * dump and reload.) We used to print just "tab" in such cases, but that is
6411 : * ambiguous and will yield the wrong result if "tab" is also a plain column
6412 : * name in the query.
6413 : *
6414 : * Returns the attname of the Var, or NULL if the Var has no attname (because
6415 : * it is a whole-row Var or a subplan output reference).
6416 : */
6417 : static char *
6418 7476 : get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
6419 : {
6420 7476 : StringInfo buf = context->buf;
6421 : RangeTblEntry *rte;
6422 : AttrNumber attnum;
6423 : int netlevelsup;
6424 : deparse_namespace *dpns;
6425 : deparse_columns *colinfo;
6426 : char *refname;
6427 : char *attname;
6428 :
6429 : /* Find appropriate nesting depth */
6430 7476 : netlevelsup = var->varlevelsup + levelsup;
6431 7476 : if (netlevelsup >= list_length(context->namespaces))
6432 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
6433 : var->varlevelsup, levelsup);
6434 7476 : dpns = (deparse_namespace *) list_nth(context->namespaces,
6435 : netlevelsup);
6436 :
6437 : /*
6438 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
6439 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
6440 : * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
6441 : * find the aliases previously assigned for this RTE.
6442 : */
6443 7476 : if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
6444 : {
6445 6138 : rte = rt_fetch(var->varno, dpns->rtable);
6446 6138 : refname = (char *) list_nth(dpns->rtable_names, var->varno - 1);
6447 6138 : colinfo = deparse_columns_fetch(var->varno, dpns);
6448 6138 : attnum = var->varattno;
6449 : }
6450 : else
6451 : {
6452 1338 : resolve_special_varno((Node *) var, context, NULL,
6453 : get_special_variable);
6454 1338 : return NULL;
6455 : }
6456 :
6457 : /*
6458 : * The planner will sometimes emit Vars referencing resjunk elements of a
6459 : * subquery's target list (this is currently only possible if it chooses
6460 : * to generate a "physical tlist" for a SubqueryScan or CteScan node).
6461 : * Although we prefer to print subquery-referencing Vars using the
6462 : * subquery's alias, that's not possible for resjunk items since they have
6463 : * no alias. So in that case, drill down to the subplan and print the
6464 : * contents of the referenced tlist item. This works because in a plan
6465 : * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
6466 : * we'll have set dpns->inner_planstate to reference the child plan node.
6467 : */
6468 12306 : if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
6469 165 : attnum > list_length(rte->eref->colnames) &&
6470 0 : dpns->inner_planstate)
6471 : {
6472 : TargetEntry *tle;
6473 : deparse_namespace save_dpns;
6474 :
6475 0 : tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
6476 0 : if (!tle)
6477 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
6478 : var->varattno, rte->eref->aliasname);
6479 :
6480 0 : Assert(netlevelsup == 0);
6481 0 : push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
6482 :
6483 : /*
6484 : * Force parentheses because our caller probably assumed a Var is a
6485 : * simple expression.
6486 : */
6487 0 : if (!IsA(tle->expr, Var))
6488 0 : appendStringInfoChar(buf, '(');
6489 0 : get_rule_expr((Node *) tle->expr, context, true);
6490 0 : if (!IsA(tle->expr, Var))
6491 0 : appendStringInfoChar(buf, ')');
6492 :
6493 0 : pop_child_plan(dpns, &save_dpns);
6494 0 : return NULL;
6495 : }
6496 :
6497 : /*
6498 : * If it's an unnamed join, look at the expansion of the alias variable.
6499 : * If it's a simple reference to one of the input vars, then recursively
6500 : * print the name of that var instead. When it's not a simple reference,
6501 : * we have to just print the unqualified join column name. (This can only
6502 : * happen with "dangerous" merged columns in a JOIN USING; we took pains
6503 : * previously to make the unqualified column name unique in such cases.)
6504 : *
6505 : * This wouldn't work in decompiling plan trees, because we don't store
6506 : * joinaliasvars lists after planning; but a plan tree should never
6507 : * contain a join alias variable.
6508 : */
6509 6138 : if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
6510 : {
6511 446 : if (rte->joinaliasvars == NIL)
6512 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
6513 446 : if (attnum > 0)
6514 : {
6515 : Var *aliasvar;
6516 :
6517 446 : aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
6518 : /* we intentionally don't strip implicit coercions here */
6519 446 : if (aliasvar && IsA(aliasvar, Var))
6520 : {
6521 430 : return get_variable(aliasvar, var->varlevelsup + levelsup,
6522 : istoplevel, context);
6523 : }
6524 : }
6525 :
6526 : /*
6527 : * Unnamed join has no refname. (Note: since it's unnamed, there is
6528 : * no way the user could have referenced it to create a whole-row Var
6529 : * for it. So we don't have to cover that case below.)
6530 : */
6531 16 : Assert(refname == NULL);
6532 : }
6533 :
6534 5708 : if (attnum == InvalidAttrNumber)
6535 35 : attname = NULL;
6536 5673 : else if (attnum > 0)
6537 : {
6538 : /* Get column name to use from the colinfo struct */
6539 5456 : if (attnum > colinfo->num_cols)
6540 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
6541 : attnum, rte->eref->aliasname);
6542 5456 : attname = colinfo->colnames[attnum - 1];
6543 5456 : if (attname == NULL) /* dropped column? */
6544 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
6545 : attnum, rte->eref->aliasname);
6546 : }
6547 : else
6548 : {
6549 : /* System column - name is fixed, get it from the catalog */
6550 217 : attname = get_rte_attribute_name(rte, attnum);
6551 : }
6552 :
6553 5708 : if (refname && (context->varprefix || attname == NULL))
6554 : {
6555 3190 : appendStringInfoString(buf, quote_identifier(refname));
6556 3190 : appendStringInfoChar(buf, '.');
6557 : }
6558 5708 : if (attname)
6559 5673 : appendStringInfoString(buf, quote_identifier(attname));
6560 : else
6561 : {
6562 35 : appendStringInfoChar(buf, '*');
6563 35 : if (istoplevel)
6564 3 : appendStringInfo(buf, "::%s",
6565 : format_type_with_typemod(var->vartype,
6566 : var->vartypmod));
6567 : }
6568 :
6569 5708 : return attname;
6570 : }
6571 :
6572 : /*
6573 : * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
6574 : * routine is actually a callback for get_special_varno, which handles finding
6575 : * the correct TargetEntry. We get the expression contained in that
6576 : * TargetEntry and just need to deparse it, a job we can throw back on
6577 : * get_rule_expr.
6578 : */
6579 : static void
6580 1338 : get_special_variable(Node *node, deparse_context *context, void *private)
6581 : {
6582 1338 : StringInfo buf = context->buf;
6583 :
6584 : /*
6585 : * Force parentheses because our caller probably assumed a Var is a simple
6586 : * expression.
6587 : */
6588 1338 : if (!IsA(node, Var))
6589 189 : appendStringInfoChar(buf, '(');
6590 1338 : get_rule_expr(node, context, true);
6591 1338 : if (!IsA(node, Var))
6592 189 : appendStringInfoChar(buf, ')');
6593 1338 : }
6594 :
6595 : /*
6596 : * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
6597 : * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
6598 : * invoke the callback provided.
6599 : */
6600 : static void
6601 3402 : resolve_special_varno(Node *node, deparse_context *context, void *private,
6602 : void (*callback) (Node *, deparse_context *, void *))
6603 : {
6604 : Var *var;
6605 : deparse_namespace *dpns;
6606 :
6607 : /* If it's not a Var, invoke the callback. */
6608 3402 : if (!IsA(node, Var))
6609 : {
6610 189 : callback(node, context, private);
6611 189 : return;
6612 : }
6613 :
6614 : /* Find appropriate nesting depth */
6615 3213 : var = (Var *) node;
6616 3213 : dpns = (deparse_namespace *) list_nth(context->namespaces,
6617 3213 : var->varlevelsup);
6618 :
6619 : /*
6620 : * It's a special RTE, so recurse.
6621 : */
6622 3213 : if (var->varno == OUTER_VAR && dpns->outer_tlist)
6623 : {
6624 : TargetEntry *tle;
6625 : deparse_namespace save_dpns;
6626 :
6627 1405 : tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
6628 1405 : if (!tle)
6629 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
6630 :
6631 1405 : push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
6632 1405 : resolve_special_varno((Node *) tle->expr, context, private, callback);
6633 1405 : pop_child_plan(dpns, &save_dpns);
6634 1405 : return;
6635 : }
6636 1808 : else if (var->varno == INNER_VAR && dpns->inner_tlist)
6637 : {
6638 : TargetEntry *tle;
6639 : deparse_namespace save_dpns;
6640 :
6641 435 : tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
6642 435 : if (!tle)
6643 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
6644 :
6645 435 : push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
6646 435 : resolve_special_varno((Node *) tle->expr, context, private, callback);
6647 435 : pop_child_plan(dpns, &save_dpns);
6648 435 : return;
6649 : }
6650 1373 : else if (var->varno == INDEX_VAR && dpns->index_tlist)
6651 : {
6652 : TargetEntry *tle;
6653 :
6654 224 : tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
6655 224 : if (!tle)
6656 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
6657 :
6658 224 : resolve_special_varno((Node *) tle->expr, context, private, callback);
6659 224 : return;
6660 : }
6661 1149 : else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
6662 0 : elog(ERROR, "bogus varno: %d", var->varno);
6663 :
6664 : /* Not special. Just invoke the callback. */
6665 1149 : callback(node, context, private);
6666 : }
6667 :
6668 : /*
6669 : * Get the name of a field of an expression of composite type. The
6670 : * expression is usually a Var, but we handle other cases too.
6671 : *
6672 : * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
6673 : *
6674 : * This is fairly straightforward when the expression has a named composite
6675 : * type; we need only look up the type in the catalogs. However, the type
6676 : * could also be RECORD. Since no actual table or view column is allowed to
6677 : * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
6678 : * or to a subquery output. We drill down to find the ultimate defining
6679 : * expression and attempt to infer the field name from it. We ereport if we
6680 : * can't determine the name.
6681 : *
6682 : * Similarly, a PARAM of type RECORD has to refer to some expression of
6683 : * a determinable composite type.
6684 : */
6685 : static const char *
6686 14 : get_name_for_var_field(Var *var, int fieldno,
6687 : int levelsup, deparse_context *context)
6688 : {
6689 : RangeTblEntry *rte;
6690 : AttrNumber attnum;
6691 : int netlevelsup;
6692 : deparse_namespace *dpns;
6693 : TupleDesc tupleDesc;
6694 : Node *expr;
6695 :
6696 : /*
6697 : * If it's a RowExpr that was expanded from a whole-row Var, use the
6698 : * column names attached to it.
6699 : */
6700 14 : if (IsA(var, RowExpr))
6701 : {
6702 0 : RowExpr *r = (RowExpr *) var;
6703 :
6704 0 : if (fieldno > 0 && fieldno <= list_length(r->colnames))
6705 0 : return strVal(list_nth(r->colnames, fieldno - 1));
6706 : }
6707 :
6708 : /*
6709 : * If it's a Param of type RECORD, try to find what the Param refers to.
6710 : */
6711 14 : if (IsA(var, Param))
6712 : {
6713 2 : Param *param = (Param *) var;
6714 : ListCell *ancestor_cell;
6715 :
6716 2 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
6717 2 : if (expr)
6718 : {
6719 : /* Found a match, so recurse to decipher the field name */
6720 : deparse_namespace save_dpns;
6721 : const char *result;
6722 :
6723 2 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
6724 2 : result = get_name_for_var_field((Var *) expr, fieldno,
6725 : 0, context);
6726 2 : pop_ancestor_plan(dpns, &save_dpns);
6727 2 : return result;
6728 : }
6729 : }
6730 :
6731 : /*
6732 : * If it's a Var of type RECORD, we have to find what the Var refers to;
6733 : * if not, we can use get_expr_result_type. If that fails, we try
6734 : * lookup_rowtype_tupdesc, which will probably fail too, but will ereport
6735 : * an acceptable message.
6736 : */
6737 14 : if (!IsA(var, Var) ||
6738 2 : var->vartype != RECORDOID)
6739 : {
6740 12 : if (get_expr_result_type((Node *) var, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
6741 0 : tupleDesc = lookup_rowtype_tupdesc_copy(exprType((Node *) var),
6742 : exprTypmod((Node *) var));
6743 12 : Assert(tupleDesc);
6744 : /* Got the tupdesc, so we can extract the field name */
6745 12 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
6746 12 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
6747 : }
6748 :
6749 : /* Find appropriate nesting depth */
6750 0 : netlevelsup = var->varlevelsup + levelsup;
6751 0 : if (netlevelsup >= list_length(context->namespaces))
6752 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
6753 : var->varlevelsup, levelsup);
6754 0 : dpns = (deparse_namespace *) list_nth(context->namespaces,
6755 : netlevelsup);
6756 :
6757 : /*
6758 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
6759 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
6760 : * down into the subplans, or INDEX_VAR, which is resolved similarly.
6761 : */
6762 0 : if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
6763 : {
6764 0 : rte = rt_fetch(var->varno, dpns->rtable);
6765 0 : attnum = var->varattno;
6766 : }
6767 0 : else if (var->varno == OUTER_VAR && dpns->outer_tlist)
6768 : {
6769 : TargetEntry *tle;
6770 : deparse_namespace save_dpns;
6771 : const char *result;
6772 :
6773 0 : tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
6774 0 : if (!tle)
6775 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
6776 :
6777 0 : Assert(netlevelsup == 0);
6778 0 : push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
6779 :
6780 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
6781 : levelsup, context);
6782 :
6783 0 : pop_child_plan(dpns, &save_dpns);
6784 0 : return result;
6785 : }
6786 0 : else if (var->varno == INNER_VAR && dpns->inner_tlist)
6787 : {
6788 : TargetEntry *tle;
6789 : deparse_namespace save_dpns;
6790 : const char *result;
6791 :
6792 0 : tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
6793 0 : if (!tle)
6794 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
6795 :
6796 0 : Assert(netlevelsup == 0);
6797 0 : push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
6798 :
6799 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
6800 : levelsup, context);
6801 :
6802 0 : pop_child_plan(dpns, &save_dpns);
6803 0 : return result;
6804 : }
6805 0 : else if (var->varno == INDEX_VAR && dpns->index_tlist)
6806 : {
6807 : TargetEntry *tle;
6808 : const char *result;
6809 :
6810 0 : tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
6811 0 : if (!tle)
6812 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
6813 :
6814 0 : Assert(netlevelsup == 0);
6815 :
6816 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
6817 : levelsup, context);
6818 :
6819 0 : return result;
6820 : }
6821 : else
6822 : {
6823 0 : elog(ERROR, "bogus varno: %d", var->varno);
6824 : return NULL; /* keep compiler quiet */
6825 : }
6826 :
6827 0 : if (attnum == InvalidAttrNumber)
6828 : {
6829 : /* Var is whole-row reference to RTE, so select the right field */
6830 0 : return get_rte_attribute_name(rte, fieldno);
6831 : }
6832 :
6833 : /*
6834 : * This part has essentially the same logic as the parser's
6835 : * expandRecordVariable() function, but we are dealing with a different
6836 : * representation of the input context, and we only need one field name
6837 : * not a TupleDesc. Also, we need special cases for finding subquery and
6838 : * CTE subplans when deparsing Plan trees.
6839 : */
6840 0 : expr = (Node *) var; /* default if we can't drill down */
6841 :
6842 0 : switch (rte->rtekind)
6843 : {
6844 : case RTE_RELATION:
6845 : case RTE_VALUES:
6846 : case RTE_NAMEDTUPLESTORE:
6847 :
6848 : /*
6849 : * This case should not occur: a column of a table or values list
6850 : * shouldn't have type RECORD. Fall through and fail (most
6851 : * likely) at the bottom.
6852 : */
6853 0 : break;
6854 : case RTE_SUBQUERY:
6855 : /* Subselect-in-FROM: examine sub-select's output expr */
6856 : {
6857 0 : if (rte->subquery)
6858 : {
6859 0 : TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
6860 : attnum);
6861 :
6862 0 : if (ste == NULL || ste->resjunk)
6863 0 : elog(ERROR, "subquery %s does not have attribute %d",
6864 : rte->eref->aliasname, attnum);
6865 0 : expr = (Node *) ste->expr;
6866 0 : if (IsA(expr, Var))
6867 : {
6868 : /*
6869 : * Recurse into the sub-select to see what its Var
6870 : * refers to. We have to build an additional level of
6871 : * namespace to keep in step with varlevelsup in the
6872 : * subselect.
6873 : */
6874 : deparse_namespace mydpns;
6875 : const char *result;
6876 :
6877 0 : set_deparse_for_query(&mydpns, rte->subquery,
6878 : context->namespaces);
6879 :
6880 0 : context->namespaces = lcons(&mydpns,
6881 : context->namespaces);
6882 :
6883 0 : result = get_name_for_var_field((Var *) expr, fieldno,
6884 : 0, context);
6885 :
6886 0 : context->namespaces =
6887 0 : list_delete_first(context->namespaces);
6888 :
6889 0 : return result;
6890 : }
6891 : /* else fall through to inspect the expression */
6892 : }
6893 : else
6894 : {
6895 : /*
6896 : * We're deparsing a Plan tree so we don't have complete
6897 : * RTE entries (in particular, rte->subquery is NULL). But
6898 : * the only place we'd see a Var directly referencing a
6899 : * SUBQUERY RTE is in a SubqueryScan plan node, and we can
6900 : * look into the child plan's tlist instead.
6901 : */
6902 : TargetEntry *tle;
6903 : deparse_namespace save_dpns;
6904 : const char *result;
6905 :
6906 0 : if (!dpns->inner_planstate)
6907 0 : elog(ERROR, "failed to find plan for subquery %s",
6908 : rte->eref->aliasname);
6909 0 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
6910 0 : if (!tle)
6911 0 : elog(ERROR, "bogus varattno for subquery var: %d",
6912 : attnum);
6913 0 : Assert(netlevelsup == 0);
6914 0 : push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
6915 :
6916 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
6917 : levelsup, context);
6918 :
6919 0 : pop_child_plan(dpns, &save_dpns);
6920 0 : return result;
6921 : }
6922 : }
6923 0 : break;
6924 : case RTE_JOIN:
6925 : /* Join RTE --- recursively inspect the alias variable */
6926 0 : if (rte->joinaliasvars == NIL)
6927 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
6928 0 : Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
6929 0 : expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
6930 0 : Assert(expr != NULL);
6931 : /* we intentionally don't strip implicit coercions here */
6932 0 : if (IsA(expr, Var))
6933 0 : return get_name_for_var_field((Var *) expr, fieldno,
6934 0 : var->varlevelsup + levelsup,
6935 : context);
6936 : /* else fall through to inspect the expression */
6937 0 : break;
6938 : case RTE_FUNCTION:
6939 : case RTE_TABLEFUNC:
6940 :
6941 : /*
6942 : * We couldn't get here unless a function is declared with one of
6943 : * its result columns as RECORD, which is not allowed.
6944 : */
6945 0 : break;
6946 : case RTE_CTE:
6947 : /* CTE reference: examine subquery's output expr */
6948 : {
6949 0 : CommonTableExpr *cte = NULL;
6950 : Index ctelevelsup;
6951 : ListCell *lc;
6952 :
6953 : /*
6954 : * Try to find the referenced CTE using the namespace stack.
6955 : */
6956 0 : ctelevelsup = rte->ctelevelsup + netlevelsup;
6957 0 : if (ctelevelsup >= list_length(context->namespaces))
6958 0 : lc = NULL;
6959 : else
6960 : {
6961 : deparse_namespace *ctedpns;
6962 :
6963 0 : ctedpns = (deparse_namespace *)
6964 0 : list_nth(context->namespaces, ctelevelsup);
6965 0 : foreach(lc, ctedpns->ctes)
6966 : {
6967 0 : cte = (CommonTableExpr *) lfirst(lc);
6968 0 : if (strcmp(cte->ctename, rte->ctename) == 0)
6969 0 : break;
6970 : }
6971 : }
6972 0 : if (lc != NULL)
6973 : {
6974 0 : Query *ctequery = (Query *) cte->ctequery;
6975 0 : TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
6976 : attnum);
6977 :
6978 0 : if (ste == NULL || ste->resjunk)
6979 0 : elog(ERROR, "subquery %s does not have attribute %d",
6980 : rte->eref->aliasname, attnum);
6981 0 : expr = (Node *) ste->expr;
6982 0 : if (IsA(expr, Var))
6983 : {
6984 : /*
6985 : * Recurse into the CTE to see what its Var refers to.
6986 : * We have to build an additional level of namespace
6987 : * to keep in step with varlevelsup in the CTE.
6988 : * Furthermore it could be an outer CTE, so we may
6989 : * have to delete some levels of namespace.
6990 : */
6991 0 : List *save_nslist = context->namespaces;
6992 : List *new_nslist;
6993 : deparse_namespace mydpns;
6994 : const char *result;
6995 :
6996 0 : set_deparse_for_query(&mydpns, ctequery,
6997 : context->namespaces);
6998 :
6999 0 : new_nslist = list_copy_tail(context->namespaces,
7000 : ctelevelsup);
7001 0 : context->namespaces = lcons(&mydpns, new_nslist);
7002 :
7003 0 : result = get_name_for_var_field((Var *) expr, fieldno,
7004 : 0, context);
7005 :
7006 0 : context->namespaces = save_nslist;
7007 :
7008 0 : return result;
7009 : }
7010 : /* else fall through to inspect the expression */
7011 : }
7012 : else
7013 : {
7014 : /*
7015 : * We're deparsing a Plan tree so we don't have a CTE
7016 : * list. But the only place we'd see a Var directly
7017 : * referencing a CTE RTE is in a CteScan plan node, and we
7018 : * can look into the subplan's tlist instead.
7019 : */
7020 : TargetEntry *tle;
7021 : deparse_namespace save_dpns;
7022 : const char *result;
7023 :
7024 0 : if (!dpns->inner_planstate)
7025 0 : elog(ERROR, "failed to find plan for CTE %s",
7026 : rte->eref->aliasname);
7027 0 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
7028 0 : if (!tle)
7029 0 : elog(ERROR, "bogus varattno for subquery var: %d",
7030 : attnum);
7031 0 : Assert(netlevelsup == 0);
7032 0 : push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
7033 :
7034 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
7035 : levelsup, context);
7036 :
7037 0 : pop_child_plan(dpns, &save_dpns);
7038 0 : return result;
7039 : }
7040 : }
7041 0 : break;
7042 : }
7043 :
7044 : /*
7045 : * We now have an expression we can't expand any more, so see if
7046 : * get_expr_result_type() can do anything with it. If not, pass to
7047 : * lookup_rowtype_tupdesc() which will probably fail, but will give an
7048 : * appropriate error message while failing.
7049 : */
7050 0 : if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
7051 0 : tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
7052 : exprTypmod(expr));
7053 0 : Assert(tupleDesc);
7054 : /* Got the tupdesc, so we can extract the field name */
7055 0 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
7056 0 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
7057 : }
7058 :
7059 : /*
7060 : * Try to find the referenced expression for a PARAM_EXEC Param that might
7061 : * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
7062 : *
7063 : * If successful, return the expression and set *dpns_p and *ancestor_cell_p
7064 : * appropriately for calling push_ancestor_plan(). If no referent can be
7065 : * found, return NULL.
7066 : */
7067 : static Node *
7068 227 : find_param_referent(Param *param, deparse_context *context,
7069 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
7070 : {
7071 : /* Initialize output parameters to prevent compiler warnings */
7072 227 : *dpns_p = NULL;
7073 227 : *ancestor_cell_p = NULL;
7074 :
7075 : /*
7076 : * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
7077 : * SubPlan argument. This will necessarily be in some ancestor of the
7078 : * current expression's PlanState.
7079 : */
7080 227 : if (param->paramkind == PARAM_EXEC)
7081 : {
7082 : deparse_namespace *dpns;
7083 : PlanState *child_ps;
7084 : bool in_same_plan_level;
7085 : ListCell *lc;
7086 :
7087 227 : dpns = (deparse_namespace *) linitial(context->namespaces);
7088 227 : child_ps = dpns->planstate;
7089 227 : in_same_plan_level = true;
7090 :
7091 389 : foreach(lc, dpns->ancestors)
7092 : {
7093 356 : PlanState *ps = (PlanState *) lfirst(lc);
7094 : ListCell *lc2;
7095 :
7096 : /*
7097 : * NestLoops transmit params to their inner child only; also, once
7098 : * we've crawled up out of a subplan, this couldn't possibly be
7099 : * the right match.
7100 : */
7101 546 : if (IsA(ps, NestLoopState) &&
7102 373 : child_ps == innerPlanState(ps) &&
7103 : in_same_plan_level)
7104 : {
7105 175 : NestLoop *nl = (NestLoop *) ps->plan;
7106 :
7107 244 : foreach(lc2, nl->nestParams)
7108 : {
7109 238 : NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
7110 :
7111 238 : if (nlp->paramno == param->paramid)
7112 : {
7113 : /* Found a match, so return it */
7114 169 : *dpns_p = dpns;
7115 169 : *ancestor_cell_p = lc;
7116 169 : return (Node *) nlp->paramval;
7117 : }
7118 : }
7119 : }
7120 :
7121 : /*
7122 : * Check to see if we're crawling up from a subplan.
7123 : */
7124 378 : foreach(lc2, ps->subPlan)
7125 : {
7126 33 : SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
7127 33 : SubPlan *subplan = sstate->subplan;
7128 : ListCell *lc3;
7129 : ListCell *lc4;
7130 :
7131 33 : if (child_ps != sstate->planstate)
7132 2 : continue;
7133 :
7134 : /* Matched subplan, so check its arguments */
7135 35 : forboth(lc3, subplan->parParam, lc4, subplan->args)
7136 : {
7137 29 : int paramid = lfirst_int(lc3);
7138 29 : Node *arg = (Node *) lfirst(lc4);
7139 :
7140 29 : if (paramid == param->paramid)
7141 : {
7142 : /* Found a match, so return it */
7143 25 : *dpns_p = dpns;
7144 25 : *ancestor_cell_p = lc;
7145 25 : return arg;
7146 : }
7147 : }
7148 :
7149 : /* Keep looking, but we are emerging from a subplan. */
7150 6 : in_same_plan_level = false;
7151 6 : break;
7152 : }
7153 :
7154 : /*
7155 : * Likewise check to see if we're emerging from an initplan.
7156 : * Initplans never have any parParams, so no need to search that
7157 : * list, but we need to know if we should reset
7158 : * in_same_plan_level.
7159 : */
7160 380 : foreach(lc2, ps->initPlan)
7161 : {
7162 32 : SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
7163 :
7164 32 : if (child_ps != sstate->planstate)
7165 28 : continue;
7166 :
7167 : /* No parameters to be had here. */
7168 4 : Assert(sstate->subplan->parParam == NIL);
7169 :
7170 : /* Keep looking, but we are emerging from an initplan. */
7171 4 : in_same_plan_level = false;
7172 4 : break;
7173 : }
7174 :
7175 : /* No luck, crawl up to next ancestor */
7176 162 : child_ps = ps;
7177 : }
7178 : }
7179 :
7180 : /* No referent found */
7181 33 : return NULL;
7182 : }
7183 :
7184 : /*
7185 : * Display a Param appropriately.
7186 : */
7187 : static void
7188 225 : get_parameter(Param *param, deparse_context *context)
7189 : {
7190 : Node *expr;
7191 : deparse_namespace *dpns;
7192 : ListCell *ancestor_cell;
7193 :
7194 : /*
7195 : * If it's a PARAM_EXEC parameter, try to locate the expression from which
7196 : * the parameter was computed. Note that failing to find a referent isn't
7197 : * an error, since the Param might well be a subplan output rather than an
7198 : * input.
7199 : */
7200 225 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
7201 225 : if (expr)
7202 : {
7203 : /* Found a match, so print it */
7204 : deparse_namespace save_dpns;
7205 : bool save_varprefix;
7206 : bool need_paren;
7207 :
7208 : /* Switch attention to the ancestor plan node */
7209 192 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
7210 :
7211 : /*
7212 : * Force prefixing of Vars, since they won't belong to the relation
7213 : * being scanned in the original plan node.
7214 : */
7215 192 : save_varprefix = context->varprefix;
7216 192 : context->varprefix = true;
7217 :
7218 : /*
7219 : * A Param's expansion is typically a Var, Aggref, or upper-level
7220 : * Param, which wouldn't need extra parentheses. Otherwise, insert
7221 : * parens to ensure the expression looks atomic.
7222 : */
7223 192 : need_paren = !(IsA(expr, Var) ||
7224 0 : IsA(expr, Aggref) ||
7225 0 : IsA(expr, Param));
7226 192 : if (need_paren)
7227 0 : appendStringInfoChar(context->buf, '(');
7228 :
7229 192 : get_rule_expr(expr, context, false);
7230 :
7231 192 : if (need_paren)
7232 0 : appendStringInfoChar(context->buf, ')');
7233 :
7234 192 : context->varprefix = save_varprefix;
7235 :
7236 192 : pop_ancestor_plan(dpns, &save_dpns);
7237 :
7238 417 : return;
7239 : }
7240 :
7241 : /*
7242 : * Not PARAM_EXEC, or couldn't find referent: just print $N.
7243 : */
7244 33 : appendStringInfo(context->buf, "$%d", param->paramid);
7245 : }
7246 :
7247 : /*
7248 : * get_simple_binary_op_name
7249 : *
7250 : * helper function for isSimpleNode
7251 : * will return single char binary operator name, or NULL if it's not
7252 : */
7253 : static const char *
7254 0 : get_simple_binary_op_name(OpExpr *expr)
7255 : {
7256 0 : List *args = expr->args;
7257 :
7258 0 : if (list_length(args) == 2)
7259 : {
7260 : /* binary operator */
7261 0 : Node *arg1 = (Node *) linitial(args);
7262 0 : Node *arg2 = (Node *) lsecond(args);
7263 : const char *op;
7264 :
7265 0 : op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
7266 0 : if (strlen(op) == 1)
7267 0 : return op;
7268 : }
7269 0 : return NULL;
7270 : }
7271 :
7272 :
7273 : /*
7274 : * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
7275 : *
7276 : * true : simple in the context of parent node's type
7277 : * false : not simple
7278 : */
7279 : static bool
7280 370 : isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
7281 : {
7282 370 : if (!node)
7283 0 : return false;
7284 :
7285 370 : switch (nodeTag(node))
7286 : {
7287 : case T_Var:
7288 : case T_Const:
7289 : case T_Param:
7290 : case T_CoerceToDomainValue:
7291 : case T_SetToDefault:
7292 : case T_CurrentOfExpr:
7293 : /* single words: always simple */
7294 310 : return true;
7295 :
7296 : case T_ArrayRef:
7297 : case T_ArrayExpr:
7298 : case T_RowExpr:
7299 : case T_CoalesceExpr:
7300 : case T_MinMaxExpr:
7301 : case T_SQLValueFunction:
7302 : case T_XmlExpr:
7303 : case T_NextValueExpr:
7304 : case T_NullIfExpr:
7305 : case T_Aggref:
7306 : case T_WindowFunc:
7307 : case T_FuncExpr:
7308 : /* function-like: name(..) or name[..] */
7309 42 : return true;
7310 :
7311 : /* CASE keywords act as parentheses */
7312 : case T_CaseExpr:
7313 0 : return true;
7314 :
7315 : case T_FieldSelect:
7316 :
7317 : /*
7318 : * appears simple since . has top precedence, unless parent is
7319 : * T_FieldSelect itself!
7320 : */
7321 3 : return (IsA(parentNode, FieldSelect) ? false : true);
7322 :
7323 : case T_FieldStore:
7324 :
7325 : /*
7326 : * treat like FieldSelect (probably doesn't matter)
7327 : */
7328 0 : return (IsA(parentNode, FieldStore) ? false : true);
7329 :
7330 : case T_CoerceToDomain:
7331 : /* maybe simple, check args */
7332 0 : return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
7333 : node, prettyFlags);
7334 : case T_RelabelType:
7335 0 : return isSimpleNode((Node *) ((RelabelType *) node)->arg,
7336 : node, prettyFlags);
7337 : case T_CoerceViaIO:
7338 0 : return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
7339 : node, prettyFlags);
7340 : case T_ArrayCoerceExpr:
7341 0 : return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
7342 : node, prettyFlags);
7343 : case T_ConvertRowtypeExpr:
7344 0 : return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
7345 : node, prettyFlags);
7346 :
7347 : case T_OpExpr:
7348 : {
7349 : /* depends on parent node type; needs further checking */
7350 14 : if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
7351 : {
7352 : const char *op;
7353 : const char *parentOp;
7354 : bool is_lopriop;
7355 : bool is_hipriop;
7356 : bool is_lopriparent;
7357 : bool is_hipriparent;
7358 :
7359 0 : op = get_simple_binary_op_name((OpExpr *) node);
7360 0 : if (!op)
7361 0 : return false;
7362 :
7363 : /* We know only the basic operators + - and * / % */
7364 0 : is_lopriop = (strchr("+-", *op) != NULL);
7365 0 : is_hipriop = (strchr("*/%", *op) != NULL);
7366 0 : if (!(is_lopriop || is_hipriop))
7367 0 : return false;
7368 :
7369 0 : parentOp = get_simple_binary_op_name((OpExpr *) parentNode);
7370 0 : if (!parentOp)
7371 0 : return false;
7372 :
7373 0 : is_lopriparent = (strchr("+-", *parentOp) != NULL);
7374 0 : is_hipriparent = (strchr("*/%", *parentOp) != NULL);
7375 0 : if (!(is_lopriparent || is_hipriparent))
7376 0 : return false;
7377 :
7378 0 : if (is_hipriop && is_lopriparent)
7379 0 : return true; /* op binds tighter than parent */
7380 :
7381 0 : if (is_lopriop && is_hipriparent)
7382 0 : return false;
7383 :
7384 : /*
7385 : * Operators are same priority --- can skip parens only if
7386 : * we have (a - b) - c, not a - (b - c).
7387 : */
7388 0 : if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
7389 0 : return true;
7390 :
7391 0 : return false;
7392 : }
7393 : /* else do the same stuff as for T_SubLink et al. */
7394 : /* FALL THROUGH */
7395 : }
7396 :
7397 : case T_SubLink:
7398 : case T_NullTest:
7399 : case T_BooleanTest:
7400 : case T_DistinctExpr:
7401 15 : switch (nodeTag(parentNode))
7402 : {
7403 : case T_FuncExpr:
7404 : {
7405 : /* special handling for casts */
7406 2 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
7407 :
7408 2 : if (type == COERCE_EXPLICIT_CAST ||
7409 : type == COERCE_IMPLICIT_CAST)
7410 2 : return false;
7411 0 : return true; /* own parentheses */
7412 : }
7413 : case T_BoolExpr: /* lower precedence */
7414 : case T_ArrayRef: /* other separators */
7415 : case T_ArrayExpr: /* other separators */
7416 : case T_RowExpr: /* other separators */
7417 : case T_CoalesceExpr: /* own parentheses */
7418 : case T_MinMaxExpr: /* own parentheses */
7419 : case T_XmlExpr: /* own parentheses */
7420 : case T_NullIfExpr: /* other separators */
7421 : case T_Aggref: /* own parentheses */
7422 : case T_WindowFunc: /* own parentheses */
7423 : case T_CaseExpr: /* other separators */
7424 12 : return true;
7425 : default:
7426 1 : return false;
7427 : }
7428 :
7429 : case T_BoolExpr:
7430 0 : switch (nodeTag(parentNode))
7431 : {
7432 : case T_BoolExpr:
7433 0 : if (prettyFlags & PRETTYFLAG_PAREN)
7434 : {
7435 : BoolExprType type;
7436 : BoolExprType parentType;
7437 :
7438 0 : type = ((BoolExpr *) node)->boolop;
7439 0 : parentType = ((BoolExpr *) parentNode)->boolop;
7440 0 : switch (type)
7441 : {
7442 : case NOT_EXPR:
7443 : case AND_EXPR:
7444 0 : if (parentType == AND_EXPR || parentType == OR_EXPR)
7445 0 : return true;
7446 0 : break;
7447 : case OR_EXPR:
7448 0 : if (parentType == OR_EXPR)
7449 0 : return true;
7450 0 : break;
7451 : }
7452 : }
7453 0 : return false;
7454 : case T_FuncExpr:
7455 : {
7456 : /* special handling for casts */
7457 0 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
7458 :
7459 0 : if (type == COERCE_EXPLICIT_CAST ||
7460 : type == COERCE_IMPLICIT_CAST)
7461 0 : return false;
7462 0 : return true; /* own parentheses */
7463 : }
7464 : case T_ArrayRef: /* other separators */
7465 : case T_ArrayExpr: /* other separators */
7466 : case T_RowExpr: /* other separators */
7467 : case T_CoalesceExpr: /* own parentheses */
7468 : case T_MinMaxExpr: /* own parentheses */
7469 : case T_XmlExpr: /* own parentheses */
7470 : case T_NullIfExpr: /* other separators */
7471 : case T_Aggref: /* own parentheses */
7472 : case T_WindowFunc: /* own parentheses */
7473 : case T_CaseExpr: /* other separators */
7474 0 : return true;
7475 : default:
7476 0 : return false;
7477 : }
7478 :
7479 : default:
7480 0 : break;
7481 : }
7482 : /* those we don't know: in dubio complexo */
7483 0 : return false;
7484 : }
7485 :
7486 :
7487 : /*
7488 : * appendContextKeyword - append a keyword to buffer
7489 : *
7490 : * If prettyPrint is enabled, perform a line break, and adjust indentation.
7491 : * Otherwise, just append the keyword.
7492 : */
7493 : static void
7494 1802 : appendContextKeyword(deparse_context *context, const char *str,
7495 : int indentBefore, int indentAfter, int indentPlus)
7496 : {
7497 1802 : StringInfo buf = context->buf;
7498 :
7499 1802 : if (PRETTY_INDENT(context))
7500 : {
7501 : int indentAmount;
7502 :
7503 1786 : context->indentLevel += indentBefore;
7504 :
7505 : /* remove any trailing spaces currently in the buffer ... */
7506 1786 : removeStringInfoSpaces(buf);
7507 : /* ... then add a newline and some spaces */
7508 1786 : appendStringInfoChar(buf, '\n');
7509 :
7510 1786 : if (context->indentLevel < PRETTYINDENT_LIMIT)
7511 1786 : indentAmount = Max(context->indentLevel, 0) + indentPlus;
7512 : else
7513 : {
7514 : /*
7515 : * If we're indented more than PRETTYINDENT_LIMIT characters, try
7516 : * to conserve horizontal space by reducing the per-level
7517 : * indentation. For best results the scale factor here should
7518 : * divide all the indent amounts that get added to indentLevel
7519 : * (PRETTYINDENT_STD, etc). It's important that the indentation
7520 : * not grow unboundedly, else deeply-nested trees use O(N^2)
7521 : * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
7522 : */
7523 0 : indentAmount = PRETTYINDENT_LIMIT +
7524 0 : (context->indentLevel - PRETTYINDENT_LIMIT) /
7525 : (PRETTYINDENT_STD / 2);
7526 0 : indentAmount %= PRETTYINDENT_LIMIT;
7527 : /* scale/wrap logic affects indentLevel, but not indentPlus */
7528 0 : indentAmount += indentPlus;
7529 : }
7530 1786 : appendStringInfoSpaces(buf, indentAmount);
7531 :
7532 1786 : appendStringInfoString(buf, str);
7533 :
7534 1786 : context->indentLevel += indentAfter;
7535 1786 : if (context->indentLevel < 0)
7536 0 : context->indentLevel = 0;
7537 : }
7538 : else
7539 16 : appendStringInfoString(buf, str);
7540 1802 : }
7541 :
7542 : /*
7543 : * removeStringInfoSpaces - delete trailing spaces from a buffer.
7544 : *
7545 : * Possibly this should move to stringinfo.c at some point.
7546 : */
7547 : static void
7548 1805 : removeStringInfoSpaces(StringInfo str)
7549 : {
7550 4678 : while (str->len > 0 && str->data[str->len - 1] == ' ')
7551 1068 : str->data[--(str->len)] = '\0';
7552 1805 : }
7553 :
7554 :
7555 : /*
7556 : * get_rule_expr_paren - deparse expr using get_rule_expr,
7557 : * embracing the string with parentheses if necessary for prettyPrint.
7558 : *
7559 : * Never embrace if prettyFlags=0, because it's done in the calling node.
7560 : *
7561 : * Any node that does *not* embrace its argument node by sql syntax (with
7562 : * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
7563 : * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
7564 : * added.
7565 : */
7566 : static void
7567 6978 : get_rule_expr_paren(Node *node, deparse_context *context,
7568 : bool showimplicit, Node *parentNode)
7569 : {
7570 : bool need_paren;
7571 :
7572 7348 : need_paren = PRETTY_PAREN(context) &&
7573 370 : !isSimpleNode(node, parentNode, context->prettyFlags);
7574 :
7575 6978 : if (need_paren)
7576 3 : appendStringInfoChar(context->buf, '(');
7577 :
7578 6978 : get_rule_expr(node, context, showimplicit);
7579 :
7580 6978 : if (need_paren)
7581 3 : appendStringInfoChar(context->buf, ')');
7582 6978 : }
7583 :
7584 :
7585 : /* ----------
7586 : * get_rule_expr - Parse back an expression
7587 : *
7588 : * Note: showimplicit determines whether we display any implicit cast that
7589 : * is present at the top of the expression tree. It is a passed argument,
7590 : * not a field of the context struct, because we change the value as we
7591 : * recurse down into the expression. In general we suppress implicit casts
7592 : * when the result type is known with certainty (eg, the arguments of an
7593 : * OR must be boolean). We display implicit casts for arguments of functions
7594 : * and operators, since this is needed to be certain that the same function
7595 : * or operator will be chosen when the expression is re-parsed.
7596 : * ----------
7597 : */
7598 : static void
7599 14239 : get_rule_expr(Node *node, deparse_context *context,
7600 : bool showimplicit)
7601 : {
7602 14239 : StringInfo buf = context->buf;
7603 :
7604 14239 : if (node == NULL)
7605 14240 : return;
7606 :
7607 : /* Guard against excessively long or deeply-nested queries */
7608 14238 : CHECK_FOR_INTERRUPTS();
7609 14238 : check_stack_depth();
7610 :
7611 : /*
7612 : * Each level of get_rule_expr must emit an indivisible term
7613 : * (parenthesized if necessary) to ensure result is reparsed into the same
7614 : * expression tree. The only exception is that when the input is a List,
7615 : * we emit the component items comma-separated with no surrounding
7616 : * decoration; this is convenient for most callers.
7617 : */
7618 14238 : switch (nodeTag(node))
7619 : {
7620 : case T_Var:
7621 5985 : (void) get_variable((Var *) node, 0, false, context);
7622 5985 : break;
7623 :
7624 : case T_Const:
7625 3170 : get_const_expr((Const *) node, context, 0);
7626 3170 : break;
7627 :
7628 : case T_Param:
7629 225 : get_parameter((Param *) node, context);
7630 225 : break;
7631 :
7632 : case T_Aggref:
7633 49 : get_agg_expr((Aggref *) node, context, (Aggref *) node);
7634 49 : break;
7635 :
7636 : case T_GroupingFunc:
7637 : {
7638 6 : GroupingFunc *gexpr = (GroupingFunc *) node;
7639 :
7640 6 : appendStringInfoString(buf, "GROUPING(");
7641 6 : get_rule_expr((Node *) gexpr->args, context, true);
7642 6 : appendStringInfoChar(buf, ')');
7643 : }
7644 6 : break;
7645 :
7646 : case T_WindowFunc:
7647 2 : get_windowfunc_expr((WindowFunc *) node, context);
7648 2 : break;
7649 :
7650 : case T_ArrayRef:
7651 : {
7652 27 : ArrayRef *aref = (ArrayRef *) node;
7653 : bool need_parens;
7654 :
7655 : /*
7656 : * If the argument is a CaseTestExpr, we must be inside a
7657 : * FieldStore, ie, we are assigning to an element of an array
7658 : * within a composite column. Since we already punted on
7659 : * displaying the FieldStore's target information, just punt
7660 : * here too, and display only the assignment source
7661 : * expression.
7662 : */
7663 27 : if (IsA(aref->refexpr, CaseTestExpr))
7664 : {
7665 0 : Assert(aref->refassgnexpr);
7666 0 : get_rule_expr((Node *) aref->refassgnexpr,
7667 : context, showimplicit);
7668 0 : break;
7669 : }
7670 :
7671 : /*
7672 : * Parenthesize the argument unless it's a simple Var or a
7673 : * FieldSelect. (In particular, if it's another ArrayRef, we
7674 : * *must* parenthesize to avoid confusion.)
7675 : */
7676 40 : need_parens = !IsA(aref->refexpr, Var) &&
7677 13 : !IsA(aref->refexpr, FieldSelect);
7678 27 : if (need_parens)
7679 13 : appendStringInfoChar(buf, '(');
7680 27 : get_rule_expr((Node *) aref->refexpr, context, showimplicit);
7681 27 : if (need_parens)
7682 13 : appendStringInfoChar(buf, ')');
7683 :
7684 : /*
7685 : * If there's a refassgnexpr, we want to print the node in the
7686 : * format "array[subscripts] := refassgnexpr". This is not
7687 : * legal SQL, so decompilation of INSERT or UPDATE statements
7688 : * should always use processIndirection as part of the
7689 : * statement-level syntax. We should only see this when
7690 : * EXPLAIN tries to print the targetlist of a plan resulting
7691 : * from such a statement.
7692 : */
7693 27 : if (aref->refassgnexpr)
7694 : {
7695 : Node *refassgnexpr;
7696 :
7697 : /*
7698 : * Use processIndirection to print this node's subscripts
7699 : * as well as any additional field selections or
7700 : * subscripting in immediate descendants. It returns the
7701 : * RHS expr that is actually being "assigned".
7702 : */
7703 2 : refassgnexpr = processIndirection(node, context);
7704 2 : appendStringInfoString(buf, " := ");
7705 2 : get_rule_expr(refassgnexpr, context, showimplicit);
7706 : }
7707 : else
7708 : {
7709 : /* Just an ordinary array fetch, so print subscripts */
7710 25 : printSubscripts(aref, context);
7711 : }
7712 : }
7713 27 : break;
7714 :
7715 : case T_FuncExpr:
7716 836 : get_func_expr((FuncExpr *) node, context, showimplicit);
7717 836 : break;
7718 :
7719 : case T_NamedArgExpr:
7720 : {
7721 3 : NamedArgExpr *na = (NamedArgExpr *) node;
7722 :
7723 3 : appendStringInfo(buf, "%s => ", quote_identifier(na->name));
7724 3 : get_rule_expr((Node *) na->arg, context, showimplicit);
7725 : }
7726 3 : break;
7727 :
7728 : case T_OpExpr:
7729 2743 : get_oper_expr((OpExpr *) node, context);
7730 2743 : break;
7731 :
7732 : case T_DistinctExpr:
7733 : {
7734 2 : DistinctExpr *expr = (DistinctExpr *) node;
7735 2 : List *args = expr->args;
7736 2 : Node *arg1 = (Node *) linitial(args);
7737 2 : Node *arg2 = (Node *) lsecond(args);
7738 :
7739 2 : if (!PRETTY_PAREN(context))
7740 1 : appendStringInfoChar(buf, '(');
7741 2 : get_rule_expr_paren(arg1, context, true, node);
7742 2 : appendStringInfoString(buf, " IS DISTINCT FROM ");
7743 2 : get_rule_expr_paren(arg2, context, true, node);
7744 2 : if (!PRETTY_PAREN(context))
7745 1 : appendStringInfoChar(buf, ')');
7746 : }
7747 2 : break;
7748 :
7749 : case T_NullIfExpr:
7750 : {
7751 0 : NullIfExpr *nullifexpr = (NullIfExpr *) node;
7752 :
7753 0 : appendStringInfoString(buf, "NULLIF(");
7754 0 : get_rule_expr((Node *) nullifexpr->args, context, true);
7755 0 : appendStringInfoChar(buf, ')');
7756 : }
7757 0 : break;
7758 :
7759 : case T_ScalarArrayOpExpr:
7760 : {
7761 46 : ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
7762 46 : List *args = expr->args;
7763 46 : Node *arg1 = (Node *) linitial(args);
7764 46 : Node *arg2 = (Node *) lsecond(args);
7765 :
7766 46 : if (!PRETTY_PAREN(context))
7767 44 : appendStringInfoChar(buf, '(');
7768 46 : get_rule_expr_paren(arg1, context, true, node);
7769 46 : appendStringInfo(buf, " %s %s (",
7770 : generate_operator_name(expr->opno,
7771 : exprType(arg1),
7772 : get_base_element_type(exprType(arg2))),
7773 46 : expr->useOr ? "ANY" : "ALL");
7774 46 : get_rule_expr_paren(arg2, context, true, node);
7775 :
7776 : /*
7777 : * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
7778 : * a bare sub-SELECT. Since we're here, the sub-SELECT must
7779 : * be meant as a scalar sub-SELECT yielding an array value to
7780 : * be used in ScalarArrayOpExpr; but the grammar will
7781 : * preferentially interpret such a construct as an ANY/ALL
7782 : * SubLink. To prevent misparsing the output that way, insert
7783 : * a dummy coercion (which will be stripped by parse analysis,
7784 : * so no inefficiency is added in dump and reload). This is
7785 : * indeed most likely what the user wrote to get the construct
7786 : * accepted in the first place.
7787 : */
7788 47 : if (IsA(arg2, SubLink) &&
7789 1 : ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
7790 1 : appendStringInfo(buf, "::%s",
7791 : format_type_with_typemod(exprType(arg2),
7792 : exprTypmod(arg2)));
7793 46 : appendStringInfoChar(buf, ')');
7794 46 : if (!PRETTY_PAREN(context))
7795 44 : appendStringInfoChar(buf, ')');
7796 : }
7797 46 : break;
7798 :
7799 : case T_BoolExpr:
7800 : {
7801 398 : BoolExpr *expr = (BoolExpr *) node;
7802 398 : Node *first_arg = linitial(expr->args);
7803 398 : ListCell *arg = lnext(list_head(expr->args));
7804 :
7805 398 : switch (expr->boolop)
7806 : {
7807 : case AND_EXPR:
7808 347 : if (!PRETTY_PAREN(context))
7809 341 : appendStringInfoChar(buf, '(');
7810 347 : get_rule_expr_paren(first_arg, context,
7811 : false, node);
7812 1143 : while (arg)
7813 : {
7814 449 : appendStringInfoString(buf, " AND ");
7815 449 : get_rule_expr_paren((Node *) lfirst(arg), context,
7816 : false, node);
7817 449 : arg = lnext(arg);
7818 : }
7819 347 : if (!PRETTY_PAREN(context))
7820 341 : appendStringInfoChar(buf, ')');
7821 347 : break;
7822 :
7823 : case OR_EXPR:
7824 39 : if (!PRETTY_PAREN(context))
7825 39 : appendStringInfoChar(buf, '(');
7826 39 : get_rule_expr_paren(first_arg, context,
7827 : false, node);
7828 126 : while (arg)
7829 : {
7830 48 : appendStringInfoString(buf, " OR ");
7831 48 : get_rule_expr_paren((Node *) lfirst(arg), context,
7832 : false, node);
7833 48 : arg = lnext(arg);
7834 : }
7835 39 : if (!PRETTY_PAREN(context))
7836 39 : appendStringInfoChar(buf, ')');
7837 39 : break;
7838 :
7839 : case NOT_EXPR:
7840 12 : if (!PRETTY_PAREN(context))
7841 12 : appendStringInfoChar(buf, '(');
7842 12 : appendStringInfoString(buf, "NOT ");
7843 12 : get_rule_expr_paren(first_arg, context,
7844 : false, node);
7845 12 : if (!PRETTY_PAREN(context))
7846 12 : appendStringInfoChar(buf, ')');
7847 12 : break;
7848 :
7849 : default:
7850 0 : elog(ERROR, "unrecognized boolop: %d",
7851 : (int) expr->boolop);
7852 : }
7853 : }
7854 398 : break;
7855 :
7856 : case T_SubLink:
7857 38 : get_sublink_expr((SubLink *) node, context);
7858 38 : break;
7859 :
7860 : case T_SubPlan:
7861 : {
7862 14 : SubPlan *subplan = (SubPlan *) node;
7863 :
7864 : /*
7865 : * We cannot see an already-planned subplan in rule deparsing,
7866 : * only while EXPLAINing a query plan. We don't try to
7867 : * reconstruct the original SQL, just reference the subplan
7868 : * that appears elsewhere in EXPLAIN's result.
7869 : */
7870 14 : if (subplan->useHashTable)
7871 6 : appendStringInfo(buf, "(hashed %s)", subplan->plan_name);
7872 : else
7873 8 : appendStringInfo(buf, "(%s)", subplan->plan_name);
7874 : }
7875 14 : break;
7876 :
7877 : case T_AlternativeSubPlan:
7878 : {
7879 9 : AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
7880 : ListCell *lc;
7881 :
7882 : /* As above, this can only happen during EXPLAIN */
7883 9 : appendStringInfoString(buf, "(alternatives: ");
7884 27 : foreach(lc, asplan->subplans)
7885 : {
7886 18 : SubPlan *splan = lfirst_node(SubPlan, lc);
7887 :
7888 18 : if (splan->useHashTable)
7889 9 : appendStringInfo(buf, "hashed %s", splan->plan_name);
7890 : else
7891 9 : appendStringInfoString(buf, splan->plan_name);
7892 18 : if (lnext(lc))
7893 9 : appendStringInfoString(buf, " or ");
7894 : }
7895 9 : appendStringInfoChar(buf, ')');
7896 : }
7897 9 : break;
7898 :
7899 : case T_FieldSelect:
7900 : {
7901 12 : FieldSelect *fselect = (FieldSelect *) node;
7902 12 : Node *arg = (Node *) fselect->arg;
7903 12 : int fno = fselect->fieldnum;
7904 : const char *fieldname;
7905 : bool need_parens;
7906 :
7907 : /*
7908 : * Parenthesize the argument unless it's an ArrayRef or
7909 : * another FieldSelect. Note in particular that it would be
7910 : * WRONG to not parenthesize a Var argument; simplicity is not
7911 : * the issue here, having the right number of names is.
7912 : */
7913 12 : need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect);
7914 12 : if (need_parens)
7915 2 : appendStringInfoChar(buf, '(');
7916 12 : get_rule_expr(arg, context, true);
7917 12 : if (need_parens)
7918 2 : appendStringInfoChar(buf, ')');
7919 :
7920 : /*
7921 : * Get and print the field name.
7922 : */
7923 12 : fieldname = get_name_for_var_field((Var *) arg, fno,
7924 : 0, context);
7925 12 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
7926 : }
7927 12 : break;
7928 :
7929 : case T_FieldStore:
7930 : {
7931 0 : FieldStore *fstore = (FieldStore *) node;
7932 : bool need_parens;
7933 :
7934 : /*
7935 : * There is no good way to represent a FieldStore as real SQL,
7936 : * so decompilation of INSERT or UPDATE statements should
7937 : * always use processIndirection as part of the
7938 : * statement-level syntax. We should only get here when
7939 : * EXPLAIN tries to print the targetlist of a plan resulting
7940 : * from such a statement. The plan case is even harder than
7941 : * ordinary rules would be, because the planner tries to
7942 : * collapse multiple assignments to the same field or subfield
7943 : * into one FieldStore; so we can see a list of target fields
7944 : * not just one, and the arguments could be FieldStores
7945 : * themselves. We don't bother to try to print the target
7946 : * field names; we just print the source arguments, with a
7947 : * ROW() around them if there's more than one. This isn't
7948 : * terribly complete, but it's probably good enough for
7949 : * EXPLAIN's purposes; especially since anything more would be
7950 : * either hopelessly confusing or an even poorer
7951 : * representation of what the plan is actually doing.
7952 : */
7953 0 : need_parens = (list_length(fstore->newvals) != 1);
7954 0 : if (need_parens)
7955 0 : appendStringInfoString(buf, "ROW(");
7956 0 : get_rule_expr((Node *) fstore->newvals, context, showimplicit);
7957 0 : if (need_parens)
7958 0 : appendStringInfoChar(buf, ')');
7959 : }
7960 0 : break;
7961 :
7962 : case T_RelabelType:
7963 : {
7964 115 : RelabelType *relabel = (RelabelType *) node;
7965 115 : Node *arg = (Node *) relabel->arg;
7966 :
7967 115 : if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
7968 : !showimplicit)
7969 : {
7970 : /* don't show the implicit cast */
7971 0 : get_rule_expr_paren(arg, context, false, node);
7972 : }
7973 : else
7974 : {
7975 115 : get_coercion_expr(arg, context,
7976 : relabel->resulttype,
7977 : relabel->resulttypmod,
7978 : node);
7979 : }
7980 : }
7981 115 : break;
7982 :
7983 : case T_CoerceViaIO:
7984 : {
7985 18 : CoerceViaIO *iocoerce = (CoerceViaIO *) node;
7986 18 : Node *arg = (Node *) iocoerce->arg;
7987 :
7988 18 : if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
7989 : !showimplicit)
7990 : {
7991 : /* don't show the implicit cast */
7992 5 : get_rule_expr_paren(arg, context, false, node);
7993 : }
7994 : else
7995 : {
7996 13 : get_coercion_expr(arg, context,
7997 : iocoerce->resulttype,
7998 : -1,
7999 : node);
8000 : }
8001 : }
8002 18 : break;
8003 :
8004 : case T_ArrayCoerceExpr:
8005 : {
8006 3 : ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
8007 3 : Node *arg = (Node *) acoerce->arg;
8008 :
8009 3 : if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
8010 : !showimplicit)
8011 : {
8012 : /* don't show the implicit cast */
8013 0 : get_rule_expr_paren(arg, context, false, node);
8014 : }
8015 : else
8016 : {
8017 3 : get_coercion_expr(arg, context,
8018 : acoerce->resulttype,
8019 : acoerce->resulttypmod,
8020 : node);
8021 : }
8022 : }
8023 3 : break;
8024 :
8025 : case T_ConvertRowtypeExpr:
8026 : {
8027 0 : ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
8028 0 : Node *arg = (Node *) convert->arg;
8029 :
8030 0 : if (convert->convertformat == COERCE_IMPLICIT_CAST &&
8031 : !showimplicit)
8032 : {
8033 : /* don't show the implicit cast */
8034 0 : get_rule_expr_paren(arg, context, false, node);
8035 : }
8036 : else
8037 : {
8038 0 : get_coercion_expr(arg, context,
8039 : convert->resulttype, -1,
8040 : node);
8041 : }
8042 : }
8043 0 : break;
8044 :
8045 : case T_CollateExpr:
8046 : {
8047 3 : CollateExpr *collate = (CollateExpr *) node;
8048 3 : Node *arg = (Node *) collate->arg;
8049 :
8050 3 : if (!PRETTY_PAREN(context))
8051 3 : appendStringInfoChar(buf, '(');
8052 3 : get_rule_expr_paren(arg, context, showimplicit, node);
8053 3 : appendStringInfo(buf, " COLLATE %s",
8054 : generate_collation_name(collate->collOid));
8055 3 : if (!PRETTY_PAREN(context))
8056 3 : appendStringInfoChar(buf, ')');
8057 : }
8058 3 : break;
8059 :
8060 : case T_CaseExpr:
8061 : {
8062 25 : CaseExpr *caseexpr = (CaseExpr *) node;
8063 : ListCell *temp;
8064 :
8065 25 : appendContextKeyword(context, "CASE",
8066 : 0, PRETTYINDENT_VAR, 0);
8067 25 : if (caseexpr->arg)
8068 : {
8069 4 : appendStringInfoChar(buf, ' ');
8070 4 : get_rule_expr((Node *) caseexpr->arg, context, true);
8071 : }
8072 93 : foreach(temp, caseexpr->args)
8073 : {
8074 68 : CaseWhen *when = (CaseWhen *) lfirst(temp);
8075 68 : Node *w = (Node *) when->expr;
8076 :
8077 68 : if (caseexpr->arg)
8078 : {
8079 : /*
8080 : * The parser should have produced WHEN clauses of the
8081 : * form "CaseTestExpr = RHS", possibly with an
8082 : * implicit coercion inserted above the CaseTestExpr.
8083 : * For accurate decompilation of rules it's essential
8084 : * that we show just the RHS. However in an
8085 : * expression that's been through the optimizer, the
8086 : * WHEN clause could be almost anything (since the
8087 : * equality operator could have been expanded into an
8088 : * inline function). If we don't recognize the form
8089 : * of the WHEN clause, just punt and display it as-is.
8090 : */
8091 14 : if (IsA(w, OpExpr))
8092 : {
8093 14 : List *args = ((OpExpr *) w)->args;
8094 :
8095 28 : if (list_length(args) == 2 &&
8096 14 : IsA(strip_implicit_coercions(linitial(args)),
8097 : CaseTestExpr))
8098 14 : w = (Node *) lsecond(args);
8099 : }
8100 : }
8101 :
8102 68 : if (!PRETTY_INDENT(context))
8103 4 : appendStringInfoChar(buf, ' ');
8104 68 : appendContextKeyword(context, "WHEN ",
8105 : 0, 0, 0);
8106 68 : get_rule_expr(w, context, false);
8107 68 : appendStringInfoString(buf, " THEN ");
8108 68 : get_rule_expr((Node *) when->result, context, true);
8109 : }
8110 25 : if (!PRETTY_INDENT(context))
8111 4 : appendStringInfoChar(buf, ' ');
8112 25 : appendContextKeyword(context, "ELSE ",
8113 : 0, 0, 0);
8114 25 : get_rule_expr((Node *) caseexpr->defresult, context, true);
8115 25 : if (!PRETTY_INDENT(context))
8116 4 : appendStringInfoChar(buf, ' ');
8117 25 : appendContextKeyword(context, "END",
8118 : -PRETTYINDENT_VAR, 0, 0);
8119 : }
8120 25 : break;
8121 :
8122 : case T_CaseTestExpr:
8123 : {
8124 : /*
8125 : * Normally we should never get here, since for expressions
8126 : * that can contain this node type we attempt to avoid
8127 : * recursing to it. But in an optimized expression we might
8128 : * be unable to avoid that (see comments for CaseExpr). If we
8129 : * do see one, print it as CASE_TEST_EXPR.
8130 : */
8131 0 : appendStringInfoString(buf, "CASE_TEST_EXPR");
8132 : }
8133 0 : break;
8134 :
8135 : case T_ArrayExpr:
8136 : {
8137 42 : ArrayExpr *arrayexpr = (ArrayExpr *) node;
8138 :
8139 42 : appendStringInfoString(buf, "ARRAY[");
8140 42 : get_rule_expr((Node *) arrayexpr->elements, context, true);
8141 42 : appendStringInfoChar(buf, ']');
8142 :
8143 : /*
8144 : * If the array isn't empty, we assume its elements are
8145 : * coerced to the desired type. If it's empty, though, we
8146 : * need an explicit coercion to the array type.
8147 : */
8148 42 : if (arrayexpr->elements == NIL)
8149 1 : appendStringInfo(buf, "::%s",
8150 : format_type_with_typemod(arrayexpr->array_typeid, -1));
8151 : }
8152 42 : break;
8153 :
8154 : case T_RowExpr:
8155 : {
8156 3 : RowExpr *rowexpr = (RowExpr *) node;
8157 3 : TupleDesc tupdesc = NULL;
8158 : ListCell *arg;
8159 : int i;
8160 : char *sep;
8161 :
8162 : /*
8163 : * If it's a named type and not RECORD, we may have to skip
8164 : * dropped columns and/or claim there are NULLs for added
8165 : * columns.
8166 : */
8167 3 : if (rowexpr->row_typeid != RECORDOID)
8168 : {
8169 2 : tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
8170 2 : Assert(list_length(rowexpr->args) <= tupdesc->natts);
8171 : }
8172 :
8173 : /*
8174 : * SQL99 allows "ROW" to be omitted when there is more than
8175 : * one column, but for simplicity we always print it.
8176 : */
8177 3 : appendStringInfoString(buf, "ROW(");
8178 3 : sep = "";
8179 3 : i = 0;
8180 8 : foreach(arg, rowexpr->args)
8181 : {
8182 5 : Node *e = (Node *) lfirst(arg);
8183 :
8184 8 : if (tupdesc == NULL ||
8185 3 : !TupleDescAttr(tupdesc, i)->attisdropped)
8186 : {
8187 5 : appendStringInfoString(buf, sep);
8188 : /* Whole-row Vars need special treatment here */
8189 5 : get_rule_expr_toplevel(e, context, true);
8190 5 : sep = ", ";
8191 : }
8192 5 : i++;
8193 : }
8194 3 : if (tupdesc != NULL)
8195 : {
8196 4 : while (i < tupdesc->natts)
8197 : {
8198 0 : if (!TupleDescAttr(tupdesc, i)->attisdropped)
8199 : {
8200 0 : appendStringInfoString(buf, sep);
8201 0 : appendStringInfoString(buf, "NULL");
8202 0 : sep = ", ";
8203 : }
8204 0 : i++;
8205 : }
8206 :
8207 2 : ReleaseTupleDesc(tupdesc);
8208 : }
8209 3 : appendStringInfoChar(buf, ')');
8210 3 : if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
8211 2 : appendStringInfo(buf, "::%s",
8212 : format_type_with_typemod(rowexpr->row_typeid, -1));
8213 : }
8214 3 : break;
8215 :
8216 : case T_RowCompareExpr:
8217 : {
8218 2 : RowCompareExpr *rcexpr = (RowCompareExpr *) node;
8219 : ListCell *arg;
8220 : char *sep;
8221 :
8222 : /*
8223 : * SQL99 allows "ROW" to be omitted when there is more than
8224 : * one column, but for simplicity we always print it.
8225 : */
8226 2 : appendStringInfoString(buf, "(ROW(");
8227 2 : sep = "";
8228 6 : foreach(arg, rcexpr->largs)
8229 : {
8230 4 : Node *e = (Node *) lfirst(arg);
8231 :
8232 4 : appendStringInfoString(buf, sep);
8233 4 : get_rule_expr(e, context, true);
8234 4 : sep = ", ";
8235 : }
8236 :
8237 : /*
8238 : * We assume that the name of the first-column operator will
8239 : * do for all the rest too. This is definitely open to
8240 : * failure, eg if some but not all operators were renamed
8241 : * since the construct was parsed, but there seems no way to
8242 : * be perfect.
8243 : */
8244 6 : appendStringInfo(buf, ") %s ROW(",
8245 2 : generate_operator_name(linitial_oid(rcexpr->opnos),
8246 2 : exprType(linitial(rcexpr->largs)),
8247 2 : exprType(linitial(rcexpr->rargs))));
8248 2 : sep = "";
8249 6 : foreach(arg, rcexpr->rargs)
8250 : {
8251 4 : Node *e = (Node *) lfirst(arg);
8252 :
8253 4 : appendStringInfoString(buf, sep);
8254 4 : get_rule_expr(e, context, true);
8255 4 : sep = ", ";
8256 : }
8257 2 : appendStringInfoString(buf, "))");
8258 : }
8259 2 : break;
8260 :
8261 : case T_CoalesceExpr:
8262 : {
8263 50 : CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
8264 :
8265 50 : appendStringInfoString(buf, "COALESCE(");
8266 50 : get_rule_expr((Node *) coalesceexpr->args, context, true);
8267 50 : appendStringInfoChar(buf, ')');
8268 : }
8269 50 : break;
8270 :
8271 : case T_MinMaxExpr:
8272 : {
8273 3 : MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
8274 :
8275 3 : switch (minmaxexpr->op)
8276 : {
8277 : case IS_GREATEST:
8278 1 : appendStringInfoString(buf, "GREATEST(");
8279 1 : break;
8280 : case IS_LEAST:
8281 2 : appendStringInfoString(buf, "LEAST(");
8282 2 : break;
8283 : }
8284 3 : get_rule_expr((Node *) minmaxexpr->args, context, true);
8285 3 : appendStringInfoChar(buf, ')');
8286 : }
8287 3 : break;
8288 :
8289 : case T_SQLValueFunction:
8290 : {
8291 35 : SQLValueFunction *svf = (SQLValueFunction *) node;
8292 :
8293 : /*
8294 : * Note: this code knows that typmod for time, timestamp, and
8295 : * timestamptz just prints as integer.
8296 : */
8297 35 : switch (svf->op)
8298 : {
8299 : case SVFOP_CURRENT_DATE:
8300 1 : appendStringInfoString(buf, "CURRENT_DATE");
8301 1 : break;
8302 : case SVFOP_CURRENT_TIME:
8303 0 : appendStringInfoString(buf, "CURRENT_TIME");
8304 0 : break;
8305 : case SVFOP_CURRENT_TIME_N:
8306 0 : appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
8307 0 : break;
8308 : case SVFOP_CURRENT_TIMESTAMP:
8309 0 : appendStringInfoString(buf, "CURRENT_TIMESTAMP");
8310 0 : break;
8311 : case SVFOP_CURRENT_TIMESTAMP_N:
8312 1 : appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
8313 : svf->typmod);
8314 1 : break;
8315 : case SVFOP_LOCALTIME:
8316 0 : appendStringInfoString(buf, "LOCALTIME");
8317 0 : break;
8318 : case SVFOP_LOCALTIME_N:
8319 0 : appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
8320 0 : break;
8321 : case SVFOP_LOCALTIMESTAMP:
8322 0 : appendStringInfoString(buf, "LOCALTIMESTAMP");
8323 0 : break;
8324 : case SVFOP_LOCALTIMESTAMP_N:
8325 1 : appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
8326 : svf->typmod);
8327 1 : break;
8328 : case SVFOP_CURRENT_ROLE:
8329 0 : appendStringInfoString(buf, "CURRENT_ROLE");
8330 0 : break;
8331 : case SVFOP_CURRENT_USER:
8332 32 : appendStringInfoString(buf, "CURRENT_USER");
8333 32 : break;
8334 : case SVFOP_USER:
8335 0 : appendStringInfoString(buf, "USER");
8336 0 : break;
8337 : case SVFOP_SESSION_USER:
8338 0 : appendStringInfoString(buf, "SESSION_USER");
8339 0 : break;
8340 : case SVFOP_CURRENT_CATALOG:
8341 0 : appendStringInfoString(buf, "CURRENT_CATALOG");
8342 0 : break;
8343 : case SVFOP_CURRENT_SCHEMA:
8344 0 : appendStringInfoString(buf, "CURRENT_SCHEMA");
8345 0 : break;
8346 : }
8347 : }
8348 35 : break;
8349 :
8350 : case T_XmlExpr:
8351 : {
8352 1 : XmlExpr *xexpr = (XmlExpr *) node;
8353 1 : bool needcomma = false;
8354 : ListCell *arg;
8355 : ListCell *narg;
8356 : Const *con;
8357 :
8358 1 : switch (xexpr->op)
8359 : {
8360 : case IS_XMLCONCAT:
8361 0 : appendStringInfoString(buf, "XMLCONCAT(");
8362 0 : break;
8363 : case IS_XMLELEMENT:
8364 0 : appendStringInfoString(buf, "XMLELEMENT(");
8365 0 : break;
8366 : case IS_XMLFOREST:
8367 0 : appendStringInfoString(buf, "XMLFOREST(");
8368 0 : break;
8369 : case IS_XMLPARSE:
8370 1 : appendStringInfoString(buf, "XMLPARSE(");
8371 1 : break;
8372 : case IS_XMLPI:
8373 0 : appendStringInfoString(buf, "XMLPI(");
8374 0 : break;
8375 : case IS_XMLROOT:
8376 0 : appendStringInfoString(buf, "XMLROOT(");
8377 0 : break;
8378 : case IS_XMLSERIALIZE:
8379 0 : appendStringInfoString(buf, "XMLSERIALIZE(");
8380 0 : break;
8381 : case IS_DOCUMENT:
8382 0 : break;
8383 : }
8384 1 : if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
8385 : {
8386 1 : if (xexpr->xmloption == XMLOPTION_DOCUMENT)
8387 0 : appendStringInfoString(buf, "DOCUMENT ");
8388 : else
8389 1 : appendStringInfoString(buf, "CONTENT ");
8390 : }
8391 1 : if (xexpr->name)
8392 : {
8393 0 : appendStringInfo(buf, "NAME %s",
8394 0 : quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));
8395 0 : needcomma = true;
8396 : }
8397 1 : if (xexpr->named_args)
8398 : {
8399 0 : if (xexpr->op != IS_XMLFOREST)
8400 : {
8401 0 : if (needcomma)
8402 0 : appendStringInfoString(buf, ", ");
8403 0 : appendStringInfoString(buf, "XMLATTRIBUTES(");
8404 0 : needcomma = false;
8405 : }
8406 0 : forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
8407 : {
8408 0 : Node *e = (Node *) lfirst(arg);
8409 0 : char *argname = strVal(lfirst(narg));
8410 :
8411 0 : if (needcomma)
8412 0 : appendStringInfoString(buf, ", ");
8413 0 : get_rule_expr((Node *) e, context, true);
8414 0 : appendStringInfo(buf, " AS %s",
8415 0 : quote_identifier(map_xml_name_to_sql_identifier(argname)));
8416 0 : needcomma = true;
8417 : }
8418 0 : if (xexpr->op != IS_XMLFOREST)
8419 0 : appendStringInfoChar(buf, ')');
8420 : }
8421 1 : if (xexpr->args)
8422 : {
8423 1 : if (needcomma)
8424 0 : appendStringInfoString(buf, ", ");
8425 1 : switch (xexpr->op)
8426 : {
8427 : case IS_XMLCONCAT:
8428 : case IS_XMLELEMENT:
8429 : case IS_XMLFOREST:
8430 : case IS_XMLPI:
8431 : case IS_XMLSERIALIZE:
8432 : /* no extra decoration needed */
8433 0 : get_rule_expr((Node *) xexpr->args, context, true);
8434 0 : break;
8435 : case IS_XMLPARSE:
8436 1 : Assert(list_length(xexpr->args) == 2);
8437 :
8438 1 : get_rule_expr((Node *) linitial(xexpr->args),
8439 : context, true);
8440 :
8441 1 : con = lsecond_node(Const, xexpr->args);
8442 1 : Assert(!con->constisnull);
8443 1 : if (DatumGetBool(con->constvalue))
8444 0 : appendStringInfoString(buf,
8445 : " PRESERVE WHITESPACE");
8446 : else
8447 1 : appendStringInfoString(buf,
8448 : " STRIP WHITESPACE");
8449 1 : break;
8450 : case IS_XMLROOT:
8451 0 : Assert(list_length(xexpr->args) == 3);
8452 :
8453 0 : get_rule_expr((Node *) linitial(xexpr->args),
8454 : context, true);
8455 :
8456 0 : appendStringInfoString(buf, ", VERSION ");
8457 0 : con = (Const *) lsecond(xexpr->args);
8458 0 : if (IsA(con, Const) &&
8459 0 : con->constisnull)
8460 0 : appendStringInfoString(buf, "NO VALUE");
8461 : else
8462 0 : get_rule_expr((Node *) con, context, false);
8463 :
8464 0 : con = lthird_node(Const, xexpr->args);
8465 0 : if (con->constisnull)
8466 : /* suppress STANDALONE NO VALUE */ ;
8467 : else
8468 : {
8469 0 : switch (DatumGetInt32(con->constvalue))
8470 : {
8471 : case XML_STANDALONE_YES:
8472 0 : appendStringInfoString(buf,
8473 : ", STANDALONE YES");
8474 0 : break;
8475 : case XML_STANDALONE_NO:
8476 0 : appendStringInfoString(buf,
8477 : ", STANDALONE NO");
8478 0 : break;
8479 : case XML_STANDALONE_NO_VALUE:
8480 0 : appendStringInfoString(buf,
8481 : ", STANDALONE NO VALUE");
8482 0 : break;
8483 : default:
8484 0 : break;
8485 : }
8486 : }
8487 0 : break;
8488 : case IS_DOCUMENT:
8489 0 : get_rule_expr_paren((Node *) xexpr->args, context, false, node);
8490 0 : break;
8491 : }
8492 :
8493 : }
8494 1 : if (xexpr->op == IS_XMLSERIALIZE)
8495 0 : appendStringInfo(buf, " AS %s",
8496 : format_type_with_typemod(xexpr->type,
8497 : xexpr->typmod));
8498 1 : if (xexpr->op == IS_DOCUMENT)
8499 0 : appendStringInfoString(buf, " IS DOCUMENT");
8500 : else
8501 1 : appendStringInfoChar(buf, ')');
8502 : }
8503 1 : break;
8504 :
8505 : case T_NullTest:
8506 : {
8507 133 : NullTest *ntest = (NullTest *) node;
8508 :
8509 133 : if (!PRETTY_PAREN(context))
8510 133 : appendStringInfoChar(buf, '(');
8511 133 : get_rule_expr_paren((Node *) ntest->arg, context, true, node);
8512 :
8513 : /*
8514 : * For scalar inputs, we prefer to print as IS [NOT] NULL,
8515 : * which is shorter and traditional. If it's a rowtype input
8516 : * but we're applying a scalar test, must print IS [NOT]
8517 : * DISTINCT FROM NULL to be semantically correct.
8518 : */
8519 263 : if (ntest->argisrow ||
8520 130 : !type_is_rowtype(exprType((Node *) ntest->arg)))
8521 : {
8522 131 : switch (ntest->nulltesttype)
8523 : {
8524 : case IS_NULL:
8525 16 : appendStringInfoString(buf, " IS NULL");
8526 16 : break;
8527 : case IS_NOT_NULL:
8528 115 : appendStringInfoString(buf, " IS NOT NULL");
8529 115 : break;
8530 : default:
8531 0 : elog(ERROR, "unrecognized nulltesttype: %d",
8532 : (int) ntest->nulltesttype);
8533 : }
8534 131 : }
8535 : else
8536 : {
8537 2 : switch (ntest->nulltesttype)
8538 : {
8539 : case IS_NULL:
8540 1 : appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
8541 1 : break;
8542 : case IS_NOT_NULL:
8543 1 : appendStringInfoString(buf, " IS DISTINCT FROM NULL");
8544 1 : break;
8545 : default:
8546 0 : elog(ERROR, "unrecognized nulltesttype: %d",
8547 : (int) ntest->nulltesttype);
8548 : }
8549 : }
8550 133 : if (!PRETTY_PAREN(context))
8551 133 : appendStringInfoChar(buf, ')');
8552 : }
8553 133 : break;
8554 :
8555 : case T_BooleanTest:
8556 : {
8557 1 : BooleanTest *btest = (BooleanTest *) node;
8558 :
8559 1 : if (!PRETTY_PAREN(context))
8560 1 : appendStringInfoChar(buf, '(');
8561 1 : get_rule_expr_paren((Node *) btest->arg, context, false, node);
8562 1 : switch (btest->booltesttype)
8563 : {
8564 : case IS_TRUE:
8565 0 : appendStringInfoString(buf, " IS TRUE");
8566 0 : break;
8567 : case IS_NOT_TRUE:
8568 1 : appendStringInfoString(buf, " IS NOT TRUE");
8569 1 : break;
8570 : case IS_FALSE:
8571 0 : appendStringInfoString(buf, " IS FALSE");
8572 0 : break;
8573 : case IS_NOT_FALSE:
8574 0 : appendStringInfoString(buf, " IS NOT FALSE");
8575 0 : break;
8576 : case IS_UNKNOWN:
8577 0 : appendStringInfoString(buf, " IS UNKNOWN");
8578 0 : break;
8579 : case IS_NOT_UNKNOWN:
8580 0 : appendStringInfoString(buf, " IS NOT UNKNOWN");
8581 0 : break;
8582 : default:
8583 0 : elog(ERROR, "unrecognized booltesttype: %d",
8584 : (int) btest->booltesttype);
8585 : }
8586 1 : if (!PRETTY_PAREN(context))
8587 1 : appendStringInfoChar(buf, ')');
8588 : }
8589 1 : break;
8590 :
8591 : case T_CoerceToDomain:
8592 : {
8593 5 : CoerceToDomain *ctest = (CoerceToDomain *) node;
8594 5 : Node *arg = (Node *) ctest->arg;
8595 :
8596 5 : if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
8597 : !showimplicit)
8598 : {
8599 : /* don't show the implicit cast */
8600 5 : get_rule_expr(arg, context, false);
8601 : }
8602 : else
8603 : {
8604 0 : get_coercion_expr(arg, context,
8605 : ctest->resulttype,
8606 : ctest->resulttypmod,
8607 : node);
8608 : }
8609 : }
8610 5 : break;
8611 :
8612 : case T_CoerceToDomainValue:
8613 46 : appendStringInfoString(buf, "VALUE");
8614 46 : break;
8615 :
8616 : case T_SetToDefault:
8617 0 : appendStringInfoString(buf, "DEFAULT");
8618 0 : break;
8619 :
8620 : case T_CurrentOfExpr:
8621 : {
8622 3 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
8623 :
8624 3 : if (cexpr->cursor_name)
8625 3 : appendStringInfo(buf, "CURRENT OF %s",
8626 3 : quote_identifier(cexpr->cursor_name));
8627 : else
8628 0 : appendStringInfo(buf, "CURRENT OF $%d",
8629 : cexpr->cursor_param);
8630 : }
8631 3 : break;
8632 :
8633 : case T_NextValueExpr:
8634 : {
8635 0 : NextValueExpr *nvexpr = (NextValueExpr *) node;
8636 :
8637 : /*
8638 : * This isn't exactly nextval(), but that seems close enough
8639 : * for EXPLAIN's purposes.
8640 : */
8641 0 : appendStringInfoString(buf, "nextval(");
8642 0 : simple_quote_literal(buf,
8643 0 : generate_relation_name(nvexpr->seqid,
8644 : NIL));
8645 0 : appendStringInfoChar(buf, ')');
8646 : }
8647 0 : break;
8648 :
8649 : case T_InferenceElem:
8650 : {
8651 4 : InferenceElem *iexpr = (InferenceElem *) node;
8652 : bool save_varprefix;
8653 : bool need_parens;
8654 :
8655 : /*
8656 : * InferenceElem can only refer to target relation, so a
8657 : * prefix is not useful, and indeed would cause parse errors.
8658 : */
8659 4 : save_varprefix = context->varprefix;
8660 4 : context->varprefix = false;
8661 :
8662 : /*
8663 : * Parenthesize the element unless it's a simple Var or a bare
8664 : * function call. Follows pg_get_indexdef_worker().
8665 : */
8666 4 : need_parens = !IsA(iexpr->expr, Var);
8667 4 : if (IsA(iexpr->expr, FuncExpr) &&
8668 0 : ((FuncExpr *) iexpr->expr)->funcformat ==
8669 : COERCE_EXPLICIT_CALL)
8670 0 : need_parens = false;
8671 :
8672 4 : if (need_parens)
8673 0 : appendStringInfoChar(buf, '(');
8674 4 : get_rule_expr((Node *) iexpr->expr,
8675 : context, false);
8676 4 : if (need_parens)
8677 0 : appendStringInfoChar(buf, ')');
8678 :
8679 4 : context->varprefix = save_varprefix;
8680 :
8681 4 : if (iexpr->infercollid)
8682 2 : appendStringInfo(buf, " COLLATE %s",
8683 : generate_collation_name(iexpr->infercollid));
8684 :
8685 : /* Add the operator class name, if not default */
8686 4 : if (iexpr->inferopclass)
8687 : {
8688 2 : Oid inferopclass = iexpr->inferopclass;
8689 2 : Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
8690 :
8691 2 : get_opclass_name(inferopclass, inferopcinputtype, buf);
8692 : }
8693 : }
8694 4 : break;
8695 :
8696 : case T_PartitionBoundSpec:
8697 : {
8698 37 : PartitionBoundSpec *spec = (PartitionBoundSpec *) node;
8699 : ListCell *cell;
8700 : char *sep;
8701 :
8702 37 : switch (spec->strategy)
8703 : {
8704 : case PARTITION_STRATEGY_LIST:
8705 11 : Assert(spec->listdatums != NIL);
8706 :
8707 11 : appendStringInfoString(buf, "FOR VALUES IN (");
8708 11 : sep = "";
8709 22 : foreach(cell, spec->listdatums)
8710 : {
8711 11 : Const *val = castNode(Const, lfirst(cell));
8712 :
8713 11 : appendStringInfoString(buf, sep);
8714 11 : get_const_expr(val, context, -1);
8715 11 : sep = ", ";
8716 : }
8717 :
8718 11 : appendStringInfoChar(buf, ')');
8719 11 : break;
8720 :
8721 : case PARTITION_STRATEGY_RANGE:
8722 26 : Assert(spec->lowerdatums != NIL &&
8723 : spec->upperdatums != NIL &&
8724 : list_length(spec->lowerdatums) ==
8725 : list_length(spec->upperdatums));
8726 :
8727 26 : appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
8728 : get_range_partbound_string(spec->lowerdatums),
8729 : get_range_partbound_string(spec->upperdatums));
8730 26 : break;
8731 :
8732 : default:
8733 0 : elog(ERROR, "unrecognized partition strategy: %d",
8734 : (int) spec->strategy);
8735 : break;
8736 : }
8737 : }
8738 37 : break;
8739 :
8740 : case T_List:
8741 : {
8742 : char *sep;
8743 : ListCell *l;
8744 :
8745 140 : sep = "";
8746 424 : foreach(l, (List *) node)
8747 : {
8748 284 : appendStringInfoString(buf, sep);
8749 284 : get_rule_expr((Node *) lfirst(l), context, showimplicit);
8750 284 : sep = ", ";
8751 : }
8752 : }
8753 140 : break;
8754 :
8755 : case T_TableFunc:
8756 4 : get_tablefunc((TableFunc *) node, context, showimplicit);
8757 4 : break;
8758 :
8759 : default:
8760 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
8761 : break;
8762 : }
8763 : }
8764 :
8765 : /*
8766 : * get_rule_expr_toplevel - Parse back a toplevel expression
8767 : *
8768 : * Same as get_rule_expr(), except that if the expr is just a Var, we pass
8769 : * istoplevel = true not false to get_variable(). This causes whole-row Vars
8770 : * to get printed with decoration that will prevent expansion of "*".
8771 : * We need to use this in contexts such as ROW() and VALUES(), where the
8772 : * parser would expand "foo.*" appearing at top level. (In principle we'd
8773 : * use this in get_target_list() too, but that has additional worries about
8774 : * whether to print AS, so it needs to invoke get_variable() directly anyway.)
8775 : */
8776 : static void
8777 179 : get_rule_expr_toplevel(Node *node, deparse_context *context,
8778 : bool showimplicit)
8779 : {
8780 179 : if (node && IsA(node, Var))
8781 22 : (void) get_variable((Var *) node, 0, true, context);
8782 : else
8783 157 : get_rule_expr(node, context, showimplicit);
8784 179 : }
8785 :
8786 : /*
8787 : * get_rule_expr_funccall - Parse back a function-call expression
8788 : *
8789 : * Same as get_rule_expr(), except that we guarantee that the output will
8790 : * look like a function call, or like one of the things the grammar treats as
8791 : * equivalent to a function call (see the func_expr_windowless production).
8792 : * This is needed in places where the grammar uses func_expr_windowless and
8793 : * you can't substitute a parenthesized a_expr. If what we have isn't going
8794 : * to look like a function call, wrap it in a dummy CAST() expression, which
8795 : * will satisfy the grammar --- and, indeed, is likely what the user wrote to
8796 : * produce such a thing.
8797 : */
8798 : static void
8799 45 : get_rule_expr_funccall(Node *node, deparse_context *context,
8800 : bool showimplicit)
8801 : {
8802 45 : if (looks_like_function(node))
8803 43 : get_rule_expr(node, context, showimplicit);
8804 : else
8805 : {
8806 2 : StringInfo buf = context->buf;
8807 :
8808 2 : appendStringInfoString(buf, "CAST(");
8809 : /* no point in showing any top-level implicit cast */
8810 2 : get_rule_expr(node, context, false);
8811 2 : appendStringInfo(buf, " AS %s)",
8812 : format_type_with_typemod(exprType(node),
8813 : exprTypmod(node)));
8814 : }
8815 45 : }
8816 :
8817 : /*
8818 : * Helper function to identify node types that satisfy func_expr_windowless.
8819 : * If in doubt, "false" is always a safe answer.
8820 : */
8821 : static bool
8822 66 : looks_like_function(Node *node)
8823 : {
8824 66 : if (node == NULL)
8825 0 : return false; /* probably shouldn't happen */
8826 66 : switch (nodeTag(node))
8827 : {
8828 : case T_FuncExpr:
8829 : /* OK, unless it's going to deparse as a cast */
8830 44 : return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL);
8831 : case T_NullIfExpr:
8832 : case T_CoalesceExpr:
8833 : case T_MinMaxExpr:
8834 : case T_SQLValueFunction:
8835 : case T_XmlExpr:
8836 : /* these are all accepted by func_expr_common_subexpr */
8837 3 : return true;
8838 : default:
8839 19 : break;
8840 : }
8841 19 : return false;
8842 : }
8843 :
8844 :
8845 : /*
8846 : * get_oper_expr - Parse back an OpExpr node
8847 : */
8848 : static void
8849 2743 : get_oper_expr(OpExpr *expr, deparse_context *context)
8850 : {
8851 2743 : StringInfo buf = context->buf;
8852 2743 : Oid opno = expr->opno;
8853 2743 : List *args = expr->args;
8854 :
8855 2743 : if (!PRETTY_PAREN(context))
8856 2588 : appendStringInfoChar(buf, '(');
8857 2743 : if (list_length(args) == 2)
8858 : {
8859 : /* binary operator */
8860 2743 : Node *arg1 = (Node *) linitial(args);
8861 2743 : Node *arg2 = (Node *) lsecond(args);
8862 :
8863 2743 : get_rule_expr_paren(arg1, context, true, (Node *) expr);
8864 2743 : appendStringInfo(buf, " %s ",
8865 : generate_operator_name(opno,
8866 : exprType(arg1),
8867 : exprType(arg2)));
8868 2743 : get_rule_expr_paren(arg2, context, true, (Node *) expr);
8869 : }
8870 : else
8871 : {
8872 : /* unary operator --- but which side? */
8873 0 : Node *arg = (Node *) linitial(args);
8874 : HeapTuple tp;
8875 : Form_pg_operator optup;
8876 :
8877 0 : tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
8878 0 : if (!HeapTupleIsValid(tp))
8879 0 : elog(ERROR, "cache lookup failed for operator %u", opno);
8880 0 : optup = (Form_pg_operator) GETSTRUCT(tp);
8881 0 : switch (optup->oprkind)
8882 : {
8883 : case 'l':
8884 0 : appendStringInfo(buf, "%s ",
8885 : generate_operator_name(opno,
8886 : InvalidOid,
8887 : exprType(arg)));
8888 0 : get_rule_expr_paren(arg, context, true, (Node *) expr);
8889 0 : break;
8890 : case 'r':
8891 0 : get_rule_expr_paren(arg, context, true, (Node *) expr);
8892 0 : appendStringInfo(buf, " %s",
8893 : generate_operator_name(opno,
8894 : exprType(arg),
8895 : InvalidOid));
8896 0 : break;
8897 : default:
8898 0 : elog(ERROR, "bogus oprkind: %d", optup->oprkind);
8899 : }
8900 0 : ReleaseSysCache(tp);
8901 : }
8902 2743 : if (!PRETTY_PAREN(context))
8903 2588 : appendStringInfoChar(buf, ')');
8904 2743 : }
8905 :
8906 : /*
8907 : * get_func_expr - Parse back a FuncExpr node
8908 : */
8909 : static void
8910 836 : get_func_expr(FuncExpr *expr, deparse_context *context,
8911 : bool showimplicit)
8912 : {
8913 836 : StringInfo buf = context->buf;
8914 836 : Oid funcoid = expr->funcid;
8915 : Oid argtypes[FUNC_MAX_ARGS];
8916 : int nargs;
8917 : List *argnames;
8918 : bool use_variadic;
8919 : ListCell *l;
8920 :
8921 : /*
8922 : * If the function call came from an implicit coercion, then just show the
8923 : * first argument --- unless caller wants to see implicit coercions.
8924 : */
8925 836 : if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
8926 : {
8927 110 : get_rule_expr_paren((Node *) linitial(expr->args), context,
8928 : false, (Node *) expr);
8929 110 : return;
8930 : }
8931 :
8932 : /*
8933 : * If the function call came from a cast, then show the first argument
8934 : * plus an explicit cast operation.
8935 : */
8936 1412 : if (expr->funcformat == COERCE_EXPLICIT_CAST ||
8937 686 : expr->funcformat == COERCE_IMPLICIT_CAST)
8938 : {
8939 122 : Node *arg = linitial(expr->args);
8940 122 : Oid rettype = expr->funcresulttype;
8941 : int32 coercedTypmod;
8942 :
8943 : /* Get the typmod if this is a length-coercion function */
8944 122 : (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
8945 :
8946 122 : get_coercion_expr(arg, context,
8947 : rettype, coercedTypmod,
8948 : (Node *) expr);
8949 :
8950 122 : return;
8951 : }
8952 :
8953 : /*
8954 : * Normal function: display as proname(args). First we need to extract
8955 : * the argument datatypes.
8956 : */
8957 604 : if (list_length(expr->args) > FUNC_MAX_ARGS)
8958 0 : ereport(ERROR,
8959 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
8960 : errmsg("too many arguments")));
8961 604 : nargs = 0;
8962 604 : argnames = NIL;
8963 1226 : foreach(l, expr->args)
8964 : {
8965 622 : Node *arg = (Node *) lfirst(l);
8966 :
8967 622 : if (IsA(arg, NamedArgExpr))
8968 3 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
8969 622 : argtypes[nargs] = exprType(arg);
8970 622 : nargs++;
8971 : }
8972 :
8973 1208 : appendStringInfo(buf, "%s(",
8974 : generate_function_name(funcoid, nargs,
8975 : argnames, argtypes,
8976 604 : expr->funcvariadic,
8977 : &use_variadic,
8978 : context->special_exprkind));
8979 604 : nargs = 0;
8980 1226 : foreach(l, expr->args)
8981 : {
8982 622 : if (nargs++ > 0)
8983 73 : appendStringInfoString(buf, ", ");
8984 622 : if (use_variadic && lnext(l) == NULL)
8985 0 : appendStringInfoString(buf, "VARIADIC ");
8986 622 : get_rule_expr((Node *) lfirst(l), context, true);
8987 : }
8988 604 : appendStringInfoChar(buf, ')');
8989 : }
8990 :
8991 : /*
8992 : * get_agg_expr - Parse back an Aggref node
8993 : */
8994 : static void
8995 49 : get_agg_expr(Aggref *aggref, deparse_context *context,
8996 : Aggref *original_aggref)
8997 : {
8998 49 : StringInfo buf = context->buf;
8999 : Oid argtypes[FUNC_MAX_ARGS];
9000 : int nargs;
9001 : bool use_variadic;
9002 :
9003 : /*
9004 : * For a combining aggregate, we look up and deparse the corresponding
9005 : * partial aggregate instead. This is necessary because our input
9006 : * argument list has been replaced; the new argument list always has just
9007 : * one element, which will point to a partial Aggref that supplies us with
9008 : * transition states to combine.
9009 : */
9010 49 : if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
9011 : {
9012 0 : TargetEntry *tle = linitial_node(TargetEntry, aggref->args);
9013 :
9014 0 : Assert(list_length(aggref->args) == 1);
9015 0 : resolve_special_varno((Node *) tle->expr, context, original_aggref,
9016 : get_agg_combine_expr);
9017 0 : return;
9018 : }
9019 :
9020 : /*
9021 : * Mark as PARTIAL, if appropriate. We look to the original aggref so as
9022 : * to avoid printing this when recursing from the code just above.
9023 : */
9024 49 : if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))
9025 0 : appendStringInfoString(buf, "PARTIAL ");
9026 :
9027 : /* Extract the argument types as seen by the parser */
9028 49 : nargs = get_aggregate_argtypes(aggref, argtypes);
9029 :
9030 : /* Print the aggregate name, schema-qualified if needed */
9031 98 : appendStringInfo(buf, "%s(%s",
9032 : generate_function_name(aggref->aggfnoid, nargs,
9033 : NIL, argtypes,
9034 49 : aggref->aggvariadic,
9035 : &use_variadic,
9036 : context->special_exprkind),
9037 49 : (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
9038 :
9039 49 : if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
9040 : {
9041 : /*
9042 : * Ordered-set aggregates do not use "*" syntax. Also, we needn't
9043 : * worry about inserting VARIADIC. So we can just dump the direct
9044 : * args as-is.
9045 : */
9046 3 : Assert(!aggref->aggvariadic);
9047 3 : get_rule_expr((Node *) aggref->aggdirectargs, context, true);
9048 3 : Assert(aggref->aggorder != NIL);
9049 3 : appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
9050 3 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
9051 : }
9052 : else
9053 : {
9054 : /* aggstar can be set only in zero-argument aggregates */
9055 46 : if (aggref->aggstar)
9056 4 : appendStringInfoChar(buf, '*');
9057 : else
9058 : {
9059 : ListCell *l;
9060 : int i;
9061 :
9062 42 : i = 0;
9063 100 : foreach(l, aggref->args)
9064 : {
9065 58 : TargetEntry *tle = (TargetEntry *) lfirst(l);
9066 58 : Node *arg = (Node *) tle->expr;
9067 :
9068 58 : Assert(!IsA(arg, NamedArgExpr));
9069 58 : if (tle->resjunk)
9070 2 : continue;
9071 56 : if (i++ > 0)
9072 14 : appendStringInfoString(buf, ", ");
9073 56 : if (use_variadic && i == nargs)
9074 0 : appendStringInfoString(buf, "VARIADIC ");
9075 56 : get_rule_expr(arg, context, true);
9076 : }
9077 : }
9078 :
9079 46 : if (aggref->aggorder != NIL)
9080 : {
9081 5 : appendStringInfoString(buf, " ORDER BY ");
9082 5 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
9083 : }
9084 : }
9085 :
9086 49 : if (aggref->aggfilter != NULL)
9087 : {
9088 1 : appendStringInfoString(buf, ") FILTER (WHERE ");
9089 1 : get_rule_expr((Node *) aggref->aggfilter, context, false);
9090 : }
9091 :
9092 49 : appendStringInfoChar(buf, ')');
9093 : }
9094 :
9095 : /*
9096 : * This is a helper function for get_agg_expr(). It's used when we deparse
9097 : * a combining Aggref; resolve_special_varno locates the corresponding partial
9098 : * Aggref and then calls this.
9099 : */
9100 : static void
9101 0 : get_agg_combine_expr(Node *node, deparse_context *context, void *private)
9102 : {
9103 : Aggref *aggref;
9104 0 : Aggref *original_aggref = private;
9105 :
9106 0 : if (!IsA(node, Aggref))
9107 0 : elog(ERROR, "combining Aggref does not point to an Aggref");
9108 :
9109 0 : aggref = (Aggref *) node;
9110 0 : get_agg_expr(aggref, context, original_aggref);
9111 0 : }
9112 :
9113 : /*
9114 : * get_windowfunc_expr - Parse back a WindowFunc node
9115 : */
9116 : static void
9117 2 : get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
9118 : {
9119 2 : StringInfo buf = context->buf;
9120 : Oid argtypes[FUNC_MAX_ARGS];
9121 : int nargs;
9122 : List *argnames;
9123 : ListCell *l;
9124 :
9125 2 : if (list_length(wfunc->args) > FUNC_MAX_ARGS)
9126 0 : ereport(ERROR,
9127 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
9128 : errmsg("too many arguments")));
9129 2 : nargs = 0;
9130 2 : argnames = NIL;
9131 4 : foreach(l, wfunc->args)
9132 : {
9133 2 : Node *arg = (Node *) lfirst(l);
9134 :
9135 2 : if (IsA(arg, NamedArgExpr))
9136 0 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
9137 2 : argtypes[nargs] = exprType(arg);
9138 2 : nargs++;
9139 : }
9140 :
9141 2 : appendStringInfo(buf, "%s(",
9142 : generate_function_name(wfunc->winfnoid, nargs,
9143 : argnames, argtypes,
9144 : false, NULL,
9145 : context->special_exprkind));
9146 : /* winstar can be set only in zero-argument aggregates */
9147 2 : if (wfunc->winstar)
9148 0 : appendStringInfoChar(buf, '*');
9149 : else
9150 2 : get_rule_expr((Node *) wfunc->args, context, true);
9151 :
9152 2 : if (wfunc->aggfilter != NULL)
9153 : {
9154 0 : appendStringInfoString(buf, ") FILTER (WHERE ");
9155 0 : get_rule_expr((Node *) wfunc->aggfilter, context, false);
9156 : }
9157 :
9158 2 : appendStringInfoString(buf, ") OVER ");
9159 :
9160 2 : foreach(l, context->windowClause)
9161 : {
9162 1 : WindowClause *wc = (WindowClause *) lfirst(l);
9163 :
9164 1 : if (wc->winref == wfunc->winref)
9165 : {
9166 1 : if (wc->name)
9167 0 : appendStringInfoString(buf, quote_identifier(wc->name));
9168 : else
9169 1 : get_rule_windowspec(wc, context->windowTList, context);
9170 1 : break;
9171 : }
9172 : }
9173 2 : if (l == NULL)
9174 : {
9175 1 : if (context->windowClause)
9176 0 : elog(ERROR, "could not find window clause for winref %u",
9177 : wfunc->winref);
9178 :
9179 : /*
9180 : * In EXPLAIN, we don't have window context information available, so
9181 : * we have to settle for this:
9182 : */
9183 1 : appendStringInfoString(buf, "(?)");
9184 : }
9185 2 : }
9186 :
9187 : /* ----------
9188 : * get_coercion_expr
9189 : *
9190 : * Make a string representation of a value coerced to a specific type
9191 : * ----------
9192 : */
9193 : static void
9194 253 : get_coercion_expr(Node *arg, deparse_context *context,
9195 : Oid resulttype, int32 resulttypmod,
9196 : Node *parentNode)
9197 : {
9198 253 : StringInfo buf = context->buf;
9199 :
9200 : /*
9201 : * Since parse_coerce.c doesn't immediately collapse application of
9202 : * length-coercion functions to constants, what we'll typically see in
9203 : * such cases is a Const with typmod -1 and a length-coercion function
9204 : * right above it. Avoid generating redundant output. However, beware of
9205 : * suppressing casts when the user actually wrote something like
9206 : * 'foo'::text::char(3).
9207 : *
9208 : * Note: it might seem that we are missing the possibility of needing to
9209 : * print a COLLATE clause for such a Const. However, a Const could only
9210 : * have nondefault collation in a post-constant-folding tree, in which the
9211 : * length coercion would have been folded too. See also the special
9212 : * handling of CollateExpr in coerce_to_target_type(): any collation
9213 : * marking will be above the coercion node, not below it.
9214 : */
9215 307 : if (arg && IsA(arg, Const) &&
9216 58 : ((Const *) arg)->consttype == resulttype &&
9217 4 : ((Const *) arg)->consttypmod == -1)
9218 : {
9219 : /* Show the constant without normal ::typename decoration */
9220 4 : get_const_expr((Const *) arg, context, -1);
9221 : }
9222 : else
9223 : {
9224 249 : if (!PRETTY_PAREN(context))
9225 213 : appendStringInfoChar(buf, '(');
9226 249 : get_rule_expr_paren(arg, context, false, parentNode);
9227 249 : if (!PRETTY_PAREN(context))
9228 213 : appendStringInfoChar(buf, ')');
9229 : }
9230 253 : appendStringInfo(buf, "::%s",
9231 : format_type_with_typemod(resulttype, resulttypmod));
9232 253 : }
9233 :
9234 : /* ----------
9235 : * get_const_expr
9236 : *
9237 : * Make a string representation of a Const
9238 : *
9239 : * showtype can be -1 to never show "::typename" decoration, or +1 to always
9240 : * show it, or 0 to show it only if the constant wouldn't be assumed to be
9241 : * the right type by default.
9242 : *
9243 : * If the Const's collation isn't default for its type, show that too.
9244 : * We mustn't do this when showtype is -1 (since that means the caller will
9245 : * print "::typename", and we can't put a COLLATE clause in between). It's
9246 : * caller's responsibility that collation isn't missed in such cases.
9247 : * ----------
9248 : */
9249 : static void
9250 3258 : get_const_expr(Const *constval, deparse_context *context, int showtype)
9251 : {
9252 3258 : StringInfo buf = context->buf;
9253 : Oid typoutput;
9254 : bool typIsVarlena;
9255 : char *extval;
9256 3258 : bool needlabel = false;
9257 :
9258 3258 : if (constval->constisnull)
9259 : {
9260 : /*
9261 : * Always label the type of a NULL constant to prevent misdecisions
9262 : * about type when reparsing.
9263 : */
9264 57 : appendStringInfoString(buf, "NULL");
9265 57 : if (showtype >= 0)
9266 : {
9267 57 : appendStringInfo(buf, "::%s",
9268 : format_type_with_typemod(constval->consttype,
9269 : constval->consttypmod));
9270 57 : get_const_collation(constval, context);
9271 : }
9272 202 : return;
9273 : }
9274 :
9275 3201 : getTypeOutputInfo(constval->consttype,
9276 : &typoutput, &typIsVarlena);
9277 :
9278 3201 : extval = OidOutputFunctionCall(typoutput, constval->constvalue);
9279 :
9280 3201 : switch (constval->consttype)
9281 : {
9282 : case INT4OID:
9283 :
9284 : /*
9285 : * INT4 can be printed without any decoration, unless it is
9286 : * negative; in that case print it as '-nnn'::integer to ensure
9287 : * that the output will re-parse as a constant, not as a constant
9288 : * plus operator. In most cases we could get away with printing
9289 : * (-nnn) instead, because of the way that gram.y handles negative
9290 : * literals; but that doesn't work for INT_MIN, and it doesn't
9291 : * seem that much prettier anyway.
9292 : */
9293 1565 : if (extval[0] != '-')
9294 1551 : appendStringInfoString(buf, extval);
9295 : else
9296 : {
9297 14 : appendStringInfo(buf, "'%s'", extval);
9298 14 : needlabel = true; /* we must attach a cast */
9299 : }
9300 1565 : break;
9301 :
9302 : case NUMERICOID:
9303 :
9304 : /*
9305 : * NUMERIC can be printed without quotes if it looks like a float
9306 : * constant (not an integer, and not Infinity or NaN) and doesn't
9307 : * have a leading sign (for the same reason as for INT4).
9308 : */
9309 80 : if (isdigit((unsigned char) extval[0]) &&
9310 40 : strcspn(extval, "eE.") != strlen(extval))
9311 : {
9312 38 : appendStringInfoString(buf, extval);
9313 : }
9314 : else
9315 : {
9316 2 : appendStringInfo(buf, "'%s'", extval);
9317 2 : needlabel = true; /* we must attach a cast */
9318 : }
9319 40 : break;
9320 :
9321 : case BITOID:
9322 : case VARBITOID:
9323 30 : appendStringInfo(buf, "B'%s'", extval);
9324 30 : break;
9325 :
9326 : case BOOLOID:
9327 16 : if (strcmp(extval, "t") == 0)
9328 3 : appendStringInfoString(buf, "true");
9329 : else
9330 13 : appendStringInfoString(buf, "false");
9331 16 : break;
9332 :
9333 : default:
9334 1550 : simple_quote_literal(buf, extval);
9335 1550 : break;
9336 : }
9337 :
9338 3201 : pfree(extval);
9339 :
9340 3201 : if (showtype < 0)
9341 88 : return;
9342 :
9343 : /*
9344 : * For showtype == 0, append ::typename unless the constant will be
9345 : * implicitly typed as the right type when it is read in.
9346 : *
9347 : * XXX this code has to be kept in sync with the behavior of the parser,
9348 : * especially make_const.
9349 : */
9350 3113 : switch (constval->consttype)
9351 : {
9352 : case BOOLOID:
9353 : case UNKNOWNOID:
9354 : /* These types can be left unlabeled */
9355 16 : needlabel = false;
9356 16 : break;
9357 : case INT4OID:
9358 : /* We determined above whether a label is needed */
9359 1514 : break;
9360 : case NUMERICOID:
9361 :
9362 : /*
9363 : * Float-looking constants will be typed as numeric, which we
9364 : * checked above; but if there's a nondefault typmod we need to
9365 : * show it.
9366 : */
9367 40 : needlabel |= (constval->consttypmod >= 0);
9368 40 : break;
9369 : default:
9370 1543 : needlabel = true;
9371 1543 : break;
9372 : }
9373 3113 : if (needlabel || showtype > 0)
9374 1559 : appendStringInfo(buf, "::%s",
9375 : format_type_with_typemod(constval->consttype,
9376 : constval->consttypmod));
9377 :
9378 3113 : get_const_collation(constval, context);
9379 : }
9380 :
9381 : /*
9382 : * helper for get_const_expr: append COLLATE if needed
9383 : */
9384 : static void
9385 3170 : get_const_collation(Const *constval, deparse_context *context)
9386 : {
9387 3170 : StringInfo buf = context->buf;
9388 :
9389 3170 : if (OidIsValid(constval->constcollid))
9390 : {
9391 468 : Oid typcollation = get_typcollation(constval->consttype);
9392 :
9393 468 : if (constval->constcollid != typcollation)
9394 : {
9395 0 : appendStringInfo(buf, " COLLATE %s",
9396 : generate_collation_name(constval->constcollid));
9397 : }
9398 : }
9399 3170 : }
9400 :
9401 : /*
9402 : * simple_quote_literal - Format a string as a SQL literal, append to buf
9403 : */
9404 : static void
9405 1570 : simple_quote_literal(StringInfo buf, const char *val)
9406 : {
9407 : const char *valptr;
9408 :
9409 : /*
9410 : * We form the string literal according to the prevailing setting of
9411 : * standard_conforming_strings; we never use E''. User is responsible for
9412 : * making sure result is used correctly.
9413 : */
9414 1570 : appendStringInfoChar(buf, '\'');
9415 17003 : for (valptr = val; *valptr; valptr++)
9416 : {
9417 15433 : char ch = *valptr;
9418 :
9419 15433 : if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
9420 20 : appendStringInfoChar(buf, ch);
9421 15433 : appendStringInfoChar(buf, ch);
9422 : }
9423 1570 : appendStringInfoChar(buf, '\'');
9424 1570 : }
9425 :
9426 :
9427 : /* ----------
9428 : * get_sublink_expr - Parse back a sublink
9429 : * ----------
9430 : */
9431 : static void
9432 38 : get_sublink_expr(SubLink *sublink, deparse_context *context)
9433 : {
9434 38 : StringInfo buf = context->buf;
9435 38 : Query *query = (Query *) (sublink->subselect);
9436 38 : char *opname = NULL;
9437 : bool need_paren;
9438 :
9439 38 : if (sublink->subLinkType == ARRAY_SUBLINK)
9440 2 : appendStringInfoString(buf, "ARRAY(");
9441 : else
9442 36 : appendStringInfoChar(buf, '(');
9443 :
9444 : /*
9445 : * Note that we print the name of only the first operator, when there are
9446 : * multiple combining operators. This is an approximation that could go
9447 : * wrong in various scenarios (operators in different schemas, renamed
9448 : * operators, etc) but there is not a whole lot we can do about it, since
9449 : * the syntax allows only one operator to be shown.
9450 : */
9451 38 : if (sublink->testexpr)
9452 : {
9453 2 : if (IsA(sublink->testexpr, OpExpr))
9454 : {
9455 : /* single combining operator */
9456 2 : OpExpr *opexpr = (OpExpr *) sublink->testexpr;
9457 :
9458 2 : get_rule_expr(linitial(opexpr->args), context, true);
9459 4 : opname = generate_operator_name(opexpr->opno,
9460 2 : exprType(linitial(opexpr->args)),
9461 2 : exprType(lsecond(opexpr->args)));
9462 : }
9463 0 : else if (IsA(sublink->testexpr, BoolExpr))
9464 : {
9465 : /* multiple combining operators, = or <> cases */
9466 : char *sep;
9467 : ListCell *l;
9468 :
9469 0 : appendStringInfoChar(buf, '(');
9470 0 : sep = "";
9471 0 : foreach(l, ((BoolExpr *) sublink->testexpr)->args)
9472 : {
9473 0 : OpExpr *opexpr = lfirst_node(OpExpr, l);
9474 :
9475 0 : appendStringInfoString(buf, sep);
9476 0 : get_rule_expr(linitial(opexpr->args), context, true);
9477 0 : if (!opname)
9478 0 : opname = generate_operator_name(opexpr->opno,
9479 0 : exprType(linitial(opexpr->args)),
9480 0 : exprType(lsecond(opexpr->args)));
9481 0 : sep = ", ";
9482 : }
9483 0 : appendStringInfoChar(buf, ')');
9484 : }
9485 0 : else if (IsA(sublink->testexpr, RowCompareExpr))
9486 : {
9487 : /* multiple combining operators, < <= > >= cases */
9488 0 : RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
9489 :
9490 0 : appendStringInfoChar(buf, '(');
9491 0 : get_rule_expr((Node *) rcexpr->largs, context, true);
9492 0 : opname = generate_operator_name(linitial_oid(rcexpr->opnos),
9493 0 : exprType(linitial(rcexpr->largs)),
9494 0 : exprType(linitial(rcexpr->rargs)));
9495 0 : appendStringInfoChar(buf, ')');
9496 : }
9497 : else
9498 0 : elog(ERROR, "unrecognized testexpr type: %d",
9499 : (int) nodeTag(sublink->testexpr));
9500 : }
9501 :
9502 38 : need_paren = true;
9503 :
9504 38 : switch (sublink->subLinkType)
9505 : {
9506 : case EXISTS_SUBLINK:
9507 25 : appendStringInfoString(buf, "EXISTS ");
9508 25 : break;
9509 :
9510 : case ANY_SUBLINK:
9511 2 : if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
9512 2 : appendStringInfoString(buf, " IN ");
9513 : else
9514 0 : appendStringInfo(buf, " %s ANY ", opname);
9515 2 : break;
9516 :
9517 : case ALL_SUBLINK:
9518 0 : appendStringInfo(buf, " %s ALL ", opname);
9519 0 : break;
9520 :
9521 : case ROWCOMPARE_SUBLINK:
9522 0 : appendStringInfo(buf, " %s ", opname);
9523 0 : break;
9524 :
9525 : case EXPR_SUBLINK:
9526 : case MULTIEXPR_SUBLINK:
9527 : case ARRAY_SUBLINK:
9528 11 : need_paren = false;
9529 11 : break;
9530 :
9531 : case CTE_SUBLINK: /* shouldn't occur in a SubLink */
9532 : default:
9533 0 : elog(ERROR, "unrecognized sublink type: %d",
9534 : (int) sublink->subLinkType);
9535 : break;
9536 : }
9537 :
9538 38 : if (need_paren)
9539 27 : appendStringInfoChar(buf, '(');
9540 :
9541 38 : get_query_def(query, buf, context->namespaces, NULL,
9542 : context->prettyFlags, context->wrapColumn,
9543 : context->indentLevel);
9544 :
9545 38 : if (need_paren)
9546 27 : appendStringInfoString(buf, "))");
9547 : else
9548 11 : appendStringInfoChar(buf, ')');
9549 38 : }
9550 :
9551 :
9552 : /* ----------
9553 : * get_tablefunc - Parse back a table function
9554 : * ----------
9555 : */
9556 : static void
9557 5 : get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
9558 : {
9559 5 : StringInfo buf = context->buf;
9560 :
9561 : /* XMLTABLE is the only existing implementation. */
9562 :
9563 5 : appendStringInfoString(buf, "XMLTABLE(");
9564 :
9565 5 : if (tf->ns_uris != NIL)
9566 : {
9567 : ListCell *lc1,
9568 : *lc2;
9569 0 : bool first = true;
9570 :
9571 0 : appendStringInfoString(buf, "XMLNAMESPACES (");
9572 0 : forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
9573 : {
9574 0 : Node *expr = (Node *) lfirst(lc1);
9575 0 : char *name = strVal(lfirst(lc2));
9576 :
9577 0 : if (!first)
9578 0 : appendStringInfoString(buf, ", ");
9579 : else
9580 0 : first = false;
9581 :
9582 0 : if (name != NULL)
9583 : {
9584 0 : get_rule_expr(expr, context, showimplicit);
9585 0 : appendStringInfo(buf, " AS %s", name);
9586 : }
9587 : else
9588 : {
9589 0 : appendStringInfoString(buf, "DEFAULT ");
9590 0 : get_rule_expr(expr, context, showimplicit);
9591 : }
9592 : }
9593 0 : appendStringInfoString(buf, "), ");
9594 : }
9595 :
9596 5 : appendStringInfoChar(buf, '(');
9597 5 : get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
9598 5 : appendStringInfoString(buf, ") PASSING (");
9599 5 : get_rule_expr((Node *) tf->docexpr, context, showimplicit);
9600 5 : appendStringInfoChar(buf, ')');
9601 :
9602 5 : if (tf->colexprs != NIL)
9603 : {
9604 : ListCell *l1;
9605 : ListCell *l2;
9606 : ListCell *l3;
9607 : ListCell *l4;
9608 : ListCell *l5;
9609 5 : int colnum = 0;
9610 :
9611 5 : l2 = list_head(tf->coltypes);
9612 5 : l3 = list_head(tf->coltypmods);
9613 5 : l4 = list_head(tf->colexprs);
9614 5 : l5 = list_head(tf->coldefexprs);
9615 :
9616 5 : appendStringInfoString(buf, " COLUMNS ");
9617 39 : foreach(l1, tf->colnames)
9618 : {
9619 34 : char *colname = strVal(lfirst(l1));
9620 : Oid typid;
9621 : int32 typmod;
9622 : Node *colexpr;
9623 : Node *coldefexpr;
9624 34 : bool ordinality = tf->ordinalitycol == colnum;
9625 34 : bool notnull = bms_is_member(colnum, tf->notnulls);
9626 :
9627 34 : typid = lfirst_oid(l2);
9628 34 : l2 = lnext(l2);
9629 34 : typmod = lfirst_int(l3);
9630 34 : l3 = lnext(l3);
9631 34 : colexpr = (Node *) lfirst(l4);
9632 34 : l4 = lnext(l4);
9633 34 : coldefexpr = (Node *) lfirst(l5);
9634 34 : l5 = lnext(l5);
9635 :
9636 34 : if (colnum > 0)
9637 29 : appendStringInfoString(buf, ", ");
9638 34 : colnum++;
9639 :
9640 34 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
9641 : ordinality ? "FOR ORDINALITY" :
9642 : format_type_with_typemod(typid, typmod));
9643 34 : if (ordinality)
9644 4 : continue;
9645 :
9646 30 : if (coldefexpr != NULL)
9647 : {
9648 4 : appendStringInfoString(buf, " DEFAULT (");
9649 4 : get_rule_expr((Node *) coldefexpr, context, showimplicit);
9650 4 : appendStringInfoChar(buf, ')');
9651 : }
9652 30 : if (colexpr != NULL)
9653 : {
9654 28 : appendStringInfoString(buf, " PATH (");
9655 28 : get_rule_expr((Node *) colexpr, context, showimplicit);
9656 28 : appendStringInfoChar(buf, ')');
9657 : }
9658 30 : if (notnull)
9659 4 : appendStringInfoString(buf, " NOT NULL");
9660 : }
9661 : }
9662 :
9663 5 : appendStringInfoChar(buf, ')');
9664 5 : }
9665 :
9666 : /* ----------
9667 : * get_from_clause - Parse back a FROM clause
9668 : *
9669 : * "prefix" is the keyword that denotes the start of the list of FROM
9670 : * elements. It is FROM when used to parse back SELECT and UPDATE, but
9671 : * is USING when parsing back DELETE.
9672 : * ----------
9673 : */
9674 : static void
9675 297 : get_from_clause(Query *query, const char *prefix, deparse_context *context)
9676 : {
9677 297 : StringInfo buf = context->buf;
9678 297 : bool first = true;
9679 : ListCell *l;
9680 :
9681 : /*
9682 : * We use the query's jointree as a guide to what to print. However, we
9683 : * must ignore auto-added RTEs that are marked not inFromCl. (These can
9684 : * only appear at the top level of the jointree, so it's sufficient to
9685 : * check here.) This check also ensures we ignore the rule pseudo-RTEs
9686 : * for NEW and OLD.
9687 : */
9688 624 : foreach(l, query->jointree->fromlist)
9689 : {
9690 327 : Node *jtnode = (Node *) lfirst(l);
9691 :
9692 327 : if (IsA(jtnode, RangeTblRef))
9693 : {
9694 238 : int varno = ((RangeTblRef *) jtnode)->rtindex;
9695 238 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
9696 :
9697 238 : if (!rte->inFromCl)
9698 25 : continue;
9699 : }
9700 :
9701 302 : if (first)
9702 : {
9703 272 : appendContextKeyword(context, prefix,
9704 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
9705 272 : first = false;
9706 :
9707 272 : get_from_clause_item(jtnode, query, context);
9708 : }
9709 : else
9710 : {
9711 : StringInfoData itembuf;
9712 :
9713 30 : appendStringInfoString(buf, ", ");
9714 :
9715 : /*
9716 : * Put the new FROM item's text into itembuf so we can decide
9717 : * after we've got it whether or not it needs to go on a new line.
9718 : */
9719 30 : initStringInfo(&itembuf);
9720 30 : context->buf = &itembuf;
9721 :
9722 30 : get_from_clause_item(jtnode, query, context);
9723 :
9724 : /* Restore context's output buffer */
9725 30 : context->buf = buf;
9726 :
9727 : /* Consider line-wrapping if enabled */
9728 30 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
9729 : {
9730 : /* Does the new item start with a new line? */
9731 30 : if (itembuf.len > 0 && itembuf.data[0] == '\n')
9732 : {
9733 : /* If so, we shouldn't add anything */
9734 : /* instead, remove any trailing spaces currently in buf */
9735 0 : removeStringInfoSpaces(buf);
9736 : }
9737 : else
9738 : {
9739 : char *trailing_nl;
9740 :
9741 : /* Locate the start of the current line in the buffer */
9742 30 : trailing_nl = strrchr(buf->data, '\n');
9743 30 : if (trailing_nl == NULL)
9744 0 : trailing_nl = buf->data;
9745 : else
9746 30 : trailing_nl++;
9747 :
9748 : /*
9749 : * Add a newline, plus some indentation, if the new item
9750 : * would cause an overflow.
9751 : */
9752 30 : if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
9753 30 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
9754 : PRETTYINDENT_STD,
9755 : PRETTYINDENT_VAR);
9756 : }
9757 : }
9758 :
9759 : /* Add the new item */
9760 30 : appendStringInfoString(buf, itembuf.data);
9761 :
9762 : /* clean up */
9763 30 : pfree(itembuf.data);
9764 : }
9765 : }
9766 297 : }
9767 :
9768 : static void
9769 574 : get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
9770 : {
9771 574 : StringInfo buf = context->buf;
9772 574 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
9773 :
9774 574 : if (IsA(jtnode, RangeTblRef))
9775 : {
9776 438 : int varno = ((RangeTblRef *) jtnode)->rtindex;
9777 438 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
9778 438 : char *refname = get_rtable_name(varno, context);
9779 438 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
9780 438 : RangeTblFunction *rtfunc1 = NULL;
9781 : bool printalias;
9782 :
9783 438 : if (rte->lateral)
9784 2 : appendStringInfoString(buf, "LATERAL ");
9785 :
9786 : /* Print the FROM item proper */
9787 438 : switch (rte->rtekind)
9788 : {
9789 : case RTE_RELATION:
9790 : /* Normal relation RTE */
9791 734 : appendStringInfo(buf, "%s%s",
9792 367 : only_marker(rte),
9793 : generate_relation_name(rte->relid,
9794 : context->namespaces));
9795 367 : break;
9796 : case RTE_SUBQUERY:
9797 : /* Subquery RTE */
9798 21 : appendStringInfoChar(buf, '(');
9799 21 : get_query_def(rte->subquery, buf, context->namespaces, NULL,
9800 : context->prettyFlags, context->wrapColumn,
9801 : context->indentLevel);
9802 21 : appendStringInfoChar(buf, ')');
9803 21 : break;
9804 : case RTE_FUNCTION:
9805 : /* Function RTE */
9806 42 : rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
9807 :
9808 : /*
9809 : * Omit ROWS FROM() syntax for just one function, unless it
9810 : * has both a coldeflist and WITH ORDINALITY. If it has both,
9811 : * we must use ROWS FROM() syntax to avoid ambiguity about
9812 : * whether the coldeflist includes the ordinality column.
9813 : */
9814 79 : if (list_length(rte->functions) == 1 &&
9815 37 : (rtfunc1->funccolnames == NIL || !rte->funcordinality))
9816 : {
9817 37 : get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
9818 : /* we'll print the coldeflist below, if it has one */
9819 : }
9820 : else
9821 : {
9822 : bool all_unnest;
9823 : ListCell *lc;
9824 :
9825 : /*
9826 : * If all the function calls in the list are to unnest,
9827 : * and none need a coldeflist, then collapse the list back
9828 : * down to UNNEST(args). (If we had more than one
9829 : * built-in unnest function, this would get more
9830 : * difficult.)
9831 : *
9832 : * XXX This is pretty ugly, since it makes not-terribly-
9833 : * future-proof assumptions about what the parser would do
9834 : * with the output; but the alternative is to emit our
9835 : * nonstandard ROWS FROM() notation for what might have
9836 : * been a perfectly spec-compliant multi-argument
9837 : * UNNEST().
9838 : */
9839 5 : all_unnest = true;
9840 13 : foreach(lc, rte->functions)
9841 : {
9842 11 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
9843 :
9844 22 : if (!IsA(rtfunc->funcexpr, FuncExpr) ||
9845 19 : ((FuncExpr *) rtfunc->funcexpr)->funcid != F_ARRAY_UNNEST ||
9846 8 : rtfunc->funccolnames != NIL)
9847 : {
9848 3 : all_unnest = false;
9849 3 : break;
9850 : }
9851 : }
9852 :
9853 5 : if (all_unnest)
9854 : {
9855 2 : List *allargs = NIL;
9856 :
9857 8 : foreach(lc, rte->functions)
9858 : {
9859 6 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
9860 6 : List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
9861 :
9862 6 : allargs = list_concat(allargs, list_copy(args));
9863 : }
9864 :
9865 2 : appendStringInfoString(buf, "UNNEST(");
9866 2 : get_rule_expr((Node *) allargs, context, true);
9867 2 : appendStringInfoChar(buf, ')');
9868 : }
9869 : else
9870 : {
9871 3 : int funcno = 0;
9872 :
9873 3 : appendStringInfoString(buf, "ROWS FROM(");
9874 11 : foreach(lc, rte->functions)
9875 : {
9876 8 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
9877 :
9878 8 : if (funcno > 0)
9879 5 : appendStringInfoString(buf, ", ");
9880 8 : get_rule_expr_funccall(rtfunc->funcexpr, context, true);
9881 8 : if (rtfunc->funccolnames != NIL)
9882 : {
9883 : /* Reconstruct the column definition list */
9884 1 : appendStringInfoString(buf, " AS ");
9885 1 : get_from_clause_coldeflist(rtfunc,
9886 : NULL,
9887 : context);
9888 : }
9889 8 : funcno++;
9890 : }
9891 3 : appendStringInfoChar(buf, ')');
9892 : }
9893 : /* prevent printing duplicate coldeflist below */
9894 5 : rtfunc1 = NULL;
9895 : }
9896 42 : if (rte->funcordinality)
9897 3 : appendStringInfoString(buf, " WITH ORDINALITY");
9898 42 : break;
9899 : case RTE_TABLEFUNC:
9900 1 : get_tablefunc(rte->tablefunc, context, true);
9901 1 : break;
9902 : case RTE_VALUES:
9903 : /* Values list RTE */
9904 1 : appendStringInfoChar(buf, '(');
9905 1 : get_values_def(rte->values_lists, context);
9906 1 : appendStringInfoChar(buf, ')');
9907 1 : break;
9908 : case RTE_CTE:
9909 6 : appendStringInfoString(buf, quote_identifier(rte->ctename));
9910 6 : break;
9911 : default:
9912 0 : elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
9913 : break;
9914 : }
9915 :
9916 : /* Print the relation alias, if needed */
9917 438 : printalias = false;
9918 438 : if (rte->alias != NULL)
9919 : {
9920 : /* Always print alias if user provided one */
9921 218 : printalias = true;
9922 : }
9923 220 : else if (colinfo->printaliases)
9924 : {
9925 : /* Always print alias if we need to print column aliases */
9926 21 : printalias = true;
9927 : }
9928 199 : else if (rte->rtekind == RTE_RELATION)
9929 : {
9930 : /*
9931 : * No need to print alias if it's same as relation name (this
9932 : * would normally be the case, but not if set_rtable_names had to
9933 : * resolve a conflict).
9934 : */
9935 193 : if (strcmp(refname, get_relation_name(rte->relid)) != 0)
9936 5 : printalias = true;
9937 : }
9938 6 : else if (rte->rtekind == RTE_FUNCTION)
9939 : {
9940 : /*
9941 : * For a function RTE, always print alias. This covers possible
9942 : * renaming of the function and/or instability of the
9943 : * FigureColname rules for things that aren't simple functions.
9944 : * Note we'd need to force it anyway for the columndef list case.
9945 : */
9946 0 : printalias = true;
9947 : }
9948 6 : else if (rte->rtekind == RTE_VALUES)
9949 : {
9950 : /* Alias is syntactically required for VALUES */
9951 1 : printalias = true;
9952 : }
9953 5 : else if (rte->rtekind == RTE_CTE)
9954 : {
9955 : /*
9956 : * No need to print alias if it's same as CTE name (this would
9957 : * normally be the case, but not if set_rtable_names had to
9958 : * resolve a conflict).
9959 : */
9960 4 : if (strcmp(refname, rte->ctename) != 0)
9961 1 : printalias = true;
9962 : }
9963 438 : if (printalias)
9964 246 : appendStringInfo(buf, " %s", quote_identifier(refname));
9965 :
9966 : /* Print the column definitions or aliases, if needed */
9967 438 : if (rtfunc1 && rtfunc1->funccolnames != NIL)
9968 : {
9969 : /* Reconstruct the columndef list, which is also the aliases */
9970 0 : get_from_clause_coldeflist(rtfunc1, colinfo, context);
9971 : }
9972 : else
9973 : {
9974 : /* Else print column aliases as needed */
9975 438 : get_column_alias_list(colinfo, context);
9976 : }
9977 :
9978 : /* Tablesample clause must go after any alias */
9979 438 : if (rte->rtekind == RTE_RELATION && rte->tablesample)
9980 4 : get_tablesample_def(rte->tablesample, context);
9981 : }
9982 136 : else if (IsA(jtnode, JoinExpr))
9983 : {
9984 136 : JoinExpr *j = (JoinExpr *) jtnode;
9985 136 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
9986 : bool need_paren_on_right;
9987 :
9988 337 : need_paren_on_right = PRETTY_PAREN(context) &&
9989 136 : !IsA(j->rarg, RangeTblRef) &&
9990 0 : !(IsA(j->rarg, JoinExpr) &&((JoinExpr *) j->rarg)->alias != NULL);
9991 :
9992 136 : if (!PRETTY_PAREN(context) || j->alias != NULL)
9993 85 : appendStringInfoChar(buf, '(');
9994 :
9995 136 : get_from_clause_item(j->larg, query, context);
9996 :
9997 136 : switch (j->jointype)
9998 : {
9999 : case JOIN_INNER:
10000 80 : if (j->quals)
10001 75 : appendContextKeyword(context, " JOIN ",
10002 : -PRETTYINDENT_STD,
10003 : PRETTYINDENT_STD,
10004 : PRETTYINDENT_JOIN);
10005 : else
10006 5 : appendContextKeyword(context, " CROSS JOIN ",
10007 : -PRETTYINDENT_STD,
10008 : PRETTYINDENT_STD,
10009 : PRETTYINDENT_JOIN);
10010 80 : break;
10011 : case JOIN_LEFT:
10012 39 : appendContextKeyword(context, " LEFT JOIN ",
10013 : -PRETTYINDENT_STD,
10014 : PRETTYINDENT_STD,
10015 : PRETTYINDENT_JOIN);
10016 39 : break;
10017 : case JOIN_FULL:
10018 17 : appendContextKeyword(context, " FULL JOIN ",
10019 : -PRETTYINDENT_STD,
10020 : PRETTYINDENT_STD,
10021 : PRETTYINDENT_JOIN);
10022 17 : break;
10023 : case JOIN_RIGHT:
10024 0 : appendContextKeyword(context, " RIGHT JOIN ",
10025 : -PRETTYINDENT_STD,
10026 : PRETTYINDENT_STD,
10027 : PRETTYINDENT_JOIN);
10028 0 : break;
10029 : default:
10030 0 : elog(ERROR, "unrecognized join type: %d",
10031 : (int) j->jointype);
10032 : }
10033 :
10034 136 : if (need_paren_on_right)
10035 0 : appendStringInfoChar(buf, '(');
10036 136 : get_from_clause_item(j->rarg, query, context);
10037 136 : if (need_paren_on_right)
10038 0 : appendStringInfoChar(buf, ')');
10039 :
10040 136 : if (j->usingClause)
10041 : {
10042 : ListCell *lc;
10043 59 : bool first = true;
10044 :
10045 59 : appendStringInfoString(buf, " USING (");
10046 : /* Use the assigned names, not what's in usingClause */
10047 143 : foreach(lc, colinfo->usingNames)
10048 : {
10049 84 : char *colname = (char *) lfirst(lc);
10050 :
10051 84 : if (first)
10052 59 : first = false;
10053 : else
10054 25 : appendStringInfoString(buf, ", ");
10055 84 : appendStringInfoString(buf, quote_identifier(colname));
10056 : }
10057 59 : appendStringInfoChar(buf, ')');
10058 : }
10059 77 : else if (j->quals)
10060 : {
10061 71 : appendStringInfoString(buf, " ON ");
10062 71 : if (!PRETTY_PAREN(context))
10063 71 : appendStringInfoChar(buf, '(');
10064 71 : get_rule_expr(j->quals, context, false);
10065 71 : if (!PRETTY_PAREN(context))
10066 71 : appendStringInfoChar(buf, ')');
10067 : }
10068 6 : else if (j->jointype != JOIN_INNER)
10069 : {
10070 : /* If we didn't say CROSS JOIN above, we must provide an ON */
10071 1 : appendStringInfoString(buf, " ON TRUE");
10072 : }
10073 :
10074 136 : if (!PRETTY_PAREN(context) || j->alias != NULL)
10075 85 : appendStringInfoChar(buf, ')');
10076 :
10077 : /* Yes, it's correct to put alias after the right paren ... */
10078 136 : if (j->alias != NULL)
10079 : {
10080 14 : appendStringInfo(buf, " %s",
10081 14 : quote_identifier(j->alias->aliasname));
10082 14 : get_column_alias_list(colinfo, context);
10083 : }
10084 : }
10085 : else
10086 0 : elog(ERROR, "unrecognized node type: %d",
10087 : (int) nodeTag(jtnode));
10088 574 : }
10089 :
10090 : /*
10091 : * get_column_alias_list - print column alias list for an RTE
10092 : *
10093 : * Caller must already have printed the relation's alias name.
10094 : */
10095 : static void
10096 452 : get_column_alias_list(deparse_columns *colinfo, deparse_context *context)
10097 : {
10098 452 : StringInfo buf = context->buf;
10099 : int i;
10100 452 : bool first = true;
10101 :
10102 : /* Don't print aliases if not needed */
10103 452 : if (!colinfo->printaliases)
10104 807 : return;
10105 :
10106 587 : for (i = 0; i < colinfo->num_new_cols; i++)
10107 : {
10108 490 : char *colname = colinfo->new_colnames[i];
10109 :
10110 490 : if (first)
10111 : {
10112 97 : appendStringInfoChar(buf, '(');
10113 97 : first = false;
10114 : }
10115 : else
10116 393 : appendStringInfoString(buf, ", ");
10117 490 : appendStringInfoString(buf, quote_identifier(colname));
10118 : }
10119 97 : if (!first)
10120 97 : appendStringInfoChar(buf, ')');
10121 : }
10122 :
10123 : /*
10124 : * get_from_clause_coldeflist - reproduce FROM clause coldeflist
10125 : *
10126 : * When printing a top-level coldeflist (which is syntactically also the
10127 : * relation's column alias list), use column names from colinfo. But when
10128 : * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
10129 : * original coldeflist's names, which are available in rtfunc->funccolnames.
10130 : * Pass NULL for colinfo to select the latter behavior.
10131 : *
10132 : * The coldeflist is appended immediately (no space) to buf. Caller is
10133 : * responsible for ensuring that an alias or AS is present before it.
10134 : */
10135 : static void
10136 1 : get_from_clause_coldeflist(RangeTblFunction *rtfunc,
10137 : deparse_columns *colinfo,
10138 : deparse_context *context)
10139 : {
10140 1 : StringInfo buf = context->buf;
10141 : ListCell *l1;
10142 : ListCell *l2;
10143 : ListCell *l3;
10144 : ListCell *l4;
10145 : int i;
10146 :
10147 1 : appendStringInfoChar(buf, '(');
10148 :
10149 : /* there's no forfour(), so must chase one list the hard way */
10150 1 : i = 0;
10151 1 : l4 = list_head(rtfunc->funccolnames);
10152 4 : forthree(l1, rtfunc->funccoltypes,
10153 : l2, rtfunc->funccoltypmods,
10154 : l3, rtfunc->funccolcollations)
10155 : {
10156 3 : Oid atttypid = lfirst_oid(l1);
10157 3 : int32 atttypmod = lfirst_int(l2);
10158 3 : Oid attcollation = lfirst_oid(l3);
10159 : char *attname;
10160 :
10161 3 : if (colinfo)
10162 0 : attname = colinfo->colnames[i];
10163 : else
10164 3 : attname = strVal(lfirst(l4));
10165 :
10166 3 : Assert(attname); /* shouldn't be any dropped columns here */
10167 :
10168 3 : if (i > 0)
10169 2 : appendStringInfoString(buf, ", ");
10170 3 : appendStringInfo(buf, "%s %s",
10171 : quote_identifier(attname),
10172 : format_type_with_typemod(atttypid, atttypmod));
10173 4 : if (OidIsValid(attcollation) &&
10174 1 : attcollation != get_typcollation(atttypid))
10175 0 : appendStringInfo(buf, " COLLATE %s",
10176 : generate_collation_name(attcollation));
10177 :
10178 3 : l4 = lnext(l4);
10179 3 : i++;
10180 : }
10181 :
10182 1 : appendStringInfoChar(buf, ')');
10183 1 : }
10184 :
10185 : /*
10186 : * get_tablesample_def - print a TableSampleClause
10187 : */
10188 : static void
10189 4 : get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
10190 : {
10191 4 : StringInfo buf = context->buf;
10192 : Oid argtypes[1];
10193 : int nargs;
10194 : ListCell *l;
10195 :
10196 : /*
10197 : * We should qualify the handler's function name if it wouldn't be
10198 : * resolved by lookup in the current search path.
10199 : */
10200 4 : argtypes[0] = INTERNALOID;
10201 4 : appendStringInfo(buf, " TABLESAMPLE %s (",
10202 : generate_function_name(tablesample->tsmhandler, 1,
10203 : NIL, argtypes,
10204 : false, NULL, EXPR_KIND_NONE));
10205 :
10206 4 : nargs = 0;
10207 8 : foreach(l, tablesample->args)
10208 : {
10209 4 : if (nargs++ > 0)
10210 0 : appendStringInfoString(buf, ", ");
10211 4 : get_rule_expr((Node *) lfirst(l), context, false);
10212 : }
10213 4 : appendStringInfoChar(buf, ')');
10214 :
10215 4 : if (tablesample->repeatable != NULL)
10216 : {
10217 2 : appendStringInfoString(buf, " REPEATABLE (");
10218 2 : get_rule_expr((Node *) tablesample->repeatable, context, false);
10219 2 : appendStringInfoChar(buf, ')');
10220 : }
10221 4 : }
10222 :
10223 : /*
10224 : * get_opclass_name - fetch name of an index operator class
10225 : *
10226 : * The opclass name is appended (after a space) to buf.
10227 : *
10228 : * Output is suppressed if the opclass is the default for the given
10229 : * actual_datatype. (If you don't want this behavior, just pass
10230 : * InvalidOid for actual_datatype.)
10231 : */
10232 : static void
10233 136 : get_opclass_name(Oid opclass, Oid actual_datatype,
10234 : StringInfo buf)
10235 : {
10236 : HeapTuple ht_opc;
10237 : Form_pg_opclass opcrec;
10238 : char *opcname;
10239 : char *nspname;
10240 :
10241 136 : ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
10242 136 : if (!HeapTupleIsValid(ht_opc))
10243 0 : elog(ERROR, "cache lookup failed for opclass %u", opclass);
10244 136 : opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
10245 :
10246 272 : if (!OidIsValid(actual_datatype) ||
10247 136 : GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
10248 : {
10249 : /* Okay, we need the opclass name. Do we need to qualify it? */
10250 3 : opcname = NameStr(opcrec->opcname);
10251 3 : if (OpclassIsVisible(opclass))
10252 3 : appendStringInfo(buf, " %s", quote_identifier(opcname));
10253 : else
10254 : {
10255 0 : nspname = get_namespace_name(opcrec->opcnamespace);
10256 0 : appendStringInfo(buf, " %s.%s",
10257 : quote_identifier(nspname),
10258 : quote_identifier(opcname));
10259 : }
10260 : }
10261 136 : ReleaseSysCache(ht_opc);
10262 136 : }
10263 :
10264 : /*
10265 : * processIndirection - take care of array and subfield assignment
10266 : *
10267 : * We strip any top-level FieldStore or assignment ArrayRef nodes that
10268 : * appear in the input, printing them as decoration for the base column
10269 : * name (which we assume the caller just printed). We might also need to
10270 : * strip CoerceToDomain nodes, but only ones that appear above assignment
10271 : * nodes.
10272 : *
10273 : * Returns the subexpression that's to be assigned.
10274 : */
10275 : static Node *
10276 93 : processIndirection(Node *node, deparse_context *context)
10277 : {
10278 93 : StringInfo buf = context->buf;
10279 93 : CoerceToDomain *cdomain = NULL;
10280 :
10281 : for (;;)
10282 : {
10283 117 : if (node == NULL)
10284 0 : break;
10285 117 : if (IsA(node, FieldStore))
10286 : {
10287 10 : FieldStore *fstore = (FieldStore *) node;
10288 : Oid typrelid;
10289 : char *fieldname;
10290 :
10291 : /* lookup tuple type */
10292 10 : typrelid = get_typ_typrelid(fstore->resulttype);
10293 10 : if (!OidIsValid(typrelid))
10294 0 : elog(ERROR, "argument type %s of FieldStore is not a tuple type",
10295 : format_type_be(fstore->resulttype));
10296 :
10297 : /*
10298 : * Print the field name. There should only be one target field in
10299 : * stored rules. There could be more than that in executable
10300 : * target lists, but this function cannot be used for that case.
10301 : */
10302 10 : Assert(list_length(fstore->fieldnums) == 1);
10303 10 : fieldname = get_relid_attribute_name(typrelid,
10304 10 : linitial_int(fstore->fieldnums));
10305 10 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
10306 :
10307 : /*
10308 : * We ignore arg since it should be an uninteresting reference to
10309 : * the target column or subcolumn.
10310 : */
10311 10 : node = (Node *) linitial(fstore->newvals);
10312 : }
10313 107 : else if (IsA(node, ArrayRef))
10314 : {
10315 12 : ArrayRef *aref = (ArrayRef *) node;
10316 :
10317 12 : if (aref->refassgnexpr == NULL)
10318 0 : break;
10319 12 : printSubscripts(aref, context);
10320 :
10321 : /*
10322 : * We ignore refexpr since it should be an uninteresting reference
10323 : * to the target column or subcolumn.
10324 : */
10325 12 : node = (Node *) aref->refassgnexpr;
10326 : }
10327 95 : else if (IsA(node, CoerceToDomain))
10328 : {
10329 2 : cdomain = (CoerceToDomain *) node;
10330 : /* If it's an explicit domain coercion, we're done */
10331 2 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
10332 0 : break;
10333 : /* Tentatively descend past the CoerceToDomain */
10334 2 : node = (Node *) cdomain->arg;
10335 : }
10336 : else
10337 93 : break;
10338 24 : }
10339 :
10340 : /*
10341 : * If we descended past a CoerceToDomain whose argument turned out not to
10342 : * be a FieldStore or array assignment, back up to the CoerceToDomain.
10343 : * (This is not enough to be fully correct if there are nested implicit
10344 : * CoerceToDomains, but such cases shouldn't ever occur.)
10345 : */
10346 93 : if (cdomain && node == (Node *) cdomain->arg)
10347 0 : node = (Node *) cdomain;
10348 :
10349 93 : return node;
10350 : }
10351 :
10352 : static void
10353 37 : printSubscripts(ArrayRef *aref, deparse_context *context)
10354 : {
10355 37 : StringInfo buf = context->buf;
10356 : ListCell *lowlist_item;
10357 : ListCell *uplist_item;
10358 :
10359 37 : lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */
10360 74 : foreach(uplist_item, aref->refupperindexpr)
10361 : {
10362 37 : appendStringInfoChar(buf, '[');
10363 37 : if (lowlist_item)
10364 : {
10365 : /* If subexpression is NULL, get_rule_expr prints nothing */
10366 0 : get_rule_expr((Node *) lfirst(lowlist_item), context, false);
10367 0 : appendStringInfoChar(buf, ':');
10368 0 : lowlist_item = lnext(lowlist_item);
10369 : }
10370 : /* If subexpression is NULL, get_rule_expr prints nothing */
10371 37 : get_rule_expr((Node *) lfirst(uplist_item), context, false);
10372 37 : appendStringInfoChar(buf, ']');
10373 : }
10374 37 : }
10375 :
10376 : /*
10377 : * quote_identifier - Quote an identifier only if needed
10378 : *
10379 : * When quotes are needed, we palloc the required space; slightly
10380 : * space-wasteful but well worth it for notational simplicity.
10381 : */
10382 : const char *
10383 29536 : quote_identifier(const char *ident)
10384 : {
10385 : /*
10386 : * Can avoid quoting if ident starts with a lowercase letter or underscore
10387 : * and contains only lowercase letters, digits, and underscores, *and* is
10388 : * not any SQL keyword. Otherwise, supply quotes.
10389 : */
10390 29536 : int nquotes = 0;
10391 : bool safe;
10392 : const char *ptr;
10393 : char *result;
10394 : char *optr;
10395 :
10396 : /*
10397 : * would like to use <ctype.h> macros here, but they might yield unwanted
10398 : * locale-specific results...
10399 : */
10400 29536 : safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
10401 :
10402 247016 : for (ptr = ident; *ptr; ptr++)
10403 : {
10404 217480 : char ch = *ptr;
10405 :
10406 217480 : if ((ch >= 'a' && ch <= 'z') ||
10407 35064 : (ch >= '0' && ch <= '9') ||
10408 : (ch == '_'))
10409 : {
10410 : /* okay */
10411 : }
10412 : else
10413 : {
10414 1174 : safe = false;
10415 1174 : if (ch == '"')
10416 0 : nquotes++;
10417 : }
10418 : }
10419 :
10420 29536 : if (quote_all_identifiers)
10421 0 : safe = false;
10422 :
10423 29536 : if (safe)
10424 : {
10425 : /*
10426 : * Check for keyword. We quote keywords except for unreserved ones.
10427 : * (In some cases we could avoid quoting a col_name or type_func_name
10428 : * keyword, but it seems much harder than it's worth to tell that.)
10429 : *
10430 : * Note: ScanKeywordLookup() does case-insensitive comparison, but
10431 : * that's fine, since we already know we have all-lower-case.
10432 : */
10433 29313 : const ScanKeyword *keyword = ScanKeywordLookup(ident,
10434 : ScanKeywords,
10435 : NumScanKeywords);
10436 :
10437 29313 : if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD)
10438 159 : safe = false;
10439 : }
10440 :
10441 29536 : if (safe)
10442 29154 : return ident; /* no change needed */
10443 :
10444 382 : result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
10445 :
10446 382 : optr = result;
10447 382 : *optr++ = '"';
10448 3506 : for (ptr = ident; *ptr; ptr++)
10449 : {
10450 3124 : char ch = *ptr;
10451 :
10452 3124 : if (ch == '"')
10453 0 : *optr++ = '"';
10454 3124 : *optr++ = ch;
10455 : }
10456 382 : *optr++ = '"';
10457 382 : *optr = '\0';
10458 :
10459 382 : return result;
10460 : }
10461 :
10462 : /*
10463 : * quote_qualified_identifier - Quote a possibly-qualified identifier
10464 : *
10465 : * Return a name of the form qualifier.ident, or just ident if qualifier
10466 : * is NULL, quoting each component if necessary. The result is palloc'd.
10467 : */
10468 : char *
10469 12237 : quote_qualified_identifier(const char *qualifier,
10470 : const char *ident)
10471 : {
10472 : StringInfoData buf;
10473 :
10474 12237 : initStringInfo(&buf);
10475 12237 : if (qualifier)
10476 2343 : appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
10477 12237 : appendStringInfoString(&buf, quote_identifier(ident));
10478 12237 : return buf.data;
10479 : }
10480 :
10481 : /*
10482 : * get_relation_name
10483 : * Get the unqualified name of a relation specified by OID
10484 : *
10485 : * This differs from the underlying get_rel_name() function in that it will
10486 : * throw error instead of silently returning NULL if the OID is bad.
10487 : */
10488 : static char *
10489 484 : get_relation_name(Oid relid)
10490 : {
10491 484 : char *relname = get_rel_name(relid);
10492 :
10493 484 : if (!relname)
10494 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
10495 484 : return relname;
10496 : }
10497 :
10498 : /*
10499 : * generate_relation_name
10500 : * Compute the name to display for a relation specified by OID
10501 : *
10502 : * The result includes all necessary quoting and schema-prefixing.
10503 : *
10504 : * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
10505 : * We will forcibly qualify the relation name if it equals any CTE name
10506 : * visible in the namespace list.
10507 : */
10508 : static char *
10509 569 : generate_relation_name(Oid relid, List *namespaces)
10510 : {
10511 : HeapTuple tp;
10512 : Form_pg_class reltup;
10513 : bool need_qual;
10514 : ListCell *nslist;
10515 : char *relname;
10516 : char *nspname;
10517 : char *result;
10518 :
10519 569 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
10520 569 : if (!HeapTupleIsValid(tp))
10521 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
10522 569 : reltup = (Form_pg_class) GETSTRUCT(tp);
10523 569 : relname = NameStr(reltup->relname);
10524 :
10525 : /* Check for conflicting CTE name */
10526 569 : need_qual = false;
10527 1046 : foreach(nslist, namespaces)
10528 : {
10529 477 : deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
10530 : ListCell *ctlist;
10531 :
10532 481 : foreach(ctlist, dpns->ctes)
10533 : {
10534 4 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);
10535 :
10536 4 : if (strcmp(cte->ctename, relname) == 0)
10537 : {
10538 0 : need_qual = true;
10539 0 : break;
10540 : }
10541 : }
10542 477 : if (need_qual)
10543 0 : break;
10544 : }
10545 :
10546 : /* Otherwise, qualify the name if not visible in search path */
10547 569 : if (!need_qual)
10548 569 : need_qual = !RelationIsVisible(relid);
10549 :
10550 569 : if (need_qual)
10551 24 : nspname = get_namespace_name(reltup->relnamespace);
10552 : else
10553 545 : nspname = NULL;
10554 :
10555 569 : result = quote_qualified_identifier(nspname, relname);
10556 :
10557 569 : ReleaseSysCache(tp);
10558 :
10559 569 : return result;
10560 : }
10561 :
10562 : /*
10563 : * generate_qualified_relation_name
10564 : * Compute the name to display for a relation specified by OID
10565 : *
10566 : * As above, but unconditionally schema-qualify the name.
10567 : */
10568 : static char *
10569 21 : generate_qualified_relation_name(Oid relid)
10570 : {
10571 : HeapTuple tp;
10572 : Form_pg_class reltup;
10573 : char *relname;
10574 : char *nspname;
10575 : char *result;
10576 :
10577 21 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
10578 21 : if (!HeapTupleIsValid(tp))
10579 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
10580 21 : reltup = (Form_pg_class) GETSTRUCT(tp);
10581 21 : relname = NameStr(reltup->relname);
10582 :
10583 21 : nspname = get_namespace_name(reltup->relnamespace);
10584 21 : if (!nspname)
10585 0 : elog(ERROR, "cache lookup failed for namespace %u",
10586 : reltup->relnamespace);
10587 :
10588 21 : result = quote_qualified_identifier(nspname, relname);
10589 :
10590 21 : ReleaseSysCache(tp);
10591 :
10592 21 : return result;
10593 : }
10594 :
10595 : /*
10596 : * generate_function_name
10597 : * Compute the name to display for a function specified by OID,
10598 : * given that it is being called with the specified actual arg names and
10599 : * types. (Those matter because of ambiguous-function resolution rules.)
10600 : *
10601 : * If we're dealing with a potentially variadic function (in practice, this
10602 : * means a FuncExpr or Aggref, not some other way of calling a function), then
10603 : * has_variadic must specify whether variadic arguments have been merged,
10604 : * and *use_variadic_p will be set to indicate whether to print VARIADIC in
10605 : * the output. For non-FuncExpr cases, has_variadic should be FALSE and
10606 : * use_variadic_p can be NULL.
10607 : *
10608 : * The result includes all necessary quoting and schema-prefixing.
10609 : */
10610 : static char *
10611 679 : generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
10612 : bool has_variadic, bool *use_variadic_p,
10613 : ParseExprKind special_exprkind)
10614 : {
10615 : char *result;
10616 : HeapTuple proctup;
10617 : Form_pg_proc procform;
10618 : char *proname;
10619 : bool use_variadic;
10620 : char *nspname;
10621 : FuncDetailCode p_result;
10622 : Oid p_funcid;
10623 : Oid p_rettype;
10624 : bool p_retset;
10625 : int p_nvargs;
10626 : Oid p_vatype;
10627 : Oid *p_true_typeids;
10628 679 : bool force_qualify = false;
10629 :
10630 679 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
10631 679 : if (!HeapTupleIsValid(proctup))
10632 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
10633 679 : procform = (Form_pg_proc) GETSTRUCT(proctup);
10634 679 : proname = NameStr(procform->proname);
10635 :
10636 : /*
10637 : * Due to parser hacks to avoid needing to reserve CUBE, we need to force
10638 : * qualification in some special cases.
10639 : */
10640 679 : if (special_exprkind == EXPR_KIND_GROUP_BY)
10641 : {
10642 0 : if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
10643 0 : force_qualify = true;
10644 : }
10645 :
10646 : /*
10647 : * Determine whether VARIADIC should be printed. We must do this first
10648 : * since it affects the lookup rules in func_get_detail().
10649 : *
10650 : * Currently, we always print VARIADIC if the function has a merged
10651 : * variadic-array argument. Note that this is always the case for
10652 : * functions taking a VARIADIC argument type other than VARIADIC ANY.
10653 : *
10654 : * In principle, if VARIADIC wasn't originally specified and the array
10655 : * actual argument is deconstructable, we could print the array elements
10656 : * separately and not print VARIADIC, thus more nearly reproducing the
10657 : * original input. For the moment that seems like too much complication
10658 : * for the benefit, and anyway we do not know whether VARIADIC was
10659 : * originally specified if it's a non-ANY type.
10660 : */
10661 679 : if (use_variadic_p)
10662 : {
10663 : /* Parser should not have set funcvariadic unless fn is variadic */
10664 653 : Assert(!has_variadic || OidIsValid(procform->provariadic));
10665 653 : use_variadic = has_variadic;
10666 653 : *use_variadic_p = use_variadic;
10667 : }
10668 : else
10669 : {
10670 26 : Assert(!has_variadic);
10671 26 : use_variadic = false;
10672 : }
10673 :
10674 : /*
10675 : * The idea here is to schema-qualify only if the parser would fail to
10676 : * resolve the correct function given the unqualified func name with the
10677 : * specified argtypes and VARIADIC flag. But if we already decided to
10678 : * force qualification, then we can skip the lookup and pretend we didn't
10679 : * find it.
10680 : */
10681 679 : if (!force_qualify)
10682 679 : p_result = func_get_detail(list_make1(makeString(proname)),
10683 : NIL, argnames, nargs, argtypes,
10684 : !use_variadic, true,
10685 : &p_funcid, &p_rettype,
10686 : &p_retset, &p_nvargs, &p_vatype,
10687 : &p_true_typeids, NULL);
10688 : else
10689 : {
10690 0 : p_result = FUNCDETAIL_NOTFOUND;
10691 0 : p_funcid = InvalidOid;
10692 : }
10693 :
10694 679 : if ((p_result == FUNCDETAIL_NORMAL ||
10695 0 : p_result == FUNCDETAIL_AGGREGATE ||
10696 679 : p_result == FUNCDETAIL_WINDOWFUNC) &&
10697 679 : p_funcid == funcid)
10698 679 : nspname = NULL;
10699 : else
10700 0 : nspname = get_namespace_name(procform->pronamespace);
10701 :
10702 679 : result = quote_qualified_identifier(nspname, proname);
10703 :
10704 679 : ReleaseSysCache(proctup);
10705 :
10706 679 : return result;
10707 : }
10708 :
10709 : /*
10710 : * generate_operator_name
10711 : * Compute the name to display for an operator specified by OID,
10712 : * given that it is being called with the specified actual arg types.
10713 : * (Arg types matter because of ambiguous-operator resolution rules.
10714 : * Pass InvalidOid for unused arg of a unary operator.)
10715 : *
10716 : * The result includes all necessary quoting and schema-prefixing,
10717 : * plus the OPERATOR() decoration needed to use a qualified operator name
10718 : * in an expression.
10719 : */
10720 : static char *
10721 2796 : generate_operator_name(Oid operid, Oid arg1, Oid arg2)
10722 : {
10723 : StringInfoData buf;
10724 : HeapTuple opertup;
10725 : Form_pg_operator operform;
10726 : char *oprname;
10727 : char *nspname;
10728 : Operator p_result;
10729 :
10730 2796 : initStringInfo(&buf);
10731 :
10732 2796 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
10733 2796 : if (!HeapTupleIsValid(opertup))
10734 0 : elog(ERROR, "cache lookup failed for operator %u", operid);
10735 2796 : operform = (Form_pg_operator) GETSTRUCT(opertup);
10736 2796 : oprname = NameStr(operform->oprname);
10737 :
10738 : /*
10739 : * The idea here is to schema-qualify only if the parser would fail to
10740 : * resolve the correct operator given the unqualified op name with the
10741 : * specified argtypes.
10742 : */
10743 2796 : switch (operform->oprkind)
10744 : {
10745 : case 'b':
10746 2796 : p_result = oper(NULL, list_make1(makeString(oprname)), arg1, arg2,
10747 : true, -1);
10748 2796 : break;
10749 : case 'l':
10750 0 : p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2,
10751 : true, -1);
10752 0 : break;
10753 : case 'r':
10754 0 : p_result = right_oper(NULL, list_make1(makeString(oprname)), arg1,
10755 : true, -1);
10756 0 : break;
10757 : default:
10758 0 : elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
10759 : p_result = NULL; /* keep compiler quiet */
10760 : break;
10761 : }
10762 :
10763 2796 : if (p_result != NULL && oprid(p_result) == operid)
10764 2796 : nspname = NULL;
10765 : else
10766 : {
10767 0 : nspname = get_namespace_name(operform->oprnamespace);
10768 0 : appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
10769 : }
10770 :
10771 2796 : appendStringInfoString(&buf, oprname);
10772 :
10773 2796 : if (nspname)
10774 0 : appendStringInfoChar(&buf, ')');
10775 :
10776 2796 : if (p_result != NULL)
10777 2796 : ReleaseSysCache(p_result);
10778 :
10779 2796 : ReleaseSysCache(opertup);
10780 :
10781 2796 : return buf.data;
10782 : }
10783 :
10784 : /*
10785 : * generate_collation_name
10786 : * Compute the name to display for a collation specified by OID
10787 : *
10788 : * The result includes all necessary quoting and schema-prefixing.
10789 : */
10790 : char *
10791 12 : generate_collation_name(Oid collid)
10792 : {
10793 : HeapTuple tp;
10794 : Form_pg_collation colltup;
10795 : char *collname;
10796 : char *nspname;
10797 : char *result;
10798 :
10799 12 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
10800 12 : if (!HeapTupleIsValid(tp))
10801 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
10802 12 : colltup = (Form_pg_collation) GETSTRUCT(tp);
10803 12 : collname = NameStr(colltup->collname);
10804 :
10805 12 : if (!CollationIsVisible(collid))
10806 0 : nspname = get_namespace_name(colltup->collnamespace);
10807 : else
10808 12 : nspname = NULL;
10809 :
10810 12 : result = quote_qualified_identifier(nspname, collname);
10811 :
10812 12 : ReleaseSysCache(tp);
10813 :
10814 12 : return result;
10815 : }
10816 :
10817 : /*
10818 : * Given a C string, produce a TEXT datum.
10819 : *
10820 : * We assume that the input was palloc'd and may be freed.
10821 : */
10822 : static text *
10823 614 : string_to_text(char *str)
10824 : {
10825 : text *result;
10826 :
10827 614 : result = cstring_to_text(str);
10828 614 : pfree(str);
10829 614 : return result;
10830 : }
10831 :
10832 : /*
10833 : * Generate a C string representing a relation's reloptions, or NULL if none.
10834 : */
10835 : static char *
10836 93 : flatten_reloptions(Oid relid)
10837 : {
10838 93 : char *result = NULL;
10839 : HeapTuple tuple;
10840 : Datum reloptions;
10841 : bool isnull;
10842 :
10843 93 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
10844 93 : if (!HeapTupleIsValid(tuple))
10845 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
10846 :
10847 93 : reloptions = SysCacheGetAttr(RELOID, tuple,
10848 : Anum_pg_class_reloptions, &isnull);
10849 93 : if (!isnull)
10850 : {
10851 : StringInfoData buf;
10852 : Datum *options;
10853 : int noptions;
10854 : int i;
10855 :
10856 0 : initStringInfo(&buf);
10857 :
10858 0 : deconstruct_array(DatumGetArrayTypeP(reloptions),
10859 : TEXTOID, -1, false, 'i',
10860 : &options, NULL, &noptions);
10861 :
10862 0 : for (i = 0; i < noptions; i++)
10863 : {
10864 0 : char *option = TextDatumGetCString(options[i]);
10865 : char *name;
10866 : char *separator;
10867 : char *value;
10868 :
10869 : /*
10870 : * Each array element should have the form name=value. If the "="
10871 : * is missing for some reason, treat it like an empty value.
10872 : */
10873 0 : name = option;
10874 0 : separator = strchr(option, '=');
10875 0 : if (separator)
10876 : {
10877 0 : *separator = '\0';
10878 0 : value = separator + 1;
10879 : }
10880 : else
10881 0 : value = "";
10882 :
10883 0 : if (i > 0)
10884 0 : appendStringInfoString(&buf, ", ");
10885 0 : appendStringInfo(&buf, "%s=", quote_identifier(name));
10886 :
10887 : /*
10888 : * In general we need to quote the value; but to avoid unnecessary
10889 : * clutter, do not quote if it is an identifier that would not
10890 : * need quoting. (We could also allow numbers, but that is a bit
10891 : * trickier than it looks --- for example, are leading zeroes
10892 : * significant? We don't want to assume very much here about what
10893 : * custom reloptions might mean.)
10894 : */
10895 0 : if (quote_identifier(value) == value)
10896 0 : appendStringInfoString(&buf, value);
10897 : else
10898 0 : simple_quote_literal(&buf, value);
10899 :
10900 0 : pfree(option);
10901 : }
10902 :
10903 0 : result = buf.data;
10904 : }
10905 :
10906 93 : ReleaseSysCache(tuple);
10907 :
10908 93 : return result;
10909 : }
10910 :
10911 : /*
10912 : * get_one_range_partition_bound_string
10913 : * A C string representation of one range partition bound
10914 : */
10915 : char *
10916 56 : get_range_partbound_string(List *bound_datums)
10917 : {
10918 : deparse_context context;
10919 56 : StringInfo buf = makeStringInfo();
10920 : ListCell *cell;
10921 : char *sep;
10922 :
10923 56 : memset(&context, 0, sizeof(deparse_context));
10924 56 : context.buf = buf;
10925 :
10926 56 : appendStringInfoString(buf, "(");
10927 56 : sep = "";
10928 160 : foreach(cell, bound_datums)
10929 : {
10930 104 : PartitionRangeDatum *datum =
10931 104 : castNode(PartitionRangeDatum, lfirst(cell));
10932 :
10933 104 : appendStringInfoString(buf, sep);
10934 104 : if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
10935 21 : appendStringInfoString(buf, "MINVALUE");
10936 83 : else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
10937 10 : appendStringInfoString(buf, "MAXVALUE");
10938 : else
10939 : {
10940 73 : Const *val = castNode(Const, datum->value);
10941 :
10942 73 : get_const_expr(val, &context, -1);
10943 : }
10944 104 : sep = ", ";
10945 : }
10946 56 : appendStringInfoChar(buf, ')');
10947 :
10948 56 : return buf->data;
10949 : }
|