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-2025, 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/relation.h"
25 : #include "access/table.h"
26 : #include "catalog/pg_aggregate.h"
27 : #include "catalog/pg_am.h"
28 : #include "catalog/pg_authid.h"
29 : #include "catalog/pg_collation.h"
30 : #include "catalog/pg_constraint.h"
31 : #include "catalog/pg_depend.h"
32 : #include "catalog/pg_language.h"
33 : #include "catalog/pg_opclass.h"
34 : #include "catalog/pg_operator.h"
35 : #include "catalog/pg_partitioned_table.h"
36 : #include "catalog/pg_proc.h"
37 : #include "catalog/pg_statistic_ext.h"
38 : #include "catalog/pg_trigger.h"
39 : #include "catalog/pg_type.h"
40 : #include "commands/defrem.h"
41 : #include "commands/tablespace.h"
42 : #include "common/keywords.h"
43 : #include "executor/spi.h"
44 : #include "funcapi.h"
45 : #include "mb/pg_wchar.h"
46 : #include "miscadmin.h"
47 : #include "nodes/makefuncs.h"
48 : #include "nodes/nodeFuncs.h"
49 : #include "nodes/pathnodes.h"
50 : #include "optimizer/optimizer.h"
51 : #include "parser/parse_agg.h"
52 : #include "parser/parse_func.h"
53 : #include "parser/parse_oper.h"
54 : #include "parser/parse_relation.h"
55 : #include "parser/parser.h"
56 : #include "parser/parsetree.h"
57 : #include "rewrite/rewriteHandler.h"
58 : #include "rewrite/rewriteManip.h"
59 : #include "rewrite/rewriteSupport.h"
60 : #include "utils/array.h"
61 : #include "utils/builtins.h"
62 : #include "utils/fmgroids.h"
63 : #include "utils/guc.h"
64 : #include "utils/hsearch.h"
65 : #include "utils/lsyscache.h"
66 : #include "utils/partcache.h"
67 : #include "utils/rel.h"
68 : #include "utils/ruleutils.h"
69 : #include "utils/snapmgr.h"
70 : #include "utils/syscache.h"
71 : #include "utils/typcache.h"
72 : #include "utils/varlena.h"
73 : #include "utils/xml.h"
74 :
75 : /* ----------
76 : * Pretty formatting constants
77 : * ----------
78 : */
79 :
80 : /* Indent counts */
81 : #define PRETTYINDENT_STD 8
82 : #define PRETTYINDENT_JOIN 4
83 : #define PRETTYINDENT_VAR 4
84 :
85 : #define PRETTYINDENT_LIMIT 40 /* wrap limit */
86 :
87 : /* Pretty flags */
88 : #define PRETTYFLAG_PAREN 0x0001
89 : #define PRETTYFLAG_INDENT 0x0002
90 : #define PRETTYFLAG_SCHEMA 0x0004
91 :
92 : /* Standard conversion of a "bool pretty" option to detailed flags */
93 : #define GET_PRETTY_FLAGS(pretty) \
94 : ((pretty) ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) \
95 : : PRETTYFLAG_INDENT)
96 :
97 : /* Default line length for pretty-print wrapping: 0 means wrap always */
98 : #define WRAP_COLUMN_DEFAULT 0
99 :
100 : /* macros to test if pretty action needed */
101 : #define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
102 : #define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
103 : #define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
104 :
105 :
106 : /* ----------
107 : * Local data types
108 : * ----------
109 : */
110 :
111 : /* Context info needed for invoking a recursive querytree display routine */
112 : typedef struct
113 : {
114 : StringInfo buf; /* output buffer to append to */
115 : List *namespaces; /* List of deparse_namespace nodes */
116 : TupleDesc resultDesc; /* if top level of a view, the view's tupdesc */
117 : List *targetList; /* Current query level's SELECT targetlist */
118 : List *windowClause; /* Current query level's WINDOW clause */
119 : int prettyFlags; /* enabling of pretty-print functions */
120 : int wrapColumn; /* max line length, or -1 for no limit */
121 : int indentLevel; /* current indent level for pretty-print */
122 : bool varprefix; /* true to print prefixes on Vars */
123 : bool colNamesVisible; /* do we care about output column names? */
124 : bool inGroupBy; /* deparsing GROUP BY clause? */
125 : bool varInOrderBy; /* deparsing simple Var in ORDER BY? */
126 : Bitmapset *appendparents; /* if not null, map child Vars of these relids
127 : * back to the parent rel */
128 : } deparse_context;
129 :
130 : /*
131 : * Each level of query context around a subtree needs a level of Var namespace.
132 : * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
133 : * the current context's namespaces list.
134 : *
135 : * rtable is the list of actual RTEs from the Query or PlannedStmt.
136 : * rtable_names holds the alias name to be used for each RTE (either a C
137 : * string, or NULL for nameless RTEs such as unnamed joins).
138 : * rtable_columns holds the column alias names to be used for each RTE.
139 : *
140 : * subplans is a list of Plan trees for SubPlans and CTEs (it's only used
141 : * in the PlannedStmt case).
142 : * ctes is a list of CommonTableExpr nodes (only used in the Query case).
143 : * appendrels, if not null (it's only used in the PlannedStmt case), is an
144 : * array of AppendRelInfo nodes, indexed by child relid. We use that to map
145 : * child-table Vars to their inheritance parents.
146 : *
147 : * In some cases we need to make names of merged JOIN USING columns unique
148 : * across the whole query, not only per-RTE. If so, unique_using is true
149 : * and using_names is a list of C strings representing names already assigned
150 : * to USING columns.
151 : *
152 : * When deparsing plan trees, there is always just a single item in the
153 : * deparse_namespace list (since a plan tree never contains Vars with
154 : * varlevelsup > 0). We store the Plan node that is the immediate
155 : * parent of the expression to be deparsed, as well as a list of that
156 : * Plan's ancestors. In addition, we store its outer and inner subplan nodes,
157 : * as well as their targetlists, and the index tlist if the current plan node
158 : * might contain INDEX_VAR Vars. (These fields could be derived on-the-fly
159 : * from the current Plan node, but it seems notationally clearer to set them
160 : * up as separate fields.)
161 : */
162 : typedef struct
163 : {
164 : List *rtable; /* List of RangeTblEntry nodes */
165 : List *rtable_names; /* Parallel list of names for RTEs */
166 : List *rtable_columns; /* Parallel list of deparse_columns structs */
167 : List *subplans; /* List of Plan trees for SubPlans */
168 : List *ctes; /* List of CommonTableExpr nodes */
169 : AppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */
170 : char *ret_old_alias; /* alias for OLD in RETURNING list */
171 : char *ret_new_alias; /* alias for NEW in RETURNING list */
172 : /* Workspace for column alias assignment: */
173 : bool unique_using; /* Are we making USING names globally unique */
174 : List *using_names; /* List of assigned names for USING columns */
175 : /* Remaining fields are used only when deparsing a Plan tree: */
176 : Plan *plan; /* immediate parent of current expression */
177 : List *ancestors; /* ancestors of plan */
178 : Plan *outer_plan; /* outer subnode, or NULL if none */
179 : Plan *inner_plan; /* inner subnode, or NULL if none */
180 : List *outer_tlist; /* referent for OUTER_VAR Vars */
181 : List *inner_tlist; /* referent for INNER_VAR Vars */
182 : List *index_tlist; /* referent for INDEX_VAR Vars */
183 : /* Special namespace representing a function signature: */
184 : char *funcname;
185 : int numargs;
186 : char **argnames;
187 : } deparse_namespace;
188 :
189 : /*
190 : * Per-relation data about column alias names.
191 : *
192 : * Selecting aliases is unreasonably complicated because of the need to dump
193 : * rules/views whose underlying tables may have had columns added, deleted, or
194 : * renamed since the query was parsed. We must nonetheless print the rule/view
195 : * in a form that can be reloaded and will produce the same results as before.
196 : *
197 : * For each RTE used in the query, we must assign column aliases that are
198 : * unique within that RTE. SQL does not require this of the original query,
199 : * but due to factors such as *-expansion we need to be able to uniquely
200 : * reference every column in a decompiled query. As long as we qualify all
201 : * column references, per-RTE uniqueness is sufficient for that.
202 : *
203 : * However, we can't ensure per-column name uniqueness for unnamed join RTEs,
204 : * since they just inherit column names from their input RTEs, and we can't
205 : * rename the columns at the join level. Most of the time this isn't an issue
206 : * because we don't need to reference the join's output columns as such; we
207 : * can reference the input columns instead. That approach can fail for merged
208 : * JOIN USING columns, however, so when we have one of those in an unnamed
209 : * join, we have to make that column's alias globally unique across the whole
210 : * query to ensure it can be referenced unambiguously.
211 : *
212 : * Another problem is that a JOIN USING clause requires the columns to be
213 : * merged to have the same aliases in both input RTEs, and that no other
214 : * columns in those RTEs or their children conflict with the USING names.
215 : * To handle that, we do USING-column alias assignment in a recursive
216 : * traversal of the query's jointree. When descending through a JOIN with
217 : * USING, we preassign the USING column names to the child columns, overriding
218 : * other rules for column alias assignment. We also mark each RTE with a list
219 : * of all USING column names selected for joins containing that RTE, so that
220 : * when we assign other columns' aliases later, we can avoid conflicts.
221 : *
222 : * Another problem is that if a JOIN's input tables have had columns added or
223 : * deleted since the query was parsed, we must generate a column alias list
224 : * for the join that matches the current set of input columns --- otherwise, a
225 : * change in the number of columns in the left input would throw off matching
226 : * of aliases to columns of the right input. Thus, positions in the printable
227 : * column alias list are not necessarily one-for-one with varattnos of the
228 : * JOIN, so we need a separate new_colnames[] array for printing purposes.
229 : *
230 : * Finally, when dealing with wide tables we risk O(N^2) costs in assigning
231 : * non-duplicate column names. We ameliorate that by using a hash table that
232 : * holds all the strings appearing in colnames, new_colnames, and parentUsing.
233 : */
234 : typedef struct
235 : {
236 : /*
237 : * colnames is an array containing column aliases to use for columns that
238 : * existed when the query was parsed. Dropped columns have NULL entries.
239 : * This array can be directly indexed by varattno to get a Var's name.
240 : *
241 : * Non-NULL entries are guaranteed unique within the RTE, *except* when
242 : * this is for an unnamed JOIN RTE. In that case we merely copy up names
243 : * from the two input RTEs.
244 : *
245 : * During the recursive descent in set_using_names(), forcible assignment
246 : * of a child RTE's column name is represented by pre-setting that element
247 : * of the child's colnames array. So at that stage, NULL entries in this
248 : * array just mean that no name has been preassigned, not necessarily that
249 : * the column is dropped.
250 : */
251 : int num_cols; /* length of colnames[] array */
252 : char **colnames; /* array of C strings and NULLs */
253 :
254 : /*
255 : * new_colnames is an array containing column aliases to use for columns
256 : * that would exist if the query was re-parsed against the current
257 : * definitions of its base tables. This is what to print as the column
258 : * alias list for the RTE. This array does not include dropped columns,
259 : * but it will include columns added since original parsing. Indexes in
260 : * it therefore have little to do with current varattno values. As above,
261 : * entries are unique unless this is for an unnamed JOIN RTE. (In such an
262 : * RTE, we never actually print this array, but we must compute it anyway
263 : * for possible use in computing column names of upper joins.) The
264 : * parallel array is_new_col marks which of these columns are new since
265 : * original parsing. Entries with is_new_col false must match the
266 : * non-NULL colnames entries one-for-one.
267 : */
268 : int num_new_cols; /* length of new_colnames[] array */
269 : char **new_colnames; /* array of C strings */
270 : bool *is_new_col; /* array of bool flags */
271 :
272 : /* This flag tells whether we should actually print a column alias list */
273 : bool printaliases;
274 :
275 : /* This list has all names used as USING names in joins above this RTE */
276 : List *parentUsing; /* names assigned to parent merged columns */
277 :
278 : /*
279 : * If this struct is for a JOIN RTE, we fill these fields during the
280 : * set_using_names() pass to describe its relationship to its child RTEs.
281 : *
282 : * leftattnos and rightattnos are arrays with one entry per existing
283 : * output column of the join (hence, indexable by join varattno). For a
284 : * simple reference to a column of the left child, leftattnos[i] is the
285 : * child RTE's attno and rightattnos[i] is zero; and conversely for a
286 : * column of the right child. But for merged columns produced by JOIN
287 : * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.
288 : * Note that a simple reference might be to a child RTE column that's been
289 : * dropped; but that's OK since the column could not be used in the query.
290 : *
291 : * If it's a JOIN USING, usingNames holds the alias names selected for the
292 : * merged columns (these might be different from the original USING list,
293 : * if we had to modify names to achieve uniqueness).
294 : */
295 : int leftrti; /* rangetable index of left child */
296 : int rightrti; /* rangetable index of right child */
297 : int *leftattnos; /* left-child varattnos of join cols, or 0 */
298 : int *rightattnos; /* right-child varattnos of join cols, or 0 */
299 : List *usingNames; /* names assigned to merged columns */
300 :
301 : /*
302 : * Hash table holding copies of all the strings appearing in this struct's
303 : * colnames, new_colnames, and parentUsing. We use a hash table only for
304 : * sufficiently wide relations, and only during the colname-assignment
305 : * functions set_relation_column_names and set_join_column_names;
306 : * otherwise, names_hash is NULL.
307 : */
308 : HTAB *names_hash; /* entries are just strings */
309 : } deparse_columns;
310 :
311 : /* This macro is analogous to rt_fetch(), but for deparse_columns structs */
312 : #define deparse_columns_fetch(rangetable_index, dpns) \
313 : ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
314 :
315 : /*
316 : * Entry in set_rtable_names' hash table
317 : */
318 : typedef struct
319 : {
320 : char name[NAMEDATALEN]; /* Hash key --- must be first */
321 : int counter; /* Largest addition used so far for name */
322 : } NameHashEntry;
323 :
324 : /* Callback signature for resolve_special_varno() */
325 : typedef void (*rsv_callback) (Node *node, deparse_context *context,
326 : void *callback_arg);
327 :
328 :
329 : /* ----------
330 : * Global data
331 : * ----------
332 : */
333 : static SPIPlanPtr plan_getrulebyoid = NULL;
334 : static const char *const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
335 : static SPIPlanPtr plan_getviewrule = NULL;
336 : static const char *const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
337 :
338 : /* GUC parameters */
339 : bool quote_all_identifiers = false;
340 :
341 :
342 : /* ----------
343 : * Local functions
344 : *
345 : * Most of these functions used to use fixed-size buffers to build their
346 : * results. Now, they take an (already initialized) StringInfo object
347 : * as a parameter, and append their text output to its contents.
348 : * ----------
349 : */
350 : static char *deparse_expression_pretty(Node *expr, List *dpcontext,
351 : bool forceprefix, bool showimplicit,
352 : int prettyFlags, int startIndent);
353 : static char *pg_get_viewdef_worker(Oid viewoid,
354 : int prettyFlags, int wrapColumn);
355 : static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
356 : static int decompile_column_index_array(Datum column_index_array, Oid relId,
357 : bool withPeriod, StringInfo buf);
358 : static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
359 : static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
360 : const Oid *excludeOps,
361 : bool attrsOnly, bool keysOnly,
362 : bool showTblSpc, bool inherits,
363 : int prettyFlags, bool missing_ok);
364 : static char *pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
365 : bool missing_ok);
366 : static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
367 : bool attrsOnly, bool missing_ok);
368 : static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
369 : int prettyFlags, bool missing_ok);
370 : static text *pg_get_expr_worker(text *expr, Oid relid, int prettyFlags);
371 : static int print_function_arguments(StringInfo buf, HeapTuple proctup,
372 : bool print_table_args, bool print_defaults);
373 : static void print_function_rettype(StringInfo buf, HeapTuple proctup);
374 : static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
375 : static void print_function_sqlbody(StringInfo buf, HeapTuple proctup);
376 : static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
377 : Bitmapset *rels_used);
378 : static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
379 : List *parent_namespaces);
380 : static void set_simple_column_names(deparse_namespace *dpns);
381 : static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
382 : static void set_using_names(deparse_namespace *dpns, Node *jtnode,
383 : List *parentUsing);
384 : static void set_relation_column_names(deparse_namespace *dpns,
385 : RangeTblEntry *rte,
386 : deparse_columns *colinfo);
387 : static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
388 : deparse_columns *colinfo);
389 : static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
390 : deparse_columns *colinfo);
391 : static char *make_colname_unique(char *colname, deparse_namespace *dpns,
392 : deparse_columns *colinfo);
393 : static void expand_colnames_array_to(deparse_columns *colinfo, int n);
394 : static void build_colinfo_names_hash(deparse_columns *colinfo);
395 : static void add_to_names_hash(deparse_columns *colinfo, const char *name);
396 : static void destroy_colinfo_names_hash(deparse_columns *colinfo);
397 : static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
398 : deparse_columns *colinfo);
399 : static char *get_rtable_name(int rtindex, deparse_context *context);
400 : static void set_deparse_plan(deparse_namespace *dpns, Plan *plan);
401 : static Plan *find_recursive_union(deparse_namespace *dpns,
402 : WorkTableScan *wtscan);
403 : static void push_child_plan(deparse_namespace *dpns, Plan *plan,
404 : deparse_namespace *save_dpns);
405 : static void pop_child_plan(deparse_namespace *dpns,
406 : deparse_namespace *save_dpns);
407 : static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
408 : deparse_namespace *save_dpns);
409 : static void pop_ancestor_plan(deparse_namespace *dpns,
410 : deparse_namespace *save_dpns);
411 : static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
412 : int prettyFlags);
413 : static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
414 : int prettyFlags, int wrapColumn);
415 : static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
416 : TupleDesc resultDesc, bool colNamesVisible,
417 : int prettyFlags, int wrapColumn, int startIndent);
418 : static void get_values_def(List *values_lists, deparse_context *context);
419 : static void get_with_clause(Query *query, deparse_context *context);
420 : static void get_select_query_def(Query *query, deparse_context *context);
421 : static void get_insert_query_def(Query *query, deparse_context *context);
422 : static void get_update_query_def(Query *query, deparse_context *context);
423 : static void get_update_query_targetlist_def(Query *query, List *targetList,
424 : deparse_context *context,
425 : RangeTblEntry *rte);
426 : static void get_delete_query_def(Query *query, deparse_context *context);
427 : static void get_merge_query_def(Query *query, deparse_context *context);
428 : static void get_utility_query_def(Query *query, deparse_context *context);
429 : static void get_basic_select_query(Query *query, deparse_context *context);
430 : static void get_target_list(List *targetList, deparse_context *context);
431 : static void get_returning_clause(Query *query, deparse_context *context);
432 : static void get_setop_query(Node *setOp, Query *query,
433 : deparse_context *context);
434 : static Node *get_rule_sortgroupclause(Index ref, List *tlist,
435 : bool force_colno,
436 : deparse_context *context);
437 : static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
438 : bool omit_parens, deparse_context *context);
439 : static void get_rule_orderby(List *orderList, List *targetList,
440 : bool force_colno, deparse_context *context);
441 : static void get_rule_windowclause(Query *query, deparse_context *context);
442 : static void get_rule_windowspec(WindowClause *wc, List *targetList,
443 : deparse_context *context);
444 : static void get_window_frame_options(int frameOptions,
445 : Node *startOffset, Node *endOffset,
446 : deparse_context *context);
447 : static char *get_variable(Var *var, int levelsup, bool istoplevel,
448 : deparse_context *context);
449 : static void get_special_variable(Node *node, deparse_context *context,
450 : void *callback_arg);
451 : static void resolve_special_varno(Node *node, deparse_context *context,
452 : rsv_callback callback, void *callback_arg);
453 : static Node *find_param_referent(Param *param, deparse_context *context,
454 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
455 : static SubPlan *find_param_generator(Param *param, deparse_context *context,
456 : int *column_p);
457 : static SubPlan *find_param_generator_initplan(Param *param, Plan *plan,
458 : int *column_p);
459 : static void get_parameter(Param *param, deparse_context *context);
460 : static const char *get_simple_binary_op_name(OpExpr *expr);
461 : static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
462 : static void appendContextKeyword(deparse_context *context, const char *str,
463 : int indentBefore, int indentAfter, int indentPlus);
464 : static void removeStringInfoSpaces(StringInfo str);
465 : static void get_rule_expr(Node *node, deparse_context *context,
466 : bool showimplicit);
467 : static void get_rule_expr_toplevel(Node *node, deparse_context *context,
468 : bool showimplicit);
469 : static void get_rule_list_toplevel(List *lst, deparse_context *context,
470 : bool showimplicit);
471 : static void get_rule_expr_funccall(Node *node, deparse_context *context,
472 : bool showimplicit);
473 : static bool looks_like_function(Node *node);
474 : static void get_oper_expr(OpExpr *expr, deparse_context *context);
475 : static void get_func_expr(FuncExpr *expr, deparse_context *context,
476 : bool showimplicit);
477 : static void get_agg_expr(Aggref *aggref, deparse_context *context,
478 : Aggref *original_aggref);
479 : static void get_agg_expr_helper(Aggref *aggref, deparse_context *context,
480 : Aggref *original_aggref, const char *funcname,
481 : const char *options, bool is_json_objectagg);
482 : static void get_agg_combine_expr(Node *node, deparse_context *context,
483 : void *callback_arg);
484 : static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
485 : static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
486 : const char *funcname, const char *options,
487 : bool is_json_objectagg);
488 : static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
489 : static void get_coercion_expr(Node *arg, deparse_context *context,
490 : Oid resulttype, int32 resulttypmod,
491 : Node *parentNode);
492 : static void get_const_expr(Const *constval, deparse_context *context,
493 : int showtype);
494 : static void get_const_collation(Const *constval, deparse_context *context);
495 : static void get_json_format(JsonFormat *format, StringInfo buf);
496 : static void get_json_returning(JsonReturning *returning, StringInfo buf,
497 : bool json_format_by_default);
498 : static void get_json_constructor(JsonConstructorExpr *ctor,
499 : deparse_context *context, bool showimplicit);
500 : static void get_json_constructor_options(JsonConstructorExpr *ctor,
501 : StringInfo buf);
502 : static void get_json_agg_constructor(JsonConstructorExpr *ctor,
503 : deparse_context *context,
504 : const char *funcname,
505 : bool is_json_objectagg);
506 : static void simple_quote_literal(StringInfo buf, const char *val);
507 : static void get_sublink_expr(SubLink *sublink, deparse_context *context);
508 : static void get_tablefunc(TableFunc *tf, deparse_context *context,
509 : bool showimplicit);
510 : static void get_from_clause(Query *query, const char *prefix,
511 : deparse_context *context);
512 : static void get_from_clause_item(Node *jtnode, Query *query,
513 : deparse_context *context);
514 : static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
515 : deparse_context *context);
516 : static void get_column_alias_list(deparse_columns *colinfo,
517 : deparse_context *context);
518 : static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
519 : deparse_columns *colinfo,
520 : deparse_context *context);
521 : static void get_tablesample_def(TableSampleClause *tablesample,
522 : deparse_context *context);
523 : static void get_opclass_name(Oid opclass, Oid actual_datatype,
524 : StringInfo buf);
525 : static Node *processIndirection(Node *node, deparse_context *context);
526 : static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
527 : static char *get_relation_name(Oid relid);
528 : static char *generate_relation_name(Oid relid, List *namespaces);
529 : static char *generate_qualified_relation_name(Oid relid);
530 : static char *generate_function_name(Oid funcid, int nargs,
531 : List *argnames, Oid *argtypes,
532 : bool has_variadic, bool *use_variadic_p,
533 : bool inGroupBy);
534 : static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
535 : static void add_cast_to(StringInfo buf, Oid typid);
536 : static char *generate_qualified_type_name(Oid typid);
537 : static text *string_to_text(char *str);
538 : static char *flatten_reloptions(Oid relid);
539 : static void get_reloptions(StringInfo buf, Datum reloptions);
540 : static void get_json_path_spec(Node *path_spec, deparse_context *context,
541 : bool showimplicit);
542 : static void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
543 : deparse_context *context,
544 : bool showimplicit);
545 : static void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
546 : deparse_context *context,
547 : bool showimplicit,
548 : bool needcomma);
549 :
550 : #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
551 :
552 :
553 : /* ----------
554 : * pg_get_ruledef - Do it all and return a text
555 : * that could be used as a statement
556 : * to recreate the rule
557 : * ----------
558 : */
559 : Datum
560 684 : pg_get_ruledef(PG_FUNCTION_ARGS)
561 : {
562 684 : Oid ruleoid = PG_GETARG_OID(0);
563 : int prettyFlags;
564 : char *res;
565 :
566 684 : prettyFlags = PRETTYFLAG_INDENT;
567 :
568 684 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
569 :
570 684 : if (res == NULL)
571 6 : PG_RETURN_NULL();
572 :
573 678 : PG_RETURN_TEXT_P(string_to_text(res));
574 : }
575 :
576 :
577 : Datum
578 114 : pg_get_ruledef_ext(PG_FUNCTION_ARGS)
579 : {
580 114 : Oid ruleoid = PG_GETARG_OID(0);
581 114 : bool pretty = PG_GETARG_BOOL(1);
582 : int prettyFlags;
583 : char *res;
584 :
585 114 : prettyFlags = GET_PRETTY_FLAGS(pretty);
586 :
587 114 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
588 :
589 114 : if (res == NULL)
590 0 : PG_RETURN_NULL();
591 :
592 114 : PG_RETURN_TEXT_P(string_to_text(res));
593 : }
594 :
595 :
596 : static char *
597 798 : pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
598 : {
599 : Datum args[1];
600 : char nulls[1];
601 : int spirc;
602 : HeapTuple ruletup;
603 : TupleDesc rulettc;
604 : StringInfoData buf;
605 :
606 : /*
607 : * Do this first so that string is alloc'd in outer context not SPI's.
608 : */
609 798 : initStringInfo(&buf);
610 :
611 : /*
612 : * Connect to SPI manager
613 : */
614 798 : SPI_connect();
615 :
616 : /*
617 : * On the first call prepare the plan to lookup pg_rewrite. We read
618 : * pg_rewrite over the SPI manager instead of using the syscache to be
619 : * checked for read access on pg_rewrite.
620 : */
621 798 : if (plan_getrulebyoid == NULL)
622 : {
623 : Oid argtypes[1];
624 : SPIPlanPtr plan;
625 :
626 46 : argtypes[0] = OIDOID;
627 46 : plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
628 46 : if (plan == NULL)
629 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
630 46 : SPI_keepplan(plan);
631 46 : plan_getrulebyoid = plan;
632 : }
633 :
634 : /*
635 : * Get the pg_rewrite tuple for this rule
636 : */
637 798 : args[0] = ObjectIdGetDatum(ruleoid);
638 798 : nulls[0] = ' ';
639 798 : spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
640 798 : if (spirc != SPI_OK_SELECT)
641 0 : elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
642 798 : if (SPI_processed != 1)
643 : {
644 : /*
645 : * There is no tuple data available here, just keep the output buffer
646 : * empty.
647 : */
648 : }
649 : else
650 : {
651 : /*
652 : * Get the rule's definition and put it into executor's memory
653 : */
654 792 : ruletup = SPI_tuptable->vals[0];
655 792 : rulettc = SPI_tuptable->tupdesc;
656 792 : make_ruledef(&buf, ruletup, rulettc, prettyFlags);
657 : }
658 :
659 : /*
660 : * Disconnect from SPI manager
661 : */
662 798 : if (SPI_finish() != SPI_OK_FINISH)
663 0 : elog(ERROR, "SPI_finish failed");
664 :
665 798 : if (buf.len == 0)
666 6 : return NULL;
667 :
668 792 : return buf.data;
669 : }
670 :
671 :
672 : /* ----------
673 : * pg_get_viewdef - Mainly the same thing, but we
674 : * only return the SELECT part of a view
675 : * ----------
676 : */
677 : Datum
678 2930 : pg_get_viewdef(PG_FUNCTION_ARGS)
679 : {
680 : /* By OID */
681 2930 : Oid viewoid = PG_GETARG_OID(0);
682 : int prettyFlags;
683 : char *res;
684 :
685 2930 : prettyFlags = PRETTYFLAG_INDENT;
686 :
687 2930 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
688 :
689 2930 : if (res == NULL)
690 6 : PG_RETURN_NULL();
691 :
692 2924 : PG_RETURN_TEXT_P(string_to_text(res));
693 : }
694 :
695 :
696 : Datum
697 562 : pg_get_viewdef_ext(PG_FUNCTION_ARGS)
698 : {
699 : /* By OID */
700 562 : Oid viewoid = PG_GETARG_OID(0);
701 562 : bool pretty = PG_GETARG_BOOL(1);
702 : int prettyFlags;
703 : char *res;
704 :
705 562 : prettyFlags = GET_PRETTY_FLAGS(pretty);
706 :
707 562 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
708 :
709 562 : if (res == NULL)
710 0 : PG_RETURN_NULL();
711 :
712 562 : PG_RETURN_TEXT_P(string_to_text(res));
713 : }
714 :
715 : Datum
716 6 : pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
717 : {
718 : /* By OID */
719 6 : Oid viewoid = PG_GETARG_OID(0);
720 6 : int wrap = PG_GETARG_INT32(1);
721 : int prettyFlags;
722 : char *res;
723 :
724 : /* calling this implies we want pretty printing */
725 6 : prettyFlags = GET_PRETTY_FLAGS(true);
726 :
727 6 : res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
728 :
729 6 : if (res == NULL)
730 0 : PG_RETURN_NULL();
731 :
732 6 : PG_RETURN_TEXT_P(string_to_text(res));
733 : }
734 :
735 : Datum
736 72 : pg_get_viewdef_name(PG_FUNCTION_ARGS)
737 : {
738 : /* By qualified name */
739 72 : text *viewname = PG_GETARG_TEXT_PP(0);
740 : int prettyFlags;
741 : RangeVar *viewrel;
742 : Oid viewoid;
743 : char *res;
744 :
745 72 : prettyFlags = PRETTYFLAG_INDENT;
746 :
747 : /* Look up view name. Can't lock it - we might not have privileges. */
748 72 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
749 72 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
750 :
751 72 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
752 :
753 72 : if (res == NULL)
754 0 : PG_RETURN_NULL();
755 :
756 72 : PG_RETURN_TEXT_P(string_to_text(res));
757 : }
758 :
759 :
760 : Datum
761 402 : pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
762 : {
763 : /* By qualified name */
764 402 : text *viewname = PG_GETARG_TEXT_PP(0);
765 402 : bool pretty = PG_GETARG_BOOL(1);
766 : int prettyFlags;
767 : RangeVar *viewrel;
768 : Oid viewoid;
769 : char *res;
770 :
771 402 : prettyFlags = GET_PRETTY_FLAGS(pretty);
772 :
773 : /* Look up view name. Can't lock it - we might not have privileges. */
774 402 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
775 402 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
776 :
777 402 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
778 :
779 402 : if (res == NULL)
780 0 : PG_RETURN_NULL();
781 :
782 402 : PG_RETURN_TEXT_P(string_to_text(res));
783 : }
784 :
785 : /*
786 : * Common code for by-OID and by-name variants of pg_get_viewdef
787 : */
788 : static char *
789 3972 : pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
790 : {
791 : Datum args[2];
792 : char nulls[2];
793 : int spirc;
794 : HeapTuple ruletup;
795 : TupleDesc rulettc;
796 : StringInfoData buf;
797 :
798 : /*
799 : * Do this first so that string is alloc'd in outer context not SPI's.
800 : */
801 3972 : initStringInfo(&buf);
802 :
803 : /*
804 : * Connect to SPI manager
805 : */
806 3972 : SPI_connect();
807 :
808 : /*
809 : * On the first call prepare the plan to lookup pg_rewrite. We read
810 : * pg_rewrite over the SPI manager instead of using the syscache to be
811 : * checked for read access on pg_rewrite.
812 : */
813 3972 : if (plan_getviewrule == NULL)
814 : {
815 : Oid argtypes[2];
816 : SPIPlanPtr plan;
817 :
818 246 : argtypes[0] = OIDOID;
819 246 : argtypes[1] = NAMEOID;
820 246 : plan = SPI_prepare(query_getviewrule, 2, argtypes);
821 246 : if (plan == NULL)
822 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
823 246 : SPI_keepplan(plan);
824 246 : plan_getviewrule = plan;
825 : }
826 :
827 : /*
828 : * Get the pg_rewrite tuple for the view's SELECT rule
829 : */
830 3972 : args[0] = ObjectIdGetDatum(viewoid);
831 3972 : args[1] = DirectFunctionCall1(namein, CStringGetDatum(ViewSelectRuleName));
832 3972 : nulls[0] = ' ';
833 3972 : nulls[1] = ' ';
834 3972 : spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
835 3972 : if (spirc != SPI_OK_SELECT)
836 0 : elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
837 3972 : if (SPI_processed != 1)
838 : {
839 : /*
840 : * There is no tuple data available here, just keep the output buffer
841 : * empty.
842 : */
843 : }
844 : else
845 : {
846 : /*
847 : * Get the rule's definition and put it into executor's memory
848 : */
849 3966 : ruletup = SPI_tuptable->vals[0];
850 3966 : rulettc = SPI_tuptable->tupdesc;
851 3966 : make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
852 : }
853 :
854 : /*
855 : * Disconnect from SPI manager
856 : */
857 3972 : if (SPI_finish() != SPI_OK_FINISH)
858 0 : elog(ERROR, "SPI_finish failed");
859 :
860 3972 : if (buf.len == 0)
861 6 : return NULL;
862 :
863 3966 : return buf.data;
864 : }
865 :
866 : /* ----------
867 : * pg_get_triggerdef - Get the definition of a trigger
868 : * ----------
869 : */
870 : Datum
871 164 : pg_get_triggerdef(PG_FUNCTION_ARGS)
872 : {
873 164 : Oid trigid = PG_GETARG_OID(0);
874 : char *res;
875 :
876 164 : res = pg_get_triggerdef_worker(trigid, false);
877 :
878 164 : if (res == NULL)
879 6 : PG_RETURN_NULL();
880 :
881 158 : PG_RETURN_TEXT_P(string_to_text(res));
882 : }
883 :
884 : Datum
885 1620 : pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
886 : {
887 1620 : Oid trigid = PG_GETARG_OID(0);
888 1620 : bool pretty = PG_GETARG_BOOL(1);
889 : char *res;
890 :
891 1620 : res = pg_get_triggerdef_worker(trigid, pretty);
892 :
893 1620 : if (res == NULL)
894 0 : PG_RETURN_NULL();
895 :
896 1620 : PG_RETURN_TEXT_P(string_to_text(res));
897 : }
898 :
899 : static char *
900 1784 : pg_get_triggerdef_worker(Oid trigid, bool pretty)
901 : {
902 : HeapTuple ht_trig;
903 : Form_pg_trigger trigrec;
904 : StringInfoData buf;
905 : Relation tgrel;
906 : ScanKeyData skey[1];
907 : SysScanDesc tgscan;
908 1784 : int findx = 0;
909 : char *tgname;
910 : char *tgoldtable;
911 : char *tgnewtable;
912 : Datum value;
913 : bool isnull;
914 :
915 : /*
916 : * Fetch the pg_trigger tuple by the Oid of the trigger
917 : */
918 1784 : tgrel = table_open(TriggerRelationId, AccessShareLock);
919 :
920 1784 : ScanKeyInit(&skey[0],
921 : Anum_pg_trigger_oid,
922 : BTEqualStrategyNumber, F_OIDEQ,
923 : ObjectIdGetDatum(trigid));
924 :
925 1784 : tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
926 : NULL, 1, skey);
927 :
928 1784 : ht_trig = systable_getnext(tgscan);
929 :
930 1784 : if (!HeapTupleIsValid(ht_trig))
931 : {
932 6 : systable_endscan(tgscan);
933 6 : table_close(tgrel, AccessShareLock);
934 6 : return NULL;
935 : }
936 :
937 1778 : trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
938 :
939 : /*
940 : * Start the trigger definition. Note that the trigger's name should never
941 : * be schema-qualified, but the trigger rel's name may be.
942 : */
943 1778 : initStringInfo(&buf);
944 :
945 1778 : tgname = NameStr(trigrec->tgname);
946 3556 : appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
947 1778 : OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
948 : quote_identifier(tgname));
949 :
950 1778 : if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
951 700 : appendStringInfoString(&buf, "BEFORE");
952 1078 : else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
953 1054 : appendStringInfoString(&buf, "AFTER");
954 24 : else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
955 24 : appendStringInfoString(&buf, "INSTEAD OF");
956 : else
957 0 : elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
958 :
959 1778 : if (TRIGGER_FOR_INSERT(trigrec->tgtype))
960 : {
961 1222 : appendStringInfoString(&buf, " INSERT");
962 1222 : findx++;
963 : }
964 1778 : if (TRIGGER_FOR_DELETE(trigrec->tgtype))
965 : {
966 288 : if (findx > 0)
967 144 : appendStringInfoString(&buf, " OR DELETE");
968 : else
969 144 : appendStringInfoString(&buf, " DELETE");
970 288 : findx++;
971 : }
972 1778 : if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
973 : {
974 960 : if (findx > 0)
975 548 : appendStringInfoString(&buf, " OR UPDATE");
976 : else
977 412 : appendStringInfoString(&buf, " UPDATE");
978 960 : findx++;
979 : /* tgattr is first var-width field, so OK to access directly */
980 960 : if (trigrec->tgattr.dim1 > 0)
981 : {
982 : int i;
983 :
984 100 : appendStringInfoString(&buf, " OF ");
985 222 : for (i = 0; i < trigrec->tgattr.dim1; i++)
986 : {
987 : char *attname;
988 :
989 122 : if (i > 0)
990 22 : appendStringInfoString(&buf, ", ");
991 122 : attname = get_attname(trigrec->tgrelid,
992 122 : trigrec->tgattr.values[i], false);
993 122 : appendStringInfoString(&buf, quote_identifier(attname));
994 : }
995 : }
996 : }
997 1778 : if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
998 : {
999 0 : if (findx > 0)
1000 0 : appendStringInfoString(&buf, " OR TRUNCATE");
1001 : else
1002 0 : appendStringInfoString(&buf, " TRUNCATE");
1003 0 : findx++;
1004 : }
1005 :
1006 : /*
1007 : * In non-pretty mode, always schema-qualify the target table name for
1008 : * safety. In pretty mode, schema-qualify only if not visible.
1009 : */
1010 3556 : appendStringInfo(&buf, " ON %s ",
1011 : pretty ?
1012 138 : generate_relation_name(trigrec->tgrelid, NIL) :
1013 1640 : generate_qualified_relation_name(trigrec->tgrelid));
1014 :
1015 1778 : if (OidIsValid(trigrec->tgconstraint))
1016 : {
1017 0 : if (OidIsValid(trigrec->tgconstrrelid))
1018 0 : appendStringInfo(&buf, "FROM %s ",
1019 : generate_relation_name(trigrec->tgconstrrelid, NIL));
1020 0 : if (!trigrec->tgdeferrable)
1021 0 : appendStringInfoString(&buf, "NOT ");
1022 0 : appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
1023 0 : if (trigrec->tginitdeferred)
1024 0 : appendStringInfoString(&buf, "DEFERRED ");
1025 : else
1026 0 : appendStringInfoString(&buf, "IMMEDIATE ");
1027 : }
1028 :
1029 1778 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
1030 : tgrel->rd_att, &isnull);
1031 1778 : if (!isnull)
1032 128 : tgoldtable = NameStr(*DatumGetName(value));
1033 : else
1034 1650 : tgoldtable = NULL;
1035 1778 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
1036 : tgrel->rd_att, &isnull);
1037 1778 : if (!isnull)
1038 144 : tgnewtable = NameStr(*DatumGetName(value));
1039 : else
1040 1634 : tgnewtable = NULL;
1041 1778 : if (tgoldtable != NULL || tgnewtable != NULL)
1042 : {
1043 200 : appendStringInfoString(&buf, "REFERENCING ");
1044 200 : if (tgoldtable != NULL)
1045 128 : appendStringInfo(&buf, "OLD TABLE AS %s ",
1046 : quote_identifier(tgoldtable));
1047 200 : if (tgnewtable != NULL)
1048 144 : appendStringInfo(&buf, "NEW TABLE AS %s ",
1049 : quote_identifier(tgnewtable));
1050 : }
1051 :
1052 1778 : if (TRIGGER_FOR_ROW(trigrec->tgtype))
1053 1382 : appendStringInfoString(&buf, "FOR EACH ROW ");
1054 : else
1055 396 : appendStringInfoString(&buf, "FOR EACH STATEMENT ");
1056 :
1057 : /* If the trigger has a WHEN qualification, add that */
1058 1778 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
1059 : tgrel->rd_att, &isnull);
1060 1778 : if (!isnull)
1061 : {
1062 : Node *qual;
1063 : char relkind;
1064 : deparse_context context;
1065 : deparse_namespace dpns;
1066 : RangeTblEntry *oldrte;
1067 : RangeTblEntry *newrte;
1068 :
1069 172 : appendStringInfoString(&buf, "WHEN (");
1070 :
1071 172 : qual = stringToNode(TextDatumGetCString(value));
1072 :
1073 172 : relkind = get_rel_relkind(trigrec->tgrelid);
1074 :
1075 : /* Build minimal OLD and NEW RTEs for the rel */
1076 172 : oldrte = makeNode(RangeTblEntry);
1077 172 : oldrte->rtekind = RTE_RELATION;
1078 172 : oldrte->relid = trigrec->tgrelid;
1079 172 : oldrte->relkind = relkind;
1080 172 : oldrte->rellockmode = AccessShareLock;
1081 172 : oldrte->alias = makeAlias("old", NIL);
1082 172 : oldrte->eref = oldrte->alias;
1083 172 : oldrte->lateral = false;
1084 172 : oldrte->inh = false;
1085 172 : oldrte->inFromCl = true;
1086 :
1087 172 : newrte = makeNode(RangeTblEntry);
1088 172 : newrte->rtekind = RTE_RELATION;
1089 172 : newrte->relid = trigrec->tgrelid;
1090 172 : newrte->relkind = relkind;
1091 172 : newrte->rellockmode = AccessShareLock;
1092 172 : newrte->alias = makeAlias("new", NIL);
1093 172 : newrte->eref = newrte->alias;
1094 172 : newrte->lateral = false;
1095 172 : newrte->inh = false;
1096 172 : newrte->inFromCl = true;
1097 :
1098 : /* Build two-element rtable */
1099 172 : memset(&dpns, 0, sizeof(dpns));
1100 172 : dpns.rtable = list_make2(oldrte, newrte);
1101 172 : dpns.subplans = NIL;
1102 172 : dpns.ctes = NIL;
1103 172 : dpns.appendrels = NULL;
1104 172 : set_rtable_names(&dpns, NIL, NULL);
1105 172 : set_simple_column_names(&dpns);
1106 :
1107 : /* Set up context with one-deep namespace stack */
1108 172 : context.buf = &buf;
1109 172 : context.namespaces = list_make1(&dpns);
1110 172 : context.resultDesc = NULL;
1111 172 : context.targetList = NIL;
1112 172 : context.windowClause = NIL;
1113 172 : context.varprefix = true;
1114 172 : context.prettyFlags = GET_PRETTY_FLAGS(pretty);
1115 172 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
1116 172 : context.indentLevel = PRETTYINDENT_STD;
1117 172 : context.colNamesVisible = true;
1118 172 : context.inGroupBy = false;
1119 172 : context.varInOrderBy = false;
1120 172 : context.appendparents = NULL;
1121 :
1122 172 : get_rule_expr(qual, &context, false);
1123 :
1124 172 : appendStringInfoString(&buf, ") ");
1125 : }
1126 :
1127 1778 : appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
1128 : generate_function_name(trigrec->tgfoid, 0,
1129 : NIL, NULL,
1130 : false, NULL, false));
1131 :
1132 1778 : if (trigrec->tgnargs > 0)
1133 : {
1134 : char *p;
1135 : int i;
1136 :
1137 536 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
1138 : tgrel->rd_att, &isnull);
1139 536 : if (isnull)
1140 0 : elog(ERROR, "tgargs is null for trigger %u", trigid);
1141 536 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
1142 1196 : for (i = 0; i < trigrec->tgnargs; i++)
1143 : {
1144 660 : if (i > 0)
1145 124 : appendStringInfoString(&buf, ", ");
1146 660 : simple_quote_literal(&buf, p);
1147 : /* advance p to next string embedded in tgargs */
1148 6434 : while (*p)
1149 5774 : p++;
1150 660 : p++;
1151 : }
1152 : }
1153 :
1154 : /* We deliberately do not put semi-colon at end */
1155 1778 : appendStringInfoChar(&buf, ')');
1156 :
1157 : /* Clean up */
1158 1778 : systable_endscan(tgscan);
1159 :
1160 1778 : table_close(tgrel, AccessShareLock);
1161 :
1162 1778 : return buf.data;
1163 : }
1164 :
1165 : /* ----------
1166 : * pg_get_indexdef - Get the definition of an index
1167 : *
1168 : * In the extended version, there is a colno argument as well as pretty bool.
1169 : * if colno == 0, we want a complete index definition.
1170 : * if colno > 0, we only want the Nth index key's variable or expression.
1171 : *
1172 : * Note that the SQL-function versions of this omit any info about the
1173 : * index tablespace; this is intentional because pg_dump wants it that way.
1174 : * However pg_get_indexdef_string() includes the index tablespace.
1175 : * ----------
1176 : */
1177 : Datum
1178 7412 : pg_get_indexdef(PG_FUNCTION_ARGS)
1179 : {
1180 7412 : Oid indexrelid = PG_GETARG_OID(0);
1181 : int prettyFlags;
1182 : char *res;
1183 :
1184 7412 : prettyFlags = PRETTYFLAG_INDENT;
1185 :
1186 7412 : res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1187 : false, false,
1188 : false, false,
1189 : prettyFlags, true);
1190 :
1191 7412 : if (res == NULL)
1192 6 : PG_RETURN_NULL();
1193 :
1194 7406 : PG_RETURN_TEXT_P(string_to_text(res));
1195 : }
1196 :
1197 : Datum
1198 2000 : pg_get_indexdef_ext(PG_FUNCTION_ARGS)
1199 : {
1200 2000 : Oid indexrelid = PG_GETARG_OID(0);
1201 2000 : int32 colno = PG_GETARG_INT32(1);
1202 2000 : bool pretty = PG_GETARG_BOOL(2);
1203 : int prettyFlags;
1204 : char *res;
1205 :
1206 2000 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1207 :
1208 2000 : res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1209 : colno != 0, false,
1210 : false, false,
1211 : prettyFlags, true);
1212 :
1213 2000 : if (res == NULL)
1214 0 : PG_RETURN_NULL();
1215 :
1216 2000 : PG_RETURN_TEXT_P(string_to_text(res));
1217 : }
1218 :
1219 : /*
1220 : * Internal version for use by ALTER TABLE.
1221 : * Includes a tablespace clause in the result.
1222 : * Returns a palloc'd C string; no pretty-printing.
1223 : */
1224 : char *
1225 216 : pg_get_indexdef_string(Oid indexrelid)
1226 : {
1227 216 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1228 : false, false,
1229 : true, true,
1230 : 0, false);
1231 : }
1232 :
1233 : /* Internal version that just reports the key-column definitions */
1234 : char *
1235 970 : pg_get_indexdef_columns(Oid indexrelid, bool pretty)
1236 : {
1237 : int prettyFlags;
1238 :
1239 970 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1240 :
1241 970 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1242 : true, true,
1243 : false, false,
1244 : prettyFlags, false);
1245 : }
1246 :
1247 : /* Internal version, extensible with flags to control its behavior */
1248 : char *
1249 8 : pg_get_indexdef_columns_extended(Oid indexrelid, bits16 flags)
1250 : {
1251 8 : bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1252 8 : bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1253 : int prettyFlags;
1254 :
1255 8 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1256 :
1257 8 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1258 : true, keys_only,
1259 : false, false,
1260 : prettyFlags, false);
1261 : }
1262 :
1263 : /*
1264 : * Internal workhorse to decompile an index definition.
1265 : *
1266 : * This is now used for exclusion constraints as well: if excludeOps is not
1267 : * NULL then it points to an array of exclusion operator OIDs.
1268 : */
1269 : static char *
1270 10722 : pg_get_indexdef_worker(Oid indexrelid, int colno,
1271 : const Oid *excludeOps,
1272 : bool attrsOnly, bool keysOnly,
1273 : bool showTblSpc, bool inherits,
1274 : int prettyFlags, bool missing_ok)
1275 : {
1276 : /* might want a separate isConstraint parameter later */
1277 10722 : bool isConstraint = (excludeOps != NULL);
1278 : HeapTuple ht_idx;
1279 : HeapTuple ht_idxrel;
1280 : HeapTuple ht_am;
1281 : Form_pg_index idxrec;
1282 : Form_pg_class idxrelrec;
1283 : Form_pg_am amrec;
1284 : IndexAmRoutine *amroutine;
1285 : List *indexprs;
1286 : ListCell *indexpr_item;
1287 : List *context;
1288 : Oid indrelid;
1289 : int keyno;
1290 : Datum indcollDatum;
1291 : Datum indclassDatum;
1292 : Datum indoptionDatum;
1293 : oidvector *indcollation;
1294 : oidvector *indclass;
1295 : int2vector *indoption;
1296 : StringInfoData buf;
1297 : char *str;
1298 : char *sep;
1299 :
1300 : /*
1301 : * Fetch the pg_index tuple by the Oid of the index
1302 : */
1303 10722 : ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
1304 10722 : if (!HeapTupleIsValid(ht_idx))
1305 : {
1306 6 : if (missing_ok)
1307 6 : return NULL;
1308 0 : elog(ERROR, "cache lookup failed for index %u", indexrelid);
1309 : }
1310 10716 : idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
1311 :
1312 10716 : indrelid = idxrec->indrelid;
1313 : Assert(indexrelid == idxrec->indexrelid);
1314 :
1315 : /* Must get indcollation, indclass, and indoption the hard way */
1316 10716 : indcollDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1317 : Anum_pg_index_indcollation);
1318 10716 : indcollation = (oidvector *) DatumGetPointer(indcollDatum);
1319 :
1320 10716 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1321 : Anum_pg_index_indclass);
1322 10716 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
1323 :
1324 10716 : indoptionDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1325 : Anum_pg_index_indoption);
1326 10716 : indoption = (int2vector *) DatumGetPointer(indoptionDatum);
1327 :
1328 : /*
1329 : * Fetch the pg_class tuple of the index relation
1330 : */
1331 10716 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
1332 10716 : if (!HeapTupleIsValid(ht_idxrel))
1333 0 : elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1334 10716 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
1335 :
1336 : /*
1337 : * Fetch the pg_am tuple of the index' access method
1338 : */
1339 10716 : ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
1340 10716 : if (!HeapTupleIsValid(ht_am))
1341 0 : elog(ERROR, "cache lookup failed for access method %u",
1342 : idxrelrec->relam);
1343 10716 : amrec = (Form_pg_am) GETSTRUCT(ht_am);
1344 :
1345 : /* Fetch the index AM's API struct */
1346 10716 : amroutine = GetIndexAmRoutine(amrec->amhandler);
1347 :
1348 : /*
1349 : * Get the index expressions, if any. (NOTE: we do not use the relcache
1350 : * versions of the expressions and predicate, because we want to display
1351 : * non-const-folded expressions.)
1352 : */
1353 10716 : if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs, NULL))
1354 : {
1355 : Datum exprsDatum;
1356 : char *exprsString;
1357 :
1358 740 : exprsDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1359 : Anum_pg_index_indexprs);
1360 740 : exprsString = TextDatumGetCString(exprsDatum);
1361 740 : indexprs = (List *) stringToNode(exprsString);
1362 740 : pfree(exprsString);
1363 : }
1364 : else
1365 9976 : indexprs = NIL;
1366 :
1367 10716 : indexpr_item = list_head(indexprs);
1368 :
1369 10716 : context = deparse_context_for(get_relation_name(indrelid), indrelid);
1370 :
1371 : /*
1372 : * Start the index definition. Note that the index's name should never be
1373 : * schema-qualified, but the indexed rel's name may be.
1374 : */
1375 10716 : initStringInfo(&buf);
1376 :
1377 10716 : if (!attrsOnly)
1378 : {
1379 9276 : if (!isConstraint)
1380 18320 : appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
1381 9160 : idxrec->indisunique ? "UNIQUE " : "",
1382 9160 : quote_identifier(NameStr(idxrelrec->relname)),
1383 9160 : idxrelrec->relkind == RELKIND_PARTITIONED_INDEX
1384 824 : && !inherits ? "ONLY " : "",
1385 9160 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
1386 1538 : generate_relation_name(indrelid, NIL) :
1387 7622 : generate_qualified_relation_name(indrelid),
1388 9160 : quote_identifier(NameStr(amrec->amname)));
1389 : else /* currently, must be EXCLUDE constraint */
1390 116 : appendStringInfo(&buf, "EXCLUDE USING %s (",
1391 116 : quote_identifier(NameStr(amrec->amname)));
1392 : }
1393 :
1394 : /*
1395 : * Report the indexed attributes
1396 : */
1397 10716 : sep = "";
1398 26956 : for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1399 : {
1400 16338 : AttrNumber attnum = idxrec->indkey.values[keyno];
1401 : Oid keycoltype;
1402 : Oid keycolcollation;
1403 :
1404 : /*
1405 : * Ignore non-key attributes if told to.
1406 : */
1407 16338 : if (keysOnly && keyno >= idxrec->indnkeyatts)
1408 98 : break;
1409 :
1410 : /* Otherwise, print INCLUDE to divide key and non-key attrs. */
1411 16240 : if (!colno && keyno == idxrec->indnkeyatts)
1412 : {
1413 316 : appendStringInfoString(&buf, ") INCLUDE (");
1414 316 : sep = "";
1415 : }
1416 :
1417 16240 : if (!colno)
1418 15598 : appendStringInfoString(&buf, sep);
1419 16240 : sep = ", ";
1420 :
1421 16240 : if (attnum != 0)
1422 : {
1423 : /* Simple index column */
1424 : char *attname;
1425 : int32 keycoltypmod;
1426 :
1427 15330 : attname = get_attname(indrelid, attnum, false);
1428 15330 : if (!colno || colno == keyno + 1)
1429 15162 : appendStringInfoString(&buf, quote_identifier(attname));
1430 15330 : get_atttypetypmodcoll(indrelid, attnum,
1431 : &keycoltype, &keycoltypmod,
1432 : &keycolcollation);
1433 : }
1434 : else
1435 : {
1436 : /* expressional index */
1437 : Node *indexkey;
1438 :
1439 910 : if (indexpr_item == NULL)
1440 0 : elog(ERROR, "too few entries in indexprs list");
1441 910 : indexkey = (Node *) lfirst(indexpr_item);
1442 910 : indexpr_item = lnext(indexprs, indexpr_item);
1443 : /* Deparse */
1444 910 : str = deparse_expression_pretty(indexkey, context, false, false,
1445 : prettyFlags, 0);
1446 910 : if (!colno || colno == keyno + 1)
1447 : {
1448 : /* Need parens if it's not a bare function call */
1449 898 : if (looks_like_function(indexkey))
1450 58 : appendStringInfoString(&buf, str);
1451 : else
1452 840 : appendStringInfo(&buf, "(%s)", str);
1453 : }
1454 910 : keycoltype = exprType(indexkey);
1455 910 : keycolcollation = exprCollation(indexkey);
1456 : }
1457 :
1458 : /* Print additional decoration for (selected) key columns */
1459 16240 : if (!attrsOnly && keyno < idxrec->indnkeyatts &&
1460 0 : (!colno || colno == keyno + 1))
1461 : {
1462 13724 : int16 opt = indoption->values[keyno];
1463 13724 : Oid indcoll = indcollation->values[keyno];
1464 13724 : Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1465 13724 : bool has_options = attoptions != (Datum) 0;
1466 :
1467 : /* Add collation, if not default for column */
1468 13724 : if (OidIsValid(indcoll) && indcoll != keycolcollation)
1469 94 : appendStringInfo(&buf, " COLLATE %s",
1470 : generate_collation_name((indcoll)));
1471 :
1472 : /* Add the operator class name, if not default */
1473 13724 : get_opclass_name(indclass->values[keyno],
1474 : has_options ? InvalidOid : keycoltype, &buf);
1475 :
1476 13724 : if (has_options)
1477 : {
1478 34 : appendStringInfoString(&buf, " (");
1479 34 : get_reloptions(&buf, attoptions);
1480 34 : appendStringInfoChar(&buf, ')');
1481 : }
1482 :
1483 : /* Add options if relevant */
1484 13724 : if (amroutine->amcanorder)
1485 : {
1486 : /* if it supports sort ordering, report DESC and NULLS opts */
1487 10786 : if (opt & INDOPTION_DESC)
1488 : {
1489 0 : appendStringInfoString(&buf, " DESC");
1490 : /* NULLS FIRST is the default in this case */
1491 0 : if (!(opt & INDOPTION_NULLS_FIRST))
1492 0 : appendStringInfoString(&buf, " NULLS LAST");
1493 : }
1494 : else
1495 : {
1496 10786 : if (opt & INDOPTION_NULLS_FIRST)
1497 0 : appendStringInfoString(&buf, " NULLS FIRST");
1498 : }
1499 : }
1500 :
1501 : /* Add the exclusion operator if relevant */
1502 13724 : if (excludeOps != NULL)
1503 148 : appendStringInfo(&buf, " WITH %s",
1504 148 : generate_operator_name(excludeOps[keyno],
1505 : keycoltype,
1506 : keycoltype));
1507 : }
1508 : }
1509 :
1510 10716 : if (!attrsOnly)
1511 : {
1512 9276 : appendStringInfoChar(&buf, ')');
1513 :
1514 9276 : if (idxrec->indnullsnotdistinct)
1515 12 : appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1516 :
1517 : /*
1518 : * If it has options, append "WITH (options)"
1519 : */
1520 9276 : str = flatten_reloptions(indexrelid);
1521 9276 : if (str)
1522 : {
1523 336 : appendStringInfo(&buf, " WITH (%s)", str);
1524 336 : pfree(str);
1525 : }
1526 :
1527 : /*
1528 : * Print tablespace, but only if requested
1529 : */
1530 9276 : if (showTblSpc)
1531 : {
1532 : Oid tblspc;
1533 :
1534 216 : tblspc = get_rel_tablespace(indexrelid);
1535 216 : if (OidIsValid(tblspc))
1536 : {
1537 54 : if (isConstraint)
1538 0 : appendStringInfoString(&buf, " USING INDEX");
1539 54 : appendStringInfo(&buf, " TABLESPACE %s",
1540 54 : quote_identifier(get_tablespace_name(tblspc)));
1541 : }
1542 : }
1543 :
1544 : /*
1545 : * If it's a partial index, decompile and append the predicate
1546 : */
1547 9276 : if (!heap_attisnull(ht_idx, Anum_pg_index_indpred, NULL))
1548 : {
1549 : Node *node;
1550 : Datum predDatum;
1551 : char *predString;
1552 :
1553 : /* Convert text string to node tree */
1554 350 : predDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1555 : Anum_pg_index_indpred);
1556 350 : predString = TextDatumGetCString(predDatum);
1557 350 : node = (Node *) stringToNode(predString);
1558 350 : pfree(predString);
1559 :
1560 : /* Deparse */
1561 350 : str = deparse_expression_pretty(node, context, false, false,
1562 : prettyFlags, 0);
1563 350 : if (isConstraint)
1564 42 : appendStringInfo(&buf, " WHERE (%s)", str);
1565 : else
1566 308 : appendStringInfo(&buf, " WHERE %s", str);
1567 : }
1568 : }
1569 :
1570 : /* Clean up */
1571 10716 : ReleaseSysCache(ht_idx);
1572 10716 : ReleaseSysCache(ht_idxrel);
1573 10716 : ReleaseSysCache(ht_am);
1574 :
1575 10716 : return buf.data;
1576 : }
1577 :
1578 : /* ----------
1579 : * pg_get_querydef
1580 : *
1581 : * Public entry point to deparse one query parsetree.
1582 : * The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
1583 : *
1584 : * The result is a palloc'd C string.
1585 : * ----------
1586 : */
1587 : char *
1588 0 : pg_get_querydef(Query *query, bool pretty)
1589 : {
1590 : StringInfoData buf;
1591 : int prettyFlags;
1592 :
1593 0 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1594 :
1595 0 : initStringInfo(&buf);
1596 :
1597 0 : get_query_def(query, &buf, NIL, NULL, true,
1598 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1599 :
1600 0 : return buf.data;
1601 : }
1602 :
1603 : /*
1604 : * pg_get_statisticsobjdef
1605 : * Get the definition of an extended statistics object
1606 : */
1607 : Datum
1608 290 : pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
1609 : {
1610 290 : Oid statextid = PG_GETARG_OID(0);
1611 : char *res;
1612 :
1613 290 : res = pg_get_statisticsobj_worker(statextid, false, true);
1614 :
1615 290 : if (res == NULL)
1616 6 : PG_RETURN_NULL();
1617 :
1618 284 : PG_RETURN_TEXT_P(string_to_text(res));
1619 : }
1620 :
1621 : /*
1622 : * Internal version for use by ALTER TABLE.
1623 : * Includes a tablespace clause in the result.
1624 : * Returns a palloc'd C string; no pretty-printing.
1625 : */
1626 : char *
1627 14 : pg_get_statisticsobjdef_string(Oid statextid)
1628 : {
1629 14 : return pg_get_statisticsobj_worker(statextid, false, false);
1630 : }
1631 :
1632 : /*
1633 : * pg_get_statisticsobjdef_columns
1634 : * Get columns and expressions for an extended statistics object
1635 : */
1636 : Datum
1637 414 : pg_get_statisticsobjdef_columns(PG_FUNCTION_ARGS)
1638 : {
1639 414 : Oid statextid = PG_GETARG_OID(0);
1640 : char *res;
1641 :
1642 414 : res = pg_get_statisticsobj_worker(statextid, true, true);
1643 :
1644 414 : if (res == NULL)
1645 0 : PG_RETURN_NULL();
1646 :
1647 414 : PG_RETURN_TEXT_P(string_to_text(res));
1648 : }
1649 :
1650 : /*
1651 : * Internal workhorse to decompile an extended statistics object.
1652 : */
1653 : static char *
1654 718 : pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
1655 : {
1656 : Form_pg_statistic_ext statextrec;
1657 : HeapTuple statexttup;
1658 : StringInfoData buf;
1659 : int colno;
1660 : char *nsp;
1661 : ArrayType *arr;
1662 : char *enabled;
1663 : Datum datum;
1664 : bool ndistinct_enabled;
1665 : bool dependencies_enabled;
1666 : bool mcv_enabled;
1667 : int i;
1668 : List *context;
1669 : ListCell *lc;
1670 718 : List *exprs = NIL;
1671 : bool has_exprs;
1672 : int ncolumns;
1673 :
1674 718 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1675 :
1676 718 : if (!HeapTupleIsValid(statexttup))
1677 : {
1678 6 : if (missing_ok)
1679 6 : return NULL;
1680 0 : elog(ERROR, "cache lookup failed for statistics object %u", statextid);
1681 : }
1682 :
1683 : /* has the statistics expressions? */
1684 712 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
1685 :
1686 712 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1687 :
1688 : /*
1689 : * Get the statistics expressions, if any. (NOTE: we do not use the
1690 : * relcache versions of the expressions, because we want to display
1691 : * non-const-folded expressions.)
1692 : */
1693 712 : if (has_exprs)
1694 : {
1695 : Datum exprsDatum;
1696 : char *exprsString;
1697 :
1698 154 : exprsDatum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1699 : Anum_pg_statistic_ext_stxexprs);
1700 154 : exprsString = TextDatumGetCString(exprsDatum);
1701 154 : exprs = (List *) stringToNode(exprsString);
1702 154 : pfree(exprsString);
1703 : }
1704 : else
1705 558 : exprs = NIL;
1706 :
1707 : /* count the number of columns (attributes and expressions) */
1708 712 : ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
1709 :
1710 712 : initStringInfo(&buf);
1711 :
1712 712 : if (!columns_only)
1713 : {
1714 298 : nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
1715 298 : appendStringInfo(&buf, "CREATE STATISTICS %s",
1716 : quote_qualified_identifier(nsp,
1717 298 : NameStr(statextrec->stxname)));
1718 :
1719 : /*
1720 : * Decode the stxkind column so that we know which stats types to
1721 : * print.
1722 : */
1723 298 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1724 : Anum_pg_statistic_ext_stxkind);
1725 298 : arr = DatumGetArrayTypeP(datum);
1726 298 : if (ARR_NDIM(arr) != 1 ||
1727 298 : ARR_HASNULL(arr) ||
1728 298 : ARR_ELEMTYPE(arr) != CHAROID)
1729 0 : elog(ERROR, "stxkind is not a 1-D char array");
1730 298 : enabled = (char *) ARR_DATA_PTR(arr);
1731 :
1732 298 : ndistinct_enabled = false;
1733 298 : dependencies_enabled = false;
1734 298 : mcv_enabled = false;
1735 :
1736 764 : for (i = 0; i < ARR_DIMS(arr)[0]; i++)
1737 : {
1738 466 : if (enabled[i] == STATS_EXT_NDISTINCT)
1739 148 : ndistinct_enabled = true;
1740 318 : else if (enabled[i] == STATS_EXT_DEPENDENCIES)
1741 100 : dependencies_enabled = true;
1742 218 : else if (enabled[i] == STATS_EXT_MCV)
1743 130 : mcv_enabled = true;
1744 :
1745 : /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
1746 : }
1747 :
1748 : /*
1749 : * If any option is disabled, then we'll need to append the types
1750 : * clause to show which options are enabled. We omit the types clause
1751 : * on purpose when all options are enabled, so a pg_dump/pg_restore
1752 : * will create all statistics types on a newer postgres version, if
1753 : * the statistics had all options enabled on the original version.
1754 : *
1755 : * But if the statistics is defined on just a single column, it has to
1756 : * be an expression statistics. In that case we don't need to specify
1757 : * kinds.
1758 : */
1759 298 : if ((!ndistinct_enabled || !dependencies_enabled || !mcv_enabled) &&
1760 : (ncolumns > 1))
1761 : {
1762 150 : bool gotone = false;
1763 :
1764 150 : appendStringInfoString(&buf, " (");
1765 :
1766 150 : if (ndistinct_enabled)
1767 : {
1768 72 : appendStringInfoString(&buf, "ndistinct");
1769 72 : gotone = true;
1770 : }
1771 :
1772 150 : if (dependencies_enabled)
1773 : {
1774 24 : appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
1775 24 : gotone = true;
1776 : }
1777 :
1778 150 : if (mcv_enabled)
1779 54 : appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
1780 :
1781 150 : appendStringInfoChar(&buf, ')');
1782 : }
1783 :
1784 298 : appendStringInfoString(&buf, " ON ");
1785 : }
1786 :
1787 : /* decode simple column references */
1788 2026 : for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
1789 : {
1790 1314 : AttrNumber attnum = statextrec->stxkeys.values[colno];
1791 : char *attname;
1792 :
1793 1314 : if (colno > 0)
1794 744 : appendStringInfoString(&buf, ", ");
1795 :
1796 1314 : attname = get_attname(statextrec->stxrelid, attnum, false);
1797 :
1798 1314 : appendStringInfoString(&buf, quote_identifier(attname));
1799 : }
1800 :
1801 712 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
1802 : statextrec->stxrelid);
1803 :
1804 964 : foreach(lc, exprs)
1805 : {
1806 252 : Node *expr = (Node *) lfirst(lc);
1807 : char *str;
1808 252 : int prettyFlags = PRETTYFLAG_PAREN;
1809 :
1810 252 : str = deparse_expression_pretty(expr, context, false, false,
1811 : prettyFlags, 0);
1812 :
1813 252 : if (colno > 0)
1814 110 : appendStringInfoString(&buf, ", ");
1815 :
1816 : /* Need parens if it's not a bare function call */
1817 252 : if (looks_like_function(expr))
1818 40 : appendStringInfoString(&buf, str);
1819 : else
1820 212 : appendStringInfo(&buf, "(%s)", str);
1821 :
1822 252 : colno++;
1823 : }
1824 :
1825 712 : if (!columns_only)
1826 298 : appendStringInfo(&buf, " FROM %s",
1827 : generate_relation_name(statextrec->stxrelid, NIL));
1828 :
1829 712 : ReleaseSysCache(statexttup);
1830 :
1831 712 : return buf.data;
1832 : }
1833 :
1834 : /*
1835 : * Generate text array of expressions for statistics object.
1836 : */
1837 : Datum
1838 24 : pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
1839 : {
1840 24 : Oid statextid = PG_GETARG_OID(0);
1841 : Form_pg_statistic_ext statextrec;
1842 : HeapTuple statexttup;
1843 : Datum datum;
1844 : List *context;
1845 : ListCell *lc;
1846 24 : List *exprs = NIL;
1847 : bool has_exprs;
1848 : char *tmp;
1849 24 : ArrayBuildState *astate = NULL;
1850 :
1851 24 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1852 :
1853 24 : if (!HeapTupleIsValid(statexttup))
1854 0 : PG_RETURN_NULL();
1855 :
1856 : /* Does the stats object have expressions? */
1857 24 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
1858 :
1859 : /* no expressions? we're done */
1860 24 : if (!has_exprs)
1861 : {
1862 12 : ReleaseSysCache(statexttup);
1863 12 : PG_RETURN_NULL();
1864 : }
1865 :
1866 12 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1867 :
1868 : /*
1869 : * Get the statistics expressions, and deparse them into text values.
1870 : */
1871 12 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1872 : Anum_pg_statistic_ext_stxexprs);
1873 12 : tmp = TextDatumGetCString(datum);
1874 12 : exprs = (List *) stringToNode(tmp);
1875 12 : pfree(tmp);
1876 :
1877 12 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
1878 : statextrec->stxrelid);
1879 :
1880 36 : foreach(lc, exprs)
1881 : {
1882 24 : Node *expr = (Node *) lfirst(lc);
1883 : char *str;
1884 24 : int prettyFlags = PRETTYFLAG_INDENT;
1885 :
1886 24 : str = deparse_expression_pretty(expr, context, false, false,
1887 : prettyFlags, 0);
1888 :
1889 24 : astate = accumArrayResult(astate,
1890 24 : PointerGetDatum(cstring_to_text(str)),
1891 : false,
1892 : TEXTOID,
1893 : CurrentMemoryContext);
1894 : }
1895 :
1896 12 : ReleaseSysCache(statexttup);
1897 :
1898 12 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1899 : }
1900 :
1901 : /*
1902 : * pg_get_partkeydef
1903 : *
1904 : * Returns the partition key specification, ie, the following:
1905 : *
1906 : * { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
1907 : */
1908 : Datum
1909 1912 : pg_get_partkeydef(PG_FUNCTION_ARGS)
1910 : {
1911 1912 : Oid relid = PG_GETARG_OID(0);
1912 : char *res;
1913 :
1914 1912 : res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
1915 :
1916 1912 : if (res == NULL)
1917 6 : PG_RETURN_NULL();
1918 :
1919 1906 : PG_RETURN_TEXT_P(string_to_text(res));
1920 : }
1921 :
1922 : /* Internal version that just reports the column definitions */
1923 : char *
1924 142 : pg_get_partkeydef_columns(Oid relid, bool pretty)
1925 : {
1926 : int prettyFlags;
1927 :
1928 142 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1929 :
1930 142 : return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
1931 : }
1932 :
1933 : /*
1934 : * Internal workhorse to decompile a partition key definition.
1935 : */
1936 : static char *
1937 2054 : pg_get_partkeydef_worker(Oid relid, int prettyFlags,
1938 : bool attrsOnly, bool missing_ok)
1939 : {
1940 : Form_pg_partitioned_table form;
1941 : HeapTuple tuple;
1942 : oidvector *partclass;
1943 : oidvector *partcollation;
1944 : List *partexprs;
1945 : ListCell *partexpr_item;
1946 : List *context;
1947 : Datum datum;
1948 : StringInfoData buf;
1949 : int keyno;
1950 : char *str;
1951 : char *sep;
1952 :
1953 2054 : tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
1954 2054 : if (!HeapTupleIsValid(tuple))
1955 : {
1956 6 : if (missing_ok)
1957 6 : return NULL;
1958 0 : elog(ERROR, "cache lookup failed for partition key of %u", relid);
1959 : }
1960 :
1961 2048 : form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
1962 :
1963 : Assert(form->partrelid == relid);
1964 :
1965 : /* Must get partclass and partcollation the hard way */
1966 2048 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1967 : Anum_pg_partitioned_table_partclass);
1968 2048 : partclass = (oidvector *) DatumGetPointer(datum);
1969 :
1970 2048 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1971 : Anum_pg_partitioned_table_partcollation);
1972 2048 : partcollation = (oidvector *) DatumGetPointer(datum);
1973 :
1974 :
1975 : /*
1976 : * Get the expressions, if any. (NOTE: we do not use the relcache
1977 : * versions of the expressions, because we want to display
1978 : * non-const-folded expressions.)
1979 : */
1980 2048 : if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprs, NULL))
1981 : {
1982 : Datum exprsDatum;
1983 : char *exprsString;
1984 :
1985 194 : exprsDatum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1986 : Anum_pg_partitioned_table_partexprs);
1987 194 : exprsString = TextDatumGetCString(exprsDatum);
1988 194 : partexprs = (List *) stringToNode(exprsString);
1989 :
1990 194 : if (!IsA(partexprs, List))
1991 0 : elog(ERROR, "unexpected node type found in partexprs: %d",
1992 : (int) nodeTag(partexprs));
1993 :
1994 194 : pfree(exprsString);
1995 : }
1996 : else
1997 1854 : partexprs = NIL;
1998 :
1999 2048 : partexpr_item = list_head(partexprs);
2000 2048 : context = deparse_context_for(get_relation_name(relid), relid);
2001 :
2002 2048 : initStringInfo(&buf);
2003 :
2004 2048 : switch (form->partstrat)
2005 : {
2006 78 : case PARTITION_STRATEGY_HASH:
2007 78 : if (!attrsOnly)
2008 78 : appendStringInfoString(&buf, "HASH");
2009 78 : break;
2010 792 : case PARTITION_STRATEGY_LIST:
2011 792 : if (!attrsOnly)
2012 752 : appendStringInfoString(&buf, "LIST");
2013 792 : break;
2014 1178 : case PARTITION_STRATEGY_RANGE:
2015 1178 : if (!attrsOnly)
2016 1076 : appendStringInfoString(&buf, "RANGE");
2017 1178 : break;
2018 0 : default:
2019 0 : elog(ERROR, "unexpected partition strategy: %d",
2020 : (int) form->partstrat);
2021 : }
2022 :
2023 2048 : if (!attrsOnly)
2024 1906 : appendStringInfoString(&buf, " (");
2025 2048 : sep = "";
2026 4278 : for (keyno = 0; keyno < form->partnatts; keyno++)
2027 : {
2028 2230 : AttrNumber attnum = form->partattrs.values[keyno];
2029 : Oid keycoltype;
2030 : Oid keycolcollation;
2031 : Oid partcoll;
2032 :
2033 2230 : appendStringInfoString(&buf, sep);
2034 2230 : sep = ", ";
2035 2230 : if (attnum != 0)
2036 : {
2037 : /* Simple attribute reference */
2038 : char *attname;
2039 : int32 keycoltypmod;
2040 :
2041 2024 : attname = get_attname(relid, attnum, false);
2042 2024 : appendStringInfoString(&buf, quote_identifier(attname));
2043 2024 : get_atttypetypmodcoll(relid, attnum,
2044 : &keycoltype, &keycoltypmod,
2045 : &keycolcollation);
2046 : }
2047 : else
2048 : {
2049 : /* Expression */
2050 : Node *partkey;
2051 :
2052 206 : if (partexpr_item == NULL)
2053 0 : elog(ERROR, "too few entries in partexprs list");
2054 206 : partkey = (Node *) lfirst(partexpr_item);
2055 206 : partexpr_item = lnext(partexprs, partexpr_item);
2056 :
2057 : /* Deparse */
2058 206 : str = deparse_expression_pretty(partkey, context, false, false,
2059 : prettyFlags, 0);
2060 : /* Need parens if it's not a bare function call */
2061 206 : if (looks_like_function(partkey))
2062 68 : appendStringInfoString(&buf, str);
2063 : else
2064 138 : appendStringInfo(&buf, "(%s)", str);
2065 :
2066 206 : keycoltype = exprType(partkey);
2067 206 : keycolcollation = exprCollation(partkey);
2068 : }
2069 :
2070 : /* Add collation, if not default for column */
2071 2230 : partcoll = partcollation->values[keyno];
2072 2230 : if (!attrsOnly && OidIsValid(partcoll) && partcoll != keycolcollation)
2073 6 : appendStringInfo(&buf, " COLLATE %s",
2074 : generate_collation_name((partcoll)));
2075 :
2076 : /* Add the operator class name, if not default */
2077 2230 : if (!attrsOnly)
2078 2034 : get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2079 : }
2080 :
2081 2048 : if (!attrsOnly)
2082 1906 : appendStringInfoChar(&buf, ')');
2083 :
2084 : /* Clean up */
2085 2048 : ReleaseSysCache(tuple);
2086 :
2087 2048 : return buf.data;
2088 : }
2089 :
2090 : /*
2091 : * pg_get_partition_constraintdef
2092 : *
2093 : * Returns partition constraint expression as a string for the input relation
2094 : */
2095 : Datum
2096 182 : pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
2097 : {
2098 182 : Oid relationId = PG_GETARG_OID(0);
2099 : Expr *constr_expr;
2100 : int prettyFlags;
2101 : List *context;
2102 : char *consrc;
2103 :
2104 182 : constr_expr = get_partition_qual_relid(relationId);
2105 :
2106 : /* Quick exit if no partition constraint */
2107 182 : if (constr_expr == NULL)
2108 18 : PG_RETURN_NULL();
2109 :
2110 : /*
2111 : * Deparse and return the constraint expression.
2112 : */
2113 164 : prettyFlags = PRETTYFLAG_INDENT;
2114 164 : context = deparse_context_for(get_relation_name(relationId), relationId);
2115 164 : consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2116 : false, prettyFlags, 0);
2117 :
2118 164 : PG_RETURN_TEXT_P(string_to_text(consrc));
2119 : }
2120 :
2121 : /*
2122 : * pg_get_partconstrdef_string
2123 : *
2124 : * Returns the partition constraint as a C-string for the input relation, with
2125 : * the given alias. No pretty-printing.
2126 : */
2127 : char *
2128 110 : pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
2129 : {
2130 : Expr *constr_expr;
2131 : List *context;
2132 :
2133 110 : constr_expr = get_partition_qual_relid(partitionId);
2134 110 : context = deparse_context_for(aliasname, partitionId);
2135 :
2136 110 : return deparse_expression((Node *) constr_expr, context, true, false);
2137 : }
2138 :
2139 : /*
2140 : * pg_get_constraintdef
2141 : *
2142 : * Returns the definition for the constraint, ie, everything that needs to
2143 : * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
2144 : */
2145 : Datum
2146 2700 : pg_get_constraintdef(PG_FUNCTION_ARGS)
2147 : {
2148 2700 : Oid constraintId = PG_GETARG_OID(0);
2149 : int prettyFlags;
2150 : char *res;
2151 :
2152 2700 : prettyFlags = PRETTYFLAG_INDENT;
2153 :
2154 2700 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2155 :
2156 2700 : if (res == NULL)
2157 6 : PG_RETURN_NULL();
2158 :
2159 2694 : PG_RETURN_TEXT_P(string_to_text(res));
2160 : }
2161 :
2162 : Datum
2163 5540 : pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
2164 : {
2165 5540 : Oid constraintId = PG_GETARG_OID(0);
2166 5540 : bool pretty = PG_GETARG_BOOL(1);
2167 : int prettyFlags;
2168 : char *res;
2169 :
2170 5540 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2171 :
2172 5540 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2173 :
2174 5540 : if (res == NULL)
2175 0 : PG_RETURN_NULL();
2176 :
2177 5540 : PG_RETURN_TEXT_P(string_to_text(res));
2178 : }
2179 :
2180 : /*
2181 : * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
2182 : */
2183 : char *
2184 596 : pg_get_constraintdef_command(Oid constraintId)
2185 : {
2186 596 : return pg_get_constraintdef_worker(constraintId, true, 0, false);
2187 : }
2188 :
2189 : /*
2190 : * As of 9.4, we now use an MVCC snapshot for this.
2191 : */
2192 : static char *
2193 8836 : pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
2194 : int prettyFlags, bool missing_ok)
2195 : {
2196 : HeapTuple tup;
2197 : Form_pg_constraint conForm;
2198 : StringInfoData buf;
2199 : SysScanDesc scandesc;
2200 : ScanKeyData scankey[1];
2201 8836 : Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());
2202 8836 : Relation relation = table_open(ConstraintRelationId, AccessShareLock);
2203 :
2204 8836 : ScanKeyInit(&scankey[0],
2205 : Anum_pg_constraint_oid,
2206 : BTEqualStrategyNumber, F_OIDEQ,
2207 : ObjectIdGetDatum(constraintId));
2208 :
2209 8836 : scandesc = systable_beginscan(relation,
2210 : ConstraintOidIndexId,
2211 : true,
2212 : snapshot,
2213 : 1,
2214 : scankey);
2215 :
2216 : /*
2217 : * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2218 : * via SearchSysCache, which works fine.
2219 : */
2220 8836 : tup = systable_getnext(scandesc);
2221 :
2222 8836 : UnregisterSnapshot(snapshot);
2223 :
2224 8836 : if (!HeapTupleIsValid(tup))
2225 : {
2226 6 : if (missing_ok)
2227 : {
2228 6 : systable_endscan(scandesc);
2229 6 : table_close(relation, AccessShareLock);
2230 6 : return NULL;
2231 : }
2232 0 : elog(ERROR, "could not find tuple for constraint %u", constraintId);
2233 : }
2234 :
2235 8830 : conForm = (Form_pg_constraint) GETSTRUCT(tup);
2236 :
2237 8830 : initStringInfo(&buf);
2238 :
2239 8830 : if (fullCommand)
2240 : {
2241 596 : if (OidIsValid(conForm->conrelid))
2242 : {
2243 : /*
2244 : * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2245 : * constraints, and other types of constraints don't inherit
2246 : * anyway so it doesn't matter whether we say ONLY or not. Someday
2247 : * we might need to let callers specify whether to put ONLY in the
2248 : * command.
2249 : */
2250 582 : appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2251 : generate_qualified_relation_name(conForm->conrelid),
2252 582 : quote_identifier(NameStr(conForm->conname)));
2253 : }
2254 : else
2255 : {
2256 : /* Must be a domain constraint */
2257 : Assert(OidIsValid(conForm->contypid));
2258 14 : appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2259 : generate_qualified_type_name(conForm->contypid),
2260 14 : quote_identifier(NameStr(conForm->conname)));
2261 : }
2262 : }
2263 :
2264 8830 : switch (conForm->contype)
2265 : {
2266 866 : case CONSTRAINT_FOREIGN:
2267 : {
2268 : Datum val;
2269 : bool isnull;
2270 : const char *string;
2271 :
2272 : /* Start off the constraint definition */
2273 866 : appendStringInfoString(&buf, "FOREIGN KEY (");
2274 :
2275 : /* Fetch and build referencing-column list */
2276 866 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2277 : Anum_pg_constraint_conkey);
2278 :
2279 : /* If it is a temporal foreign key then it uses PERIOD. */
2280 866 : decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
2281 :
2282 : /* add foreign relation name */
2283 866 : appendStringInfo(&buf, ") REFERENCES %s(",
2284 : generate_relation_name(conForm->confrelid,
2285 : NIL));
2286 :
2287 : /* Fetch and build referenced-column list */
2288 866 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2289 : Anum_pg_constraint_confkey);
2290 :
2291 866 : decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
2292 :
2293 866 : appendStringInfoChar(&buf, ')');
2294 :
2295 : /* Add match type */
2296 866 : switch (conForm->confmatchtype)
2297 : {
2298 40 : case FKCONSTR_MATCH_FULL:
2299 40 : string = " MATCH FULL";
2300 40 : break;
2301 0 : case FKCONSTR_MATCH_PARTIAL:
2302 0 : string = " MATCH PARTIAL";
2303 0 : break;
2304 826 : case FKCONSTR_MATCH_SIMPLE:
2305 826 : string = "";
2306 826 : break;
2307 0 : default:
2308 0 : elog(ERROR, "unrecognized confmatchtype: %d",
2309 : conForm->confmatchtype);
2310 : string = ""; /* keep compiler quiet */
2311 : break;
2312 : }
2313 866 : appendStringInfoString(&buf, string);
2314 :
2315 : /* Add ON UPDATE and ON DELETE clauses, if needed */
2316 866 : switch (conForm->confupdtype)
2317 : {
2318 706 : case FKCONSTR_ACTION_NOACTION:
2319 706 : string = NULL; /* suppress default */
2320 706 : break;
2321 0 : case FKCONSTR_ACTION_RESTRICT:
2322 0 : string = "RESTRICT";
2323 0 : break;
2324 126 : case FKCONSTR_ACTION_CASCADE:
2325 126 : string = "CASCADE";
2326 126 : break;
2327 34 : case FKCONSTR_ACTION_SETNULL:
2328 34 : string = "SET NULL";
2329 34 : break;
2330 0 : case FKCONSTR_ACTION_SETDEFAULT:
2331 0 : string = "SET DEFAULT";
2332 0 : break;
2333 0 : default:
2334 0 : elog(ERROR, "unrecognized confupdtype: %d",
2335 : conForm->confupdtype);
2336 : string = NULL; /* keep compiler quiet */
2337 : break;
2338 : }
2339 866 : if (string)
2340 160 : appendStringInfo(&buf, " ON UPDATE %s", string);
2341 :
2342 866 : switch (conForm->confdeltype)
2343 : {
2344 716 : case FKCONSTR_ACTION_NOACTION:
2345 716 : string = NULL; /* suppress default */
2346 716 : break;
2347 0 : case FKCONSTR_ACTION_RESTRICT:
2348 0 : string = "RESTRICT";
2349 0 : break;
2350 126 : case FKCONSTR_ACTION_CASCADE:
2351 126 : string = "CASCADE";
2352 126 : break;
2353 18 : case FKCONSTR_ACTION_SETNULL:
2354 18 : string = "SET NULL";
2355 18 : break;
2356 6 : case FKCONSTR_ACTION_SETDEFAULT:
2357 6 : string = "SET DEFAULT";
2358 6 : break;
2359 0 : default:
2360 0 : elog(ERROR, "unrecognized confdeltype: %d",
2361 : conForm->confdeltype);
2362 : string = NULL; /* keep compiler quiet */
2363 : break;
2364 : }
2365 866 : if (string)
2366 150 : appendStringInfo(&buf, " ON DELETE %s", string);
2367 :
2368 : /*
2369 : * Add columns specified to SET NULL or SET DEFAULT if
2370 : * provided.
2371 : */
2372 866 : val = SysCacheGetAttr(CONSTROID, tup,
2373 : Anum_pg_constraint_confdelsetcols, &isnull);
2374 866 : if (!isnull)
2375 : {
2376 12 : appendStringInfoString(&buf, " (");
2377 12 : decompile_column_index_array(val, conForm->conrelid, false, &buf);
2378 12 : appendStringInfoChar(&buf, ')');
2379 : }
2380 :
2381 866 : break;
2382 : }
2383 4770 : case CONSTRAINT_PRIMARY:
2384 : case CONSTRAINT_UNIQUE:
2385 : {
2386 : Datum val;
2387 : Oid indexId;
2388 : int keyatts;
2389 : HeapTuple indtup;
2390 :
2391 : /* Start off the constraint definition */
2392 4770 : if (conForm->contype == CONSTRAINT_PRIMARY)
2393 3986 : appendStringInfoString(&buf, "PRIMARY KEY ");
2394 : else
2395 784 : appendStringInfoString(&buf, "UNIQUE ");
2396 :
2397 4770 : indexId = conForm->conindid;
2398 :
2399 4770 : indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
2400 4770 : if (!HeapTupleIsValid(indtup))
2401 0 : elog(ERROR, "cache lookup failed for index %u", indexId);
2402 4770 : if (conForm->contype == CONSTRAINT_UNIQUE &&
2403 784 : ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
2404 0 : appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2405 :
2406 4770 : appendStringInfoChar(&buf, '(');
2407 :
2408 : /* Fetch and build target column list */
2409 4770 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2410 : Anum_pg_constraint_conkey);
2411 :
2412 4770 : keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
2413 4770 : if (conForm->conperiod)
2414 446 : appendStringInfoString(&buf, " WITHOUT OVERLAPS");
2415 :
2416 4770 : appendStringInfoChar(&buf, ')');
2417 :
2418 : /* Build including column list (from pg_index.indkeys) */
2419 4770 : val = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2420 : Anum_pg_index_indnatts);
2421 4770 : if (DatumGetInt32(val) > keyatts)
2422 : {
2423 : Datum cols;
2424 : Datum *keys;
2425 : int nKeys;
2426 : int j;
2427 :
2428 106 : appendStringInfoString(&buf, " INCLUDE (");
2429 :
2430 106 : cols = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2431 : Anum_pg_index_indkey);
2432 :
2433 106 : deconstruct_array_builtin(DatumGetArrayTypeP(cols), INT2OID,
2434 : &keys, NULL, &nKeys);
2435 :
2436 318 : for (j = keyatts; j < nKeys; j++)
2437 : {
2438 : char *colName;
2439 :
2440 212 : colName = get_attname(conForm->conrelid,
2441 212 : DatumGetInt16(keys[j]), false);
2442 212 : if (j > keyatts)
2443 106 : appendStringInfoString(&buf, ", ");
2444 212 : appendStringInfoString(&buf, quote_identifier(colName));
2445 : }
2446 :
2447 106 : appendStringInfoChar(&buf, ')');
2448 : }
2449 4770 : ReleaseSysCache(indtup);
2450 :
2451 : /* XXX why do we only print these bits if fullCommand? */
2452 4770 : if (fullCommand && OidIsValid(indexId))
2453 : {
2454 204 : char *options = flatten_reloptions(indexId);
2455 : Oid tblspc;
2456 :
2457 204 : if (options)
2458 : {
2459 0 : appendStringInfo(&buf, " WITH (%s)", options);
2460 0 : pfree(options);
2461 : }
2462 :
2463 : /*
2464 : * Print the tablespace, unless it's the database default.
2465 : * This is to help ALTER TABLE usage of this facility,
2466 : * which needs this behavior to recreate exact catalog
2467 : * state.
2468 : */
2469 204 : tblspc = get_rel_tablespace(indexId);
2470 204 : if (OidIsValid(tblspc))
2471 24 : appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2472 24 : quote_identifier(get_tablespace_name(tblspc)));
2473 : }
2474 :
2475 4770 : break;
2476 : }
2477 2690 : case CONSTRAINT_CHECK:
2478 : {
2479 : Datum val;
2480 : char *conbin;
2481 : char *consrc;
2482 : Node *expr;
2483 : List *context;
2484 :
2485 : /* Fetch constraint expression in parsetree form */
2486 2690 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2487 : Anum_pg_constraint_conbin);
2488 :
2489 2690 : conbin = TextDatumGetCString(val);
2490 2690 : expr = stringToNode(conbin);
2491 :
2492 : /* Set up deparsing context for Var nodes in constraint */
2493 2690 : if (conForm->conrelid != InvalidOid)
2494 : {
2495 : /* relation constraint */
2496 2356 : context = deparse_context_for(get_relation_name(conForm->conrelid),
2497 : conForm->conrelid);
2498 : }
2499 : else
2500 : {
2501 : /* domain constraint --- can't have Vars */
2502 334 : context = NIL;
2503 : }
2504 :
2505 2690 : consrc = deparse_expression_pretty(expr, context, false, false,
2506 : prettyFlags, 0);
2507 :
2508 : /*
2509 : * Now emit the constraint definition, adding NO INHERIT if
2510 : * necessary.
2511 : *
2512 : * There are cases where the constraint expression will be
2513 : * fully parenthesized and we don't need the outer parens ...
2514 : * but there are other cases where we do need 'em. Be
2515 : * conservative for now.
2516 : *
2517 : * Note that simply checking for leading '(' and trailing ')'
2518 : * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2519 : */
2520 2690 : appendStringInfo(&buf, "CHECK (%s)%s",
2521 : consrc,
2522 2690 : conForm->connoinherit ? " NO INHERIT" : "");
2523 2690 : break;
2524 : }
2525 388 : case CONSTRAINT_NOTNULL:
2526 : {
2527 388 : if (conForm->conrelid)
2528 : {
2529 : AttrNumber attnum;
2530 :
2531 382 : attnum = extractNotNullColumn(tup);
2532 :
2533 382 : appendStringInfo(&buf, "NOT NULL %s",
2534 382 : quote_identifier(get_attname(conForm->conrelid,
2535 : attnum, false)));
2536 382 : if (((Form_pg_constraint) GETSTRUCT(tup))->connoinherit)
2537 0 : appendStringInfoString(&buf, " NO INHERIT");
2538 : }
2539 6 : else if (conForm->contypid)
2540 : {
2541 : /* conkey is null for domain not-null constraints */
2542 6 : appendStringInfoString(&buf, "NOT NULL");
2543 : }
2544 388 : break;
2545 : }
2546 :
2547 0 : case CONSTRAINT_TRIGGER:
2548 :
2549 : /*
2550 : * There isn't an ALTER TABLE syntax for creating a user-defined
2551 : * constraint trigger, but it seems better to print something than
2552 : * throw an error; if we throw error then this function couldn't
2553 : * safely be applied to all rows of pg_constraint.
2554 : */
2555 0 : appendStringInfoString(&buf, "TRIGGER");
2556 0 : break;
2557 116 : case CONSTRAINT_EXCLUSION:
2558 : {
2559 116 : Oid indexOid = conForm->conindid;
2560 : Datum val;
2561 : Datum *elems;
2562 : int nElems;
2563 : int i;
2564 : Oid *operators;
2565 :
2566 : /* Extract operator OIDs from the pg_constraint tuple */
2567 116 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2568 : Anum_pg_constraint_conexclop);
2569 :
2570 116 : deconstruct_array_builtin(DatumGetArrayTypeP(val), OIDOID,
2571 : &elems, NULL, &nElems);
2572 :
2573 116 : operators = (Oid *) palloc(nElems * sizeof(Oid));
2574 264 : for (i = 0; i < nElems; i++)
2575 148 : operators[i] = DatumGetObjectId(elems[i]);
2576 :
2577 : /* pg_get_indexdef_worker does the rest */
2578 : /* suppress tablespace because pg_dump wants it that way */
2579 116 : appendStringInfoString(&buf,
2580 116 : pg_get_indexdef_worker(indexOid,
2581 : 0,
2582 : operators,
2583 : false,
2584 : false,
2585 : false,
2586 : false,
2587 : prettyFlags,
2588 : false));
2589 116 : break;
2590 : }
2591 0 : default:
2592 0 : elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2593 : break;
2594 : }
2595 :
2596 8830 : if (conForm->condeferrable)
2597 156 : appendStringInfoString(&buf, " DEFERRABLE");
2598 8830 : if (conForm->condeferred)
2599 66 : appendStringInfoString(&buf, " INITIALLY DEFERRED");
2600 :
2601 : /* Validated status is irrelevant when the constraint is NOT ENFORCED. */
2602 8830 : if (!conForm->conenforced)
2603 122 : appendStringInfoString(&buf, " NOT ENFORCED");
2604 8708 : else if (!conForm->convalidated)
2605 336 : appendStringInfoString(&buf, " NOT VALID");
2606 :
2607 : /* Cleanup */
2608 8830 : systable_endscan(scandesc);
2609 8830 : table_close(relation, AccessShareLock);
2610 :
2611 8830 : return buf.data;
2612 : }
2613 :
2614 :
2615 : /*
2616 : * Convert an int16[] Datum into a comma-separated list of column names
2617 : * for the indicated relation; append the list to buf. Returns the number
2618 : * of keys.
2619 : */
2620 : static int
2621 6514 : decompile_column_index_array(Datum column_index_array, Oid relId,
2622 : bool withPeriod, StringInfo buf)
2623 : {
2624 : Datum *keys;
2625 : int nKeys;
2626 : int j;
2627 :
2628 : /* Extract data from array of int16 */
2629 6514 : deconstruct_array_builtin(DatumGetArrayTypeP(column_index_array), INT2OID,
2630 : &keys, NULL, &nKeys);
2631 :
2632 15660 : for (j = 0; j < nKeys; j++)
2633 : {
2634 : char *colName;
2635 :
2636 9146 : colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2637 :
2638 9146 : if (j == 0)
2639 6514 : appendStringInfoString(buf, quote_identifier(colName));
2640 : else
2641 2920 : appendStringInfo(buf, ", %s%s",
2642 288 : (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
2643 : quote_identifier(colName));
2644 : }
2645 :
2646 6514 : return nKeys;
2647 : }
2648 :
2649 :
2650 : /* ----------
2651 : * pg_get_expr - Decompile an expression tree
2652 : *
2653 : * Input: an expression tree in nodeToString form, and a relation OID
2654 : *
2655 : * Output: reverse-listed expression
2656 : *
2657 : * Currently, the expression can only refer to a single relation, namely
2658 : * the one specified by the second parameter. This is sufficient for
2659 : * partial indexes, column default expressions, etc. We also support
2660 : * Var-free expressions, for which the OID can be InvalidOid.
2661 : *
2662 : * If the OID is nonzero but not actually valid, don't throw an error,
2663 : * just return NULL. This is a bit questionable, but it's what we've
2664 : * done historically, and it can help avoid unwanted failures when
2665 : * examining catalog entries for just-deleted relations.
2666 : *
2667 : * We expect this function to work, or throw a reasonably clean error,
2668 : * for any node tree that can appear in a catalog pg_node_tree column.
2669 : * Query trees, such as those appearing in pg_rewrite.ev_action, are
2670 : * not supported. Nor are expressions in more than one relation, which
2671 : * can appear in places like pg_rewrite.ev_qual.
2672 : * ----------
2673 : */
2674 : Datum
2675 10594 : pg_get_expr(PG_FUNCTION_ARGS)
2676 : {
2677 10594 : text *expr = PG_GETARG_TEXT_PP(0);
2678 10594 : Oid relid = PG_GETARG_OID(1);
2679 : text *result;
2680 : int prettyFlags;
2681 :
2682 10594 : prettyFlags = PRETTYFLAG_INDENT;
2683 :
2684 10594 : result = pg_get_expr_worker(expr, relid, prettyFlags);
2685 10594 : if (result)
2686 10594 : PG_RETURN_TEXT_P(result);
2687 : else
2688 0 : PG_RETURN_NULL();
2689 : }
2690 :
2691 : Datum
2692 724 : pg_get_expr_ext(PG_FUNCTION_ARGS)
2693 : {
2694 724 : text *expr = PG_GETARG_TEXT_PP(0);
2695 724 : Oid relid = PG_GETARG_OID(1);
2696 724 : bool pretty = PG_GETARG_BOOL(2);
2697 : text *result;
2698 : int prettyFlags;
2699 :
2700 724 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2701 :
2702 724 : result = pg_get_expr_worker(expr, relid, prettyFlags);
2703 724 : if (result)
2704 724 : PG_RETURN_TEXT_P(result);
2705 : else
2706 0 : PG_RETURN_NULL();
2707 : }
2708 :
2709 : static text *
2710 11318 : pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
2711 : {
2712 : Node *node;
2713 : Node *tst;
2714 : Relids relids;
2715 : List *context;
2716 : char *exprstr;
2717 11318 : Relation rel = NULL;
2718 : char *str;
2719 :
2720 : /* Convert input pg_node_tree (really TEXT) object to C string */
2721 11318 : exprstr = text_to_cstring(expr);
2722 :
2723 : /* Convert expression to node tree */
2724 11318 : node = (Node *) stringToNode(exprstr);
2725 :
2726 11318 : pfree(exprstr);
2727 :
2728 : /*
2729 : * Throw error if the input is a querytree rather than an expression tree.
2730 : * While we could support queries here, there seems no very good reason
2731 : * to. In most such catalog columns, we'll see a List of Query nodes, or
2732 : * even nested Lists, so drill down to a non-List node before checking.
2733 : */
2734 11318 : tst = node;
2735 11318 : while (tst && IsA(tst, List))
2736 0 : tst = linitial((List *) tst);
2737 11318 : if (tst && IsA(tst, Query))
2738 0 : ereport(ERROR,
2739 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2740 : errmsg("input is a query, not an expression")));
2741 :
2742 : /*
2743 : * Throw error if the expression contains Vars we won't be able to
2744 : * deparse.
2745 : */
2746 11318 : relids = pull_varnos(NULL, node);
2747 11318 : if (OidIsValid(relid))
2748 : {
2749 11240 : if (!bms_is_subset(relids, bms_make_singleton(1)))
2750 0 : ereport(ERROR,
2751 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2752 : errmsg("expression contains variables of more than one relation")));
2753 : }
2754 : else
2755 : {
2756 78 : if (!bms_is_empty(relids))
2757 0 : ereport(ERROR,
2758 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2759 : errmsg("expression contains variables")));
2760 : }
2761 :
2762 : /*
2763 : * Prepare deparse context if needed. If we are deparsing with a relid,
2764 : * we need to transiently open and lock the rel, to make sure it won't go
2765 : * away underneath us. (set_relation_column_names would lock it anyway,
2766 : * so this isn't really introducing any new behavior.)
2767 : */
2768 11318 : if (OidIsValid(relid))
2769 : {
2770 11240 : rel = try_relation_open(relid, AccessShareLock);
2771 11240 : if (rel == NULL)
2772 0 : return NULL;
2773 11240 : context = deparse_context_for(RelationGetRelationName(rel), relid);
2774 : }
2775 : else
2776 78 : context = NIL;
2777 :
2778 : /* Deparse */
2779 11318 : str = deparse_expression_pretty(node, context, false, false,
2780 : prettyFlags, 0);
2781 :
2782 11318 : if (rel != NULL)
2783 11240 : relation_close(rel, AccessShareLock);
2784 :
2785 11318 : return string_to_text(str);
2786 : }
2787 :
2788 :
2789 : /* ----------
2790 : * pg_get_userbyid - Get a user name by roleid and
2791 : * fallback to 'unknown (OID=n)'
2792 : * ----------
2793 : */
2794 : Datum
2795 1720 : pg_get_userbyid(PG_FUNCTION_ARGS)
2796 : {
2797 1720 : Oid roleid = PG_GETARG_OID(0);
2798 : Name result;
2799 : HeapTuple roletup;
2800 : Form_pg_authid role_rec;
2801 :
2802 : /*
2803 : * Allocate space for the result
2804 : */
2805 1720 : result = (Name) palloc(NAMEDATALEN);
2806 1720 : memset(NameStr(*result), 0, NAMEDATALEN);
2807 :
2808 : /*
2809 : * Get the pg_authid entry and print the result
2810 : */
2811 1720 : roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
2812 1720 : if (HeapTupleIsValid(roletup))
2813 : {
2814 1720 : role_rec = (Form_pg_authid) GETSTRUCT(roletup);
2815 1720 : *result = role_rec->rolname;
2816 1720 : ReleaseSysCache(roletup);
2817 : }
2818 : else
2819 0 : sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
2820 :
2821 1720 : PG_RETURN_NAME(result);
2822 : }
2823 :
2824 :
2825 : /*
2826 : * pg_get_serial_sequence
2827 : * Get the name of the sequence used by an identity or serial column,
2828 : * formatted suitably for passing to setval, nextval or currval.
2829 : * First parameter is not treated as double-quoted, second parameter
2830 : * is --- see documentation for reason.
2831 : */
2832 : Datum
2833 12 : pg_get_serial_sequence(PG_FUNCTION_ARGS)
2834 : {
2835 12 : text *tablename = PG_GETARG_TEXT_PP(0);
2836 12 : text *columnname = PG_GETARG_TEXT_PP(1);
2837 : RangeVar *tablerv;
2838 : Oid tableOid;
2839 : char *column;
2840 : AttrNumber attnum;
2841 12 : Oid sequenceId = InvalidOid;
2842 : Relation depRel;
2843 : ScanKeyData key[3];
2844 : SysScanDesc scan;
2845 : HeapTuple tup;
2846 :
2847 : /* Look up table name. Can't lock it - we might not have privileges. */
2848 12 : tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
2849 12 : tableOid = RangeVarGetRelid(tablerv, NoLock, false);
2850 :
2851 : /* Get the number of the column */
2852 12 : column = text_to_cstring(columnname);
2853 :
2854 12 : attnum = get_attnum(tableOid, column);
2855 12 : if (attnum == InvalidAttrNumber)
2856 0 : ereport(ERROR,
2857 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2858 : errmsg("column \"%s\" of relation \"%s\" does not exist",
2859 : column, tablerv->relname)));
2860 :
2861 : /* Search the dependency table for the dependent sequence */
2862 12 : depRel = table_open(DependRelationId, AccessShareLock);
2863 :
2864 12 : ScanKeyInit(&key[0],
2865 : Anum_pg_depend_refclassid,
2866 : BTEqualStrategyNumber, F_OIDEQ,
2867 : ObjectIdGetDatum(RelationRelationId));
2868 12 : ScanKeyInit(&key[1],
2869 : Anum_pg_depend_refobjid,
2870 : BTEqualStrategyNumber, F_OIDEQ,
2871 : ObjectIdGetDatum(tableOid));
2872 12 : ScanKeyInit(&key[2],
2873 : Anum_pg_depend_refobjsubid,
2874 : BTEqualStrategyNumber, F_INT4EQ,
2875 : Int32GetDatum(attnum));
2876 :
2877 12 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
2878 : NULL, 3, key);
2879 :
2880 30 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
2881 : {
2882 30 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
2883 :
2884 : /*
2885 : * Look for an auto dependency (serial column) or internal dependency
2886 : * (identity column) of a sequence on a column. (We need the relkind
2887 : * test because indexes can also have auto dependencies on columns.)
2888 : */
2889 30 : if (deprec->classid == RelationRelationId &&
2890 12 : deprec->objsubid == 0 &&
2891 12 : (deprec->deptype == DEPENDENCY_AUTO ||
2892 18 : deprec->deptype == DEPENDENCY_INTERNAL) &&
2893 12 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
2894 : {
2895 12 : sequenceId = deprec->objid;
2896 12 : break;
2897 : }
2898 : }
2899 :
2900 12 : systable_endscan(scan);
2901 12 : table_close(depRel, AccessShareLock);
2902 :
2903 12 : if (OidIsValid(sequenceId))
2904 : {
2905 : char *result;
2906 :
2907 12 : result = generate_qualified_relation_name(sequenceId);
2908 :
2909 12 : PG_RETURN_TEXT_P(string_to_text(result));
2910 : }
2911 :
2912 0 : PG_RETURN_NULL();
2913 : }
2914 :
2915 :
2916 : /*
2917 : * pg_get_functiondef
2918 : * Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
2919 : * the specified function.
2920 : *
2921 : * Note: if you change the output format of this function, be careful not
2922 : * to break psql's rules (in \ef and \sf) for identifying the start of the
2923 : * function body. To wit: the function body starts on a line that begins with
2924 : * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
2925 : */
2926 : Datum
2927 172 : pg_get_functiondef(PG_FUNCTION_ARGS)
2928 : {
2929 172 : Oid funcid = PG_GETARG_OID(0);
2930 : StringInfoData buf;
2931 : StringInfoData dq;
2932 : HeapTuple proctup;
2933 : Form_pg_proc proc;
2934 : bool isfunction;
2935 : Datum tmp;
2936 : bool isnull;
2937 : const char *prosrc;
2938 : const char *name;
2939 : const char *nsp;
2940 : float4 procost;
2941 : int oldlen;
2942 :
2943 172 : initStringInfo(&buf);
2944 :
2945 : /* Look up the function */
2946 172 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
2947 172 : if (!HeapTupleIsValid(proctup))
2948 6 : PG_RETURN_NULL();
2949 :
2950 166 : proc = (Form_pg_proc) GETSTRUCT(proctup);
2951 166 : name = NameStr(proc->proname);
2952 :
2953 166 : if (proc->prokind == PROKIND_AGGREGATE)
2954 0 : ereport(ERROR,
2955 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2956 : errmsg("\"%s\" is an aggregate function", name)));
2957 :
2958 166 : isfunction = (proc->prokind != PROKIND_PROCEDURE);
2959 :
2960 : /*
2961 : * We always qualify the function name, to ensure the right function gets
2962 : * replaced.
2963 : */
2964 166 : nsp = get_namespace_name_or_temp(proc->pronamespace);
2965 166 : appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
2966 : isfunction ? "FUNCTION" : "PROCEDURE",
2967 : quote_qualified_identifier(nsp, name));
2968 166 : (void) print_function_arguments(&buf, proctup, false, true);
2969 166 : appendStringInfoString(&buf, ")\n");
2970 166 : if (isfunction)
2971 : {
2972 146 : appendStringInfoString(&buf, " RETURNS ");
2973 146 : print_function_rettype(&buf, proctup);
2974 146 : appendStringInfoChar(&buf, '\n');
2975 : }
2976 :
2977 166 : print_function_trftypes(&buf, proctup);
2978 :
2979 166 : appendStringInfo(&buf, " LANGUAGE %s\n",
2980 166 : quote_identifier(get_language_name(proc->prolang, false)));
2981 :
2982 : /* Emit some miscellaneous options on one line */
2983 166 : oldlen = buf.len;
2984 :
2985 166 : if (proc->prokind == PROKIND_WINDOW)
2986 0 : appendStringInfoString(&buf, " WINDOW");
2987 166 : switch (proc->provolatile)
2988 : {
2989 12 : case PROVOLATILE_IMMUTABLE:
2990 12 : appendStringInfoString(&buf, " IMMUTABLE");
2991 12 : break;
2992 30 : case PROVOLATILE_STABLE:
2993 30 : appendStringInfoString(&buf, " STABLE");
2994 30 : break;
2995 124 : case PROVOLATILE_VOLATILE:
2996 124 : break;
2997 : }
2998 :
2999 166 : switch (proc->proparallel)
3000 : {
3001 28 : case PROPARALLEL_SAFE:
3002 28 : appendStringInfoString(&buf, " PARALLEL SAFE");
3003 28 : break;
3004 0 : case PROPARALLEL_RESTRICTED:
3005 0 : appendStringInfoString(&buf, " PARALLEL RESTRICTED");
3006 0 : break;
3007 138 : case PROPARALLEL_UNSAFE:
3008 138 : break;
3009 : }
3010 :
3011 166 : if (proc->proisstrict)
3012 50 : appendStringInfoString(&buf, " STRICT");
3013 166 : if (proc->prosecdef)
3014 6 : appendStringInfoString(&buf, " SECURITY DEFINER");
3015 166 : if (proc->proleakproof)
3016 0 : appendStringInfoString(&buf, " LEAKPROOF");
3017 :
3018 : /* This code for the default cost and rows should match functioncmds.c */
3019 166 : if (proc->prolang == INTERNALlanguageId ||
3020 166 : proc->prolang == ClanguageId)
3021 10 : procost = 1;
3022 : else
3023 156 : procost = 100;
3024 166 : if (proc->procost != procost)
3025 6 : appendStringInfo(&buf, " COST %g", proc->procost);
3026 :
3027 166 : if (proc->prorows > 0 && proc->prorows != 1000)
3028 0 : appendStringInfo(&buf, " ROWS %g", proc->prorows);
3029 :
3030 166 : if (proc->prosupport)
3031 : {
3032 : Oid argtypes[1];
3033 :
3034 : /*
3035 : * We should qualify the support function's name if it wouldn't be
3036 : * resolved by lookup in the current search path.
3037 : */
3038 0 : argtypes[0] = INTERNALOID;
3039 0 : appendStringInfo(&buf, " SUPPORT %s",
3040 : generate_function_name(proc->prosupport, 1,
3041 : NIL, argtypes,
3042 : false, NULL, false));
3043 : }
3044 :
3045 166 : if (oldlen != buf.len)
3046 64 : appendStringInfoChar(&buf, '\n');
3047 :
3048 : /* Emit any proconfig options, one per line */
3049 166 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
3050 166 : if (!isnull)
3051 : {
3052 6 : ArrayType *a = DatumGetArrayTypeP(tmp);
3053 : int i;
3054 :
3055 : Assert(ARR_ELEMTYPE(a) == TEXTOID);
3056 : Assert(ARR_NDIM(a) == 1);
3057 : Assert(ARR_LBOUND(a)[0] == 1);
3058 :
3059 36 : for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3060 : {
3061 : Datum d;
3062 :
3063 30 : d = array_ref(a, 1, &i,
3064 : -1 /* varlenarray */ ,
3065 : -1 /* TEXT's typlen */ ,
3066 : false /* TEXT's typbyval */ ,
3067 : TYPALIGN_INT /* TEXT's typalign */ ,
3068 : &isnull);
3069 30 : if (!isnull)
3070 : {
3071 30 : char *configitem = TextDatumGetCString(d);
3072 : char *pos;
3073 :
3074 30 : pos = strchr(configitem, '=');
3075 30 : if (pos == NULL)
3076 0 : continue;
3077 30 : *pos++ = '\0';
3078 :
3079 30 : appendStringInfo(&buf, " SET %s TO ",
3080 : quote_identifier(configitem));
3081 :
3082 : /*
3083 : * Variables that are marked GUC_LIST_QUOTE were already fully
3084 : * quoted by flatten_set_variable_args() before they were put
3085 : * into the proconfig array. However, because the quoting
3086 : * rules used there aren't exactly like SQL's, we have to
3087 : * break the list value apart and then quote the elements as
3088 : * string literals. (The elements may be double-quoted as-is,
3089 : * but we can't just feed them to the SQL parser; it would do
3090 : * the wrong thing with elements that are zero-length or
3091 : * longer than NAMEDATALEN.)
3092 : *
3093 : * Variables that are not so marked should just be emitted as
3094 : * simple string literals. If the variable is not known to
3095 : * guc.c, we'll do that; this makes it unsafe to use
3096 : * GUC_LIST_QUOTE for extension variables.
3097 : */
3098 30 : if (GetConfigOptionFlags(configitem, true) & GUC_LIST_QUOTE)
3099 : {
3100 : List *namelist;
3101 : ListCell *lc;
3102 :
3103 : /* Parse string into list of identifiers */
3104 12 : if (!SplitGUCList(pos, ',', &namelist))
3105 : {
3106 : /* this shouldn't fail really */
3107 0 : elog(ERROR, "invalid list syntax in proconfig item");
3108 : }
3109 42 : foreach(lc, namelist)
3110 : {
3111 30 : char *curname = (char *) lfirst(lc);
3112 :
3113 30 : simple_quote_literal(&buf, curname);
3114 30 : if (lnext(namelist, lc))
3115 18 : appendStringInfoString(&buf, ", ");
3116 : }
3117 : }
3118 : else
3119 18 : simple_quote_literal(&buf, pos);
3120 30 : appendStringInfoChar(&buf, '\n');
3121 : }
3122 : }
3123 : }
3124 :
3125 : /* And finally the function definition ... */
3126 166 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3127 166 : if (proc->prolang == SQLlanguageId && !isnull)
3128 : {
3129 114 : print_function_sqlbody(&buf, proctup);
3130 : }
3131 : else
3132 : {
3133 52 : appendStringInfoString(&buf, "AS ");
3134 :
3135 52 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
3136 52 : if (!isnull)
3137 : {
3138 10 : simple_quote_literal(&buf, TextDatumGetCString(tmp));
3139 10 : appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3140 : }
3141 :
3142 52 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosrc);
3143 52 : prosrc = TextDatumGetCString(tmp);
3144 :
3145 : /*
3146 : * We always use dollar quoting. Figure out a suitable delimiter.
3147 : *
3148 : * Since the user is likely to be editing the function body string, we
3149 : * shouldn't use a short delimiter that he might easily create a
3150 : * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3151 : * if needed.
3152 : */
3153 52 : initStringInfo(&dq);
3154 52 : appendStringInfoChar(&dq, '$');
3155 52 : appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3156 52 : while (strstr(prosrc, dq.data) != NULL)
3157 0 : appendStringInfoChar(&dq, 'x');
3158 52 : appendStringInfoChar(&dq, '$');
3159 :
3160 52 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3161 52 : appendStringInfoString(&buf, prosrc);
3162 52 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3163 : }
3164 :
3165 166 : appendStringInfoChar(&buf, '\n');
3166 :
3167 166 : ReleaseSysCache(proctup);
3168 :
3169 166 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3170 : }
3171 :
3172 : /*
3173 : * pg_get_function_arguments
3174 : * Get a nicely-formatted list of arguments for a function.
3175 : * This is everything that would go between the parentheses in
3176 : * CREATE FUNCTION.
3177 : */
3178 : Datum
3179 6650 : pg_get_function_arguments(PG_FUNCTION_ARGS)
3180 : {
3181 6650 : Oid funcid = PG_GETARG_OID(0);
3182 : StringInfoData buf;
3183 : HeapTuple proctup;
3184 :
3185 6650 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3186 6650 : if (!HeapTupleIsValid(proctup))
3187 6 : PG_RETURN_NULL();
3188 :
3189 6644 : initStringInfo(&buf);
3190 :
3191 6644 : (void) print_function_arguments(&buf, proctup, false, true);
3192 :
3193 6644 : ReleaseSysCache(proctup);
3194 :
3195 6644 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3196 : }
3197 :
3198 : /*
3199 : * pg_get_function_identity_arguments
3200 : * Get a formatted list of arguments for a function.
3201 : * This is everything that would go between the parentheses in
3202 : * ALTER FUNCTION, etc. In particular, don't print defaults.
3203 : */
3204 : Datum
3205 6118 : pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
3206 : {
3207 6118 : Oid funcid = PG_GETARG_OID(0);
3208 : StringInfoData buf;
3209 : HeapTuple proctup;
3210 :
3211 6118 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3212 6118 : if (!HeapTupleIsValid(proctup))
3213 6 : PG_RETURN_NULL();
3214 :
3215 6112 : initStringInfo(&buf);
3216 :
3217 6112 : (void) print_function_arguments(&buf, proctup, false, false);
3218 :
3219 6112 : ReleaseSysCache(proctup);
3220 :
3221 6112 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3222 : }
3223 :
3224 : /*
3225 : * pg_get_function_result
3226 : * Get a nicely-formatted version of the result type of a function.
3227 : * This is what would appear after RETURNS in CREATE FUNCTION.
3228 : */
3229 : Datum
3230 5776 : pg_get_function_result(PG_FUNCTION_ARGS)
3231 : {
3232 5776 : Oid funcid = PG_GETARG_OID(0);
3233 : StringInfoData buf;
3234 : HeapTuple proctup;
3235 :
3236 5776 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3237 5776 : if (!HeapTupleIsValid(proctup))
3238 6 : PG_RETURN_NULL();
3239 :
3240 5770 : if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
3241 : {
3242 318 : ReleaseSysCache(proctup);
3243 318 : PG_RETURN_NULL();
3244 : }
3245 :
3246 5452 : initStringInfo(&buf);
3247 :
3248 5452 : print_function_rettype(&buf, proctup);
3249 :
3250 5452 : ReleaseSysCache(proctup);
3251 :
3252 5452 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3253 : }
3254 :
3255 : /*
3256 : * Guts of pg_get_function_result: append the function's return type
3257 : * to the specified buffer.
3258 : */
3259 : static void
3260 5598 : print_function_rettype(StringInfo buf, HeapTuple proctup)
3261 : {
3262 5598 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3263 5598 : int ntabargs = 0;
3264 : StringInfoData rbuf;
3265 :
3266 5598 : initStringInfo(&rbuf);
3267 :
3268 5598 : if (proc->proretset)
3269 : {
3270 : /* It might be a table function; try to print the arguments */
3271 598 : appendStringInfoString(&rbuf, "TABLE(");
3272 598 : ntabargs = print_function_arguments(&rbuf, proctup, true, false);
3273 598 : if (ntabargs > 0)
3274 118 : appendStringInfoChar(&rbuf, ')');
3275 : else
3276 480 : resetStringInfo(&rbuf);
3277 : }
3278 :
3279 5598 : if (ntabargs == 0)
3280 : {
3281 : /* Not a table function, so do the normal thing */
3282 5480 : if (proc->proretset)
3283 480 : appendStringInfoString(&rbuf, "SETOF ");
3284 5480 : appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3285 : }
3286 :
3287 5598 : appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
3288 5598 : }
3289 :
3290 : /*
3291 : * Common code for pg_get_function_arguments and pg_get_function_result:
3292 : * append the desired subset of arguments to buf. We print only TABLE
3293 : * arguments when print_table_args is true, and all the others when it's false.
3294 : * We print argument defaults only if print_defaults is true.
3295 : * Function return value is the number of arguments printed.
3296 : */
3297 : static int
3298 13520 : print_function_arguments(StringInfo buf, HeapTuple proctup,
3299 : bool print_table_args, bool print_defaults)
3300 : {
3301 13520 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3302 : int numargs;
3303 : Oid *argtypes;
3304 : char **argnames;
3305 : char *argmodes;
3306 13520 : int insertorderbyat = -1;
3307 : int argsprinted;
3308 : int inputargno;
3309 : int nlackdefaults;
3310 13520 : List *argdefaults = NIL;
3311 13520 : ListCell *nextargdefault = NULL;
3312 : int i;
3313 :
3314 13520 : numargs = get_func_arg_info(proctup,
3315 : &argtypes, &argnames, &argmodes);
3316 :
3317 13520 : nlackdefaults = numargs;
3318 13520 : if (print_defaults && proc->pronargdefaults > 0)
3319 : {
3320 : Datum proargdefaults;
3321 : bool isnull;
3322 :
3323 50 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3324 : Anum_pg_proc_proargdefaults,
3325 : &isnull);
3326 50 : if (!isnull)
3327 : {
3328 : char *str;
3329 :
3330 50 : str = TextDatumGetCString(proargdefaults);
3331 50 : argdefaults = castNode(List, stringToNode(str));
3332 50 : pfree(str);
3333 50 : nextargdefault = list_head(argdefaults);
3334 : /* nlackdefaults counts only *input* arguments lacking defaults */
3335 50 : nlackdefaults = proc->pronargs - list_length(argdefaults);
3336 : }
3337 : }
3338 :
3339 : /* Check for special treatment of ordered-set aggregates */
3340 13520 : if (proc->prokind == PROKIND_AGGREGATE)
3341 : {
3342 : HeapTuple aggtup;
3343 : Form_pg_aggregate agg;
3344 :
3345 1750 : aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(proc->oid));
3346 1750 : if (!HeapTupleIsValid(aggtup))
3347 0 : elog(ERROR, "cache lookup failed for aggregate %u",
3348 : proc->oid);
3349 1750 : agg = (Form_pg_aggregate) GETSTRUCT(aggtup);
3350 1750 : if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3351 76 : insertorderbyat = agg->aggnumdirectargs;
3352 1750 : ReleaseSysCache(aggtup);
3353 : }
3354 :
3355 13520 : argsprinted = 0;
3356 13520 : inputargno = 0;
3357 27402 : for (i = 0; i < numargs; i++)
3358 : {
3359 13882 : Oid argtype = argtypes[i];
3360 13882 : char *argname = argnames ? argnames[i] : NULL;
3361 13882 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
3362 : const char *modename;
3363 : bool isinput;
3364 :
3365 13882 : switch (argmode)
3366 : {
3367 11448 : case PROARGMODE_IN:
3368 :
3369 : /*
3370 : * For procedures, explicitly mark all argument modes, so as
3371 : * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3372 : */
3373 11448 : if (proc->prokind == PROKIND_PROCEDURE)
3374 728 : modename = "IN ";
3375 : else
3376 10720 : modename = "";
3377 11448 : isinput = true;
3378 11448 : break;
3379 160 : case PROARGMODE_INOUT:
3380 160 : modename = "INOUT ";
3381 160 : isinput = true;
3382 160 : break;
3383 1376 : case PROARGMODE_OUT:
3384 1376 : modename = "OUT ";
3385 1376 : isinput = false;
3386 1376 : break;
3387 274 : case PROARGMODE_VARIADIC:
3388 274 : modename = "VARIADIC ";
3389 274 : isinput = true;
3390 274 : break;
3391 624 : case PROARGMODE_TABLE:
3392 624 : modename = "";
3393 624 : isinput = false;
3394 624 : break;
3395 0 : default:
3396 0 : elog(ERROR, "invalid parameter mode '%c'", argmode);
3397 : modename = NULL; /* keep compiler quiet */
3398 : isinput = false;
3399 : break;
3400 : }
3401 13882 : if (isinput)
3402 11882 : inputargno++; /* this is a 1-based counter */
3403 :
3404 13882 : if (print_table_args != (argmode == PROARGMODE_TABLE))
3405 1100 : continue;
3406 :
3407 12782 : if (argsprinted == insertorderbyat)
3408 : {
3409 76 : if (argsprinted)
3410 76 : appendStringInfoChar(buf, ' ');
3411 76 : appendStringInfoString(buf, "ORDER BY ");
3412 : }
3413 12706 : else if (argsprinted)
3414 4162 : appendStringInfoString(buf, ", ");
3415 :
3416 12782 : appendStringInfoString(buf, modename);
3417 12782 : if (argname && argname[0])
3418 4622 : appendStringInfo(buf, "%s ", quote_identifier(argname));
3419 12782 : appendStringInfoString(buf, format_type_be(argtype));
3420 12782 : if (print_defaults && isinput && inputargno > nlackdefaults)
3421 : {
3422 : Node *expr;
3423 :
3424 : Assert(nextargdefault != NULL);
3425 70 : expr = (Node *) lfirst(nextargdefault);
3426 70 : nextargdefault = lnext(argdefaults, nextargdefault);
3427 :
3428 70 : appendStringInfo(buf, " DEFAULT %s",
3429 : deparse_expression(expr, NIL, false, false));
3430 : }
3431 12782 : argsprinted++;
3432 :
3433 : /* nasty hack: print the last arg twice for variadic ordered-set agg */
3434 12782 : if (argsprinted == insertorderbyat && i == numargs - 1)
3435 : {
3436 38 : i--;
3437 : /* aggs shouldn't have defaults anyway, but just to be sure ... */
3438 38 : print_defaults = false;
3439 : }
3440 : }
3441 :
3442 13520 : return argsprinted;
3443 : }
3444 :
3445 : static bool
3446 96 : is_input_argument(int nth, const char *argmodes)
3447 : {
3448 : return (!argmodes
3449 42 : || argmodes[nth] == PROARGMODE_IN
3450 18 : || argmodes[nth] == PROARGMODE_INOUT
3451 138 : || argmodes[nth] == PROARGMODE_VARIADIC);
3452 : }
3453 :
3454 : /*
3455 : * Append used transformed types to specified buffer
3456 : */
3457 : static void
3458 166 : print_function_trftypes(StringInfo buf, HeapTuple proctup)
3459 : {
3460 : Oid *trftypes;
3461 : int ntypes;
3462 :
3463 166 : ntypes = get_func_trftypes(proctup, &trftypes);
3464 166 : if (ntypes > 0)
3465 : {
3466 : int i;
3467 :
3468 6 : appendStringInfoString(buf, " TRANSFORM ");
3469 16 : for (i = 0; i < ntypes; i++)
3470 : {
3471 10 : if (i != 0)
3472 4 : appendStringInfoString(buf, ", ");
3473 10 : appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3474 : }
3475 6 : appendStringInfoChar(buf, '\n');
3476 : }
3477 166 : }
3478 :
3479 : /*
3480 : * Get textual representation of a function argument's default value. The
3481 : * second argument of this function is the argument number among all arguments
3482 : * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
3483 : * how information_schema.sql uses it.
3484 : */
3485 : Datum
3486 54 : pg_get_function_arg_default(PG_FUNCTION_ARGS)
3487 : {
3488 54 : Oid funcid = PG_GETARG_OID(0);
3489 54 : int32 nth_arg = PG_GETARG_INT32(1);
3490 : HeapTuple proctup;
3491 : Form_pg_proc proc;
3492 : int numargs;
3493 : Oid *argtypes;
3494 : char **argnames;
3495 : char *argmodes;
3496 : int i;
3497 : List *argdefaults;
3498 : Node *node;
3499 : char *str;
3500 : int nth_inputarg;
3501 : Datum proargdefaults;
3502 : bool isnull;
3503 : int nth_default;
3504 :
3505 54 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3506 54 : if (!HeapTupleIsValid(proctup))
3507 12 : PG_RETURN_NULL();
3508 :
3509 42 : numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3510 42 : if (nth_arg < 1 || nth_arg > numargs || !is_input_argument(nth_arg - 1, argmodes))
3511 : {
3512 12 : ReleaseSysCache(proctup);
3513 12 : PG_RETURN_NULL();
3514 : }
3515 :
3516 30 : nth_inputarg = 0;
3517 84 : for (i = 0; i < nth_arg; i++)
3518 54 : if (is_input_argument(i, argmodes))
3519 48 : nth_inputarg++;
3520 :
3521 30 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3522 : Anum_pg_proc_proargdefaults,
3523 : &isnull);
3524 30 : if (isnull)
3525 : {
3526 0 : ReleaseSysCache(proctup);
3527 0 : PG_RETURN_NULL();
3528 : }
3529 :
3530 30 : str = TextDatumGetCString(proargdefaults);
3531 30 : argdefaults = castNode(List, stringToNode(str));
3532 30 : pfree(str);
3533 :
3534 30 : proc = (Form_pg_proc) GETSTRUCT(proctup);
3535 :
3536 : /*
3537 : * Calculate index into proargdefaults: proargdefaults corresponds to the
3538 : * last N input arguments, where N = pronargdefaults.
3539 : */
3540 30 : nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3541 :
3542 30 : if (nth_default < 0 || nth_default >= list_length(argdefaults))
3543 : {
3544 6 : ReleaseSysCache(proctup);
3545 6 : PG_RETURN_NULL();
3546 : }
3547 24 : node = list_nth(argdefaults, nth_default);
3548 24 : str = deparse_expression(node, NIL, false, false);
3549 :
3550 24 : ReleaseSysCache(proctup);
3551 :
3552 24 : PG_RETURN_TEXT_P(string_to_text(str));
3553 : }
3554 :
3555 : static void
3556 236 : print_function_sqlbody(StringInfo buf, HeapTuple proctup)
3557 : {
3558 : int numargs;
3559 : Oid *argtypes;
3560 : char **argnames;
3561 : char *argmodes;
3562 236 : deparse_namespace dpns = {0};
3563 : Datum tmp;
3564 : Node *n;
3565 :
3566 236 : dpns.funcname = pstrdup(NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname));
3567 236 : numargs = get_func_arg_info(proctup,
3568 : &argtypes, &argnames, &argmodes);
3569 236 : dpns.numargs = numargs;
3570 236 : dpns.argnames = argnames;
3571 :
3572 236 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosqlbody);
3573 236 : n = stringToNode(TextDatumGetCString(tmp));
3574 :
3575 236 : if (IsA(n, List))
3576 : {
3577 : List *stmts;
3578 : ListCell *lc;
3579 :
3580 178 : stmts = linitial(castNode(List, n));
3581 :
3582 178 : appendStringInfoString(buf, "BEGIN ATOMIC\n");
3583 :
3584 340 : foreach(lc, stmts)
3585 : {
3586 162 : Query *query = lfirst_node(Query, lc);
3587 :
3588 : /* It seems advisable to get at least AccessShareLock on rels */
3589 162 : AcquireRewriteLocks(query, false, false);
3590 162 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3591 : PRETTYFLAG_INDENT, WRAP_COLUMN_DEFAULT, 1);
3592 162 : appendStringInfoChar(buf, ';');
3593 162 : appendStringInfoChar(buf, '\n');
3594 : }
3595 :
3596 178 : appendStringInfoString(buf, "END");
3597 : }
3598 : else
3599 : {
3600 58 : Query *query = castNode(Query, n);
3601 :
3602 : /* It seems advisable to get at least AccessShareLock on rels */
3603 58 : AcquireRewriteLocks(query, false, false);
3604 58 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3605 : 0, WRAP_COLUMN_DEFAULT, 0);
3606 : }
3607 236 : }
3608 :
3609 : Datum
3610 5252 : pg_get_function_sqlbody(PG_FUNCTION_ARGS)
3611 : {
3612 5252 : Oid funcid = PG_GETARG_OID(0);
3613 : StringInfoData buf;
3614 : HeapTuple proctup;
3615 : bool isnull;
3616 :
3617 5252 : initStringInfo(&buf);
3618 :
3619 : /* Look up the function */
3620 5252 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3621 5252 : if (!HeapTupleIsValid(proctup))
3622 0 : PG_RETURN_NULL();
3623 :
3624 5252 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3625 5252 : if (isnull)
3626 : {
3627 5130 : ReleaseSysCache(proctup);
3628 5130 : PG_RETURN_NULL();
3629 : }
3630 :
3631 122 : print_function_sqlbody(&buf, proctup);
3632 :
3633 122 : ReleaseSysCache(proctup);
3634 :
3635 122 : PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
3636 : }
3637 :
3638 :
3639 : /*
3640 : * deparse_expression - General utility for deparsing expressions
3641 : *
3642 : * calls deparse_expression_pretty with all prettyPrinting disabled
3643 : */
3644 : char *
3645 70988 : deparse_expression(Node *expr, List *dpcontext,
3646 : bool forceprefix, bool showimplicit)
3647 : {
3648 70988 : return deparse_expression_pretty(expr, dpcontext, forceprefix,
3649 : showimplicit, 0, 0);
3650 : }
3651 :
3652 : /* ----------
3653 : * deparse_expression_pretty - General utility for deparsing expressions
3654 : *
3655 : * expr is the node tree to be deparsed. It must be a transformed expression
3656 : * tree (ie, not the raw output of gram.y).
3657 : *
3658 : * dpcontext is a list of deparse_namespace nodes representing the context
3659 : * for interpreting Vars in the node tree. It can be NIL if no Vars are
3660 : * expected.
3661 : *
3662 : * forceprefix is true to force all Vars to be prefixed with their table names.
3663 : *
3664 : * showimplicit is true to force all implicit casts to be shown explicitly.
3665 : *
3666 : * Tries to pretty up the output according to prettyFlags and startIndent.
3667 : *
3668 : * The result is a palloc'd string.
3669 : * ----------
3670 : */
3671 : static char *
3672 86902 : deparse_expression_pretty(Node *expr, List *dpcontext,
3673 : bool forceprefix, bool showimplicit,
3674 : int prettyFlags, int startIndent)
3675 : {
3676 : StringInfoData buf;
3677 : deparse_context context;
3678 :
3679 86902 : initStringInfo(&buf);
3680 86902 : context.buf = &buf;
3681 86902 : context.namespaces = dpcontext;
3682 86902 : context.resultDesc = NULL;
3683 86902 : context.targetList = NIL;
3684 86902 : context.windowClause = NIL;
3685 86902 : context.varprefix = forceprefix;
3686 86902 : context.prettyFlags = prettyFlags;
3687 86902 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
3688 86902 : context.indentLevel = startIndent;
3689 86902 : context.colNamesVisible = true;
3690 86902 : context.inGroupBy = false;
3691 86902 : context.varInOrderBy = false;
3692 86902 : context.appendparents = NULL;
3693 :
3694 86902 : get_rule_expr(expr, &context, showimplicit);
3695 :
3696 86902 : return buf.data;
3697 : }
3698 :
3699 : /* ----------
3700 : * deparse_context_for - Build deparse context for a single relation
3701 : *
3702 : * Given the reference name (alias) and OID of a relation, build deparsing
3703 : * context for an expression referencing only that relation (as varno 1,
3704 : * varlevelsup 0). This is sufficient for many uses of deparse_expression.
3705 : * ----------
3706 : */
3707 : List *
3708 28382 : deparse_context_for(const char *aliasname, Oid relid)
3709 : {
3710 : deparse_namespace *dpns;
3711 : RangeTblEntry *rte;
3712 :
3713 28382 : dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
3714 :
3715 : /* Build a minimal RTE for the rel */
3716 28382 : rte = makeNode(RangeTblEntry);
3717 28382 : rte->rtekind = RTE_RELATION;
3718 28382 : rte->relid = relid;
3719 28382 : rte->relkind = RELKIND_RELATION; /* no need for exactness here */
3720 28382 : rte->rellockmode = AccessShareLock;
3721 28382 : rte->alias = makeAlias(aliasname, NIL);
3722 28382 : rte->eref = rte->alias;
3723 28382 : rte->lateral = false;
3724 28382 : rte->inh = false;
3725 28382 : rte->inFromCl = true;
3726 :
3727 : /* Build one-element rtable */
3728 28382 : dpns->rtable = list_make1(rte);
3729 28382 : dpns->subplans = NIL;
3730 28382 : dpns->ctes = NIL;
3731 28382 : dpns->appendrels = NULL;
3732 28382 : set_rtable_names(dpns, NIL, NULL);
3733 28382 : set_simple_column_names(dpns);
3734 :
3735 : /* Return a one-deep namespace stack */
3736 28382 : return list_make1(dpns);
3737 : }
3738 :
3739 : /*
3740 : * deparse_context_for_plan_tree - Build deparse context for a Plan tree
3741 : *
3742 : * When deparsing an expression in a Plan tree, we use the plan's rangetable
3743 : * to resolve names of simple Vars. The initialization of column names for
3744 : * this is rather expensive if the rangetable is large, and it'll be the same
3745 : * for every expression in the Plan tree; so we do it just once and re-use
3746 : * the result of this function for each expression. (Note that the result
3747 : * is not usable until set_deparse_context_plan() is applied to it.)
3748 : *
3749 : * In addition to the PlannedStmt, pass the per-RTE alias names
3750 : * assigned by a previous call to select_rtable_names_for_explain.
3751 : */
3752 : List *
3753 23676 : deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
3754 : {
3755 : deparse_namespace *dpns;
3756 :
3757 23676 : dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
3758 :
3759 : /* Initialize fields that stay the same across the whole plan tree */
3760 23676 : dpns->rtable = pstmt->rtable;
3761 23676 : dpns->rtable_names = rtable_names;
3762 23676 : dpns->subplans = pstmt->subplans;
3763 23676 : dpns->ctes = NIL;
3764 23676 : if (pstmt->appendRelations)
3765 : {
3766 : /* Set up the array, indexed by child relid */
3767 3802 : int ntables = list_length(dpns->rtable);
3768 : ListCell *lc;
3769 :
3770 3802 : dpns->appendrels = (AppendRelInfo **)
3771 3802 : palloc0((ntables + 1) * sizeof(AppendRelInfo *));
3772 20694 : foreach(lc, pstmt->appendRelations)
3773 : {
3774 16892 : AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
3775 16892 : Index crelid = appinfo->child_relid;
3776 :
3777 : Assert(crelid > 0 && crelid <= ntables);
3778 : Assert(dpns->appendrels[crelid] == NULL);
3779 16892 : dpns->appendrels[crelid] = appinfo;
3780 : }
3781 : }
3782 : else
3783 19874 : dpns->appendrels = NULL; /* don't need it */
3784 :
3785 : /*
3786 : * Set up column name aliases, ignoring any join RTEs; they don't matter
3787 : * because plan trees don't contain any join alias Vars.
3788 : */
3789 23676 : set_simple_column_names(dpns);
3790 :
3791 : /* Return a one-deep namespace stack */
3792 23676 : return list_make1(dpns);
3793 : }
3794 :
3795 : /*
3796 : * set_deparse_context_plan - Specify Plan node containing expression
3797 : *
3798 : * When deparsing an expression in a Plan tree, we might have to resolve
3799 : * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
3800 : * provide the parent Plan node. Then OUTER_VAR and INNER_VAR references
3801 : * can be resolved by drilling down into the left and right child plans.
3802 : * Similarly, INDEX_VAR references can be resolved by reference to the
3803 : * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
3804 : * ForeignScan and CustomScan nodes. (Note that we don't currently support
3805 : * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
3806 : * for those, we can only deparse the indexqualorig fields, which won't
3807 : * contain INDEX_VAR Vars.)
3808 : *
3809 : * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
3810 : * the most-closely-nested first. This is needed to resolve PARAM_EXEC
3811 : * Params. Note we assume that all the Plan nodes share the same rtable.
3812 : *
3813 : * For a ModifyTable plan, we might also need to resolve references to OLD/NEW
3814 : * variables in the RETURNING list, so we copy the alias names of the OLD and
3815 : * NEW rows from the ModifyTable plan node.
3816 : *
3817 : * Once this function has been called, deparse_expression() can be called on
3818 : * subsidiary expression(s) of the specified Plan node. To deparse
3819 : * expressions of a different Plan node in the same Plan tree, re-call this
3820 : * function to identify the new parent Plan node.
3821 : *
3822 : * The result is the same List passed in; this is a notational convenience.
3823 : */
3824 : List *
3825 52468 : set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
3826 : {
3827 : deparse_namespace *dpns;
3828 :
3829 : /* Should always have one-entry namespace list for Plan deparsing */
3830 : Assert(list_length(dpcontext) == 1);
3831 52468 : dpns = (deparse_namespace *) linitial(dpcontext);
3832 :
3833 : /* Set our attention on the specific plan node passed in */
3834 52468 : dpns->ancestors = ancestors;
3835 52468 : set_deparse_plan(dpns, plan);
3836 :
3837 : /* For ModifyTable, set aliases for OLD and NEW in RETURNING */
3838 52468 : if (IsA(plan, ModifyTable))
3839 : {
3840 210 : dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias;
3841 210 : dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias;
3842 : }
3843 :
3844 52468 : return dpcontext;
3845 : }
3846 :
3847 : /*
3848 : * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
3849 : *
3850 : * Determine the relation aliases we'll use during an EXPLAIN operation.
3851 : * This is just a frontend to set_rtable_names. We have to expose the aliases
3852 : * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
3853 : */
3854 : List *
3855 23676 : select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
3856 : {
3857 : deparse_namespace dpns;
3858 :
3859 23676 : memset(&dpns, 0, sizeof(dpns));
3860 23676 : dpns.rtable = rtable;
3861 23676 : dpns.subplans = NIL;
3862 23676 : dpns.ctes = NIL;
3863 23676 : dpns.appendrels = NULL;
3864 23676 : set_rtable_names(&dpns, NIL, rels_used);
3865 : /* We needn't bother computing column aliases yet */
3866 :
3867 23676 : return dpns.rtable_names;
3868 : }
3869 :
3870 : /*
3871 : * set_rtable_names: select RTE aliases to be used in printing a query
3872 : *
3873 : * We fill in dpns->rtable_names with a list of names that is one-for-one with
3874 : * the already-filled dpns->rtable list. Each RTE name is unique among those
3875 : * in the new namespace plus any ancestor namespaces listed in
3876 : * parent_namespaces.
3877 : *
3878 : * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
3879 : *
3880 : * Note that this function is only concerned with relation names, not column
3881 : * names.
3882 : */
3883 : static void
3884 58968 : set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
3885 : Bitmapset *rels_used)
3886 : {
3887 : HASHCTL hash_ctl;
3888 : HTAB *names_hash;
3889 : NameHashEntry *hentry;
3890 : bool found;
3891 : int rtindex;
3892 : ListCell *lc;
3893 :
3894 58968 : dpns->rtable_names = NIL;
3895 : /* nothing more to do if empty rtable */
3896 58968 : if (dpns->rtable == NIL)
3897 646 : return;
3898 :
3899 : /*
3900 : * We use a hash table to hold known names, so that this process is O(N)
3901 : * not O(N^2) for N names.
3902 : */
3903 58322 : hash_ctl.keysize = NAMEDATALEN;
3904 58322 : hash_ctl.entrysize = sizeof(NameHashEntry);
3905 58322 : hash_ctl.hcxt = CurrentMemoryContext;
3906 58322 : names_hash = hash_create("set_rtable_names names",
3907 58322 : list_length(dpns->rtable),
3908 : &hash_ctl,
3909 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
3910 :
3911 : /* Preload the hash table with names appearing in parent_namespaces */
3912 60186 : foreach(lc, parent_namespaces)
3913 : {
3914 1864 : deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
3915 : ListCell *lc2;
3916 :
3917 6618 : foreach(lc2, olddpns->rtable_names)
3918 : {
3919 4754 : char *oldname = (char *) lfirst(lc2);
3920 :
3921 4754 : if (oldname == NULL)
3922 336 : continue;
3923 4418 : hentry = (NameHashEntry *) hash_search(names_hash,
3924 : oldname,
3925 : HASH_ENTER,
3926 : &found);
3927 : /* we do not complain about duplicate names in parent namespaces */
3928 4418 : hentry->counter = 0;
3929 : }
3930 : }
3931 :
3932 : /* Now we can scan the rtable */
3933 58322 : rtindex = 1;
3934 163294 : foreach(lc, dpns->rtable)
3935 : {
3936 104972 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
3937 : char *refname;
3938 :
3939 : /* Just in case this takes an unreasonable amount of time ... */
3940 104972 : CHECK_FOR_INTERRUPTS();
3941 :
3942 104972 : if (rels_used && !bms_is_member(rtindex, rels_used))
3943 : {
3944 : /* Ignore unreferenced RTE */
3945 17738 : refname = NULL;
3946 : }
3947 87234 : else if (rte->alias)
3948 : {
3949 : /* If RTE has a user-defined alias, prefer that */
3950 59994 : refname = rte->alias->aliasname;
3951 : }
3952 27240 : else if (rte->rtekind == RTE_RELATION)
3953 : {
3954 : /* Use the current actual name of the relation */
3955 22410 : refname = get_rel_name(rte->relid);
3956 : }
3957 4830 : else if (rte->rtekind == RTE_JOIN)
3958 : {
3959 : /* Unnamed join has no refname */
3960 1866 : refname = NULL;
3961 : }
3962 : else
3963 : {
3964 : /* Otherwise use whatever the parser assigned */
3965 2964 : refname = rte->eref->aliasname;
3966 : }
3967 :
3968 : /*
3969 : * If the selected name isn't unique, append digits to make it so, and
3970 : * make a new hash entry for it once we've got a unique name. For a
3971 : * very long input name, we might have to truncate to stay within
3972 : * NAMEDATALEN.
3973 : */
3974 104972 : if (refname)
3975 : {
3976 85368 : hentry = (NameHashEntry *) hash_search(names_hash,
3977 : refname,
3978 : HASH_ENTER,
3979 : &found);
3980 85368 : if (found)
3981 : {
3982 : /* Name already in use, must choose a new one */
3983 13922 : int refnamelen = strlen(refname);
3984 13922 : char *modname = (char *) palloc(refnamelen + 16);
3985 : NameHashEntry *hentry2;
3986 :
3987 : do
3988 : {
3989 13928 : hentry->counter++;
3990 : for (;;)
3991 : {
3992 13940 : memcpy(modname, refname, refnamelen);
3993 13940 : sprintf(modname + refnamelen, "_%d", hentry->counter);
3994 13940 : if (strlen(modname) < NAMEDATALEN)
3995 13928 : break;
3996 : /* drop chars from refname to keep all the digits */
3997 12 : refnamelen = pg_mbcliplen(refname, refnamelen,
3998 : refnamelen - 1);
3999 : }
4000 13928 : hentry2 = (NameHashEntry *) hash_search(names_hash,
4001 : modname,
4002 : HASH_ENTER,
4003 : &found);
4004 13928 : } while (found);
4005 13922 : hentry2->counter = 0; /* init new hash entry */
4006 13922 : refname = modname;
4007 : }
4008 : else
4009 : {
4010 : /* Name not previously used, need only initialize hentry */
4011 71446 : hentry->counter = 0;
4012 : }
4013 : }
4014 :
4015 104972 : dpns->rtable_names = lappend(dpns->rtable_names, refname);
4016 104972 : rtindex++;
4017 : }
4018 :
4019 58322 : hash_destroy(names_hash);
4020 : }
4021 :
4022 : /*
4023 : * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
4024 : *
4025 : * For convenience, this is defined to initialize the deparse_namespace struct
4026 : * from scratch.
4027 : */
4028 : static void
4029 6738 : set_deparse_for_query(deparse_namespace *dpns, Query *query,
4030 : List *parent_namespaces)
4031 : {
4032 : ListCell *lc;
4033 : ListCell *lc2;
4034 :
4035 : /* Initialize *dpns and fill rtable/ctes links */
4036 6738 : memset(dpns, 0, sizeof(deparse_namespace));
4037 6738 : dpns->rtable = query->rtable;
4038 6738 : dpns->subplans = NIL;
4039 6738 : dpns->ctes = query->cteList;
4040 6738 : dpns->appendrels = NULL;
4041 6738 : dpns->ret_old_alias = query->returningOldAlias;
4042 6738 : dpns->ret_new_alias = query->returningNewAlias;
4043 :
4044 : /* Assign a unique relation alias to each RTE */
4045 6738 : set_rtable_names(dpns, parent_namespaces, NULL);
4046 :
4047 : /* Initialize dpns->rtable_columns to contain zeroed structs */
4048 6738 : dpns->rtable_columns = NIL;
4049 18808 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4050 12070 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4051 : palloc0(sizeof(deparse_columns)));
4052 :
4053 : /* If it's a utility query, it won't have a jointree */
4054 6738 : if (query->jointree)
4055 : {
4056 : /* Detect whether global uniqueness of USING names is needed */
4057 6716 : dpns->unique_using =
4058 6716 : has_dangerous_join_using(dpns, (Node *) query->jointree);
4059 :
4060 : /*
4061 : * Select names for columns merged by USING, via a recursive pass over
4062 : * the query jointree.
4063 : */
4064 6716 : set_using_names(dpns, (Node *) query->jointree, NIL);
4065 : }
4066 :
4067 : /*
4068 : * Now assign remaining column aliases for each RTE. We do this in a
4069 : * linear scan of the rtable, so as to process RTEs whether or not they
4070 : * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4071 : * etc). JOIN RTEs must be processed after their children, but this is
4072 : * okay because they appear later in the rtable list than their children
4073 : * (cf Asserts in identify_join_columns()).
4074 : */
4075 18808 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4076 : {
4077 12070 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4078 12070 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4079 :
4080 12070 : if (rte->rtekind == RTE_JOIN)
4081 1522 : set_join_column_names(dpns, rte, colinfo);
4082 : else
4083 10548 : set_relation_column_names(dpns, rte, colinfo);
4084 : }
4085 6738 : }
4086 :
4087 : /*
4088 : * set_simple_column_names: fill in column aliases for non-query situations
4089 : *
4090 : * This handles EXPLAIN and cases where we only have relation RTEs. Without
4091 : * a join tree, we can't do anything smart about join RTEs, but we don't
4092 : * need to, because EXPLAIN should never see join alias Vars anyway.
4093 : * If we find a join RTE we'll just skip it, leaving its deparse_columns
4094 : * struct all-zero. If somehow we try to deparse a join alias Var, we'll
4095 : * error out cleanly because the struct's num_cols will be zero.
4096 : */
4097 : static void
4098 52230 : set_simple_column_names(deparse_namespace *dpns)
4099 : {
4100 : ListCell *lc;
4101 : ListCell *lc2;
4102 :
4103 : /* Initialize dpns->rtable_columns to contain zeroed structs */
4104 52230 : dpns->rtable_columns = NIL;
4105 145132 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4106 92902 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4107 : palloc0(sizeof(deparse_columns)));
4108 :
4109 : /* Assign unique column aliases within each non-join RTE */
4110 145132 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4111 : {
4112 92902 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4113 92902 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4114 :
4115 92902 : if (rte->rtekind != RTE_JOIN)
4116 87254 : set_relation_column_names(dpns, rte, colinfo);
4117 : }
4118 52230 : }
4119 :
4120 : /*
4121 : * has_dangerous_join_using: search jointree for unnamed JOIN USING
4122 : *
4123 : * Merged columns of a JOIN USING may act differently from either of the input
4124 : * columns, either because they are merged with COALESCE (in a FULL JOIN) or
4125 : * because an implicit coercion of the underlying input column is required.
4126 : * In such a case the column must be referenced as a column of the JOIN not as
4127 : * a column of either input. And this is problematic if the join is unnamed
4128 : * (alias-less): we cannot qualify the column's name with an RTE name, since
4129 : * there is none. (Forcibly assigning an alias to the join is not a solution,
4130 : * since that will prevent legal references to tables below the join.)
4131 : * To ensure that every column in the query is unambiguously referenceable,
4132 : * we must assign such merged columns names that are globally unique across
4133 : * the whole query, aliasing other columns out of the way as necessary.
4134 : *
4135 : * Because the ensuing re-aliasing is fairly damaging to the readability of
4136 : * the query, we don't do this unless we have to. So, we must pre-scan
4137 : * the join tree to see if we have to, before starting set_using_names().
4138 : */
4139 : static bool
4140 15528 : has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
4141 : {
4142 15528 : if (IsA(jtnode, RangeTblRef))
4143 : {
4144 : /* nothing to do here */
4145 : }
4146 8172 : else if (IsA(jtnode, FromExpr))
4147 : {
4148 6716 : FromExpr *f = (FromExpr *) jtnode;
4149 : ListCell *lc;
4150 :
4151 12688 : foreach(lc, f->fromlist)
4152 : {
4153 6044 : if (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))
4154 72 : return true;
4155 : }
4156 : }
4157 1456 : else if (IsA(jtnode, JoinExpr))
4158 : {
4159 1456 : JoinExpr *j = (JoinExpr *) jtnode;
4160 :
4161 : /* Is it an unnamed JOIN with USING? */
4162 1456 : if (j->alias == NULL && j->usingClause)
4163 : {
4164 : /*
4165 : * Yes, so check each join alias var to see if any of them are not
4166 : * simple references to underlying columns. If so, we have a
4167 : * dangerous situation and must pick unique aliases.
4168 : */
4169 310 : RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4170 :
4171 : /* We need only examine the merged columns */
4172 644 : for (int i = 0; i < jrte->joinmergedcols; i++)
4173 : {
4174 406 : Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4175 :
4176 406 : if (!IsA(aliasvar, Var))
4177 72 : return true;
4178 : }
4179 : }
4180 :
4181 : /* Nope, but inspect children */
4182 1384 : if (has_dangerous_join_using(dpns, j->larg))
4183 0 : return true;
4184 1384 : if (has_dangerous_join_using(dpns, j->rarg))
4185 0 : return true;
4186 : }
4187 : else
4188 0 : elog(ERROR, "unrecognized node type: %d",
4189 : (int) nodeTag(jtnode));
4190 15384 : return false;
4191 : }
4192 :
4193 : /*
4194 : * set_using_names: select column aliases to be used for merged USING columns
4195 : *
4196 : * We do this during a recursive descent of the query jointree.
4197 : * dpns->unique_using must already be set to determine the global strategy.
4198 : *
4199 : * Column alias info is saved in the dpns->rtable_columns list, which is
4200 : * assumed to be filled with pre-zeroed deparse_columns structs.
4201 : *
4202 : * parentUsing is a list of all USING aliases assigned in parent joins of
4203 : * the current jointree node. (The passed-in list must not be modified.)
4204 : *
4205 : * Note that we do not use per-deparse_columns hash tables in this function.
4206 : * The number of names that need to be assigned should be small enough that
4207 : * we don't need to trouble with that.
4208 : */
4209 : static void
4210 15846 : set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
4211 : {
4212 15846 : if (IsA(jtnode, RangeTblRef))
4213 : {
4214 : /* nothing to do now */
4215 : }
4216 8238 : else if (IsA(jtnode, FromExpr))
4217 : {
4218 6716 : FromExpr *f = (FromExpr *) jtnode;
4219 : ListCell *lc;
4220 :
4221 12802 : foreach(lc, f->fromlist)
4222 6086 : set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4223 : }
4224 1522 : else if (IsA(jtnode, JoinExpr))
4225 : {
4226 1522 : JoinExpr *j = (JoinExpr *) jtnode;
4227 1522 : RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4228 1522 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
4229 : int *leftattnos;
4230 : int *rightattnos;
4231 : deparse_columns *leftcolinfo;
4232 : deparse_columns *rightcolinfo;
4233 : int i;
4234 : ListCell *lc;
4235 :
4236 : /* Get info about the shape of the join */
4237 1522 : identify_join_columns(j, rte, colinfo);
4238 1522 : leftattnos = colinfo->leftattnos;
4239 1522 : rightattnos = colinfo->rightattnos;
4240 :
4241 : /* Look up the not-yet-filled-in child deparse_columns structs */
4242 1522 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4243 1522 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4244 :
4245 : /*
4246 : * If this join is unnamed, then we cannot substitute new aliases at
4247 : * this level, so any name requirements pushed down to here must be
4248 : * pushed down again to the children.
4249 : */
4250 1522 : if (rte->alias == NULL)
4251 : {
4252 1552 : for (i = 0; i < colinfo->num_cols; i++)
4253 : {
4254 138 : char *colname = colinfo->colnames[i];
4255 :
4256 138 : if (colname == NULL)
4257 24 : continue;
4258 :
4259 : /* Push down to left column, unless it's a system column */
4260 114 : if (leftattnos[i] > 0)
4261 : {
4262 102 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4263 102 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4264 : }
4265 :
4266 : /* Same on the righthand side */
4267 114 : if (rightattnos[i] > 0)
4268 : {
4269 114 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4270 114 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4271 : }
4272 : }
4273 : }
4274 :
4275 : /*
4276 : * If there's a USING clause, select the USING column names and push
4277 : * those names down to the children. We have two strategies:
4278 : *
4279 : * If dpns->unique_using is true, we force all USING names to be
4280 : * unique across the whole query level. In principle we'd only need
4281 : * the names of dangerous USING columns to be globally unique, but to
4282 : * safely assign all USING names in a single pass, we have to enforce
4283 : * the same uniqueness rule for all of them. However, if a USING
4284 : * column's name has been pushed down from the parent, we should use
4285 : * it as-is rather than making a uniqueness adjustment. This is
4286 : * necessary when we're at an unnamed join, and it creates no risk of
4287 : * ambiguity. Also, if there's a user-written output alias for a
4288 : * merged column, we prefer to use that rather than the input name;
4289 : * this simplifies the logic and seems likely to lead to less aliasing
4290 : * overall.
4291 : *
4292 : * If dpns->unique_using is false, we only need USING names to be
4293 : * unique within their own join RTE. We still need to honor
4294 : * pushed-down names, though.
4295 : *
4296 : * Though significantly different in results, these two strategies are
4297 : * implemented by the same code, with only the difference of whether
4298 : * to put assigned names into dpns->using_names.
4299 : */
4300 1522 : if (j->usingClause)
4301 : {
4302 : /* Copy the input parentUsing list so we don't modify it */
4303 448 : parentUsing = list_copy(parentUsing);
4304 :
4305 : /* USING names must correspond to the first join output columns */
4306 448 : expand_colnames_array_to(colinfo, list_length(j->usingClause));
4307 448 : i = 0;
4308 1052 : foreach(lc, j->usingClause)
4309 : {
4310 604 : char *colname = strVal(lfirst(lc));
4311 :
4312 : /* Assert it's a merged column */
4313 : Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4314 :
4315 : /* Adopt passed-down name if any, else select unique name */
4316 604 : if (colinfo->colnames[i] != NULL)
4317 102 : colname = colinfo->colnames[i];
4318 : else
4319 : {
4320 : /* Prefer user-written output alias if any */
4321 502 : if (rte->alias && i < list_length(rte->alias->colnames))
4322 0 : colname = strVal(list_nth(rte->alias->colnames, i));
4323 : /* Make it appropriately unique */
4324 502 : colname = make_colname_unique(colname, dpns, colinfo);
4325 502 : if (dpns->unique_using)
4326 126 : dpns->using_names = lappend(dpns->using_names,
4327 : colname);
4328 : /* Save it as output column name, too */
4329 502 : colinfo->colnames[i] = colname;
4330 : }
4331 :
4332 : /* Remember selected names for use later */
4333 604 : colinfo->usingNames = lappend(colinfo->usingNames, colname);
4334 604 : parentUsing = lappend(parentUsing, colname);
4335 :
4336 : /* Push down to left column, unless it's a system column */
4337 604 : if (leftattnos[i] > 0)
4338 : {
4339 604 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4340 604 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4341 : }
4342 :
4343 : /* Same on the righthand side */
4344 604 : if (rightattnos[i] > 0)
4345 : {
4346 604 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4347 604 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4348 : }
4349 :
4350 604 : i++;
4351 : }
4352 : }
4353 :
4354 : /* Mark child deparse_columns structs with correct parentUsing info */
4355 1522 : leftcolinfo->parentUsing = parentUsing;
4356 1522 : rightcolinfo->parentUsing = parentUsing;
4357 :
4358 : /* Now recursively assign USING column names in children */
4359 1522 : set_using_names(dpns, j->larg, parentUsing);
4360 1522 : set_using_names(dpns, j->rarg, parentUsing);
4361 : }
4362 : else
4363 0 : elog(ERROR, "unrecognized node type: %d",
4364 : (int) nodeTag(jtnode));
4365 15846 : }
4366 :
4367 : /*
4368 : * set_relation_column_names: select column aliases for a non-join RTE
4369 : *
4370 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4371 : * If any colnames entries are already filled in, those override local
4372 : * choices.
4373 : */
4374 : static void
4375 97802 : set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4376 : deparse_columns *colinfo)
4377 : {
4378 : int ncolumns;
4379 : char **real_colnames;
4380 : bool changed_any;
4381 : int noldcolumns;
4382 : int i;
4383 : int j;
4384 :
4385 : /*
4386 : * Construct an array of the current "real" column names of the RTE.
4387 : * real_colnames[] will be indexed by physical column number, with NULL
4388 : * entries for dropped columns.
4389 : */
4390 97802 : if (rte->rtekind == RTE_RELATION)
4391 : {
4392 : /* Relation --- look to the system catalogs for up-to-date info */
4393 : Relation rel;
4394 : TupleDesc tupdesc;
4395 :
4396 84136 : rel = relation_open(rte->relid, AccessShareLock);
4397 84136 : tupdesc = RelationGetDescr(rel);
4398 :
4399 84136 : ncolumns = tupdesc->natts;
4400 84136 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4401 :
4402 524474 : for (i = 0; i < ncolumns; i++)
4403 : {
4404 440338 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4405 :
4406 440338 : if (attr->attisdropped)
4407 3578 : real_colnames[i] = NULL;
4408 : else
4409 436760 : real_colnames[i] = pstrdup(NameStr(attr->attname));
4410 : }
4411 84136 : relation_close(rel, AccessShareLock);
4412 : }
4413 : else
4414 : {
4415 : /* Otherwise get the column names from eref or expandRTE() */
4416 : List *colnames;
4417 : ListCell *lc;
4418 :
4419 : /*
4420 : * Functions returning composites have the annoying property that some
4421 : * of the composite type's columns might have been dropped since the
4422 : * query was parsed. If possible, use expandRTE() to handle that
4423 : * case, since it has the tedious logic needed to find out about
4424 : * dropped columns. However, if we're explaining a plan, then we
4425 : * don't have rte->functions because the planner thinks that won't be
4426 : * needed later, and that breaks expandRTE(). So in that case we have
4427 : * to rely on rte->eref, which may lead us to report a dropped
4428 : * column's old name; that seems close enough for EXPLAIN's purposes.
4429 : *
4430 : * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4431 : * which should be sufficiently up-to-date: no other RTE types can
4432 : * have columns get dropped from under them after parsing.
4433 : */
4434 13666 : if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4435 : {
4436 : /* Since we're not creating Vars, rtindex etc. don't matter */
4437 834 : expandRTE(rte, 1, 0, VAR_RETURNING_DEFAULT, -1,
4438 : true /* include dropped */ , &colnames, NULL);
4439 : }
4440 : else
4441 12832 : colnames = rte->eref->colnames;
4442 :
4443 13666 : ncolumns = list_length(colnames);
4444 13666 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4445 :
4446 13666 : i = 0;
4447 45558 : foreach(lc, colnames)
4448 : {
4449 : /*
4450 : * If the column name we find here is an empty string, then it's a
4451 : * dropped column, so change to NULL.
4452 : */
4453 31892 : char *cname = strVal(lfirst(lc));
4454 :
4455 31892 : if (cname[0] == '\0')
4456 54 : cname = NULL;
4457 31892 : real_colnames[i] = cname;
4458 31892 : i++;
4459 : }
4460 : }
4461 :
4462 : /*
4463 : * Ensure colinfo->colnames has a slot for each column. (It could be long
4464 : * enough already, if we pushed down a name for the last column.) Note:
4465 : * it's possible that there are now more columns than there were when the
4466 : * query was parsed, ie colnames could be longer than rte->eref->colnames.
4467 : * We must assign unique aliases to the new columns too, else there could
4468 : * be unresolved conflicts when the view/rule is reloaded.
4469 : */
4470 97802 : expand_colnames_array_to(colinfo, ncolumns);
4471 : Assert(colinfo->num_cols == ncolumns);
4472 :
4473 : /*
4474 : * Make sufficiently large new_colnames and is_new_col arrays, too.
4475 : *
4476 : * Note: because we leave colinfo->num_new_cols zero until after the loop,
4477 : * colname_is_unique will not consult that array, which is fine because it
4478 : * would only be duplicate effort.
4479 : */
4480 97802 : colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4481 97802 : colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4482 :
4483 : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4484 97802 : build_colinfo_names_hash(colinfo);
4485 :
4486 : /*
4487 : * Scan the columns, select a unique alias for each one, and store it in
4488 : * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4489 : * entries for dropped columns, the latter omits them. Also mark
4490 : * new_colnames entries as to whether they are new since parse time; this
4491 : * is the case for entries beyond the length of rte->eref->colnames.
4492 : */
4493 97802 : noldcolumns = list_length(rte->eref->colnames);
4494 97802 : changed_any = false;
4495 97802 : j = 0;
4496 570032 : for (i = 0; i < ncolumns; i++)
4497 : {
4498 472230 : char *real_colname = real_colnames[i];
4499 472230 : char *colname = colinfo->colnames[i];
4500 :
4501 : /* Skip dropped columns */
4502 472230 : if (real_colname == NULL)
4503 : {
4504 : Assert(colname == NULL); /* colnames[i] is already NULL */
4505 3632 : continue;
4506 : }
4507 :
4508 : /* If alias already assigned, that's what to use */
4509 468598 : if (colname == NULL)
4510 : {
4511 : /* If user wrote an alias, prefer that over real column name */
4512 467492 : if (rte->alias && i < list_length(rte->alias->colnames))
4513 42916 : colname = strVal(list_nth(rte->alias->colnames, i));
4514 : else
4515 424576 : colname = real_colname;
4516 :
4517 : /* Unique-ify and insert into colinfo */
4518 467492 : colname = make_colname_unique(colname, dpns, colinfo);
4519 :
4520 467492 : colinfo->colnames[i] = colname;
4521 467492 : add_to_names_hash(colinfo, colname);
4522 : }
4523 :
4524 : /* Put names of non-dropped columns in new_colnames[] too */
4525 468598 : colinfo->new_colnames[j] = colname;
4526 : /* And mark them as new or not */
4527 468598 : colinfo->is_new_col[j] = (i >= noldcolumns);
4528 468598 : j++;
4529 :
4530 : /* Remember if any assigned aliases differ from "real" name */
4531 468598 : if (!changed_any && strcmp(colname, real_colname) != 0)
4532 1162 : changed_any = true;
4533 : }
4534 :
4535 : /* We're now done needing the colinfo's names_hash */
4536 97802 : destroy_colinfo_names_hash(colinfo);
4537 :
4538 : /*
4539 : * Set correct length for new_colnames[] array. (Note: if columns have
4540 : * been added, colinfo->num_cols includes them, which is not really quite
4541 : * right but is harmless, since any new columns must be at the end where
4542 : * they won't affect varattnos of pre-existing columns.)
4543 : */
4544 97802 : colinfo->num_new_cols = j;
4545 :
4546 : /*
4547 : * For a relation RTE, we need only print the alias column names if any
4548 : * are different from the underlying "real" names. For a function RTE,
4549 : * always emit a complete column alias list; this is to protect against
4550 : * possible instability of the default column names (eg, from altering
4551 : * parameter names). For tablefunc RTEs, we never print aliases, because
4552 : * the column names are part of the clause itself. For other RTE types,
4553 : * print if we changed anything OR if there were user-written column
4554 : * aliases (since the latter would be part of the underlying "reality").
4555 : */
4556 97802 : if (rte->rtekind == RTE_RELATION)
4557 84136 : colinfo->printaliases = changed_any;
4558 13666 : else if (rte->rtekind == RTE_FUNCTION)
4559 1412 : colinfo->printaliases = true;
4560 12254 : else if (rte->rtekind == RTE_TABLEFUNC)
4561 188 : colinfo->printaliases = false;
4562 12066 : else if (rte->alias && rte->alias->colnames != NIL)
4563 810 : colinfo->printaliases = true;
4564 : else
4565 11256 : colinfo->printaliases = changed_any;
4566 97802 : }
4567 :
4568 : /*
4569 : * set_join_column_names: select column aliases for a join RTE
4570 : *
4571 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4572 : * If any colnames entries are already filled in, those override local
4573 : * choices. Also, names for USING columns were already chosen by
4574 : * set_using_names(). We further expect that column alias selection has been
4575 : * completed for both input RTEs.
4576 : */
4577 : static void
4578 1522 : set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4579 : deparse_columns *colinfo)
4580 : {
4581 : deparse_columns *leftcolinfo;
4582 : deparse_columns *rightcolinfo;
4583 : bool changed_any;
4584 : int noldcolumns;
4585 : int nnewcolumns;
4586 1522 : Bitmapset *leftmerged = NULL;
4587 1522 : Bitmapset *rightmerged = NULL;
4588 : int i;
4589 : int j;
4590 : int ic;
4591 : int jc;
4592 :
4593 : /* Look up the previously-filled-in child deparse_columns structs */
4594 1522 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4595 1522 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4596 :
4597 : /*
4598 : * Ensure colinfo->colnames has a slot for each column. (It could be long
4599 : * enough already, if we pushed down a name for the last column.) Note:
4600 : * it's possible that one or both inputs now have more columns than there
4601 : * were when the query was parsed, but we'll deal with that below. We
4602 : * only need entries in colnames for pre-existing columns.
4603 : */
4604 1522 : noldcolumns = list_length(rte->eref->colnames);
4605 1522 : expand_colnames_array_to(colinfo, noldcolumns);
4606 : Assert(colinfo->num_cols == noldcolumns);
4607 :
4608 : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4609 1522 : build_colinfo_names_hash(colinfo);
4610 :
4611 : /*
4612 : * Scan the join output columns, select an alias for each one, and store
4613 : * it in colinfo->colnames. If there are USING columns, set_using_names()
4614 : * already selected their names, so we can start the loop at the first
4615 : * non-merged column.
4616 : */
4617 1522 : changed_any = false;
4618 49936 : for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4619 : {
4620 48414 : char *colname = colinfo->colnames[i];
4621 : char *real_colname;
4622 :
4623 : /* Join column must refer to at least one input column */
4624 : Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4625 :
4626 : /* Get the child column name */
4627 48414 : if (colinfo->leftattnos[i] > 0)
4628 34102 : real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4629 14312 : else if (colinfo->rightattnos[i] > 0)
4630 14312 : real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4631 : else
4632 : {
4633 : /* We're joining system columns --- use eref name */
4634 0 : real_colname = strVal(list_nth(rte->eref->colnames, i));
4635 : }
4636 :
4637 : /* If child col has been dropped, no need to assign a join colname */
4638 48414 : if (real_colname == NULL)
4639 : {
4640 6 : colinfo->colnames[i] = NULL;
4641 6 : continue;
4642 : }
4643 :
4644 : /* In an unnamed join, just report child column names as-is */
4645 48408 : if (rte->alias == NULL)
4646 : {
4647 48030 : colinfo->colnames[i] = real_colname;
4648 48030 : add_to_names_hash(colinfo, real_colname);
4649 48030 : continue;
4650 : }
4651 :
4652 : /* If alias already assigned, that's what to use */
4653 378 : if (colname == NULL)
4654 : {
4655 : /* If user wrote an alias, prefer that over real column name */
4656 378 : if (rte->alias && i < list_length(rte->alias->colnames))
4657 96 : colname = strVal(list_nth(rte->alias->colnames, i));
4658 : else
4659 282 : colname = real_colname;
4660 :
4661 : /* Unique-ify and insert into colinfo */
4662 378 : colname = make_colname_unique(colname, dpns, colinfo);
4663 :
4664 378 : colinfo->colnames[i] = colname;
4665 378 : add_to_names_hash(colinfo, colname);
4666 : }
4667 :
4668 : /* Remember if any assigned aliases differ from "real" name */
4669 378 : if (!changed_any && strcmp(colname, real_colname) != 0)
4670 24 : changed_any = true;
4671 : }
4672 :
4673 : /*
4674 : * Calculate number of columns the join would have if it were re-parsed
4675 : * now, and create storage for the new_colnames and is_new_col arrays.
4676 : *
4677 : * Note: colname_is_unique will be consulting new_colnames[] during the
4678 : * loops below, so its not-yet-filled entries must be zeroes.
4679 : */
4680 3044 : nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
4681 1522 : list_length(colinfo->usingNames);
4682 1522 : colinfo->num_new_cols = nnewcolumns;
4683 1522 : colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
4684 1522 : colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
4685 :
4686 : /*
4687 : * Generating the new_colnames array is a bit tricky since any new columns
4688 : * added since parse time must be inserted in the right places. This code
4689 : * must match the parser, which will order a join's columns as merged
4690 : * columns first (in USING-clause order), then non-merged columns from the
4691 : * left input (in attnum order), then non-merged columns from the right
4692 : * input (ditto). If one of the inputs is itself a join, its columns will
4693 : * be ordered according to the same rule, which means newly-added columns
4694 : * might not be at the end. We can figure out what's what by consulting
4695 : * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
4696 : *
4697 : * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
4698 : * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
4699 : * meanings for the current child RTE.
4700 : */
4701 :
4702 : /* Handle merged columns; they are first and can't be new */
4703 1522 : i = j = 0;
4704 1522 : while (i < noldcolumns &&
4705 2126 : colinfo->leftattnos[i] != 0 &&
4706 2126 : colinfo->rightattnos[i] != 0)
4707 : {
4708 : /* column name is already determined and known unique */
4709 604 : colinfo->new_colnames[j] = colinfo->colnames[i];
4710 604 : colinfo->is_new_col[j] = false;
4711 :
4712 : /* build bitmapsets of child attnums of merged columns */
4713 604 : if (colinfo->leftattnos[i] > 0)
4714 604 : leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
4715 604 : if (colinfo->rightattnos[i] > 0)
4716 604 : rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);
4717 :
4718 604 : i++, j++;
4719 : }
4720 :
4721 : /* Handle non-merged left-child columns */
4722 1522 : ic = 0;
4723 36714 : for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
4724 : {
4725 35192 : char *child_colname = leftcolinfo->new_colnames[jc];
4726 :
4727 35192 : if (!leftcolinfo->is_new_col[jc])
4728 : {
4729 : /* Advance ic to next non-dropped old column of left child */
4730 34784 : while (ic < leftcolinfo->num_cols &&
4731 34784 : leftcolinfo->colnames[ic] == NULL)
4732 84 : ic++;
4733 : Assert(ic < leftcolinfo->num_cols);
4734 34700 : ic++;
4735 : /* If it is a merged column, we already processed it */
4736 34700 : if (bms_is_member(ic, leftmerged))
4737 604 : continue;
4738 : /* Else, advance i to the corresponding existing join column */
4739 34102 : while (i < colinfo->num_cols &&
4740 34102 : colinfo->colnames[i] == NULL)
4741 6 : i++;
4742 : Assert(i < colinfo->num_cols);
4743 : Assert(ic == colinfo->leftattnos[i]);
4744 : /* Use the already-assigned name of this column */
4745 34096 : colinfo->new_colnames[j] = colinfo->colnames[i];
4746 34096 : i++;
4747 : }
4748 : else
4749 : {
4750 : /*
4751 : * Unique-ify the new child column name and assign, unless we're
4752 : * in an unnamed join, in which case just copy
4753 : */
4754 492 : if (rte->alias != NULL)
4755 : {
4756 264 : colinfo->new_colnames[j] =
4757 132 : make_colname_unique(child_colname, dpns, colinfo);
4758 132 : if (!changed_any &&
4759 108 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4760 12 : changed_any = true;
4761 : }
4762 : else
4763 360 : colinfo->new_colnames[j] = child_colname;
4764 492 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
4765 : }
4766 :
4767 34588 : colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
4768 34588 : j++;
4769 : }
4770 :
4771 : /* Handle non-merged right-child columns in exactly the same way */
4772 1522 : ic = 0;
4773 16606 : for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
4774 : {
4775 15084 : char *child_colname = rightcolinfo->new_colnames[jc];
4776 :
4777 15084 : if (!rightcolinfo->is_new_col[jc])
4778 : {
4779 : /* Advance ic to next non-dropped old column of right child */
4780 14916 : while (ic < rightcolinfo->num_cols &&
4781 14916 : rightcolinfo->colnames[ic] == NULL)
4782 0 : ic++;
4783 : Assert(ic < rightcolinfo->num_cols);
4784 14916 : ic++;
4785 : /* If it is a merged column, we already processed it */
4786 14916 : if (bms_is_member(ic, rightmerged))
4787 604 : continue;
4788 : /* Else, advance i to the corresponding existing join column */
4789 14312 : while (i < colinfo->num_cols &&
4790 14312 : colinfo->colnames[i] == NULL)
4791 0 : i++;
4792 : Assert(i < colinfo->num_cols);
4793 : Assert(ic == colinfo->rightattnos[i]);
4794 : /* Use the already-assigned name of this column */
4795 14312 : colinfo->new_colnames[j] = colinfo->colnames[i];
4796 14312 : i++;
4797 : }
4798 : else
4799 : {
4800 : /*
4801 : * Unique-ify the new child column name and assign, unless we're
4802 : * in an unnamed join, in which case just copy
4803 : */
4804 168 : if (rte->alias != NULL)
4805 : {
4806 48 : colinfo->new_colnames[j] =
4807 24 : make_colname_unique(child_colname, dpns, colinfo);
4808 24 : if (!changed_any &&
4809 24 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4810 12 : changed_any = true;
4811 : }
4812 : else
4813 144 : colinfo->new_colnames[j] = child_colname;
4814 168 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
4815 : }
4816 :
4817 14480 : colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
4818 14480 : j++;
4819 : }
4820 :
4821 : /* Assert we processed the right number of columns */
4822 : #ifdef USE_ASSERT_CHECKING
4823 : while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
4824 : i++;
4825 : Assert(i == colinfo->num_cols);
4826 : Assert(j == nnewcolumns);
4827 : #endif
4828 :
4829 : /* We're now done needing the colinfo's names_hash */
4830 1522 : destroy_colinfo_names_hash(colinfo);
4831 :
4832 : /*
4833 : * For a named join, print column aliases if we changed any from the child
4834 : * names. Unnamed joins cannot print aliases.
4835 : */
4836 1522 : if (rte->alias != NULL)
4837 108 : colinfo->printaliases = changed_any;
4838 : else
4839 1414 : colinfo->printaliases = false;
4840 1522 : }
4841 :
4842 : /*
4843 : * colname_is_unique: is colname distinct from already-chosen column names?
4844 : *
4845 : * dpns is query-wide info, colinfo is for the column's RTE
4846 : */
4847 : static bool
4848 470878 : colname_is_unique(const char *colname, deparse_namespace *dpns,
4849 : deparse_columns *colinfo)
4850 : {
4851 : int i;
4852 : ListCell *lc;
4853 :
4854 : /*
4855 : * If we have a hash table, consult that instead of linearly scanning the
4856 : * colinfo's strings.
4857 : */
4858 470878 : if (colinfo->names_hash)
4859 : {
4860 18518 : if (hash_search(colinfo->names_hash,
4861 : colname,
4862 : HASH_FIND,
4863 : NULL) != NULL)
4864 0 : return false;
4865 : }
4866 : else
4867 : {
4868 : /* Check against already-assigned column aliases within RTE */
4869 6136830 : for (i = 0; i < colinfo->num_cols; i++)
4870 : {
4871 5686748 : char *oldname = colinfo->colnames[i];
4872 :
4873 5686748 : if (oldname && strcmp(oldname, colname) == 0)
4874 2278 : return false;
4875 : }
4876 :
4877 : /*
4878 : * If we're building a new_colnames array, check that too (this will
4879 : * be partially but not completely redundant with the previous checks)
4880 : */
4881 451354 : for (i = 0; i < colinfo->num_new_cols; i++)
4882 : {
4883 1296 : char *oldname = colinfo->new_colnames[i];
4884 :
4885 1296 : if (oldname && strcmp(oldname, colname) == 0)
4886 24 : return false;
4887 : }
4888 :
4889 : /*
4890 : * Also check against names already assigned for parent-join USING
4891 : * cols
4892 : */
4893 452794 : foreach(lc, colinfo->parentUsing)
4894 : {
4895 2742 : char *oldname = (char *) lfirst(lc);
4896 :
4897 2742 : if (strcmp(oldname, colname) == 0)
4898 6 : return false;
4899 : }
4900 : }
4901 :
4902 : /*
4903 : * Also check against USING-column names that must be globally unique.
4904 : * These are not hashed, but there should be few of them.
4905 : */
4906 469410 : foreach(lc, dpns->using_names)
4907 : {
4908 882 : char *oldname = (char *) lfirst(lc);
4909 :
4910 882 : if (strcmp(oldname, colname) == 0)
4911 42 : return false;
4912 : }
4913 :
4914 468528 : return true;
4915 : }
4916 :
4917 : /*
4918 : * make_colname_unique: modify colname if necessary to make it unique
4919 : *
4920 : * dpns is query-wide info, colinfo is for the column's RTE
4921 : */
4922 : static char *
4923 468528 : make_colname_unique(char *colname, deparse_namespace *dpns,
4924 : deparse_columns *colinfo)
4925 : {
4926 : /*
4927 : * If the selected name isn't unique, append digits to make it so. For a
4928 : * very long input name, we might have to truncate to stay within
4929 : * NAMEDATALEN.
4930 : */
4931 468528 : if (!colname_is_unique(colname, dpns, colinfo))
4932 : {
4933 1626 : int colnamelen = strlen(colname);
4934 1626 : char *modname = (char *) palloc(colnamelen + 16);
4935 1626 : int i = 0;
4936 :
4937 : do
4938 : {
4939 2350 : i++;
4940 : for (;;)
4941 : {
4942 2350 : memcpy(modname, colname, colnamelen);
4943 2350 : sprintf(modname + colnamelen, "_%d", i);
4944 2350 : if (strlen(modname) < NAMEDATALEN)
4945 2350 : break;
4946 : /* drop chars from colname to keep all the digits */
4947 0 : colnamelen = pg_mbcliplen(colname, colnamelen,
4948 : colnamelen - 1);
4949 : }
4950 2350 : } while (!colname_is_unique(modname, dpns, colinfo));
4951 1626 : colname = modname;
4952 : }
4953 468528 : return colname;
4954 : }
4955 :
4956 : /*
4957 : * expand_colnames_array_to: make colinfo->colnames at least n items long
4958 : *
4959 : * Any added array entries are initialized to zero.
4960 : */
4961 : static void
4962 101196 : expand_colnames_array_to(deparse_columns *colinfo, int n)
4963 : {
4964 101196 : if (n > colinfo->num_cols)
4965 : {
4966 98690 : if (colinfo->colnames == NULL)
4967 97202 : colinfo->colnames = palloc0_array(char *, n);
4968 : else
4969 1488 : colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
4970 98690 : colinfo->num_cols = n;
4971 : }
4972 101196 : }
4973 :
4974 : /*
4975 : * build_colinfo_names_hash: optionally construct a hash table for colinfo
4976 : */
4977 : static void
4978 99324 : build_colinfo_names_hash(deparse_columns *colinfo)
4979 : {
4980 : HASHCTL hash_ctl;
4981 : int i;
4982 : ListCell *lc;
4983 :
4984 : /*
4985 : * Use a hash table only for RTEs with at least 32 columns. (The cutoff
4986 : * is somewhat arbitrary, but let's choose it so that this code does get
4987 : * exercised in the regression tests.)
4988 : */
4989 99324 : if (colinfo->num_cols < 32)
4990 97980 : return;
4991 :
4992 : /*
4993 : * Set up the hash table. The entries are just strings with no other
4994 : * payload.
4995 : */
4996 1344 : hash_ctl.keysize = NAMEDATALEN;
4997 1344 : hash_ctl.entrysize = NAMEDATALEN;
4998 1344 : hash_ctl.hcxt = CurrentMemoryContext;
4999 2688 : colinfo->names_hash = hash_create("deparse_columns names",
5000 1344 : colinfo->num_cols + colinfo->num_new_cols,
5001 : &hash_ctl,
5002 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
5003 :
5004 : /*
5005 : * Preload the hash table with any names already present (these would have
5006 : * come from set_using_names).
5007 : */
5008 63050 : for (i = 0; i < colinfo->num_cols; i++)
5009 : {
5010 61706 : char *oldname = colinfo->colnames[i];
5011 :
5012 61706 : if (oldname)
5013 0 : add_to_names_hash(colinfo, oldname);
5014 : }
5015 :
5016 1344 : for (i = 0; i < colinfo->num_new_cols; i++)
5017 : {
5018 0 : char *oldname = colinfo->new_colnames[i];
5019 :
5020 0 : if (oldname)
5021 0 : add_to_names_hash(colinfo, oldname);
5022 : }
5023 :
5024 1344 : foreach(lc, colinfo->parentUsing)
5025 : {
5026 0 : char *oldname = (char *) lfirst(lc);
5027 :
5028 0 : add_to_names_hash(colinfo, oldname);
5029 : }
5030 : }
5031 :
5032 : /*
5033 : * add_to_names_hash: add a string to the names_hash, if we're using one
5034 : */
5035 : static void
5036 516560 : add_to_names_hash(deparse_columns *colinfo, const char *name)
5037 : {
5038 516560 : if (colinfo->names_hash)
5039 61706 : (void) hash_search(colinfo->names_hash,
5040 : name,
5041 : HASH_ENTER,
5042 : NULL);
5043 516560 : }
5044 :
5045 : /*
5046 : * destroy_colinfo_names_hash: destroy hash table when done with it
5047 : */
5048 : static void
5049 99324 : destroy_colinfo_names_hash(deparse_columns *colinfo)
5050 : {
5051 99324 : if (colinfo->names_hash)
5052 : {
5053 1344 : hash_destroy(colinfo->names_hash);
5054 1344 : colinfo->names_hash = NULL;
5055 : }
5056 99324 : }
5057 :
5058 : /*
5059 : * identify_join_columns: figure out where columns of a join come from
5060 : *
5061 : * Fills the join-specific fields of the colinfo struct, except for
5062 : * usingNames which is filled later.
5063 : */
5064 : static void
5065 1522 : identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
5066 : deparse_columns *colinfo)
5067 : {
5068 : int numjoincols;
5069 : int jcolno;
5070 : int rcolno;
5071 : ListCell *lc;
5072 :
5073 : /* Extract left/right child RT indexes */
5074 1522 : if (IsA(j->larg, RangeTblRef))
5075 976 : colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
5076 546 : else if (IsA(j->larg, JoinExpr))
5077 546 : colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
5078 : else
5079 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5080 : (int) nodeTag(j->larg));
5081 1522 : if (IsA(j->rarg, RangeTblRef))
5082 1522 : colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
5083 0 : else if (IsA(j->rarg, JoinExpr))
5084 0 : colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
5085 : else
5086 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5087 : (int) nodeTag(j->rarg));
5088 :
5089 : /* Assert children will be processed earlier than join in second pass */
5090 : Assert(colinfo->leftrti < j->rtindex);
5091 : Assert(colinfo->rightrti < j->rtindex);
5092 :
5093 : /* Initialize result arrays with zeroes */
5094 1522 : numjoincols = list_length(jrte->joinaliasvars);
5095 : Assert(numjoincols == list_length(jrte->eref->colnames));
5096 1522 : colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
5097 1522 : colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
5098 :
5099 : /*
5100 : * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
5101 : * Recall that the column(s) merged due to USING are the first column(s)
5102 : * of the join output. We need not do anything special while scanning
5103 : * joinleftcols, but while scanning joinrightcols we must distinguish
5104 : * merged from unmerged columns.
5105 : */
5106 1522 : jcolno = 0;
5107 36228 : foreach(lc, jrte->joinleftcols)
5108 : {
5109 34706 : int leftattno = lfirst_int(lc);
5110 :
5111 34706 : colinfo->leftattnos[jcolno++] = leftattno;
5112 : }
5113 1522 : rcolno = 0;
5114 16438 : foreach(lc, jrte->joinrightcols)
5115 : {
5116 14916 : int rightattno = lfirst_int(lc);
5117 :
5118 14916 : if (rcolno < jrte->joinmergedcols) /* merged column? */
5119 604 : colinfo->rightattnos[rcolno] = rightattno;
5120 : else
5121 14312 : colinfo->rightattnos[jcolno++] = rightattno;
5122 14916 : rcolno++;
5123 : }
5124 : Assert(jcolno == numjoincols);
5125 1522 : }
5126 :
5127 : /*
5128 : * get_rtable_name: convenience function to get a previously assigned RTE alias
5129 : *
5130 : * The RTE must belong to the topmost namespace level in "context".
5131 : */
5132 : static char *
5133 7412 : get_rtable_name(int rtindex, deparse_context *context)
5134 : {
5135 7412 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
5136 :
5137 : Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
5138 7412 : return (char *) list_nth(dpns->rtable_names, rtindex - 1);
5139 : }
5140 :
5141 : /*
5142 : * set_deparse_plan: set up deparse_namespace to parse subexpressions
5143 : * of a given Plan node
5144 : *
5145 : * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
5146 : * and index_tlist fields. Caller must already have adjusted the ancestors
5147 : * list if necessary. Note that the rtable, subplans, and ctes fields do
5148 : * not need to change when shifting attention to different plan nodes in a
5149 : * single plan tree.
5150 : */
5151 : static void
5152 124818 : set_deparse_plan(deparse_namespace *dpns, Plan *plan)
5153 : {
5154 124818 : dpns->plan = plan;
5155 :
5156 : /*
5157 : * We special-case Append and MergeAppend to pretend that the first child
5158 : * plan is the OUTER referent; we have to interpret OUTER Vars in their
5159 : * tlists according to one of the children, and the first one is the most
5160 : * natural choice.
5161 : */
5162 124818 : if (IsA(plan, Append))
5163 4046 : dpns->outer_plan = linitial(((Append *) plan)->appendplans);
5164 120772 : else if (IsA(plan, MergeAppend))
5165 504 : dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
5166 : else
5167 120268 : dpns->outer_plan = outerPlan(plan);
5168 :
5169 124818 : if (dpns->outer_plan)
5170 56840 : dpns->outer_tlist = dpns->outer_plan->targetlist;
5171 : else
5172 67978 : dpns->outer_tlist = NIL;
5173 :
5174 : /*
5175 : * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
5176 : * use OUTER because that could someday conflict with the normal meaning.)
5177 : * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
5178 : * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
5179 : * that as INNER referent.
5180 : *
5181 : * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
5182 : * INNER referent. This is the join from the target relation to the data
5183 : * source, and all INNER_VAR Vars in other parts of the query refer to its
5184 : * targetlist.
5185 : *
5186 : * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the
5187 : * excluded expression's tlist. (Similar to the SubqueryScan we don't want
5188 : * to reuse OUTER, it's used for RETURNING in some modify table cases,
5189 : * although not INSERT .. CONFLICT).
5190 : */
5191 124818 : if (IsA(plan, SubqueryScan))
5192 644 : dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5193 124174 : else if (IsA(plan, CteScan))
5194 552 : dpns->inner_plan = list_nth(dpns->subplans,
5195 552 : ((CteScan *) plan)->ctePlanId - 1);
5196 123622 : else if (IsA(plan, WorkTableScan))
5197 174 : dpns->inner_plan = find_recursive_union(dpns,
5198 : (WorkTableScan *) plan);
5199 123448 : else if (IsA(plan, ModifyTable))
5200 : {
5201 402 : if (((ModifyTable *) plan)->operation == CMD_MERGE)
5202 60 : dpns->inner_plan = outerPlan(plan);
5203 : else
5204 342 : dpns->inner_plan = plan;
5205 : }
5206 : else
5207 123046 : dpns->inner_plan = innerPlan(plan);
5208 :
5209 124818 : if (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)
5210 170 : dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
5211 124648 : else if (dpns->inner_plan)
5212 20816 : dpns->inner_tlist = dpns->inner_plan->targetlist;
5213 : else
5214 103832 : dpns->inner_tlist = NIL;
5215 :
5216 : /* Set up referent for INDEX_VAR Vars, if needed */
5217 124818 : if (IsA(plan, IndexOnlyScan))
5218 3142 : dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5219 121676 : else if (IsA(plan, ForeignScan))
5220 3062 : dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5221 118614 : else if (IsA(plan, CustomScan))
5222 0 : dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5223 : else
5224 118614 : dpns->index_tlist = NIL;
5225 124818 : }
5226 :
5227 : /*
5228 : * Locate the ancestor plan node that is the RecursiveUnion generating
5229 : * the WorkTableScan's work table. We can match on wtParam, since that
5230 : * should be unique within the plan tree.
5231 : */
5232 : static Plan *
5233 174 : find_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)
5234 : {
5235 : ListCell *lc;
5236 :
5237 438 : foreach(lc, dpns->ancestors)
5238 : {
5239 438 : Plan *ancestor = (Plan *) lfirst(lc);
5240 :
5241 438 : if (IsA(ancestor, RecursiveUnion) &&
5242 174 : ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5243 174 : return ancestor;
5244 : }
5245 0 : elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5246 : wtscan->wtParam);
5247 : return NULL;
5248 : }
5249 :
5250 : /*
5251 : * push_child_plan: temporarily transfer deparsing attention to a child plan
5252 : *
5253 : * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
5254 : * deparse context in case the referenced expression itself uses
5255 : * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
5256 : * affecting levelsup issues (although in a Plan tree there really shouldn't
5257 : * be any).
5258 : *
5259 : * Caller must provide a local deparse_namespace variable to save the
5260 : * previous state for pop_child_plan.
5261 : */
5262 : static void
5263 68022 : push_child_plan(deparse_namespace *dpns, Plan *plan,
5264 : deparse_namespace *save_dpns)
5265 : {
5266 : /* Save state for restoration later */
5267 68022 : *save_dpns = *dpns;
5268 :
5269 : /* Link current plan node into ancestors list */
5270 68022 : dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5271 :
5272 : /* Set attention on selected child */
5273 68022 : set_deparse_plan(dpns, plan);
5274 68022 : }
5275 :
5276 : /*
5277 : * pop_child_plan: undo the effects of push_child_plan
5278 : */
5279 : static void
5280 68022 : pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5281 : {
5282 : List *ancestors;
5283 :
5284 : /* Get rid of ancestors list cell added by push_child_plan */
5285 68022 : ancestors = list_delete_first(dpns->ancestors);
5286 :
5287 : /* Restore fields changed by push_child_plan */
5288 68022 : *dpns = *save_dpns;
5289 :
5290 : /* Make sure dpns->ancestors is right (may be unnecessary) */
5291 68022 : dpns->ancestors = ancestors;
5292 68022 : }
5293 :
5294 : /*
5295 : * push_ancestor_plan: temporarily transfer deparsing attention to an
5296 : * ancestor plan
5297 : *
5298 : * When expanding a Param reference, we must adjust the deparse context
5299 : * to match the plan node that contains the expression being printed;
5300 : * otherwise we'd fail if that expression itself contains a Param or
5301 : * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
5302 : *
5303 : * The target ancestor is conveniently identified by the ListCell holding it
5304 : * in dpns->ancestors.
5305 : *
5306 : * Caller must provide a local deparse_namespace variable to save the
5307 : * previous state for pop_ancestor_plan.
5308 : */
5309 : static void
5310 4328 : push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
5311 : deparse_namespace *save_dpns)
5312 : {
5313 4328 : Plan *plan = (Plan *) lfirst(ancestor_cell);
5314 :
5315 : /* Save state for restoration later */
5316 4328 : *save_dpns = *dpns;
5317 :
5318 : /* Build a new ancestor list with just this node's ancestors */
5319 4328 : dpns->ancestors =
5320 4328 : list_copy_tail(dpns->ancestors,
5321 4328 : list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5322 :
5323 : /* Set attention on selected ancestor */
5324 4328 : set_deparse_plan(dpns, plan);
5325 4328 : }
5326 :
5327 : /*
5328 : * pop_ancestor_plan: undo the effects of push_ancestor_plan
5329 : */
5330 : static void
5331 4328 : pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5332 : {
5333 : /* Free the ancestor list made in push_ancestor_plan */
5334 4328 : list_free(dpns->ancestors);
5335 :
5336 : /* Restore fields changed by push_ancestor_plan */
5337 4328 : *dpns = *save_dpns;
5338 4328 : }
5339 :
5340 :
5341 : /* ----------
5342 : * make_ruledef - reconstruct the CREATE RULE command
5343 : * for a given pg_rewrite tuple
5344 : * ----------
5345 : */
5346 : static void
5347 792 : make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5348 : int prettyFlags)
5349 : {
5350 : char *rulename;
5351 : char ev_type;
5352 : Oid ev_class;
5353 : bool is_instead;
5354 : char *ev_qual;
5355 : char *ev_action;
5356 : List *actions;
5357 : Relation ev_relation;
5358 792 : TupleDesc viewResultDesc = NULL;
5359 : int fno;
5360 : Datum dat;
5361 : bool isnull;
5362 :
5363 : /*
5364 : * Get the attribute values from the rules tuple
5365 : */
5366 792 : fno = SPI_fnumber(rulettc, "rulename");
5367 792 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5368 : Assert(!isnull);
5369 792 : rulename = NameStr(*(DatumGetName(dat)));
5370 :
5371 792 : fno = SPI_fnumber(rulettc, "ev_type");
5372 792 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5373 : Assert(!isnull);
5374 792 : ev_type = DatumGetChar(dat);
5375 :
5376 792 : fno = SPI_fnumber(rulettc, "ev_class");
5377 792 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5378 : Assert(!isnull);
5379 792 : ev_class = DatumGetObjectId(dat);
5380 :
5381 792 : fno = SPI_fnumber(rulettc, "is_instead");
5382 792 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5383 : Assert(!isnull);
5384 792 : is_instead = DatumGetBool(dat);
5385 :
5386 792 : fno = SPI_fnumber(rulettc, "ev_qual");
5387 792 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5388 : Assert(ev_qual != NULL);
5389 :
5390 792 : fno = SPI_fnumber(rulettc, "ev_action");
5391 792 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5392 : Assert(ev_action != NULL);
5393 792 : actions = (List *) stringToNode(ev_action);
5394 792 : if (actions == NIL)
5395 0 : elog(ERROR, "invalid empty ev_action list");
5396 :
5397 792 : ev_relation = table_open(ev_class, AccessShareLock);
5398 :
5399 : /*
5400 : * Build the rules definition text
5401 : */
5402 792 : appendStringInfo(buf, "CREATE RULE %s AS",
5403 : quote_identifier(rulename));
5404 :
5405 792 : if (prettyFlags & PRETTYFLAG_INDENT)
5406 792 : appendStringInfoString(buf, "\n ON ");
5407 : else
5408 0 : appendStringInfoString(buf, " ON ");
5409 :
5410 : /* The event the rule is fired for */
5411 792 : switch (ev_type)
5412 : {
5413 6 : case '1':
5414 6 : appendStringInfoString(buf, "SELECT");
5415 6 : viewResultDesc = RelationGetDescr(ev_relation);
5416 6 : break;
5417 :
5418 214 : case '2':
5419 214 : appendStringInfoString(buf, "UPDATE");
5420 214 : break;
5421 :
5422 420 : case '3':
5423 420 : appendStringInfoString(buf, "INSERT");
5424 420 : break;
5425 :
5426 152 : case '4':
5427 152 : appendStringInfoString(buf, "DELETE");
5428 152 : break;
5429 :
5430 0 : default:
5431 0 : ereport(ERROR,
5432 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5433 : errmsg("rule \"%s\" has unsupported event type %d",
5434 : rulename, ev_type)));
5435 : break;
5436 : }
5437 :
5438 : /* The relation the rule is fired on */
5439 792 : appendStringInfo(buf, " TO %s",
5440 792 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
5441 114 : generate_relation_name(ev_class, NIL) :
5442 678 : generate_qualified_relation_name(ev_class));
5443 :
5444 : /* If the rule has an event qualification, add it */
5445 792 : if (strcmp(ev_qual, "<>") != 0)
5446 : {
5447 : Node *qual;
5448 : Query *query;
5449 : deparse_context context;
5450 : deparse_namespace dpns;
5451 :
5452 188 : if (prettyFlags & PRETTYFLAG_INDENT)
5453 188 : appendStringInfoString(buf, "\n ");
5454 188 : appendStringInfoString(buf, " WHERE ");
5455 :
5456 188 : qual = stringToNode(ev_qual);
5457 :
5458 : /*
5459 : * We need to make a context for recognizing any Vars in the qual
5460 : * (which can only be references to OLD and NEW). Use the rtable of
5461 : * the first query in the action list for this purpose.
5462 : */
5463 188 : query = (Query *) linitial(actions);
5464 :
5465 : /*
5466 : * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5467 : * into the SELECT, and that's what we need to look at. (Ugly kluge
5468 : * ... try to fix this when we redesign querytrees.)
5469 : */
5470 188 : query = getInsertSelectQuery(query, NULL);
5471 :
5472 : /* Must acquire locks right away; see notes in get_query_def() */
5473 188 : AcquireRewriteLocks(query, false, false);
5474 :
5475 188 : context.buf = buf;
5476 188 : context.namespaces = list_make1(&dpns);
5477 188 : context.resultDesc = NULL;
5478 188 : context.targetList = NIL;
5479 188 : context.windowClause = NIL;
5480 188 : context.varprefix = (list_length(query->rtable) != 1);
5481 188 : context.prettyFlags = prettyFlags;
5482 188 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
5483 188 : context.indentLevel = PRETTYINDENT_STD;
5484 188 : context.colNamesVisible = true;
5485 188 : context.inGroupBy = false;
5486 188 : context.varInOrderBy = false;
5487 188 : context.appendparents = NULL;
5488 :
5489 188 : set_deparse_for_query(&dpns, query, NIL);
5490 :
5491 188 : get_rule_expr(qual, &context, false);
5492 : }
5493 :
5494 792 : appendStringInfoString(buf, " DO ");
5495 :
5496 : /* The INSTEAD keyword (if so) */
5497 792 : if (is_instead)
5498 474 : appendStringInfoString(buf, "INSTEAD ");
5499 :
5500 : /* Finally the rules actions */
5501 792 : if (list_length(actions) > 1)
5502 : {
5503 : ListCell *action;
5504 : Query *query;
5505 :
5506 32 : appendStringInfoChar(buf, '(');
5507 96 : foreach(action, actions)
5508 : {
5509 64 : query = (Query *) lfirst(action);
5510 64 : get_query_def(query, buf, NIL, viewResultDesc, true,
5511 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5512 64 : if (prettyFlags)
5513 64 : appendStringInfoString(buf, ";\n");
5514 : else
5515 0 : appendStringInfoString(buf, "; ");
5516 : }
5517 32 : appendStringInfoString(buf, ");");
5518 : }
5519 : else
5520 : {
5521 : Query *query;
5522 :
5523 760 : query = (Query *) linitial(actions);
5524 760 : get_query_def(query, buf, NIL, viewResultDesc, true,
5525 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5526 760 : appendStringInfoChar(buf, ';');
5527 : }
5528 :
5529 792 : table_close(ev_relation, AccessShareLock);
5530 792 : }
5531 :
5532 :
5533 : /* ----------
5534 : * make_viewdef - reconstruct the SELECT part of a
5535 : * view rewrite rule
5536 : * ----------
5537 : */
5538 : static void
5539 3966 : make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5540 : int prettyFlags, int wrapColumn)
5541 : {
5542 : Query *query;
5543 : char ev_type;
5544 : Oid ev_class;
5545 : bool is_instead;
5546 : char *ev_qual;
5547 : char *ev_action;
5548 : List *actions;
5549 : Relation ev_relation;
5550 : int fno;
5551 : Datum dat;
5552 : bool isnull;
5553 :
5554 : /*
5555 : * Get the attribute values from the rules tuple
5556 : */
5557 3966 : fno = SPI_fnumber(rulettc, "ev_type");
5558 3966 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5559 : Assert(!isnull);
5560 3966 : ev_type = DatumGetChar(dat);
5561 :
5562 3966 : fno = SPI_fnumber(rulettc, "ev_class");
5563 3966 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5564 : Assert(!isnull);
5565 3966 : ev_class = DatumGetObjectId(dat);
5566 :
5567 3966 : fno = SPI_fnumber(rulettc, "is_instead");
5568 3966 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5569 : Assert(!isnull);
5570 3966 : is_instead = DatumGetBool(dat);
5571 :
5572 3966 : fno = SPI_fnumber(rulettc, "ev_qual");
5573 3966 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5574 : Assert(ev_qual != NULL);
5575 :
5576 3966 : fno = SPI_fnumber(rulettc, "ev_action");
5577 3966 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5578 : Assert(ev_action != NULL);
5579 3966 : actions = (List *) stringToNode(ev_action);
5580 :
5581 3966 : if (list_length(actions) != 1)
5582 : {
5583 : /* keep output buffer empty and leave */
5584 0 : return;
5585 : }
5586 :
5587 3966 : query = (Query *) linitial(actions);
5588 :
5589 3966 : if (ev_type != '1' || !is_instead ||
5590 3966 : strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5591 : {
5592 : /* keep output buffer empty and leave */
5593 0 : return;
5594 : }
5595 :
5596 3966 : ev_relation = table_open(ev_class, AccessShareLock);
5597 :
5598 3966 : get_query_def(query, buf, NIL, RelationGetDescr(ev_relation), true,
5599 : prettyFlags, wrapColumn, 0);
5600 3966 : appendStringInfoChar(buf, ';');
5601 :
5602 3966 : table_close(ev_relation, AccessShareLock);
5603 : }
5604 :
5605 :
5606 : /* ----------
5607 : * get_query_def - Parse back one query parsetree
5608 : *
5609 : * query: parsetree to be displayed
5610 : * buf: output text is appended to buf
5611 : * parentnamespace: list (initially empty) of outer-level deparse_namespace's
5612 : * resultDesc: if not NULL, the output tuple descriptor for the view
5613 : * represented by a SELECT query. We use the column names from it
5614 : * to label SELECT output columns, in preference to names in the query
5615 : * colNamesVisible: true if the surrounding context cares about the output
5616 : * column names at all (as, for example, an EXISTS() context does not);
5617 : * when false, we can suppress dummy column labels such as "?column?"
5618 : * prettyFlags: bitmask of PRETTYFLAG_XXX options
5619 : * wrapColumn: maximum line length, or -1 to disable wrapping
5620 : * startIndent: initial indentation amount
5621 : * ----------
5622 : */
5623 : static void
5624 6514 : get_query_def(Query *query, StringInfo buf, List *parentnamespace,
5625 : TupleDesc resultDesc, bool colNamesVisible,
5626 : int prettyFlags, int wrapColumn, int startIndent)
5627 : {
5628 : deparse_context context;
5629 : deparse_namespace dpns;
5630 : int rtable_size;
5631 :
5632 : /* Guard against excessively long or deeply-nested queries */
5633 6514 : CHECK_FOR_INTERRUPTS();
5634 6514 : check_stack_depth();
5635 :
5636 13028 : rtable_size = query->hasGroupRTE ?
5637 6514 : list_length(query->rtable) - 1 :
5638 6254 : list_length(query->rtable);
5639 :
5640 : /*
5641 : * Replace any Vars in the query's targetlist and havingQual that
5642 : * reference GROUP outputs with the underlying grouping expressions.
5643 : */
5644 6514 : if (query->hasGroupRTE)
5645 : {
5646 260 : query->targetList = (List *)
5647 260 : flatten_group_exprs(NULL, query, (Node *) query->targetList);
5648 260 : query->havingQual =
5649 260 : flatten_group_exprs(NULL, query, query->havingQual);
5650 : }
5651 :
5652 : /*
5653 : * Before we begin to examine the query, acquire locks on referenced
5654 : * relations, and fix up deleted columns in JOIN RTEs. This ensures
5655 : * consistent results. Note we assume it's OK to scribble on the passed
5656 : * querytree!
5657 : *
5658 : * We are only deparsing the query (we are not about to execute it), so we
5659 : * only need AccessShareLock on the relations it mentions.
5660 : */
5661 6514 : AcquireRewriteLocks(query, false, false);
5662 :
5663 6514 : context.buf = buf;
5664 6514 : context.namespaces = lcons(&dpns, list_copy(parentnamespace));
5665 6514 : context.resultDesc = NULL;
5666 6514 : context.targetList = NIL;
5667 6514 : context.windowClause = NIL;
5668 6514 : context.varprefix = (parentnamespace != NIL ||
5669 : rtable_size != 1);
5670 6514 : context.prettyFlags = prettyFlags;
5671 6514 : context.wrapColumn = wrapColumn;
5672 6514 : context.indentLevel = startIndent;
5673 6514 : context.colNamesVisible = colNamesVisible;
5674 6514 : context.inGroupBy = false;
5675 6514 : context.varInOrderBy = false;
5676 6514 : context.appendparents = NULL;
5677 :
5678 6514 : set_deparse_for_query(&dpns, query, parentnamespace);
5679 :
5680 6514 : switch (query->commandType)
5681 : {
5682 5616 : case CMD_SELECT:
5683 : /* We set context.resultDesc only if it's a SELECT */
5684 5616 : context.resultDesc = resultDesc;
5685 5616 : get_select_query_def(query, &context);
5686 5616 : break;
5687 :
5688 214 : case CMD_UPDATE:
5689 214 : get_update_query_def(query, &context);
5690 214 : break;
5691 :
5692 472 : case CMD_INSERT:
5693 472 : get_insert_query_def(query, &context);
5694 472 : break;
5695 :
5696 118 : case CMD_DELETE:
5697 118 : get_delete_query_def(query, &context);
5698 118 : break;
5699 :
5700 12 : case CMD_MERGE:
5701 12 : get_merge_query_def(query, &context);
5702 12 : break;
5703 :
5704 60 : case CMD_NOTHING:
5705 60 : appendStringInfoString(buf, "NOTHING");
5706 60 : break;
5707 :
5708 22 : case CMD_UTILITY:
5709 22 : get_utility_query_def(query, &context);
5710 22 : break;
5711 :
5712 0 : default:
5713 0 : elog(ERROR, "unrecognized query command type: %d",
5714 : query->commandType);
5715 : break;
5716 : }
5717 6514 : }
5718 :
5719 : /* ----------
5720 : * get_values_def - Parse back a VALUES list
5721 : * ----------
5722 : */
5723 : static void
5724 302 : get_values_def(List *values_lists, deparse_context *context)
5725 : {
5726 302 : StringInfo buf = context->buf;
5727 302 : bool first_list = true;
5728 : ListCell *vtl;
5729 :
5730 302 : appendStringInfoString(buf, "VALUES ");
5731 :
5732 856 : foreach(vtl, values_lists)
5733 : {
5734 554 : List *sublist = (List *) lfirst(vtl);
5735 554 : bool first_col = true;
5736 : ListCell *lc;
5737 :
5738 554 : if (first_list)
5739 302 : first_list = false;
5740 : else
5741 252 : appendStringInfoString(buf, ", ");
5742 :
5743 554 : appendStringInfoChar(buf, '(');
5744 2150 : foreach(lc, sublist)
5745 : {
5746 1596 : Node *col = (Node *) lfirst(lc);
5747 :
5748 1596 : if (first_col)
5749 554 : first_col = false;
5750 : else
5751 1042 : appendStringInfoChar(buf, ',');
5752 :
5753 : /*
5754 : * Print the value. Whole-row Vars need special treatment.
5755 : */
5756 1596 : get_rule_expr_toplevel(col, context, false);
5757 : }
5758 554 : appendStringInfoChar(buf, ')');
5759 : }
5760 302 : }
5761 :
5762 : /* ----------
5763 : * get_with_clause - Parse back a WITH clause
5764 : * ----------
5765 : */
5766 : static void
5767 6432 : get_with_clause(Query *query, deparse_context *context)
5768 : {
5769 6432 : StringInfo buf = context->buf;
5770 : const char *sep;
5771 : ListCell *l;
5772 :
5773 6432 : if (query->cteList == NIL)
5774 6318 : return;
5775 :
5776 114 : if (PRETTY_INDENT(context))
5777 : {
5778 114 : context->indentLevel += PRETTYINDENT_STD;
5779 114 : appendStringInfoChar(buf, ' ');
5780 : }
5781 :
5782 114 : if (query->hasRecursive)
5783 68 : sep = "WITH RECURSIVE ";
5784 : else
5785 46 : sep = "WITH ";
5786 290 : foreach(l, query->cteList)
5787 : {
5788 176 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
5789 :
5790 176 : appendStringInfoString(buf, sep);
5791 176 : appendStringInfoString(buf, quote_identifier(cte->ctename));
5792 176 : if (cte->aliascolnames)
5793 : {
5794 68 : bool first = true;
5795 : ListCell *col;
5796 :
5797 68 : appendStringInfoChar(buf, '(');
5798 172 : foreach(col, cte->aliascolnames)
5799 : {
5800 104 : if (first)
5801 68 : first = false;
5802 : else
5803 36 : appendStringInfoString(buf, ", ");
5804 104 : appendStringInfoString(buf,
5805 104 : quote_identifier(strVal(lfirst(col))));
5806 : }
5807 68 : appendStringInfoChar(buf, ')');
5808 : }
5809 176 : appendStringInfoString(buf, " AS ");
5810 176 : switch (cte->ctematerialized)
5811 : {
5812 158 : case CTEMaterializeDefault:
5813 158 : break;
5814 18 : case CTEMaterializeAlways:
5815 18 : appendStringInfoString(buf, "MATERIALIZED ");
5816 18 : break;
5817 0 : case CTEMaterializeNever:
5818 0 : appendStringInfoString(buf, "NOT MATERIALIZED ");
5819 0 : break;
5820 : }
5821 176 : appendStringInfoChar(buf, '(');
5822 176 : if (PRETTY_INDENT(context))
5823 176 : appendContextKeyword(context, "", 0, 0, 0);
5824 176 : get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
5825 : true,
5826 : context->prettyFlags, context->wrapColumn,
5827 : context->indentLevel);
5828 176 : if (PRETTY_INDENT(context))
5829 176 : appendContextKeyword(context, "", 0, 0, 0);
5830 176 : appendStringInfoChar(buf, ')');
5831 :
5832 176 : if (cte->search_clause)
5833 : {
5834 6 : bool first = true;
5835 : ListCell *lc;
5836 :
5837 6 : appendStringInfo(buf, " SEARCH %s FIRST BY ",
5838 6 : cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
5839 :
5840 18 : foreach(lc, cte->search_clause->search_col_list)
5841 : {
5842 12 : if (first)
5843 6 : first = false;
5844 : else
5845 6 : appendStringInfoString(buf, ", ");
5846 12 : appendStringInfoString(buf,
5847 12 : quote_identifier(strVal(lfirst(lc))));
5848 : }
5849 :
5850 6 : appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
5851 : }
5852 :
5853 176 : if (cte->cycle_clause)
5854 : {
5855 12 : bool first = true;
5856 : ListCell *lc;
5857 :
5858 12 : appendStringInfoString(buf, " CYCLE ");
5859 :
5860 36 : foreach(lc, cte->cycle_clause->cycle_col_list)
5861 : {
5862 24 : if (first)
5863 12 : first = false;
5864 : else
5865 12 : appendStringInfoString(buf, ", ");
5866 24 : appendStringInfoString(buf,
5867 24 : quote_identifier(strVal(lfirst(lc))));
5868 : }
5869 :
5870 12 : appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
5871 :
5872 : {
5873 12 : Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
5874 12 : Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
5875 :
5876 18 : if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
5877 6 : cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
5878 : {
5879 6 : appendStringInfoString(buf, " TO ");
5880 6 : get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
5881 6 : appendStringInfoString(buf, " DEFAULT ");
5882 6 : get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
5883 : }
5884 : }
5885 :
5886 12 : appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
5887 : }
5888 :
5889 176 : sep = ", ";
5890 : }
5891 :
5892 114 : if (PRETTY_INDENT(context))
5893 : {
5894 114 : context->indentLevel -= PRETTYINDENT_STD;
5895 114 : appendContextKeyword(context, "", 0, 0, 0);
5896 : }
5897 : else
5898 0 : appendStringInfoChar(buf, ' ');
5899 : }
5900 :
5901 : /* ----------
5902 : * get_select_query_def - Parse back a SELECT parsetree
5903 : * ----------
5904 : */
5905 : static void
5906 5616 : get_select_query_def(Query *query, deparse_context *context)
5907 : {
5908 5616 : StringInfo buf = context->buf;
5909 : bool force_colno;
5910 : ListCell *l;
5911 :
5912 : /* Insert the WITH clause if given */
5913 5616 : get_with_clause(query, context);
5914 :
5915 : /* Subroutines may need to consult the SELECT targetlist and windowClause */
5916 5616 : context->targetList = query->targetList;
5917 5616 : context->windowClause = query->windowClause;
5918 :
5919 : /*
5920 : * If the Query node has a setOperations tree, then it's the top level of
5921 : * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
5922 : * fields are interesting in the top query itself.
5923 : */
5924 5616 : if (query->setOperations)
5925 : {
5926 176 : get_setop_query(query->setOperations, query, context);
5927 : /* ORDER BY clauses must be simple in this case */
5928 176 : force_colno = true;
5929 : }
5930 : else
5931 : {
5932 5440 : get_basic_select_query(query, context);
5933 5440 : force_colno = false;
5934 : }
5935 :
5936 : /* Add the ORDER BY clause if given */
5937 5616 : if (query->sortClause != NIL)
5938 : {
5939 216 : appendContextKeyword(context, " ORDER BY ",
5940 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5941 216 : get_rule_orderby(query->sortClause, query->targetList,
5942 : force_colno, context);
5943 : }
5944 :
5945 : /*
5946 : * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
5947 : * standard spelling of LIMIT.
5948 : */
5949 5616 : if (query->limitOffset != NULL)
5950 : {
5951 44 : appendContextKeyword(context, " OFFSET ",
5952 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5953 44 : get_rule_expr(query->limitOffset, context, false);
5954 : }
5955 5616 : if (query->limitCount != NULL)
5956 : {
5957 116 : if (query->limitOption == LIMIT_OPTION_WITH_TIES)
5958 : {
5959 : /*
5960 : * The limitCount arg is a c_expr, so it needs parens. Simple
5961 : * literals and function expressions would not need parens, but
5962 : * unfortunately it's hard to tell if the expression will be
5963 : * printed as a simple literal like 123 or as a typecast
5964 : * expression, like '-123'::int4. The grammar accepts the former
5965 : * without quoting, but not the latter.
5966 : */
5967 66 : appendContextKeyword(context, " FETCH FIRST ",
5968 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5969 66 : appendStringInfoChar(buf, '(');
5970 66 : get_rule_expr(query->limitCount, context, false);
5971 66 : appendStringInfoChar(buf, ')');
5972 66 : appendStringInfoString(buf, " ROWS WITH TIES");
5973 : }
5974 : else
5975 : {
5976 50 : appendContextKeyword(context, " LIMIT ",
5977 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5978 50 : if (IsA(query->limitCount, Const) &&
5979 22 : ((Const *) query->limitCount)->constisnull)
5980 22 : appendStringInfoString(buf, "ALL");
5981 : else
5982 28 : get_rule_expr(query->limitCount, context, false);
5983 : }
5984 : }
5985 :
5986 : /* Add FOR [KEY] UPDATE/SHARE clauses if present */
5987 5616 : if (query->hasForUpdate)
5988 : {
5989 12 : foreach(l, query->rowMarks)
5990 : {
5991 6 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
5992 :
5993 : /* don't print implicit clauses */
5994 6 : if (rc->pushedDown)
5995 0 : continue;
5996 :
5997 6 : switch (rc->strength)
5998 : {
5999 0 : case LCS_NONE:
6000 : /* we intentionally throw an error for LCS_NONE */
6001 0 : elog(ERROR, "unrecognized LockClauseStrength %d",
6002 : (int) rc->strength);
6003 : break;
6004 0 : case LCS_FORKEYSHARE:
6005 0 : appendContextKeyword(context, " FOR KEY SHARE",
6006 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6007 0 : break;
6008 0 : case LCS_FORSHARE:
6009 0 : appendContextKeyword(context, " FOR SHARE",
6010 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6011 0 : break;
6012 0 : case LCS_FORNOKEYUPDATE:
6013 0 : appendContextKeyword(context, " FOR NO KEY UPDATE",
6014 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6015 0 : break;
6016 6 : case LCS_FORUPDATE:
6017 6 : appendContextKeyword(context, " FOR UPDATE",
6018 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6019 6 : break;
6020 : }
6021 :
6022 6 : appendStringInfo(buf, " OF %s",
6023 6 : quote_identifier(get_rtable_name(rc->rti,
6024 : context)));
6025 6 : if (rc->waitPolicy == LockWaitError)
6026 0 : appendStringInfoString(buf, " NOWAIT");
6027 6 : else if (rc->waitPolicy == LockWaitSkip)
6028 0 : appendStringInfoString(buf, " SKIP LOCKED");
6029 : }
6030 : }
6031 5616 : }
6032 :
6033 : /*
6034 : * Detect whether query looks like SELECT ... FROM VALUES(),
6035 : * with no need to rename the output columns of the VALUES RTE.
6036 : * If so, return the VALUES RTE. Otherwise return NULL.
6037 : */
6038 : static RangeTblEntry *
6039 5440 : get_simple_values_rte(Query *query, TupleDesc resultDesc)
6040 : {
6041 5440 : RangeTblEntry *result = NULL;
6042 : ListCell *lc;
6043 :
6044 : /*
6045 : * We want to detect a match even if the Query also contains OLD or NEW
6046 : * rule RTEs. So the idea is to scan the rtable and see if there is only
6047 : * one inFromCl RTE that is a VALUES RTE.
6048 : */
6049 5866 : foreach(lc, query->rtable)
6050 : {
6051 4956 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
6052 :
6053 4956 : if (rte->rtekind == RTE_VALUES && rte->inFromCl)
6054 : {
6055 246 : if (result)
6056 4530 : return NULL; /* multiple VALUES (probably not possible) */
6057 246 : result = rte;
6058 : }
6059 4710 : else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
6060 180 : continue; /* ignore rule entries */
6061 : else
6062 4530 : return NULL; /* something else -> not simple VALUES */
6063 : }
6064 :
6065 : /*
6066 : * We don't need to check the targetlist in any great detail, because
6067 : * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
6068 : * appear inside auto-generated sub-queries with very restricted
6069 : * structure. However, DefineView might have modified the tlist by
6070 : * injecting new column aliases, or we might have some other column
6071 : * aliases forced by a resultDesc. We can only simplify if the RTE's
6072 : * column names match the names that get_target_list() would select.
6073 : */
6074 910 : if (result)
6075 : {
6076 : ListCell *lcn;
6077 : int colno;
6078 :
6079 246 : if (list_length(query->targetList) != list_length(result->eref->colnames))
6080 0 : return NULL; /* this probably cannot happen */
6081 246 : colno = 0;
6082 890 : forboth(lc, query->targetList, lcn, result->eref->colnames)
6083 : {
6084 656 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
6085 656 : char *cname = strVal(lfirst(lcn));
6086 : char *colname;
6087 :
6088 656 : if (tle->resjunk)
6089 12 : return NULL; /* this probably cannot happen */
6090 :
6091 : /* compute name that get_target_list would use for column */
6092 656 : colno++;
6093 656 : if (resultDesc && colno <= resultDesc->natts)
6094 30 : colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6095 : else
6096 626 : colname = tle->resname;
6097 :
6098 : /* does it match the VALUES RTE? */
6099 656 : if (colname == NULL || strcmp(colname, cname) != 0)
6100 12 : return NULL; /* column name has been changed */
6101 : }
6102 : }
6103 :
6104 898 : return result;
6105 : }
6106 :
6107 : static void
6108 5440 : get_basic_select_query(Query *query, deparse_context *context)
6109 : {
6110 5440 : StringInfo buf = context->buf;
6111 : RangeTblEntry *values_rte;
6112 : char *sep;
6113 : ListCell *l;
6114 :
6115 5440 : if (PRETTY_INDENT(context))
6116 : {
6117 5382 : context->indentLevel += PRETTYINDENT_STD;
6118 5382 : appendStringInfoChar(buf, ' ');
6119 : }
6120 :
6121 : /*
6122 : * If the query looks like SELECT * FROM (VALUES ...), then print just the
6123 : * VALUES part. This reverses what transformValuesClause() did at parse
6124 : * time.
6125 : */
6126 5440 : values_rte = get_simple_values_rte(query, context->resultDesc);
6127 5440 : if (values_rte)
6128 : {
6129 234 : get_values_def(values_rte->values_lists, context);
6130 234 : return;
6131 : }
6132 :
6133 : /*
6134 : * Build up the query string - first we say SELECT
6135 : */
6136 5206 : if (query->isReturn)
6137 64 : appendStringInfoString(buf, "RETURN");
6138 : else
6139 5142 : appendStringInfoString(buf, "SELECT");
6140 :
6141 : /* Add the DISTINCT clause if given */
6142 5206 : if (query->distinctClause != NIL)
6143 : {
6144 0 : if (query->hasDistinctOn)
6145 : {
6146 0 : appendStringInfoString(buf, " DISTINCT ON (");
6147 0 : sep = "";
6148 0 : foreach(l, query->distinctClause)
6149 : {
6150 0 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6151 :
6152 0 : appendStringInfoString(buf, sep);
6153 0 : get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6154 : false, context);
6155 0 : sep = ", ";
6156 : }
6157 0 : appendStringInfoChar(buf, ')');
6158 : }
6159 : else
6160 0 : appendStringInfoString(buf, " DISTINCT");
6161 : }
6162 :
6163 : /* Then we tell what to select (the targetlist) */
6164 5206 : get_target_list(query->targetList, context);
6165 :
6166 : /* Add the FROM clause if needed */
6167 5206 : get_from_clause(query, " FROM ", context);
6168 :
6169 : /* Add the WHERE clause if given */
6170 5206 : if (query->jointree->quals != NULL)
6171 : {
6172 1632 : appendContextKeyword(context, " WHERE ",
6173 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6174 1632 : get_rule_expr(query->jointree->quals, context, false);
6175 : }
6176 :
6177 : /* Add the GROUP BY clause if given */
6178 5206 : if (query->groupClause != NULL || query->groupingSets != NULL)
6179 : {
6180 : bool save_ingroupby;
6181 :
6182 260 : appendContextKeyword(context, " GROUP BY ",
6183 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6184 260 : if (query->groupDistinct)
6185 0 : appendStringInfoString(buf, "DISTINCT ");
6186 :
6187 260 : save_ingroupby = context->inGroupBy;
6188 260 : context->inGroupBy = true;
6189 :
6190 260 : if (query->groupingSets == NIL)
6191 : {
6192 254 : sep = "";
6193 572 : foreach(l, query->groupClause)
6194 : {
6195 318 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6196 :
6197 318 : appendStringInfoString(buf, sep);
6198 318 : get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
6199 : false, context);
6200 318 : sep = ", ";
6201 : }
6202 : }
6203 : else
6204 : {
6205 6 : sep = "";
6206 12 : foreach(l, query->groupingSets)
6207 : {
6208 6 : GroupingSet *grp = lfirst(l);
6209 :
6210 6 : appendStringInfoString(buf, sep);
6211 6 : get_rule_groupingset(grp, query->targetList, true, context);
6212 6 : sep = ", ";
6213 : }
6214 : }
6215 :
6216 260 : context->inGroupBy = save_ingroupby;
6217 : }
6218 :
6219 : /* Add the HAVING clause if given */
6220 5206 : if (query->havingQual != NULL)
6221 : {
6222 16 : appendContextKeyword(context, " HAVING ",
6223 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6224 16 : get_rule_expr(query->havingQual, context, false);
6225 : }
6226 :
6227 : /* Add the WINDOW clause if needed */
6228 5206 : if (query->windowClause != NIL)
6229 42 : get_rule_windowclause(query, context);
6230 : }
6231 :
6232 : /* ----------
6233 : * get_target_list - Parse back a SELECT target list
6234 : *
6235 : * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.
6236 : * ----------
6237 : */
6238 : static void
6239 5382 : get_target_list(List *targetList, deparse_context *context)
6240 : {
6241 5382 : StringInfo buf = context->buf;
6242 : StringInfoData targetbuf;
6243 5382 : bool last_was_multiline = false;
6244 : char *sep;
6245 : int colno;
6246 : ListCell *l;
6247 :
6248 : /* we use targetbuf to hold each TLE's text temporarily */
6249 5382 : initStringInfo(&targetbuf);
6250 :
6251 5382 : sep = " ";
6252 5382 : colno = 0;
6253 26710 : foreach(l, targetList)
6254 : {
6255 21328 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6256 : char *colname;
6257 : char *attname;
6258 :
6259 21328 : if (tle->resjunk)
6260 40 : continue; /* ignore junk entries */
6261 :
6262 21288 : appendStringInfoString(buf, sep);
6263 21288 : sep = ", ";
6264 21288 : colno++;
6265 :
6266 : /*
6267 : * Put the new field text into targetbuf so we can decide after we've
6268 : * got it whether or not it needs to go on a new line.
6269 : */
6270 21288 : resetStringInfo(&targetbuf);
6271 21288 : context->buf = &targetbuf;
6272 :
6273 : /*
6274 : * We special-case Var nodes rather than using get_rule_expr. This is
6275 : * needed because get_rule_expr will display a whole-row Var as
6276 : * "foo.*", which is the preferred notation in most contexts, but at
6277 : * the top level of a SELECT list it's not right (the parser will
6278 : * expand that notation into multiple columns, yielding behavior
6279 : * different from a whole-row Var). We need to call get_variable
6280 : * directly so that we can tell it to do the right thing, and so that
6281 : * we can get the attribute name which is the default AS label.
6282 : */
6283 21288 : if (tle->expr && (IsA(tle->expr, Var)))
6284 : {
6285 16548 : attname = get_variable((Var *) tle->expr, 0, true, context);
6286 : }
6287 : else
6288 : {
6289 4740 : get_rule_expr((Node *) tle->expr, context, true);
6290 :
6291 : /*
6292 : * When colNamesVisible is true, we should always show the
6293 : * assigned column name explicitly. Otherwise, show it only if
6294 : * it's not FigureColname's fallback.
6295 : */
6296 4740 : attname = context->colNamesVisible ? NULL : "?column?";
6297 : }
6298 :
6299 : /*
6300 : * Figure out what the result column should be called. In the context
6301 : * of a view, use the view's tuple descriptor (so as to pick up the
6302 : * effects of any column RENAME that's been done on the view).
6303 : * Otherwise, just use what we can find in the TLE.
6304 : */
6305 21288 : if (context->resultDesc && colno <= context->resultDesc->natts)
6306 19340 : colname = NameStr(TupleDescAttr(context->resultDesc,
6307 : colno - 1)->attname);
6308 : else
6309 1948 : colname = tle->resname;
6310 :
6311 : /* Show AS unless the column's name is correct as-is */
6312 21288 : if (colname) /* resname could be NULL */
6313 : {
6314 21224 : if (attname == NULL || strcmp(attname, colname) != 0)
6315 6638 : appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
6316 : }
6317 :
6318 : /* Restore context's output buffer */
6319 21288 : context->buf = buf;
6320 :
6321 : /* Consider line-wrapping if enabled */
6322 21288 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
6323 : {
6324 : int leading_nl_pos;
6325 :
6326 : /* Does the new field start with a new line? */
6327 21230 : if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
6328 462 : leading_nl_pos = 0;
6329 : else
6330 20768 : leading_nl_pos = -1;
6331 :
6332 : /* If so, we shouldn't add anything */
6333 21230 : if (leading_nl_pos >= 0)
6334 : {
6335 : /* instead, remove any trailing spaces currently in buf */
6336 462 : removeStringInfoSpaces(buf);
6337 : }
6338 : else
6339 : {
6340 : char *trailing_nl;
6341 :
6342 : /* Locate the start of the current line in the output buffer */
6343 20768 : trailing_nl = strrchr(buf->data, '\n');
6344 20768 : if (trailing_nl == NULL)
6345 6506 : trailing_nl = buf->data;
6346 : else
6347 14262 : trailing_nl++;
6348 :
6349 : /*
6350 : * Add a newline, plus some indentation, if the new field is
6351 : * not the first and either the new field would cause an
6352 : * overflow or the last field used more than one line.
6353 : */
6354 20768 : if (colno > 1 &&
6355 15466 : ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
6356 : last_was_multiline))
6357 15466 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
6358 : PRETTYINDENT_STD, PRETTYINDENT_VAR);
6359 : }
6360 :
6361 : /* Remember this field's multiline status for next iteration */
6362 21230 : last_was_multiline =
6363 21230 : (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
6364 : }
6365 :
6366 : /* Add the new field */
6367 21288 : appendBinaryStringInfo(buf, targetbuf.data, targetbuf.len);
6368 : }
6369 :
6370 : /* clean up */
6371 5382 : pfree(targetbuf.data);
6372 5382 : }
6373 :
6374 : static void
6375 176 : get_returning_clause(Query *query, deparse_context *context)
6376 : {
6377 176 : StringInfo buf = context->buf;
6378 :
6379 176 : if (query->returningList)
6380 : {
6381 176 : bool have_with = false;
6382 :
6383 176 : appendContextKeyword(context, " RETURNING",
6384 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6385 :
6386 : /* Add WITH (OLD/NEW) options, if they're not the defaults */
6387 176 : if (query->returningOldAlias && strcmp(query->returningOldAlias, "old") != 0)
6388 : {
6389 18 : appendStringInfo(buf, " WITH (OLD AS %s",
6390 18 : quote_identifier(query->returningOldAlias));
6391 18 : have_with = true;
6392 : }
6393 176 : if (query->returningNewAlias && strcmp(query->returningNewAlias, "new") != 0)
6394 : {
6395 18 : if (have_with)
6396 12 : appendStringInfo(buf, ", NEW AS %s",
6397 12 : quote_identifier(query->returningNewAlias));
6398 : else
6399 : {
6400 6 : appendStringInfo(buf, " WITH (NEW AS %s",
6401 6 : quote_identifier(query->returningNewAlias));
6402 6 : have_with = true;
6403 : }
6404 : }
6405 176 : if (have_with)
6406 24 : appendStringInfoChar(buf, ')');
6407 :
6408 : /* Add the returning expressions themselves */
6409 176 : get_target_list(query->returningList, context);
6410 : }
6411 176 : }
6412 :
6413 : static void
6414 792 : get_setop_query(Node *setOp, Query *query, deparse_context *context)
6415 : {
6416 792 : StringInfo buf = context->buf;
6417 : bool need_paren;
6418 :
6419 : /* Guard against excessively long or deeply-nested queries */
6420 792 : CHECK_FOR_INTERRUPTS();
6421 792 : check_stack_depth();
6422 :
6423 792 : if (IsA(setOp, RangeTblRef))
6424 : {
6425 484 : RangeTblRef *rtr = (RangeTblRef *) setOp;
6426 484 : RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
6427 484 : Query *subquery = rte->subquery;
6428 :
6429 : Assert(subquery != NULL);
6430 :
6431 : /*
6432 : * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.
6433 : * Also add parens if the leaf query contains its own set operations.
6434 : * (That shouldn't happen unless one of the other clauses is also
6435 : * present, see transformSetOperationTree; but let's be safe.)
6436 : */
6437 1452 : need_paren = (subquery->cteList ||
6438 484 : subquery->sortClause ||
6439 484 : subquery->rowMarks ||
6440 484 : subquery->limitOffset ||
6441 1452 : subquery->limitCount ||
6442 484 : subquery->setOperations);
6443 484 : if (need_paren)
6444 0 : appendStringInfoChar(buf, '(');
6445 484 : get_query_def(subquery, buf, context->namespaces,
6446 484 : context->resultDesc, context->colNamesVisible,
6447 : context->prettyFlags, context->wrapColumn,
6448 : context->indentLevel);
6449 484 : if (need_paren)
6450 0 : appendStringInfoChar(buf, ')');
6451 : }
6452 308 : else if (IsA(setOp, SetOperationStmt))
6453 : {
6454 308 : SetOperationStmt *op = (SetOperationStmt *) setOp;
6455 : int subindent;
6456 : bool save_colnamesvisible;
6457 :
6458 : /*
6459 : * We force parens when nesting two SetOperationStmts, except when the
6460 : * lefthand input is another setop of the same kind. Syntactically,
6461 : * we could omit parens in rather more cases, but it seems best to use
6462 : * parens to flag cases where the setop operator changes. If we use
6463 : * parens, we also increase the indentation level for the child query.
6464 : *
6465 : * There are some cases in which parens are needed around a leaf query
6466 : * too, but those are more easily handled at the next level down (see
6467 : * code above).
6468 : */
6469 308 : if (IsA(op->larg, SetOperationStmt))
6470 : {
6471 132 : SetOperationStmt *lop = (SetOperationStmt *) op->larg;
6472 :
6473 132 : if (op->op == lop->op && op->all == lop->all)
6474 132 : need_paren = false;
6475 : else
6476 0 : need_paren = true;
6477 : }
6478 : else
6479 176 : need_paren = false;
6480 :
6481 308 : if (need_paren)
6482 : {
6483 0 : appendStringInfoChar(buf, '(');
6484 0 : subindent = PRETTYINDENT_STD;
6485 0 : appendContextKeyword(context, "", subindent, 0, 0);
6486 : }
6487 : else
6488 308 : subindent = 0;
6489 :
6490 308 : get_setop_query(op->larg, query, context);
6491 :
6492 308 : if (need_paren)
6493 0 : appendContextKeyword(context, ") ", -subindent, 0, 0);
6494 308 : else if (PRETTY_INDENT(context))
6495 308 : appendContextKeyword(context, "", -subindent, 0, 0);
6496 : else
6497 0 : appendStringInfoChar(buf, ' ');
6498 :
6499 308 : switch (op->op)
6500 : {
6501 308 : case SETOP_UNION:
6502 308 : appendStringInfoString(buf, "UNION ");
6503 308 : break;
6504 0 : case SETOP_INTERSECT:
6505 0 : appendStringInfoString(buf, "INTERSECT ");
6506 0 : break;
6507 0 : case SETOP_EXCEPT:
6508 0 : appendStringInfoString(buf, "EXCEPT ");
6509 0 : break;
6510 0 : default:
6511 0 : elog(ERROR, "unrecognized set op: %d",
6512 : (int) op->op);
6513 : }
6514 308 : if (op->all)
6515 296 : appendStringInfoString(buf, "ALL ");
6516 :
6517 : /* Always parenthesize if RHS is another setop */
6518 308 : need_paren = IsA(op->rarg, SetOperationStmt);
6519 :
6520 : /*
6521 : * The indentation code here is deliberately a bit different from that
6522 : * for the lefthand input, because we want the line breaks in
6523 : * different places.
6524 : */
6525 308 : if (need_paren)
6526 : {
6527 0 : appendStringInfoChar(buf, '(');
6528 0 : subindent = PRETTYINDENT_STD;
6529 : }
6530 : else
6531 308 : subindent = 0;
6532 308 : appendContextKeyword(context, "", subindent, 0, 0);
6533 :
6534 : /*
6535 : * The output column names of the RHS sub-select don't matter.
6536 : */
6537 308 : save_colnamesvisible = context->colNamesVisible;
6538 308 : context->colNamesVisible = false;
6539 :
6540 308 : get_setop_query(op->rarg, query, context);
6541 :
6542 308 : context->colNamesVisible = save_colnamesvisible;
6543 :
6544 308 : if (PRETTY_INDENT(context))
6545 308 : context->indentLevel -= subindent;
6546 308 : if (need_paren)
6547 0 : appendContextKeyword(context, ")", 0, 0, 0);
6548 : }
6549 : else
6550 : {
6551 0 : elog(ERROR, "unrecognized node type: %d",
6552 : (int) nodeTag(setOp));
6553 : }
6554 792 : }
6555 :
6556 : /*
6557 : * Display a sort/group clause.
6558 : *
6559 : * Also returns the expression tree, so caller need not find it again.
6560 : */
6561 : static Node *
6562 748 : get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
6563 : deparse_context *context)
6564 : {
6565 748 : StringInfo buf = context->buf;
6566 : TargetEntry *tle;
6567 : Node *expr;
6568 :
6569 748 : tle = get_sortgroupref_tle(ref, tlist);
6570 748 : expr = (Node *) tle->expr;
6571 :
6572 : /*
6573 : * Use column-number form if requested by caller. Otherwise, if
6574 : * expression is a constant, force it to be dumped with an explicit cast
6575 : * as decoration --- this is because a simple integer constant is
6576 : * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
6577 : * we dump it without any decoration. Similarly, if it's just a Var,
6578 : * there is risk of misinterpretation if the column name is reassigned in
6579 : * the SELECT list, so we may need to force table qualification. And, if
6580 : * it's anything more complex than a simple Var, then force extra parens
6581 : * around it, to ensure it can't be misinterpreted as a cube() or rollup()
6582 : * construct.
6583 : */
6584 748 : if (force_colno)
6585 : {
6586 : Assert(!tle->resjunk);
6587 12 : appendStringInfo(buf, "%d", tle->resno);
6588 : }
6589 736 : else if (!expr)
6590 : /* do nothing, probably can't happen */ ;
6591 736 : else if (IsA(expr, Const))
6592 0 : get_const_expr((Const *) expr, context, 1);
6593 736 : else if (IsA(expr, Var))
6594 : {
6595 : /* Tell get_variable to check for name conflict */
6596 708 : bool save_varinorderby = context->varInOrderBy;
6597 :
6598 708 : context->varInOrderBy = true;
6599 708 : (void) get_variable((Var *) expr, 0, false, context);
6600 708 : context->varInOrderBy = save_varinorderby;
6601 : }
6602 : else
6603 : {
6604 : /*
6605 : * We must force parens for function-like expressions even if
6606 : * PRETTY_PAREN is off, since those are the ones in danger of
6607 : * misparsing. For other expressions we need to force them only if
6608 : * PRETTY_PAREN is on, since otherwise the expression will output them
6609 : * itself. (We can't skip the parens.)
6610 : */
6611 56 : bool need_paren = (PRETTY_PAREN(context)
6612 28 : || IsA(expr, FuncExpr)
6613 24 : || IsA(expr, Aggref)
6614 24 : || IsA(expr, WindowFunc)
6615 56 : || IsA(expr, JsonConstructorExpr));
6616 :
6617 28 : if (need_paren)
6618 4 : appendStringInfoChar(context->buf, '(');
6619 28 : get_rule_expr(expr, context, true);
6620 28 : if (need_paren)
6621 4 : appendStringInfoChar(context->buf, ')');
6622 : }
6623 :
6624 748 : return expr;
6625 : }
6626 :
6627 : /*
6628 : * Display a GroupingSet
6629 : */
6630 : static void
6631 18 : get_rule_groupingset(GroupingSet *gset, List *targetlist,
6632 : bool omit_parens, deparse_context *context)
6633 : {
6634 : ListCell *l;
6635 18 : StringInfo buf = context->buf;
6636 18 : bool omit_child_parens = true;
6637 18 : char *sep = "";
6638 :
6639 18 : switch (gset->kind)
6640 : {
6641 0 : case GROUPING_SET_EMPTY:
6642 0 : appendStringInfoString(buf, "()");
6643 0 : return;
6644 :
6645 12 : case GROUPING_SET_SIMPLE:
6646 : {
6647 12 : if (!omit_parens || list_length(gset->content) != 1)
6648 12 : appendStringInfoChar(buf, '(');
6649 :
6650 42 : foreach(l, gset->content)
6651 : {
6652 30 : Index ref = lfirst_int(l);
6653 :
6654 30 : appendStringInfoString(buf, sep);
6655 30 : get_rule_sortgroupclause(ref, targetlist,
6656 : false, context);
6657 30 : sep = ", ";
6658 : }
6659 :
6660 12 : if (!omit_parens || list_length(gset->content) != 1)
6661 12 : appendStringInfoChar(buf, ')');
6662 : }
6663 12 : return;
6664 :
6665 6 : case GROUPING_SET_ROLLUP:
6666 6 : appendStringInfoString(buf, "ROLLUP(");
6667 6 : break;
6668 0 : case GROUPING_SET_CUBE:
6669 0 : appendStringInfoString(buf, "CUBE(");
6670 0 : break;
6671 0 : case GROUPING_SET_SETS:
6672 0 : appendStringInfoString(buf, "GROUPING SETS (");
6673 0 : omit_child_parens = false;
6674 0 : break;
6675 : }
6676 :
6677 18 : foreach(l, gset->content)
6678 : {
6679 12 : appendStringInfoString(buf, sep);
6680 12 : get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
6681 12 : sep = ", ";
6682 : }
6683 :
6684 6 : appendStringInfoChar(buf, ')');
6685 : }
6686 :
6687 : /*
6688 : * Display an ORDER BY list.
6689 : */
6690 : static void
6691 368 : get_rule_orderby(List *orderList, List *targetList,
6692 : bool force_colno, deparse_context *context)
6693 : {
6694 368 : StringInfo buf = context->buf;
6695 : const char *sep;
6696 : ListCell *l;
6697 :
6698 368 : sep = "";
6699 768 : foreach(l, orderList)
6700 : {
6701 400 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6702 : Node *sortexpr;
6703 : Oid sortcoltype;
6704 : TypeCacheEntry *typentry;
6705 :
6706 400 : appendStringInfoString(buf, sep);
6707 400 : sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
6708 : force_colno, context);
6709 400 : sortcoltype = exprType(sortexpr);
6710 : /* See whether operator is default < or > for datatype */
6711 400 : typentry = lookup_type_cache(sortcoltype,
6712 : TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
6713 400 : if (srt->sortop == typentry->lt_opr)
6714 : {
6715 : /* ASC is default, so emit nothing for it */
6716 372 : if (srt->nulls_first)
6717 0 : appendStringInfoString(buf, " NULLS FIRST");
6718 : }
6719 28 : else if (srt->sortop == typentry->gt_opr)
6720 : {
6721 10 : appendStringInfoString(buf, " DESC");
6722 : /* DESC defaults to NULLS FIRST */
6723 10 : if (!srt->nulls_first)
6724 2 : appendStringInfoString(buf, " NULLS LAST");
6725 : }
6726 : else
6727 : {
6728 18 : appendStringInfo(buf, " USING %s",
6729 : generate_operator_name(srt->sortop,
6730 : sortcoltype,
6731 : sortcoltype));
6732 : /* be specific to eliminate ambiguity */
6733 18 : if (srt->nulls_first)
6734 0 : appendStringInfoString(buf, " NULLS FIRST");
6735 : else
6736 18 : appendStringInfoString(buf, " NULLS LAST");
6737 : }
6738 400 : sep = ", ";
6739 : }
6740 368 : }
6741 :
6742 : /*
6743 : * Display a WINDOW clause.
6744 : *
6745 : * Note that the windowClause list might contain only anonymous window
6746 : * specifications, in which case we should print nothing here.
6747 : */
6748 : static void
6749 42 : get_rule_windowclause(Query *query, deparse_context *context)
6750 : {
6751 42 : StringInfo buf = context->buf;
6752 : const char *sep;
6753 : ListCell *l;
6754 :
6755 42 : sep = NULL;
6756 84 : foreach(l, query->windowClause)
6757 : {
6758 42 : WindowClause *wc = (WindowClause *) lfirst(l);
6759 :
6760 42 : if (wc->name == NULL)
6761 42 : continue; /* ignore anonymous windows */
6762 :
6763 0 : if (sep == NULL)
6764 0 : appendContextKeyword(context, " WINDOW ",
6765 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6766 : else
6767 0 : appendStringInfoString(buf, sep);
6768 :
6769 0 : appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
6770 :
6771 0 : get_rule_windowspec(wc, query->targetList, context);
6772 :
6773 0 : sep = ", ";
6774 : }
6775 42 : }
6776 :
6777 : /*
6778 : * Display a window definition
6779 : */
6780 : static void
6781 42 : get_rule_windowspec(WindowClause *wc, List *targetList,
6782 : deparse_context *context)
6783 : {
6784 42 : StringInfo buf = context->buf;
6785 42 : bool needspace = false;
6786 : const char *sep;
6787 : ListCell *l;
6788 :
6789 42 : appendStringInfoChar(buf, '(');
6790 42 : if (wc->refname)
6791 : {
6792 0 : appendStringInfoString(buf, quote_identifier(wc->refname));
6793 0 : needspace = true;
6794 : }
6795 : /* partition clauses are always inherited, so only print if no refname */
6796 42 : if (wc->partitionClause && !wc->refname)
6797 : {
6798 0 : if (needspace)
6799 0 : appendStringInfoChar(buf, ' ');
6800 0 : appendStringInfoString(buf, "PARTITION BY ");
6801 0 : sep = "";
6802 0 : foreach(l, wc->partitionClause)
6803 : {
6804 0 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6805 :
6806 0 : appendStringInfoString(buf, sep);
6807 0 : get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
6808 : false, context);
6809 0 : sep = ", ";
6810 : }
6811 0 : needspace = true;
6812 : }
6813 : /* print ordering clause only if not inherited */
6814 42 : if (wc->orderClause && !wc->copiedOrder)
6815 : {
6816 42 : if (needspace)
6817 0 : appendStringInfoChar(buf, ' ');
6818 42 : appendStringInfoString(buf, "ORDER BY ");
6819 42 : get_rule_orderby(wc->orderClause, targetList, false, context);
6820 42 : needspace = true;
6821 : }
6822 : /* framing clause is never inherited, so print unless it's default */
6823 42 : if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
6824 : {
6825 42 : if (needspace)
6826 42 : appendStringInfoChar(buf, ' ');
6827 42 : get_window_frame_options(wc->frameOptions,
6828 : wc->startOffset, wc->endOffset,
6829 : context);
6830 : }
6831 42 : appendStringInfoChar(buf, ')');
6832 42 : }
6833 :
6834 : /*
6835 : * Append the description of a window's framing options to context->buf
6836 : */
6837 : static void
6838 232 : get_window_frame_options(int frameOptions,
6839 : Node *startOffset, Node *endOffset,
6840 : deparse_context *context)
6841 : {
6842 232 : StringInfo buf = context->buf;
6843 :
6844 232 : if (frameOptions & FRAMEOPTION_NONDEFAULT)
6845 : {
6846 232 : if (frameOptions & FRAMEOPTION_RANGE)
6847 20 : appendStringInfoString(buf, "RANGE ");
6848 212 : else if (frameOptions & FRAMEOPTION_ROWS)
6849 200 : appendStringInfoString(buf, "ROWS ");
6850 12 : else if (frameOptions & FRAMEOPTION_GROUPS)
6851 12 : appendStringInfoString(buf, "GROUPS ");
6852 : else
6853 : Assert(false);
6854 232 : if (frameOptions & FRAMEOPTION_BETWEEN)
6855 92 : appendStringInfoString(buf, "BETWEEN ");
6856 232 : if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
6857 146 : appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
6858 86 : else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
6859 26 : appendStringInfoString(buf, "CURRENT ROW ");
6860 60 : else if (frameOptions & FRAMEOPTION_START_OFFSET)
6861 : {
6862 60 : get_rule_expr(startOffset, context, false);
6863 60 : if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
6864 60 : appendStringInfoString(buf, " PRECEDING ");
6865 0 : else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
6866 0 : appendStringInfoString(buf, " FOLLOWING ");
6867 : else
6868 : Assert(false);
6869 : }
6870 : else
6871 : Assert(false);
6872 232 : if (frameOptions & FRAMEOPTION_BETWEEN)
6873 : {
6874 92 : appendStringInfoString(buf, "AND ");
6875 92 : if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
6876 20 : appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
6877 72 : else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
6878 6 : appendStringInfoString(buf, "CURRENT ROW ");
6879 66 : else if (frameOptions & FRAMEOPTION_END_OFFSET)
6880 : {
6881 66 : get_rule_expr(endOffset, context, false);
6882 66 : if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
6883 0 : appendStringInfoString(buf, " PRECEDING ");
6884 66 : else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
6885 66 : appendStringInfoString(buf, " FOLLOWING ");
6886 : else
6887 : Assert(false);
6888 : }
6889 : else
6890 : Assert(false);
6891 : }
6892 232 : if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
6893 6 : appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
6894 226 : else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
6895 6 : appendStringInfoString(buf, "EXCLUDE GROUP ");
6896 220 : else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
6897 6 : appendStringInfoString(buf, "EXCLUDE TIES ");
6898 : /* we will now have a trailing space; remove it */
6899 232 : buf->data[--(buf->len)] = '\0';
6900 : }
6901 232 : }
6902 :
6903 : /*
6904 : * Return the description of a window's framing options as a palloc'd string
6905 : */
6906 : char *
6907 190 : get_window_frame_options_for_explain(int frameOptions,
6908 : Node *startOffset, Node *endOffset,
6909 : List *dpcontext, bool forceprefix)
6910 : {
6911 : StringInfoData buf;
6912 : deparse_context context;
6913 :
6914 190 : initStringInfo(&buf);
6915 190 : context.buf = &buf;
6916 190 : context.namespaces = dpcontext;
6917 190 : context.resultDesc = NULL;
6918 190 : context.targetList = NIL;
6919 190 : context.windowClause = NIL;
6920 190 : context.varprefix = forceprefix;
6921 190 : context.prettyFlags = 0;
6922 190 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
6923 190 : context.indentLevel = 0;
6924 190 : context.colNamesVisible = true;
6925 190 : context.inGroupBy = false;
6926 190 : context.varInOrderBy = false;
6927 190 : context.appendparents = NULL;
6928 :
6929 190 : get_window_frame_options(frameOptions, startOffset, endOffset, &context);
6930 :
6931 190 : return buf.data;
6932 : }
6933 :
6934 : /* ----------
6935 : * get_insert_query_def - Parse back an INSERT parsetree
6936 : * ----------
6937 : */
6938 : static void
6939 472 : get_insert_query_def(Query *query, deparse_context *context)
6940 : {
6941 472 : StringInfo buf = context->buf;
6942 472 : RangeTblEntry *select_rte = NULL;
6943 472 : RangeTblEntry *values_rte = NULL;
6944 : RangeTblEntry *rte;
6945 : char *sep;
6946 : ListCell *l;
6947 : List *strippedexprs;
6948 :
6949 : /* Insert the WITH clause if given */
6950 472 : get_with_clause(query, context);
6951 :
6952 : /*
6953 : * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
6954 : * single RTE for the SELECT or VALUES. Plain VALUES has neither.
6955 : */
6956 1838 : foreach(l, query->rtable)
6957 : {
6958 1366 : rte = (RangeTblEntry *) lfirst(l);
6959 :
6960 1366 : if (rte->rtekind == RTE_SUBQUERY)
6961 : {
6962 62 : if (select_rte)
6963 0 : elog(ERROR, "too many subquery RTEs in INSERT");
6964 62 : select_rte = rte;
6965 : }
6966 :
6967 1366 : if (rte->rtekind == RTE_VALUES)
6968 : {
6969 56 : if (values_rte)
6970 0 : elog(ERROR, "too many values RTEs in INSERT");
6971 56 : values_rte = rte;
6972 : }
6973 : }
6974 472 : if (select_rte && values_rte)
6975 0 : elog(ERROR, "both subquery and values RTEs in INSERT");
6976 :
6977 : /*
6978 : * Start the query with INSERT INTO relname
6979 : */
6980 472 : rte = rt_fetch(query->resultRelation, query->rtable);
6981 : Assert(rte->rtekind == RTE_RELATION);
6982 :
6983 472 : if (PRETTY_INDENT(context))
6984 : {
6985 472 : context->indentLevel += PRETTYINDENT_STD;
6986 472 : appendStringInfoChar(buf, ' ');
6987 : }
6988 472 : appendStringInfo(buf, "INSERT INTO %s",
6989 : generate_relation_name(rte->relid, NIL));
6990 :
6991 : /* Print the relation alias, if needed; INSERT requires explicit AS */
6992 472 : get_rte_alias(rte, query->resultRelation, true, context);
6993 :
6994 : /* always want a space here */
6995 472 : appendStringInfoChar(buf, ' ');
6996 :
6997 : /*
6998 : * Add the insert-column-names list. Any indirection decoration needed on
6999 : * the column names can be inferred from the top targetlist.
7000 : */
7001 472 : strippedexprs = NIL;
7002 472 : sep = "";
7003 472 : if (query->targetList)
7004 472 : appendStringInfoChar(buf, '(');
7005 1764 : foreach(l, query->targetList)
7006 : {
7007 1292 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7008 :
7009 1292 : if (tle->resjunk)
7010 0 : continue; /* ignore junk entries */
7011 :
7012 1292 : appendStringInfoString(buf, sep);
7013 1292 : sep = ", ";
7014 :
7015 : /*
7016 : * Put out name of target column; look in the catalogs, not at
7017 : * tle->resname, since resname will fail to track RENAME.
7018 : */
7019 1292 : appendStringInfoString(buf,
7020 1292 : quote_identifier(get_attname(rte->relid,
7021 1292 : tle->resno,
7022 : false)));
7023 :
7024 : /*
7025 : * Print any indirection needed (subfields or subscripts), and strip
7026 : * off the top-level nodes representing the indirection assignments.
7027 : * Add the stripped expressions to strippedexprs. (If it's a
7028 : * single-VALUES statement, the stripped expressions are the VALUES to
7029 : * print below. Otherwise they're just Vars and not really
7030 : * interesting.)
7031 : */
7032 1292 : strippedexprs = lappend(strippedexprs,
7033 1292 : processIndirection((Node *) tle->expr,
7034 : context));
7035 : }
7036 472 : if (query->targetList)
7037 472 : appendStringInfoString(buf, ") ");
7038 :
7039 472 : if (query->override)
7040 : {
7041 0 : if (query->override == OVERRIDING_SYSTEM_VALUE)
7042 0 : appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
7043 0 : else if (query->override == OVERRIDING_USER_VALUE)
7044 0 : appendStringInfoString(buf, "OVERRIDING USER VALUE ");
7045 : }
7046 :
7047 472 : if (select_rte)
7048 : {
7049 : /* Add the SELECT */
7050 62 : get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
7051 : false,
7052 : context->prettyFlags, context->wrapColumn,
7053 : context->indentLevel);
7054 : }
7055 410 : else if (values_rte)
7056 : {
7057 : /* Add the multi-VALUES expression lists */
7058 56 : get_values_def(values_rte->values_lists, context);
7059 : }
7060 354 : else if (strippedexprs)
7061 : {
7062 : /* Add the single-VALUES expression list */
7063 354 : appendContextKeyword(context, "VALUES (",
7064 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7065 354 : get_rule_list_toplevel(strippedexprs, context, false);
7066 354 : appendStringInfoChar(buf, ')');
7067 : }
7068 : else
7069 : {
7070 : /* No expressions, so it must be DEFAULT VALUES */
7071 0 : appendStringInfoString(buf, "DEFAULT VALUES");
7072 : }
7073 :
7074 : /* Add ON CONFLICT if present */
7075 472 : if (query->onConflict)
7076 : {
7077 30 : OnConflictExpr *confl = query->onConflict;
7078 :
7079 30 : appendStringInfoString(buf, " ON CONFLICT");
7080 :
7081 30 : if (confl->arbiterElems)
7082 : {
7083 : /* Add the single-VALUES expression list */
7084 24 : appendStringInfoChar(buf, '(');
7085 24 : get_rule_expr((Node *) confl->arbiterElems, context, false);
7086 24 : appendStringInfoChar(buf, ')');
7087 :
7088 : /* Add a WHERE clause (for partial indexes) if given */
7089 24 : if (confl->arbiterWhere != NULL)
7090 : {
7091 : bool save_varprefix;
7092 :
7093 : /*
7094 : * Force non-prefixing of Vars, since parser assumes that they
7095 : * belong to target relation. WHERE clause does not use
7096 : * InferenceElem, so this is separately required.
7097 : */
7098 12 : save_varprefix = context->varprefix;
7099 12 : context->varprefix = false;
7100 :
7101 12 : appendContextKeyword(context, " WHERE ",
7102 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7103 12 : get_rule_expr(confl->arbiterWhere, context, false);
7104 :
7105 12 : context->varprefix = save_varprefix;
7106 : }
7107 : }
7108 6 : else if (OidIsValid(confl->constraint))
7109 : {
7110 0 : char *constraint = get_constraint_name(confl->constraint);
7111 :
7112 0 : if (!constraint)
7113 0 : elog(ERROR, "cache lookup failed for constraint %u",
7114 : confl->constraint);
7115 0 : appendStringInfo(buf, " ON CONSTRAINT %s",
7116 : quote_identifier(constraint));
7117 : }
7118 :
7119 30 : if (confl->action == ONCONFLICT_NOTHING)
7120 : {
7121 18 : appendStringInfoString(buf, " DO NOTHING");
7122 : }
7123 : else
7124 : {
7125 12 : appendStringInfoString(buf, " DO UPDATE SET ");
7126 : /* Deparse targetlist */
7127 12 : get_update_query_targetlist_def(query, confl->onConflictSet,
7128 : context, rte);
7129 :
7130 : /* Add a WHERE clause if given */
7131 12 : if (confl->onConflictWhere != NULL)
7132 : {
7133 12 : appendContextKeyword(context, " WHERE ",
7134 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7135 12 : get_rule_expr(confl->onConflictWhere, context, false);
7136 : }
7137 : }
7138 : }
7139 :
7140 : /* Add RETURNING if present */
7141 472 : if (query->returningList)
7142 96 : get_returning_clause(query, context);
7143 472 : }
7144 :
7145 :
7146 : /* ----------
7147 : * get_update_query_def - Parse back an UPDATE parsetree
7148 : * ----------
7149 : */
7150 : static void
7151 214 : get_update_query_def(Query *query, deparse_context *context)
7152 : {
7153 214 : StringInfo buf = context->buf;
7154 : RangeTblEntry *rte;
7155 :
7156 : /* Insert the WITH clause if given */
7157 214 : get_with_clause(query, context);
7158 :
7159 : /*
7160 : * Start the query with UPDATE relname SET
7161 : */
7162 214 : rte = rt_fetch(query->resultRelation, query->rtable);
7163 : Assert(rte->rtekind == RTE_RELATION);
7164 214 : if (PRETTY_INDENT(context))
7165 : {
7166 214 : appendStringInfoChar(buf, ' ');
7167 214 : context->indentLevel += PRETTYINDENT_STD;
7168 : }
7169 428 : appendStringInfo(buf, "UPDATE %s%s",
7170 214 : only_marker(rte),
7171 : generate_relation_name(rte->relid, NIL));
7172 :
7173 : /* Print the relation alias, if needed */
7174 214 : get_rte_alias(rte, query->resultRelation, false, context);
7175 :
7176 214 : appendStringInfoString(buf, " SET ");
7177 :
7178 : /* Deparse targetlist */
7179 214 : get_update_query_targetlist_def(query, query->targetList, context, rte);
7180 :
7181 : /* Add the FROM clause if needed */
7182 214 : get_from_clause(query, " FROM ", context);
7183 :
7184 : /* Add a WHERE clause if given */
7185 214 : if (query->jointree->quals != NULL)
7186 : {
7187 168 : appendContextKeyword(context, " WHERE ",
7188 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7189 168 : get_rule_expr(query->jointree->quals, context, false);
7190 : }
7191 :
7192 : /* Add RETURNING if present */
7193 214 : if (query->returningList)
7194 52 : get_returning_clause(query, context);
7195 214 : }
7196 :
7197 :
7198 : /* ----------
7199 : * get_update_query_targetlist_def - Parse back an UPDATE targetlist
7200 : * ----------
7201 : */
7202 : static void
7203 250 : get_update_query_targetlist_def(Query *query, List *targetList,
7204 : deparse_context *context, RangeTblEntry *rte)
7205 : {
7206 250 : StringInfo buf = context->buf;
7207 : ListCell *l;
7208 : ListCell *next_ma_cell;
7209 : int remaining_ma_columns;
7210 : const char *sep;
7211 : SubLink *cur_ma_sublink;
7212 : List *ma_sublinks;
7213 :
7214 : /*
7215 : * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
7216 : * into a list. We expect them to appear, in ID order, in resjunk tlist
7217 : * entries.
7218 : */
7219 250 : ma_sublinks = NIL;
7220 250 : if (query->hasSubLinks) /* else there can't be any */
7221 : {
7222 42 : foreach(l, targetList)
7223 : {
7224 30 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7225 :
7226 30 : if (tle->resjunk && IsA(tle->expr, SubLink))
7227 : {
7228 6 : SubLink *sl = (SubLink *) tle->expr;
7229 :
7230 6 : if (sl->subLinkType == MULTIEXPR_SUBLINK)
7231 : {
7232 6 : ma_sublinks = lappend(ma_sublinks, sl);
7233 : Assert(sl->subLinkId == list_length(ma_sublinks));
7234 : }
7235 : }
7236 : }
7237 : }
7238 250 : next_ma_cell = list_head(ma_sublinks);
7239 250 : cur_ma_sublink = NULL;
7240 250 : remaining_ma_columns = 0;
7241 :
7242 : /* Add the comma separated list of 'attname = value' */
7243 250 : sep = "";
7244 644 : foreach(l, targetList)
7245 : {
7246 394 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7247 : Node *expr;
7248 :
7249 394 : if (tle->resjunk)
7250 6 : continue; /* ignore junk entries */
7251 :
7252 : /* Emit separator (OK whether we're in multiassignment or not) */
7253 388 : appendStringInfoString(buf, sep);
7254 388 : sep = ", ";
7255 :
7256 : /*
7257 : * Check to see if we're starting a multiassignment group: if so,
7258 : * output a left paren.
7259 : */
7260 388 : if (next_ma_cell != NULL && cur_ma_sublink == NULL)
7261 : {
7262 : /*
7263 : * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
7264 : * Param. That could be buried under FieldStores and
7265 : * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
7266 : * and underneath those there could be an implicit type coercion.
7267 : * Because we would ignore implicit type coercions anyway, we
7268 : * don't need to be as careful as processIndirection() is about
7269 : * descending past implicit CoerceToDomains.
7270 : */
7271 6 : expr = (Node *) tle->expr;
7272 12 : while (expr)
7273 : {
7274 12 : if (IsA(expr, FieldStore))
7275 : {
7276 0 : FieldStore *fstore = (FieldStore *) expr;
7277 :
7278 0 : expr = (Node *) linitial(fstore->newvals);
7279 : }
7280 12 : else if (IsA(expr, SubscriptingRef))
7281 : {
7282 6 : SubscriptingRef *sbsref = (SubscriptingRef *) expr;
7283 :
7284 6 : if (sbsref->refassgnexpr == NULL)
7285 0 : break;
7286 :
7287 6 : expr = (Node *) sbsref->refassgnexpr;
7288 : }
7289 6 : else if (IsA(expr, CoerceToDomain))
7290 : {
7291 0 : CoerceToDomain *cdomain = (CoerceToDomain *) expr;
7292 :
7293 0 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
7294 0 : break;
7295 0 : expr = (Node *) cdomain->arg;
7296 : }
7297 : else
7298 6 : break;
7299 : }
7300 6 : expr = strip_implicit_coercions(expr);
7301 :
7302 6 : if (expr && IsA(expr, Param) &&
7303 6 : ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
7304 : {
7305 6 : cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
7306 6 : next_ma_cell = lnext(ma_sublinks, next_ma_cell);
7307 6 : remaining_ma_columns = count_nonjunk_tlist_entries(((Query *) cur_ma_sublink->subselect)->targetList);
7308 : Assert(((Param *) expr)->paramid ==
7309 : ((cur_ma_sublink->subLinkId << 16) | 1));
7310 6 : appendStringInfoChar(buf, '(');
7311 : }
7312 : }
7313 :
7314 : /*
7315 : * Put out name of target column; look in the catalogs, not at
7316 : * tle->resname, since resname will fail to track RENAME.
7317 : */
7318 388 : appendStringInfoString(buf,
7319 388 : quote_identifier(get_attname(rte->relid,
7320 388 : tle->resno,
7321 : false)));
7322 :
7323 : /*
7324 : * Print any indirection needed (subfields or subscripts), and strip
7325 : * off the top-level nodes representing the indirection assignments.
7326 : */
7327 388 : expr = processIndirection((Node *) tle->expr, context);
7328 :
7329 : /*
7330 : * If we're in a multiassignment, skip printing anything more, unless
7331 : * this is the last column; in which case, what we print should be the
7332 : * sublink, not the Param.
7333 : */
7334 388 : if (cur_ma_sublink != NULL)
7335 : {
7336 18 : if (--remaining_ma_columns > 0)
7337 12 : continue; /* not the last column of multiassignment */
7338 6 : appendStringInfoChar(buf, ')');
7339 6 : expr = (Node *) cur_ma_sublink;
7340 6 : cur_ma_sublink = NULL;
7341 : }
7342 :
7343 376 : appendStringInfoString(buf, " = ");
7344 :
7345 376 : get_rule_expr(expr, context, false);
7346 : }
7347 250 : }
7348 :
7349 :
7350 : /* ----------
7351 : * get_delete_query_def - Parse back a DELETE parsetree
7352 : * ----------
7353 : */
7354 : static void
7355 118 : get_delete_query_def(Query *query, deparse_context *context)
7356 : {
7357 118 : StringInfo buf = context->buf;
7358 : RangeTblEntry *rte;
7359 :
7360 : /* Insert the WITH clause if given */
7361 118 : get_with_clause(query, context);
7362 :
7363 : /*
7364 : * Start the query with DELETE FROM relname
7365 : */
7366 118 : rte = rt_fetch(query->resultRelation, query->rtable);
7367 : Assert(rte->rtekind == RTE_RELATION);
7368 118 : if (PRETTY_INDENT(context))
7369 : {
7370 118 : appendStringInfoChar(buf, ' ');
7371 118 : context->indentLevel += PRETTYINDENT_STD;
7372 : }
7373 236 : appendStringInfo(buf, "DELETE FROM %s%s",
7374 118 : only_marker(rte),
7375 : generate_relation_name(rte->relid, NIL));
7376 :
7377 : /* Print the relation alias, if needed */
7378 118 : get_rte_alias(rte, query->resultRelation, false, context);
7379 :
7380 : /* Add the USING clause if given */
7381 118 : get_from_clause(query, " USING ", context);
7382 :
7383 : /* Add a WHERE clause if given */
7384 118 : if (query->jointree->quals != NULL)
7385 : {
7386 118 : appendContextKeyword(context, " WHERE ",
7387 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7388 118 : get_rule_expr(query->jointree->quals, context, false);
7389 : }
7390 :
7391 : /* Add RETURNING if present */
7392 118 : if (query->returningList)
7393 22 : get_returning_clause(query, context);
7394 118 : }
7395 :
7396 :
7397 : /* ----------
7398 : * get_merge_query_def - Parse back a MERGE parsetree
7399 : * ----------
7400 : */
7401 : static void
7402 12 : get_merge_query_def(Query *query, deparse_context *context)
7403 : {
7404 12 : StringInfo buf = context->buf;
7405 : RangeTblEntry *rte;
7406 : ListCell *lc;
7407 : bool haveNotMatchedBySource;
7408 :
7409 : /* Insert the WITH clause if given */
7410 12 : get_with_clause(query, context);
7411 :
7412 : /*
7413 : * Start the query with MERGE INTO relname
7414 : */
7415 12 : rte = rt_fetch(query->resultRelation, query->rtable);
7416 : Assert(rte->rtekind == RTE_RELATION);
7417 12 : if (PRETTY_INDENT(context))
7418 : {
7419 12 : appendStringInfoChar(buf, ' ');
7420 12 : context->indentLevel += PRETTYINDENT_STD;
7421 : }
7422 24 : appendStringInfo(buf, "MERGE INTO %s%s",
7423 12 : only_marker(rte),
7424 : generate_relation_name(rte->relid, NIL));
7425 :
7426 : /* Print the relation alias, if needed */
7427 12 : get_rte_alias(rte, query->resultRelation, false, context);
7428 :
7429 : /* Print the source relation and join clause */
7430 12 : get_from_clause(query, " USING ", context);
7431 12 : appendContextKeyword(context, " ON ",
7432 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7433 12 : get_rule_expr(query->mergeJoinCondition, context, false);
7434 :
7435 : /*
7436 : * Test for any NOT MATCHED BY SOURCE actions. If there are none, then
7437 : * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
7438 : * SQL standard. Otherwise, we have a non-SQL-standard query, so output
7439 : * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
7440 : * more explicit.
7441 : */
7442 12 : haveNotMatchedBySource = false;
7443 84 : foreach(lc, query->mergeActionList)
7444 : {
7445 78 : MergeAction *action = lfirst_node(MergeAction, lc);
7446 :
7447 78 : if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
7448 : {
7449 6 : haveNotMatchedBySource = true;
7450 6 : break;
7451 : }
7452 : }
7453 :
7454 : /* Print each merge action */
7455 90 : foreach(lc, query->mergeActionList)
7456 : {
7457 78 : MergeAction *action = lfirst_node(MergeAction, lc);
7458 :
7459 78 : appendContextKeyword(context, " WHEN ",
7460 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7461 78 : switch (action->matchKind)
7462 : {
7463 36 : case MERGE_WHEN_MATCHED:
7464 36 : appendStringInfoString(buf, "MATCHED");
7465 36 : break;
7466 6 : case MERGE_WHEN_NOT_MATCHED_BY_SOURCE:
7467 6 : appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
7468 6 : break;
7469 36 : case MERGE_WHEN_NOT_MATCHED_BY_TARGET:
7470 36 : if (haveNotMatchedBySource)
7471 6 : appendStringInfoString(buf, "NOT MATCHED BY TARGET");
7472 : else
7473 30 : appendStringInfoString(buf, "NOT MATCHED");
7474 36 : break;
7475 0 : default:
7476 0 : elog(ERROR, "unrecognized matchKind: %d",
7477 : (int) action->matchKind);
7478 : }
7479 :
7480 78 : if (action->qual)
7481 : {
7482 48 : appendContextKeyword(context, " AND ",
7483 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7484 48 : get_rule_expr(action->qual, context, false);
7485 : }
7486 78 : appendContextKeyword(context, " THEN ",
7487 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7488 :
7489 78 : if (action->commandType == CMD_INSERT)
7490 : {
7491 : /* This generally matches get_insert_query_def() */
7492 36 : List *strippedexprs = NIL;
7493 36 : const char *sep = "";
7494 : ListCell *lc2;
7495 :
7496 36 : appendStringInfoString(buf, "INSERT");
7497 :
7498 36 : if (action->targetList)
7499 30 : appendStringInfoString(buf, " (");
7500 102 : foreach(lc2, action->targetList)
7501 : {
7502 66 : TargetEntry *tle = (TargetEntry *) lfirst(lc2);
7503 :
7504 : Assert(!tle->resjunk);
7505 :
7506 66 : appendStringInfoString(buf, sep);
7507 66 : sep = ", ";
7508 :
7509 66 : appendStringInfoString(buf,
7510 66 : quote_identifier(get_attname(rte->relid,
7511 66 : tle->resno,
7512 : false)));
7513 66 : strippedexprs = lappend(strippedexprs,
7514 66 : processIndirection((Node *) tle->expr,
7515 : context));
7516 : }
7517 36 : if (action->targetList)
7518 30 : appendStringInfoChar(buf, ')');
7519 :
7520 36 : if (action->override)
7521 : {
7522 6 : if (action->override == OVERRIDING_SYSTEM_VALUE)
7523 0 : appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
7524 6 : else if (action->override == OVERRIDING_USER_VALUE)
7525 6 : appendStringInfoString(buf, " OVERRIDING USER VALUE");
7526 : }
7527 :
7528 36 : if (strippedexprs)
7529 : {
7530 30 : appendContextKeyword(context, " VALUES (",
7531 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);
7532 30 : get_rule_list_toplevel(strippedexprs, context, false);
7533 30 : appendStringInfoChar(buf, ')');
7534 : }
7535 : else
7536 6 : appendStringInfoString(buf, " DEFAULT VALUES");
7537 : }
7538 42 : else if (action->commandType == CMD_UPDATE)
7539 : {
7540 24 : appendStringInfoString(buf, "UPDATE SET ");
7541 24 : get_update_query_targetlist_def(query, action->targetList,
7542 : context, rte);
7543 : }
7544 18 : else if (action->commandType == CMD_DELETE)
7545 12 : appendStringInfoString(buf, "DELETE");
7546 6 : else if (action->commandType == CMD_NOTHING)
7547 6 : appendStringInfoString(buf, "DO NOTHING");
7548 : }
7549 :
7550 : /* Add RETURNING if present */
7551 12 : if (query->returningList)
7552 6 : get_returning_clause(query, context);
7553 12 : }
7554 :
7555 :
7556 : /* ----------
7557 : * get_utility_query_def - Parse back a UTILITY parsetree
7558 : * ----------
7559 : */
7560 : static void
7561 22 : get_utility_query_def(Query *query, deparse_context *context)
7562 : {
7563 22 : StringInfo buf = context->buf;
7564 :
7565 22 : if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7566 22 : {
7567 22 : NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7568 :
7569 22 : appendContextKeyword(context, "",
7570 : 0, PRETTYINDENT_STD, 1);
7571 22 : appendStringInfo(buf, "NOTIFY %s",
7572 22 : quote_identifier(stmt->conditionname));
7573 22 : if (stmt->payload)
7574 : {
7575 0 : appendStringInfoString(buf, ", ");
7576 0 : simple_quote_literal(buf, stmt->payload);
7577 : }
7578 : }
7579 : else
7580 : {
7581 : /* Currently only NOTIFY utility commands can appear in rules */
7582 0 : elog(ERROR, "unexpected utility statement type");
7583 : }
7584 22 : }
7585 :
7586 : /*
7587 : * Display a Var appropriately.
7588 : *
7589 : * In some cases (currently only when recursing into an unnamed join)
7590 : * the Var's varlevelsup has to be interpreted with respect to a context
7591 : * above the current one; levelsup indicates the offset.
7592 : *
7593 : * If istoplevel is true, the Var is at the top level of a SELECT's
7594 : * targetlist, which means we need special treatment of whole-row Vars.
7595 : * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
7596 : * dirty hack to prevent "tab.*" from being expanded into multiple columns.
7597 : * (The parser will strip the useless coercion, so no inefficiency is added in
7598 : * dump and reload.) We used to print just "tab" in such cases, but that is
7599 : * ambiguous and will yield the wrong result if "tab" is also a plain column
7600 : * name in the query.
7601 : *
7602 : * Returns the attname of the Var, or NULL if the Var has no attname (because
7603 : * it is a whole-row Var or a subplan output reference).
7604 : */
7605 : static char *
7606 172986 : get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
7607 : {
7608 172986 : StringInfo buf = context->buf;
7609 : RangeTblEntry *rte;
7610 : AttrNumber attnum;
7611 : int netlevelsup;
7612 : deparse_namespace *dpns;
7613 : int varno;
7614 : AttrNumber varattno;
7615 : deparse_columns *colinfo;
7616 : char *refname;
7617 : char *attname;
7618 : bool need_prefix;
7619 :
7620 : /* Find appropriate nesting depth */
7621 172986 : netlevelsup = var->varlevelsup + levelsup;
7622 172986 : if (netlevelsup >= list_length(context->namespaces))
7623 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
7624 : var->varlevelsup, levelsup);
7625 172986 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7626 : netlevelsup);
7627 :
7628 : /*
7629 : * If we have a syntactic referent for the Var, and we're working from a
7630 : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
7631 : * on the semantic referent. (Forcing use of the semantic referent when
7632 : * printing plan trees is a design choice that's perhaps more motivated by
7633 : * backwards compatibility than anything else. But it does have the
7634 : * advantage of making plans more explicit.)
7635 : */
7636 172986 : if (var->varnosyn > 0 && dpns->plan == NULL)
7637 : {
7638 41598 : varno = var->varnosyn;
7639 41598 : varattno = var->varattnosyn;
7640 : }
7641 : else
7642 : {
7643 131388 : varno = var->varno;
7644 131388 : varattno = var->varattno;
7645 : }
7646 :
7647 : /*
7648 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
7649 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
7650 : * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
7651 : * find the aliases previously assigned for this RTE.
7652 : */
7653 172986 : if (varno >= 1 && varno <= list_length(dpns->rtable))
7654 : {
7655 : /*
7656 : * We might have been asked to map child Vars to some parent relation.
7657 : */
7658 130772 : if (context->appendparents && dpns->appendrels)
7659 : {
7660 3744 : int pvarno = varno;
7661 3744 : AttrNumber pvarattno = varattno;
7662 3744 : AppendRelInfo *appinfo = dpns->appendrels[pvarno];
7663 3744 : bool found = false;
7664 :
7665 : /* Only map up to inheritance parents, not UNION ALL appendrels */
7666 7622 : while (appinfo &&
7667 4168 : rt_fetch(appinfo->parent_relid,
7668 4168 : dpns->rtable)->rtekind == RTE_RELATION)
7669 : {
7670 3878 : found = false;
7671 3878 : if (pvarattno > 0) /* system columns stay as-is */
7672 : {
7673 3604 : if (pvarattno > appinfo->num_child_cols)
7674 0 : break; /* safety check */
7675 3604 : pvarattno = appinfo->parent_colnos[pvarattno - 1];
7676 3604 : if (pvarattno == 0)
7677 0 : break; /* Var is local to child */
7678 : }
7679 :
7680 3878 : pvarno = appinfo->parent_relid;
7681 3878 : found = true;
7682 :
7683 : /* If the parent is itself a child, continue up. */
7684 : Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
7685 3878 : appinfo = dpns->appendrels[pvarno];
7686 : }
7687 :
7688 : /*
7689 : * If we found an ancestral rel, and that rel is included in
7690 : * appendparents, print that column not the original one.
7691 : */
7692 3744 : if (found && bms_is_member(pvarno, context->appendparents))
7693 : {
7694 3088 : varno = pvarno;
7695 3088 : varattno = pvarattno;
7696 : }
7697 : }
7698 :
7699 130772 : rte = rt_fetch(varno, dpns->rtable);
7700 :
7701 : /* might be returning old/new column value */
7702 130772 : if (var->varreturningtype == VAR_RETURNING_OLD)
7703 416 : refname = dpns->ret_old_alias;
7704 130356 : else if (var->varreturningtype == VAR_RETURNING_NEW)
7705 414 : refname = dpns->ret_new_alias;
7706 : else
7707 129942 : refname = (char *) list_nth(dpns->rtable_names, varno - 1);
7708 :
7709 130772 : colinfo = deparse_columns_fetch(varno, dpns);
7710 130772 : attnum = varattno;
7711 : }
7712 : else
7713 : {
7714 42214 : resolve_special_varno((Node *) var, context,
7715 : get_special_variable, NULL);
7716 42214 : return NULL;
7717 : }
7718 :
7719 : /*
7720 : * The planner will sometimes emit Vars referencing resjunk elements of a
7721 : * subquery's target list (this is currently only possible if it chooses
7722 : * to generate a "physical tlist" for a SubqueryScan or CteScan node).
7723 : * Although we prefer to print subquery-referencing Vars using the
7724 : * subquery's alias, that's not possible for resjunk items since they have
7725 : * no alias. So in that case, drill down to the subplan and print the
7726 : * contents of the referenced tlist item. This works because in a plan
7727 : * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
7728 : * we'll have set dpns->inner_plan to reference the child plan node.
7729 : */
7730 135032 : if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
7731 4260 : attnum > list_length(rte->eref->colnames) &&
7732 2 : dpns->inner_plan)
7733 : {
7734 : TargetEntry *tle;
7735 : deparse_namespace save_dpns;
7736 :
7737 2 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
7738 2 : if (!tle)
7739 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
7740 : attnum, rte->eref->aliasname);
7741 :
7742 : Assert(netlevelsup == 0);
7743 2 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7744 :
7745 : /*
7746 : * Force parentheses because our caller probably assumed a Var is a
7747 : * simple expression.
7748 : */
7749 2 : if (!IsA(tle->expr, Var))
7750 0 : appendStringInfoChar(buf, '(');
7751 2 : get_rule_expr((Node *) tle->expr, context, true);
7752 2 : if (!IsA(tle->expr, Var))
7753 0 : appendStringInfoChar(buf, ')');
7754 :
7755 2 : pop_child_plan(dpns, &save_dpns);
7756 2 : return NULL;
7757 : }
7758 :
7759 : /*
7760 : * If it's an unnamed join, look at the expansion of the alias variable.
7761 : * If it's a simple reference to one of the input vars, then recursively
7762 : * print the name of that var instead. When it's not a simple reference,
7763 : * we have to just print the unqualified join column name. (This can only
7764 : * happen with "dangerous" merged columns in a JOIN USING; we took pains
7765 : * previously to make the unqualified column name unique in such cases.)
7766 : *
7767 : * This wouldn't work in decompiling plan trees, because we don't store
7768 : * joinaliasvars lists after planning; but a plan tree should never
7769 : * contain a join alias variable.
7770 : */
7771 130770 : if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
7772 : {
7773 96 : if (rte->joinaliasvars == NIL)
7774 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
7775 96 : if (attnum > 0)
7776 : {
7777 : Var *aliasvar;
7778 :
7779 96 : aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
7780 : /* we intentionally don't strip implicit coercions here */
7781 96 : if (aliasvar && IsA(aliasvar, Var))
7782 : {
7783 0 : return get_variable(aliasvar, var->varlevelsup + levelsup,
7784 : istoplevel, context);
7785 : }
7786 : }
7787 :
7788 : /*
7789 : * Unnamed join has no refname. (Note: since it's unnamed, there is
7790 : * no way the user could have referenced it to create a whole-row Var
7791 : * for it. So we don't have to cover that case below.)
7792 : */
7793 : Assert(refname == NULL);
7794 : }
7795 :
7796 130770 : if (attnum == InvalidAttrNumber)
7797 1016 : attname = NULL;
7798 129754 : else if (attnum > 0)
7799 : {
7800 : /* Get column name to use from the colinfo struct */
7801 127884 : if (attnum > colinfo->num_cols)
7802 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
7803 : attnum, rte->eref->aliasname);
7804 127884 : attname = colinfo->colnames[attnum - 1];
7805 :
7806 : /*
7807 : * If we find a Var referencing a dropped column, it seems better to
7808 : * print something (anything) than to fail. In general this should
7809 : * not happen, but it used to be possible for some cases involving
7810 : * functions returning named composite types, and perhaps there are
7811 : * still bugs out there.
7812 : */
7813 127884 : if (attname == NULL)
7814 6 : attname = "?dropped?column?";
7815 : }
7816 : else
7817 : {
7818 : /* System column - name is fixed, get it from the catalog */
7819 1870 : attname = get_rte_attribute_name(rte, attnum);
7820 : }
7821 :
7822 197118 : need_prefix = (context->varprefix || attname == NULL ||
7823 66348 : var->varreturningtype != VAR_RETURNING_DEFAULT);
7824 :
7825 : /*
7826 : * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
7827 : * clause, we may need to add a table-name prefix to prevent
7828 : * findTargetlistEntrySQL92 from misinterpreting the name as an
7829 : * output-column name. To avoid cluttering the output with unnecessary
7830 : * prefixes, do so only if there is a name match to a SELECT tlist item
7831 : * that is different from the Var.
7832 : */
7833 130770 : if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
7834 : {
7835 276 : int colno = 0;
7836 :
7837 1036 : foreach_node(TargetEntry, tle, context->targetList)
7838 : {
7839 : char *colname;
7840 :
7841 496 : if (tle->resjunk)
7842 0 : continue; /* ignore junk entries */
7843 496 : colno++;
7844 :
7845 : /* This must match colname-choosing logic in get_target_list() */
7846 496 : if (context->resultDesc && colno <= context->resultDesc->natts)
7847 496 : colname = NameStr(TupleDescAttr(context->resultDesc,
7848 : colno - 1)->attname);
7849 : else
7850 0 : colname = tle->resname;
7851 :
7852 496 : if (colname && strcmp(colname, attname) == 0 &&
7853 204 : !equal(var, tle->expr))
7854 : {
7855 12 : need_prefix = true;
7856 12 : break;
7857 : }
7858 : }
7859 : }
7860 :
7861 130770 : if (refname && need_prefix)
7862 : {
7863 64240 : appendStringInfoString(buf, quote_identifier(refname));
7864 64240 : appendStringInfoChar(buf, '.');
7865 : }
7866 130770 : if (attname)
7867 129754 : appendStringInfoString(buf, quote_identifier(attname));
7868 : else
7869 : {
7870 1016 : appendStringInfoChar(buf, '*');
7871 1016 : if (istoplevel)
7872 84 : appendStringInfo(buf, "::%s",
7873 : format_type_with_typemod(var->vartype,
7874 : var->vartypmod));
7875 : }
7876 :
7877 130770 : return attname;
7878 : }
7879 :
7880 : /*
7881 : * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
7882 : * routine is actually a callback for resolve_special_varno, which handles
7883 : * finding the correct TargetEntry. We get the expression contained in that
7884 : * TargetEntry and just need to deparse it, a job we can throw back on
7885 : * get_rule_expr.
7886 : */
7887 : static void
7888 42214 : get_special_variable(Node *node, deparse_context *context, void *callback_arg)
7889 : {
7890 42214 : StringInfo buf = context->buf;
7891 :
7892 : /*
7893 : * For a non-Var referent, force parentheses because our caller probably
7894 : * assumed a Var is a simple expression.
7895 : */
7896 42214 : if (!IsA(node, Var))
7897 3596 : appendStringInfoChar(buf, '(');
7898 42214 : get_rule_expr(node, context, true);
7899 42214 : if (!IsA(node, Var))
7900 3596 : appendStringInfoChar(buf, ')');
7901 42214 : }
7902 :
7903 : /*
7904 : * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
7905 : * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
7906 : * invoke the callback provided.
7907 : */
7908 : static void
7909 115496 : resolve_special_varno(Node *node, deparse_context *context,
7910 : rsv_callback callback, void *callback_arg)
7911 : {
7912 : Var *var;
7913 : deparse_namespace *dpns;
7914 :
7915 : /* This function is recursive, so let's be paranoid. */
7916 115496 : check_stack_depth();
7917 :
7918 : /* If it's not a Var, invoke the callback. */
7919 115496 : if (!IsA(node, Var))
7920 : {
7921 3856 : (*callback) (node, context, callback_arg);
7922 3856 : return;
7923 : }
7924 :
7925 : /* Find appropriate nesting depth */
7926 111640 : var = (Var *) node;
7927 111640 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7928 111640 : var->varlevelsup);
7929 :
7930 : /*
7931 : * If varno is special, recurse. (Don't worry about varnosyn; if we're
7932 : * here, we already decided not to use that.)
7933 : */
7934 111640 : if (var->varno == OUTER_VAR && dpns->outer_tlist)
7935 : {
7936 : TargetEntry *tle;
7937 : deparse_namespace save_dpns;
7938 : Bitmapset *save_appendparents;
7939 :
7940 54584 : tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
7941 54584 : if (!tle)
7942 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
7943 :
7944 : /*
7945 : * If we're descending to the first child of an Append or MergeAppend,
7946 : * update appendparents. This will affect deparsing of all Vars
7947 : * appearing within the eventually-resolved subexpression.
7948 : */
7949 54584 : save_appendparents = context->appendparents;
7950 :
7951 54584 : if (IsA(dpns->plan, Append))
7952 4088 : context->appendparents = bms_union(context->appendparents,
7953 4088 : ((Append *) dpns->plan)->apprelids);
7954 50496 : else if (IsA(dpns->plan, MergeAppend))
7955 578 : context->appendparents = bms_union(context->appendparents,
7956 578 : ((MergeAppend *) dpns->plan)->apprelids);
7957 :
7958 54584 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
7959 54584 : resolve_special_varno((Node *) tle->expr, context,
7960 : callback, callback_arg);
7961 54584 : pop_child_plan(dpns, &save_dpns);
7962 54584 : context->appendparents = save_appendparents;
7963 54584 : return;
7964 : }
7965 57056 : else if (var->varno == INNER_VAR && dpns->inner_tlist)
7966 : {
7967 : TargetEntry *tle;
7968 : deparse_namespace save_dpns;
7969 :
7970 13304 : tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
7971 13304 : if (!tle)
7972 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
7973 :
7974 13304 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7975 13304 : resolve_special_varno((Node *) tle->expr, context,
7976 : callback, callback_arg);
7977 13304 : pop_child_plan(dpns, &save_dpns);
7978 13304 : return;
7979 : }
7980 43752 : else if (var->varno == INDEX_VAR && dpns->index_tlist)
7981 : {
7982 : TargetEntry *tle;
7983 :
7984 5134 : tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
7985 5134 : if (!tle)
7986 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
7987 :
7988 5134 : resolve_special_varno((Node *) tle->expr, context,
7989 : callback, callback_arg);
7990 5134 : return;
7991 : }
7992 38618 : else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
7993 0 : elog(ERROR, "bogus varno: %d", var->varno);
7994 :
7995 : /* Not special. Just invoke the callback. */
7996 38618 : (*callback) (node, context, callback_arg);
7997 : }
7998 :
7999 : /*
8000 : * Get the name of a field of an expression of composite type. The
8001 : * expression is usually a Var, but we handle other cases too.
8002 : *
8003 : * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
8004 : *
8005 : * This is fairly straightforward when the expression has a named composite
8006 : * type; we need only look up the type in the catalogs. However, the type
8007 : * could also be RECORD. Since no actual table or view column is allowed to
8008 : * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
8009 : * or to a subquery output. We drill down to find the ultimate defining
8010 : * expression and attempt to infer the field name from it. We ereport if we
8011 : * can't determine the name.
8012 : *
8013 : * Similarly, a PARAM of type RECORD has to refer to some expression of
8014 : * a determinable composite type.
8015 : */
8016 : static const char *
8017 1304 : get_name_for_var_field(Var *var, int fieldno,
8018 : int levelsup, deparse_context *context)
8019 : {
8020 : RangeTblEntry *rte;
8021 : AttrNumber attnum;
8022 : int netlevelsup;
8023 : deparse_namespace *dpns;
8024 : int varno;
8025 : AttrNumber varattno;
8026 : TupleDesc tupleDesc;
8027 : Node *expr;
8028 :
8029 : /*
8030 : * If it's a RowExpr that was expanded from a whole-row Var, use the
8031 : * column names attached to it. (We could let get_expr_result_tupdesc()
8032 : * handle this, but it's much cheaper to just pull out the name we need.)
8033 : */
8034 1304 : if (IsA(var, RowExpr))
8035 : {
8036 36 : RowExpr *r = (RowExpr *) var;
8037 :
8038 36 : if (fieldno > 0 && fieldno <= list_length(r->colnames))
8039 36 : return strVal(list_nth(r->colnames, fieldno - 1));
8040 : }
8041 :
8042 : /*
8043 : * If it's a Param of type RECORD, try to find what the Param refers to.
8044 : */
8045 1268 : if (IsA(var, Param))
8046 : {
8047 18 : Param *param = (Param *) var;
8048 : ListCell *ancestor_cell;
8049 :
8050 18 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8051 18 : if (expr)
8052 : {
8053 : /* Found a match, so recurse to decipher the field name */
8054 : deparse_namespace save_dpns;
8055 : const char *result;
8056 :
8057 18 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
8058 18 : result = get_name_for_var_field((Var *) expr, fieldno,
8059 : 0, context);
8060 18 : pop_ancestor_plan(dpns, &save_dpns);
8061 18 : return result;
8062 : }
8063 : }
8064 :
8065 : /*
8066 : * If it's a Var of type RECORD, we have to find what the Var refers to;
8067 : * if not, we can use get_expr_result_tupdesc().
8068 : */
8069 1250 : if (!IsA(var, Var) ||
8070 1170 : var->vartype != RECORDOID)
8071 : {
8072 1004 : tupleDesc = get_expr_result_tupdesc((Node *) var, false);
8073 : /* Got the tupdesc, so we can extract the field name */
8074 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
8075 1004 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
8076 : }
8077 :
8078 : /* Find appropriate nesting depth */
8079 246 : netlevelsup = var->varlevelsup + levelsup;
8080 246 : if (netlevelsup >= list_length(context->namespaces))
8081 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
8082 : var->varlevelsup, levelsup);
8083 246 : dpns = (deparse_namespace *) list_nth(context->namespaces,
8084 : netlevelsup);
8085 :
8086 : /*
8087 : * If we have a syntactic referent for the Var, and we're working from a
8088 : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8089 : * on the semantic referent. (See comments in get_variable().)
8090 : */
8091 246 : if (var->varnosyn > 0 && dpns->plan == NULL)
8092 : {
8093 96 : varno = var->varnosyn;
8094 96 : varattno = var->varattnosyn;
8095 : }
8096 : else
8097 : {
8098 150 : varno = var->varno;
8099 150 : varattno = var->varattno;
8100 : }
8101 :
8102 : /*
8103 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
8104 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8105 : * down into the subplans, or INDEX_VAR, which is resolved similarly.
8106 : *
8107 : * Note: unlike get_variable and resolve_special_varno, we need not worry
8108 : * about inheritance mapping: a child Var should have the same datatype as
8109 : * its parent, and here we're really only interested in the Var's type.
8110 : */
8111 246 : if (varno >= 1 && varno <= list_length(dpns->rtable))
8112 : {
8113 168 : rte = rt_fetch(varno, dpns->rtable);
8114 168 : attnum = varattno;
8115 : }
8116 78 : else if (varno == OUTER_VAR && dpns->outer_tlist)
8117 : {
8118 : TargetEntry *tle;
8119 : deparse_namespace save_dpns;
8120 : const char *result;
8121 :
8122 60 : tle = get_tle_by_resno(dpns->outer_tlist, varattno);
8123 60 : if (!tle)
8124 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
8125 :
8126 : Assert(netlevelsup == 0);
8127 60 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8128 :
8129 60 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8130 : levelsup, context);
8131 :
8132 60 : pop_child_plan(dpns, &save_dpns);
8133 60 : return result;
8134 : }
8135 18 : else if (varno == INNER_VAR && dpns->inner_tlist)
8136 : {
8137 : TargetEntry *tle;
8138 : deparse_namespace save_dpns;
8139 : const char *result;
8140 :
8141 18 : tle = get_tle_by_resno(dpns->inner_tlist, varattno);
8142 18 : if (!tle)
8143 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
8144 :
8145 : Assert(netlevelsup == 0);
8146 18 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8147 :
8148 18 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8149 : levelsup, context);
8150 :
8151 18 : pop_child_plan(dpns, &save_dpns);
8152 18 : return result;
8153 : }
8154 0 : else if (varno == INDEX_VAR && dpns->index_tlist)
8155 : {
8156 : TargetEntry *tle;
8157 : const char *result;
8158 :
8159 0 : tle = get_tle_by_resno(dpns->index_tlist, varattno);
8160 0 : if (!tle)
8161 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
8162 :
8163 : Assert(netlevelsup == 0);
8164 :
8165 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8166 : levelsup, context);
8167 :
8168 0 : return result;
8169 : }
8170 : else
8171 : {
8172 0 : elog(ERROR, "bogus varno: %d", varno);
8173 : return NULL; /* keep compiler quiet */
8174 : }
8175 :
8176 168 : if (attnum == InvalidAttrNumber)
8177 : {
8178 : /* Var is whole-row reference to RTE, so select the right field */
8179 24 : return get_rte_attribute_name(rte, fieldno);
8180 : }
8181 :
8182 : /*
8183 : * This part has essentially the same logic as the parser's
8184 : * expandRecordVariable() function, but we are dealing with a different
8185 : * representation of the input context, and we only need one field name
8186 : * not a TupleDesc. Also, we need special cases for finding subquery and
8187 : * CTE subplans when deparsing Plan trees.
8188 : */
8189 144 : expr = (Node *) var; /* default if we can't drill down */
8190 :
8191 144 : switch (rte->rtekind)
8192 : {
8193 0 : case RTE_RELATION:
8194 : case RTE_VALUES:
8195 : case RTE_NAMEDTUPLESTORE:
8196 : case RTE_RESULT:
8197 :
8198 : /*
8199 : * This case should not occur: a column of a table, values list,
8200 : * or ENR shouldn't have type RECORD. Fall through and fail (most
8201 : * likely) at the bottom.
8202 : */
8203 0 : break;
8204 72 : case RTE_SUBQUERY:
8205 : /* Subselect-in-FROM: examine sub-select's output expr */
8206 : {
8207 72 : if (rte->subquery)
8208 : {
8209 42 : TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
8210 : attnum);
8211 :
8212 42 : if (ste == NULL || ste->resjunk)
8213 0 : elog(ERROR, "subquery %s does not have attribute %d",
8214 : rte->eref->aliasname, attnum);
8215 42 : expr = (Node *) ste->expr;
8216 42 : if (IsA(expr, Var))
8217 : {
8218 : /*
8219 : * Recurse into the sub-select to see what its Var
8220 : * refers to. We have to build an additional level of
8221 : * namespace to keep in step with varlevelsup in the
8222 : * subselect; furthermore, the subquery RTE might be
8223 : * from an outer query level, in which case the
8224 : * namespace for the subselect must have that outer
8225 : * level as parent namespace.
8226 : */
8227 18 : List *save_nslist = context->namespaces;
8228 : List *parent_namespaces;
8229 : deparse_namespace mydpns;
8230 : const char *result;
8231 :
8232 18 : parent_namespaces = list_copy_tail(context->namespaces,
8233 : netlevelsup);
8234 :
8235 18 : set_deparse_for_query(&mydpns, rte->subquery,
8236 : parent_namespaces);
8237 :
8238 18 : context->namespaces = lcons(&mydpns, parent_namespaces);
8239 :
8240 18 : result = get_name_for_var_field((Var *) expr, fieldno,
8241 : 0, context);
8242 :
8243 18 : context->namespaces = save_nslist;
8244 :
8245 18 : return result;
8246 : }
8247 : /* else fall through to inspect the expression */
8248 : }
8249 : else
8250 : {
8251 : /*
8252 : * We're deparsing a Plan tree so we don't have complete
8253 : * RTE entries (in particular, rte->subquery is NULL). But
8254 : * the only place we'd normally see a Var directly
8255 : * referencing a SUBQUERY RTE is in a SubqueryScan plan
8256 : * node, and we can look into the child plan's tlist
8257 : * instead. An exception occurs if the subquery was
8258 : * proven empty and optimized away: then we'd find such a
8259 : * Var in a childless Result node, and there's nothing in
8260 : * the plan tree that would let us figure out what it had
8261 : * originally referenced. In that case, fall back on
8262 : * printing "fN", analogously to the default column names
8263 : * for RowExprs.
8264 : */
8265 : TargetEntry *tle;
8266 : deparse_namespace save_dpns;
8267 : const char *result;
8268 :
8269 30 : if (!dpns->inner_plan)
8270 : {
8271 12 : char *dummy_name = palloc(32);
8272 :
8273 : Assert(dpns->plan && IsA(dpns->plan, Result));
8274 12 : snprintf(dummy_name, 32, "f%d", fieldno);
8275 12 : return dummy_name;
8276 : }
8277 : Assert(dpns->plan && IsA(dpns->plan, SubqueryScan));
8278 :
8279 18 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8280 18 : if (!tle)
8281 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8282 : attnum);
8283 : Assert(netlevelsup == 0);
8284 18 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8285 :
8286 18 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8287 : levelsup, context);
8288 :
8289 18 : pop_child_plan(dpns, &save_dpns);
8290 18 : return result;
8291 : }
8292 : }
8293 24 : break;
8294 0 : case RTE_JOIN:
8295 : /* Join RTE --- recursively inspect the alias variable */
8296 0 : if (rte->joinaliasvars == NIL)
8297 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
8298 : Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
8299 0 : expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
8300 : Assert(expr != NULL);
8301 : /* we intentionally don't strip implicit coercions here */
8302 0 : if (IsA(expr, Var))
8303 0 : return get_name_for_var_field((Var *) expr, fieldno,
8304 0 : var->varlevelsup + levelsup,
8305 : context);
8306 : /* else fall through to inspect the expression */
8307 0 : break;
8308 0 : case RTE_FUNCTION:
8309 : case RTE_TABLEFUNC:
8310 :
8311 : /*
8312 : * We couldn't get here unless a function is declared with one of
8313 : * its result columns as RECORD, which is not allowed.
8314 : */
8315 0 : break;
8316 72 : case RTE_CTE:
8317 : /* CTE reference: examine subquery's output expr */
8318 : {
8319 72 : CommonTableExpr *cte = NULL;
8320 : Index ctelevelsup;
8321 : ListCell *lc;
8322 :
8323 : /*
8324 : * Try to find the referenced CTE using the namespace stack.
8325 : */
8326 72 : ctelevelsup = rte->ctelevelsup + netlevelsup;
8327 72 : if (ctelevelsup >= list_length(context->namespaces))
8328 12 : lc = NULL;
8329 : else
8330 : {
8331 : deparse_namespace *ctedpns;
8332 :
8333 : ctedpns = (deparse_namespace *)
8334 60 : list_nth(context->namespaces, ctelevelsup);
8335 66 : foreach(lc, ctedpns->ctes)
8336 : {
8337 36 : cte = (CommonTableExpr *) lfirst(lc);
8338 36 : if (strcmp(cte->ctename, rte->ctename) == 0)
8339 30 : break;
8340 : }
8341 : }
8342 72 : if (lc != NULL)
8343 : {
8344 30 : Query *ctequery = (Query *) cte->ctequery;
8345 30 : TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
8346 : attnum);
8347 :
8348 30 : if (ste == NULL || ste->resjunk)
8349 0 : elog(ERROR, "CTE %s does not have attribute %d",
8350 : rte->eref->aliasname, attnum);
8351 30 : expr = (Node *) ste->expr;
8352 30 : if (IsA(expr, Var))
8353 : {
8354 : /*
8355 : * Recurse into the CTE to see what its Var refers to.
8356 : * We have to build an additional level of namespace
8357 : * to keep in step with varlevelsup in the CTE;
8358 : * furthermore it could be an outer CTE (compare
8359 : * SUBQUERY case above).
8360 : */
8361 18 : List *save_nslist = context->namespaces;
8362 : List *parent_namespaces;
8363 : deparse_namespace mydpns;
8364 : const char *result;
8365 :
8366 18 : parent_namespaces = list_copy_tail(context->namespaces,
8367 : ctelevelsup);
8368 :
8369 18 : set_deparse_for_query(&mydpns, ctequery,
8370 : parent_namespaces);
8371 :
8372 18 : context->namespaces = lcons(&mydpns, parent_namespaces);
8373 :
8374 18 : result = get_name_for_var_field((Var *) expr, fieldno,
8375 : 0, context);
8376 :
8377 18 : context->namespaces = save_nslist;
8378 :
8379 18 : return result;
8380 : }
8381 : /* else fall through to inspect the expression */
8382 : }
8383 : else
8384 : {
8385 : /*
8386 : * We're deparsing a Plan tree so we don't have a CTE
8387 : * list. But the only places we'd normally see a Var
8388 : * directly referencing a CTE RTE are in CteScan or
8389 : * WorkTableScan plan nodes. For those cases,
8390 : * set_deparse_plan arranged for dpns->inner_plan to be
8391 : * the plan node that emits the CTE or RecursiveUnion
8392 : * result, and we can look at its tlist instead. As
8393 : * above, this can fail if the CTE has been proven empty,
8394 : * in which case fall back to "fN".
8395 : */
8396 : TargetEntry *tle;
8397 : deparse_namespace save_dpns;
8398 : const char *result;
8399 :
8400 42 : if (!dpns->inner_plan)
8401 : {
8402 6 : char *dummy_name = palloc(32);
8403 :
8404 : Assert(dpns->plan && IsA(dpns->plan, Result));
8405 6 : snprintf(dummy_name, 32, "f%d", fieldno);
8406 6 : return dummy_name;
8407 : }
8408 : Assert(dpns->plan && (IsA(dpns->plan, CteScan) ||
8409 : IsA(dpns->plan, WorkTableScan)));
8410 :
8411 36 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8412 36 : if (!tle)
8413 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8414 : attnum);
8415 : Assert(netlevelsup == 0);
8416 36 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8417 :
8418 36 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8419 : levelsup, context);
8420 :
8421 36 : pop_child_plan(dpns, &save_dpns);
8422 36 : return result;
8423 : }
8424 : }
8425 12 : break;
8426 0 : case RTE_GROUP:
8427 :
8428 : /*
8429 : * We couldn't get here: any Vars that reference the RTE_GROUP RTE
8430 : * should have been replaced with the underlying grouping
8431 : * expressions.
8432 : */
8433 0 : break;
8434 : }
8435 :
8436 : /*
8437 : * We now have an expression we can't expand any more, so see if
8438 : * get_expr_result_tupdesc() can do anything with it.
8439 : */
8440 36 : tupleDesc = get_expr_result_tupdesc(expr, false);
8441 : /* Got the tupdesc, so we can extract the field name */
8442 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
8443 36 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
8444 : }
8445 :
8446 : /*
8447 : * Try to find the referenced expression for a PARAM_EXEC Param that might
8448 : * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
8449 : *
8450 : * If successful, return the expression and set *dpns_p and *ancestor_cell_p
8451 : * appropriately for calling push_ancestor_plan(). If no referent can be
8452 : * found, return NULL.
8453 : */
8454 : static Node *
8455 6840 : find_param_referent(Param *param, deparse_context *context,
8456 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
8457 : {
8458 : /* Initialize output parameters to prevent compiler warnings */
8459 6840 : *dpns_p = NULL;
8460 6840 : *ancestor_cell_p = NULL;
8461 :
8462 : /*
8463 : * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
8464 : * SubPlan argument. This will necessarily be in some ancestor of the
8465 : * current expression's Plan node.
8466 : */
8467 6840 : if (param->paramkind == PARAM_EXEC)
8468 : {
8469 : deparse_namespace *dpns;
8470 : Plan *child_plan;
8471 : ListCell *lc;
8472 :
8473 5936 : dpns = (deparse_namespace *) linitial(context->namespaces);
8474 5936 : child_plan = dpns->plan;
8475 :
8476 10436 : foreach(lc, dpns->ancestors)
8477 : {
8478 8828 : Node *ancestor = (Node *) lfirst(lc);
8479 : ListCell *lc2;
8480 :
8481 : /*
8482 : * NestLoops transmit params to their inner child only.
8483 : */
8484 8828 : if (IsA(ancestor, NestLoop) &&
8485 3894 : child_plan == innerPlan(ancestor))
8486 : {
8487 3810 : NestLoop *nl = (NestLoop *) ancestor;
8488 :
8489 4772 : foreach(lc2, nl->nestParams)
8490 : {
8491 4640 : NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
8492 :
8493 4640 : if (nlp->paramno == param->paramid)
8494 : {
8495 : /* Found a match, so return it */
8496 3678 : *dpns_p = dpns;
8497 3678 : *ancestor_cell_p = lc;
8498 3678 : return (Node *) nlp->paramval;
8499 : }
8500 : }
8501 : }
8502 :
8503 : /*
8504 : * If ancestor is a SubPlan, check the arguments it provides.
8505 : */
8506 5150 : if (IsA(ancestor, SubPlan))
8507 354 : {
8508 1004 : SubPlan *subplan = (SubPlan *) ancestor;
8509 : ListCell *lc3;
8510 : ListCell *lc4;
8511 :
8512 1340 : forboth(lc3, subplan->parParam, lc4, subplan->args)
8513 : {
8514 986 : int paramid = lfirst_int(lc3);
8515 986 : Node *arg = (Node *) lfirst(lc4);
8516 :
8517 986 : if (paramid == param->paramid)
8518 : {
8519 : /*
8520 : * Found a match, so return it. But, since Vars in
8521 : * the arg are to be evaluated in the surrounding
8522 : * context, we have to point to the next ancestor item
8523 : * that is *not* a SubPlan.
8524 : */
8525 : ListCell *rest;
8526 :
8527 650 : for_each_cell(rest, dpns->ancestors,
8528 : lnext(dpns->ancestors, lc))
8529 : {
8530 650 : Node *ancestor2 = (Node *) lfirst(rest);
8531 :
8532 650 : if (!IsA(ancestor2, SubPlan))
8533 : {
8534 650 : *dpns_p = dpns;
8535 650 : *ancestor_cell_p = rest;
8536 650 : return arg;
8537 : }
8538 : }
8539 0 : elog(ERROR, "SubPlan cannot be outermost ancestor");
8540 : }
8541 : }
8542 :
8543 : /* SubPlan isn't a kind of Plan, so skip the rest */
8544 354 : continue;
8545 : }
8546 :
8547 : /*
8548 : * We need not consider the ancestor's initPlan list, since
8549 : * initplans never have any parParams.
8550 : */
8551 :
8552 : /* No luck, crawl up to next ancestor */
8553 4146 : child_plan = (Plan *) ancestor;
8554 : }
8555 : }
8556 :
8557 : /* No referent found */
8558 2512 : return NULL;
8559 : }
8560 :
8561 : /*
8562 : * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.
8563 : *
8564 : * If successful, return the generating subplan/initplan and set *column_p
8565 : * to the subplan's 0-based output column number.
8566 : * Otherwise, return NULL.
8567 : */
8568 : static SubPlan *
8569 2512 : find_param_generator(Param *param, deparse_context *context, int *column_p)
8570 : {
8571 : /* Initialize output parameter to prevent compiler warnings */
8572 2512 : *column_p = 0;
8573 :
8574 : /*
8575 : * If it's a PARAM_EXEC parameter, search the current plan node as well as
8576 : * ancestor nodes looking for a subplan or initplan that emits the value
8577 : * for the Param. It could appear in the setParams of an initplan or
8578 : * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
8579 : */
8580 2512 : if (param->paramkind == PARAM_EXEC)
8581 : {
8582 : SubPlan *result;
8583 : deparse_namespace *dpns;
8584 : ListCell *lc;
8585 :
8586 1608 : dpns = (deparse_namespace *) linitial(context->namespaces);
8587 :
8588 : /* First check the innermost plan node's initplans */
8589 1608 : result = find_param_generator_initplan(param, dpns->plan, column_p);
8590 1608 : if (result)
8591 474 : return result;
8592 :
8593 : /*
8594 : * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
8595 : * which can be referenced by Params elsewhere in the targetlist.
8596 : * (Such Params should always be in the same targetlist, so there's no
8597 : * need to do this work at upper plan nodes.)
8598 : */
8599 5828 : foreach_node(TargetEntry, tle, dpns->plan->targetlist)
8600 : {
8601 3664 : if (tle->expr && IsA(tle->expr, SubPlan))
8602 : {
8603 100 : SubPlan *subplan = (SubPlan *) tle->expr;
8604 :
8605 100 : if (subplan->subLinkType == MULTIEXPR_SUBLINK)
8606 : {
8607 78 : foreach_int(paramid, subplan->setParam)
8608 : {
8609 78 : if (paramid == param->paramid)
8610 : {
8611 : /* Found a match, so return it. */
8612 52 : *column_p = foreach_current_index(paramid);
8613 52 : return subplan;
8614 : }
8615 : }
8616 : }
8617 : }
8618 : }
8619 :
8620 : /* No luck, so check the ancestor nodes */
8621 1412 : foreach(lc, dpns->ancestors)
8622 : {
8623 1412 : Node *ancestor = (Node *) lfirst(lc);
8624 :
8625 : /*
8626 : * If ancestor is a SubPlan, check the paramIds it provides.
8627 : */
8628 1412 : if (IsA(ancestor, SubPlan))
8629 0 : {
8630 210 : SubPlan *subplan = (SubPlan *) ancestor;
8631 :
8632 236 : foreach_int(paramid, subplan->paramIds)
8633 : {
8634 236 : if (paramid == param->paramid)
8635 : {
8636 : /* Found a match, so return it. */
8637 210 : *column_p = foreach_current_index(paramid);
8638 210 : return subplan;
8639 : }
8640 : }
8641 :
8642 : /* SubPlan isn't a kind of Plan, so skip the rest */
8643 0 : continue;
8644 : }
8645 :
8646 : /*
8647 : * Otherwise, it's some kind of Plan node, so check its initplans.
8648 : */
8649 1202 : result = find_param_generator_initplan(param, (Plan *) ancestor,
8650 : column_p);
8651 1202 : if (result)
8652 872 : return result;
8653 :
8654 : /* No luck, crawl up to next ancestor */
8655 : }
8656 : }
8657 :
8658 : /* No generator found */
8659 904 : return NULL;
8660 : }
8661 :
8662 : /*
8663 : * Subroutine for find_param_generator: search one Plan node's initplans
8664 : */
8665 : static SubPlan *
8666 2810 : find_param_generator_initplan(Param *param, Plan *plan, int *column_p)
8667 : {
8668 4414 : foreach_node(SubPlan, subplan, plan->initPlan)
8669 : {
8670 1772 : foreach_int(paramid, subplan->setParam)
8671 : {
8672 1492 : if (paramid == param->paramid)
8673 : {
8674 : /* Found a match, so return it. */
8675 1346 : *column_p = foreach_current_index(paramid);
8676 1346 : return subplan;
8677 : }
8678 : }
8679 : }
8680 1464 : return NULL;
8681 : }
8682 :
8683 : /*
8684 : * Display a Param appropriately.
8685 : */
8686 : static void
8687 6822 : get_parameter(Param *param, deparse_context *context)
8688 : {
8689 : Node *expr;
8690 : deparse_namespace *dpns;
8691 : ListCell *ancestor_cell;
8692 : SubPlan *subplan;
8693 : int column;
8694 :
8695 : /*
8696 : * If it's a PARAM_EXEC parameter, try to locate the expression from which
8697 : * the parameter was computed. This stanza handles only cases in which
8698 : * the Param represents an input to the subplan we are currently in.
8699 : */
8700 6822 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8701 6822 : if (expr)
8702 : {
8703 : /* Found a match, so print it */
8704 : deparse_namespace save_dpns;
8705 : bool save_varprefix;
8706 : bool need_paren;
8707 :
8708 : /* Switch attention to the ancestor plan node */
8709 4310 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
8710 :
8711 : /*
8712 : * Force prefixing of Vars, since they won't belong to the relation
8713 : * being scanned in the original plan node.
8714 : */
8715 4310 : save_varprefix = context->varprefix;
8716 4310 : context->varprefix = true;
8717 :
8718 : /*
8719 : * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
8720 : * upper-level Param, which wouldn't need extra parentheses.
8721 : * Otherwise, insert parens to ensure the expression looks atomic.
8722 : */
8723 4328 : need_paren = !(IsA(expr, Var) ||
8724 18 : IsA(expr, Aggref) ||
8725 18 : IsA(expr, GroupingFunc) ||
8726 12 : IsA(expr, Param));
8727 4310 : if (need_paren)
8728 0 : appendStringInfoChar(context->buf, '(');
8729 :
8730 4310 : get_rule_expr(expr, context, false);
8731 :
8732 4310 : if (need_paren)
8733 0 : appendStringInfoChar(context->buf, ')');
8734 :
8735 4310 : context->varprefix = save_varprefix;
8736 :
8737 4310 : pop_ancestor_plan(dpns, &save_dpns);
8738 :
8739 4310 : return;
8740 : }
8741 :
8742 : /*
8743 : * Alternatively, maybe it's a subplan output, which we print as a
8744 : * reference to the subplan. (We could drill down into the subplan and
8745 : * print the relevant targetlist expression, but that has been deemed too
8746 : * confusing since it would violate normal SQL scope rules. Also, we're
8747 : * relying on this reference to show that the testexpr containing the
8748 : * Param has anything to do with that subplan at all.)
8749 : */
8750 2512 : subplan = find_param_generator(param, context, &column);
8751 2512 : if (subplan)
8752 : {
8753 1608 : appendStringInfo(context->buf, "(%s%s).col%d",
8754 1608 : subplan->useHashTable ? "hashed " : "",
8755 : subplan->plan_name, column + 1);
8756 :
8757 1608 : return;
8758 : }
8759 :
8760 : /*
8761 : * If it's an external parameter, see if the outermost namespace provides
8762 : * function argument names.
8763 : */
8764 904 : if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
8765 : {
8766 904 : dpns = llast(context->namespaces);
8767 904 : if (dpns->argnames &&
8768 68 : param->paramid > 0 &&
8769 68 : param->paramid <= dpns->numargs)
8770 : {
8771 68 : char *argname = dpns->argnames[param->paramid - 1];
8772 :
8773 68 : if (argname)
8774 : {
8775 68 : bool should_qualify = false;
8776 : ListCell *lc;
8777 :
8778 : /*
8779 : * Qualify the parameter name if there are any other deparse
8780 : * namespaces with range tables. This avoids qualifying in
8781 : * trivial cases like "RETURN a + b", but makes it safe in all
8782 : * other cases.
8783 : */
8784 156 : foreach(lc, context->namespaces)
8785 : {
8786 118 : deparse_namespace *depns = lfirst(lc);
8787 :
8788 118 : if (depns->rtable_names != NIL)
8789 : {
8790 30 : should_qualify = true;
8791 30 : break;
8792 : }
8793 : }
8794 68 : if (should_qualify)
8795 : {
8796 30 : appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
8797 30 : appendStringInfoChar(context->buf, '.');
8798 : }
8799 :
8800 68 : appendStringInfoString(context->buf, quote_identifier(argname));
8801 68 : return;
8802 : }
8803 : }
8804 : }
8805 :
8806 : /*
8807 : * Not PARAM_EXEC, or couldn't find referent: just print $N.
8808 : *
8809 : * It's a bug if we get here for anything except PARAM_EXTERN Params, but
8810 : * in production builds printing $N seems more useful than failing.
8811 : */
8812 : Assert(param->paramkind == PARAM_EXTERN);
8813 :
8814 836 : appendStringInfo(context->buf, "$%d", param->paramid);
8815 : }
8816 :
8817 : /*
8818 : * get_simple_binary_op_name
8819 : *
8820 : * helper function for isSimpleNode
8821 : * will return single char binary operator name, or NULL if it's not
8822 : */
8823 : static const char *
8824 150 : get_simple_binary_op_name(OpExpr *expr)
8825 : {
8826 150 : List *args = expr->args;
8827 :
8828 150 : if (list_length(args) == 2)
8829 : {
8830 : /* binary operator */
8831 150 : Node *arg1 = (Node *) linitial(args);
8832 150 : Node *arg2 = (Node *) lsecond(args);
8833 : const char *op;
8834 :
8835 150 : op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
8836 150 : if (strlen(op) == 1)
8837 150 : return op;
8838 : }
8839 0 : return NULL;
8840 : }
8841 :
8842 :
8843 : /*
8844 : * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
8845 : *
8846 : * true : simple in the context of parent node's type
8847 : * false : not simple
8848 : */
8849 : static bool
8850 5536 : isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
8851 : {
8852 5536 : if (!node)
8853 0 : return false;
8854 :
8855 5536 : switch (nodeTag(node))
8856 : {
8857 4674 : case T_Var:
8858 : case T_Const:
8859 : case T_Param:
8860 : case T_CoerceToDomainValue:
8861 : case T_SetToDefault:
8862 : case T_CurrentOfExpr:
8863 : /* single words: always simple */
8864 4674 : return true;
8865 :
8866 460 : case T_SubscriptingRef:
8867 : case T_ArrayExpr:
8868 : case T_RowExpr:
8869 : case T_CoalesceExpr:
8870 : case T_MinMaxExpr:
8871 : case T_SQLValueFunction:
8872 : case T_XmlExpr:
8873 : case T_NextValueExpr:
8874 : case T_NullIfExpr:
8875 : case T_Aggref:
8876 : case T_GroupingFunc:
8877 : case T_WindowFunc:
8878 : case T_MergeSupportFunc:
8879 : case T_FuncExpr:
8880 : case T_JsonConstructorExpr:
8881 : case T_JsonExpr:
8882 : /* function-like: name(..) or name[..] */
8883 460 : return true;
8884 :
8885 : /* CASE keywords act as parentheses */
8886 0 : case T_CaseExpr:
8887 0 : return true;
8888 :
8889 54 : case T_FieldSelect:
8890 :
8891 : /*
8892 : * appears simple since . has top precedence, unless parent is
8893 : * T_FieldSelect itself!
8894 : */
8895 54 : return !IsA(parentNode, FieldSelect);
8896 :
8897 0 : case T_FieldStore:
8898 :
8899 : /*
8900 : * treat like FieldSelect (probably doesn't matter)
8901 : */
8902 0 : return !IsA(parentNode, FieldStore);
8903 :
8904 0 : case T_CoerceToDomain:
8905 : /* maybe simple, check args */
8906 0 : return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
8907 : node, prettyFlags);
8908 18 : case T_RelabelType:
8909 18 : return isSimpleNode((Node *) ((RelabelType *) node)->arg,
8910 : node, prettyFlags);
8911 0 : case T_CoerceViaIO:
8912 0 : return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
8913 : node, prettyFlags);
8914 0 : case T_ArrayCoerceExpr:
8915 0 : return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
8916 : node, prettyFlags);
8917 0 : case T_ConvertRowtypeExpr:
8918 0 : return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
8919 : node, prettyFlags);
8920 0 : case T_ReturningExpr:
8921 0 : return isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr,
8922 : node, prettyFlags);
8923 :
8924 276 : case T_OpExpr:
8925 : {
8926 : /* depends on parent node type; needs further checking */
8927 276 : if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
8928 : {
8929 : const char *op;
8930 : const char *parentOp;
8931 : bool is_lopriop;
8932 : bool is_hipriop;
8933 : bool is_lopriparent;
8934 : bool is_hipriparent;
8935 :
8936 78 : op = get_simple_binary_op_name((OpExpr *) node);
8937 78 : if (!op)
8938 0 : return false;
8939 :
8940 : /* We know only the basic operators + - and * / % */
8941 78 : is_lopriop = (strchr("+-", *op) != NULL);
8942 78 : is_hipriop = (strchr("*/%", *op) != NULL);
8943 78 : if (!(is_lopriop || is_hipriop))
8944 6 : return false;
8945 :
8946 72 : parentOp = get_simple_binary_op_name((OpExpr *) parentNode);
8947 72 : if (!parentOp)
8948 0 : return false;
8949 :
8950 72 : is_lopriparent = (strchr("+-", *parentOp) != NULL);
8951 72 : is_hipriparent = (strchr("*/%", *parentOp) != NULL);
8952 72 : if (!(is_lopriparent || is_hipriparent))
8953 0 : return false;
8954 :
8955 72 : if (is_hipriop && is_lopriparent)
8956 12 : return true; /* op binds tighter than parent */
8957 :
8958 60 : if (is_lopriop && is_hipriparent)
8959 48 : return false;
8960 :
8961 : /*
8962 : * Operators are same priority --- can skip parens only if
8963 : * we have (a - b) - c, not a - (b - c).
8964 : */
8965 12 : if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
8966 6 : return true;
8967 :
8968 6 : return false;
8969 : }
8970 : /* else do the same stuff as for T_SubLink et al. */
8971 : }
8972 : /* FALLTHROUGH */
8973 :
8974 : case T_SubLink:
8975 : case T_NullTest:
8976 : case T_BooleanTest:
8977 : case T_DistinctExpr:
8978 : case T_JsonIsPredicate:
8979 228 : switch (nodeTag(parentNode))
8980 : {
8981 36 : case T_FuncExpr:
8982 : {
8983 : /* special handling for casts and COERCE_SQL_SYNTAX */
8984 36 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
8985 :
8986 36 : if (type == COERCE_EXPLICIT_CAST ||
8987 6 : type == COERCE_IMPLICIT_CAST ||
8988 : type == COERCE_SQL_SYNTAX)
8989 36 : return false;
8990 0 : return true; /* own parentheses */
8991 : }
8992 162 : case T_BoolExpr: /* lower precedence */
8993 : case T_SubscriptingRef: /* other separators */
8994 : case T_ArrayExpr: /* other separators */
8995 : case T_RowExpr: /* other separators */
8996 : case T_CoalesceExpr: /* own parentheses */
8997 : case T_MinMaxExpr: /* own parentheses */
8998 : case T_XmlExpr: /* own parentheses */
8999 : case T_NullIfExpr: /* other separators */
9000 : case T_Aggref: /* own parentheses */
9001 : case T_GroupingFunc: /* own parentheses */
9002 : case T_WindowFunc: /* own parentheses */
9003 : case T_CaseExpr: /* other separators */
9004 162 : return true;
9005 30 : default:
9006 30 : return false;
9007 : }
9008 :
9009 18 : case T_BoolExpr:
9010 18 : switch (nodeTag(parentNode))
9011 : {
9012 18 : case T_BoolExpr:
9013 18 : if (prettyFlags & PRETTYFLAG_PAREN)
9014 : {
9015 : BoolExprType type;
9016 : BoolExprType parentType;
9017 :
9018 18 : type = ((BoolExpr *) node)->boolop;
9019 18 : parentType = ((BoolExpr *) parentNode)->boolop;
9020 : switch (type)
9021 : {
9022 12 : case NOT_EXPR:
9023 : case AND_EXPR:
9024 12 : if (parentType == AND_EXPR || parentType == OR_EXPR)
9025 12 : return true;
9026 0 : break;
9027 6 : case OR_EXPR:
9028 6 : if (parentType == OR_EXPR)
9029 0 : return true;
9030 6 : break;
9031 : }
9032 : }
9033 6 : return false;
9034 0 : case T_FuncExpr:
9035 : {
9036 : /* special handling for casts and COERCE_SQL_SYNTAX */
9037 0 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9038 :
9039 0 : if (type == COERCE_EXPLICIT_CAST ||
9040 0 : type == COERCE_IMPLICIT_CAST ||
9041 : type == COERCE_SQL_SYNTAX)
9042 0 : return false;
9043 0 : return true; /* own parentheses */
9044 : }
9045 0 : case T_SubscriptingRef: /* other separators */
9046 : case T_ArrayExpr: /* other separators */
9047 : case T_RowExpr: /* other separators */
9048 : case T_CoalesceExpr: /* own parentheses */
9049 : case T_MinMaxExpr: /* own parentheses */
9050 : case T_XmlExpr: /* own parentheses */
9051 : case T_NullIfExpr: /* other separators */
9052 : case T_Aggref: /* own parentheses */
9053 : case T_GroupingFunc: /* own parentheses */
9054 : case T_WindowFunc: /* own parentheses */
9055 : case T_CaseExpr: /* other separators */
9056 : case T_JsonExpr: /* own parentheses */
9057 0 : return true;
9058 0 : default:
9059 0 : return false;
9060 : }
9061 :
9062 0 : case T_JsonValueExpr:
9063 : /* maybe simple, check args */
9064 0 : return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
9065 : node, prettyFlags);
9066 :
9067 6 : default:
9068 6 : break;
9069 : }
9070 : /* those we don't know: in dubio complexo */
9071 6 : return false;
9072 : }
9073 :
9074 :
9075 : /*
9076 : * appendContextKeyword - append a keyword to buffer
9077 : *
9078 : * If prettyPrint is enabled, perform a line break, and adjust indentation.
9079 : * Otherwise, just append the keyword.
9080 : */
9081 : static void
9082 31556 : appendContextKeyword(deparse_context *context, const char *str,
9083 : int indentBefore, int indentAfter, int indentPlus)
9084 : {
9085 31556 : StringInfo buf = context->buf;
9086 :
9087 31556 : if (PRETTY_INDENT(context))
9088 : {
9089 : int indentAmount;
9090 :
9091 30640 : context->indentLevel += indentBefore;
9092 :
9093 : /* remove any trailing spaces currently in the buffer ... */
9094 30640 : removeStringInfoSpaces(buf);
9095 : /* ... then add a newline and some spaces */
9096 30640 : appendStringInfoChar(buf, '\n');
9097 :
9098 30640 : if (context->indentLevel < PRETTYINDENT_LIMIT)
9099 30640 : indentAmount = Max(context->indentLevel, 0) + indentPlus;
9100 : else
9101 : {
9102 : /*
9103 : * If we're indented more than PRETTYINDENT_LIMIT characters, try
9104 : * to conserve horizontal space by reducing the per-level
9105 : * indentation. For best results the scale factor here should
9106 : * divide all the indent amounts that get added to indentLevel
9107 : * (PRETTYINDENT_STD, etc). It's important that the indentation
9108 : * not grow unboundedly, else deeply-nested trees use O(N^2)
9109 : * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
9110 : */
9111 0 : indentAmount = PRETTYINDENT_LIMIT +
9112 0 : (context->indentLevel - PRETTYINDENT_LIMIT) /
9113 : (PRETTYINDENT_STD / 2);
9114 0 : indentAmount %= PRETTYINDENT_LIMIT;
9115 : /* scale/wrap logic affects indentLevel, but not indentPlus */
9116 0 : indentAmount += indentPlus;
9117 : }
9118 30640 : appendStringInfoSpaces(buf, indentAmount);
9119 :
9120 30640 : appendStringInfoString(buf, str);
9121 :
9122 30640 : context->indentLevel += indentAfter;
9123 30640 : if (context->indentLevel < 0)
9124 0 : context->indentLevel = 0;
9125 : }
9126 : else
9127 916 : appendStringInfoString(buf, str);
9128 31556 : }
9129 :
9130 : /*
9131 : * removeStringInfoSpaces - delete trailing spaces from a buffer.
9132 : *
9133 : * Possibly this should move to stringinfo.c at some point.
9134 : */
9135 : static void
9136 31102 : removeStringInfoSpaces(StringInfo str)
9137 : {
9138 48576 : while (str->len > 0 && str->data[str->len - 1] == ' ')
9139 17474 : str->data[--(str->len)] = '\0';
9140 31102 : }
9141 :
9142 :
9143 : /*
9144 : * get_rule_expr_paren - deparse expr using get_rule_expr,
9145 : * embracing the string with parentheses if necessary for prettyPrint.
9146 : *
9147 : * Never embrace if prettyFlags=0, because it's done in the calling node.
9148 : *
9149 : * Any node that does *not* embrace its argument node by sql syntax (with
9150 : * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
9151 : * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
9152 : * added.
9153 : */
9154 : static void
9155 164954 : get_rule_expr_paren(Node *node, deparse_context *context,
9156 : bool showimplicit, Node *parentNode)
9157 : {
9158 : bool need_paren;
9159 :
9160 170472 : need_paren = PRETTY_PAREN(context) &&
9161 5518 : !isSimpleNode(node, parentNode, context->prettyFlags);
9162 :
9163 164954 : if (need_paren)
9164 138 : appendStringInfoChar(context->buf, '(');
9165 :
9166 164954 : get_rule_expr(node, context, showimplicit);
9167 :
9168 164954 : if (need_paren)
9169 138 : appendStringInfoChar(context->buf, ')');
9170 164954 : }
9171 :
9172 : static void
9173 84 : get_json_behavior(JsonBehavior *behavior, deparse_context *context,
9174 : const char *on)
9175 : {
9176 : /*
9177 : * The order of array elements must correspond to the order of
9178 : * JsonBehaviorType members.
9179 : */
9180 84 : const char *behavior_names[] =
9181 : {
9182 : " NULL",
9183 : " ERROR",
9184 : " EMPTY",
9185 : " TRUE",
9186 : " FALSE",
9187 : " UNKNOWN",
9188 : " EMPTY ARRAY",
9189 : " EMPTY OBJECT",
9190 : " DEFAULT "
9191 : };
9192 :
9193 84 : if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
9194 0 : elog(ERROR, "invalid json behavior type: %d", behavior->btype);
9195 :
9196 84 : appendStringInfoString(context->buf, behavior_names[behavior->btype]);
9197 :
9198 84 : if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
9199 18 : get_rule_expr(behavior->expr, context, false);
9200 :
9201 84 : appendStringInfo(context->buf, " ON %s", on);
9202 84 : }
9203 :
9204 : /*
9205 : * get_json_expr_options
9206 : *
9207 : * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and
9208 : * JSON_TABLE columns.
9209 : */
9210 : static void
9211 456 : get_json_expr_options(JsonExpr *jsexpr, deparse_context *context,
9212 : JsonBehaviorType default_behavior)
9213 : {
9214 456 : if (jsexpr->op == JSON_QUERY_OP)
9215 : {
9216 210 : if (jsexpr->wrapper == JSW_CONDITIONAL)
9217 12 : appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
9218 198 : else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
9219 30 : appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
9220 : /* The default */
9221 168 : else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
9222 168 : appendStringInfoString(context->buf, " WITHOUT WRAPPER");
9223 :
9224 210 : if (jsexpr->omit_quotes)
9225 42 : appendStringInfoString(context->buf, " OMIT QUOTES");
9226 : /* The default */
9227 : else
9228 168 : appendStringInfoString(context->buf, " KEEP QUOTES");
9229 : }
9230 :
9231 456 : if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
9232 30 : get_json_behavior(jsexpr->on_empty, context, "EMPTY");
9233 :
9234 456 : if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
9235 48 : get_json_behavior(jsexpr->on_error, context, "ERROR");
9236 456 : }
9237 :
9238 : /* ----------
9239 : * get_rule_expr - Parse back an expression
9240 : *
9241 : * Note: showimplicit determines whether we display any implicit cast that
9242 : * is present at the top of the expression tree. It is a passed argument,
9243 : * not a field of the context struct, because we change the value as we
9244 : * recurse down into the expression. In general we suppress implicit casts
9245 : * when the result type is known with certainty (eg, the arguments of an
9246 : * OR must be boolean). We display implicit casts for arguments of functions
9247 : * and operators, since this is needed to be certain that the same function
9248 : * or operator will be chosen when the expression is re-parsed.
9249 : * ----------
9250 : */
9251 : static void
9252 338314 : get_rule_expr(Node *node, deparse_context *context,
9253 : bool showimplicit)
9254 : {
9255 338314 : StringInfo buf = context->buf;
9256 :
9257 338314 : if (node == NULL)
9258 90 : return;
9259 :
9260 : /* Guard against excessively long or deeply-nested queries */
9261 338224 : CHECK_FOR_INTERRUPTS();
9262 338224 : check_stack_depth();
9263 :
9264 : /*
9265 : * Each level of get_rule_expr must emit an indivisible term
9266 : * (parenthesized if necessary) to ensure result is reparsed into the same
9267 : * expression tree. The only exception is that when the input is a List,
9268 : * we emit the component items comma-separated with no surrounding
9269 : * decoration; this is convenient for most callers.
9270 : */
9271 338224 : switch (nodeTag(node))
9272 : {
9273 154300 : case T_Var:
9274 154300 : (void) get_variable((Var *) node, 0, false, context);
9275 154300 : break;
9276 :
9277 63278 : case T_Const:
9278 63278 : get_const_expr((Const *) node, context, 0);
9279 63278 : break;
9280 :
9281 6822 : case T_Param:
9282 6822 : get_parameter((Param *) node, context);
9283 6822 : break;
9284 :
9285 1770 : case T_Aggref:
9286 1770 : get_agg_expr((Aggref *) node, context, (Aggref *) node);
9287 1770 : break;
9288 :
9289 112 : case T_GroupingFunc:
9290 : {
9291 112 : GroupingFunc *gexpr = (GroupingFunc *) node;
9292 :
9293 112 : appendStringInfoString(buf, "GROUPING(");
9294 112 : get_rule_expr((Node *) gexpr->args, context, true);
9295 112 : appendStringInfoChar(buf, ')');
9296 : }
9297 112 : break;
9298 :
9299 306 : case T_WindowFunc:
9300 306 : get_windowfunc_expr((WindowFunc *) node, context);
9301 306 : break;
9302 :
9303 6 : case T_MergeSupportFunc:
9304 6 : appendStringInfoString(buf, "MERGE_ACTION()");
9305 6 : break;
9306 :
9307 352 : case T_SubscriptingRef:
9308 : {
9309 352 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
9310 : bool need_parens;
9311 :
9312 : /*
9313 : * If the argument is a CaseTestExpr, we must be inside a
9314 : * FieldStore, ie, we are assigning to an element of an array
9315 : * within a composite column. Since we already punted on
9316 : * displaying the FieldStore's target information, just punt
9317 : * here too, and display only the assignment source
9318 : * expression.
9319 : */
9320 352 : if (IsA(sbsref->refexpr, CaseTestExpr))
9321 : {
9322 : Assert(sbsref->refassgnexpr);
9323 0 : get_rule_expr((Node *) sbsref->refassgnexpr,
9324 : context, showimplicit);
9325 0 : break;
9326 : }
9327 :
9328 : /*
9329 : * Parenthesize the argument unless it's a simple Var or a
9330 : * FieldSelect. (In particular, if it's another
9331 : * SubscriptingRef, we *must* parenthesize to avoid
9332 : * confusion.)
9333 : */
9334 530 : need_parens = !IsA(sbsref->refexpr, Var) &&
9335 178 : !IsA(sbsref->refexpr, FieldSelect);
9336 352 : if (need_parens)
9337 118 : appendStringInfoChar(buf, '(');
9338 352 : get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
9339 352 : if (need_parens)
9340 118 : appendStringInfoChar(buf, ')');
9341 :
9342 : /*
9343 : * If there's a refassgnexpr, we want to print the node in the
9344 : * format "container[subscripts] := refassgnexpr". This is
9345 : * not legal SQL, so decompilation of INSERT or UPDATE
9346 : * statements should always use processIndirection as part of
9347 : * the statement-level syntax. We should only see this when
9348 : * EXPLAIN tries to print the targetlist of a plan resulting
9349 : * from such a statement.
9350 : */
9351 352 : if (sbsref->refassgnexpr)
9352 : {
9353 : Node *refassgnexpr;
9354 :
9355 : /*
9356 : * Use processIndirection to print this node's subscripts
9357 : * as well as any additional field selections or
9358 : * subscripting in immediate descendants. It returns the
9359 : * RHS expr that is actually being "assigned".
9360 : */
9361 12 : refassgnexpr = processIndirection(node, context);
9362 12 : appendStringInfoString(buf, " := ");
9363 12 : get_rule_expr(refassgnexpr, context, showimplicit);
9364 : }
9365 : else
9366 : {
9367 : /* Just an ordinary container fetch, so print subscripts */
9368 340 : printSubscripts(sbsref, context);
9369 : }
9370 : }
9371 352 : break;
9372 :
9373 13198 : case T_FuncExpr:
9374 13198 : get_func_expr((FuncExpr *) node, context, showimplicit);
9375 13198 : break;
9376 :
9377 30 : case T_NamedArgExpr:
9378 : {
9379 30 : NamedArgExpr *na = (NamedArgExpr *) node;
9380 :
9381 30 : appendStringInfo(buf, "%s => ", quote_identifier(na->name));
9382 30 : get_rule_expr((Node *) na->arg, context, showimplicit);
9383 : }
9384 30 : break;
9385 :
9386 61612 : case T_OpExpr:
9387 61612 : get_oper_expr((OpExpr *) node, context);
9388 61612 : break;
9389 :
9390 18 : case T_DistinctExpr:
9391 : {
9392 18 : DistinctExpr *expr = (DistinctExpr *) node;
9393 18 : List *args = expr->args;
9394 18 : Node *arg1 = (Node *) linitial(args);
9395 18 : Node *arg2 = (Node *) lsecond(args);
9396 :
9397 18 : if (!PRETTY_PAREN(context))
9398 12 : appendStringInfoChar(buf, '(');
9399 18 : get_rule_expr_paren(arg1, context, true, node);
9400 18 : appendStringInfoString(buf, " IS DISTINCT FROM ");
9401 18 : get_rule_expr_paren(arg2, context, true, node);
9402 18 : if (!PRETTY_PAREN(context))
9403 12 : appendStringInfoChar(buf, ')');
9404 : }
9405 18 : break;
9406 :
9407 256 : case T_NullIfExpr:
9408 : {
9409 256 : NullIfExpr *nullifexpr = (NullIfExpr *) node;
9410 :
9411 256 : appendStringInfoString(buf, "NULLIF(");
9412 256 : get_rule_expr((Node *) nullifexpr->args, context, true);
9413 256 : appendStringInfoChar(buf, ')');
9414 : }
9415 256 : break;
9416 :
9417 3020 : case T_ScalarArrayOpExpr:
9418 : {
9419 3020 : ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
9420 3020 : List *args = expr->args;
9421 3020 : Node *arg1 = (Node *) linitial(args);
9422 3020 : Node *arg2 = (Node *) lsecond(args);
9423 :
9424 3020 : if (!PRETTY_PAREN(context))
9425 3008 : appendStringInfoChar(buf, '(');
9426 3020 : get_rule_expr_paren(arg1, context, true, node);
9427 3020 : appendStringInfo(buf, " %s %s (",
9428 : generate_operator_name(expr->opno,
9429 : exprType(arg1),
9430 : get_base_element_type(exprType(arg2))),
9431 3020 : expr->useOr ? "ANY" : "ALL");
9432 3020 : get_rule_expr_paren(arg2, context, true, node);
9433 :
9434 : /*
9435 : * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
9436 : * a bare sub-SELECT. Since we're here, the sub-SELECT must
9437 : * be meant as a scalar sub-SELECT yielding an array value to
9438 : * be used in ScalarArrayOpExpr; but the grammar will
9439 : * preferentially interpret such a construct as an ANY/ALL
9440 : * SubLink. To prevent misparsing the output that way, insert
9441 : * a dummy coercion (which will be stripped by parse analysis,
9442 : * so no inefficiency is added in dump and reload). This is
9443 : * indeed most likely what the user wrote to get the construct
9444 : * accepted in the first place.
9445 : */
9446 3020 : if (IsA(arg2, SubLink) &&
9447 6 : ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
9448 6 : appendStringInfo(buf, "::%s",
9449 : format_type_with_typemod(exprType(arg2),
9450 : exprTypmod(arg2)));
9451 3020 : appendStringInfoChar(buf, ')');
9452 3020 : if (!PRETTY_PAREN(context))
9453 3008 : appendStringInfoChar(buf, ')');
9454 : }
9455 3020 : break;
9456 :
9457 11302 : case T_BoolExpr:
9458 : {
9459 11302 : BoolExpr *expr = (BoolExpr *) node;
9460 11302 : Node *first_arg = linitial(expr->args);
9461 : ListCell *arg;
9462 :
9463 11302 : switch (expr->boolop)
9464 : {
9465 8998 : case AND_EXPR:
9466 8998 : if (!PRETTY_PAREN(context))
9467 8932 : appendStringInfoChar(buf, '(');
9468 8998 : get_rule_expr_paren(first_arg, context,
9469 : false, node);
9470 20536 : for_each_from(arg, expr->args, 1)
9471 : {
9472 11538 : appendStringInfoString(buf, " AND ");
9473 11538 : get_rule_expr_paren((Node *) lfirst(arg), context,
9474 : false, node);
9475 : }
9476 8998 : if (!PRETTY_PAREN(context))
9477 8932 : appendStringInfoChar(buf, ')');
9478 8998 : break;
9479 :
9480 1932 : case OR_EXPR:
9481 1932 : if (!PRETTY_PAREN(context))
9482 1920 : appendStringInfoChar(buf, '(');
9483 1932 : get_rule_expr_paren(first_arg, context,
9484 : false, node);
9485 4578 : for_each_from(arg, expr->args, 1)
9486 : {
9487 2646 : appendStringInfoString(buf, " OR ");
9488 2646 : get_rule_expr_paren((Node *) lfirst(arg), context,
9489 : false, node);
9490 : }
9491 1932 : if (!PRETTY_PAREN(context))
9492 1920 : appendStringInfoChar(buf, ')');
9493 1932 : break;
9494 :
9495 372 : case NOT_EXPR:
9496 372 : if (!PRETTY_PAREN(context))
9497 360 : appendStringInfoChar(buf, '(');
9498 372 : appendStringInfoString(buf, "NOT ");
9499 372 : get_rule_expr_paren(first_arg, context,
9500 : false, node);
9501 372 : if (!PRETTY_PAREN(context))
9502 360 : appendStringInfoChar(buf, ')');
9503 372 : break;
9504 :
9505 0 : default:
9506 0 : elog(ERROR, "unrecognized boolop: %d",
9507 : (int) expr->boolop);
9508 : }
9509 : }
9510 11302 : break;
9511 :
9512 484 : case T_SubLink:
9513 484 : get_sublink_expr((SubLink *) node, context);
9514 484 : break;
9515 :
9516 712 : case T_SubPlan:
9517 : {
9518 712 : SubPlan *subplan = (SubPlan *) node;
9519 :
9520 : /*
9521 : * We cannot see an already-planned subplan in rule deparsing,
9522 : * only while EXPLAINing a query plan. We don't try to
9523 : * reconstruct the original SQL, just reference the subplan
9524 : * that appears elsewhere in EXPLAIN's result. It does seem
9525 : * useful to show the subLinkType and testexpr (if any), and
9526 : * we also note whether the subplan will be hashed.
9527 : */
9528 712 : switch (subplan->subLinkType)
9529 : {
9530 90 : case EXISTS_SUBLINK:
9531 90 : appendStringInfoString(buf, "EXISTS(");
9532 : Assert(subplan->testexpr == NULL);
9533 90 : break;
9534 6 : case ALL_SUBLINK:
9535 6 : appendStringInfoString(buf, "(ALL ");
9536 : Assert(subplan->testexpr != NULL);
9537 6 : break;
9538 166 : case ANY_SUBLINK:
9539 166 : appendStringInfoString(buf, "(ANY ");
9540 : Assert(subplan->testexpr != NULL);
9541 166 : break;
9542 6 : case ROWCOMPARE_SUBLINK:
9543 : /* Parenthesizing the testexpr seems sufficient */
9544 6 : appendStringInfoChar(buf, '(');
9545 : Assert(subplan->testexpr != NULL);
9546 6 : break;
9547 406 : case EXPR_SUBLINK:
9548 : /* No need to decorate these subplan references */
9549 406 : appendStringInfoChar(buf, '(');
9550 : Assert(subplan->testexpr == NULL);
9551 406 : break;
9552 26 : case MULTIEXPR_SUBLINK:
9553 : /* MULTIEXPR isn't executed in the normal way */
9554 26 : appendStringInfoString(buf, "(rescan ");
9555 : Assert(subplan->testexpr == NULL);
9556 26 : break;
9557 12 : case ARRAY_SUBLINK:
9558 12 : appendStringInfoString(buf, "ARRAY(");
9559 : Assert(subplan->testexpr == NULL);
9560 12 : break;
9561 0 : case CTE_SUBLINK:
9562 : /* This case is unreachable within expressions */
9563 0 : appendStringInfoString(buf, "CTE(");
9564 : Assert(subplan->testexpr == NULL);
9565 0 : break;
9566 : }
9567 :
9568 712 : if (subplan->testexpr != NULL)
9569 : {
9570 : deparse_namespace *dpns;
9571 :
9572 : /*
9573 : * Push SubPlan into ancestors list while deparsing
9574 : * testexpr, so that we can handle PARAM_EXEC references
9575 : * to the SubPlan's paramIds. (This makes it look like
9576 : * the SubPlan is an "ancestor" of the current plan node,
9577 : * which is a little weird, but it does no harm.) In this
9578 : * path, we don't need to mention the SubPlan explicitly,
9579 : * because the referencing Params will show its existence.
9580 : */
9581 178 : dpns = (deparse_namespace *) linitial(context->namespaces);
9582 178 : dpns->ancestors = lcons(subplan, dpns->ancestors);
9583 :
9584 178 : get_rule_expr(subplan->testexpr, context, showimplicit);
9585 178 : appendStringInfoChar(buf, ')');
9586 :
9587 178 : dpns->ancestors = list_delete_first(dpns->ancestors);
9588 : }
9589 : else
9590 : {
9591 : /* No referencing Params, so show the SubPlan's name */
9592 534 : if (subplan->useHashTable)
9593 0 : appendStringInfo(buf, "hashed %s)", subplan->plan_name);
9594 : else
9595 534 : appendStringInfo(buf, "%s)", subplan->plan_name);
9596 : }
9597 : }
9598 712 : break;
9599 :
9600 0 : case T_AlternativeSubPlan:
9601 : {
9602 0 : AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
9603 : ListCell *lc;
9604 :
9605 : /*
9606 : * This case cannot be reached in normal usage, since no
9607 : * AlternativeSubPlan can appear either in parsetrees or
9608 : * finished plan trees. We keep it just in case somebody
9609 : * wants to use this code to print planner data structures.
9610 : */
9611 0 : appendStringInfoString(buf, "(alternatives: ");
9612 0 : foreach(lc, asplan->subplans)
9613 : {
9614 0 : SubPlan *splan = lfirst_node(SubPlan, lc);
9615 :
9616 0 : if (splan->useHashTable)
9617 0 : appendStringInfo(buf, "hashed %s", splan->plan_name);
9618 : else
9619 0 : appendStringInfoString(buf, splan->plan_name);
9620 0 : if (lnext(asplan->subplans, lc))
9621 0 : appendStringInfoString(buf, " or ");
9622 : }
9623 0 : appendStringInfoChar(buf, ')');
9624 : }
9625 0 : break;
9626 :
9627 1118 : case T_FieldSelect:
9628 : {
9629 1118 : FieldSelect *fselect = (FieldSelect *) node;
9630 1118 : Node *arg = (Node *) fselect->arg;
9631 1118 : int fno = fselect->fieldnum;
9632 : const char *fieldname;
9633 : bool need_parens;
9634 :
9635 : /*
9636 : * Parenthesize the argument unless it's an SubscriptingRef or
9637 : * another FieldSelect. Note in particular that it would be
9638 : * WRONG to not parenthesize a Var argument; simplicity is not
9639 : * the issue here, having the right number of names is.
9640 : */
9641 2200 : need_parens = !IsA(arg, SubscriptingRef) &&
9642 1082 : !IsA(arg, FieldSelect);
9643 1118 : if (need_parens)
9644 1082 : appendStringInfoChar(buf, '(');
9645 1118 : get_rule_expr(arg, context, true);
9646 1118 : if (need_parens)
9647 1082 : appendStringInfoChar(buf, ')');
9648 :
9649 : /*
9650 : * Get and print the field name.
9651 : */
9652 1118 : fieldname = get_name_for_var_field((Var *) arg, fno,
9653 : 0, context);
9654 1118 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
9655 : }
9656 1118 : break;
9657 :
9658 6 : case T_FieldStore:
9659 : {
9660 6 : FieldStore *fstore = (FieldStore *) node;
9661 : bool need_parens;
9662 :
9663 : /*
9664 : * There is no good way to represent a FieldStore as real SQL,
9665 : * so decompilation of INSERT or UPDATE statements should
9666 : * always use processIndirection as part of the
9667 : * statement-level syntax. We should only get here when
9668 : * EXPLAIN tries to print the targetlist of a plan resulting
9669 : * from such a statement. The plan case is even harder than
9670 : * ordinary rules would be, because the planner tries to
9671 : * collapse multiple assignments to the same field or subfield
9672 : * into one FieldStore; so we can see a list of target fields
9673 : * not just one, and the arguments could be FieldStores
9674 : * themselves. We don't bother to try to print the target
9675 : * field names; we just print the source arguments, with a
9676 : * ROW() around them if there's more than one. This isn't
9677 : * terribly complete, but it's probably good enough for
9678 : * EXPLAIN's purposes; especially since anything more would be
9679 : * either hopelessly confusing or an even poorer
9680 : * representation of what the plan is actually doing.
9681 : */
9682 6 : need_parens = (list_length(fstore->newvals) != 1);
9683 6 : if (need_parens)
9684 6 : appendStringInfoString(buf, "ROW(");
9685 6 : get_rule_expr((Node *) fstore->newvals, context, showimplicit);
9686 6 : if (need_parens)
9687 6 : appendStringInfoChar(buf, ')');
9688 : }
9689 6 : break;
9690 :
9691 2636 : case T_RelabelType:
9692 : {
9693 2636 : RelabelType *relabel = (RelabelType *) node;
9694 2636 : Node *arg = (Node *) relabel->arg;
9695 :
9696 2636 : if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
9697 2430 : !showimplicit)
9698 : {
9699 : /* don't show the implicit cast */
9700 98 : get_rule_expr_paren(arg, context, false, node);
9701 : }
9702 : else
9703 : {
9704 2538 : get_coercion_expr(arg, context,
9705 : relabel->resulttype,
9706 : relabel->resulttypmod,
9707 : node);
9708 : }
9709 : }
9710 2636 : break;
9711 :
9712 680 : case T_CoerceViaIO:
9713 : {
9714 680 : CoerceViaIO *iocoerce = (CoerceViaIO *) node;
9715 680 : Node *arg = (Node *) iocoerce->arg;
9716 :
9717 680 : if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
9718 24 : !showimplicit)
9719 : {
9720 : /* don't show the implicit cast */
9721 24 : get_rule_expr_paren(arg, context, false, node);
9722 : }
9723 : else
9724 : {
9725 656 : get_coercion_expr(arg, context,
9726 : iocoerce->resulttype,
9727 : -1,
9728 : node);
9729 : }
9730 : }
9731 680 : break;
9732 :
9733 48 : case T_ArrayCoerceExpr:
9734 : {
9735 48 : ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
9736 48 : Node *arg = (Node *) acoerce->arg;
9737 :
9738 48 : if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
9739 48 : !showimplicit)
9740 : {
9741 : /* don't show the implicit cast */
9742 0 : get_rule_expr_paren(arg, context, false, node);
9743 : }
9744 : else
9745 : {
9746 48 : get_coercion_expr(arg, context,
9747 : acoerce->resulttype,
9748 : acoerce->resulttypmod,
9749 : node);
9750 : }
9751 : }
9752 48 : break;
9753 :
9754 88 : case T_ConvertRowtypeExpr:
9755 : {
9756 88 : ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
9757 88 : Node *arg = (Node *) convert->arg;
9758 :
9759 88 : if (convert->convertformat == COERCE_IMPLICIT_CAST &&
9760 82 : !showimplicit)
9761 : {
9762 : /* don't show the implicit cast */
9763 24 : get_rule_expr_paren(arg, context, false, node);
9764 : }
9765 : else
9766 : {
9767 64 : get_coercion_expr(arg, context,
9768 : convert->resulttype, -1,
9769 : node);
9770 : }
9771 : }
9772 88 : break;
9773 :
9774 90 : case T_CollateExpr:
9775 : {
9776 90 : CollateExpr *collate = (CollateExpr *) node;
9777 90 : Node *arg = (Node *) collate->arg;
9778 :
9779 90 : if (!PRETTY_PAREN(context))
9780 84 : appendStringInfoChar(buf, '(');
9781 90 : get_rule_expr_paren(arg, context, showimplicit, node);
9782 90 : appendStringInfo(buf, " COLLATE %s",
9783 : generate_collation_name(collate->collOid));
9784 90 : if (!PRETTY_PAREN(context))
9785 84 : appendStringInfoChar(buf, ')');
9786 : }
9787 90 : break;
9788 :
9789 594 : case T_CaseExpr:
9790 : {
9791 594 : CaseExpr *caseexpr = (CaseExpr *) node;
9792 : ListCell *temp;
9793 :
9794 594 : appendContextKeyword(context, "CASE",
9795 : 0, PRETTYINDENT_VAR, 0);
9796 594 : if (caseexpr->arg)
9797 : {
9798 174 : appendStringInfoChar(buf, ' ');
9799 174 : get_rule_expr((Node *) caseexpr->arg, context, true);
9800 : }
9801 2626 : foreach(temp, caseexpr->args)
9802 : {
9803 2032 : CaseWhen *when = (CaseWhen *) lfirst(temp);
9804 2032 : Node *w = (Node *) when->expr;
9805 :
9806 2032 : if (caseexpr->arg)
9807 : {
9808 : /*
9809 : * The parser should have produced WHEN clauses of the
9810 : * form "CaseTestExpr = RHS", possibly with an
9811 : * implicit coercion inserted above the CaseTestExpr.
9812 : * For accurate decompilation of rules it's essential
9813 : * that we show just the RHS. However in an
9814 : * expression that's been through the optimizer, the
9815 : * WHEN clause could be almost anything (since the
9816 : * equality operator could have been expanded into an
9817 : * inline function). If we don't recognize the form
9818 : * of the WHEN clause, just punt and display it as-is.
9819 : */
9820 748 : if (IsA(w, OpExpr))
9821 : {
9822 748 : List *args = ((OpExpr *) w)->args;
9823 :
9824 748 : if (list_length(args) == 2 &&
9825 748 : IsA(strip_implicit_coercions(linitial(args)),
9826 : CaseTestExpr))
9827 748 : w = (Node *) lsecond(args);
9828 : }
9829 : }
9830 :
9831 2032 : if (!PRETTY_INDENT(context))
9832 118 : appendStringInfoChar(buf, ' ');
9833 2032 : appendContextKeyword(context, "WHEN ",
9834 : 0, 0, 0);
9835 2032 : get_rule_expr(w, context, false);
9836 2032 : appendStringInfoString(buf, " THEN ");
9837 2032 : get_rule_expr((Node *) when->result, context, true);
9838 : }
9839 594 : if (!PRETTY_INDENT(context))
9840 108 : appendStringInfoChar(buf, ' ');
9841 594 : appendContextKeyword(context, "ELSE ",
9842 : 0, 0, 0);
9843 594 : get_rule_expr((Node *) caseexpr->defresult, context, true);
9844 594 : if (!PRETTY_INDENT(context))
9845 108 : appendStringInfoChar(buf, ' ');
9846 594 : appendContextKeyword(context, "END",
9847 : -PRETTYINDENT_VAR, 0, 0);
9848 : }
9849 594 : break;
9850 :
9851 0 : case T_CaseTestExpr:
9852 : {
9853 : /*
9854 : * Normally we should never get here, since for expressions
9855 : * that can contain this node type we attempt to avoid
9856 : * recursing to it. But in an optimized expression we might
9857 : * be unable to avoid that (see comments for CaseExpr). If we
9858 : * do see one, print it as CASE_TEST_EXPR.
9859 : */
9860 0 : appendStringInfoString(buf, "CASE_TEST_EXPR");
9861 : }
9862 0 : break;
9863 :
9864 566 : case T_ArrayExpr:
9865 : {
9866 566 : ArrayExpr *arrayexpr = (ArrayExpr *) node;
9867 :
9868 566 : appendStringInfoString(buf, "ARRAY[");
9869 566 : get_rule_expr((Node *) arrayexpr->elements, context, true);
9870 566 : appendStringInfoChar(buf, ']');
9871 :
9872 : /*
9873 : * If the array isn't empty, we assume its elements are
9874 : * coerced to the desired type. If it's empty, though, we
9875 : * need an explicit coercion to the array type.
9876 : */
9877 566 : if (arrayexpr->elements == NIL)
9878 6 : appendStringInfo(buf, "::%s",
9879 : format_type_with_typemod(arrayexpr->array_typeid, -1));
9880 : }
9881 566 : break;
9882 :
9883 192 : case T_RowExpr:
9884 : {
9885 192 : RowExpr *rowexpr = (RowExpr *) node;
9886 192 : TupleDesc tupdesc = NULL;
9887 : ListCell *arg;
9888 : int i;
9889 : char *sep;
9890 :
9891 : /*
9892 : * If it's a named type and not RECORD, we may have to skip
9893 : * dropped columns and/or claim there are NULLs for added
9894 : * columns.
9895 : */
9896 192 : if (rowexpr->row_typeid != RECORDOID)
9897 : {
9898 54 : tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
9899 : Assert(list_length(rowexpr->args) <= tupdesc->natts);
9900 : }
9901 :
9902 : /*
9903 : * SQL99 allows "ROW" to be omitted when there is more than
9904 : * one column, but for simplicity we always print it.
9905 : */
9906 192 : appendStringInfoString(buf, "ROW(");
9907 192 : sep = "";
9908 192 : i = 0;
9909 570 : foreach(arg, rowexpr->args)
9910 : {
9911 378 : Node *e = (Node *) lfirst(arg);
9912 :
9913 378 : if (tupdesc == NULL ||
9914 120 : !TupleDescAttr(tupdesc, i)->attisdropped)
9915 : {
9916 378 : appendStringInfoString(buf, sep);
9917 : /* Whole-row Vars need special treatment here */
9918 378 : get_rule_expr_toplevel(e, context, true);
9919 378 : sep = ", ";
9920 : }
9921 378 : i++;
9922 : }
9923 192 : if (tupdesc != NULL)
9924 : {
9925 54 : while (i < tupdesc->natts)
9926 : {
9927 0 : if (!TupleDescAttr(tupdesc, i)->attisdropped)
9928 : {
9929 0 : appendStringInfoString(buf, sep);
9930 0 : appendStringInfoString(buf, "NULL");
9931 0 : sep = ", ";
9932 : }
9933 0 : i++;
9934 : }
9935 :
9936 54 : ReleaseTupleDesc(tupdesc);
9937 : }
9938 192 : appendStringInfoChar(buf, ')');
9939 192 : if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
9940 36 : appendStringInfo(buf, "::%s",
9941 : format_type_with_typemod(rowexpr->row_typeid, -1));
9942 : }
9943 192 : break;
9944 :
9945 96 : case T_RowCompareExpr:
9946 : {
9947 96 : RowCompareExpr *rcexpr = (RowCompareExpr *) node;
9948 :
9949 : /*
9950 : * SQL99 allows "ROW" to be omitted when there is more than
9951 : * one column, but for simplicity we always print it. Within
9952 : * a ROW expression, whole-row Vars need special treatment, so
9953 : * use get_rule_list_toplevel.
9954 : */
9955 96 : appendStringInfoString(buf, "(ROW(");
9956 96 : get_rule_list_toplevel(rcexpr->largs, context, true);
9957 :
9958 : /*
9959 : * We assume that the name of the first-column operator will
9960 : * do for all the rest too. This is definitely open to
9961 : * failure, eg if some but not all operators were renamed
9962 : * since the construct was parsed, but there seems no way to
9963 : * be perfect.
9964 : */
9965 96 : appendStringInfo(buf, ") %s ROW(",
9966 96 : generate_operator_name(linitial_oid(rcexpr->opnos),
9967 96 : exprType(linitial(rcexpr->largs)),
9968 96 : exprType(linitial(rcexpr->rargs))));
9969 96 : get_rule_list_toplevel(rcexpr->rargs, context, true);
9970 96 : appendStringInfoString(buf, "))");
9971 : }
9972 96 : break;
9973 :
9974 1146 : case T_CoalesceExpr:
9975 : {
9976 1146 : CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
9977 :
9978 1146 : appendStringInfoString(buf, "COALESCE(");
9979 1146 : get_rule_expr((Node *) coalesceexpr->args, context, true);
9980 1146 : appendStringInfoChar(buf, ')');
9981 : }
9982 1146 : break;
9983 :
9984 36 : case T_MinMaxExpr:
9985 : {
9986 36 : MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
9987 :
9988 36 : switch (minmaxexpr->op)
9989 : {
9990 6 : case IS_GREATEST:
9991 6 : appendStringInfoString(buf, "GREATEST(");
9992 6 : break;
9993 30 : case IS_LEAST:
9994 30 : appendStringInfoString(buf, "LEAST(");
9995 30 : break;
9996 : }
9997 36 : get_rule_expr((Node *) minmaxexpr->args, context, true);
9998 36 : appendStringInfoChar(buf, ')');
9999 : }
10000 36 : break;
10001 :
10002 756 : case T_SQLValueFunction:
10003 : {
10004 756 : SQLValueFunction *svf = (SQLValueFunction *) node;
10005 :
10006 : /*
10007 : * Note: this code knows that typmod for time, timestamp, and
10008 : * timestamptz just prints as integer.
10009 : */
10010 756 : switch (svf->op)
10011 : {
10012 104 : case SVFOP_CURRENT_DATE:
10013 104 : appendStringInfoString(buf, "CURRENT_DATE");
10014 104 : break;
10015 12 : case SVFOP_CURRENT_TIME:
10016 12 : appendStringInfoString(buf, "CURRENT_TIME");
10017 12 : break;
10018 12 : case SVFOP_CURRENT_TIME_N:
10019 12 : appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
10020 12 : break;
10021 12 : case SVFOP_CURRENT_TIMESTAMP:
10022 12 : appendStringInfoString(buf, "CURRENT_TIMESTAMP");
10023 12 : break;
10024 124 : case SVFOP_CURRENT_TIMESTAMP_N:
10025 124 : appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
10026 : svf->typmod);
10027 124 : break;
10028 12 : case SVFOP_LOCALTIME:
10029 12 : appendStringInfoString(buf, "LOCALTIME");
10030 12 : break;
10031 12 : case SVFOP_LOCALTIME_N:
10032 12 : appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
10033 12 : break;
10034 30 : case SVFOP_LOCALTIMESTAMP:
10035 30 : appendStringInfoString(buf, "LOCALTIMESTAMP");
10036 30 : break;
10037 18 : case SVFOP_LOCALTIMESTAMP_N:
10038 18 : appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
10039 : svf->typmod);
10040 18 : break;
10041 12 : case SVFOP_CURRENT_ROLE:
10042 12 : appendStringInfoString(buf, "CURRENT_ROLE");
10043 12 : break;
10044 338 : case SVFOP_CURRENT_USER:
10045 338 : appendStringInfoString(buf, "CURRENT_USER");
10046 338 : break;
10047 12 : case SVFOP_USER:
10048 12 : appendStringInfoString(buf, "USER");
10049 12 : break;
10050 34 : case SVFOP_SESSION_USER:
10051 34 : appendStringInfoString(buf, "SESSION_USER");
10052 34 : break;
10053 12 : case SVFOP_CURRENT_CATALOG:
10054 12 : appendStringInfoString(buf, "CURRENT_CATALOG");
10055 12 : break;
10056 12 : case SVFOP_CURRENT_SCHEMA:
10057 12 : appendStringInfoString(buf, "CURRENT_SCHEMA");
10058 12 : break;
10059 : }
10060 : }
10061 756 : break;
10062 :
10063 242 : case T_XmlExpr:
10064 : {
10065 242 : XmlExpr *xexpr = (XmlExpr *) node;
10066 242 : bool needcomma = false;
10067 : ListCell *arg;
10068 : ListCell *narg;
10069 : Const *con;
10070 :
10071 242 : switch (xexpr->op)
10072 : {
10073 22 : case IS_XMLCONCAT:
10074 22 : appendStringInfoString(buf, "XMLCONCAT(");
10075 22 : break;
10076 44 : case IS_XMLELEMENT:
10077 44 : appendStringInfoString(buf, "XMLELEMENT(");
10078 44 : break;
10079 22 : case IS_XMLFOREST:
10080 22 : appendStringInfoString(buf, "XMLFOREST(");
10081 22 : break;
10082 22 : case IS_XMLPARSE:
10083 22 : appendStringInfoString(buf, "XMLPARSE(");
10084 22 : break;
10085 22 : case IS_XMLPI:
10086 22 : appendStringInfoString(buf, "XMLPI(");
10087 22 : break;
10088 22 : case IS_XMLROOT:
10089 22 : appendStringInfoString(buf, "XMLROOT(");
10090 22 : break;
10091 88 : case IS_XMLSERIALIZE:
10092 88 : appendStringInfoString(buf, "XMLSERIALIZE(");
10093 88 : break;
10094 0 : case IS_DOCUMENT:
10095 0 : break;
10096 : }
10097 242 : if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
10098 : {
10099 110 : if (xexpr->xmloption == XMLOPTION_DOCUMENT)
10100 44 : appendStringInfoString(buf, "DOCUMENT ");
10101 : else
10102 66 : appendStringInfoString(buf, "CONTENT ");
10103 : }
10104 242 : if (xexpr->name)
10105 : {
10106 66 : appendStringInfo(buf, "NAME %s",
10107 66 : quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));
10108 66 : needcomma = true;
10109 : }
10110 242 : if (xexpr->named_args)
10111 : {
10112 44 : if (xexpr->op != IS_XMLFOREST)
10113 : {
10114 22 : if (needcomma)
10115 22 : appendStringInfoString(buf, ", ");
10116 22 : appendStringInfoString(buf, "XMLATTRIBUTES(");
10117 22 : needcomma = false;
10118 : }
10119 154 : forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
10120 : {
10121 110 : Node *e = (Node *) lfirst(arg);
10122 110 : char *argname = strVal(lfirst(narg));
10123 :
10124 110 : if (needcomma)
10125 66 : appendStringInfoString(buf, ", ");
10126 110 : get_rule_expr((Node *) e, context, true);
10127 110 : appendStringInfo(buf, " AS %s",
10128 110 : quote_identifier(map_xml_name_to_sql_identifier(argname)));
10129 110 : needcomma = true;
10130 : }
10131 44 : if (xexpr->op != IS_XMLFOREST)
10132 22 : appendStringInfoChar(buf, ')');
10133 : }
10134 242 : if (xexpr->args)
10135 : {
10136 220 : if (needcomma)
10137 66 : appendStringInfoString(buf, ", ");
10138 220 : switch (xexpr->op)
10139 : {
10140 176 : case IS_XMLCONCAT:
10141 : case IS_XMLELEMENT:
10142 : case IS_XMLFOREST:
10143 : case IS_XMLPI:
10144 : case IS_XMLSERIALIZE:
10145 : /* no extra decoration needed */
10146 176 : get_rule_expr((Node *) xexpr->args, context, true);
10147 176 : break;
10148 22 : case IS_XMLPARSE:
10149 : Assert(list_length(xexpr->args) == 2);
10150 :
10151 22 : get_rule_expr((Node *) linitial(xexpr->args),
10152 : context, true);
10153 :
10154 22 : con = lsecond_node(Const, xexpr->args);
10155 : Assert(!con->constisnull);
10156 22 : if (DatumGetBool(con->constvalue))
10157 0 : appendStringInfoString(buf,
10158 : " PRESERVE WHITESPACE");
10159 : else
10160 22 : appendStringInfoString(buf,
10161 : " STRIP WHITESPACE");
10162 22 : break;
10163 22 : case IS_XMLROOT:
10164 : Assert(list_length(xexpr->args) == 3);
10165 :
10166 22 : get_rule_expr((Node *) linitial(xexpr->args),
10167 : context, true);
10168 :
10169 22 : appendStringInfoString(buf, ", VERSION ");
10170 22 : con = (Const *) lsecond(xexpr->args);
10171 22 : if (IsA(con, Const) &&
10172 22 : con->constisnull)
10173 22 : appendStringInfoString(buf, "NO VALUE");
10174 : else
10175 0 : get_rule_expr((Node *) con, context, false);
10176 :
10177 22 : con = lthird_node(Const, xexpr->args);
10178 22 : if (con->constisnull)
10179 : /* suppress STANDALONE NO VALUE */ ;
10180 : else
10181 : {
10182 22 : switch (DatumGetInt32(con->constvalue))
10183 : {
10184 22 : case XML_STANDALONE_YES:
10185 22 : appendStringInfoString(buf,
10186 : ", STANDALONE YES");
10187 22 : break;
10188 0 : case XML_STANDALONE_NO:
10189 0 : appendStringInfoString(buf,
10190 : ", STANDALONE NO");
10191 0 : break;
10192 0 : case XML_STANDALONE_NO_VALUE:
10193 0 : appendStringInfoString(buf,
10194 : ", STANDALONE NO VALUE");
10195 0 : break;
10196 0 : default:
10197 0 : break;
10198 : }
10199 : }
10200 22 : break;
10201 0 : case IS_DOCUMENT:
10202 0 : get_rule_expr_paren((Node *) xexpr->args, context, false, node);
10203 0 : break;
10204 : }
10205 : }
10206 242 : if (xexpr->op == IS_XMLSERIALIZE)
10207 : {
10208 88 : appendStringInfo(buf, " AS %s",
10209 : format_type_with_typemod(xexpr->type,
10210 : xexpr->typmod));
10211 88 : if (xexpr->indent)
10212 22 : appendStringInfoString(buf, " INDENT");
10213 : else
10214 66 : appendStringInfoString(buf, " NO INDENT");
10215 : }
10216 :
10217 242 : if (xexpr->op == IS_DOCUMENT)
10218 0 : appendStringInfoString(buf, " IS DOCUMENT");
10219 : else
10220 242 : appendStringInfoChar(buf, ')');
10221 : }
10222 242 : break;
10223 :
10224 2688 : case T_NullTest:
10225 : {
10226 2688 : NullTest *ntest = (NullTest *) node;
10227 :
10228 2688 : if (!PRETTY_PAREN(context))
10229 2634 : appendStringInfoChar(buf, '(');
10230 2688 : get_rule_expr_paren((Node *) ntest->arg, context, true, node);
10231 :
10232 : /*
10233 : * For scalar inputs, we prefer to print as IS [NOT] NULL,
10234 : * which is shorter and traditional. If it's a rowtype input
10235 : * but we're applying a scalar test, must print IS [NOT]
10236 : * DISTINCT FROM NULL to be semantically correct.
10237 : */
10238 2688 : if (ntest->argisrow ||
10239 2614 : !type_is_rowtype(exprType((Node *) ntest->arg)))
10240 : {
10241 5340 : switch (ntest->nulltesttype)
10242 : {
10243 840 : case IS_NULL:
10244 840 : appendStringInfoString(buf, " IS NULL");
10245 840 : break;
10246 1830 : case IS_NOT_NULL:
10247 1830 : appendStringInfoString(buf, " IS NOT NULL");
10248 1830 : break;
10249 0 : default:
10250 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10251 : (int) ntest->nulltesttype);
10252 : }
10253 : }
10254 : else
10255 : {
10256 18 : switch (ntest->nulltesttype)
10257 : {
10258 6 : case IS_NULL:
10259 6 : appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
10260 6 : break;
10261 12 : case IS_NOT_NULL:
10262 12 : appendStringInfoString(buf, " IS DISTINCT FROM NULL");
10263 12 : break;
10264 0 : default:
10265 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10266 : (int) ntest->nulltesttype);
10267 : }
10268 : }
10269 2688 : if (!PRETTY_PAREN(context))
10270 2634 : appendStringInfoChar(buf, ')');
10271 : }
10272 2688 : break;
10273 :
10274 306 : case T_BooleanTest:
10275 : {
10276 306 : BooleanTest *btest = (BooleanTest *) node;
10277 :
10278 306 : if (!PRETTY_PAREN(context))
10279 306 : appendStringInfoChar(buf, '(');
10280 306 : get_rule_expr_paren((Node *) btest->arg, context, false, node);
10281 306 : switch (btest->booltesttype)
10282 : {
10283 36 : case IS_TRUE:
10284 36 : appendStringInfoString(buf, " IS TRUE");
10285 36 : break;
10286 138 : case IS_NOT_TRUE:
10287 138 : appendStringInfoString(buf, " IS NOT TRUE");
10288 138 : break;
10289 0 : case IS_FALSE:
10290 0 : appendStringInfoString(buf, " IS FALSE");
10291 0 : break;
10292 54 : case IS_NOT_FALSE:
10293 54 : appendStringInfoString(buf, " IS NOT FALSE");
10294 54 : break;
10295 24 : case IS_UNKNOWN:
10296 24 : appendStringInfoString(buf, " IS UNKNOWN");
10297 24 : break;
10298 54 : case IS_NOT_UNKNOWN:
10299 54 : appendStringInfoString(buf, " IS NOT UNKNOWN");
10300 54 : break;
10301 0 : default:
10302 0 : elog(ERROR, "unrecognized booltesttype: %d",
10303 : (int) btest->booltesttype);
10304 : }
10305 306 : if (!PRETTY_PAREN(context))
10306 306 : appendStringInfoChar(buf, ')');
10307 : }
10308 306 : break;
10309 :
10310 92 : case T_CoerceToDomain:
10311 : {
10312 92 : CoerceToDomain *ctest = (CoerceToDomain *) node;
10313 92 : Node *arg = (Node *) ctest->arg;
10314 :
10315 92 : if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
10316 68 : !showimplicit)
10317 : {
10318 : /* don't show the implicit cast */
10319 44 : get_rule_expr(arg, context, false);
10320 : }
10321 : else
10322 : {
10323 48 : get_coercion_expr(arg, context,
10324 : ctest->resulttype,
10325 : ctest->resulttypmod,
10326 : node);
10327 : }
10328 : }
10329 92 : break;
10330 :
10331 534 : case T_CoerceToDomainValue:
10332 534 : appendStringInfoString(buf, "VALUE");
10333 534 : break;
10334 :
10335 100 : case T_SetToDefault:
10336 100 : appendStringInfoString(buf, "DEFAULT");
10337 100 : break;
10338 :
10339 24 : case T_CurrentOfExpr:
10340 : {
10341 24 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
10342 :
10343 24 : if (cexpr->cursor_name)
10344 24 : appendStringInfo(buf, "CURRENT OF %s",
10345 24 : quote_identifier(cexpr->cursor_name));
10346 : else
10347 0 : appendStringInfo(buf, "CURRENT OF $%d",
10348 : cexpr->cursor_param);
10349 : }
10350 24 : break;
10351 :
10352 0 : case T_NextValueExpr:
10353 : {
10354 0 : NextValueExpr *nvexpr = (NextValueExpr *) node;
10355 :
10356 : /*
10357 : * This isn't exactly nextval(), but that seems close enough
10358 : * for EXPLAIN's purposes.
10359 : */
10360 0 : appendStringInfoString(buf, "nextval(");
10361 0 : simple_quote_literal(buf,
10362 0 : generate_relation_name(nvexpr->seqid,
10363 : NIL));
10364 0 : appendStringInfoChar(buf, ')');
10365 : }
10366 0 : break;
10367 :
10368 24 : case T_InferenceElem:
10369 : {
10370 24 : InferenceElem *iexpr = (InferenceElem *) node;
10371 : bool save_varprefix;
10372 : bool need_parens;
10373 :
10374 : /*
10375 : * InferenceElem can only refer to target relation, so a
10376 : * prefix is not useful, and indeed would cause parse errors.
10377 : */
10378 24 : save_varprefix = context->varprefix;
10379 24 : context->varprefix = false;
10380 :
10381 : /*
10382 : * Parenthesize the element unless it's a simple Var or a bare
10383 : * function call. Follows pg_get_indexdef_worker().
10384 : */
10385 24 : need_parens = !IsA(iexpr->expr, Var);
10386 24 : if (IsA(iexpr->expr, FuncExpr) &&
10387 0 : ((FuncExpr *) iexpr->expr)->funcformat ==
10388 : COERCE_EXPLICIT_CALL)
10389 0 : need_parens = false;
10390 :
10391 24 : if (need_parens)
10392 0 : appendStringInfoChar(buf, '(');
10393 24 : get_rule_expr((Node *) iexpr->expr,
10394 : context, false);
10395 24 : if (need_parens)
10396 0 : appendStringInfoChar(buf, ')');
10397 :
10398 24 : context->varprefix = save_varprefix;
10399 :
10400 24 : if (iexpr->infercollid)
10401 12 : appendStringInfo(buf, " COLLATE %s",
10402 : generate_collation_name(iexpr->infercollid));
10403 :
10404 : /* Add the operator class name, if not default */
10405 24 : if (iexpr->inferopclass)
10406 : {
10407 12 : Oid inferopclass = iexpr->inferopclass;
10408 12 : Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
10409 :
10410 12 : get_opclass_name(inferopclass, inferopcinputtype, buf);
10411 : }
10412 : }
10413 24 : break;
10414 :
10415 12 : case T_ReturningExpr:
10416 : {
10417 12 : ReturningExpr *retExpr = (ReturningExpr *) node;
10418 :
10419 : /*
10420 : * We cannot see a ReturningExpr in rule deparsing, only while
10421 : * EXPLAINing a query plan (ReturningExpr nodes are only ever
10422 : * adding during query rewriting). Just display the expression
10423 : * returned (an expanded view column).
10424 : */
10425 12 : get_rule_expr((Node *) retExpr->retexpr, context, showimplicit);
10426 : }
10427 12 : break;
10428 :
10429 5290 : case T_PartitionBoundSpec:
10430 : {
10431 5290 : PartitionBoundSpec *spec = (PartitionBoundSpec *) node;
10432 : ListCell *cell;
10433 : char *sep;
10434 :
10435 5290 : if (spec->is_default)
10436 : {
10437 228 : appendStringInfoString(buf, "DEFAULT");
10438 228 : break;
10439 : }
10440 :
10441 5062 : switch (spec->strategy)
10442 : {
10443 248 : case PARTITION_STRATEGY_HASH:
10444 : Assert(spec->modulus > 0 && spec->remainder >= 0);
10445 : Assert(spec->modulus > spec->remainder);
10446 :
10447 248 : appendStringInfoString(buf, "FOR VALUES");
10448 248 : appendStringInfo(buf, " WITH (modulus %d, remainder %d)",
10449 : spec->modulus, spec->remainder);
10450 248 : break;
10451 :
10452 1792 : case PARTITION_STRATEGY_LIST:
10453 : Assert(spec->listdatums != NIL);
10454 :
10455 1792 : appendStringInfoString(buf, "FOR VALUES IN (");
10456 1792 : sep = "";
10457 4952 : foreach(cell, spec->listdatums)
10458 : {
10459 3160 : Const *val = lfirst_node(Const, cell);
10460 :
10461 3160 : appendStringInfoString(buf, sep);
10462 3160 : get_const_expr(val, context, -1);
10463 3160 : sep = ", ";
10464 : }
10465 :
10466 1792 : appendStringInfoChar(buf, ')');
10467 1792 : break;
10468 :
10469 3022 : case PARTITION_STRATEGY_RANGE:
10470 : Assert(spec->lowerdatums != NIL &&
10471 : spec->upperdatums != NIL &&
10472 : list_length(spec->lowerdatums) ==
10473 : list_length(spec->upperdatums));
10474 :
10475 3022 : appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
10476 : get_range_partbound_string(spec->lowerdatums),
10477 : get_range_partbound_string(spec->upperdatums));
10478 3022 : break;
10479 :
10480 0 : default:
10481 0 : elog(ERROR, "unrecognized partition strategy: %d",
10482 : (int) spec->strategy);
10483 : break;
10484 : }
10485 : }
10486 5062 : break;
10487 :
10488 150 : case T_JsonValueExpr:
10489 : {
10490 150 : JsonValueExpr *jve = (JsonValueExpr *) node;
10491 :
10492 150 : get_rule_expr((Node *) jve->raw_expr, context, false);
10493 150 : get_json_format(jve->format, context->buf);
10494 : }
10495 150 : break;
10496 :
10497 174 : case T_JsonConstructorExpr:
10498 174 : get_json_constructor((JsonConstructorExpr *) node, context, false);
10499 174 : break;
10500 :
10501 60 : case T_JsonIsPredicate:
10502 : {
10503 60 : JsonIsPredicate *pred = (JsonIsPredicate *) node;
10504 :
10505 60 : if (!PRETTY_PAREN(context))
10506 30 : appendStringInfoChar(context->buf, '(');
10507 :
10508 60 : get_rule_expr_paren(pred->expr, context, true, node);
10509 :
10510 60 : appendStringInfoString(context->buf, " IS JSON");
10511 :
10512 : /* TODO: handle FORMAT clause */
10513 :
10514 60 : switch (pred->item_type)
10515 : {
10516 12 : case JS_TYPE_SCALAR:
10517 12 : appendStringInfoString(context->buf, " SCALAR");
10518 12 : break;
10519 12 : case JS_TYPE_ARRAY:
10520 12 : appendStringInfoString(context->buf, " ARRAY");
10521 12 : break;
10522 12 : case JS_TYPE_OBJECT:
10523 12 : appendStringInfoString(context->buf, " OBJECT");
10524 12 : break;
10525 24 : default:
10526 24 : break;
10527 : }
10528 :
10529 60 : if (pred->unique_keys)
10530 12 : appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
10531 :
10532 60 : if (!PRETTY_PAREN(context))
10533 30 : appendStringInfoChar(context->buf, ')');
10534 : }
10535 60 : break;
10536 :
10537 60 : case T_JsonExpr:
10538 : {
10539 60 : JsonExpr *jexpr = (JsonExpr *) node;
10540 :
10541 60 : switch (jexpr->op)
10542 : {
10543 12 : case JSON_EXISTS_OP:
10544 12 : appendStringInfoString(buf, "JSON_EXISTS(");
10545 12 : break;
10546 36 : case JSON_QUERY_OP:
10547 36 : appendStringInfoString(buf, "JSON_QUERY(");
10548 36 : break;
10549 12 : case JSON_VALUE_OP:
10550 12 : appendStringInfoString(buf, "JSON_VALUE(");
10551 12 : break;
10552 0 : default:
10553 0 : elog(ERROR, "unrecognized JsonExpr op: %d",
10554 : (int) jexpr->op);
10555 : }
10556 :
10557 60 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
10558 :
10559 60 : appendStringInfoString(buf, ", ");
10560 :
10561 60 : get_json_path_spec(jexpr->path_spec, context, showimplicit);
10562 :
10563 60 : if (jexpr->passing_values)
10564 : {
10565 : ListCell *lc1,
10566 : *lc2;
10567 12 : bool needcomma = false;
10568 :
10569 12 : appendStringInfoString(buf, " PASSING ");
10570 :
10571 48 : forboth(lc1, jexpr->passing_names,
10572 : lc2, jexpr->passing_values)
10573 : {
10574 36 : if (needcomma)
10575 24 : appendStringInfoString(buf, ", ");
10576 36 : needcomma = true;
10577 :
10578 36 : get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
10579 36 : appendStringInfo(buf, " AS %s",
10580 36 : quote_identifier(lfirst_node(String, lc1)->sval));
10581 : }
10582 : }
10583 :
10584 60 : if (jexpr->op != JSON_EXISTS_OP ||
10585 12 : jexpr->returning->typid != BOOLOID)
10586 48 : get_json_returning(jexpr->returning, context->buf,
10587 48 : jexpr->op == JSON_QUERY_OP);
10588 :
10589 60 : get_json_expr_options(jexpr, context,
10590 60 : jexpr->op != JSON_EXISTS_OP ?
10591 : JSON_BEHAVIOR_NULL :
10592 : JSON_BEHAVIOR_FALSE);
10593 :
10594 60 : appendStringInfoChar(buf, ')');
10595 : }
10596 60 : break;
10597 :
10598 2766 : case T_List:
10599 : {
10600 : char *sep;
10601 : ListCell *l;
10602 :
10603 2766 : sep = "";
10604 7688 : foreach(l, (List *) node)
10605 : {
10606 4922 : appendStringInfoString(buf, sep);
10607 4922 : get_rule_expr((Node *) lfirst(l), context, showimplicit);
10608 4922 : sep = ", ";
10609 : }
10610 : }
10611 2766 : break;
10612 :
10613 72 : case T_TableFunc:
10614 72 : get_tablefunc((TableFunc *) node, context, showimplicit);
10615 72 : break;
10616 :
10617 0 : default:
10618 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
10619 : break;
10620 : }
10621 : }
10622 :
10623 : /*
10624 : * get_rule_expr_toplevel - Parse back a toplevel expression
10625 : *
10626 : * Same as get_rule_expr(), except that if the expr is just a Var, we pass
10627 : * istoplevel = true not false to get_variable(). This causes whole-row Vars
10628 : * to get printed with decoration that will prevent expansion of "*".
10629 : * We need to use this in contexts such as ROW() and VALUES(), where the
10630 : * parser would expand "foo.*" appearing at top level. (In principle we'd
10631 : * use this in get_target_list() too, but that has additional worries about
10632 : * whether to print AS, so it needs to invoke get_variable() directly anyway.)
10633 : */
10634 : static void
10635 3434 : get_rule_expr_toplevel(Node *node, deparse_context *context,
10636 : bool showimplicit)
10637 : {
10638 3434 : if (node && IsA(node, Var))
10639 1430 : (void) get_variable((Var *) node, 0, true, context);
10640 : else
10641 2004 : get_rule_expr(node, context, showimplicit);
10642 3434 : }
10643 :
10644 : /*
10645 : * get_rule_list_toplevel - Parse back a list of toplevel expressions
10646 : *
10647 : * Apply get_rule_expr_toplevel() to each element of a List.
10648 : *
10649 : * This adds commas between the expressions, but caller is responsible
10650 : * for printing surrounding decoration.
10651 : */
10652 : static void
10653 576 : get_rule_list_toplevel(List *lst, deparse_context *context,
10654 : bool showimplicit)
10655 : {
10656 : const char *sep;
10657 : ListCell *lc;
10658 :
10659 576 : sep = "";
10660 2036 : foreach(lc, lst)
10661 : {
10662 1460 : Node *e = (Node *) lfirst(lc);
10663 :
10664 1460 : appendStringInfoString(context->buf, sep);
10665 1460 : get_rule_expr_toplevel(e, context, showimplicit);
10666 1460 : sep = ", ";
10667 : }
10668 576 : }
10669 :
10670 : /*
10671 : * get_rule_expr_funccall - Parse back a function-call expression
10672 : *
10673 : * Same as get_rule_expr(), except that we guarantee that the output will
10674 : * look like a function call, or like one of the things the grammar treats as
10675 : * equivalent to a function call (see the func_expr_windowless production).
10676 : * This is needed in places where the grammar uses func_expr_windowless and
10677 : * you can't substitute a parenthesized a_expr. If what we have isn't going
10678 : * to look like a function call, wrap it in a dummy CAST() expression, which
10679 : * will satisfy the grammar --- and, indeed, is likely what the user wrote to
10680 : * produce such a thing.
10681 : */
10682 : static void
10683 852 : get_rule_expr_funccall(Node *node, deparse_context *context,
10684 : bool showimplicit)
10685 : {
10686 852 : if (looks_like_function(node))
10687 840 : get_rule_expr(node, context, showimplicit);
10688 : else
10689 : {
10690 12 : StringInfo buf = context->buf;
10691 :
10692 12 : appendStringInfoString(buf, "CAST(");
10693 : /* no point in showing any top-level implicit cast */
10694 12 : get_rule_expr(node, context, false);
10695 12 : appendStringInfo(buf, " AS %s)",
10696 : format_type_with_typemod(exprType(node),
10697 : exprTypmod(node)));
10698 : }
10699 852 : }
10700 :
10701 : /*
10702 : * Helper function to identify node types that satisfy func_expr_windowless.
10703 : * If in doubt, "false" is always a safe answer.
10704 : */
10705 : static bool
10706 2208 : looks_like_function(Node *node)
10707 : {
10708 2208 : if (node == NULL)
10709 0 : return false; /* probably shouldn't happen */
10710 2208 : switch (nodeTag(node))
10711 : {
10712 904 : case T_FuncExpr:
10713 : /* OK, unless it's going to deparse as a cast */
10714 922 : return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
10715 18 : ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
10716 108 : case T_NullIfExpr:
10717 : case T_CoalesceExpr:
10718 : case T_MinMaxExpr:
10719 : case T_SQLValueFunction:
10720 : case T_XmlExpr:
10721 : case T_JsonExpr:
10722 : /* these are all accepted by func_expr_common_subexpr */
10723 108 : return true;
10724 1196 : default:
10725 1196 : break;
10726 : }
10727 1196 : return false;
10728 : }
10729 :
10730 :
10731 : /*
10732 : * get_oper_expr - Parse back an OpExpr node
10733 : */
10734 : static void
10735 61612 : get_oper_expr(OpExpr *expr, deparse_context *context)
10736 : {
10737 61612 : StringInfo buf = context->buf;
10738 61612 : Oid opno = expr->opno;
10739 61612 : List *args = expr->args;
10740 :
10741 61612 : if (!PRETTY_PAREN(context))
10742 59342 : appendStringInfoChar(buf, '(');
10743 61612 : if (list_length(args) == 2)
10744 : {
10745 : /* binary operator */
10746 61582 : Node *arg1 = (Node *) linitial(args);
10747 61582 : Node *arg2 = (Node *) lsecond(args);
10748 :
10749 61582 : get_rule_expr_paren(arg1, context, true, (Node *) expr);
10750 61582 : appendStringInfo(buf, " %s ",
10751 : generate_operator_name(opno,
10752 : exprType(arg1),
10753 : exprType(arg2)));
10754 61582 : get_rule_expr_paren(arg2, context, true, (Node *) expr);
10755 : }
10756 : else
10757 : {
10758 : /* prefix operator */
10759 30 : Node *arg = (Node *) linitial(args);
10760 :
10761 30 : appendStringInfo(buf, "%s ",
10762 : generate_operator_name(opno,
10763 : InvalidOid,
10764 : exprType(arg)));
10765 30 : get_rule_expr_paren(arg, context, true, (Node *) expr);
10766 : }
10767 61612 : if (!PRETTY_PAREN(context))
10768 59342 : appendStringInfoChar(buf, ')');
10769 61612 : }
10770 :
10771 : /*
10772 : * get_func_expr - Parse back a FuncExpr node
10773 : */
10774 : static void
10775 13198 : get_func_expr(FuncExpr *expr, deparse_context *context,
10776 : bool showimplicit)
10777 : {
10778 13198 : StringInfo buf = context->buf;
10779 13198 : Oid funcoid = expr->funcid;
10780 : Oid argtypes[FUNC_MAX_ARGS];
10781 : int nargs;
10782 : List *argnames;
10783 : bool use_variadic;
10784 : ListCell *l;
10785 :
10786 : /*
10787 : * If the function call came from an implicit coercion, then just show the
10788 : * first argument --- unless caller wants to see implicit coercions.
10789 : */
10790 13198 : if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
10791 : {
10792 1614 : get_rule_expr_paren((Node *) linitial(expr->args), context,
10793 : false, (Node *) expr);
10794 3674 : return;
10795 : }
10796 :
10797 : /*
10798 : * If the function call came from a cast, then show the first argument
10799 : * plus an explicit cast operation.
10800 : */
10801 11584 : if (expr->funcformat == COERCE_EXPLICIT_CAST ||
10802 10822 : expr->funcformat == COERCE_IMPLICIT_CAST)
10803 : {
10804 1886 : Node *arg = linitial(expr->args);
10805 1886 : Oid rettype = expr->funcresulttype;
10806 : int32 coercedTypmod;
10807 :
10808 : /* Get the typmod if this is a length-coercion function */
10809 1886 : (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
10810 :
10811 1886 : get_coercion_expr(arg, context,
10812 : rettype, coercedTypmod,
10813 : (Node *) expr);
10814 :
10815 1886 : return;
10816 : }
10817 :
10818 : /*
10819 : * If the function was called using one of the SQL spec's random special
10820 : * syntaxes, try to reproduce that. If we don't recognize the function,
10821 : * fall through.
10822 : */
10823 9698 : if (expr->funcformat == COERCE_SQL_SYNTAX)
10824 : {
10825 180 : if (get_func_sql_syntax(expr, context))
10826 174 : return;
10827 : }
10828 :
10829 : /*
10830 : * Normal function: display as proname(args). First we need to extract
10831 : * the argument datatypes.
10832 : */
10833 9524 : if (list_length(expr->args) > FUNC_MAX_ARGS)
10834 0 : ereport(ERROR,
10835 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
10836 : errmsg("too many arguments")));
10837 9524 : nargs = 0;
10838 9524 : argnames = NIL;
10839 19726 : foreach(l, expr->args)
10840 : {
10841 10202 : Node *arg = (Node *) lfirst(l);
10842 :
10843 10202 : if (IsA(arg, NamedArgExpr))
10844 30 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
10845 10202 : argtypes[nargs] = exprType(arg);
10846 10202 : nargs++;
10847 : }
10848 :
10849 9524 : appendStringInfo(buf, "%s(",
10850 : generate_function_name(funcoid, nargs,
10851 : argnames, argtypes,
10852 9524 : expr->funcvariadic,
10853 : &use_variadic,
10854 9524 : context->inGroupBy));
10855 9524 : nargs = 0;
10856 19726 : foreach(l, expr->args)
10857 : {
10858 10202 : if (nargs++ > 0)
10859 1870 : appendStringInfoString(buf, ", ");
10860 10202 : if (use_variadic && lnext(expr->args, l) == NULL)
10861 12 : appendStringInfoString(buf, "VARIADIC ");
10862 10202 : get_rule_expr((Node *) lfirst(l), context, true);
10863 : }
10864 9524 : appendStringInfoChar(buf, ')');
10865 : }
10866 :
10867 : /*
10868 : * get_agg_expr - Parse back an Aggref node
10869 : */
10870 : static void
10871 2030 : get_agg_expr(Aggref *aggref, deparse_context *context,
10872 : Aggref *original_aggref)
10873 : {
10874 2030 : get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
10875 : false);
10876 2030 : }
10877 :
10878 : /*
10879 : * get_agg_expr_helper - subroutine for get_agg_expr and
10880 : * get_json_agg_constructor
10881 : */
10882 : static void
10883 2084 : get_agg_expr_helper(Aggref *aggref, deparse_context *context,
10884 : Aggref *original_aggref, const char *funcname,
10885 : const char *options, bool is_json_objectagg)
10886 : {
10887 2084 : StringInfo buf = context->buf;
10888 : Oid argtypes[FUNC_MAX_ARGS];
10889 : int nargs;
10890 2084 : bool use_variadic = false;
10891 :
10892 : /*
10893 : * For a combining aggregate, we look up and deparse the corresponding
10894 : * partial aggregate instead. This is necessary because our input
10895 : * argument list has been replaced; the new argument list always has just
10896 : * one element, which will point to a partial Aggref that supplies us with
10897 : * transition states to combine.
10898 : */
10899 2084 : if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
10900 : {
10901 : TargetEntry *tle;
10902 :
10903 : Assert(list_length(aggref->args) == 1);
10904 260 : tle = linitial_node(TargetEntry, aggref->args);
10905 260 : resolve_special_varno((Node *) tle->expr, context,
10906 : get_agg_combine_expr, original_aggref);
10907 260 : return;
10908 : }
10909 :
10910 : /*
10911 : * Mark as PARTIAL, if appropriate. We look to the original aggref so as
10912 : * to avoid printing this when recursing from the code just above.
10913 : */
10914 1824 : if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))
10915 60 : appendStringInfoString(buf, "PARTIAL ");
10916 :
10917 : /* Extract the argument types as seen by the parser */
10918 1824 : nargs = get_aggregate_argtypes(aggref, argtypes);
10919 :
10920 1824 : if (!funcname)
10921 1770 : funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
10922 1770 : argtypes, aggref->aggvariadic,
10923 : &use_variadic,
10924 1770 : context->inGroupBy);
10925 :
10926 : /* Print the aggregate name, schema-qualified if needed */
10927 1824 : appendStringInfo(buf, "%s(%s", funcname,
10928 1824 : (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
10929 :
10930 1824 : if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
10931 : {
10932 : /*
10933 : * Ordered-set aggregates do not use "*" syntax. Also, we needn't
10934 : * worry about inserting VARIADIC. So we can just dump the direct
10935 : * args as-is.
10936 : */
10937 : Assert(!aggref->aggvariadic);
10938 28 : get_rule_expr((Node *) aggref->aggdirectargs, context, true);
10939 : Assert(aggref->aggorder != NIL);
10940 28 : appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
10941 28 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
10942 : }
10943 : else
10944 : {
10945 : /* aggstar can be set only in zero-argument aggregates */
10946 1796 : if (aggref->aggstar)
10947 280 : appendStringInfoChar(buf, '*');
10948 : else
10949 : {
10950 : ListCell *l;
10951 : int i;
10952 :
10953 1516 : i = 0;
10954 3220 : foreach(l, aggref->args)
10955 : {
10956 1704 : TargetEntry *tle = (TargetEntry *) lfirst(l);
10957 1704 : Node *arg = (Node *) tle->expr;
10958 :
10959 : Assert(!IsA(arg, NamedArgExpr));
10960 1704 : if (tle->resjunk)
10961 50 : continue;
10962 1654 : if (i++ > 0)
10963 : {
10964 138 : if (is_json_objectagg)
10965 : {
10966 : /*
10967 : * the ABSENT ON NULL and WITH UNIQUE args are printed
10968 : * separately, so ignore them here
10969 : */
10970 30 : if (i > 2)
10971 0 : break;
10972 :
10973 30 : appendStringInfoString(buf, " : ");
10974 : }
10975 : else
10976 108 : appendStringInfoString(buf, ", ");
10977 : }
10978 1654 : if (use_variadic && i == nargs)
10979 8 : appendStringInfoString(buf, "VARIADIC ");
10980 1654 : get_rule_expr(arg, context, true);
10981 : }
10982 : }
10983 :
10984 1796 : if (aggref->aggorder != NIL)
10985 : {
10986 82 : appendStringInfoString(buf, " ORDER BY ");
10987 82 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
10988 : }
10989 : }
10990 :
10991 1824 : if (options)
10992 54 : appendStringInfoString(buf, options);
10993 :
10994 1824 : if (aggref->aggfilter != NULL)
10995 : {
10996 46 : appendStringInfoString(buf, ") FILTER (WHERE ");
10997 46 : get_rule_expr((Node *) aggref->aggfilter, context, false);
10998 : }
10999 :
11000 1824 : appendStringInfoChar(buf, ')');
11001 : }
11002 :
11003 : /*
11004 : * This is a helper function for get_agg_expr(). It's used when we deparse
11005 : * a combining Aggref; resolve_special_varno locates the corresponding partial
11006 : * Aggref and then calls this.
11007 : */
11008 : static void
11009 260 : get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
11010 : {
11011 : Aggref *aggref;
11012 260 : Aggref *original_aggref = callback_arg;
11013 :
11014 260 : if (!IsA(node, Aggref))
11015 0 : elog(ERROR, "combining Aggref does not point to an Aggref");
11016 :
11017 260 : aggref = (Aggref *) node;
11018 260 : get_agg_expr(aggref, context, original_aggref);
11019 260 : }
11020 :
11021 : /*
11022 : * get_windowfunc_expr - Parse back a WindowFunc node
11023 : */
11024 : static void
11025 306 : get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
11026 : {
11027 306 : get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
11028 306 : }
11029 :
11030 :
11031 : /*
11032 : * get_windowfunc_expr_helper - subroutine for get_windowfunc_expr and
11033 : * get_json_agg_constructor
11034 : */
11035 : static void
11036 318 : get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
11037 : const char *funcname, const char *options,
11038 : bool is_json_objectagg)
11039 : {
11040 318 : StringInfo buf = context->buf;
11041 : Oid argtypes[FUNC_MAX_ARGS];
11042 : int nargs;
11043 : List *argnames;
11044 : ListCell *l;
11045 :
11046 318 : if (list_length(wfunc->args) > FUNC_MAX_ARGS)
11047 0 : ereport(ERROR,
11048 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
11049 : errmsg("too many arguments")));
11050 318 : nargs = 0;
11051 318 : argnames = NIL;
11052 534 : foreach(l, wfunc->args)
11053 : {
11054 216 : Node *arg = (Node *) lfirst(l);
11055 :
11056 216 : if (IsA(arg, NamedArgExpr))
11057 0 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
11058 216 : argtypes[nargs] = exprType(arg);
11059 216 : nargs++;
11060 : }
11061 :
11062 318 : if (!funcname)
11063 306 : funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
11064 : argtypes, false, NULL,
11065 306 : context->inGroupBy);
11066 :
11067 318 : appendStringInfo(buf, "%s(", funcname);
11068 :
11069 : /* winstar can be set only in zero-argument aggregates */
11070 318 : if (wfunc->winstar)
11071 24 : appendStringInfoChar(buf, '*');
11072 : else
11073 : {
11074 294 : if (is_json_objectagg)
11075 : {
11076 6 : get_rule_expr((Node *) linitial(wfunc->args), context, false);
11077 6 : appendStringInfoString(buf, " : ");
11078 6 : get_rule_expr((Node *) lsecond(wfunc->args), context, false);
11079 : }
11080 : else
11081 288 : get_rule_expr((Node *) wfunc->args, context, true);
11082 : }
11083 :
11084 318 : if (options)
11085 12 : appendStringInfoString(buf, options);
11086 :
11087 318 : if (wfunc->aggfilter != NULL)
11088 : {
11089 0 : appendStringInfoString(buf, ") FILTER (WHERE ");
11090 0 : get_rule_expr((Node *) wfunc->aggfilter, context, false);
11091 : }
11092 :
11093 318 : appendStringInfoString(buf, ") OVER ");
11094 :
11095 318 : if (context->windowClause)
11096 : {
11097 : /* Query-decompilation case: search the windowClause list */
11098 42 : foreach(l, context->windowClause)
11099 : {
11100 42 : WindowClause *wc = (WindowClause *) lfirst(l);
11101 :
11102 42 : if (wc->winref == wfunc->winref)
11103 : {
11104 42 : if (wc->name)
11105 0 : appendStringInfoString(buf, quote_identifier(wc->name));
11106 : else
11107 42 : get_rule_windowspec(wc, context->targetList, context);
11108 42 : break;
11109 : }
11110 : }
11111 42 : if (l == NULL)
11112 0 : elog(ERROR, "could not find window clause for winref %u",
11113 : wfunc->winref);
11114 : }
11115 : else
11116 : {
11117 : /*
11118 : * In EXPLAIN, search the namespace stack for a matching WindowAgg
11119 : * node (probably it's always the first entry), and print winname.
11120 : */
11121 276 : foreach(l, context->namespaces)
11122 : {
11123 276 : deparse_namespace *dpns = (deparse_namespace *) lfirst(l);
11124 :
11125 276 : if (dpns->plan && IsA(dpns->plan, WindowAgg))
11126 : {
11127 276 : WindowAgg *wagg = (WindowAgg *) dpns->plan;
11128 :
11129 276 : if (wagg->winref == wfunc->winref)
11130 : {
11131 276 : appendStringInfoString(buf, quote_identifier(wagg->winname));
11132 276 : break;
11133 : }
11134 : }
11135 : }
11136 276 : if (l == NULL)
11137 0 : elog(ERROR, "could not find window clause for winref %u",
11138 : wfunc->winref);
11139 : }
11140 318 : }
11141 :
11142 : /*
11143 : * get_func_sql_syntax - Parse back a SQL-syntax function call
11144 : *
11145 : * Returns true if we successfully deparsed, false if we did not
11146 : * recognize the function.
11147 : */
11148 : static bool
11149 180 : get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
11150 : {
11151 180 : StringInfo buf = context->buf;
11152 180 : Oid funcoid = expr->funcid;
11153 :
11154 180 : switch (funcoid)
11155 : {
11156 24 : case F_TIMEZONE_INTERVAL_TIMESTAMP:
11157 : case F_TIMEZONE_INTERVAL_TIMESTAMPTZ:
11158 : case F_TIMEZONE_INTERVAL_TIMETZ:
11159 : case F_TIMEZONE_TEXT_TIMESTAMP:
11160 : case F_TIMEZONE_TEXT_TIMESTAMPTZ:
11161 : case F_TIMEZONE_TEXT_TIMETZ:
11162 : /* AT TIME ZONE ... note reversed argument order */
11163 24 : appendStringInfoChar(buf, '(');
11164 24 : get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
11165 : (Node *) expr);
11166 24 : appendStringInfoString(buf, " AT TIME ZONE ");
11167 24 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11168 : (Node *) expr);
11169 24 : appendStringInfoChar(buf, ')');
11170 24 : return true;
11171 :
11172 18 : case F_TIMEZONE_TIMESTAMP:
11173 : case F_TIMEZONE_TIMESTAMPTZ:
11174 : case F_TIMEZONE_TIMETZ:
11175 : /* AT LOCAL */
11176 18 : appendStringInfoChar(buf, '(');
11177 18 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11178 : (Node *) expr);
11179 18 : appendStringInfoString(buf, " AT LOCAL)");
11180 18 : return true;
11181 :
11182 6 : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL:
11183 : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ:
11184 : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL:
11185 : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ:
11186 : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_INTERVAL:
11187 : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_TIMESTAMP:
11188 : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_INTERVAL:
11189 : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_TIMESTAMP:
11190 : case F_OVERLAPS_TIMETZ_TIMETZ_TIMETZ_TIMETZ:
11191 : case F_OVERLAPS_TIME_INTERVAL_TIME_INTERVAL:
11192 : case F_OVERLAPS_TIME_INTERVAL_TIME_TIME:
11193 : case F_OVERLAPS_TIME_TIME_TIME_INTERVAL:
11194 : case F_OVERLAPS_TIME_TIME_TIME_TIME:
11195 : /* (x1, x2) OVERLAPS (y1, y2) */
11196 6 : appendStringInfoString(buf, "((");
11197 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11198 6 : appendStringInfoString(buf, ", ");
11199 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11200 6 : appendStringInfoString(buf, ") OVERLAPS (");
11201 6 : get_rule_expr((Node *) lthird(expr->args), context, false);
11202 6 : appendStringInfoString(buf, ", ");
11203 6 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11204 6 : appendStringInfoString(buf, "))");
11205 6 : return true;
11206 :
11207 18 : case F_EXTRACT_TEXT_DATE:
11208 : case F_EXTRACT_TEXT_TIME:
11209 : case F_EXTRACT_TEXT_TIMETZ:
11210 : case F_EXTRACT_TEXT_TIMESTAMP:
11211 : case F_EXTRACT_TEXT_TIMESTAMPTZ:
11212 : case F_EXTRACT_TEXT_INTERVAL:
11213 : /* EXTRACT (x FROM y) */
11214 18 : appendStringInfoString(buf, "EXTRACT(");
11215 : {
11216 18 : Const *con = (Const *) linitial(expr->args);
11217 :
11218 : Assert(IsA(con, Const) &&
11219 : con->consttype == TEXTOID &&
11220 : !con->constisnull);
11221 18 : appendStringInfoString(buf, TextDatumGetCString(con->constvalue));
11222 : }
11223 18 : appendStringInfoString(buf, " FROM ");
11224 18 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11225 18 : appendStringInfoChar(buf, ')');
11226 18 : return true;
11227 :
11228 12 : case F_IS_NORMALIZED:
11229 : /* IS xxx NORMALIZED */
11230 12 : appendStringInfoChar(buf, '(');
11231 12 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11232 : (Node *) expr);
11233 12 : appendStringInfoString(buf, " IS");
11234 12 : if (list_length(expr->args) == 2)
11235 : {
11236 6 : Const *con = (Const *) lsecond(expr->args);
11237 :
11238 : Assert(IsA(con, Const) &&
11239 : con->consttype == TEXTOID &&
11240 : !con->constisnull);
11241 6 : appendStringInfo(buf, " %s",
11242 6 : TextDatumGetCString(con->constvalue));
11243 : }
11244 12 : appendStringInfoString(buf, " NORMALIZED)");
11245 12 : return true;
11246 :
11247 6 : case F_PG_COLLATION_FOR:
11248 : /* COLLATION FOR */
11249 6 : appendStringInfoString(buf, "COLLATION FOR (");
11250 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11251 6 : appendStringInfoChar(buf, ')');
11252 6 : return true;
11253 :
11254 12 : case F_NORMALIZE:
11255 : /* NORMALIZE() */
11256 12 : appendStringInfoString(buf, "NORMALIZE(");
11257 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11258 12 : if (list_length(expr->args) == 2)
11259 : {
11260 6 : Const *con = (Const *) lsecond(expr->args);
11261 :
11262 : Assert(IsA(con, Const) &&
11263 : con->consttype == TEXTOID &&
11264 : !con->constisnull);
11265 6 : appendStringInfo(buf, ", %s",
11266 6 : TextDatumGetCString(con->constvalue));
11267 : }
11268 12 : appendStringInfoChar(buf, ')');
11269 12 : return true;
11270 :
11271 12 : case F_OVERLAY_BIT_BIT_INT4:
11272 : case F_OVERLAY_BIT_BIT_INT4_INT4:
11273 : case F_OVERLAY_BYTEA_BYTEA_INT4:
11274 : case F_OVERLAY_BYTEA_BYTEA_INT4_INT4:
11275 : case F_OVERLAY_TEXT_TEXT_INT4:
11276 : case F_OVERLAY_TEXT_TEXT_INT4_INT4:
11277 : /* OVERLAY() */
11278 12 : appendStringInfoString(buf, "OVERLAY(");
11279 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11280 12 : appendStringInfoString(buf, " PLACING ");
11281 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11282 12 : appendStringInfoString(buf, " FROM ");
11283 12 : get_rule_expr((Node *) lthird(expr->args), context, false);
11284 12 : if (list_length(expr->args) == 4)
11285 : {
11286 6 : appendStringInfoString(buf, " FOR ");
11287 6 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11288 : }
11289 12 : appendStringInfoChar(buf, ')');
11290 12 : return true;
11291 :
11292 6 : case F_POSITION_BIT_BIT:
11293 : case F_POSITION_BYTEA_BYTEA:
11294 : case F_POSITION_TEXT_TEXT:
11295 : /* POSITION() ... extra parens since args are b_expr not a_expr */
11296 6 : appendStringInfoString(buf, "POSITION((");
11297 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11298 6 : appendStringInfoString(buf, ") IN (");
11299 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11300 6 : appendStringInfoString(buf, "))");
11301 6 : return true;
11302 :
11303 6 : case F_SUBSTRING_BIT_INT4:
11304 : case F_SUBSTRING_BIT_INT4_INT4:
11305 : case F_SUBSTRING_BYTEA_INT4:
11306 : case F_SUBSTRING_BYTEA_INT4_INT4:
11307 : case F_SUBSTRING_TEXT_INT4:
11308 : case F_SUBSTRING_TEXT_INT4_INT4:
11309 : /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
11310 6 : appendStringInfoString(buf, "SUBSTRING(");
11311 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11312 6 : appendStringInfoString(buf, " FROM ");
11313 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11314 6 : if (list_length(expr->args) == 3)
11315 : {
11316 6 : appendStringInfoString(buf, " FOR ");
11317 6 : get_rule_expr((Node *) lthird(expr->args), context, false);
11318 : }
11319 6 : appendStringInfoChar(buf, ')');
11320 6 : return true;
11321 :
11322 6 : case F_SUBSTRING_TEXT_TEXT_TEXT:
11323 : /* SUBSTRING SIMILAR/ESCAPE */
11324 6 : appendStringInfoString(buf, "SUBSTRING(");
11325 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11326 6 : appendStringInfoString(buf, " SIMILAR ");
11327 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11328 6 : appendStringInfoString(buf, " ESCAPE ");
11329 6 : get_rule_expr((Node *) lthird(expr->args), context, false);
11330 6 : appendStringInfoChar(buf, ')');
11331 6 : return true;
11332 :
11333 12 : case F_BTRIM_BYTEA_BYTEA:
11334 : case F_BTRIM_TEXT:
11335 : case F_BTRIM_TEXT_TEXT:
11336 : /* TRIM() */
11337 12 : appendStringInfoString(buf, "TRIM(BOTH");
11338 12 : if (list_length(expr->args) == 2)
11339 : {
11340 12 : appendStringInfoChar(buf, ' ');
11341 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11342 : }
11343 12 : appendStringInfoString(buf, " FROM ");
11344 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11345 12 : appendStringInfoChar(buf, ')');
11346 12 : return true;
11347 :
11348 12 : case F_LTRIM_BYTEA_BYTEA:
11349 : case F_LTRIM_TEXT:
11350 : case F_LTRIM_TEXT_TEXT:
11351 : /* TRIM() */
11352 12 : appendStringInfoString(buf, "TRIM(LEADING");
11353 12 : if (list_length(expr->args) == 2)
11354 : {
11355 12 : appendStringInfoChar(buf, ' ');
11356 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11357 : }
11358 12 : appendStringInfoString(buf, " FROM ");
11359 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11360 12 : appendStringInfoChar(buf, ')');
11361 12 : return true;
11362 :
11363 12 : case F_RTRIM_BYTEA_BYTEA:
11364 : case F_RTRIM_TEXT:
11365 : case F_RTRIM_TEXT_TEXT:
11366 : /* TRIM() */
11367 12 : appendStringInfoString(buf, "TRIM(TRAILING");
11368 12 : if (list_length(expr->args) == 2)
11369 : {
11370 6 : appendStringInfoChar(buf, ' ');
11371 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11372 : }
11373 12 : appendStringInfoString(buf, " FROM ");
11374 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11375 12 : appendStringInfoChar(buf, ')');
11376 12 : return true;
11377 :
11378 12 : case F_SYSTEM_USER:
11379 12 : appendStringInfoString(buf, "SYSTEM_USER");
11380 12 : return true;
11381 :
11382 0 : case F_XMLEXISTS:
11383 : /* XMLEXISTS ... extra parens because args are c_expr */
11384 0 : appendStringInfoString(buf, "XMLEXISTS((");
11385 0 : get_rule_expr((Node *) linitial(expr->args), context, false);
11386 0 : appendStringInfoString(buf, ") PASSING (");
11387 0 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11388 0 : appendStringInfoString(buf, "))");
11389 0 : return true;
11390 : }
11391 6 : return false;
11392 : }
11393 :
11394 : /* ----------
11395 : * get_coercion_expr
11396 : *
11397 : * Make a string representation of a value coerced to a specific type
11398 : * ----------
11399 : */
11400 : static void
11401 5240 : get_coercion_expr(Node *arg, deparse_context *context,
11402 : Oid resulttype, int32 resulttypmod,
11403 : Node *parentNode)
11404 : {
11405 5240 : StringInfo buf = context->buf;
11406 :
11407 : /*
11408 : * Since parse_coerce.c doesn't immediately collapse application of
11409 : * length-coercion functions to constants, what we'll typically see in
11410 : * such cases is a Const with typmod -1 and a length-coercion function
11411 : * right above it. Avoid generating redundant output. However, beware of
11412 : * suppressing casts when the user actually wrote something like
11413 : * 'foo'::text::char(3).
11414 : *
11415 : * Note: it might seem that we are missing the possibility of needing to
11416 : * print a COLLATE clause for such a Const. However, a Const could only
11417 : * have nondefault collation in a post-constant-folding tree, in which the
11418 : * length coercion would have been folded too. See also the special
11419 : * handling of CollateExpr in coerce_to_target_type(): any collation
11420 : * marking will be above the coercion node, not below it.
11421 : */
11422 5240 : if (arg && IsA(arg, Const) &&
11423 654 : ((Const *) arg)->consttype == resulttype &&
11424 24 : ((Const *) arg)->consttypmod == -1)
11425 : {
11426 : /* Show the constant without normal ::typename decoration */
11427 24 : get_const_expr((Const *) arg, context, -1);
11428 : }
11429 : else
11430 : {
11431 5216 : if (!PRETTY_PAREN(context))
11432 4828 : appendStringInfoChar(buf, '(');
11433 5216 : get_rule_expr_paren(arg, context, false, parentNode);
11434 5216 : if (!PRETTY_PAREN(context))
11435 4828 : appendStringInfoChar(buf, ')');
11436 : }
11437 :
11438 : /*
11439 : * Never emit resulttype(arg) functional notation. A pg_proc entry could
11440 : * take precedence, and a resulttype in pg_temp would require schema
11441 : * qualification that format_type_with_typemod() would usually omit. We've
11442 : * standardized on arg::resulttype, but CAST(arg AS resulttype) notation
11443 : * would work fine.
11444 : */
11445 5240 : appendStringInfo(buf, "::%s",
11446 : format_type_with_typemod(resulttype, resulttypmod));
11447 5240 : }
11448 :
11449 : /* ----------
11450 : * get_const_expr
11451 : *
11452 : * Make a string representation of a Const
11453 : *
11454 : * showtype can be -1 to never show "::typename" decoration, or +1 to always
11455 : * show it, or 0 to show it only if the constant wouldn't be assumed to be
11456 : * the right type by default.
11457 : *
11458 : * If the Const's collation isn't default for its type, show that too.
11459 : * We mustn't do this when showtype is -1 (since that means the caller will
11460 : * print "::typename", and we can't put a COLLATE clause in between). It's
11461 : * caller's responsibility that collation isn't missed in such cases.
11462 : * ----------
11463 : */
11464 : static void
11465 73796 : get_const_expr(Const *constval, deparse_context *context, int showtype)
11466 : {
11467 73796 : StringInfo buf = context->buf;
11468 : Oid typoutput;
11469 : bool typIsVarlena;
11470 : char *extval;
11471 73796 : bool needlabel = false;
11472 :
11473 73796 : if (constval->constisnull)
11474 : {
11475 : /*
11476 : * Always label the type of a NULL constant to prevent misdecisions
11477 : * about type when reparsing.
11478 : */
11479 1140 : appendStringInfoString(buf, "NULL");
11480 1140 : if (showtype >= 0)
11481 : {
11482 1068 : appendStringInfo(buf, "::%s",
11483 : format_type_with_typemod(constval->consttype,
11484 : constval->consttypmod));
11485 1068 : get_const_collation(constval, context);
11486 : }
11487 11586 : return;
11488 : }
11489 :
11490 72656 : getTypeOutputInfo(constval->consttype,
11491 : &typoutput, &typIsVarlena);
11492 :
11493 72656 : extval = OidOutputFunctionCall(typoutput, constval->constvalue);
11494 :
11495 72656 : switch (constval->consttype)
11496 : {
11497 42280 : case INT4OID:
11498 :
11499 : /*
11500 : * INT4 can be printed without any decoration, unless it is
11501 : * negative; in that case print it as '-nnn'::integer to ensure
11502 : * that the output will re-parse as a constant, not as a constant
11503 : * plus operator. In most cases we could get away with printing
11504 : * (-nnn) instead, because of the way that gram.y handles negative
11505 : * literals; but that doesn't work for INT_MIN, and it doesn't
11506 : * seem that much prettier anyway.
11507 : */
11508 42280 : if (extval[0] != '-')
11509 41718 : appendStringInfoString(buf, extval);
11510 : else
11511 : {
11512 562 : appendStringInfo(buf, "'%s'", extval);
11513 562 : needlabel = true; /* we must attach a cast */
11514 : }
11515 42280 : break;
11516 :
11517 1114 : case NUMERICOID:
11518 :
11519 : /*
11520 : * NUMERIC can be printed without quotes if it looks like a float
11521 : * constant (not an integer, and not Infinity or NaN) and doesn't
11522 : * have a leading sign (for the same reason as for INT4).
11523 : */
11524 1114 : if (isdigit((unsigned char) extval[0]) &&
11525 1114 : strcspn(extval, "eE.") != strlen(extval))
11526 : {
11527 410 : appendStringInfoString(buf, extval);
11528 : }
11529 : else
11530 : {
11531 704 : appendStringInfo(buf, "'%s'", extval);
11532 704 : needlabel = true; /* we must attach a cast */
11533 : }
11534 1114 : break;
11535 :
11536 1574 : case BOOLOID:
11537 1574 : if (strcmp(extval, "t") == 0)
11538 766 : appendStringInfoString(buf, "true");
11539 : else
11540 808 : appendStringInfoString(buf, "false");
11541 1574 : break;
11542 :
11543 27688 : default:
11544 27688 : simple_quote_literal(buf, extval);
11545 27688 : break;
11546 : }
11547 :
11548 72656 : pfree(extval);
11549 :
11550 72656 : if (showtype < 0)
11551 10446 : return;
11552 :
11553 : /*
11554 : * For showtype == 0, append ::typename unless the constant will be
11555 : * implicitly typed as the right type when it is read in.
11556 : *
11557 : * XXX this code has to be kept in sync with the behavior of the parser,
11558 : * especially make_const.
11559 : */
11560 62210 : switch (constval->consttype)
11561 : {
11562 1654 : case BOOLOID:
11563 : case UNKNOWNOID:
11564 : /* These types can be left unlabeled */
11565 1654 : needlabel = false;
11566 1654 : break;
11567 36180 : case INT4OID:
11568 : /* We determined above whether a label is needed */
11569 36180 : break;
11570 1114 : case NUMERICOID:
11571 :
11572 : /*
11573 : * Float-looking constants will be typed as numeric, which we
11574 : * checked above; but if there's a nondefault typmod we need to
11575 : * show it.
11576 : */
11577 1114 : needlabel |= (constval->consttypmod >= 0);
11578 1114 : break;
11579 23262 : default:
11580 23262 : needlabel = true;
11581 23262 : break;
11582 : }
11583 62210 : if (needlabel || showtype > 0)
11584 24502 : appendStringInfo(buf, "::%s",
11585 : format_type_with_typemod(constval->consttype,
11586 : constval->consttypmod));
11587 :
11588 62210 : get_const_collation(constval, context);
11589 : }
11590 :
11591 : /*
11592 : * helper for get_const_expr: append COLLATE if needed
11593 : */
11594 : static void
11595 63278 : get_const_collation(Const *constval, deparse_context *context)
11596 : {
11597 63278 : StringInfo buf = context->buf;
11598 :
11599 63278 : if (OidIsValid(constval->constcollid))
11600 : {
11601 9086 : Oid typcollation = get_typcollation(constval->consttype);
11602 :
11603 9086 : if (constval->constcollid != typcollation)
11604 : {
11605 70 : appendStringInfo(buf, " COLLATE %s",
11606 : generate_collation_name(constval->constcollid));
11607 : }
11608 : }
11609 63278 : }
11610 :
11611 : /*
11612 : * get_json_path_spec - Parse back a JSON path specification
11613 : */
11614 : static void
11615 456 : get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
11616 : {
11617 456 : if (IsA(path_spec, Const))
11618 456 : get_const_expr((Const *) path_spec, context, -1);
11619 : else
11620 0 : get_rule_expr(path_spec, context, showimplicit);
11621 456 : }
11622 :
11623 : /*
11624 : * get_json_format - Parse back a JsonFormat node
11625 : */
11626 : static void
11627 186 : get_json_format(JsonFormat *format, StringInfo buf)
11628 : {
11629 186 : if (format->format_type == JS_FORMAT_DEFAULT)
11630 108 : return;
11631 :
11632 78 : appendStringInfoString(buf,
11633 78 : format->format_type == JS_FORMAT_JSONB ?
11634 : " FORMAT JSONB" : " FORMAT JSON");
11635 :
11636 78 : if (format->encoding != JS_ENC_DEFAULT)
11637 : {
11638 : const char *encoding;
11639 :
11640 6 : encoding =
11641 12 : format->encoding == JS_ENC_UTF16 ? "UTF16" :
11642 6 : format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
11643 :
11644 6 : appendStringInfo(buf, " ENCODING %s", encoding);
11645 : }
11646 : }
11647 :
11648 : /*
11649 : * get_json_returning - Parse back a JsonReturning structure
11650 : */
11651 : static void
11652 168 : get_json_returning(JsonReturning *returning, StringInfo buf,
11653 : bool json_format_by_default)
11654 : {
11655 168 : if (!OidIsValid(returning->typid))
11656 0 : return;
11657 :
11658 168 : appendStringInfo(buf, " RETURNING %s",
11659 : format_type_with_typemod(returning->typid,
11660 : returning->typmod));
11661 :
11662 324 : if (!json_format_by_default ||
11663 156 : returning->format->format_type !=
11664 156 : (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
11665 36 : get_json_format(returning->format, buf);
11666 : }
11667 :
11668 : /*
11669 : * get_json_constructor - Parse back a JsonConstructorExpr node
11670 : */
11671 : static void
11672 174 : get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
11673 : bool showimplicit)
11674 : {
11675 174 : StringInfo buf = context->buf;
11676 : const char *funcname;
11677 : bool is_json_object;
11678 : int curridx;
11679 : ListCell *lc;
11680 :
11681 174 : if (ctor->type == JSCTOR_JSON_OBJECTAGG)
11682 : {
11683 36 : get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
11684 36 : return;
11685 : }
11686 138 : else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
11687 : {
11688 30 : get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
11689 30 : return;
11690 : }
11691 :
11692 108 : switch (ctor->type)
11693 : {
11694 30 : case JSCTOR_JSON_OBJECT:
11695 30 : funcname = "JSON_OBJECT";
11696 30 : break;
11697 12 : case JSCTOR_JSON_ARRAY:
11698 12 : funcname = "JSON_ARRAY";
11699 12 : break;
11700 42 : case JSCTOR_JSON_PARSE:
11701 42 : funcname = "JSON";
11702 42 : break;
11703 12 : case JSCTOR_JSON_SCALAR:
11704 12 : funcname = "JSON_SCALAR";
11705 12 : break;
11706 12 : case JSCTOR_JSON_SERIALIZE:
11707 12 : funcname = "JSON_SERIALIZE";
11708 12 : break;
11709 0 : default:
11710 0 : elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
11711 : }
11712 :
11713 108 : appendStringInfo(buf, "%s(", funcname);
11714 :
11715 108 : is_json_object = ctor->type == JSCTOR_JSON_OBJECT;
11716 282 : foreach(lc, ctor->args)
11717 : {
11718 174 : curridx = foreach_current_index(lc);
11719 174 : if (curridx > 0)
11720 : {
11721 : const char *sep;
11722 :
11723 66 : sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
11724 66 : appendStringInfoString(buf, sep);
11725 : }
11726 :
11727 174 : get_rule_expr((Node *) lfirst(lc), context, true);
11728 : }
11729 :
11730 108 : get_json_constructor_options(ctor, buf);
11731 108 : appendStringInfoChar(buf, ')');
11732 : }
11733 :
11734 : /*
11735 : * Append options, if any, to the JSON constructor being deparsed
11736 : */
11737 : static void
11738 174 : get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
11739 : {
11740 174 : if (ctor->absent_on_null)
11741 : {
11742 24 : if (ctor->type == JSCTOR_JSON_OBJECT ||
11743 24 : ctor->type == JSCTOR_JSON_OBJECTAGG)
11744 0 : appendStringInfoString(buf, " ABSENT ON NULL");
11745 : }
11746 : else
11747 : {
11748 150 : if (ctor->type == JSCTOR_JSON_ARRAY ||
11749 150 : ctor->type == JSCTOR_JSON_ARRAYAGG)
11750 18 : appendStringInfoString(buf, " NULL ON NULL");
11751 : }
11752 :
11753 174 : if (ctor->unique)
11754 24 : appendStringInfoString(buf, " WITH UNIQUE KEYS");
11755 :
11756 : /*
11757 : * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
11758 : * support one.
11759 : */
11760 174 : if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
11761 120 : get_json_returning(ctor->returning, buf, true);
11762 174 : }
11763 :
11764 : /*
11765 : * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
11766 : */
11767 : static void
11768 66 : get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
11769 : const char *funcname, bool is_json_objectagg)
11770 : {
11771 : StringInfoData options;
11772 :
11773 66 : initStringInfo(&options);
11774 66 : get_json_constructor_options(ctor, &options);
11775 :
11776 66 : if (IsA(ctor->func, Aggref))
11777 54 : get_agg_expr_helper((Aggref *) ctor->func, context,
11778 54 : (Aggref *) ctor->func,
11779 54 : funcname, options.data, is_json_objectagg);
11780 12 : else if (IsA(ctor->func, WindowFunc))
11781 12 : get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
11782 12 : funcname, options.data,
11783 : is_json_objectagg);
11784 : else
11785 0 : elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
11786 : nodeTag(ctor->func));
11787 66 : }
11788 :
11789 : /*
11790 : * simple_quote_literal - Format a string as a SQL literal, append to buf
11791 : */
11792 : static void
11793 28794 : simple_quote_literal(StringInfo buf, const char *val)
11794 : {
11795 : const char *valptr;
11796 :
11797 : /*
11798 : * We form the string literal according to the prevailing setting of
11799 : * standard_conforming_strings; we never use E''. User is responsible for
11800 : * making sure result is used correctly.
11801 : */
11802 28794 : appendStringInfoChar(buf, '\'');
11803 292058 : for (valptr = val; *valptr; valptr++)
11804 : {
11805 263264 : char ch = *valptr;
11806 :
11807 263264 : if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
11808 306 : appendStringInfoChar(buf, ch);
11809 263264 : appendStringInfoChar(buf, ch);
11810 : }
11811 28794 : appendStringInfoChar(buf, '\'');
11812 28794 : }
11813 :
11814 :
11815 : /* ----------
11816 : * get_sublink_expr - Parse back a sublink
11817 : * ----------
11818 : */
11819 : static void
11820 484 : get_sublink_expr(SubLink *sublink, deparse_context *context)
11821 : {
11822 484 : StringInfo buf = context->buf;
11823 484 : Query *query = (Query *) (sublink->subselect);
11824 484 : char *opname = NULL;
11825 : bool need_paren;
11826 :
11827 484 : if (sublink->subLinkType == ARRAY_SUBLINK)
11828 24 : appendStringInfoString(buf, "ARRAY(");
11829 : else
11830 460 : appendStringInfoChar(buf, '(');
11831 :
11832 : /*
11833 : * Note that we print the name of only the first operator, when there are
11834 : * multiple combining operators. This is an approximation that could go
11835 : * wrong in various scenarios (operators in different schemas, renamed
11836 : * operators, etc) but there is not a whole lot we can do about it, since
11837 : * the syntax allows only one operator to be shown.
11838 : */
11839 484 : if (sublink->testexpr)
11840 : {
11841 18 : if (IsA(sublink->testexpr, OpExpr))
11842 : {
11843 : /* single combining operator */
11844 6 : OpExpr *opexpr = (OpExpr *) sublink->testexpr;
11845 :
11846 6 : get_rule_expr(linitial(opexpr->args), context, true);
11847 6 : opname = generate_operator_name(opexpr->opno,
11848 6 : exprType(linitial(opexpr->args)),
11849 6 : exprType(lsecond(opexpr->args)));
11850 : }
11851 12 : else if (IsA(sublink->testexpr, BoolExpr))
11852 : {
11853 : /* multiple combining operators, = or <> cases */
11854 : char *sep;
11855 : ListCell *l;
11856 :
11857 6 : appendStringInfoChar(buf, '(');
11858 6 : sep = "";
11859 18 : foreach(l, ((BoolExpr *) sublink->testexpr)->args)
11860 : {
11861 12 : OpExpr *opexpr = lfirst_node(OpExpr, l);
11862 :
11863 12 : appendStringInfoString(buf, sep);
11864 12 : get_rule_expr(linitial(opexpr->args), context, true);
11865 12 : if (!opname)
11866 6 : opname = generate_operator_name(opexpr->opno,
11867 6 : exprType(linitial(opexpr->args)),
11868 6 : exprType(lsecond(opexpr->args)));
11869 12 : sep = ", ";
11870 : }
11871 6 : appendStringInfoChar(buf, ')');
11872 : }
11873 6 : else if (IsA(sublink->testexpr, RowCompareExpr))
11874 : {
11875 : /* multiple combining operators, < <= > >= cases */
11876 6 : RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
11877 :
11878 6 : appendStringInfoChar(buf, '(');
11879 6 : get_rule_expr((Node *) rcexpr->largs, context, true);
11880 6 : opname = generate_operator_name(linitial_oid(rcexpr->opnos),
11881 6 : exprType(linitial(rcexpr->largs)),
11882 6 : exprType(linitial(rcexpr->rargs)));
11883 6 : appendStringInfoChar(buf, ')');
11884 : }
11885 : else
11886 0 : elog(ERROR, "unrecognized testexpr type: %d",
11887 : (int) nodeTag(sublink->testexpr));
11888 : }
11889 :
11890 484 : need_paren = true;
11891 :
11892 484 : switch (sublink->subLinkType)
11893 : {
11894 188 : case EXISTS_SUBLINK:
11895 188 : appendStringInfoString(buf, "EXISTS ");
11896 188 : break;
11897 :
11898 12 : case ANY_SUBLINK:
11899 12 : if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
11900 6 : appendStringInfoString(buf, " IN ");
11901 : else
11902 6 : appendStringInfo(buf, " %s ANY ", opname);
11903 12 : break;
11904 :
11905 6 : case ALL_SUBLINK:
11906 6 : appendStringInfo(buf, " %s ALL ", opname);
11907 6 : break;
11908 :
11909 0 : case ROWCOMPARE_SUBLINK:
11910 0 : appendStringInfo(buf, " %s ", opname);
11911 0 : break;
11912 :
11913 278 : case EXPR_SUBLINK:
11914 : case MULTIEXPR_SUBLINK:
11915 : case ARRAY_SUBLINK:
11916 278 : need_paren = false;
11917 278 : break;
11918 :
11919 0 : case CTE_SUBLINK: /* shouldn't occur in a SubLink */
11920 : default:
11921 0 : elog(ERROR, "unrecognized sublink type: %d",
11922 : (int) sublink->subLinkType);
11923 : break;
11924 : }
11925 :
11926 484 : if (need_paren)
11927 206 : appendStringInfoChar(buf, '(');
11928 :
11929 484 : get_query_def(query, buf, context->namespaces, NULL, false,
11930 : context->prettyFlags, context->wrapColumn,
11931 : context->indentLevel);
11932 :
11933 484 : if (need_paren)
11934 206 : appendStringInfoString(buf, "))");
11935 : else
11936 278 : appendStringInfoChar(buf, ')');
11937 484 : }
11938 :
11939 :
11940 : /* ----------
11941 : * get_xmltable - Parse back a XMLTABLE function
11942 : * ----------
11943 : */
11944 : static void
11945 74 : get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
11946 : {
11947 74 : StringInfo buf = context->buf;
11948 :
11949 74 : appendStringInfoString(buf, "XMLTABLE(");
11950 :
11951 74 : if (tf->ns_uris != NIL)
11952 : {
11953 : ListCell *lc1,
11954 : *lc2;
11955 22 : bool first = true;
11956 :
11957 22 : appendStringInfoString(buf, "XMLNAMESPACES (");
11958 44 : forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
11959 : {
11960 22 : Node *expr = (Node *) lfirst(lc1);
11961 22 : String *ns_node = lfirst_node(String, lc2);
11962 :
11963 22 : if (!first)
11964 0 : appendStringInfoString(buf, ", ");
11965 : else
11966 22 : first = false;
11967 :
11968 22 : if (ns_node != NULL)
11969 : {
11970 22 : get_rule_expr(expr, context, showimplicit);
11971 22 : appendStringInfo(buf, " AS %s",
11972 22 : quote_identifier(strVal(ns_node)));
11973 : }
11974 : else
11975 : {
11976 0 : appendStringInfoString(buf, "DEFAULT ");
11977 0 : get_rule_expr(expr, context, showimplicit);
11978 : }
11979 : }
11980 22 : appendStringInfoString(buf, "), ");
11981 : }
11982 :
11983 74 : appendStringInfoChar(buf, '(');
11984 74 : get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
11985 74 : appendStringInfoString(buf, ") PASSING (");
11986 74 : get_rule_expr((Node *) tf->docexpr, context, showimplicit);
11987 74 : appendStringInfoChar(buf, ')');
11988 :
11989 74 : if (tf->colexprs != NIL)
11990 : {
11991 : ListCell *l1;
11992 : ListCell *l2;
11993 : ListCell *l3;
11994 : ListCell *l4;
11995 : ListCell *l5;
11996 74 : int colnum = 0;
11997 :
11998 74 : appendStringInfoString(buf, " COLUMNS ");
11999 440 : forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
12000 : l4, tf->colexprs, l5, tf->coldefexprs)
12001 : {
12002 366 : char *colname = strVal(lfirst(l1));
12003 366 : Oid typid = lfirst_oid(l2);
12004 366 : int32 typmod = lfirst_int(l3);
12005 366 : Node *colexpr = (Node *) lfirst(l4);
12006 366 : Node *coldefexpr = (Node *) lfirst(l5);
12007 366 : bool ordinality = (tf->ordinalitycol == colnum);
12008 366 : bool notnull = bms_is_member(colnum, tf->notnulls);
12009 :
12010 366 : if (colnum > 0)
12011 292 : appendStringInfoString(buf, ", ");
12012 366 : colnum++;
12013 :
12014 692 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
12015 : ordinality ? "FOR ORDINALITY" :
12016 326 : format_type_with_typemod(typid, typmod));
12017 366 : if (ordinality)
12018 40 : continue;
12019 :
12020 326 : if (coldefexpr != NULL)
12021 : {
12022 40 : appendStringInfoString(buf, " DEFAULT (");
12023 40 : get_rule_expr((Node *) coldefexpr, context, showimplicit);
12024 40 : appendStringInfoChar(buf, ')');
12025 : }
12026 326 : if (colexpr != NULL)
12027 : {
12028 302 : appendStringInfoString(buf, " PATH (");
12029 302 : get_rule_expr((Node *) colexpr, context, showimplicit);
12030 302 : appendStringInfoChar(buf, ')');
12031 : }
12032 326 : if (notnull)
12033 40 : appendStringInfoString(buf, " NOT NULL");
12034 : }
12035 : }
12036 :
12037 74 : appendStringInfoChar(buf, ')');
12038 74 : }
12039 :
12040 : /*
12041 : * get_json_table_nested_columns - Parse back nested JSON_TABLE columns
12042 : */
12043 : static void
12044 102 : get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
12045 : deparse_context *context, bool showimplicit,
12046 : bool needcomma)
12047 : {
12048 102 : if (IsA(plan, JsonTablePathScan))
12049 : {
12050 72 : JsonTablePathScan *scan = castNode(JsonTablePathScan, plan);
12051 :
12052 72 : if (needcomma)
12053 48 : appendStringInfoChar(context->buf, ',');
12054 :
12055 72 : appendStringInfoChar(context->buf, ' ');
12056 72 : appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
12057 72 : get_const_expr(scan->path->value, context, -1);
12058 72 : appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
12059 72 : get_json_table_columns(tf, scan, context, showimplicit);
12060 : }
12061 30 : else if (IsA(plan, JsonTableSiblingJoin))
12062 : {
12063 30 : JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
12064 :
12065 30 : get_json_table_nested_columns(tf, join->lplan, context, showimplicit,
12066 : needcomma);
12067 30 : get_json_table_nested_columns(tf, join->rplan, context, showimplicit,
12068 : true);
12069 : }
12070 102 : }
12071 :
12072 : /*
12073 : * get_json_table_columns - Parse back JSON_TABLE columns
12074 : */
12075 : static void
12076 180 : get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
12077 : deparse_context *context,
12078 : bool showimplicit)
12079 : {
12080 180 : StringInfo buf = context->buf;
12081 : ListCell *lc_colname;
12082 : ListCell *lc_coltype;
12083 : ListCell *lc_coltypmod;
12084 : ListCell *lc_colvalexpr;
12085 180 : int colnum = 0;
12086 :
12087 180 : appendStringInfoChar(buf, ' ');
12088 180 : appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12089 :
12090 180 : if (PRETTY_INDENT(context))
12091 138 : context->indentLevel += PRETTYINDENT_VAR;
12092 :
12093 858 : forfour(lc_colname, tf->colnames,
12094 : lc_coltype, tf->coltypes,
12095 : lc_coltypmod, tf->coltypmods,
12096 : lc_colvalexpr, tf->colvalexprs)
12097 : {
12098 726 : char *colname = strVal(lfirst(lc_colname));
12099 : JsonExpr *colexpr;
12100 : Oid typid;
12101 : int32 typmod;
12102 : bool ordinality;
12103 : JsonBehaviorType default_behavior;
12104 :
12105 726 : typid = lfirst_oid(lc_coltype);
12106 726 : typmod = lfirst_int(lc_coltypmod);
12107 726 : colexpr = castNode(JsonExpr, lfirst(lc_colvalexpr));
12108 :
12109 : /* Skip columns that don't belong to this scan. */
12110 726 : if (scan->colMin < 0 || colnum < scan->colMin)
12111 : {
12112 264 : colnum++;
12113 264 : continue;
12114 : }
12115 462 : if (colnum > scan->colMax)
12116 48 : break;
12117 :
12118 414 : if (colnum > scan->colMin)
12119 258 : appendStringInfoString(buf, ", ");
12120 :
12121 414 : colnum++;
12122 :
12123 414 : ordinality = !colexpr;
12124 :
12125 414 : appendContextKeyword(context, "", 0, 0, 0);
12126 :
12127 810 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
12128 : ordinality ? "FOR ORDINALITY" :
12129 396 : format_type_with_typemod(typid, typmod));
12130 414 : if (ordinality)
12131 18 : continue;
12132 :
12133 : /*
12134 : * Set default_behavior to guide get_json_expr_options() on whether to
12135 : * emit the ON ERROR / EMPTY clauses.
12136 : */
12137 396 : if (colexpr->op == JSON_EXISTS_OP)
12138 : {
12139 36 : appendStringInfoString(buf, " EXISTS");
12140 36 : default_behavior = JSON_BEHAVIOR_FALSE;
12141 : }
12142 : else
12143 : {
12144 360 : if (colexpr->op == JSON_QUERY_OP)
12145 : {
12146 : char typcategory;
12147 : bool typispreferred;
12148 :
12149 174 : get_type_category_preferred(typid, &typcategory, &typispreferred);
12150 :
12151 174 : if (typcategory == TYPCATEGORY_STRING)
12152 36 : appendStringInfoString(buf,
12153 36 : colexpr->format->format_type == JS_FORMAT_JSONB ?
12154 : " FORMAT JSONB" : " FORMAT JSON");
12155 : }
12156 :
12157 360 : default_behavior = JSON_BEHAVIOR_NULL;
12158 : }
12159 :
12160 396 : appendStringInfoString(buf, " PATH ");
12161 :
12162 396 : get_json_path_spec(colexpr->path_spec, context, showimplicit);
12163 :
12164 396 : get_json_expr_options(colexpr, context, default_behavior);
12165 : }
12166 :
12167 180 : if (scan->child)
12168 42 : get_json_table_nested_columns(tf, scan->child, context, showimplicit,
12169 42 : scan->colMin >= 0);
12170 :
12171 180 : if (PRETTY_INDENT(context))
12172 138 : context->indentLevel -= PRETTYINDENT_VAR;
12173 :
12174 180 : appendContextKeyword(context, ")", 0, 0, 0);
12175 180 : }
12176 :
12177 : /* ----------
12178 : * get_json_table - Parse back a JSON_TABLE function
12179 : * ----------
12180 : */
12181 : static void
12182 108 : get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
12183 : {
12184 108 : StringInfo buf = context->buf;
12185 108 : JsonExpr *jexpr = castNode(JsonExpr, tf->docexpr);
12186 108 : JsonTablePathScan *root = castNode(JsonTablePathScan, tf->plan);
12187 :
12188 108 : appendStringInfoString(buf, "JSON_TABLE(");
12189 :
12190 108 : if (PRETTY_INDENT(context))
12191 66 : context->indentLevel += PRETTYINDENT_VAR;
12192 :
12193 108 : appendContextKeyword(context, "", 0, 0, 0);
12194 :
12195 108 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12196 :
12197 108 : appendStringInfoString(buf, ", ");
12198 :
12199 108 : get_const_expr(root->path->value, context, -1);
12200 :
12201 108 : appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12202 :
12203 108 : if (jexpr->passing_values)
12204 : {
12205 : ListCell *lc1,
12206 : *lc2;
12207 84 : bool needcomma = false;
12208 :
12209 84 : appendStringInfoChar(buf, ' ');
12210 84 : appendContextKeyword(context, "PASSING ", 0, 0, 0);
12211 :
12212 84 : if (PRETTY_INDENT(context))
12213 42 : context->indentLevel += PRETTYINDENT_VAR;
12214 :
12215 252 : forboth(lc1, jexpr->passing_names,
12216 : lc2, jexpr->passing_values)
12217 : {
12218 168 : if (needcomma)
12219 84 : appendStringInfoString(buf, ", ");
12220 168 : needcomma = true;
12221 :
12222 168 : appendContextKeyword(context, "", 0, 0, 0);
12223 :
12224 168 : get_rule_expr((Node *) lfirst(lc2), context, false);
12225 168 : appendStringInfo(buf, " AS %s",
12226 168 : quote_identifier((lfirst_node(String, lc1))->sval)
12227 : );
12228 : }
12229 :
12230 84 : if (PRETTY_INDENT(context))
12231 42 : context->indentLevel -= PRETTYINDENT_VAR;
12232 : }
12233 :
12234 108 : get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12235 : showimplicit);
12236 :
12237 108 : if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12238 6 : get_json_behavior(jexpr->on_error, context, "ERROR");
12239 :
12240 108 : if (PRETTY_INDENT(context))
12241 66 : context->indentLevel -= PRETTYINDENT_VAR;
12242 :
12243 108 : appendContextKeyword(context, ")", 0, 0, 0);
12244 108 : }
12245 :
12246 : /* ----------
12247 : * get_tablefunc - Parse back a table function
12248 : * ----------
12249 : */
12250 : static void
12251 182 : get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
12252 : {
12253 : /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12254 :
12255 182 : if (tf->functype == TFT_XMLTABLE)
12256 74 : get_xmltable(tf, context, showimplicit);
12257 108 : else if (tf->functype == TFT_JSON_TABLE)
12258 108 : get_json_table(tf, context, showimplicit);
12259 182 : }
12260 :
12261 : /* ----------
12262 : * get_from_clause - Parse back a FROM clause
12263 : *
12264 : * "prefix" is the keyword that denotes the start of the list of FROM
12265 : * elements. It is FROM when used to parse back SELECT and UPDATE, but
12266 : * is USING when parsing back DELETE.
12267 : * ----------
12268 : */
12269 : static void
12270 5550 : get_from_clause(Query *query, const char *prefix, deparse_context *context)
12271 : {
12272 5550 : StringInfo buf = context->buf;
12273 5550 : bool first = true;
12274 : ListCell *l;
12275 :
12276 : /*
12277 : * We use the query's jointree as a guide to what to print. However, we
12278 : * must ignore auto-added RTEs that are marked not inFromCl. (These can
12279 : * only appear at the top level of the jointree, so it's sufficient to
12280 : * check here.) This check also ensures we ignore the rule pseudo-RTEs
12281 : * for NEW and OLD.
12282 : */
12283 11096 : foreach(l, query->jointree->fromlist)
12284 : {
12285 5546 : Node *jtnode = (Node *) lfirst(l);
12286 :
12287 5546 : if (IsA(jtnode, RangeTblRef))
12288 : {
12289 4570 : int varno = ((RangeTblRef *) jtnode)->rtindex;
12290 4570 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12291 :
12292 4570 : if (!rte->inFromCl)
12293 586 : continue;
12294 : }
12295 :
12296 4960 : if (first)
12297 : {
12298 4554 : appendContextKeyword(context, prefix,
12299 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
12300 4554 : first = false;
12301 :
12302 4554 : get_from_clause_item(jtnode, query, context);
12303 : }
12304 : else
12305 : {
12306 : StringInfoData itembuf;
12307 :
12308 406 : appendStringInfoString(buf, ", ");
12309 :
12310 : /*
12311 : * Put the new FROM item's text into itembuf so we can decide
12312 : * after we've got it whether or not it needs to go on a new line.
12313 : */
12314 406 : initStringInfo(&itembuf);
12315 406 : context->buf = &itembuf;
12316 :
12317 406 : get_from_clause_item(jtnode, query, context);
12318 :
12319 : /* Restore context's output buffer */
12320 406 : context->buf = buf;
12321 :
12322 : /* Consider line-wrapping if enabled */
12323 406 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
12324 : {
12325 : /* Does the new item start with a new line? */
12326 406 : if (itembuf.len > 0 && itembuf.data[0] == '\n')
12327 : {
12328 : /* If so, we shouldn't add anything */
12329 : /* instead, remove any trailing spaces currently in buf */
12330 0 : removeStringInfoSpaces(buf);
12331 : }
12332 : else
12333 : {
12334 : char *trailing_nl;
12335 :
12336 : /* Locate the start of the current line in the buffer */
12337 406 : trailing_nl = strrchr(buf->data, '\n');
12338 406 : if (trailing_nl == NULL)
12339 0 : trailing_nl = buf->data;
12340 : else
12341 406 : trailing_nl++;
12342 :
12343 : /*
12344 : * Add a newline, plus some indentation, if the new item
12345 : * would cause an overflow.
12346 : */
12347 406 : if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
12348 406 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
12349 : PRETTYINDENT_STD,
12350 : PRETTYINDENT_VAR);
12351 : }
12352 : }
12353 :
12354 : /* Add the new item */
12355 406 : appendBinaryStringInfo(buf, itembuf.data, itembuf.len);
12356 :
12357 : /* clean up */
12358 406 : pfree(itembuf.data);
12359 : }
12360 : }
12361 5550 : }
12362 :
12363 : static void
12364 8004 : get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
12365 : {
12366 8004 : StringInfo buf = context->buf;
12367 8004 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
12368 :
12369 8004 : if (IsA(jtnode, RangeTblRef))
12370 : {
12371 6482 : int varno = ((RangeTblRef *) jtnode)->rtindex;
12372 6482 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12373 6482 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
12374 6482 : RangeTblFunction *rtfunc1 = NULL;
12375 :
12376 6482 : if (rte->lateral)
12377 118 : appendStringInfoString(buf, "LATERAL ");
12378 :
12379 : /* Print the FROM item proper */
12380 6482 : switch (rte->rtekind)
12381 : {
12382 5002 : case RTE_RELATION:
12383 : /* Normal relation RTE */
12384 10004 : appendStringInfo(buf, "%s%s",
12385 5002 : only_marker(rte),
12386 : generate_relation_name(rte->relid,
12387 : context->namespaces));
12388 5002 : break;
12389 298 : case RTE_SUBQUERY:
12390 : /* Subquery RTE */
12391 298 : appendStringInfoChar(buf, '(');
12392 298 : get_query_def(rte->subquery, buf, context->namespaces, NULL,
12393 : true,
12394 : context->prettyFlags, context->wrapColumn,
12395 : context->indentLevel);
12396 298 : appendStringInfoChar(buf, ')');
12397 298 : break;
12398 834 : case RTE_FUNCTION:
12399 : /* Function RTE */
12400 834 : rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
12401 :
12402 : /*
12403 : * Omit ROWS FROM() syntax for just one function, unless it
12404 : * has both a coldeflist and WITH ORDINALITY. If it has both,
12405 : * we must use ROWS FROM() syntax to avoid ambiguity about
12406 : * whether the coldeflist includes the ordinality column.
12407 : */
12408 834 : if (list_length(rte->functions) == 1 &&
12409 804 : (rtfunc1->funccolnames == NIL || !rte->funcordinality))
12410 : {
12411 804 : get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
12412 : /* we'll print the coldeflist below, if it has one */
12413 : }
12414 : else
12415 : {
12416 : bool all_unnest;
12417 : ListCell *lc;
12418 :
12419 : /*
12420 : * If all the function calls in the list are to unnest,
12421 : * and none need a coldeflist, then collapse the list back
12422 : * down to UNNEST(args). (If we had more than one
12423 : * built-in unnest function, this would get more
12424 : * difficult.)
12425 : *
12426 : * XXX This is pretty ugly, since it makes not-terribly-
12427 : * future-proof assumptions about what the parser would do
12428 : * with the output; but the alternative is to emit our
12429 : * nonstandard ROWS FROM() notation for what might have
12430 : * been a perfectly spec-compliant multi-argument
12431 : * UNNEST().
12432 : */
12433 30 : all_unnest = true;
12434 78 : foreach(lc, rte->functions)
12435 : {
12436 66 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12437 :
12438 66 : if (!IsA(rtfunc->funcexpr, FuncExpr) ||
12439 66 : ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
12440 48 : rtfunc->funccolnames != NIL)
12441 : {
12442 18 : all_unnest = false;
12443 18 : break;
12444 : }
12445 : }
12446 :
12447 30 : if (all_unnest)
12448 : {
12449 12 : List *allargs = NIL;
12450 :
12451 48 : foreach(lc, rte->functions)
12452 : {
12453 36 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12454 36 : List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
12455 :
12456 36 : allargs = list_concat(allargs, args);
12457 : }
12458 :
12459 12 : appendStringInfoString(buf, "UNNEST(");
12460 12 : get_rule_expr((Node *) allargs, context, true);
12461 12 : appendStringInfoChar(buf, ')');
12462 : }
12463 : else
12464 : {
12465 18 : int funcno = 0;
12466 :
12467 18 : appendStringInfoString(buf, "ROWS FROM(");
12468 66 : foreach(lc, rte->functions)
12469 : {
12470 48 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12471 :
12472 48 : if (funcno > 0)
12473 30 : appendStringInfoString(buf, ", ");
12474 48 : get_rule_expr_funccall(rtfunc->funcexpr, context, true);
12475 48 : if (rtfunc->funccolnames != NIL)
12476 : {
12477 : /* Reconstruct the column definition list */
12478 6 : appendStringInfoString(buf, " AS ");
12479 6 : get_from_clause_coldeflist(rtfunc,
12480 : NULL,
12481 : context);
12482 : }
12483 48 : funcno++;
12484 : }
12485 18 : appendStringInfoChar(buf, ')');
12486 : }
12487 : /* prevent printing duplicate coldeflist below */
12488 30 : rtfunc1 = NULL;
12489 : }
12490 834 : if (rte->funcordinality)
12491 18 : appendStringInfoString(buf, " WITH ORDINALITY");
12492 834 : break;
12493 110 : case RTE_TABLEFUNC:
12494 110 : get_tablefunc(rte->tablefunc, context, true);
12495 110 : break;
12496 12 : case RTE_VALUES:
12497 : /* Values list RTE */
12498 12 : appendStringInfoChar(buf, '(');
12499 12 : get_values_def(rte->values_lists, context);
12500 12 : appendStringInfoChar(buf, ')');
12501 12 : break;
12502 226 : case RTE_CTE:
12503 226 : appendStringInfoString(buf, quote_identifier(rte->ctename));
12504 226 : break;
12505 0 : default:
12506 0 : elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
12507 : break;
12508 : }
12509 :
12510 : /* Print the relation alias, if needed */
12511 6482 : get_rte_alias(rte, varno, false, context);
12512 :
12513 : /* Print the column definitions or aliases, if needed */
12514 6482 : if (rtfunc1 && rtfunc1->funccolnames != NIL)
12515 : {
12516 : /* Reconstruct the columndef list, which is also the aliases */
12517 0 : get_from_clause_coldeflist(rtfunc1, colinfo, context);
12518 : }
12519 : else
12520 : {
12521 : /* Else print column aliases as needed */
12522 6482 : get_column_alias_list(colinfo, context);
12523 : }
12524 :
12525 : /* Tablesample clause must go after any alias */
12526 6482 : if (rte->rtekind == RTE_RELATION && rte->tablesample)
12527 44 : get_tablesample_def(rte->tablesample, context);
12528 : }
12529 1522 : else if (IsA(jtnode, JoinExpr))
12530 : {
12531 1522 : JoinExpr *j = (JoinExpr *) jtnode;
12532 1522 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
12533 : bool need_paren_on_right;
12534 :
12535 3482 : need_paren_on_right = PRETTY_PAREN(context) &&
12536 1522 : !IsA(j->rarg, RangeTblRef) &&
12537 0 : !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
12538 :
12539 1522 : if (!PRETTY_PAREN(context) || j->alias != NULL)
12540 1192 : appendStringInfoChar(buf, '(');
12541 :
12542 1522 : get_from_clause_item(j->larg, query, context);
12543 :
12544 1522 : switch (j->jointype)
12545 : {
12546 844 : case JOIN_INNER:
12547 844 : if (j->quals)
12548 802 : appendContextKeyword(context, " JOIN ",
12549 : -PRETTYINDENT_STD,
12550 : PRETTYINDENT_STD,
12551 : PRETTYINDENT_JOIN);
12552 : else
12553 42 : appendContextKeyword(context, " CROSS JOIN ",
12554 : -PRETTYINDENT_STD,
12555 : PRETTYINDENT_STD,
12556 : PRETTYINDENT_JOIN);
12557 844 : break;
12558 576 : case JOIN_LEFT:
12559 576 : appendContextKeyword(context, " LEFT JOIN ",
12560 : -PRETTYINDENT_STD,
12561 : PRETTYINDENT_STD,
12562 : PRETTYINDENT_JOIN);
12563 576 : break;
12564 102 : case JOIN_FULL:
12565 102 : appendContextKeyword(context, " FULL JOIN ",
12566 : -PRETTYINDENT_STD,
12567 : PRETTYINDENT_STD,
12568 : PRETTYINDENT_JOIN);
12569 102 : break;
12570 0 : case JOIN_RIGHT:
12571 0 : appendContextKeyword(context, " RIGHT JOIN ",
12572 : -PRETTYINDENT_STD,
12573 : PRETTYINDENT_STD,
12574 : PRETTYINDENT_JOIN);
12575 0 : break;
12576 0 : default:
12577 0 : elog(ERROR, "unrecognized join type: %d",
12578 : (int) j->jointype);
12579 : }
12580 :
12581 1522 : if (need_paren_on_right)
12582 0 : appendStringInfoChar(buf, '(');
12583 1522 : get_from_clause_item(j->rarg, query, context);
12584 1522 : if (need_paren_on_right)
12585 0 : appendStringInfoChar(buf, ')');
12586 :
12587 1522 : if (j->usingClause)
12588 : {
12589 : ListCell *lc;
12590 448 : bool first = true;
12591 :
12592 448 : appendStringInfoString(buf, " USING (");
12593 : /* Use the assigned names, not what's in usingClause */
12594 1052 : foreach(lc, colinfo->usingNames)
12595 : {
12596 604 : char *colname = (char *) lfirst(lc);
12597 :
12598 604 : if (first)
12599 448 : first = false;
12600 : else
12601 156 : appendStringInfoString(buf, ", ");
12602 604 : appendStringInfoString(buf, quote_identifier(colname));
12603 : }
12604 448 : appendStringInfoChar(buf, ')');
12605 :
12606 448 : if (j->join_using_alias)
12607 12 : appendStringInfo(buf, " AS %s",
12608 12 : quote_identifier(j->join_using_alias->aliasname));
12609 : }
12610 1074 : else if (j->quals)
12611 : {
12612 1026 : appendStringInfoString(buf, " ON ");
12613 1026 : if (!PRETTY_PAREN(context))
12614 1020 : appendStringInfoChar(buf, '(');
12615 1026 : get_rule_expr(j->quals, context, false);
12616 1026 : if (!PRETTY_PAREN(context))
12617 1020 : appendStringInfoChar(buf, ')');
12618 : }
12619 48 : else if (j->jointype != JOIN_INNER)
12620 : {
12621 : /* If we didn't say CROSS JOIN above, we must provide an ON */
12622 6 : appendStringInfoString(buf, " ON TRUE");
12623 : }
12624 :
12625 1522 : if (!PRETTY_PAREN(context) || j->alias != NULL)
12626 1192 : appendStringInfoChar(buf, ')');
12627 :
12628 : /* Yes, it's correct to put alias after the right paren ... */
12629 1522 : if (j->alias != NULL)
12630 : {
12631 : /*
12632 : * Note that it's correct to emit an alias clause if and only if
12633 : * there was one originally. Otherwise we'd be converting a named
12634 : * join to unnamed or vice versa, which creates semantic
12635 : * subtleties we don't want. However, we might print a different
12636 : * alias name than was there originally.
12637 : */
12638 108 : appendStringInfo(buf, " %s",
12639 108 : quote_identifier(get_rtable_name(j->rtindex,
12640 : context)));
12641 108 : get_column_alias_list(colinfo, context);
12642 : }
12643 : }
12644 : else
12645 0 : elog(ERROR, "unrecognized node type: %d",
12646 : (int) nodeTag(jtnode));
12647 8004 : }
12648 :
12649 : /*
12650 : * get_rte_alias - print the relation's alias, if needed
12651 : *
12652 : * If printed, the alias is preceded by a space, or by " AS " if use_as is true.
12653 : */
12654 : static void
12655 7298 : get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
12656 : deparse_context *context)
12657 : {
12658 7298 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
12659 7298 : char *refname = get_rtable_name(varno, context);
12660 7298 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
12661 7298 : bool printalias = false;
12662 :
12663 7298 : if (rte->alias != NULL)
12664 : {
12665 : /* Always print alias if user provided one */
12666 3244 : printalias = true;
12667 : }
12668 4054 : else if (colinfo->printaliases)
12669 : {
12670 : /* Always print alias if we need to print column aliases */
12671 318 : printalias = true;
12672 : }
12673 3736 : else if (rte->rtekind == RTE_RELATION)
12674 : {
12675 : /*
12676 : * No need to print alias if it's same as relation name (this would
12677 : * normally be the case, but not if set_rtable_names had to resolve a
12678 : * conflict).
12679 : */
12680 3414 : if (strcmp(refname, get_relation_name(rte->relid)) != 0)
12681 84 : printalias = true;
12682 : }
12683 322 : else if (rte->rtekind == RTE_FUNCTION)
12684 : {
12685 : /*
12686 : * For a function RTE, always print alias. This covers possible
12687 : * renaming of the function and/or instability of the FigureColname
12688 : * rules for things that aren't simple functions. Note we'd need to
12689 : * force it anyway for the columndef list case.
12690 : */
12691 0 : printalias = true;
12692 : }
12693 322 : else if (rte->rtekind == RTE_SUBQUERY ||
12694 298 : rte->rtekind == RTE_VALUES)
12695 : {
12696 : /*
12697 : * For a subquery, always print alias. This makes the output
12698 : * SQL-spec-compliant, even though we allow such aliases to be omitted
12699 : * on input.
12700 : */
12701 36 : printalias = true;
12702 : }
12703 286 : else if (rte->rtekind == RTE_CTE)
12704 : {
12705 : /*
12706 : * No need to print alias if it's same as CTE name (this would
12707 : * normally be the case, but not if set_rtable_names had to resolve a
12708 : * conflict).
12709 : */
12710 182 : if (strcmp(refname, rte->ctename) != 0)
12711 30 : printalias = true;
12712 : }
12713 :
12714 7298 : if (printalias)
12715 3712 : appendStringInfo(context->buf, "%s%s",
12716 : use_as ? " AS " : " ",
12717 : quote_identifier(refname));
12718 7298 : }
12719 :
12720 : /*
12721 : * get_column_alias_list - print column alias list for an RTE
12722 : *
12723 : * Caller must already have printed the relation's alias name.
12724 : */
12725 : static void
12726 6590 : get_column_alias_list(deparse_columns *colinfo, deparse_context *context)
12727 : {
12728 6590 : StringInfo buf = context->buf;
12729 : int i;
12730 6590 : bool first = true;
12731 :
12732 : /* Don't print aliases if not needed */
12733 6590 : if (!colinfo->printaliases)
12734 5396 : return;
12735 :
12736 9636 : for (i = 0; i < colinfo->num_new_cols; i++)
12737 : {
12738 8442 : char *colname = colinfo->new_colnames[i];
12739 :
12740 8442 : if (first)
12741 : {
12742 1194 : appendStringInfoChar(buf, '(');
12743 1194 : first = false;
12744 : }
12745 : else
12746 7248 : appendStringInfoString(buf, ", ");
12747 8442 : appendStringInfoString(buf, quote_identifier(colname));
12748 : }
12749 1194 : if (!first)
12750 1194 : appendStringInfoChar(buf, ')');
12751 : }
12752 :
12753 : /*
12754 : * get_from_clause_coldeflist - reproduce FROM clause coldeflist
12755 : *
12756 : * When printing a top-level coldeflist (which is syntactically also the
12757 : * relation's column alias list), use column names from colinfo. But when
12758 : * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
12759 : * original coldeflist's names, which are available in rtfunc->funccolnames.
12760 : * Pass NULL for colinfo to select the latter behavior.
12761 : *
12762 : * The coldeflist is appended immediately (no space) to buf. Caller is
12763 : * responsible for ensuring that an alias or AS is present before it.
12764 : */
12765 : static void
12766 6 : get_from_clause_coldeflist(RangeTblFunction *rtfunc,
12767 : deparse_columns *colinfo,
12768 : deparse_context *context)
12769 : {
12770 6 : StringInfo buf = context->buf;
12771 : ListCell *l1;
12772 : ListCell *l2;
12773 : ListCell *l3;
12774 : ListCell *l4;
12775 : int i;
12776 :
12777 6 : appendStringInfoChar(buf, '(');
12778 :
12779 6 : i = 0;
12780 24 : forfour(l1, rtfunc->funccoltypes,
12781 : l2, rtfunc->funccoltypmods,
12782 : l3, rtfunc->funccolcollations,
12783 : l4, rtfunc->funccolnames)
12784 : {
12785 18 : Oid atttypid = lfirst_oid(l1);
12786 18 : int32 atttypmod = lfirst_int(l2);
12787 18 : Oid attcollation = lfirst_oid(l3);
12788 : char *attname;
12789 :
12790 18 : if (colinfo)
12791 0 : attname = colinfo->colnames[i];
12792 : else
12793 18 : attname = strVal(lfirst(l4));
12794 :
12795 : Assert(attname); /* shouldn't be any dropped columns here */
12796 :
12797 18 : if (i > 0)
12798 12 : appendStringInfoString(buf, ", ");
12799 18 : appendStringInfo(buf, "%s %s",
12800 : quote_identifier(attname),
12801 : format_type_with_typemod(atttypid, atttypmod));
12802 24 : if (OidIsValid(attcollation) &&
12803 6 : attcollation != get_typcollation(atttypid))
12804 0 : appendStringInfo(buf, " COLLATE %s",
12805 : generate_collation_name(attcollation));
12806 :
12807 18 : i++;
12808 : }
12809 :
12810 6 : appendStringInfoChar(buf, ')');
12811 6 : }
12812 :
12813 : /*
12814 : * get_tablesample_def - print a TableSampleClause
12815 : */
12816 : static void
12817 44 : get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
12818 : {
12819 44 : StringInfo buf = context->buf;
12820 : Oid argtypes[1];
12821 : int nargs;
12822 : ListCell *l;
12823 :
12824 : /*
12825 : * We should qualify the handler's function name if it wouldn't be
12826 : * resolved by lookup in the current search path.
12827 : */
12828 44 : argtypes[0] = INTERNALOID;
12829 44 : appendStringInfo(buf, " TABLESAMPLE %s (",
12830 : generate_function_name(tablesample->tsmhandler, 1,
12831 : NIL, argtypes,
12832 : false, NULL, false));
12833 :
12834 44 : nargs = 0;
12835 88 : foreach(l, tablesample->args)
12836 : {
12837 44 : if (nargs++ > 0)
12838 0 : appendStringInfoString(buf, ", ");
12839 44 : get_rule_expr((Node *) lfirst(l), context, false);
12840 : }
12841 44 : appendStringInfoChar(buf, ')');
12842 :
12843 44 : if (tablesample->repeatable != NULL)
12844 : {
12845 22 : appendStringInfoString(buf, " REPEATABLE (");
12846 22 : get_rule_expr((Node *) tablesample->repeatable, context, false);
12847 22 : appendStringInfoChar(buf, ')');
12848 : }
12849 44 : }
12850 :
12851 : /*
12852 : * get_opclass_name - fetch name of an index operator class
12853 : *
12854 : * The opclass name is appended (after a space) to buf.
12855 : *
12856 : * Output is suppressed if the opclass is the default for the given
12857 : * actual_datatype. (If you don't want this behavior, just pass
12858 : * InvalidOid for actual_datatype.)
12859 : */
12860 : static void
12861 15776 : get_opclass_name(Oid opclass, Oid actual_datatype,
12862 : StringInfo buf)
12863 : {
12864 : HeapTuple ht_opc;
12865 : Form_pg_opclass opcrec;
12866 : char *opcname;
12867 : char *nspname;
12868 :
12869 15776 : ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
12870 15776 : if (!HeapTupleIsValid(ht_opc))
12871 0 : elog(ERROR, "cache lookup failed for opclass %u", opclass);
12872 15776 : opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
12873 :
12874 31512 : if (!OidIsValid(actual_datatype) ||
12875 15736 : GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
12876 : {
12877 : /* Okay, we need the opclass name. Do we need to qualify it? */
12878 840 : opcname = NameStr(opcrec->opcname);
12879 840 : if (OpclassIsVisible(opclass))
12880 840 : appendStringInfo(buf, " %s", quote_identifier(opcname));
12881 : else
12882 : {
12883 0 : nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
12884 0 : appendStringInfo(buf, " %s.%s",
12885 : quote_identifier(nspname),
12886 : quote_identifier(opcname));
12887 : }
12888 : }
12889 15776 : ReleaseSysCache(ht_opc);
12890 15776 : }
12891 :
12892 : /*
12893 : * generate_opclass_name
12894 : * Compute the name to display for an opclass specified by OID
12895 : *
12896 : * The result includes all necessary quoting and schema-prefixing.
12897 : */
12898 : char *
12899 6 : generate_opclass_name(Oid opclass)
12900 : {
12901 : StringInfoData buf;
12902 :
12903 6 : initStringInfo(&buf);
12904 6 : get_opclass_name(opclass, InvalidOid, &buf);
12905 :
12906 6 : return &buf.data[1]; /* get_opclass_name() prepends space */
12907 : }
12908 :
12909 : /*
12910 : * processIndirection - take care of array and subfield assignment
12911 : *
12912 : * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
12913 : * appear in the input, printing them as decoration for the base column
12914 : * name (which we assume the caller just printed). We might also need to
12915 : * strip CoerceToDomain nodes, but only ones that appear above assignment
12916 : * nodes.
12917 : *
12918 : * Returns the subexpression that's to be assigned.
12919 : */
12920 : static Node *
12921 1758 : processIndirection(Node *node, deparse_context *context)
12922 : {
12923 1758 : StringInfo buf = context->buf;
12924 1758 : CoerceToDomain *cdomain = NULL;
12925 :
12926 : for (;;)
12927 : {
12928 2064 : if (node == NULL)
12929 0 : break;
12930 2064 : if (IsA(node, FieldStore))
12931 : {
12932 108 : FieldStore *fstore = (FieldStore *) node;
12933 : Oid typrelid;
12934 : char *fieldname;
12935 :
12936 : /* lookup tuple type */
12937 108 : typrelid = get_typ_typrelid(fstore->resulttype);
12938 108 : if (!OidIsValid(typrelid))
12939 0 : elog(ERROR, "argument type %s of FieldStore is not a tuple type",
12940 : format_type_be(fstore->resulttype));
12941 :
12942 : /*
12943 : * Print the field name. There should only be one target field in
12944 : * stored rules. There could be more than that in executable
12945 : * target lists, but this function cannot be used for that case.
12946 : */
12947 : Assert(list_length(fstore->fieldnums) == 1);
12948 108 : fieldname = get_attname(typrelid,
12949 108 : linitial_int(fstore->fieldnums), false);
12950 108 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
12951 :
12952 : /*
12953 : * We ignore arg since it should be an uninteresting reference to
12954 : * the target column or subcolumn.
12955 : */
12956 108 : node = (Node *) linitial(fstore->newvals);
12957 : }
12958 1956 : else if (IsA(node, SubscriptingRef))
12959 : {
12960 138 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
12961 :
12962 138 : if (sbsref->refassgnexpr == NULL)
12963 0 : break;
12964 :
12965 138 : printSubscripts(sbsref, context);
12966 :
12967 : /*
12968 : * We ignore refexpr since it should be an uninteresting reference
12969 : * to the target column or subcolumn.
12970 : */
12971 138 : node = (Node *) sbsref->refassgnexpr;
12972 : }
12973 1818 : else if (IsA(node, CoerceToDomain))
12974 : {
12975 60 : cdomain = (CoerceToDomain *) node;
12976 : /* If it's an explicit domain coercion, we're done */
12977 60 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
12978 0 : break;
12979 : /* Tentatively descend past the CoerceToDomain */
12980 60 : node = (Node *) cdomain->arg;
12981 : }
12982 : else
12983 1758 : break;
12984 : }
12985 :
12986 : /*
12987 : * If we descended past a CoerceToDomain whose argument turned out not to
12988 : * be a FieldStore or array assignment, back up to the CoerceToDomain.
12989 : * (This is not enough to be fully correct if there are nested implicit
12990 : * CoerceToDomains, but such cases shouldn't ever occur.)
12991 : */
12992 1758 : if (cdomain && node == (Node *) cdomain->arg)
12993 0 : node = (Node *) cdomain;
12994 :
12995 1758 : return node;
12996 : }
12997 :
12998 : static void
12999 478 : printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
13000 : {
13001 478 : StringInfo buf = context->buf;
13002 : ListCell *lowlist_item;
13003 : ListCell *uplist_item;
13004 :
13005 478 : lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
13006 956 : foreach(uplist_item, sbsref->refupperindexpr)
13007 : {
13008 478 : appendStringInfoChar(buf, '[');
13009 478 : if (lowlist_item)
13010 : {
13011 : /* If subexpression is NULL, get_rule_expr prints nothing */
13012 0 : get_rule_expr((Node *) lfirst(lowlist_item), context, false);
13013 0 : appendStringInfoChar(buf, ':');
13014 0 : lowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item);
13015 : }
13016 : /* If subexpression is NULL, get_rule_expr prints nothing */
13017 478 : get_rule_expr((Node *) lfirst(uplist_item), context, false);
13018 478 : appendStringInfoChar(buf, ']');
13019 : }
13020 478 : }
13021 :
13022 : /*
13023 : * quote_identifier - Quote an identifier only if needed
13024 : *
13025 : * When quotes are needed, we palloc the required space; slightly
13026 : * space-wasteful but well worth it for notational simplicity.
13027 : */
13028 : const char *
13029 2550280 : quote_identifier(const char *ident)
13030 : {
13031 : /*
13032 : * Can avoid quoting if ident starts with a lowercase letter or underscore
13033 : * and contains only lowercase letters, digits, and underscores, *and* is
13034 : * not any SQL keyword. Otherwise, supply quotes.
13035 : */
13036 2550280 : int nquotes = 0;
13037 : bool safe;
13038 : const char *ptr;
13039 : char *result;
13040 : char *optr;
13041 :
13042 : /*
13043 : * would like to use <ctype.h> macros here, but they might yield unwanted
13044 : * locale-specific results...
13045 : */
13046 2550280 : safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
13047 :
13048 21999894 : for (ptr = ident; *ptr; ptr++)
13049 : {
13050 19449614 : char ch = *ptr;
13051 :
13052 19449614 : if ((ch >= 'a' && ch <= 'z') ||
13053 2315630 : (ch >= '0' && ch <= '9') ||
13054 : (ch == '_'))
13055 : {
13056 : /* okay */
13057 : }
13058 : else
13059 : {
13060 64960 : safe = false;
13061 64960 : if (ch == '"')
13062 182 : nquotes++;
13063 : }
13064 : }
13065 :
13066 2550280 : if (quote_all_identifiers)
13067 12360 : safe = false;
13068 :
13069 2550280 : if (safe)
13070 : {
13071 : /*
13072 : * Check for keyword. We quote keywords except for unreserved ones.
13073 : * (In some cases we could avoid quoting a col_name or type_func_name
13074 : * keyword, but it seems much harder than it's worth to tell that.)
13075 : *
13076 : * Note: ScanKeywordLookup() does case-insensitive comparison, but
13077 : * that's fine, since we already know we have all-lower-case.
13078 : */
13079 2511342 : int kwnum = ScanKeywordLookup(ident, &ScanKeywords);
13080 :
13081 2511342 : if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
13082 3386 : safe = false;
13083 : }
13084 :
13085 2550280 : if (safe)
13086 2507956 : return ident; /* no change needed */
13087 :
13088 42324 : result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13089 :
13090 42324 : optr = result;
13091 42324 : *optr++ = '"';
13092 250640 : for (ptr = ident; *ptr; ptr++)
13093 : {
13094 208316 : char ch = *ptr;
13095 :
13096 208316 : if (ch == '"')
13097 182 : *optr++ = '"';
13098 208316 : *optr++ = ch;
13099 : }
13100 42324 : *optr++ = '"';
13101 42324 : *optr = '\0';
13102 :
13103 42324 : return result;
13104 : }
13105 :
13106 : /*
13107 : * quote_qualified_identifier - Quote a possibly-qualified identifier
13108 : *
13109 : * Return a name of the form qualifier.ident, or just ident if qualifier
13110 : * is NULL, quoting each component if necessary. The result is palloc'd.
13111 : */
13112 : char *
13113 1257282 : quote_qualified_identifier(const char *qualifier,
13114 : const char *ident)
13115 : {
13116 : StringInfoData buf;
13117 :
13118 1257282 : initStringInfo(&buf);
13119 1257282 : if (qualifier)
13120 456622 : appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
13121 1257282 : appendStringInfoString(&buf, quote_identifier(ident));
13122 1257282 : return buf.data;
13123 : }
13124 :
13125 : /*
13126 : * get_relation_name
13127 : * Get the unqualified name of a relation specified by OID
13128 : *
13129 : * This differs from the underlying get_rel_name() function in that it will
13130 : * throw error instead of silently returning NULL if the OID is bad.
13131 : */
13132 : static char *
13133 19422 : get_relation_name(Oid relid)
13134 : {
13135 19422 : char *relname = get_rel_name(relid);
13136 :
13137 19422 : if (!relname)
13138 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13139 19422 : return relname;
13140 : }
13141 :
13142 : /*
13143 : * generate_relation_name
13144 : * Compute the name to display for a relation specified by OID
13145 : *
13146 : * The result includes all necessary quoting and schema-prefixing.
13147 : *
13148 : * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
13149 : * We will forcibly qualify the relation name if it equals any CTE name
13150 : * visible in the namespace list.
13151 : */
13152 : static char *
13153 8772 : generate_relation_name(Oid relid, List *namespaces)
13154 : {
13155 : HeapTuple tp;
13156 : Form_pg_class reltup;
13157 : bool need_qual;
13158 : ListCell *nslist;
13159 : char *relname;
13160 : char *nspname;
13161 : char *result;
13162 :
13163 8772 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13164 8772 : if (!HeapTupleIsValid(tp))
13165 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13166 8772 : reltup = (Form_pg_class) GETSTRUCT(tp);
13167 8772 : relname = NameStr(reltup->relname);
13168 :
13169 : /* Check for conflicting CTE name */
13170 8772 : need_qual = false;
13171 15038 : foreach(nslist, namespaces)
13172 : {
13173 6266 : deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
13174 : ListCell *ctlist;
13175 :
13176 6398 : foreach(ctlist, dpns->ctes)
13177 : {
13178 132 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);
13179 :
13180 132 : if (strcmp(cte->ctename, relname) == 0)
13181 : {
13182 0 : need_qual = true;
13183 0 : break;
13184 : }
13185 : }
13186 6266 : if (need_qual)
13187 0 : break;
13188 : }
13189 :
13190 : /* Otherwise, qualify the name if not visible in search path */
13191 8772 : if (!need_qual)
13192 8772 : need_qual = !RelationIsVisible(relid);
13193 :
13194 8772 : if (need_qual)
13195 3158 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
13196 : else
13197 5614 : nspname = NULL;
13198 :
13199 8772 : result = quote_qualified_identifier(nspname, relname);
13200 :
13201 8772 : ReleaseSysCache(tp);
13202 :
13203 8772 : return result;
13204 : }
13205 :
13206 : /*
13207 : * generate_qualified_relation_name
13208 : * Compute the name to display for a relation specified by OID
13209 : *
13210 : * As above, but unconditionally schema-qualify the name.
13211 : */
13212 : static char *
13213 10534 : generate_qualified_relation_name(Oid relid)
13214 : {
13215 : HeapTuple tp;
13216 : Form_pg_class reltup;
13217 : char *relname;
13218 : char *nspname;
13219 : char *result;
13220 :
13221 10534 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13222 10534 : if (!HeapTupleIsValid(tp))
13223 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13224 10534 : reltup = (Form_pg_class) GETSTRUCT(tp);
13225 10534 : relname = NameStr(reltup->relname);
13226 :
13227 10534 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
13228 10534 : if (!nspname)
13229 0 : elog(ERROR, "cache lookup failed for namespace %u",
13230 : reltup->relnamespace);
13231 :
13232 10534 : result = quote_qualified_identifier(nspname, relname);
13233 :
13234 10534 : ReleaseSysCache(tp);
13235 :
13236 10534 : return result;
13237 : }
13238 :
13239 : /*
13240 : * generate_function_name
13241 : * Compute the name to display for a function specified by OID,
13242 : * given that it is being called with the specified actual arg names and
13243 : * types. (Those matter because of ambiguous-function resolution rules.)
13244 : *
13245 : * If we're dealing with a potentially variadic function (in practice, this
13246 : * means a FuncExpr or Aggref, not some other way of calling a function), then
13247 : * has_variadic must specify whether variadic arguments have been merged,
13248 : * and *use_variadic_p will be set to indicate whether to print VARIADIC in
13249 : * the output. For non-FuncExpr cases, has_variadic should be false and
13250 : * use_variadic_p can be NULL.
13251 : *
13252 : * inGroupBy must be true if we're deparsing a GROUP BY clause.
13253 : *
13254 : * The result includes all necessary quoting and schema-prefixing.
13255 : */
13256 : static char *
13257 13422 : generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
13258 : bool has_variadic, bool *use_variadic_p,
13259 : bool inGroupBy)
13260 : {
13261 : char *result;
13262 : HeapTuple proctup;
13263 : Form_pg_proc procform;
13264 : char *proname;
13265 : bool use_variadic;
13266 : char *nspname;
13267 : FuncDetailCode p_result;
13268 : Oid p_funcid;
13269 : Oid p_rettype;
13270 : bool p_retset;
13271 : int p_nvargs;
13272 : Oid p_vatype;
13273 : Oid *p_true_typeids;
13274 13422 : bool force_qualify = false;
13275 :
13276 13422 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
13277 13422 : if (!HeapTupleIsValid(proctup))
13278 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
13279 13422 : procform = (Form_pg_proc) GETSTRUCT(proctup);
13280 13422 : proname = NameStr(procform->proname);
13281 :
13282 : /*
13283 : * Due to parser hacks to avoid needing to reserve CUBE, we need to force
13284 : * qualification of some function names within GROUP BY.
13285 : */
13286 13422 : if (inGroupBy)
13287 : {
13288 0 : if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
13289 0 : force_qualify = true;
13290 : }
13291 :
13292 : /*
13293 : * Determine whether VARIADIC should be printed. We must do this first
13294 : * since it affects the lookup rules in func_get_detail().
13295 : *
13296 : * We always print VARIADIC if the function has a merged variadic-array
13297 : * argument. Note that this is always the case for functions taking a
13298 : * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
13299 : * and printed the array elements as separate arguments, the call could
13300 : * match a newer non-VARIADIC function.
13301 : */
13302 13422 : if (use_variadic_p)
13303 : {
13304 : /* Parser should not have set funcvariadic unless fn is variadic */
13305 : Assert(!has_variadic || OidIsValid(procform->provariadic));
13306 11294 : use_variadic = has_variadic;
13307 11294 : *use_variadic_p = use_variadic;
13308 : }
13309 : else
13310 : {
13311 : Assert(!has_variadic);
13312 2128 : use_variadic = false;
13313 : }
13314 :
13315 : /*
13316 : * The idea here is to schema-qualify only if the parser would fail to
13317 : * resolve the correct function given the unqualified func name with the
13318 : * specified argtypes and VARIADIC flag. But if we already decided to
13319 : * force qualification, then we can skip the lookup and pretend we didn't
13320 : * find it.
13321 : */
13322 13422 : if (!force_qualify)
13323 13422 : p_result = func_get_detail(list_make1(makeString(proname)),
13324 : NIL, argnames, nargs, argtypes,
13325 13422 : !use_variadic, true, false,
13326 : &p_funcid, &p_rettype,
13327 : &p_retset, &p_nvargs, &p_vatype,
13328 13422 : &p_true_typeids, NULL);
13329 : else
13330 : {
13331 0 : p_result = FUNCDETAIL_NOTFOUND;
13332 0 : p_funcid = InvalidOid;
13333 : }
13334 :
13335 13422 : if ((p_result == FUNCDETAIL_NORMAL ||
13336 1722 : p_result == FUNCDETAIL_AGGREGATE ||
13337 11808 : p_result == FUNCDETAIL_WINDOWFUNC) &&
13338 11808 : p_funcid == funcid)
13339 11808 : nspname = NULL;
13340 : else
13341 1614 : nspname = get_namespace_name_or_temp(procform->pronamespace);
13342 :
13343 13422 : result = quote_qualified_identifier(nspname, proname);
13344 :
13345 13422 : ReleaseSysCache(proctup);
13346 :
13347 13422 : return result;
13348 : }
13349 :
13350 : /*
13351 : * generate_operator_name
13352 : * Compute the name to display for an operator specified by OID,
13353 : * given that it is being called with the specified actual arg types.
13354 : * (Arg types matter because of ambiguous-operator resolution rules.
13355 : * Pass InvalidOid for unused arg of a unary operator.)
13356 : *
13357 : * The result includes all necessary quoting and schema-prefixing,
13358 : * plus the OPERATOR() decoration needed to use a qualified operator name
13359 : * in an expression.
13360 : */
13361 : static char *
13362 65062 : generate_operator_name(Oid operid, Oid arg1, Oid arg2)
13363 : {
13364 : StringInfoData buf;
13365 : HeapTuple opertup;
13366 : Form_pg_operator operform;
13367 : char *oprname;
13368 : char *nspname;
13369 : Operator p_result;
13370 :
13371 65062 : initStringInfo(&buf);
13372 :
13373 65062 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
13374 65062 : if (!HeapTupleIsValid(opertup))
13375 0 : elog(ERROR, "cache lookup failed for operator %u", operid);
13376 65062 : operform = (Form_pg_operator) GETSTRUCT(opertup);
13377 65062 : oprname = NameStr(operform->oprname);
13378 :
13379 : /*
13380 : * The idea here is to schema-qualify only if the parser would fail to
13381 : * resolve the correct operator given the unqualified op name with the
13382 : * specified argtypes.
13383 : */
13384 65062 : switch (operform->oprkind)
13385 : {
13386 65032 : case 'b':
13387 65032 : p_result = oper(NULL, list_make1(makeString(oprname)), arg1, arg2,
13388 : true, -1);
13389 65032 : break;
13390 30 : case 'l':
13391 30 : p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2,
13392 : true, -1);
13393 30 : break;
13394 0 : default:
13395 0 : elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
13396 : p_result = NULL; /* keep compiler quiet */
13397 : break;
13398 : }
13399 :
13400 65062 : if (p_result != NULL && oprid(p_result) == operid)
13401 65046 : nspname = NULL;
13402 : else
13403 : {
13404 16 : nspname = get_namespace_name_or_temp(operform->oprnamespace);
13405 16 : appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
13406 : }
13407 :
13408 65062 : appendStringInfoString(&buf, oprname);
13409 :
13410 65062 : if (nspname)
13411 16 : appendStringInfoChar(&buf, ')');
13412 :
13413 65062 : if (p_result != NULL)
13414 65046 : ReleaseSysCache(p_result);
13415 :
13416 65062 : ReleaseSysCache(opertup);
13417 :
13418 65062 : return buf.data;
13419 : }
13420 :
13421 : /*
13422 : * generate_operator_clause --- generate a binary-operator WHERE clause
13423 : *
13424 : * This is used for internally-generated-and-executed SQL queries, where
13425 : * precision is essential and readability is secondary. The basic
13426 : * requirement is to append "leftop op rightop" to buf, where leftop and
13427 : * rightop are given as strings and are assumed to yield types leftoptype
13428 : * and rightoptype; the operator is identified by OID. The complexity
13429 : * comes from needing to be sure that the parser will select the desired
13430 : * operator when the query is parsed. We always name the operator using
13431 : * OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
13432 : * We have to emit casts too, if either input isn't already the input type
13433 : * of the operator; else we are at the mercy of the parser's heuristics for
13434 : * ambiguous-operator resolution. The caller must ensure that leftop and
13435 : * rightop are suitable arguments for a cast operation; it's best to insert
13436 : * parentheses if they aren't just variables or parameters.
13437 : */
13438 : void
13439 6358 : generate_operator_clause(StringInfo buf,
13440 : const char *leftop, Oid leftoptype,
13441 : Oid opoid,
13442 : const char *rightop, Oid rightoptype)
13443 : {
13444 : HeapTuple opertup;
13445 : Form_pg_operator operform;
13446 : char *oprname;
13447 : char *nspname;
13448 :
13449 6358 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
13450 6358 : if (!HeapTupleIsValid(opertup))
13451 0 : elog(ERROR, "cache lookup failed for operator %u", opoid);
13452 6358 : operform = (Form_pg_operator) GETSTRUCT(opertup);
13453 : Assert(operform->oprkind == 'b');
13454 6358 : oprname = NameStr(operform->oprname);
13455 :
13456 6358 : nspname = get_namespace_name(operform->oprnamespace);
13457 :
13458 6358 : appendStringInfoString(buf, leftop);
13459 6358 : if (leftoptype != operform->oprleft)
13460 880 : add_cast_to(buf, operform->oprleft);
13461 6358 : appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
13462 6358 : appendStringInfoString(buf, oprname);
13463 6358 : appendStringInfo(buf, ") %s", rightop);
13464 6358 : if (rightoptype != operform->oprright)
13465 720 : add_cast_to(buf, operform->oprright);
13466 :
13467 6358 : ReleaseSysCache(opertup);
13468 6358 : }
13469 :
13470 : /*
13471 : * Add a cast specification to buf. We spell out the type name the hard way,
13472 : * intentionally not using format_type_be(). This is to avoid corner cases
13473 : * for CHARACTER, BIT, and perhaps other types, where specifying the type
13474 : * using SQL-standard syntax results in undesirable data truncation. By
13475 : * doing it this way we can be certain that the cast will have default (-1)
13476 : * target typmod.
13477 : */
13478 : static void
13479 1600 : add_cast_to(StringInfo buf, Oid typid)
13480 : {
13481 : HeapTuple typetup;
13482 : Form_pg_type typform;
13483 : char *typname;
13484 : char *nspname;
13485 :
13486 1600 : typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
13487 1600 : if (!HeapTupleIsValid(typetup))
13488 0 : elog(ERROR, "cache lookup failed for type %u", typid);
13489 1600 : typform = (Form_pg_type) GETSTRUCT(typetup);
13490 :
13491 1600 : typname = NameStr(typform->typname);
13492 1600 : nspname = get_namespace_name_or_temp(typform->typnamespace);
13493 :
13494 1600 : appendStringInfo(buf, "::%s.%s",
13495 : quote_identifier(nspname), quote_identifier(typname));
13496 :
13497 1600 : ReleaseSysCache(typetup);
13498 1600 : }
13499 :
13500 : /*
13501 : * generate_qualified_type_name
13502 : * Compute the name to display for a type specified by OID
13503 : *
13504 : * This is different from format_type_be() in that we unconditionally
13505 : * schema-qualify the name. That also means no special syntax for
13506 : * SQL-standard type names ... although in current usage, this should
13507 : * only get used for domains, so such cases wouldn't occur anyway.
13508 : */
13509 : static char *
13510 14 : generate_qualified_type_name(Oid typid)
13511 : {
13512 : HeapTuple tp;
13513 : Form_pg_type typtup;
13514 : char *typname;
13515 : char *nspname;
13516 : char *result;
13517 :
13518 14 : tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
13519 14 : if (!HeapTupleIsValid(tp))
13520 0 : elog(ERROR, "cache lookup failed for type %u", typid);
13521 14 : typtup = (Form_pg_type) GETSTRUCT(tp);
13522 14 : typname = NameStr(typtup->typname);
13523 :
13524 14 : nspname = get_namespace_name_or_temp(typtup->typnamespace);
13525 14 : if (!nspname)
13526 0 : elog(ERROR, "cache lookup failed for namespace %u",
13527 : typtup->typnamespace);
13528 :
13529 14 : result = quote_qualified_identifier(nspname, typname);
13530 :
13531 14 : ReleaseSysCache(tp);
13532 :
13533 14 : return result;
13534 : }
13535 :
13536 : /*
13537 : * generate_collation_name
13538 : * Compute the name to display for a collation specified by OID
13539 : *
13540 : * The result includes all necessary quoting and schema-prefixing.
13541 : */
13542 : char *
13543 290 : generate_collation_name(Oid collid)
13544 : {
13545 : HeapTuple tp;
13546 : Form_pg_collation colltup;
13547 : char *collname;
13548 : char *nspname;
13549 : char *result;
13550 :
13551 290 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
13552 290 : if (!HeapTupleIsValid(tp))
13553 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
13554 290 : colltup = (Form_pg_collation) GETSTRUCT(tp);
13555 290 : collname = NameStr(colltup->collname);
13556 :
13557 290 : if (!CollationIsVisible(collid))
13558 0 : nspname = get_namespace_name_or_temp(colltup->collnamespace);
13559 : else
13560 290 : nspname = NULL;
13561 :
13562 290 : result = quote_qualified_identifier(nspname, collname);
13563 :
13564 290 : ReleaseSysCache(tp);
13565 :
13566 290 : return result;
13567 : }
13568 :
13569 : /*
13570 : * Given a C string, produce a TEXT datum.
13571 : *
13572 : * We assume that the input was palloc'd and may be freed.
13573 : */
13574 : static text *
13575 56672 : string_to_text(char *str)
13576 : {
13577 : text *result;
13578 :
13579 56672 : result = cstring_to_text(str);
13580 56672 : pfree(str);
13581 56672 : return result;
13582 : }
13583 :
13584 : /*
13585 : * Generate a C string representing a relation options from text[] datum.
13586 : */
13587 : static void
13588 370 : get_reloptions(StringInfo buf, Datum reloptions)
13589 : {
13590 : Datum *options;
13591 : int noptions;
13592 : int i;
13593 :
13594 370 : deconstruct_array_builtin(DatumGetArrayTypeP(reloptions), TEXTOID,
13595 : &options, NULL, &noptions);
13596 :
13597 772 : for (i = 0; i < noptions; i++)
13598 : {
13599 402 : char *option = TextDatumGetCString(options[i]);
13600 : char *name;
13601 : char *separator;
13602 : char *value;
13603 :
13604 : /*
13605 : * Each array element should have the form name=value. If the "=" is
13606 : * missing for some reason, treat it like an empty value.
13607 : */
13608 402 : name = option;
13609 402 : separator = strchr(option, '=');
13610 402 : if (separator)
13611 : {
13612 402 : *separator = '\0';
13613 402 : value = separator + 1;
13614 : }
13615 : else
13616 0 : value = "";
13617 :
13618 402 : if (i > 0)
13619 32 : appendStringInfoString(buf, ", ");
13620 402 : appendStringInfo(buf, "%s=", quote_identifier(name));
13621 :
13622 : /*
13623 : * In general we need to quote the value; but to avoid unnecessary
13624 : * clutter, do not quote if it is an identifier that would not need
13625 : * quoting. (We could also allow numbers, but that is a bit trickier
13626 : * than it looks --- for example, are leading zeroes significant? We
13627 : * don't want to assume very much here about what custom reloptions
13628 : * might mean.)
13629 : */
13630 402 : if (quote_identifier(value) == value)
13631 14 : appendStringInfoString(buf, value);
13632 : else
13633 388 : simple_quote_literal(buf, value);
13634 :
13635 402 : pfree(option);
13636 : }
13637 370 : }
13638 :
13639 : /*
13640 : * Generate a C string representing a relation's reloptions, or NULL if none.
13641 : */
13642 : static char *
13643 9480 : flatten_reloptions(Oid relid)
13644 : {
13645 9480 : char *result = NULL;
13646 : HeapTuple tuple;
13647 : Datum reloptions;
13648 : bool isnull;
13649 :
13650 9480 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13651 9480 : if (!HeapTupleIsValid(tuple))
13652 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13653 :
13654 9480 : reloptions = SysCacheGetAttr(RELOID, tuple,
13655 : Anum_pg_class_reloptions, &isnull);
13656 9480 : if (!isnull)
13657 : {
13658 : StringInfoData buf;
13659 :
13660 336 : initStringInfo(&buf);
13661 336 : get_reloptions(&buf, reloptions);
13662 :
13663 336 : result = buf.data;
13664 : }
13665 :
13666 9480 : ReleaseSysCache(tuple);
13667 :
13668 9480 : return result;
13669 : }
13670 :
13671 : /*
13672 : * get_range_partbound_string
13673 : * A C string representation of one range partition bound
13674 : */
13675 : char *
13676 6068 : get_range_partbound_string(List *bound_datums)
13677 : {
13678 : deparse_context context;
13679 6068 : StringInfo buf = makeStringInfo();
13680 : ListCell *cell;
13681 : char *sep;
13682 :
13683 6068 : memset(&context, 0, sizeof(deparse_context));
13684 6068 : context.buf = buf;
13685 :
13686 6068 : appendStringInfoChar(buf, '(');
13687 6068 : sep = "";
13688 13108 : foreach(cell, bound_datums)
13689 : {
13690 7040 : PartitionRangeDatum *datum =
13691 : lfirst_node(PartitionRangeDatum, cell);
13692 :
13693 7040 : appendStringInfoString(buf, sep);
13694 7040 : if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
13695 222 : appendStringInfoString(buf, "MINVALUE");
13696 6818 : else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
13697 120 : appendStringInfoString(buf, "MAXVALUE");
13698 : else
13699 : {
13700 6698 : Const *val = castNode(Const, datum->value);
13701 :
13702 6698 : get_const_expr(val, &context, -1);
13703 : }
13704 7040 : sep = ", ";
13705 : }
13706 6068 : appendStringInfoChar(buf, ')');
13707 :
13708 6068 : return buf->data;
13709 : }
|