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-2026, 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_propgraph_element.h"
38 : #include "catalog/pg_propgraph_element_label.h"
39 : #include "catalog/pg_propgraph_label.h"
40 : #include "catalog/pg_propgraph_label_property.h"
41 : #include "catalog/pg_propgraph_property.h"
42 : #include "catalog/pg_statistic_ext.h"
43 : #include "catalog/pg_trigger.h"
44 : #include "catalog/pg_type.h"
45 : #include "commands/defrem.h"
46 : #include "commands/tablespace.h"
47 : #include "common/keywords.h"
48 : #include "executor/spi.h"
49 : #include "funcapi.h"
50 : #include "mb/pg_wchar.h"
51 : #include "miscadmin.h"
52 : #include "nodes/makefuncs.h"
53 : #include "nodes/nodeFuncs.h"
54 : #include "nodes/pathnodes.h"
55 : #include "optimizer/optimizer.h"
56 : #include "parser/parse_agg.h"
57 : #include "parser/parse_func.h"
58 : #include "parser/parse_oper.h"
59 : #include "parser/parse_relation.h"
60 : #include "parser/parser.h"
61 : #include "parser/parsetree.h"
62 : #include "rewrite/rewriteHandler.h"
63 : #include "rewrite/rewriteManip.h"
64 : #include "rewrite/rewriteSupport.h"
65 : #include "utils/array.h"
66 : #include "utils/builtins.h"
67 : #include "utils/fmgroids.h"
68 : #include "utils/guc.h"
69 : #include "utils/hsearch.h"
70 : #include "utils/lsyscache.h"
71 : #include "utils/partcache.h"
72 : #include "utils/rel.h"
73 : #include "utils/ruleutils.h"
74 : #include "utils/snapmgr.h"
75 : #include "utils/syscache.h"
76 : #include "utils/typcache.h"
77 : #include "utils/varlena.h"
78 : #include "utils/xml.h"
79 :
80 : /* ----------
81 : * Pretty formatting constants
82 : * ----------
83 : */
84 :
85 : /* Indent counts */
86 : #define PRETTYINDENT_STD 8
87 : #define PRETTYINDENT_JOIN 4
88 : #define PRETTYINDENT_VAR 4
89 :
90 : #define PRETTYINDENT_LIMIT 40 /* wrap limit */
91 :
92 : /* Pretty flags */
93 : #define PRETTYFLAG_PAREN 0x0001
94 : #define PRETTYFLAG_INDENT 0x0002
95 : #define PRETTYFLAG_SCHEMA 0x0004
96 :
97 : /* Standard conversion of a "bool pretty" option to detailed flags */
98 : #define GET_PRETTY_FLAGS(pretty) \
99 : ((pretty) ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) \
100 : : PRETTYFLAG_INDENT)
101 :
102 : /* Default line length for pretty-print wrapping: 0 means wrap always */
103 : #define WRAP_COLUMN_DEFAULT 0
104 :
105 : /* macros to test if pretty action needed */
106 : #define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
107 : #define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
108 : #define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
109 :
110 :
111 : /* ----------
112 : * Local data types
113 : * ----------
114 : */
115 :
116 : /* Context info needed for invoking a recursive querytree display routine */
117 : typedef struct
118 : {
119 : StringInfo buf; /* output buffer to append to */
120 : List *namespaces; /* List of deparse_namespace nodes */
121 : TupleDesc resultDesc; /* if top level of a view, the view's tupdesc */
122 : List *targetList; /* Current query level's SELECT targetlist */
123 : List *windowClause; /* Current query level's WINDOW clause */
124 : int prettyFlags; /* enabling of pretty-print functions */
125 : int wrapColumn; /* max line length, or -1 for no limit */
126 : int indentLevel; /* current indent level for pretty-print */
127 : bool varprefix; /* true to print prefixes on Vars */
128 : bool colNamesVisible; /* do we care about output column names? */
129 : bool inGroupBy; /* deparsing GROUP BY clause? */
130 : bool varInOrderBy; /* deparsing simple Var in ORDER BY? */
131 : Bitmapset *appendparents; /* if not null, map child Vars of these relids
132 : * back to the parent rel */
133 : } deparse_context;
134 :
135 : /*
136 : * Each level of query context around a subtree needs a level of Var namespace.
137 : * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
138 : * the current context's namespaces list.
139 : *
140 : * rtable is the list of actual RTEs from the Query or PlannedStmt.
141 : * rtable_names holds the alias name to be used for each RTE (either a C
142 : * string, or NULL for nameless RTEs such as unnamed joins).
143 : * rtable_columns holds the column alias names to be used for each RTE.
144 : *
145 : * subplans is a list of Plan trees for SubPlans and CTEs (it's only used
146 : * in the PlannedStmt case).
147 : * ctes is a list of CommonTableExpr nodes (only used in the Query case).
148 : * appendrels, if not null (it's only used in the PlannedStmt case), is an
149 : * array of AppendRelInfo nodes, indexed by child relid. We use that to map
150 : * child-table Vars to their inheritance parents.
151 : *
152 : * In some cases we need to make names of merged JOIN USING columns unique
153 : * across the whole query, not only per-RTE. If so, unique_using is true
154 : * and using_names is a list of C strings representing names already assigned
155 : * to USING columns.
156 : *
157 : * When deparsing plan trees, there is always just a single item in the
158 : * deparse_namespace list (since a plan tree never contains Vars with
159 : * varlevelsup > 0). We store the Plan node that is the immediate
160 : * parent of the expression to be deparsed, as well as a list of that
161 : * Plan's ancestors. In addition, we store its outer and inner subplan nodes,
162 : * as well as their targetlists, and the index tlist if the current plan node
163 : * might contain INDEX_VAR Vars. (These fields could be derived on-the-fly
164 : * from the current Plan node, but it seems notationally clearer to set them
165 : * up as separate fields.)
166 : */
167 : typedef struct
168 : {
169 : List *rtable; /* List of RangeTblEntry nodes */
170 : List *rtable_names; /* Parallel list of names for RTEs */
171 : List *rtable_columns; /* Parallel list of deparse_columns structs */
172 : List *subplans; /* List of Plan trees for SubPlans */
173 : List *ctes; /* List of CommonTableExpr nodes */
174 : AppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */
175 : char *ret_old_alias; /* alias for OLD in RETURNING list */
176 : char *ret_new_alias; /* alias for NEW in RETURNING list */
177 : /* Workspace for column alias assignment: */
178 : bool unique_using; /* Are we making USING names globally unique */
179 : List *using_names; /* List of assigned names for USING columns */
180 : /* Remaining fields are used only when deparsing a Plan tree: */
181 : Plan *plan; /* immediate parent of current expression */
182 : List *ancestors; /* ancestors of plan */
183 : Plan *outer_plan; /* outer subnode, or NULL if none */
184 : Plan *inner_plan; /* inner subnode, or NULL if none */
185 : List *outer_tlist; /* referent for OUTER_VAR Vars */
186 : List *inner_tlist; /* referent for INNER_VAR Vars */
187 : List *index_tlist; /* referent for INDEX_VAR Vars */
188 : /* Special namespace representing a function signature: */
189 : char *funcname;
190 : int numargs;
191 : char **argnames;
192 : } deparse_namespace;
193 :
194 : /*
195 : * Per-relation data about column alias names.
196 : *
197 : * Selecting aliases is unreasonably complicated because of the need to dump
198 : * rules/views whose underlying tables may have had columns added, deleted, or
199 : * renamed since the query was parsed. We must nonetheless print the rule/view
200 : * in a form that can be reloaded and will produce the same results as before.
201 : *
202 : * For each RTE used in the query, we must assign column aliases that are
203 : * unique within that RTE. SQL does not require this of the original query,
204 : * but due to factors such as *-expansion we need to be able to uniquely
205 : * reference every column in a decompiled query. As long as we qualify all
206 : * column references, per-RTE uniqueness is sufficient for that.
207 : *
208 : * However, we can't ensure per-column name uniqueness for unnamed join RTEs,
209 : * since they just inherit column names from their input RTEs, and we can't
210 : * rename the columns at the join level. Most of the time this isn't an issue
211 : * because we don't need to reference the join's output columns as such; we
212 : * can reference the input columns instead. That approach can fail for merged
213 : * JOIN USING columns, however, so when we have one of those in an unnamed
214 : * join, we have to make that column's alias globally unique across the whole
215 : * query to ensure it can be referenced unambiguously.
216 : *
217 : * Another problem is that a JOIN USING clause requires the columns to be
218 : * merged to have the same aliases in both input RTEs, and that no other
219 : * columns in those RTEs or their children conflict with the USING names.
220 : * To handle that, we do USING-column alias assignment in a recursive
221 : * traversal of the query's jointree. When descending through a JOIN with
222 : * USING, we preassign the USING column names to the child columns, overriding
223 : * other rules for column alias assignment. We also mark each RTE with a list
224 : * of all USING column names selected for joins containing that RTE, so that
225 : * when we assign other columns' aliases later, we can avoid conflicts.
226 : *
227 : * Another problem is that if a JOIN's input tables have had columns added or
228 : * deleted since the query was parsed, we must generate a column alias list
229 : * for the join that matches the current set of input columns --- otherwise, a
230 : * change in the number of columns in the left input would throw off matching
231 : * of aliases to columns of the right input. Thus, positions in the printable
232 : * column alias list are not necessarily one-for-one with varattnos of the
233 : * JOIN, so we need a separate new_colnames[] array for printing purposes.
234 : *
235 : * Finally, when dealing with wide tables we risk O(N^2) costs in assigning
236 : * non-duplicate column names. We ameliorate that by using a hash table that
237 : * holds all the strings appearing in colnames, new_colnames, and parentUsing.
238 : */
239 : typedef struct
240 : {
241 : /*
242 : * colnames is an array containing column aliases to use for columns that
243 : * existed when the query was parsed. Dropped columns have NULL entries.
244 : * This array can be directly indexed by varattno to get a Var's name.
245 : *
246 : * Non-NULL entries are guaranteed unique within the RTE, *except* when
247 : * this is for an unnamed JOIN RTE. In that case we merely copy up names
248 : * from the two input RTEs.
249 : *
250 : * During the recursive descent in set_using_names(), forcible assignment
251 : * of a child RTE's column name is represented by pre-setting that element
252 : * of the child's colnames array. So at that stage, NULL entries in this
253 : * array just mean that no name has been preassigned, not necessarily that
254 : * the column is dropped.
255 : */
256 : int num_cols; /* length of colnames[] array */
257 : char **colnames; /* array of C strings and NULLs */
258 :
259 : /*
260 : * new_colnames is an array containing column aliases to use for columns
261 : * that would exist if the query was re-parsed against the current
262 : * definitions of its base tables. This is what to print as the column
263 : * alias list for the RTE. This array does not include dropped columns,
264 : * but it will include columns added since original parsing. Indexes in
265 : * it therefore have little to do with current varattno values. As above,
266 : * entries are unique unless this is for an unnamed JOIN RTE. (In such an
267 : * RTE, we never actually print this array, but we must compute it anyway
268 : * for possible use in computing column names of upper joins.) The
269 : * parallel array is_new_col marks which of these columns are new since
270 : * original parsing. Entries with is_new_col false must match the
271 : * non-NULL colnames entries one-for-one.
272 : */
273 : int num_new_cols; /* length of new_colnames[] array */
274 : char **new_colnames; /* array of C strings */
275 : bool *is_new_col; /* array of bool flags */
276 :
277 : /* This flag tells whether we should actually print a column alias list */
278 : bool printaliases;
279 :
280 : /* This list has all names used as USING names in joins above this RTE */
281 : List *parentUsing; /* names assigned to parent merged columns */
282 :
283 : /*
284 : * If this struct is for a JOIN RTE, we fill these fields during the
285 : * set_using_names() pass to describe its relationship to its child RTEs.
286 : *
287 : * leftattnos and rightattnos are arrays with one entry per existing
288 : * output column of the join (hence, indexable by join varattno). For a
289 : * simple reference to a column of the left child, leftattnos[i] is the
290 : * child RTE's attno and rightattnos[i] is zero; and conversely for a
291 : * column of the right child. But for merged columns produced by JOIN
292 : * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.
293 : * Note that a simple reference might be to a child RTE column that's been
294 : * dropped; but that's OK since the column could not be used in the query.
295 : *
296 : * If it's a JOIN USING, usingNames holds the alias names selected for the
297 : * merged columns (these might be different from the original USING list,
298 : * if we had to modify names to achieve uniqueness).
299 : */
300 : int leftrti; /* rangetable index of left child */
301 : int rightrti; /* rangetable index of right child */
302 : int *leftattnos; /* left-child varattnos of join cols, or 0 */
303 : int *rightattnos; /* right-child varattnos of join cols, or 0 */
304 : List *usingNames; /* names assigned to merged columns */
305 :
306 : /*
307 : * Hash table holding copies of all the strings appearing in this struct's
308 : * colnames, new_colnames, and parentUsing. We use a hash table only for
309 : * sufficiently wide relations, and only during the colname-assignment
310 : * functions set_relation_column_names and set_join_column_names;
311 : * otherwise, names_hash is NULL.
312 : */
313 : HTAB *names_hash; /* entries are just strings */
314 : } deparse_columns;
315 :
316 : /* This macro is analogous to rt_fetch(), but for deparse_columns structs */
317 : #define deparse_columns_fetch(rangetable_index, dpns) \
318 : ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
319 :
320 : /*
321 : * Entry in set_rtable_names' hash table
322 : */
323 : typedef struct
324 : {
325 : char name[NAMEDATALEN]; /* Hash key --- must be first */
326 : int counter; /* Largest addition used so far for name */
327 : } NameHashEntry;
328 :
329 : /* Callback signature for resolve_special_varno() */
330 : typedef void (*rsv_callback) (Node *node, deparse_context *context,
331 : void *callback_arg);
332 :
333 :
334 : /* ----------
335 : * Global data
336 : * ----------
337 : */
338 : static SPIPlanPtr plan_getrulebyoid = NULL;
339 : static const char *const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
340 : static SPIPlanPtr plan_getviewrule = NULL;
341 : static const char *const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
342 :
343 : /* GUC parameters */
344 : bool quote_all_identifiers = false;
345 :
346 :
347 : /* ----------
348 : * Local functions
349 : *
350 : * Most of these functions used to use fixed-size buffers to build their
351 : * results. Now, they take an (already initialized) StringInfo object
352 : * as a parameter, and append their text output to its contents.
353 : * ----------
354 : */
355 : static char *deparse_expression_pretty(Node *expr, List *dpcontext,
356 : bool forceprefix, bool showimplicit,
357 : int prettyFlags, int startIndent);
358 : static char *pg_get_viewdef_worker(Oid viewoid,
359 : int prettyFlags, int wrapColumn);
360 : static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
361 : static int decompile_column_index_array(Datum column_index_array, Oid relId,
362 : bool withPeriod, StringInfo buf);
363 : static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
364 : static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
365 : const Oid *excludeOps,
366 : bool attrsOnly, bool keysOnly,
367 : bool showTblSpc, bool inherits,
368 : int prettyFlags, bool missing_ok);
369 : static void make_propgraphdef_elements(StringInfo buf, Oid pgrelid, char pgekind);
370 : static void make_propgraphdef_labels(StringInfo buf, Oid elid, const char *elalias, Oid elrelid);
371 : static void make_propgraphdef_properties(StringInfo buf, Oid ellabelid, Oid elrelid);
372 : static char *pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
373 : bool missing_ok);
374 : static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
375 : bool attrsOnly, bool missing_ok);
376 : static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
377 : int prettyFlags, bool missing_ok);
378 : static text *pg_get_expr_worker(text *expr, Oid relid, int prettyFlags);
379 : static int print_function_arguments(StringInfo buf, HeapTuple proctup,
380 : bool print_table_args, bool print_defaults);
381 : static void print_function_rettype(StringInfo buf, HeapTuple proctup);
382 : static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
383 : static void print_function_sqlbody(StringInfo buf, HeapTuple proctup);
384 : static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
385 : Bitmapset *rels_used);
386 : static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
387 : List *parent_namespaces);
388 : static void set_simple_column_names(deparse_namespace *dpns);
389 : static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
390 : static void set_using_names(deparse_namespace *dpns, Node *jtnode,
391 : List *parentUsing);
392 : static void set_relation_column_names(deparse_namespace *dpns,
393 : RangeTblEntry *rte,
394 : deparse_columns *colinfo);
395 : static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
396 : deparse_columns *colinfo);
397 : static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
398 : deparse_columns *colinfo);
399 : static char *make_colname_unique(char *colname, deparse_namespace *dpns,
400 : deparse_columns *colinfo);
401 : static void expand_colnames_array_to(deparse_columns *colinfo, int n);
402 : static void build_colinfo_names_hash(deparse_columns *colinfo);
403 : static void add_to_names_hash(deparse_columns *colinfo, const char *name);
404 : static void destroy_colinfo_names_hash(deparse_columns *colinfo);
405 : static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
406 : deparse_columns *colinfo);
407 : static char *get_rtable_name(int rtindex, deparse_context *context);
408 : static void set_deparse_plan(deparse_namespace *dpns, Plan *plan);
409 : static Plan *find_recursive_union(deparse_namespace *dpns,
410 : WorkTableScan *wtscan);
411 : static void push_child_plan(deparse_namespace *dpns, Plan *plan,
412 : deparse_namespace *save_dpns);
413 : static void pop_child_plan(deparse_namespace *dpns,
414 : deparse_namespace *save_dpns);
415 : static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
416 : deparse_namespace *save_dpns);
417 : static void pop_ancestor_plan(deparse_namespace *dpns,
418 : deparse_namespace *save_dpns);
419 : static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
420 : int prettyFlags);
421 : static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
422 : int prettyFlags, int wrapColumn);
423 : static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
424 : TupleDesc resultDesc, bool colNamesVisible,
425 : int prettyFlags, int wrapColumn, int startIndent);
426 : static void get_values_def(List *values_lists, deparse_context *context);
427 : static void get_with_clause(Query *query, deparse_context *context);
428 : static void get_select_query_def(Query *query, deparse_context *context);
429 : static void get_insert_query_def(Query *query, deparse_context *context);
430 : static void get_update_query_def(Query *query, deparse_context *context);
431 : static void get_update_query_targetlist_def(Query *query, List *targetList,
432 : deparse_context *context,
433 : RangeTblEntry *rte);
434 : static void get_delete_query_def(Query *query, deparse_context *context);
435 : static void get_merge_query_def(Query *query, deparse_context *context);
436 : static void get_utility_query_def(Query *query, deparse_context *context);
437 : static char *get_lock_clause_strength(LockClauseStrength strength);
438 : static void get_basic_select_query(Query *query, deparse_context *context);
439 : static void get_target_list(List *targetList, deparse_context *context);
440 : static void get_returning_clause(Query *query, deparse_context *context);
441 : static void get_setop_query(Node *setOp, Query *query,
442 : deparse_context *context);
443 : static Node *get_rule_sortgroupclause(Index ref, List *tlist,
444 : bool force_colno,
445 : deparse_context *context);
446 : static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
447 : bool omit_parens, deparse_context *context);
448 : static void get_rule_orderby(List *orderList, List *targetList,
449 : bool force_colno, deparse_context *context);
450 : static void get_rule_windowclause(Query *query, deparse_context *context);
451 : static void get_rule_windowspec(WindowClause *wc, List *targetList,
452 : deparse_context *context);
453 : static void get_window_frame_options(int frameOptions,
454 : Node *startOffset, Node *endOffset,
455 : deparse_context *context);
456 : static char *get_variable(Var *var, int levelsup, bool istoplevel,
457 : deparse_context *context);
458 : static void get_special_variable(Node *node, deparse_context *context,
459 : void *callback_arg);
460 : static void resolve_special_varno(Node *node, deparse_context *context,
461 : rsv_callback callback, void *callback_arg);
462 : static Node *find_param_referent(Param *param, deparse_context *context,
463 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
464 : static SubPlan *find_param_generator(Param *param, deparse_context *context,
465 : int *column_p);
466 : static SubPlan *find_param_generator_initplan(Param *param, Plan *plan,
467 : int *column_p);
468 : static void get_parameter(Param *param, deparse_context *context);
469 : static const char *get_simple_binary_op_name(OpExpr *expr);
470 : static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
471 : static void appendContextKeyword(deparse_context *context, const char *str,
472 : int indentBefore, int indentAfter, int indentPlus);
473 : static void removeStringInfoSpaces(StringInfo str);
474 : static void get_rule_expr(Node *node, deparse_context *context,
475 : bool showimplicit);
476 : static void get_rule_expr_toplevel(Node *node, deparse_context *context,
477 : bool showimplicit);
478 : static void get_rule_list_toplevel(List *lst, deparse_context *context,
479 : bool showimplicit);
480 : static void get_rule_expr_funccall(Node *node, deparse_context *context,
481 : bool showimplicit);
482 : static bool looks_like_function(Node *node);
483 : static void get_oper_expr(OpExpr *expr, deparse_context *context);
484 : static void get_func_expr(FuncExpr *expr, deparse_context *context,
485 : bool showimplicit);
486 : static void get_agg_expr(Aggref *aggref, deparse_context *context,
487 : Aggref *original_aggref);
488 : static void get_agg_expr_helper(Aggref *aggref, deparse_context *context,
489 : Aggref *original_aggref, const char *funcname,
490 : const char *options, bool is_json_objectagg);
491 : static void get_agg_combine_expr(Node *node, deparse_context *context,
492 : void *callback_arg);
493 : static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
494 : static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
495 : const char *funcname, const char *options,
496 : bool is_json_objectagg);
497 : static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
498 : static void get_coercion_expr(Node *arg, deparse_context *context,
499 : Oid resulttype, int32 resulttypmod,
500 : Node *parentNode);
501 : static void get_const_expr(Const *constval, deparse_context *context,
502 : int showtype);
503 : static void get_const_collation(Const *constval, deparse_context *context);
504 : static void get_json_format(JsonFormat *format, StringInfo buf);
505 : static void get_json_returning(JsonReturning *returning, StringInfo buf,
506 : bool json_format_by_default);
507 : static void get_json_constructor(JsonConstructorExpr *ctor,
508 : deparse_context *context, bool showimplicit);
509 : static void get_json_constructor_options(JsonConstructorExpr *ctor,
510 : StringInfo buf);
511 : static void get_json_agg_constructor(JsonConstructorExpr *ctor,
512 : deparse_context *context,
513 : const char *funcname,
514 : bool is_json_objectagg);
515 : static void simple_quote_literal(StringInfo buf, const char *val);
516 : static void get_sublink_expr(SubLink *sublink, deparse_context *context);
517 : static void get_tablefunc(TableFunc *tf, deparse_context *context,
518 : bool showimplicit);
519 : static void get_from_clause(Query *query, const char *prefix,
520 : deparse_context *context);
521 : static void get_from_clause_item(Node *jtnode, Query *query,
522 : deparse_context *context);
523 : static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
524 : deparse_context *context);
525 : static void get_column_alias_list(deparse_columns *colinfo,
526 : deparse_context *context);
527 : static void get_for_portion_of(ForPortionOfExpr *forPortionOf,
528 : deparse_context *context);
529 : static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
530 : deparse_columns *colinfo,
531 : deparse_context *context);
532 : static void get_tablesample_def(TableSampleClause *tablesample,
533 : deparse_context *context);
534 : static void get_opclass_name(Oid opclass, Oid actual_datatype,
535 : StringInfo buf);
536 : static Node *processIndirection(Node *node, deparse_context *context);
537 : static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
538 : static char *get_relation_name(Oid relid);
539 : static char *generate_relation_name(Oid relid, List *namespaces);
540 : static char *generate_qualified_relation_name(Oid relid);
541 : static char *generate_function_name(Oid funcid, int nargs,
542 : List *argnames, Oid *argtypes,
543 : bool has_variadic, bool *use_variadic_p,
544 : bool inGroupBy);
545 : static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
546 : static void add_cast_to(StringInfo buf, Oid typid);
547 : static char *generate_qualified_type_name(Oid typid);
548 : static text *string_to_text(char *str);
549 : static char *flatten_reloptions(Oid relid);
550 : void get_reloptions(StringInfo buf, Datum reloptions);
551 : static void get_json_path_spec(Node *path_spec, deparse_context *context,
552 : bool showimplicit);
553 : static void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
554 : deparse_context *context,
555 : bool showimplicit);
556 : static void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
557 : deparse_context *context,
558 : bool showimplicit,
559 : bool needcomma);
560 :
561 : #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
562 :
563 :
564 : /* ----------
565 : * pg_get_ruledef - Do it all and return a text
566 : * that could be used as a statement
567 : * to recreate the rule
568 : * ----------
569 : */
570 : Datum
571 237 : pg_get_ruledef(PG_FUNCTION_ARGS)
572 : {
573 237 : Oid ruleoid = PG_GETARG_OID(0);
574 : int prettyFlags;
575 : char *res;
576 :
577 237 : prettyFlags = PRETTYFLAG_INDENT;
578 :
579 237 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
580 :
581 237 : if (res == NULL)
582 4 : PG_RETURN_NULL();
583 :
584 233 : PG_RETURN_TEXT_P(string_to_text(res));
585 : }
586 :
587 :
588 : Datum
589 76 : pg_get_ruledef_ext(PG_FUNCTION_ARGS)
590 : {
591 76 : Oid ruleoid = PG_GETARG_OID(0);
592 76 : bool pretty = PG_GETARG_BOOL(1);
593 : int prettyFlags;
594 : char *res;
595 :
596 76 : prettyFlags = GET_PRETTY_FLAGS(pretty);
597 :
598 76 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
599 :
600 76 : if (res == NULL)
601 0 : PG_RETURN_NULL();
602 :
603 76 : PG_RETURN_TEXT_P(string_to_text(res));
604 : }
605 :
606 :
607 : static char *
608 313 : pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
609 : {
610 : Datum args[1];
611 : char nulls[1];
612 : int spirc;
613 : HeapTuple ruletup;
614 : TupleDesc rulettc;
615 : StringInfoData buf;
616 :
617 : /*
618 : * Do this first so that string is alloc'd in outer context not SPI's.
619 : */
620 313 : initStringInfo(&buf);
621 :
622 : /*
623 : * Connect to SPI manager
624 : */
625 313 : SPI_connect();
626 :
627 : /*
628 : * On the first call prepare the plan to lookup pg_rewrite. We read
629 : * pg_rewrite over the SPI manager instead of using the syscache to be
630 : * checked for read access on pg_rewrite.
631 : */
632 313 : if (plan_getrulebyoid == NULL)
633 : {
634 : Oid argtypes[1];
635 : SPIPlanPtr plan;
636 :
637 24 : argtypes[0] = OIDOID;
638 24 : plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
639 24 : if (plan == NULL)
640 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
641 24 : SPI_keepplan(plan);
642 24 : plan_getrulebyoid = plan;
643 : }
644 :
645 : /*
646 : * Get the pg_rewrite tuple for this rule
647 : */
648 313 : args[0] = ObjectIdGetDatum(ruleoid);
649 313 : nulls[0] = ' ';
650 313 : spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
651 313 : if (spirc != SPI_OK_SELECT)
652 0 : elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
653 313 : if (SPI_processed != 1)
654 : {
655 : /*
656 : * There is no tuple data available here, just keep the output buffer
657 : * empty.
658 : */
659 : }
660 : else
661 : {
662 : /*
663 : * Get the rule's definition and put it into executor's memory
664 : */
665 309 : ruletup = SPI_tuptable->vals[0];
666 309 : rulettc = SPI_tuptable->tupdesc;
667 309 : make_ruledef(&buf, ruletup, rulettc, prettyFlags);
668 : }
669 :
670 : /*
671 : * Disconnect from SPI manager
672 : */
673 313 : if (SPI_finish() != SPI_OK_FINISH)
674 0 : elog(ERROR, "SPI_finish failed");
675 :
676 313 : if (buf.len == 0)
677 4 : return NULL;
678 :
679 309 : return buf.data;
680 : }
681 :
682 :
683 : /* ----------
684 : * pg_get_viewdef - Mainly the same thing, but we
685 : * only return the SELECT part of a view
686 : * ----------
687 : */
688 : Datum
689 1419 : pg_get_viewdef(PG_FUNCTION_ARGS)
690 : {
691 : /* By OID */
692 1419 : Oid viewoid = PG_GETARG_OID(0);
693 : int prettyFlags;
694 : char *res;
695 :
696 1419 : prettyFlags = PRETTYFLAG_INDENT;
697 :
698 1419 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
699 :
700 1419 : if (res == NULL)
701 4 : PG_RETURN_NULL();
702 :
703 1415 : PG_RETURN_TEXT_P(string_to_text(res));
704 : }
705 :
706 :
707 : Datum
708 379 : pg_get_viewdef_ext(PG_FUNCTION_ARGS)
709 : {
710 : /* By OID */
711 379 : Oid viewoid = PG_GETARG_OID(0);
712 379 : bool pretty = PG_GETARG_BOOL(1);
713 : int prettyFlags;
714 : char *res;
715 :
716 379 : prettyFlags = GET_PRETTY_FLAGS(pretty);
717 :
718 379 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
719 :
720 379 : if (res == NULL)
721 0 : PG_RETURN_NULL();
722 :
723 379 : PG_RETURN_TEXT_P(string_to_text(res));
724 : }
725 :
726 : Datum
727 4 : pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
728 : {
729 : /* By OID */
730 4 : Oid viewoid = PG_GETARG_OID(0);
731 4 : int wrap = PG_GETARG_INT32(1);
732 : int prettyFlags;
733 : char *res;
734 :
735 : /* calling this implies we want pretty printing */
736 4 : prettyFlags = GET_PRETTY_FLAGS(true);
737 :
738 4 : res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
739 :
740 4 : if (res == NULL)
741 0 : PG_RETURN_NULL();
742 :
743 4 : PG_RETURN_TEXT_P(string_to_text(res));
744 : }
745 :
746 : Datum
747 52 : pg_get_viewdef_name(PG_FUNCTION_ARGS)
748 : {
749 : /* By qualified name */
750 52 : text *viewname = PG_GETARG_TEXT_PP(0);
751 : int prettyFlags;
752 : RangeVar *viewrel;
753 : Oid viewoid;
754 : char *res;
755 :
756 52 : prettyFlags = PRETTYFLAG_INDENT;
757 :
758 : /* Look up view name. Can't lock it - we might not have privileges. */
759 52 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
760 52 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
761 :
762 52 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
763 :
764 52 : if (res == NULL)
765 0 : PG_RETURN_NULL();
766 :
767 52 : PG_RETURN_TEXT_P(string_to_text(res));
768 : }
769 :
770 :
771 : Datum
772 268 : pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
773 : {
774 : /* By qualified name */
775 268 : text *viewname = PG_GETARG_TEXT_PP(0);
776 268 : bool pretty = PG_GETARG_BOOL(1);
777 : int prettyFlags;
778 : RangeVar *viewrel;
779 : Oid viewoid;
780 : char *res;
781 :
782 268 : prettyFlags = GET_PRETTY_FLAGS(pretty);
783 :
784 : /* Look up view name. Can't lock it - we might not have privileges. */
785 268 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
786 268 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
787 :
788 268 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
789 :
790 268 : if (res == NULL)
791 0 : PG_RETURN_NULL();
792 :
793 268 : PG_RETURN_TEXT_P(string_to_text(res));
794 : }
795 :
796 : /*
797 : * Common code for by-OID and by-name variants of pg_get_viewdef
798 : */
799 : static char *
800 2122 : pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
801 : {
802 : Datum args[2];
803 : char nulls[2];
804 : int spirc;
805 : HeapTuple ruletup;
806 : TupleDesc rulettc;
807 : StringInfoData buf;
808 :
809 : /*
810 : * Do this first so that string is alloc'd in outer context not SPI's.
811 : */
812 2122 : initStringInfo(&buf);
813 :
814 : /*
815 : * Connect to SPI manager
816 : */
817 2122 : SPI_connect();
818 :
819 : /*
820 : * On the first call prepare the plan to lookup pg_rewrite. We read
821 : * pg_rewrite over the SPI manager instead of using the syscache to be
822 : * checked for read access on pg_rewrite.
823 : */
824 2122 : if (plan_getviewrule == NULL)
825 : {
826 : Oid argtypes[2];
827 : SPIPlanPtr plan;
828 :
829 153 : argtypes[0] = OIDOID;
830 153 : argtypes[1] = NAMEOID;
831 153 : plan = SPI_prepare(query_getviewrule, 2, argtypes);
832 153 : if (plan == NULL)
833 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
834 153 : SPI_keepplan(plan);
835 153 : plan_getviewrule = plan;
836 : }
837 :
838 : /*
839 : * Get the pg_rewrite tuple for the view's SELECT rule
840 : */
841 2122 : args[0] = ObjectIdGetDatum(viewoid);
842 2122 : args[1] = DirectFunctionCall1(namein, CStringGetDatum(ViewSelectRuleName));
843 2122 : nulls[0] = ' ';
844 2122 : nulls[1] = ' ';
845 2122 : spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
846 2122 : if (spirc != SPI_OK_SELECT)
847 0 : elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
848 2122 : if (SPI_processed != 1)
849 : {
850 : /*
851 : * There is no tuple data available here, just keep the output buffer
852 : * empty.
853 : */
854 : }
855 : else
856 : {
857 : /*
858 : * Get the rule's definition and put it into executor's memory
859 : */
860 2118 : ruletup = SPI_tuptable->vals[0];
861 2118 : rulettc = SPI_tuptable->tupdesc;
862 2118 : make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
863 : }
864 :
865 : /*
866 : * Disconnect from SPI manager
867 : */
868 2122 : if (SPI_finish() != SPI_OK_FINISH)
869 0 : elog(ERROR, "SPI_finish failed");
870 :
871 2122 : if (buf.len == 0)
872 4 : return NULL;
873 :
874 2118 : return buf.data;
875 : }
876 :
877 : /* ----------
878 : * pg_get_triggerdef - Get the definition of a trigger
879 : * ----------
880 : */
881 : Datum
882 106 : pg_get_triggerdef(PG_FUNCTION_ARGS)
883 : {
884 106 : Oid trigid = PG_GETARG_OID(0);
885 : char *res;
886 :
887 106 : res = pg_get_triggerdef_worker(trigid, false);
888 :
889 106 : if (res == NULL)
890 4 : PG_RETURN_NULL();
891 :
892 102 : PG_RETURN_TEXT_P(string_to_text(res));
893 : }
894 :
895 : Datum
896 643 : pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
897 : {
898 643 : Oid trigid = PG_GETARG_OID(0);
899 643 : bool pretty = PG_GETARG_BOOL(1);
900 : char *res;
901 :
902 643 : res = pg_get_triggerdef_worker(trigid, pretty);
903 :
904 643 : if (res == NULL)
905 0 : PG_RETURN_NULL();
906 :
907 643 : PG_RETURN_TEXT_P(string_to_text(res));
908 : }
909 :
910 : static char *
911 749 : pg_get_triggerdef_worker(Oid trigid, bool pretty)
912 : {
913 : HeapTuple ht_trig;
914 : Form_pg_trigger trigrec;
915 : StringInfoData buf;
916 : Relation tgrel;
917 : ScanKeyData skey[1];
918 : SysScanDesc tgscan;
919 749 : int findx = 0;
920 : char *tgname;
921 : char *tgoldtable;
922 : char *tgnewtable;
923 : Datum value;
924 : bool isnull;
925 :
926 : /*
927 : * Fetch the pg_trigger tuple by the Oid of the trigger
928 : */
929 749 : tgrel = table_open(TriggerRelationId, AccessShareLock);
930 :
931 749 : ScanKeyInit(&skey[0],
932 : Anum_pg_trigger_oid,
933 : BTEqualStrategyNumber, F_OIDEQ,
934 : ObjectIdGetDatum(trigid));
935 :
936 749 : tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
937 : NULL, 1, skey);
938 :
939 749 : ht_trig = systable_getnext(tgscan);
940 :
941 749 : if (!HeapTupleIsValid(ht_trig))
942 : {
943 4 : systable_endscan(tgscan);
944 4 : table_close(tgrel, AccessShareLock);
945 4 : return NULL;
946 : }
947 :
948 745 : trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
949 :
950 : /*
951 : * Start the trigger definition. Note that the trigger's name should never
952 : * be schema-qualified, but the trigger rel's name may be.
953 : */
954 745 : initStringInfo(&buf);
955 :
956 745 : tgname = NameStr(trigrec->tgname);
957 1490 : appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
958 745 : OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
959 : quote_identifier(tgname));
960 :
961 745 : if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
962 284 : appendStringInfoString(&buf, "BEFORE");
963 461 : else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
964 445 : appendStringInfoString(&buf, "AFTER");
965 16 : else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
966 16 : appendStringInfoString(&buf, "INSTEAD OF");
967 : else
968 0 : elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
969 :
970 745 : if (TRIGGER_FOR_INSERT(trigrec->tgtype))
971 : {
972 506 : appendStringInfoString(&buf, " INSERT");
973 506 : findx++;
974 : }
975 745 : if (TRIGGER_FOR_DELETE(trigrec->tgtype))
976 : {
977 117 : if (findx > 0)
978 45 : appendStringInfoString(&buf, " OR DELETE");
979 : else
980 72 : appendStringInfoString(&buf, " DELETE");
981 117 : findx++;
982 : }
983 745 : if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
984 : {
985 342 : if (findx > 0)
986 175 : appendStringInfoString(&buf, " OR UPDATE");
987 : else
988 167 : appendStringInfoString(&buf, " UPDATE");
989 342 : findx++;
990 : /* tgattr is first var-width field, so OK to access directly */
991 342 : if (trigrec->tgattr.dim1 > 0)
992 : {
993 : int i;
994 :
995 44 : appendStringInfoString(&buf, " OF ");
996 97 : for (i = 0; i < trigrec->tgattr.dim1; i++)
997 : {
998 : char *attname;
999 :
1000 53 : if (i > 0)
1001 9 : appendStringInfoString(&buf, ", ");
1002 53 : attname = get_attname(trigrec->tgrelid,
1003 53 : trigrec->tgattr.values[i], false);
1004 53 : appendStringInfoString(&buf, quote_identifier(attname));
1005 : }
1006 : }
1007 : }
1008 745 : if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
1009 : {
1010 0 : if (findx > 0)
1011 0 : appendStringInfoString(&buf, " OR TRUNCATE");
1012 : else
1013 0 : appendStringInfoString(&buf, " TRUNCATE");
1014 0 : findx++;
1015 : }
1016 :
1017 : /*
1018 : * In non-pretty mode, always schema-qualify the target table name for
1019 : * safety. In pretty mode, schema-qualify only if not visible.
1020 : */
1021 1490 : appendStringInfo(&buf, " ON %s ",
1022 : pretty ?
1023 116 : generate_relation_name(trigrec->tgrelid, NIL) :
1024 629 : generate_qualified_relation_name(trigrec->tgrelid));
1025 :
1026 745 : if (OidIsValid(trigrec->tgconstraint))
1027 : {
1028 0 : if (OidIsValid(trigrec->tgconstrrelid))
1029 0 : appendStringInfo(&buf, "FROM %s ",
1030 : generate_relation_name(trigrec->tgconstrrelid, NIL));
1031 0 : if (!trigrec->tgdeferrable)
1032 0 : appendStringInfoString(&buf, "NOT ");
1033 0 : appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
1034 0 : if (trigrec->tginitdeferred)
1035 0 : appendStringInfoString(&buf, "DEFERRED ");
1036 : else
1037 0 : appendStringInfoString(&buf, "IMMEDIATE ");
1038 : }
1039 :
1040 745 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
1041 : tgrel->rd_att, &isnull);
1042 745 : if (!isnull)
1043 57 : tgoldtable = NameStr(*DatumGetName(value));
1044 : else
1045 688 : tgoldtable = NULL;
1046 745 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
1047 : tgrel->rd_att, &isnull);
1048 745 : if (!isnull)
1049 62 : tgnewtable = NameStr(*DatumGetName(value));
1050 : else
1051 683 : tgnewtable = NULL;
1052 745 : if (tgoldtable != NULL || tgnewtable != NULL)
1053 : {
1054 88 : appendStringInfoString(&buf, "REFERENCING ");
1055 88 : if (tgoldtable != NULL)
1056 57 : appendStringInfo(&buf, "OLD TABLE AS %s ",
1057 : quote_identifier(tgoldtable));
1058 88 : if (tgnewtable != NULL)
1059 62 : appendStringInfo(&buf, "NEW TABLE AS %s ",
1060 : quote_identifier(tgnewtable));
1061 : }
1062 :
1063 745 : if (TRIGGER_FOR_ROW(trigrec->tgtype))
1064 556 : appendStringInfoString(&buf, "FOR EACH ROW ");
1065 : else
1066 189 : appendStringInfoString(&buf, "FOR EACH STATEMENT ");
1067 :
1068 : /* If the trigger has a WHEN qualification, add that */
1069 745 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
1070 : tgrel->rd_att, &isnull);
1071 745 : if (!isnull)
1072 : {
1073 : Node *qual;
1074 : char relkind;
1075 : deparse_context context;
1076 : deparse_namespace dpns;
1077 : RangeTblEntry *oldrte;
1078 : RangeTblEntry *newrte;
1079 :
1080 85 : appendStringInfoString(&buf, "WHEN (");
1081 :
1082 85 : qual = stringToNode(TextDatumGetCString(value));
1083 :
1084 85 : relkind = get_rel_relkind(trigrec->tgrelid);
1085 :
1086 : /* Build minimal OLD and NEW RTEs for the rel */
1087 85 : oldrte = makeNode(RangeTblEntry);
1088 85 : oldrte->rtekind = RTE_RELATION;
1089 85 : oldrte->relid = trigrec->tgrelid;
1090 85 : oldrte->relkind = relkind;
1091 85 : oldrte->rellockmode = AccessShareLock;
1092 85 : oldrte->alias = makeAlias("old", NIL);
1093 85 : oldrte->eref = oldrte->alias;
1094 85 : oldrte->lateral = false;
1095 85 : oldrte->inh = false;
1096 85 : oldrte->inFromCl = true;
1097 :
1098 85 : newrte = makeNode(RangeTblEntry);
1099 85 : newrte->rtekind = RTE_RELATION;
1100 85 : newrte->relid = trigrec->tgrelid;
1101 85 : newrte->relkind = relkind;
1102 85 : newrte->rellockmode = AccessShareLock;
1103 85 : newrte->alias = makeAlias("new", NIL);
1104 85 : newrte->eref = newrte->alias;
1105 85 : newrte->lateral = false;
1106 85 : newrte->inh = false;
1107 85 : newrte->inFromCl = true;
1108 :
1109 : /* Build two-element rtable */
1110 85 : memset(&dpns, 0, sizeof(dpns));
1111 85 : dpns.rtable = list_make2(oldrte, newrte);
1112 85 : dpns.subplans = NIL;
1113 85 : dpns.ctes = NIL;
1114 85 : dpns.appendrels = NULL;
1115 85 : set_rtable_names(&dpns, NIL, NULL);
1116 85 : set_simple_column_names(&dpns);
1117 :
1118 : /* Set up context with one-deep namespace stack */
1119 85 : context.buf = &buf;
1120 85 : context.namespaces = list_make1(&dpns);
1121 85 : context.resultDesc = NULL;
1122 85 : context.targetList = NIL;
1123 85 : context.windowClause = NIL;
1124 85 : context.varprefix = true;
1125 85 : context.prettyFlags = GET_PRETTY_FLAGS(pretty);
1126 85 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
1127 85 : context.indentLevel = PRETTYINDENT_STD;
1128 85 : context.colNamesVisible = true;
1129 85 : context.inGroupBy = false;
1130 85 : context.varInOrderBy = false;
1131 85 : context.appendparents = NULL;
1132 :
1133 85 : get_rule_expr(qual, &context, false);
1134 :
1135 85 : appendStringInfoString(&buf, ") ");
1136 : }
1137 :
1138 745 : appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
1139 : generate_function_name(trigrec->tgfoid, 0,
1140 : NIL, NULL,
1141 : false, NULL, false));
1142 :
1143 745 : if (trigrec->tgnargs > 0)
1144 : {
1145 : char *p;
1146 : int i;
1147 :
1148 259 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
1149 : tgrel->rd_att, &isnull);
1150 259 : if (isnull)
1151 0 : elog(ERROR, "tgargs is null for trigger %u", trigid);
1152 259 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
1153 574 : for (i = 0; i < trigrec->tgnargs; i++)
1154 : {
1155 315 : if (i > 0)
1156 56 : appendStringInfoString(&buf, ", ");
1157 315 : simple_quote_literal(&buf, p);
1158 : /* advance p to next string embedded in tgargs */
1159 3251 : while (*p)
1160 2936 : p++;
1161 315 : p++;
1162 : }
1163 : }
1164 :
1165 : /* We deliberately do not put semi-colon at end */
1166 745 : appendStringInfoChar(&buf, ')');
1167 :
1168 : /* Clean up */
1169 745 : systable_endscan(tgscan);
1170 :
1171 745 : table_close(tgrel, AccessShareLock);
1172 :
1173 745 : return buf.data;
1174 : }
1175 :
1176 : /* ----------
1177 : * pg_get_indexdef - Get the definition of an index
1178 : *
1179 : * In the extended version, there is a colno argument as well as pretty bool.
1180 : * if colno == 0, we want a complete index definition.
1181 : * if colno > 0, we only want the Nth index key's variable or expression.
1182 : *
1183 : * Note that the SQL-function versions of this omit any info about the
1184 : * index tablespace; this is intentional because pg_dump wants it that way.
1185 : * However pg_get_indexdef_string() includes the index tablespace.
1186 : * ----------
1187 : */
1188 : Datum
1189 3105 : pg_get_indexdef(PG_FUNCTION_ARGS)
1190 : {
1191 3105 : Oid indexrelid = PG_GETARG_OID(0);
1192 : int prettyFlags;
1193 : char *res;
1194 :
1195 3105 : prettyFlags = PRETTYFLAG_INDENT;
1196 :
1197 3105 : res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1198 : false, false,
1199 : false, false,
1200 : prettyFlags, true);
1201 :
1202 3105 : if (res == NULL)
1203 4 : PG_RETURN_NULL();
1204 :
1205 3101 : PG_RETURN_TEXT_P(string_to_text(res));
1206 : }
1207 :
1208 : Datum
1209 1347 : pg_get_indexdef_ext(PG_FUNCTION_ARGS)
1210 : {
1211 1347 : Oid indexrelid = PG_GETARG_OID(0);
1212 1347 : int32 colno = PG_GETARG_INT32(1);
1213 1347 : bool pretty = PG_GETARG_BOOL(2);
1214 : int prettyFlags;
1215 : char *res;
1216 :
1217 1347 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1218 :
1219 1347 : res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1220 : colno != 0, false,
1221 : false, false,
1222 : prettyFlags, true);
1223 :
1224 1347 : if (res == NULL)
1225 0 : PG_RETURN_NULL();
1226 :
1227 1347 : PG_RETURN_TEXT_P(string_to_text(res));
1228 : }
1229 :
1230 : /*
1231 : * Internal version for use by ALTER TABLE.
1232 : * Includes a tablespace clause in the result.
1233 : * Returns a palloc'd C string; no pretty-printing.
1234 : */
1235 : char *
1236 155 : pg_get_indexdef_string(Oid indexrelid)
1237 : {
1238 155 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1239 : false, false,
1240 : true, true,
1241 : 0, false);
1242 : }
1243 :
1244 : /* Internal version that just reports the key-column definitions */
1245 : char *
1246 709 : pg_get_indexdef_columns(Oid indexrelid, bool pretty)
1247 : {
1248 : int prettyFlags;
1249 :
1250 709 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1251 :
1252 709 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1253 : true, true,
1254 : false, false,
1255 : prettyFlags, false);
1256 : }
1257 :
1258 : /* Internal version, extensible with flags to control its behavior */
1259 : char *
1260 4 : pg_get_indexdef_columns_extended(Oid indexrelid, uint16 flags)
1261 : {
1262 4 : bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1263 4 : bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1264 : int prettyFlags;
1265 :
1266 4 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1267 :
1268 4 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1269 : true, keys_only,
1270 : false, false,
1271 : prettyFlags, false);
1272 : }
1273 :
1274 : /*
1275 : * Internal workhorse to decompile an index definition.
1276 : *
1277 : * This is now used for exclusion constraints as well: if excludeOps is not
1278 : * NULL then it points to an array of exclusion operator OIDs.
1279 : */
1280 : static char *
1281 5386 : pg_get_indexdef_worker(Oid indexrelid, int colno,
1282 : const Oid *excludeOps,
1283 : bool attrsOnly, bool keysOnly,
1284 : bool showTblSpc, bool inherits,
1285 : int prettyFlags, bool missing_ok)
1286 : {
1287 : /* might want a separate isConstraint parameter later */
1288 5386 : bool isConstraint = (excludeOps != NULL);
1289 : HeapTuple ht_idx;
1290 : HeapTuple ht_idxrel;
1291 : HeapTuple ht_am;
1292 : Form_pg_index idxrec;
1293 : Form_pg_class idxrelrec;
1294 : Form_pg_am amrec;
1295 : const IndexAmRoutine *amroutine;
1296 : List *indexprs;
1297 : ListCell *indexpr_item;
1298 : List *context;
1299 : Oid indrelid;
1300 : int keyno;
1301 : Datum indcollDatum;
1302 : Datum indclassDatum;
1303 : Datum indoptionDatum;
1304 : oidvector *indcollation;
1305 : oidvector *indclass;
1306 : int2vector *indoption;
1307 : StringInfoData buf;
1308 : char *str;
1309 : char *sep;
1310 :
1311 : /*
1312 : * Fetch the pg_index tuple by the Oid of the index
1313 : */
1314 5386 : ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
1315 5386 : if (!HeapTupleIsValid(ht_idx))
1316 : {
1317 4 : if (missing_ok)
1318 4 : return NULL;
1319 0 : elog(ERROR, "cache lookup failed for index %u", indexrelid);
1320 : }
1321 5382 : idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
1322 :
1323 5382 : indrelid = idxrec->indrelid;
1324 : Assert(indexrelid == idxrec->indexrelid);
1325 :
1326 : /* Must get indcollation, indclass, and indoption the hard way */
1327 5382 : indcollDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1328 : Anum_pg_index_indcollation);
1329 5382 : indcollation = (oidvector *) DatumGetPointer(indcollDatum);
1330 :
1331 5382 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1332 : Anum_pg_index_indclass);
1333 5382 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
1334 :
1335 5382 : indoptionDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1336 : Anum_pg_index_indoption);
1337 5382 : indoption = (int2vector *) DatumGetPointer(indoptionDatum);
1338 :
1339 : /*
1340 : * Fetch the pg_class tuple of the index relation
1341 : */
1342 5382 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
1343 5382 : if (!HeapTupleIsValid(ht_idxrel))
1344 0 : elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1345 5382 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
1346 :
1347 : /*
1348 : * Fetch the pg_am tuple of the index' access method
1349 : */
1350 5382 : ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
1351 5382 : if (!HeapTupleIsValid(ht_am))
1352 0 : elog(ERROR, "cache lookup failed for access method %u",
1353 : idxrelrec->relam);
1354 5382 : amrec = (Form_pg_am) GETSTRUCT(ht_am);
1355 :
1356 : /* Fetch the index AM's API struct */
1357 5382 : amroutine = GetIndexAmRoutine(amrec->amhandler);
1358 :
1359 : /*
1360 : * Get the index expressions, if any. (NOTE: we do not use the relcache
1361 : * versions of the expressions and predicate, because we want to display
1362 : * non-const-folded expressions.)
1363 : */
1364 5382 : if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs, NULL))
1365 : {
1366 : Datum exprsDatum;
1367 : char *exprsString;
1368 :
1369 389 : exprsDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1370 : Anum_pg_index_indexprs);
1371 389 : exprsString = TextDatumGetCString(exprsDatum);
1372 389 : indexprs = (List *) stringToNode(exprsString);
1373 389 : pfree(exprsString);
1374 : }
1375 : else
1376 4993 : indexprs = NIL;
1377 :
1378 5382 : indexpr_item = list_head(indexprs);
1379 :
1380 5382 : context = deparse_context_for(get_relation_name(indrelid), indrelid);
1381 :
1382 : /*
1383 : * Start the index definition. Note that the index's name should never be
1384 : * schema-qualified, but the indexed rel's name may be.
1385 : */
1386 5382 : initStringInfo(&buf);
1387 :
1388 5382 : if (!attrsOnly)
1389 : {
1390 4363 : if (!isConstraint)
1391 8594 : appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
1392 4297 : idxrec->indisunique ? "UNIQUE " : "",
1393 4297 : quote_identifier(NameStr(idxrelrec->relname)),
1394 4297 : idxrelrec->relkind == RELKIND_PARTITIONED_INDEX
1395 403 : && !inherits ? "ONLY " : "",
1396 4297 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
1397 1041 : generate_relation_name(indrelid, NIL) :
1398 3256 : generate_qualified_relation_name(indrelid),
1399 4297 : quote_identifier(NameStr(amrec->amname)));
1400 : else /* currently, must be EXCLUDE constraint */
1401 66 : appendStringInfo(&buf, "EXCLUDE USING %s (",
1402 66 : quote_identifier(NameStr(amrec->amname)));
1403 : }
1404 :
1405 : /*
1406 : * Report the indexed attributes
1407 : */
1408 5382 : sep = "";
1409 13361 : for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1410 : {
1411 8044 : AttrNumber attnum = idxrec->indkey.values[keyno];
1412 : Oid keycoltype;
1413 : Oid keycolcollation;
1414 :
1415 : /*
1416 : * Ignore non-key attributes if told to.
1417 : */
1418 8044 : if (keysOnly && keyno >= idxrec->indnkeyatts)
1419 65 : break;
1420 :
1421 : /* Otherwise, print INCLUDE to divide key and non-key attrs. */
1422 7979 : if (!colno && keyno == idxrec->indnkeyatts)
1423 : {
1424 148 : appendStringInfoString(&buf, ") INCLUDE (");
1425 148 : sep = "";
1426 : }
1427 :
1428 7979 : if (!colno)
1429 7557 : appendStringInfoString(&buf, sep);
1430 7979 : sep = ", ";
1431 :
1432 7979 : if (attnum != 0)
1433 : {
1434 : /* Simple index column */
1435 : char *attname;
1436 : int32 keycoltypmod;
1437 :
1438 7513 : attname = get_attname(indrelid, attnum, false);
1439 7513 : if (!colno || colno == keyno + 1)
1440 7405 : appendStringInfoString(&buf, quote_identifier(attname));
1441 7513 : get_atttypetypmodcoll(indrelid, attnum,
1442 : &keycoltype, &keycoltypmod,
1443 : &keycolcollation);
1444 : }
1445 : else
1446 : {
1447 : /* expressional index */
1448 : Node *indexkey;
1449 :
1450 466 : if (indexpr_item == NULL)
1451 0 : elog(ERROR, "too few entries in indexprs list");
1452 466 : indexkey = (Node *) lfirst(indexpr_item);
1453 466 : indexpr_item = lnext(indexprs, indexpr_item);
1454 : /* Deparse */
1455 466 : str = deparse_expression_pretty(indexkey, context, false, false,
1456 : prettyFlags, 0);
1457 466 : if (!colno || colno == keyno + 1)
1458 : {
1459 : /* Need parens if it's not a bare function call */
1460 458 : if (looks_like_function(indexkey))
1461 33 : appendStringInfoString(&buf, str);
1462 : else
1463 425 : appendStringInfo(&buf, "(%s)", str);
1464 : }
1465 466 : keycoltype = exprType(indexkey);
1466 466 : keycolcollation = exprCollation(indexkey);
1467 : }
1468 :
1469 : /* Print additional decoration for (selected) key columns */
1470 7979 : if (!attrsOnly && keyno < idxrec->indnkeyatts &&
1471 0 : (!colno || colno == keyno + 1))
1472 : {
1473 6297 : int16 opt = indoption->values[keyno];
1474 6297 : Oid indcoll = indcollation->values[keyno];
1475 6297 : Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1476 6297 : bool has_options = attoptions != (Datum) 0;
1477 :
1478 : /* Add collation, if not default for column */
1479 6297 : if (OidIsValid(indcoll) && indcoll != keycolcollation)
1480 62 : appendStringInfo(&buf, " COLLATE %s",
1481 : generate_collation_name((indcoll)));
1482 :
1483 : /* Add the operator class name, if not default */
1484 6297 : get_opclass_name(indclass->values[keyno],
1485 : has_options ? InvalidOid : keycoltype, &buf);
1486 :
1487 6297 : if (has_options)
1488 : {
1489 22 : appendStringInfoString(&buf, " (");
1490 22 : get_reloptions(&buf, attoptions);
1491 22 : appendStringInfoChar(&buf, ')');
1492 : }
1493 :
1494 : /* Add options if relevant */
1495 6297 : if (amroutine->amcanorder)
1496 : {
1497 : /* if it supports sort ordering, report DESC and NULLS opts */
1498 5163 : if (opt & INDOPTION_DESC)
1499 : {
1500 0 : appendStringInfoString(&buf, " DESC");
1501 : /* NULLS FIRST is the default in this case */
1502 0 : if (!(opt & INDOPTION_NULLS_FIRST))
1503 0 : appendStringInfoString(&buf, " NULLS LAST");
1504 : }
1505 : else
1506 : {
1507 5163 : if (opt & INDOPTION_NULLS_FIRST)
1508 0 : appendStringInfoString(&buf, " NULLS FIRST");
1509 : }
1510 : }
1511 :
1512 : /* Add the exclusion operator if relevant */
1513 6297 : if (excludeOps != NULL)
1514 76 : appendStringInfo(&buf, " WITH %s",
1515 76 : generate_operator_name(excludeOps[keyno],
1516 : keycoltype,
1517 : keycoltype));
1518 : }
1519 : }
1520 :
1521 5382 : if (!attrsOnly)
1522 : {
1523 4363 : appendStringInfoChar(&buf, ')');
1524 :
1525 4363 : if (idxrec->indnullsnotdistinct)
1526 8 : appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1527 :
1528 : /*
1529 : * If it has options, append "WITH (options)"
1530 : */
1531 4363 : str = flatten_reloptions(indexrelid);
1532 4363 : if (str)
1533 : {
1534 105 : appendStringInfo(&buf, " WITH (%s)", str);
1535 105 : pfree(str);
1536 : }
1537 :
1538 : /*
1539 : * Print tablespace, but only if requested
1540 : */
1541 4363 : if (showTblSpc)
1542 : {
1543 : Oid tblspc;
1544 :
1545 155 : tblspc = get_rel_tablespace(indexrelid);
1546 155 : if (OidIsValid(tblspc))
1547 : {
1548 36 : if (isConstraint)
1549 0 : appendStringInfoString(&buf, " USING INDEX");
1550 36 : appendStringInfo(&buf, " TABLESPACE %s",
1551 36 : quote_identifier(get_tablespace_name(tblspc)));
1552 : }
1553 : }
1554 :
1555 : /*
1556 : * If it's a partial index, decompile and append the predicate
1557 : */
1558 4363 : if (!heap_attisnull(ht_idx, Anum_pg_index_indpred, NULL))
1559 : {
1560 : Node *node;
1561 : Datum predDatum;
1562 : char *predString;
1563 :
1564 : /* Convert text string to node tree */
1565 204 : predDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1566 : Anum_pg_index_indpred);
1567 204 : predString = TextDatumGetCString(predDatum);
1568 204 : node = (Node *) stringToNode(predString);
1569 204 : pfree(predString);
1570 :
1571 : /* Deparse */
1572 204 : str = deparse_expression_pretty(node, context, false, false,
1573 : prettyFlags, 0);
1574 204 : if (isConstraint)
1575 28 : appendStringInfo(&buf, " WHERE (%s)", str);
1576 : else
1577 176 : appendStringInfo(&buf, " WHERE %s", str);
1578 : }
1579 : }
1580 :
1581 : /* Clean up */
1582 5382 : ReleaseSysCache(ht_idx);
1583 5382 : ReleaseSysCache(ht_idxrel);
1584 5382 : ReleaseSysCache(ht_am);
1585 :
1586 5382 : return buf.data;
1587 : }
1588 :
1589 : /* ----------
1590 : * pg_get_querydef
1591 : *
1592 : * Public entry point to deparse one query parsetree.
1593 : * The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
1594 : *
1595 : * The result is a palloc'd C string.
1596 : * ----------
1597 : */
1598 : char *
1599 0 : pg_get_querydef(Query *query, bool pretty)
1600 : {
1601 : StringInfoData buf;
1602 : int prettyFlags;
1603 :
1604 0 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1605 :
1606 0 : initStringInfo(&buf);
1607 :
1608 0 : get_query_def(query, &buf, NIL, NULL, true,
1609 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1610 :
1611 0 : return buf.data;
1612 : }
1613 :
1614 : /*
1615 : * pg_get_propgraphdef - get the definition of a property graph
1616 : */
1617 : Datum
1618 125 : pg_get_propgraphdef(PG_FUNCTION_ARGS)
1619 : {
1620 125 : Oid pgrelid = PG_GETARG_OID(0);
1621 : StringInfoData buf;
1622 : HeapTuple classtup;
1623 : Form_pg_class classform;
1624 : char *name;
1625 : char *nsp;
1626 :
1627 125 : initStringInfo(&buf);
1628 :
1629 125 : classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(pgrelid));
1630 125 : if (!HeapTupleIsValid(classtup))
1631 0 : PG_RETURN_NULL();
1632 :
1633 125 : classform = (Form_pg_class) GETSTRUCT(classtup);
1634 125 : name = NameStr(classform->relname);
1635 :
1636 125 : if (classform->relkind != RELKIND_PROPGRAPH)
1637 4 : ereport(ERROR,
1638 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1639 : errmsg("\"%s\" is not a property graph", name)));
1640 :
1641 121 : nsp = get_namespace_name(classform->relnamespace);
1642 :
1643 121 : appendStringInfo(&buf, "CREATE PROPERTY GRAPH %s",
1644 : quote_qualified_identifier(nsp, name));
1645 :
1646 121 : ReleaseSysCache(classtup);
1647 :
1648 121 : make_propgraphdef_elements(&buf, pgrelid, PGEKIND_VERTEX);
1649 121 : make_propgraphdef_elements(&buf, pgrelid, PGEKIND_EDGE);
1650 :
1651 121 : PG_RETURN_TEXT_P(string_to_text(buf.data));
1652 : }
1653 :
1654 : /*
1655 : * Generates a VERTEX TABLES (...) or EDGE TABLES (...) clause. Pass in the
1656 : * property graph relation OID and the element kind (vertex or edge). Result
1657 : * is appended to buf.
1658 : */
1659 : static void
1660 242 : make_propgraphdef_elements(StringInfo buf, Oid pgrelid, char pgekind)
1661 : {
1662 : Relation pgerel;
1663 : ScanKeyData scankey[1];
1664 : SysScanDesc scan;
1665 : bool first;
1666 : HeapTuple tup;
1667 :
1668 242 : pgerel = table_open(PropgraphElementRelationId, AccessShareLock);
1669 :
1670 242 : ScanKeyInit(&scankey[0],
1671 : Anum_pg_propgraph_element_pgepgid,
1672 : BTEqualStrategyNumber, F_OIDEQ,
1673 : ObjectIdGetDatum(pgrelid));
1674 :
1675 242 : scan = systable_beginscan(pgerel, PropgraphElementAliasIndexId, true, NULL, 1, scankey);
1676 :
1677 242 : first = true;
1678 992 : while ((tup = systable_getnext(scan)))
1679 : {
1680 750 : Form_pg_propgraph_element pgeform = (Form_pg_propgraph_element) GETSTRUCT(tup);
1681 : char *relname;
1682 : Datum datum;
1683 : bool isnull;
1684 :
1685 750 : if (pgeform->pgekind != pgekind)
1686 375 : continue;
1687 :
1688 375 : if (first)
1689 : {
1690 161 : appendStringInfo(buf, "\n %s TABLES (\n", pgekind == PGEKIND_VERTEX ? "VERTEX" : "EDGE");
1691 161 : first = false;
1692 : }
1693 : else
1694 214 : appendStringInfo(buf, ",\n");
1695 :
1696 375 : relname = get_rel_name(pgeform->pgerelid);
1697 375 : if (relname && strcmp(relname, NameStr(pgeform->pgealias)) == 0)
1698 375 : appendStringInfo(buf, " %s",
1699 : generate_relation_name(pgeform->pgerelid, NIL));
1700 : else
1701 0 : appendStringInfo(buf, " %s AS %s",
1702 : generate_relation_name(pgeform->pgerelid, NIL),
1703 0 : quote_identifier(NameStr(pgeform->pgealias)));
1704 :
1705 375 : datum = heap_getattr(tup, Anum_pg_propgraph_element_pgekey, RelationGetDescr(pgerel), &isnull);
1706 375 : if (!isnull)
1707 : {
1708 375 : appendStringInfoString(buf, " KEY (");
1709 375 : decompile_column_index_array(datum, pgeform->pgerelid, false, buf);
1710 375 : appendStringInfoString(buf, ")");
1711 : }
1712 : else
1713 0 : elog(ERROR, "null pgekey for element %u", pgeform->oid);
1714 :
1715 375 : if (pgekind == PGEKIND_EDGE)
1716 : {
1717 : Datum srckey;
1718 : Datum srcref;
1719 : Datum destkey;
1720 : Datum destref;
1721 : HeapTuple tup2;
1722 : Form_pg_propgraph_element pgeform2;
1723 :
1724 163 : datum = heap_getattr(tup, Anum_pg_propgraph_element_pgesrckey, RelationGetDescr(pgerel), &isnull);
1725 163 : srckey = isnull ? 0 : datum;
1726 163 : datum = heap_getattr(tup, Anum_pg_propgraph_element_pgesrcref, RelationGetDescr(pgerel), &isnull);
1727 163 : srcref = isnull ? 0 : datum;
1728 163 : datum = heap_getattr(tup, Anum_pg_propgraph_element_pgedestkey, RelationGetDescr(pgerel), &isnull);
1729 163 : destkey = isnull ? 0 : datum;
1730 163 : datum = heap_getattr(tup, Anum_pg_propgraph_element_pgedestref, RelationGetDescr(pgerel), &isnull);
1731 163 : destref = isnull ? 0 : datum;
1732 :
1733 163 : appendStringInfoString(buf, " SOURCE");
1734 163 : tup2 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(pgeform->pgesrcvertexid));
1735 163 : if (!tup2)
1736 0 : elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgesrcvertexid);
1737 163 : pgeform2 = (Form_pg_propgraph_element) GETSTRUCT(tup2);
1738 163 : if (srckey)
1739 : {
1740 163 : appendStringInfoString(buf, " KEY (");
1741 163 : decompile_column_index_array(srckey, pgeform->pgerelid, false, buf);
1742 163 : appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1743 163 : decompile_column_index_array(srcref, pgeform2->pgerelid, false, buf);
1744 163 : appendStringInfoString(buf, ")");
1745 : }
1746 : else
1747 0 : appendStringInfo(buf, " %s ", quote_identifier(NameStr(pgeform2->pgealias)));
1748 163 : ReleaseSysCache(tup2);
1749 :
1750 163 : appendStringInfoString(buf, " DESTINATION");
1751 163 : tup2 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(pgeform->pgedestvertexid));
1752 163 : if (!tup2)
1753 0 : elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgedestvertexid);
1754 163 : pgeform2 = (Form_pg_propgraph_element) GETSTRUCT(tup2);
1755 163 : if (destkey)
1756 : {
1757 163 : appendStringInfoString(buf, " KEY (");
1758 163 : decompile_column_index_array(destkey, pgeform->pgerelid, false, buf);
1759 163 : appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1760 163 : decompile_column_index_array(destref, pgeform2->pgerelid, false, buf);
1761 163 : appendStringInfoString(buf, ")");
1762 : }
1763 : else
1764 0 : appendStringInfo(buf, " %s", quote_identifier(NameStr(pgeform2->pgealias)));
1765 163 : ReleaseSysCache(tup2);
1766 : }
1767 :
1768 375 : make_propgraphdef_labels(buf, pgeform->oid, NameStr(pgeform->pgealias), pgeform->pgerelid);
1769 : }
1770 242 : if (!first)
1771 161 : appendStringInfo(buf, "\n )");
1772 :
1773 242 : systable_endscan(scan);
1774 242 : table_close(pgerel, AccessShareLock);
1775 242 : }
1776 :
1777 : struct oid_str_pair
1778 : {
1779 : Oid oid;
1780 : char *str;
1781 : };
1782 :
1783 : static int
1784 106 : list_oid_str_pair_cmp_by_str(const ListCell *p1, const ListCell *p2)
1785 : {
1786 106 : struct oid_str_pair *v1 = lfirst(p1);
1787 106 : struct oid_str_pair *v2 = lfirst(p2);
1788 :
1789 106 : return strcmp(v1->str, v2->str);
1790 : }
1791 :
1792 : /*
1793 : * Generates label and properties list. Pass in the element OID, the element
1794 : * alias, and the graph relation OID. Result is appended to buf.
1795 : */
1796 : static void
1797 375 : make_propgraphdef_labels(StringInfo buf, Oid elid, const char *elalias, Oid elrelid)
1798 : {
1799 : Relation pglrel;
1800 : ScanKeyData scankey[1];
1801 : SysScanDesc scan;
1802 : int count;
1803 : HeapTuple tup;
1804 375 : List *label_list = NIL;
1805 :
1806 375 : pglrel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1807 :
1808 375 : ScanKeyInit(&scankey[0],
1809 : Anum_pg_propgraph_element_label_pgelelid,
1810 : BTEqualStrategyNumber, F_OIDEQ,
1811 : ObjectIdGetDatum(elid));
1812 :
1813 : /*
1814 : * We want to output the labels in a deterministic order. So we first
1815 : * read all the data, then sort, then print it.
1816 : */
1817 375 : scan = systable_beginscan(pglrel, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, scankey);
1818 :
1819 856 : while ((tup = systable_getnext(scan)))
1820 : {
1821 481 : Form_pg_propgraph_element_label pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
1822 : struct oid_str_pair *osp;
1823 :
1824 481 : osp = palloc_object(struct oid_str_pair);
1825 481 : osp->oid = pgelform->oid;
1826 481 : osp->str = get_propgraph_label_name(pgelform->pgellabelid);
1827 :
1828 481 : label_list = lappend(label_list, osp);
1829 : }
1830 :
1831 375 : systable_endscan(scan);
1832 375 : table_close(pglrel, AccessShareLock);
1833 :
1834 375 : count = list_length(label_list);
1835 :
1836 : /* Each element has at least one label. */
1837 : Assert(count > 0);
1838 :
1839 : /*
1840 : * It is enough for the comparison function to compare just labels, since
1841 : * all the labels of an element table should have distinct names.
1842 : */
1843 375 : list_sort(label_list, list_oid_str_pair_cmp_by_str);
1844 :
1845 1231 : foreach_ptr(struct oid_str_pair, osp, label_list)
1846 : {
1847 481 : if (strcmp(osp->str, elalias) == 0)
1848 : {
1849 : /* If the default label is the only label, don't print anything. */
1850 259 : if (count != 1)
1851 30 : appendStringInfo(buf, " DEFAULT LABEL");
1852 : }
1853 : else
1854 222 : appendStringInfo(buf, " LABEL %s", quote_identifier(osp->str));
1855 :
1856 481 : make_propgraphdef_properties(buf, osp->oid, elrelid);
1857 : }
1858 375 : }
1859 :
1860 : /*
1861 : * Helper function for make_propgraphdef_properties(): Sort (propname, expr)
1862 : * pairs by name.
1863 : */
1864 : static int
1865 748 : propdata_by_name_cmp(const ListCell *a, const ListCell *b)
1866 : {
1867 748 : List *la = lfirst_node(List, a);
1868 748 : List *lb = lfirst_node(List, b);
1869 748 : char *pna = strVal(linitial(la));
1870 748 : char *pnb = strVal(linitial(lb));
1871 :
1872 748 : return strcmp(pna, pnb);
1873 : }
1874 :
1875 : /*
1876 : * Generates element table properties clause (PROPERTIES (...) or NO
1877 : * PROPERTIES). Pass in label OID and element table OID. Result is appended
1878 : * to buf.
1879 : */
1880 : static void
1881 481 : make_propgraphdef_properties(StringInfo buf, Oid ellabelid, Oid elrelid)
1882 : {
1883 : Relation plprel;
1884 : ScanKeyData scankey[1];
1885 : SysScanDesc scan;
1886 : HeapTuple tup;
1887 481 : List *outlist = NIL;
1888 :
1889 481 : plprel = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
1890 :
1891 481 : ScanKeyInit(&scankey[0],
1892 : Anum_pg_propgraph_label_property_plpellabelid,
1893 : BTEqualStrategyNumber, F_OIDEQ,
1894 : ObjectIdGetDatum(ellabelid));
1895 :
1896 : /*
1897 : * We want to output the properties in a deterministic order. So we first
1898 : * read all the data, then sort, then print it.
1899 : */
1900 481 : scan = systable_beginscan(plprel, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, scankey);
1901 :
1902 1567 : while ((tup = systable_getnext(scan)))
1903 : {
1904 1086 : Form_pg_propgraph_label_property plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
1905 : Datum exprDatum;
1906 : bool isnull;
1907 : char *tmp;
1908 : Node *expr;
1909 : char *propname;
1910 :
1911 1086 : exprDatum = heap_getattr(tup, Anum_pg_propgraph_label_property_plpexpr, RelationGetDescr(plprel), &isnull);
1912 : Assert(!isnull);
1913 1086 : tmp = TextDatumGetCString(exprDatum);
1914 1086 : expr = stringToNode(tmp);
1915 1086 : pfree(tmp);
1916 :
1917 1086 : propname = get_propgraph_property_name(plpform->plppropid);
1918 :
1919 1086 : outlist = lappend(outlist, list_make2(makeString(propname), expr));
1920 : }
1921 :
1922 481 : systable_endscan(scan);
1923 481 : table_close(plprel, AccessShareLock);
1924 :
1925 481 : list_sort(outlist, propdata_by_name_cmp);
1926 :
1927 481 : if (outlist)
1928 : {
1929 : List *context;
1930 : ListCell *lc;
1931 472 : bool first = true;
1932 :
1933 472 : context = deparse_context_for(get_relation_name(elrelid), elrelid);
1934 :
1935 472 : appendStringInfo(buf, " PROPERTIES (");
1936 :
1937 1558 : foreach(lc, outlist)
1938 : {
1939 1086 : List *data = lfirst_node(List, lc);
1940 1086 : char *propname = strVal(linitial(data));
1941 1086 : Node *expr = lsecond(data);
1942 :
1943 1086 : if (first)
1944 472 : first = false;
1945 : else
1946 614 : appendStringInfo(buf, ", ");
1947 :
1948 1086 : if (IsA(expr, Var) && strcmp(propname, get_attname(elrelid, castNode(Var, expr)->varattno, false)) == 0)
1949 885 : appendStringInfo(buf, "%s", quote_identifier(propname));
1950 : else
1951 201 : appendStringInfo(buf, "%s AS %s",
1952 : deparse_expression_pretty(expr, context, false, false, 0, 0),
1953 : quote_identifier(propname));
1954 : }
1955 :
1956 472 : appendStringInfo(buf, ")");
1957 : }
1958 : else
1959 9 : appendStringInfo(buf, " NO PROPERTIES");
1960 481 : }
1961 :
1962 : /*
1963 : * pg_get_statisticsobjdef
1964 : * Get the definition of an extended statistics object
1965 : */
1966 : Datum
1967 155 : pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
1968 : {
1969 155 : Oid statextid = PG_GETARG_OID(0);
1970 : char *res;
1971 :
1972 155 : res = pg_get_statisticsobj_worker(statextid, false, true);
1973 :
1974 155 : if (res == NULL)
1975 4 : PG_RETURN_NULL();
1976 :
1977 151 : PG_RETURN_TEXT_P(string_to_text(res));
1978 : }
1979 :
1980 : /*
1981 : * Internal version for use by ALTER TABLE.
1982 : * Returns a palloc'd C string; no pretty-printing.
1983 : */
1984 : char *
1985 53 : pg_get_statisticsobjdef_string(Oid statextid)
1986 : {
1987 53 : return pg_get_statisticsobj_worker(statextid, false, false);
1988 : }
1989 :
1990 : /*
1991 : * pg_get_statisticsobjdef_columns
1992 : * Get columns and expressions for an extended statistics object
1993 : */
1994 : Datum
1995 284 : pg_get_statisticsobjdef_columns(PG_FUNCTION_ARGS)
1996 : {
1997 284 : Oid statextid = PG_GETARG_OID(0);
1998 : char *res;
1999 :
2000 284 : res = pg_get_statisticsobj_worker(statextid, true, true);
2001 :
2002 284 : if (res == NULL)
2003 0 : PG_RETURN_NULL();
2004 :
2005 284 : PG_RETURN_TEXT_P(string_to_text(res));
2006 : }
2007 :
2008 : /*
2009 : * Internal workhorse to decompile an extended statistics object.
2010 : */
2011 : static char *
2012 492 : pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
2013 : {
2014 : Form_pg_statistic_ext statextrec;
2015 : HeapTuple statexttup;
2016 : StringInfoData buf;
2017 : int colno;
2018 : char *nsp;
2019 : ArrayType *arr;
2020 : char *enabled;
2021 : Datum datum;
2022 : bool ndistinct_enabled;
2023 : bool dependencies_enabled;
2024 : bool mcv_enabled;
2025 : int i;
2026 : List *context;
2027 : ListCell *lc;
2028 492 : List *exprs = NIL;
2029 : bool has_exprs;
2030 : int ncolumns;
2031 :
2032 492 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
2033 :
2034 492 : if (!HeapTupleIsValid(statexttup))
2035 : {
2036 4 : if (missing_ok)
2037 4 : return NULL;
2038 0 : elog(ERROR, "cache lookup failed for statistics object %u", statextid);
2039 : }
2040 :
2041 : /* has the statistics expressions? */
2042 488 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
2043 :
2044 488 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
2045 :
2046 : /*
2047 : * Get the statistics expressions, if any. (NOTE: we do not use the
2048 : * relcache versions of the expressions, because we want to display
2049 : * non-const-folded expressions.)
2050 : */
2051 488 : if (has_exprs)
2052 : {
2053 : Datum exprsDatum;
2054 : char *exprsString;
2055 :
2056 125 : exprsDatum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
2057 : Anum_pg_statistic_ext_stxexprs);
2058 125 : exprsString = TextDatumGetCString(exprsDatum);
2059 125 : exprs = (List *) stringToNode(exprsString);
2060 125 : pfree(exprsString);
2061 : }
2062 : else
2063 363 : exprs = NIL;
2064 :
2065 : /* count the number of columns (attributes and expressions) */
2066 488 : ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
2067 :
2068 488 : initStringInfo(&buf);
2069 :
2070 488 : if (!columns_only)
2071 : {
2072 204 : nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
2073 204 : appendStringInfo(&buf, "CREATE STATISTICS %s",
2074 : quote_qualified_identifier(nsp,
2075 204 : NameStr(statextrec->stxname)));
2076 :
2077 : /*
2078 : * Decode the stxkind column so that we know which stats types to
2079 : * print.
2080 : */
2081 204 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
2082 : Anum_pg_statistic_ext_stxkind);
2083 204 : arr = DatumGetArrayTypeP(datum);
2084 204 : if (ARR_NDIM(arr) != 1 ||
2085 204 : ARR_HASNULL(arr) ||
2086 204 : ARR_ELEMTYPE(arr) != CHAROID)
2087 0 : elog(ERROR, "stxkind is not a 1-D char array");
2088 204 : enabled = (char *) ARR_DATA_PTR(arr);
2089 :
2090 204 : ndistinct_enabled = false;
2091 204 : dependencies_enabled = false;
2092 204 : mcv_enabled = false;
2093 :
2094 643 : for (i = 0; i < ARR_DIMS(arr)[0]; i++)
2095 : {
2096 439 : if (enabled[i] == STATS_EXT_NDISTINCT)
2097 131 : ndistinct_enabled = true;
2098 308 : else if (enabled[i] == STATS_EXT_DEPENDENCIES)
2099 109 : dependencies_enabled = true;
2100 199 : else if (enabled[i] == STATS_EXT_MCV)
2101 118 : mcv_enabled = true;
2102 :
2103 : /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
2104 : }
2105 :
2106 : /*
2107 : * If any option is disabled, then we'll need to append the types
2108 : * clause to show which options are enabled. We omit the types clause
2109 : * on purpose when all options are enabled, so a pg_dump/pg_restore
2110 : * will create all statistics types on a newer postgres version, if
2111 : * the statistics had all options enabled on the original version.
2112 : *
2113 : * But if the statistics is defined on just a single column, it has to
2114 : * be an expression statistics. In that case we don't need to specify
2115 : * kinds.
2116 : */
2117 204 : if ((!ndistinct_enabled || !dependencies_enabled || !mcv_enabled) &&
2118 : (ncolumns > 1))
2119 : {
2120 61 : bool gotone = false;
2121 :
2122 61 : appendStringInfoString(&buf, " (");
2123 :
2124 61 : if (ndistinct_enabled)
2125 : {
2126 32 : appendStringInfoString(&buf, "ndistinct");
2127 32 : gotone = true;
2128 : }
2129 :
2130 61 : if (dependencies_enabled)
2131 : {
2132 10 : appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
2133 10 : gotone = true;
2134 : }
2135 :
2136 61 : if (mcv_enabled)
2137 19 : appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
2138 :
2139 61 : appendStringInfoChar(&buf, ')');
2140 : }
2141 :
2142 204 : appendStringInfoString(&buf, " ON ");
2143 : }
2144 :
2145 : /* decode simple column references */
2146 1393 : for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
2147 : {
2148 905 : AttrNumber attnum = statextrec->stxkeys.values[colno];
2149 : char *attname;
2150 :
2151 905 : if (colno > 0)
2152 502 : appendStringInfoString(&buf, ", ");
2153 :
2154 905 : attname = get_attname(statextrec->stxrelid, attnum, false);
2155 :
2156 905 : appendStringInfoString(&buf, quote_identifier(attname));
2157 : }
2158 :
2159 488 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
2160 : statextrec->stxrelid);
2161 :
2162 667 : foreach(lc, exprs)
2163 : {
2164 179 : Node *expr = (Node *) lfirst(lc);
2165 : char *str;
2166 179 : int prettyFlags = PRETTYFLAG_PAREN;
2167 :
2168 179 : str = deparse_expression_pretty(expr, context, false, false,
2169 : prettyFlags, 0);
2170 :
2171 179 : if (colno > 0)
2172 94 : appendStringInfoString(&buf, ", ");
2173 :
2174 : /* Need parens if it's not a bare function call */
2175 179 : if (looks_like_function(expr))
2176 21 : appendStringInfoString(&buf, str);
2177 : else
2178 158 : appendStringInfo(&buf, "(%s)", str);
2179 :
2180 179 : colno++;
2181 : }
2182 :
2183 488 : if (!columns_only)
2184 204 : appendStringInfo(&buf, " FROM %s",
2185 : generate_relation_name(statextrec->stxrelid, NIL));
2186 :
2187 488 : ReleaseSysCache(statexttup);
2188 :
2189 488 : return buf.data;
2190 : }
2191 :
2192 : /*
2193 : * Generate text array of expressions for statistics object.
2194 : */
2195 : Datum
2196 125 : pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
2197 : {
2198 125 : Oid statextid = PG_GETARG_OID(0);
2199 : Form_pg_statistic_ext statextrec;
2200 : HeapTuple statexttup;
2201 : Datum datum;
2202 : List *context;
2203 : ListCell *lc;
2204 125 : List *exprs = NIL;
2205 : bool has_exprs;
2206 : char *tmp;
2207 125 : ArrayBuildState *astate = NULL;
2208 :
2209 125 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
2210 :
2211 125 : if (!HeapTupleIsValid(statexttup))
2212 0 : PG_RETURN_NULL();
2213 :
2214 : /* Does the stats object have expressions? */
2215 125 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
2216 :
2217 : /* no expressions? we're done */
2218 125 : if (!has_exprs)
2219 : {
2220 11 : ReleaseSysCache(statexttup);
2221 11 : PG_RETURN_NULL();
2222 : }
2223 :
2224 114 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
2225 :
2226 : /*
2227 : * Get the statistics expressions, and deparse them into text values.
2228 : */
2229 114 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
2230 : Anum_pg_statistic_ext_stxexprs);
2231 114 : tmp = TextDatumGetCString(datum);
2232 114 : exprs = (List *) stringToNode(tmp);
2233 114 : pfree(tmp);
2234 :
2235 114 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
2236 : statextrec->stxrelid);
2237 :
2238 274 : foreach(lc, exprs)
2239 : {
2240 160 : Node *expr = (Node *) lfirst(lc);
2241 : char *str;
2242 160 : int prettyFlags = PRETTYFLAG_INDENT;
2243 :
2244 160 : str = deparse_expression_pretty(expr, context, false, false,
2245 : prettyFlags, 0);
2246 :
2247 160 : astate = accumArrayResult(astate,
2248 160 : PointerGetDatum(cstring_to_text(str)),
2249 : false,
2250 : TEXTOID,
2251 : CurrentMemoryContext);
2252 : }
2253 :
2254 114 : ReleaseSysCache(statexttup);
2255 :
2256 114 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
2257 : }
2258 :
2259 : /*
2260 : * pg_get_partkeydef
2261 : *
2262 : * Returns the partition key specification, ie, the following:
2263 : *
2264 : * { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
2265 : */
2266 : Datum
2267 813 : pg_get_partkeydef(PG_FUNCTION_ARGS)
2268 : {
2269 813 : Oid relid = PG_GETARG_OID(0);
2270 : char *res;
2271 :
2272 813 : res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
2273 :
2274 813 : if (res == NULL)
2275 4 : PG_RETURN_NULL();
2276 :
2277 809 : PG_RETURN_TEXT_P(string_to_text(res));
2278 : }
2279 :
2280 : /* Internal version that just reports the column definitions */
2281 : char *
2282 94 : pg_get_partkeydef_columns(Oid relid, bool pretty)
2283 : {
2284 : int prettyFlags;
2285 :
2286 94 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2287 :
2288 94 : return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
2289 : }
2290 :
2291 : /*
2292 : * Internal workhorse to decompile a partition key definition.
2293 : */
2294 : static char *
2295 907 : pg_get_partkeydef_worker(Oid relid, int prettyFlags,
2296 : bool attrsOnly, bool missing_ok)
2297 : {
2298 : Form_pg_partitioned_table form;
2299 : HeapTuple tuple;
2300 : oidvector *partclass;
2301 : oidvector *partcollation;
2302 : List *partexprs;
2303 : ListCell *partexpr_item;
2304 : List *context;
2305 : Datum datum;
2306 : StringInfoData buf;
2307 : int keyno;
2308 : char *str;
2309 : char *sep;
2310 :
2311 907 : tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
2312 907 : if (!HeapTupleIsValid(tuple))
2313 : {
2314 4 : if (missing_ok)
2315 4 : return NULL;
2316 0 : elog(ERROR, "cache lookup failed for partition key of %u", relid);
2317 : }
2318 :
2319 903 : form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
2320 :
2321 : Assert(form->partrelid == relid);
2322 :
2323 : /* Must get partclass and partcollation the hard way */
2324 903 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2325 : Anum_pg_partitioned_table_partclass);
2326 903 : partclass = (oidvector *) DatumGetPointer(datum);
2327 :
2328 903 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2329 : Anum_pg_partitioned_table_partcollation);
2330 903 : partcollation = (oidvector *) DatumGetPointer(datum);
2331 :
2332 :
2333 : /*
2334 : * Get the expressions, if any. (NOTE: we do not use the relcache
2335 : * versions of the expressions, because we want to display
2336 : * non-const-folded expressions.)
2337 : */
2338 903 : if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprs, NULL))
2339 : {
2340 : Datum exprsDatum;
2341 : char *exprsString;
2342 :
2343 84 : exprsDatum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2344 : Anum_pg_partitioned_table_partexprs);
2345 84 : exprsString = TextDatumGetCString(exprsDatum);
2346 84 : partexprs = (List *) stringToNode(exprsString);
2347 :
2348 84 : if (!IsA(partexprs, List))
2349 0 : elog(ERROR, "unexpected node type found in partexprs: %d",
2350 : (int) nodeTag(partexprs));
2351 :
2352 84 : pfree(exprsString);
2353 : }
2354 : else
2355 819 : partexprs = NIL;
2356 :
2357 903 : partexpr_item = list_head(partexprs);
2358 903 : context = deparse_context_for(get_relation_name(relid), relid);
2359 :
2360 903 : initStringInfo(&buf);
2361 :
2362 903 : switch (form->partstrat)
2363 : {
2364 59 : case PARTITION_STRATEGY_HASH:
2365 59 : if (!attrsOnly)
2366 59 : appendStringInfoString(&buf, "HASH");
2367 59 : break;
2368 339 : case PARTITION_STRATEGY_LIST:
2369 339 : if (!attrsOnly)
2370 313 : appendStringInfoString(&buf, "LIST");
2371 339 : break;
2372 505 : case PARTITION_STRATEGY_RANGE:
2373 505 : if (!attrsOnly)
2374 437 : appendStringInfoString(&buf, "RANGE");
2375 505 : break;
2376 0 : default:
2377 0 : elog(ERROR, "unexpected partition strategy: %d",
2378 : (int) form->partstrat);
2379 : }
2380 :
2381 903 : if (!attrsOnly)
2382 809 : appendStringInfoString(&buf, " (");
2383 903 : sep = "";
2384 1899 : for (keyno = 0; keyno < form->partnatts; keyno++)
2385 : {
2386 996 : AttrNumber attnum = form->partattrs.values[keyno];
2387 : Oid keycoltype;
2388 : Oid keycolcollation;
2389 : Oid partcoll;
2390 :
2391 996 : appendStringInfoString(&buf, sep);
2392 996 : sep = ", ";
2393 996 : if (attnum != 0)
2394 : {
2395 : /* Simple attribute reference */
2396 : char *attname;
2397 : int32 keycoltypmod;
2398 :
2399 904 : attname = get_attname(relid, attnum, false);
2400 904 : appendStringInfoString(&buf, quote_identifier(attname));
2401 904 : get_atttypetypmodcoll(relid, attnum,
2402 : &keycoltype, &keycoltypmod,
2403 : &keycolcollation);
2404 : }
2405 : else
2406 : {
2407 : /* Expression */
2408 : Node *partkey;
2409 :
2410 92 : if (partexpr_item == NULL)
2411 0 : elog(ERROR, "too few entries in partexprs list");
2412 92 : partkey = (Node *) lfirst(partexpr_item);
2413 92 : partexpr_item = lnext(partexprs, partexpr_item);
2414 :
2415 : /* Deparse */
2416 92 : str = deparse_expression_pretty(partkey, context, false, false,
2417 : prettyFlags, 0);
2418 : /* Need parens if it's not a bare function call */
2419 92 : if (looks_like_function(partkey))
2420 34 : appendStringInfoString(&buf, str);
2421 : else
2422 58 : appendStringInfo(&buf, "(%s)", str);
2423 :
2424 92 : keycoltype = exprType(partkey);
2425 92 : keycolcollation = exprCollation(partkey);
2426 : }
2427 :
2428 : /* Add collation, if not default for column */
2429 996 : partcoll = partcollation->values[keyno];
2430 996 : if (!attrsOnly && OidIsValid(partcoll) && partcoll != keycolcollation)
2431 4 : appendStringInfo(&buf, " COLLATE %s",
2432 : generate_collation_name((partcoll)));
2433 :
2434 : /* Add the operator class name, if not default */
2435 996 : if (!attrsOnly)
2436 866 : get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2437 : }
2438 :
2439 903 : if (!attrsOnly)
2440 809 : appendStringInfoChar(&buf, ')');
2441 :
2442 : /* Clean up */
2443 903 : ReleaseSysCache(tuple);
2444 :
2445 903 : return buf.data;
2446 : }
2447 :
2448 : /*
2449 : * pg_get_partition_constraintdef
2450 : *
2451 : * Returns partition constraint expression as a string for the input relation
2452 : */
2453 : Datum
2454 153 : pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
2455 : {
2456 153 : Oid relationId = PG_GETARG_OID(0);
2457 : Expr *constr_expr;
2458 : int prettyFlags;
2459 : List *context;
2460 : char *consrc;
2461 :
2462 153 : constr_expr = get_partition_qual_relid(relationId);
2463 :
2464 : /* Quick exit if no partition constraint */
2465 153 : if (constr_expr == NULL)
2466 16 : PG_RETURN_NULL();
2467 :
2468 : /*
2469 : * Deparse and return the constraint expression.
2470 : */
2471 137 : prettyFlags = PRETTYFLAG_INDENT;
2472 137 : context = deparse_context_for(get_relation_name(relationId), relationId);
2473 137 : consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2474 : false, prettyFlags, 0);
2475 :
2476 137 : PG_RETURN_TEXT_P(string_to_text(consrc));
2477 : }
2478 :
2479 : /*
2480 : * pg_get_partconstrdef_string
2481 : *
2482 : * Returns the partition constraint as a C-string for the input relation, with
2483 : * the given alias. No pretty-printing.
2484 : */
2485 : char *
2486 65 : pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
2487 : {
2488 : Expr *constr_expr;
2489 : List *context;
2490 :
2491 65 : constr_expr = get_partition_qual_relid(partitionId);
2492 65 : context = deparse_context_for(aliasname, partitionId);
2493 :
2494 65 : return deparse_expression((Node *) constr_expr, context, true, false);
2495 : }
2496 :
2497 : /*
2498 : * pg_get_constraintdef
2499 : *
2500 : * Returns the definition for the constraint, ie, everything that needs to
2501 : * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
2502 : */
2503 : Datum
2504 1188 : pg_get_constraintdef(PG_FUNCTION_ARGS)
2505 : {
2506 1188 : Oid constraintId = PG_GETARG_OID(0);
2507 : int prettyFlags;
2508 : char *res;
2509 :
2510 1188 : prettyFlags = PRETTYFLAG_INDENT;
2511 :
2512 1188 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2513 :
2514 1188 : if (res == NULL)
2515 4 : PG_RETURN_NULL();
2516 :
2517 1184 : PG_RETURN_TEXT_P(string_to_text(res));
2518 : }
2519 :
2520 : Datum
2521 2917 : pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
2522 : {
2523 2917 : Oid constraintId = PG_GETARG_OID(0);
2524 2917 : bool pretty = PG_GETARG_BOOL(1);
2525 : int prettyFlags;
2526 : char *res;
2527 :
2528 2917 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2529 :
2530 2917 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2531 :
2532 2917 : if (res == NULL)
2533 0 : PG_RETURN_NULL();
2534 :
2535 2917 : PG_RETURN_TEXT_P(string_to_text(res));
2536 : }
2537 :
2538 : /*
2539 : * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
2540 : */
2541 : char *
2542 452 : pg_get_constraintdef_command(Oid constraintId)
2543 : {
2544 452 : return pg_get_constraintdef_worker(constraintId, true, 0, false);
2545 : }
2546 :
2547 : /*
2548 : * As of 9.4, we now use an MVCC snapshot for this.
2549 : */
2550 : static char *
2551 4557 : pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
2552 : int prettyFlags, bool missing_ok)
2553 : {
2554 : HeapTuple tup;
2555 : Form_pg_constraint conForm;
2556 : StringInfoData buf;
2557 : SysScanDesc scandesc;
2558 : ScanKeyData scankey[1];
2559 4557 : Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());
2560 4557 : Relation relation = table_open(ConstraintRelationId, AccessShareLock);
2561 :
2562 4557 : ScanKeyInit(&scankey[0],
2563 : Anum_pg_constraint_oid,
2564 : BTEqualStrategyNumber, F_OIDEQ,
2565 : ObjectIdGetDatum(constraintId));
2566 :
2567 4557 : scandesc = systable_beginscan(relation,
2568 : ConstraintOidIndexId,
2569 : true,
2570 : snapshot,
2571 : 1,
2572 : scankey);
2573 :
2574 : /*
2575 : * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2576 : * via SearchSysCache, which works fine.
2577 : */
2578 4557 : tup = systable_getnext(scandesc);
2579 :
2580 4557 : UnregisterSnapshot(snapshot);
2581 :
2582 4557 : if (!HeapTupleIsValid(tup))
2583 : {
2584 4 : if (missing_ok)
2585 : {
2586 4 : systable_endscan(scandesc);
2587 4 : table_close(relation, AccessShareLock);
2588 4 : return NULL;
2589 : }
2590 0 : elog(ERROR, "could not find tuple for constraint %u", constraintId);
2591 : }
2592 :
2593 4553 : conForm = (Form_pg_constraint) GETSTRUCT(tup);
2594 :
2595 4553 : initStringInfo(&buf);
2596 :
2597 4553 : if (fullCommand)
2598 : {
2599 452 : if (OidIsValid(conForm->conrelid))
2600 : {
2601 : /*
2602 : * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2603 : * constraints, and other types of constraints don't inherit
2604 : * anyway so it doesn't matter whether we say ONLY or not. Someday
2605 : * we might need to let callers specify whether to put ONLY in the
2606 : * command.
2607 : */
2608 443 : appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2609 : generate_qualified_relation_name(conForm->conrelid),
2610 443 : quote_identifier(NameStr(conForm->conname)));
2611 : }
2612 : else
2613 : {
2614 : /* Must be a domain constraint */
2615 : Assert(OidIsValid(conForm->contypid));
2616 9 : appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2617 : generate_qualified_type_name(conForm->contypid),
2618 9 : quote_identifier(NameStr(conForm->conname)));
2619 : }
2620 : }
2621 :
2622 4553 : switch (conForm->contype)
2623 : {
2624 541 : case CONSTRAINT_FOREIGN:
2625 : {
2626 : Datum val;
2627 : bool isnull;
2628 : const char *string;
2629 :
2630 : /* Start off the constraint definition */
2631 541 : appendStringInfoString(&buf, "FOREIGN KEY (");
2632 :
2633 : /* Fetch and build referencing-column list */
2634 541 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2635 : Anum_pg_constraint_conkey);
2636 :
2637 : /* If it is a temporal foreign key then it uses PERIOD. */
2638 541 : decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
2639 :
2640 : /* add foreign relation name */
2641 541 : appendStringInfo(&buf, ") REFERENCES %s(",
2642 : generate_relation_name(conForm->confrelid,
2643 : NIL));
2644 :
2645 : /* Fetch and build referenced-column list */
2646 541 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2647 : Anum_pg_constraint_confkey);
2648 :
2649 541 : decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
2650 :
2651 541 : appendStringInfoChar(&buf, ')');
2652 :
2653 : /* Add match type */
2654 541 : switch (conForm->confmatchtype)
2655 : {
2656 21 : case FKCONSTR_MATCH_FULL:
2657 21 : string = " MATCH FULL";
2658 21 : break;
2659 0 : case FKCONSTR_MATCH_PARTIAL:
2660 0 : string = " MATCH PARTIAL";
2661 0 : break;
2662 520 : case FKCONSTR_MATCH_SIMPLE:
2663 520 : string = "";
2664 520 : break;
2665 0 : default:
2666 0 : elog(ERROR, "unrecognized confmatchtype: %d",
2667 : conForm->confmatchtype);
2668 : string = ""; /* keep compiler quiet */
2669 : break;
2670 : }
2671 541 : appendStringInfoString(&buf, string);
2672 :
2673 : /* Add ON UPDATE and ON DELETE clauses, if needed */
2674 541 : switch (conForm->confupdtype)
2675 : {
2676 457 : case FKCONSTR_ACTION_NOACTION:
2677 457 : string = NULL; /* suppress default */
2678 457 : break;
2679 0 : case FKCONSTR_ACTION_RESTRICT:
2680 0 : string = "RESTRICT";
2681 0 : break;
2682 67 : case FKCONSTR_ACTION_CASCADE:
2683 67 : string = "CASCADE";
2684 67 : break;
2685 17 : case FKCONSTR_ACTION_SETNULL:
2686 17 : string = "SET NULL";
2687 17 : break;
2688 0 : case FKCONSTR_ACTION_SETDEFAULT:
2689 0 : string = "SET DEFAULT";
2690 0 : break;
2691 0 : default:
2692 0 : elog(ERROR, "unrecognized confupdtype: %d",
2693 : conForm->confupdtype);
2694 : string = NULL; /* keep compiler quiet */
2695 : break;
2696 : }
2697 541 : if (string)
2698 84 : appendStringInfo(&buf, " ON UPDATE %s", string);
2699 :
2700 541 : switch (conForm->confdeltype)
2701 : {
2702 458 : case FKCONSTR_ACTION_NOACTION:
2703 458 : string = NULL; /* suppress default */
2704 458 : break;
2705 0 : case FKCONSTR_ACTION_RESTRICT:
2706 0 : string = "RESTRICT";
2707 0 : break;
2708 67 : case FKCONSTR_ACTION_CASCADE:
2709 67 : string = "CASCADE";
2710 67 : break;
2711 12 : case FKCONSTR_ACTION_SETNULL:
2712 12 : string = "SET NULL";
2713 12 : break;
2714 4 : case FKCONSTR_ACTION_SETDEFAULT:
2715 4 : string = "SET DEFAULT";
2716 4 : break;
2717 0 : default:
2718 0 : elog(ERROR, "unrecognized confdeltype: %d",
2719 : conForm->confdeltype);
2720 : string = NULL; /* keep compiler quiet */
2721 : break;
2722 : }
2723 541 : if (string)
2724 83 : appendStringInfo(&buf, " ON DELETE %s", string);
2725 :
2726 : /*
2727 : * Add columns specified to SET NULL or SET DEFAULT if
2728 : * provided.
2729 : */
2730 541 : val = SysCacheGetAttr(CONSTROID, tup,
2731 : Anum_pg_constraint_confdelsetcols, &isnull);
2732 541 : if (!isnull)
2733 : {
2734 8 : appendStringInfoString(&buf, " (");
2735 8 : decompile_column_index_array(val, conForm->conrelid, false, &buf);
2736 8 : appendStringInfoChar(&buf, ')');
2737 : }
2738 :
2739 541 : break;
2740 : }
2741 2315 : case CONSTRAINT_PRIMARY:
2742 : case CONSTRAINT_UNIQUE:
2743 : {
2744 : Datum val;
2745 : Oid indexId;
2746 : int keyatts;
2747 : HeapTuple indtup;
2748 :
2749 : /* Start off the constraint definition */
2750 2315 : if (conForm->contype == CONSTRAINT_PRIMARY)
2751 1889 : appendStringInfoString(&buf, "PRIMARY KEY ");
2752 : else
2753 426 : appendStringInfoString(&buf, "UNIQUE ");
2754 :
2755 2315 : indexId = conForm->conindid;
2756 :
2757 2315 : indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
2758 2315 : if (!HeapTupleIsValid(indtup))
2759 0 : elog(ERROR, "cache lookup failed for index %u", indexId);
2760 2315 : if (conForm->contype == CONSTRAINT_UNIQUE &&
2761 426 : ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
2762 0 : appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2763 :
2764 2315 : appendStringInfoChar(&buf, '(');
2765 :
2766 : /* Fetch and build target column list */
2767 2315 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2768 : Anum_pg_constraint_conkey);
2769 :
2770 2315 : keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
2771 2315 : if (conForm->conperiod)
2772 220 : appendStringInfoString(&buf, " WITHOUT OVERLAPS");
2773 :
2774 2315 : appendStringInfoChar(&buf, ')');
2775 :
2776 : /* Build including column list (from pg_index.indkeys) */
2777 2315 : val = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2778 : Anum_pg_index_indnatts);
2779 2315 : if (DatumGetInt32(val) > keyatts)
2780 : {
2781 : Datum cols;
2782 : Datum *keys;
2783 : int nKeys;
2784 : int j;
2785 :
2786 48 : appendStringInfoString(&buf, " INCLUDE (");
2787 :
2788 48 : cols = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2789 : Anum_pg_index_indkey);
2790 :
2791 48 : deconstruct_array_builtin(DatumGetArrayTypeP(cols), INT2OID,
2792 : &keys, NULL, &nKeys);
2793 :
2794 144 : for (j = keyatts; j < nKeys; j++)
2795 : {
2796 : char *colName;
2797 :
2798 96 : colName = get_attname(conForm->conrelid,
2799 96 : DatumGetInt16(keys[j]), false);
2800 96 : if (j > keyatts)
2801 48 : appendStringInfoString(&buf, ", ");
2802 96 : appendStringInfoString(&buf, quote_identifier(colName));
2803 : }
2804 :
2805 48 : appendStringInfoChar(&buf, ')');
2806 : }
2807 2315 : ReleaseSysCache(indtup);
2808 :
2809 : /* XXX why do we only print these bits if fullCommand? */
2810 2315 : if (fullCommand && OidIsValid(indexId))
2811 : {
2812 136 : char *options = flatten_reloptions(indexId);
2813 : Oid tblspc;
2814 :
2815 136 : if (options)
2816 : {
2817 0 : appendStringInfo(&buf, " WITH (%s)", options);
2818 0 : pfree(options);
2819 : }
2820 :
2821 : /*
2822 : * Print the tablespace, unless it's the database default.
2823 : * This is to help ALTER TABLE usage of this facility,
2824 : * which needs this behavior to recreate exact catalog
2825 : * state.
2826 : */
2827 136 : tblspc = get_rel_tablespace(indexId);
2828 136 : if (OidIsValid(tblspc))
2829 16 : appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2830 16 : quote_identifier(get_tablespace_name(tblspc)));
2831 : }
2832 :
2833 2315 : break;
2834 : }
2835 1344 : case CONSTRAINT_CHECK:
2836 : {
2837 : Datum val;
2838 : char *conbin;
2839 : char *consrc;
2840 : Node *expr;
2841 : List *context;
2842 :
2843 : /* Fetch constraint expression in parsetree form */
2844 1344 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2845 : Anum_pg_constraint_conbin);
2846 :
2847 1344 : conbin = TextDatumGetCString(val);
2848 1344 : expr = stringToNode(conbin);
2849 :
2850 : /* Set up deparsing context for Var nodes in constraint */
2851 1344 : if (conForm->conrelid != InvalidOid)
2852 : {
2853 : /* relation constraint */
2854 1204 : context = deparse_context_for(get_relation_name(conForm->conrelid),
2855 : conForm->conrelid);
2856 : }
2857 : else
2858 : {
2859 : /* domain constraint --- can't have Vars */
2860 140 : context = NIL;
2861 : }
2862 :
2863 1344 : consrc = deparse_expression_pretty(expr, context, false, false,
2864 : prettyFlags, 0);
2865 :
2866 : /*
2867 : * Now emit the constraint definition, adding NO INHERIT if
2868 : * necessary.
2869 : *
2870 : * There are cases where the constraint expression will be
2871 : * fully parenthesized and we don't need the outer parens ...
2872 : * but there are other cases where we do need 'em. Be
2873 : * conservative for now.
2874 : *
2875 : * Note that simply checking for leading '(' and trailing ')'
2876 : * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2877 : */
2878 1344 : appendStringInfo(&buf, "CHECK (%s)%s",
2879 : consrc,
2880 1344 : conForm->connoinherit ? " NO INHERIT" : "");
2881 1344 : break;
2882 : }
2883 287 : case CONSTRAINT_NOTNULL:
2884 : {
2885 287 : if (conForm->conrelid)
2886 : {
2887 : AttrNumber attnum;
2888 :
2889 230 : attnum = extractNotNullColumn(tup);
2890 :
2891 230 : appendStringInfo(&buf, "NOT NULL %s",
2892 230 : quote_identifier(get_attname(conForm->conrelid,
2893 : attnum, false)));
2894 230 : if (((Form_pg_constraint) GETSTRUCT(tup))->connoinherit)
2895 0 : appendStringInfoString(&buf, " NO INHERIT");
2896 : }
2897 57 : else if (conForm->contypid)
2898 : {
2899 : /* conkey is null for domain not-null constraints */
2900 57 : appendStringInfoString(&buf, "NOT NULL");
2901 : }
2902 287 : break;
2903 : }
2904 :
2905 0 : case CONSTRAINT_TRIGGER:
2906 :
2907 : /*
2908 : * There isn't an ALTER TABLE syntax for creating a user-defined
2909 : * constraint trigger, but it seems better to print something than
2910 : * throw an error; if we throw error then this function couldn't
2911 : * safely be applied to all rows of pg_constraint.
2912 : */
2913 0 : appendStringInfoString(&buf, "TRIGGER");
2914 0 : break;
2915 66 : case CONSTRAINT_EXCLUSION:
2916 : {
2917 66 : Oid indexOid = conForm->conindid;
2918 : Datum val;
2919 : Datum *elems;
2920 : int nElems;
2921 : int i;
2922 : Oid *operators;
2923 :
2924 : /* Extract operator OIDs from the pg_constraint tuple */
2925 66 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2926 : Anum_pg_constraint_conexclop);
2927 :
2928 66 : deconstruct_array_builtin(DatumGetArrayTypeP(val), OIDOID,
2929 : &elems, NULL, &nElems);
2930 :
2931 66 : operators = (Oid *) palloc(nElems * sizeof(Oid));
2932 142 : for (i = 0; i < nElems; i++)
2933 76 : operators[i] = DatumGetObjectId(elems[i]);
2934 :
2935 : /* pg_get_indexdef_worker does the rest */
2936 : /* suppress tablespace because pg_dump wants it that way */
2937 66 : appendStringInfoString(&buf,
2938 66 : pg_get_indexdef_worker(indexOid,
2939 : 0,
2940 : operators,
2941 : false,
2942 : false,
2943 : false,
2944 : false,
2945 : prettyFlags,
2946 : false));
2947 66 : break;
2948 : }
2949 0 : default:
2950 0 : elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2951 : break;
2952 : }
2953 :
2954 4553 : if (conForm->condeferrable)
2955 86 : appendStringInfoString(&buf, " DEFERRABLE");
2956 4553 : if (conForm->condeferred)
2957 39 : appendStringInfoString(&buf, " INITIALLY DEFERRED");
2958 :
2959 : /* Validated status is irrelevant when the constraint is NOT ENFORCED. */
2960 4553 : if (!conForm->conenforced)
2961 77 : appendStringInfoString(&buf, " NOT ENFORCED");
2962 4476 : else if (!conForm->convalidated)
2963 148 : appendStringInfoString(&buf, " NOT VALID");
2964 :
2965 : /* Cleanup */
2966 4553 : systable_endscan(scandesc);
2967 4553 : table_close(relation, AccessShareLock);
2968 :
2969 4553 : return buf.data;
2970 : }
2971 :
2972 :
2973 : /*
2974 : * Convert an int16[] Datum into a comma-separated list of column names
2975 : * for the indicated relation; append the list to buf. Returns the number
2976 : * of keys.
2977 : */
2978 : static int
2979 4432 : decompile_column_index_array(Datum column_index_array, Oid relId,
2980 : bool withPeriod, StringInfo buf)
2981 : {
2982 : Datum *keys;
2983 : int nKeys;
2984 : int j;
2985 :
2986 : /* Extract data from array of int16 */
2987 4432 : deconstruct_array_builtin(DatumGetArrayTypeP(column_index_array), INT2OID,
2988 : &keys, NULL, &nKeys);
2989 :
2990 10411 : for (j = 0; j < nKeys; j++)
2991 : {
2992 : char *colName;
2993 :
2994 5979 : colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2995 :
2996 5979 : if (j == 0)
2997 4432 : appendStringInfoString(buf, quote_identifier(colName));
2998 : else
2999 1681 : appendStringInfo(buf, ", %s%s",
3000 134 : (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
3001 : quote_identifier(colName));
3002 : }
3003 :
3004 4432 : return nKeys;
3005 : }
3006 :
3007 :
3008 : /* ----------
3009 : * pg_get_expr - Decompile an expression tree
3010 : *
3011 : * Input: an expression tree in nodeToString form, and a relation OID
3012 : *
3013 : * Output: reverse-listed expression
3014 : *
3015 : * Currently, the expression can only refer to a single relation, namely
3016 : * the one specified by the second parameter. This is sufficient for
3017 : * partial indexes, column default expressions, etc. We also support
3018 : * Var-free expressions, for which the OID can be InvalidOid.
3019 : *
3020 : * If the OID is nonzero but not actually valid, don't throw an error,
3021 : * just return NULL. This is a bit questionable, but it's what we've
3022 : * done historically, and it can help avoid unwanted failures when
3023 : * examining catalog entries for just-deleted relations.
3024 : *
3025 : * We expect this function to work, or throw a reasonably clean error,
3026 : * for any node tree that can appear in a catalog pg_node_tree column.
3027 : * Query trees, such as those appearing in pg_rewrite.ev_action, are
3028 : * not supported. Nor are expressions in more than one relation, which
3029 : * can appear in places like pg_rewrite.ev_qual.
3030 : * ----------
3031 : */
3032 : Datum
3033 5516 : pg_get_expr(PG_FUNCTION_ARGS)
3034 : {
3035 5516 : text *expr = PG_GETARG_TEXT_PP(0);
3036 5516 : Oid relid = PG_GETARG_OID(1);
3037 : text *result;
3038 : int prettyFlags;
3039 :
3040 5516 : prettyFlags = PRETTYFLAG_INDENT;
3041 :
3042 5516 : result = pg_get_expr_worker(expr, relid, prettyFlags);
3043 5516 : if (result)
3044 5516 : PG_RETURN_TEXT_P(result);
3045 : else
3046 0 : PG_RETURN_NULL();
3047 : }
3048 :
3049 : Datum
3050 572 : pg_get_expr_ext(PG_FUNCTION_ARGS)
3051 : {
3052 572 : text *expr = PG_GETARG_TEXT_PP(0);
3053 572 : Oid relid = PG_GETARG_OID(1);
3054 572 : bool pretty = PG_GETARG_BOOL(2);
3055 : text *result;
3056 : int prettyFlags;
3057 :
3058 572 : prettyFlags = GET_PRETTY_FLAGS(pretty);
3059 :
3060 572 : result = pg_get_expr_worker(expr, relid, prettyFlags);
3061 572 : if (result)
3062 572 : PG_RETURN_TEXT_P(result);
3063 : else
3064 0 : PG_RETURN_NULL();
3065 : }
3066 :
3067 : static text *
3068 6088 : pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
3069 : {
3070 : Node *node;
3071 : Node *tst;
3072 : Relids relids;
3073 : List *context;
3074 : char *exprstr;
3075 6088 : Relation rel = NULL;
3076 : char *str;
3077 :
3078 : /* Convert input pg_node_tree (really TEXT) object to C string */
3079 6088 : exprstr = text_to_cstring(expr);
3080 :
3081 : /* Convert expression to node tree */
3082 6088 : node = (Node *) stringToNode(exprstr);
3083 :
3084 6088 : pfree(exprstr);
3085 :
3086 : /*
3087 : * Throw error if the input is a querytree rather than an expression tree.
3088 : * While we could support queries here, there seems no very good reason
3089 : * to. In most such catalog columns, we'll see a List of Query nodes, or
3090 : * even nested Lists, so drill down to a non-List node before checking.
3091 : */
3092 6088 : tst = node;
3093 6088 : while (tst && IsA(tst, List))
3094 0 : tst = linitial((List *) tst);
3095 6088 : if (tst && IsA(tst, Query))
3096 0 : ereport(ERROR,
3097 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3098 : errmsg("input is a query, not an expression")));
3099 :
3100 : /*
3101 : * Throw error if the expression contains Vars we won't be able to
3102 : * deparse.
3103 : */
3104 6088 : relids = pull_varnos(NULL, node);
3105 6088 : if (OidIsValid(relid))
3106 : {
3107 6024 : if (!bms_is_subset(relids, bms_make_singleton(1)))
3108 0 : ereport(ERROR,
3109 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3110 : errmsg("expression contains variables of more than one relation")));
3111 : }
3112 : else
3113 : {
3114 64 : if (!bms_is_empty(relids))
3115 0 : ereport(ERROR,
3116 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3117 : errmsg("expression contains variables")));
3118 : }
3119 :
3120 : /*
3121 : * Prepare deparse context if needed. If we are deparsing with a relid,
3122 : * we need to transiently open and lock the rel, to make sure it won't go
3123 : * away underneath us. (set_relation_column_names would lock it anyway,
3124 : * so this isn't really introducing any new behavior.)
3125 : */
3126 6088 : if (OidIsValid(relid))
3127 : {
3128 6024 : rel = try_relation_open(relid, AccessShareLock);
3129 6024 : if (rel == NULL)
3130 0 : return NULL;
3131 6024 : context = deparse_context_for(RelationGetRelationName(rel), relid);
3132 : }
3133 : else
3134 64 : context = NIL;
3135 :
3136 : /* Deparse */
3137 6088 : str = deparse_expression_pretty(node, context, false, false,
3138 : prettyFlags, 0);
3139 :
3140 6088 : if (rel != NULL)
3141 6024 : relation_close(rel, AccessShareLock);
3142 :
3143 6088 : return string_to_text(str);
3144 : }
3145 :
3146 :
3147 : /* ----------
3148 : * pg_get_userbyid - Get a user name by roleid and
3149 : * fallback to 'unknown (OID=n)'
3150 : * ----------
3151 : */
3152 : Datum
3153 1263 : pg_get_userbyid(PG_FUNCTION_ARGS)
3154 : {
3155 1263 : Oid roleid = PG_GETARG_OID(0);
3156 : Name result;
3157 : HeapTuple roletup;
3158 : Form_pg_authid role_rec;
3159 :
3160 : /*
3161 : * Allocate space for the result
3162 : */
3163 1263 : result = (Name) palloc(NAMEDATALEN);
3164 1263 : memset(NameStr(*result), 0, NAMEDATALEN);
3165 :
3166 : /*
3167 : * Get the pg_authid entry and print the result
3168 : */
3169 1263 : roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
3170 1263 : if (HeapTupleIsValid(roletup))
3171 : {
3172 1263 : role_rec = (Form_pg_authid) GETSTRUCT(roletup);
3173 1263 : *result = role_rec->rolname;
3174 1263 : ReleaseSysCache(roletup);
3175 : }
3176 : else
3177 0 : sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
3178 :
3179 1263 : PG_RETURN_NAME(result);
3180 : }
3181 :
3182 :
3183 : /*
3184 : * pg_get_serial_sequence
3185 : * Get the name of the sequence used by an identity or serial column,
3186 : * formatted suitably for passing to setval, nextval or currval.
3187 : * First parameter is not treated as double-quoted, second parameter
3188 : * is --- see documentation for reason.
3189 : */
3190 : Datum
3191 8 : pg_get_serial_sequence(PG_FUNCTION_ARGS)
3192 : {
3193 8 : text *tablename = PG_GETARG_TEXT_PP(0);
3194 8 : text *columnname = PG_GETARG_TEXT_PP(1);
3195 : RangeVar *tablerv;
3196 : Oid tableOid;
3197 : char *column;
3198 : AttrNumber attnum;
3199 8 : Oid sequenceId = InvalidOid;
3200 : Relation depRel;
3201 : ScanKeyData key[3];
3202 : SysScanDesc scan;
3203 : HeapTuple tup;
3204 :
3205 : /* Look up table name. Can't lock it - we might not have privileges. */
3206 8 : tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
3207 8 : tableOid = RangeVarGetRelid(tablerv, NoLock, false);
3208 :
3209 : /* Get the number of the column */
3210 8 : column = text_to_cstring(columnname);
3211 :
3212 8 : attnum = get_attnum(tableOid, column);
3213 8 : if (attnum == InvalidAttrNumber)
3214 0 : ereport(ERROR,
3215 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3216 : errmsg("column \"%s\" of relation \"%s\" does not exist",
3217 : column, tablerv->relname)));
3218 :
3219 : /* Search the dependency table for the dependent sequence */
3220 8 : depRel = table_open(DependRelationId, AccessShareLock);
3221 :
3222 8 : ScanKeyInit(&key[0],
3223 : Anum_pg_depend_refclassid,
3224 : BTEqualStrategyNumber, F_OIDEQ,
3225 : ObjectIdGetDatum(RelationRelationId));
3226 8 : ScanKeyInit(&key[1],
3227 : Anum_pg_depend_refobjid,
3228 : BTEqualStrategyNumber, F_OIDEQ,
3229 : ObjectIdGetDatum(tableOid));
3230 8 : ScanKeyInit(&key[2],
3231 : Anum_pg_depend_refobjsubid,
3232 : BTEqualStrategyNumber, F_INT4EQ,
3233 : Int32GetDatum(attnum));
3234 :
3235 8 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
3236 : NULL, 3, key);
3237 :
3238 20 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
3239 : {
3240 20 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
3241 :
3242 : /*
3243 : * Look for an auto dependency (serial column) or internal dependency
3244 : * (identity column) of a sequence on a column. (We need the relkind
3245 : * test because indexes can also have auto dependencies on columns.)
3246 : */
3247 20 : if (deprec->classid == RelationRelationId &&
3248 8 : deprec->objsubid == 0 &&
3249 8 : (deprec->deptype == DEPENDENCY_AUTO ||
3250 12 : deprec->deptype == DEPENDENCY_INTERNAL) &&
3251 8 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
3252 : {
3253 8 : sequenceId = deprec->objid;
3254 8 : break;
3255 : }
3256 : }
3257 :
3258 8 : systable_endscan(scan);
3259 8 : table_close(depRel, AccessShareLock);
3260 :
3261 8 : if (OidIsValid(sequenceId))
3262 : {
3263 : char *result;
3264 :
3265 8 : result = generate_qualified_relation_name(sequenceId);
3266 :
3267 8 : PG_RETURN_TEXT_P(string_to_text(result));
3268 : }
3269 :
3270 0 : PG_RETURN_NULL();
3271 : }
3272 :
3273 :
3274 : /*
3275 : * pg_get_functiondef
3276 : * Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
3277 : * the specified function.
3278 : *
3279 : * Note: if you change the output format of this function, be careful not
3280 : * to break psql's rules (in \ef and \sf) for identifying the start of the
3281 : * function body. To wit: the function body starts on a line that begins with
3282 : * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
3283 : */
3284 : Datum
3285 127 : pg_get_functiondef(PG_FUNCTION_ARGS)
3286 : {
3287 127 : Oid funcid = PG_GETARG_OID(0);
3288 : StringInfoData buf;
3289 : StringInfoData dq;
3290 : HeapTuple proctup;
3291 : Form_pg_proc proc;
3292 : bool isfunction;
3293 : Datum tmp;
3294 : bool isnull;
3295 : const char *prosrc;
3296 : const char *name;
3297 : const char *nsp;
3298 : float4 procost;
3299 : int oldlen;
3300 :
3301 127 : initStringInfo(&buf);
3302 :
3303 : /* Look up the function */
3304 127 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3305 127 : if (!HeapTupleIsValid(proctup))
3306 4 : PG_RETURN_NULL();
3307 :
3308 123 : proc = (Form_pg_proc) GETSTRUCT(proctup);
3309 123 : name = NameStr(proc->proname);
3310 :
3311 123 : if (proc->prokind == PROKIND_AGGREGATE)
3312 0 : ereport(ERROR,
3313 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3314 : errmsg("\"%s\" is an aggregate function", name)));
3315 :
3316 123 : isfunction = (proc->prokind != PROKIND_PROCEDURE);
3317 :
3318 : /*
3319 : * We always qualify the function name, to ensure the right function gets
3320 : * replaced.
3321 : */
3322 123 : nsp = get_namespace_name_or_temp(proc->pronamespace);
3323 123 : appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
3324 : isfunction ? "FUNCTION" : "PROCEDURE",
3325 : quote_qualified_identifier(nsp, name));
3326 123 : (void) print_function_arguments(&buf, proctup, false, true);
3327 123 : appendStringInfoString(&buf, ")\n");
3328 123 : if (isfunction)
3329 : {
3330 110 : appendStringInfoString(&buf, " RETURNS ");
3331 110 : print_function_rettype(&buf, proctup);
3332 110 : appendStringInfoChar(&buf, '\n');
3333 : }
3334 :
3335 123 : print_function_trftypes(&buf, proctup);
3336 :
3337 123 : appendStringInfo(&buf, " LANGUAGE %s\n",
3338 123 : quote_identifier(get_language_name(proc->prolang, false)));
3339 :
3340 : /* Emit some miscellaneous options on one line */
3341 123 : oldlen = buf.len;
3342 :
3343 123 : if (proc->prokind == PROKIND_WINDOW)
3344 0 : appendStringInfoString(&buf, " WINDOW");
3345 123 : switch (proc->provolatile)
3346 : {
3347 8 : case PROVOLATILE_IMMUTABLE:
3348 8 : appendStringInfoString(&buf, " IMMUTABLE");
3349 8 : break;
3350 20 : case PROVOLATILE_STABLE:
3351 20 : appendStringInfoString(&buf, " STABLE");
3352 20 : break;
3353 95 : case PROVOLATILE_VOLATILE:
3354 95 : break;
3355 : }
3356 :
3357 123 : switch (proc->proparallel)
3358 : {
3359 17 : case PROPARALLEL_SAFE:
3360 17 : appendStringInfoString(&buf, " PARALLEL SAFE");
3361 17 : break;
3362 0 : case PROPARALLEL_RESTRICTED:
3363 0 : appendStringInfoString(&buf, " PARALLEL RESTRICTED");
3364 0 : break;
3365 106 : case PROPARALLEL_UNSAFE:
3366 106 : break;
3367 : }
3368 :
3369 123 : if (proc->proisstrict)
3370 32 : appendStringInfoString(&buf, " STRICT");
3371 123 : if (proc->prosecdef)
3372 4 : appendStringInfoString(&buf, " SECURITY DEFINER");
3373 123 : if (proc->proleakproof)
3374 0 : appendStringInfoString(&buf, " LEAKPROOF");
3375 :
3376 : /* This code for the default cost and rows should match functioncmds.c */
3377 123 : if (proc->prolang == INTERNALlanguageId ||
3378 123 : proc->prolang == ClanguageId)
3379 5 : procost = 1;
3380 : else
3381 118 : procost = 100;
3382 123 : if (proc->procost != procost)
3383 4 : appendStringInfo(&buf, " COST %g", proc->procost);
3384 :
3385 123 : if (proc->prorows > 0 && proc->prorows != 1000)
3386 0 : appendStringInfo(&buf, " ROWS %g", proc->prorows);
3387 :
3388 123 : if (proc->prosupport)
3389 : {
3390 : Oid argtypes[1];
3391 :
3392 : /*
3393 : * We should qualify the support function's name if it wouldn't be
3394 : * resolved by lookup in the current search path.
3395 : */
3396 0 : argtypes[0] = INTERNALOID;
3397 0 : appendStringInfo(&buf, " SUPPORT %s",
3398 : generate_function_name(proc->prosupport, 1,
3399 : NIL, argtypes,
3400 : false, NULL, false));
3401 : }
3402 :
3403 123 : if (oldlen != buf.len)
3404 41 : appendStringInfoChar(&buf, '\n');
3405 :
3406 : /* Emit any proconfig options, one per line */
3407 123 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
3408 123 : if (!isnull)
3409 : {
3410 4 : ArrayType *a = DatumGetArrayTypeP(tmp);
3411 : int i;
3412 :
3413 : Assert(ARR_ELEMTYPE(a) == TEXTOID);
3414 : Assert(ARR_NDIM(a) == 1);
3415 : Assert(ARR_LBOUND(a)[0] == 1);
3416 :
3417 28 : for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3418 : {
3419 : Datum d;
3420 :
3421 24 : d = array_ref(a, 1, &i,
3422 : -1 /* varlenarray */ ,
3423 : -1 /* TEXT's typlen */ ,
3424 : false /* TEXT's typbyval */ ,
3425 : TYPALIGN_INT /* TEXT's typalign */ ,
3426 : &isnull);
3427 24 : if (!isnull)
3428 : {
3429 24 : char *configitem = TextDatumGetCString(d);
3430 : char *pos;
3431 :
3432 24 : pos = strchr(configitem, '=');
3433 24 : if (pos == NULL)
3434 0 : continue;
3435 24 : *pos++ = '\0';
3436 :
3437 24 : appendStringInfo(&buf, " SET %s TO ",
3438 : quote_identifier(configitem));
3439 :
3440 : /*
3441 : * Variables that are marked GUC_LIST_QUOTE were already fully
3442 : * quoted by flatten_set_variable_args() before they were put
3443 : * into the proconfig array. However, because the quoting
3444 : * rules used there aren't exactly like SQL's, we have to
3445 : * break the list value apart and then quote the elements as
3446 : * string literals. (The elements may be double-quoted as-is,
3447 : * but we can't just feed them to the SQL parser; it would do
3448 : * the wrong thing with elements that are zero-length or
3449 : * longer than NAMEDATALEN.) Also, we need a special case for
3450 : * empty lists.
3451 : *
3452 : * Variables that are not so marked should just be emitted as
3453 : * simple string literals. If the variable is not known to
3454 : * guc.c, we'll do that; this makes it unsafe to use
3455 : * GUC_LIST_QUOTE for extension variables.
3456 : */
3457 24 : if (GetConfigOptionFlags(configitem, true) & GUC_LIST_QUOTE)
3458 : {
3459 : List *namelist;
3460 : ListCell *lc;
3461 :
3462 : /* Parse string into list of identifiers */
3463 12 : if (!SplitGUCList(pos, ',', &namelist))
3464 : {
3465 : /* this shouldn't fail really */
3466 0 : elog(ERROR, "invalid list syntax in proconfig item");
3467 : }
3468 : /* Special case: represent an empty list as NULL */
3469 12 : if (namelist == NIL)
3470 4 : appendStringInfoString(&buf, "NULL");
3471 32 : foreach(lc, namelist)
3472 : {
3473 20 : char *curname = (char *) lfirst(lc);
3474 :
3475 20 : simple_quote_literal(&buf, curname);
3476 20 : if (lnext(namelist, lc))
3477 12 : appendStringInfoString(&buf, ", ");
3478 : }
3479 : }
3480 : else
3481 12 : simple_quote_literal(&buf, pos);
3482 24 : appendStringInfoChar(&buf, '\n');
3483 : }
3484 : }
3485 : }
3486 :
3487 : /* And finally the function definition ... */
3488 123 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3489 123 : if (proc->prolang == SQLlanguageId && !isnull)
3490 : {
3491 91 : print_function_sqlbody(&buf, proctup);
3492 : }
3493 : else
3494 : {
3495 32 : appendStringInfoString(&buf, "AS ");
3496 :
3497 32 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
3498 32 : if (!isnull)
3499 : {
3500 5 : simple_quote_literal(&buf, TextDatumGetCString(tmp));
3501 5 : appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3502 : }
3503 :
3504 32 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosrc);
3505 32 : prosrc = TextDatumGetCString(tmp);
3506 :
3507 : /*
3508 : * We always use dollar quoting. Figure out a suitable delimiter.
3509 : *
3510 : * Since the user is likely to be editing the function body string, we
3511 : * shouldn't use a short delimiter that he might easily create a
3512 : * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3513 : * if needed.
3514 : */
3515 32 : initStringInfo(&dq);
3516 32 : appendStringInfoChar(&dq, '$');
3517 32 : appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3518 32 : while (strstr(prosrc, dq.data) != NULL)
3519 0 : appendStringInfoChar(&dq, 'x');
3520 32 : appendStringInfoChar(&dq, '$');
3521 :
3522 32 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3523 32 : appendStringInfoString(&buf, prosrc);
3524 32 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3525 : }
3526 :
3527 123 : appendStringInfoChar(&buf, '\n');
3528 :
3529 123 : ReleaseSysCache(proctup);
3530 :
3531 123 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3532 : }
3533 :
3534 : /*
3535 : * pg_get_function_arguments
3536 : * Get a nicely-formatted list of arguments for a function.
3537 : * This is everything that would go between the parentheses in
3538 : * CREATE FUNCTION.
3539 : */
3540 : Datum
3541 2486 : pg_get_function_arguments(PG_FUNCTION_ARGS)
3542 : {
3543 2486 : Oid funcid = PG_GETARG_OID(0);
3544 : StringInfoData buf;
3545 : HeapTuple proctup;
3546 :
3547 2486 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3548 2486 : if (!HeapTupleIsValid(proctup))
3549 4 : PG_RETURN_NULL();
3550 :
3551 2482 : initStringInfo(&buf);
3552 :
3553 2482 : (void) print_function_arguments(&buf, proctup, false, true);
3554 :
3555 2482 : ReleaseSysCache(proctup);
3556 :
3557 2482 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3558 : }
3559 :
3560 : /*
3561 : * pg_get_function_identity_arguments
3562 : * Get a formatted list of arguments for a function.
3563 : * This is everything that would go between the parentheses in
3564 : * ALTER FUNCTION, etc. In particular, don't print defaults.
3565 : */
3566 : Datum
3567 2120 : pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
3568 : {
3569 2120 : Oid funcid = PG_GETARG_OID(0);
3570 : StringInfoData buf;
3571 : HeapTuple proctup;
3572 :
3573 2120 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3574 2120 : if (!HeapTupleIsValid(proctup))
3575 4 : PG_RETURN_NULL();
3576 :
3577 2116 : initStringInfo(&buf);
3578 :
3579 2116 : (void) print_function_arguments(&buf, proctup, false, false);
3580 :
3581 2116 : ReleaseSysCache(proctup);
3582 :
3583 2116 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3584 : }
3585 :
3586 : /*
3587 : * pg_get_function_result
3588 : * Get a nicely-formatted version of the result type of a function.
3589 : * This is what would appear after RETURNS in CREATE FUNCTION.
3590 : */
3591 : Datum
3592 2188 : pg_get_function_result(PG_FUNCTION_ARGS)
3593 : {
3594 2188 : Oid funcid = PG_GETARG_OID(0);
3595 : StringInfoData buf;
3596 : HeapTuple proctup;
3597 :
3598 2188 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3599 2188 : if (!HeapTupleIsValid(proctup))
3600 4 : PG_RETURN_NULL();
3601 :
3602 2184 : if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
3603 : {
3604 132 : ReleaseSysCache(proctup);
3605 132 : PG_RETURN_NULL();
3606 : }
3607 :
3608 2052 : initStringInfo(&buf);
3609 :
3610 2052 : print_function_rettype(&buf, proctup);
3611 :
3612 2052 : ReleaseSysCache(proctup);
3613 :
3614 2052 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3615 : }
3616 :
3617 : /*
3618 : * Guts of pg_get_function_result: append the function's return type
3619 : * to the specified buffer.
3620 : */
3621 : static void
3622 2162 : print_function_rettype(StringInfo buf, HeapTuple proctup)
3623 : {
3624 2162 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3625 2162 : int ntabargs = 0;
3626 : StringInfoData rbuf;
3627 :
3628 2162 : initStringInfo(&rbuf);
3629 :
3630 2162 : if (proc->proretset)
3631 : {
3632 : /* It might be a table function; try to print the arguments */
3633 212 : appendStringInfoString(&rbuf, "TABLE(");
3634 212 : ntabargs = print_function_arguments(&rbuf, proctup, true, false);
3635 212 : if (ntabargs > 0)
3636 44 : appendStringInfoChar(&rbuf, ')');
3637 : else
3638 168 : resetStringInfo(&rbuf);
3639 : }
3640 :
3641 2162 : if (ntabargs == 0)
3642 : {
3643 : /* Not a table function, so do the normal thing */
3644 2118 : if (proc->proretset)
3645 168 : appendStringInfoString(&rbuf, "SETOF ");
3646 2118 : appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3647 : }
3648 :
3649 2162 : appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
3650 2162 : }
3651 :
3652 : /*
3653 : * Common code for pg_get_function_arguments and pg_get_function_result:
3654 : * append the desired subset of arguments to buf. We print only TABLE
3655 : * arguments when print_table_args is true, and all the others when it's false.
3656 : * We print argument defaults only if print_defaults is true.
3657 : * Function return value is the number of arguments printed.
3658 : */
3659 : static int
3660 4933 : print_function_arguments(StringInfo buf, HeapTuple proctup,
3661 : bool print_table_args, bool print_defaults)
3662 : {
3663 4933 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3664 : int numargs;
3665 : Oid *argtypes;
3666 : char **argnames;
3667 : char *argmodes;
3668 4933 : int insertorderbyat = -1;
3669 : int argsprinted;
3670 : int inputargno;
3671 : int nlackdefaults;
3672 4933 : List *argdefaults = NIL;
3673 4933 : ListCell *nextargdefault = NULL;
3674 : int i;
3675 :
3676 4933 : numargs = get_func_arg_info(proctup,
3677 : &argtypes, &argnames, &argmodes);
3678 :
3679 4933 : nlackdefaults = numargs;
3680 4933 : if (print_defaults && proc->pronargdefaults > 0)
3681 : {
3682 : Datum proargdefaults;
3683 : bool isnull;
3684 :
3685 21 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3686 : Anum_pg_proc_proargdefaults,
3687 : &isnull);
3688 21 : if (!isnull)
3689 : {
3690 : char *str;
3691 :
3692 21 : str = TextDatumGetCString(proargdefaults);
3693 21 : argdefaults = castNode(List, stringToNode(str));
3694 21 : pfree(str);
3695 21 : nextargdefault = list_head(argdefaults);
3696 : /* nlackdefaults counts only *input* arguments lacking defaults */
3697 21 : nlackdefaults = proc->pronargs - list_length(argdefaults);
3698 : }
3699 : }
3700 :
3701 : /* Check for special treatment of ordered-set aggregates */
3702 4933 : if (proc->prokind == PROKIND_AGGREGATE)
3703 : {
3704 : HeapTuple aggtup;
3705 : Form_pg_aggregate agg;
3706 :
3707 594 : aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(proc->oid));
3708 594 : if (!HeapTupleIsValid(aggtup))
3709 0 : elog(ERROR, "cache lookup failed for aggregate %u",
3710 : proc->oid);
3711 594 : agg = (Form_pg_aggregate) GETSTRUCT(aggtup);
3712 594 : if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3713 28 : insertorderbyat = agg->aggnumdirectargs;
3714 594 : ReleaseSysCache(aggtup);
3715 : }
3716 :
3717 4933 : argsprinted = 0;
3718 4933 : inputargno = 0;
3719 10052 : for (i = 0; i < numargs; i++)
3720 : {
3721 5119 : Oid argtype = argtypes[i];
3722 5119 : char *argname = argnames ? argnames[i] : NULL;
3723 5119 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
3724 : const char *modename;
3725 : bool isinput;
3726 :
3727 5119 : switch (argmode)
3728 : {
3729 4184 : case PROARGMODE_IN:
3730 :
3731 : /*
3732 : * For procedures, explicitly mark all argument modes, so as
3733 : * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3734 : */
3735 4184 : if (proc->prokind == PROKIND_PROCEDURE)
3736 291 : modename = "IN ";
3737 : else
3738 3893 : modename = "";
3739 4184 : isinput = true;
3740 4184 : break;
3741 50 : case PROARGMODE_INOUT:
3742 50 : modename = "INOUT ";
3743 50 : isinput = true;
3744 50 : break;
3745 517 : case PROARGMODE_OUT:
3746 517 : modename = "OUT ";
3747 517 : isinput = false;
3748 517 : break;
3749 92 : case PROARGMODE_VARIADIC:
3750 92 : modename = "VARIADIC ";
3751 92 : isinput = true;
3752 92 : break;
3753 276 : case PROARGMODE_TABLE:
3754 276 : modename = "";
3755 276 : isinput = false;
3756 276 : break;
3757 0 : default:
3758 0 : elog(ERROR, "invalid parameter mode '%c'", argmode);
3759 : modename = NULL; /* keep compiler quiet */
3760 : isinput = false;
3761 : break;
3762 : }
3763 5119 : if (isinput)
3764 4326 : inputargno++; /* this is a 1-based counter */
3765 :
3766 5119 : if (print_table_args != (argmode == PROARGMODE_TABLE))
3767 441 : continue;
3768 :
3769 4678 : if (argsprinted == insertorderbyat)
3770 : {
3771 28 : if (argsprinted)
3772 28 : appendStringInfoChar(buf, ' ');
3773 28 : appendStringInfoString(buf, "ORDER BY ");
3774 : }
3775 4650 : else if (argsprinted)
3776 1530 : appendStringInfoString(buf, ", ");
3777 :
3778 4678 : appendStringInfoString(buf, modename);
3779 4678 : if (argname && argname[0])
3780 1729 : appendStringInfo(buf, "%s ", quote_identifier(argname));
3781 4678 : appendStringInfoString(buf, format_type_be(argtype));
3782 4678 : if (print_defaults && isinput && inputargno > nlackdefaults)
3783 : {
3784 : Node *expr;
3785 :
3786 : Assert(nextargdefault != NULL);
3787 32 : expr = (Node *) lfirst(nextargdefault);
3788 32 : nextargdefault = lnext(argdefaults, nextargdefault);
3789 :
3790 32 : appendStringInfo(buf, " DEFAULT %s",
3791 : deparse_expression(expr, NIL, false, false));
3792 : }
3793 4678 : argsprinted++;
3794 :
3795 : /* nasty hack: print the last arg twice for variadic ordered-set agg */
3796 4678 : if (argsprinted == insertorderbyat && i == numargs - 1)
3797 : {
3798 14 : i--;
3799 : /* aggs shouldn't have defaults anyway, but just to be sure ... */
3800 14 : print_defaults = false;
3801 : }
3802 : }
3803 :
3804 4933 : return argsprinted;
3805 : }
3806 :
3807 : static bool
3808 64 : is_input_argument(int nth, const char *argmodes)
3809 : {
3810 : return (!argmodes
3811 28 : || argmodes[nth] == PROARGMODE_IN
3812 12 : || argmodes[nth] == PROARGMODE_INOUT
3813 92 : || argmodes[nth] == PROARGMODE_VARIADIC);
3814 : }
3815 :
3816 : /*
3817 : * Append used transformed types to specified buffer
3818 : */
3819 : static void
3820 123 : print_function_trftypes(StringInfo buf, HeapTuple proctup)
3821 : {
3822 : Oid *trftypes;
3823 : int ntypes;
3824 :
3825 123 : ntypes = get_func_trftypes(proctup, &trftypes);
3826 123 : if (ntypes > 0)
3827 : {
3828 : int i;
3829 :
3830 3 : appendStringInfoString(buf, " TRANSFORM ");
3831 8 : for (i = 0; i < ntypes; i++)
3832 : {
3833 5 : if (i != 0)
3834 2 : appendStringInfoString(buf, ", ");
3835 5 : appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3836 : }
3837 3 : appendStringInfoChar(buf, '\n');
3838 : }
3839 123 : }
3840 :
3841 : /*
3842 : * Get textual representation of a function argument's default value. The
3843 : * second argument of this function is the argument number among all arguments
3844 : * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
3845 : * how information_schema.sql uses it.
3846 : */
3847 : Datum
3848 36 : pg_get_function_arg_default(PG_FUNCTION_ARGS)
3849 : {
3850 36 : Oid funcid = PG_GETARG_OID(0);
3851 36 : int32 nth_arg = PG_GETARG_INT32(1);
3852 : HeapTuple proctup;
3853 : Form_pg_proc proc;
3854 : int numargs;
3855 : Oid *argtypes;
3856 : char **argnames;
3857 : char *argmodes;
3858 : int i;
3859 : List *argdefaults;
3860 : Node *node;
3861 : char *str;
3862 : int nth_inputarg;
3863 : Datum proargdefaults;
3864 : bool isnull;
3865 : int nth_default;
3866 :
3867 36 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3868 36 : if (!HeapTupleIsValid(proctup))
3869 8 : PG_RETURN_NULL();
3870 :
3871 28 : numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3872 28 : if (nth_arg < 1 || nth_arg > numargs || !is_input_argument(nth_arg - 1, argmodes))
3873 : {
3874 8 : ReleaseSysCache(proctup);
3875 8 : PG_RETURN_NULL();
3876 : }
3877 :
3878 20 : nth_inputarg = 0;
3879 56 : for (i = 0; i < nth_arg; i++)
3880 36 : if (is_input_argument(i, argmodes))
3881 32 : nth_inputarg++;
3882 :
3883 20 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3884 : Anum_pg_proc_proargdefaults,
3885 : &isnull);
3886 20 : if (isnull)
3887 : {
3888 0 : ReleaseSysCache(proctup);
3889 0 : PG_RETURN_NULL();
3890 : }
3891 :
3892 20 : str = TextDatumGetCString(proargdefaults);
3893 20 : argdefaults = castNode(List, stringToNode(str));
3894 20 : pfree(str);
3895 :
3896 20 : proc = (Form_pg_proc) GETSTRUCT(proctup);
3897 :
3898 : /*
3899 : * Calculate index into proargdefaults: proargdefaults corresponds to the
3900 : * last N input arguments, where N = pronargdefaults.
3901 : */
3902 20 : nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3903 :
3904 20 : if (nth_default < 0 || nth_default >= list_length(argdefaults))
3905 : {
3906 4 : ReleaseSysCache(proctup);
3907 4 : PG_RETURN_NULL();
3908 : }
3909 16 : node = list_nth(argdefaults, nth_default);
3910 16 : str = deparse_expression(node, NIL, false, false);
3911 :
3912 16 : ReleaseSysCache(proctup);
3913 :
3914 16 : PG_RETURN_TEXT_P(string_to_text(str));
3915 : }
3916 :
3917 : static void
3918 139 : print_function_sqlbody(StringInfo buf, HeapTuple proctup)
3919 : {
3920 : int numargs;
3921 : Oid *argtypes;
3922 : char **argnames;
3923 : char *argmodes;
3924 139 : deparse_namespace dpns = {0};
3925 : Datum tmp;
3926 : Node *n;
3927 :
3928 139 : dpns.funcname = pstrdup(NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname));
3929 139 : numargs = get_func_arg_info(proctup,
3930 : &argtypes, &argnames, &argmodes);
3931 139 : dpns.numargs = numargs;
3932 139 : dpns.argnames = argnames;
3933 :
3934 139 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosqlbody);
3935 139 : n = stringToNode(TextDatumGetCString(tmp));
3936 :
3937 139 : if (IsA(n, List))
3938 : {
3939 : List *stmts;
3940 : ListCell *lc;
3941 :
3942 112 : stmts = linitial(castNode(List, n));
3943 :
3944 112 : appendStringInfoString(buf, "BEGIN ATOMIC\n");
3945 :
3946 219 : foreach(lc, stmts)
3947 : {
3948 107 : Query *query = lfirst_node(Query, lc);
3949 :
3950 : /* It seems advisable to get at least AccessShareLock on rels */
3951 107 : AcquireRewriteLocks(query, false, false);
3952 107 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3953 : PRETTYFLAG_INDENT, WRAP_COLUMN_DEFAULT, 1);
3954 107 : appendStringInfoChar(buf, ';');
3955 107 : appendStringInfoChar(buf, '\n');
3956 : }
3957 :
3958 112 : appendStringInfoString(buf, "END");
3959 : }
3960 : else
3961 : {
3962 27 : Query *query = castNode(Query, n);
3963 :
3964 : /* It seems advisable to get at least AccessShareLock on rels */
3965 27 : AcquireRewriteLocks(query, false, false);
3966 27 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3967 : 0, WRAP_COLUMN_DEFAULT, 0);
3968 : }
3969 139 : }
3970 :
3971 : Datum
3972 1831 : pg_get_function_sqlbody(PG_FUNCTION_ARGS)
3973 : {
3974 1831 : Oid funcid = PG_GETARG_OID(0);
3975 : StringInfoData buf;
3976 : HeapTuple proctup;
3977 : bool isnull;
3978 :
3979 1831 : initStringInfo(&buf);
3980 :
3981 : /* Look up the function */
3982 1831 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3983 1831 : if (!HeapTupleIsValid(proctup))
3984 0 : PG_RETURN_NULL();
3985 :
3986 1831 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3987 1831 : if (isnull)
3988 : {
3989 1783 : ReleaseSysCache(proctup);
3990 1783 : PG_RETURN_NULL();
3991 : }
3992 :
3993 48 : print_function_sqlbody(&buf, proctup);
3994 :
3995 48 : ReleaseSysCache(proctup);
3996 :
3997 48 : PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
3998 : }
3999 :
4000 :
4001 : /*
4002 : * deparse_expression - General utility for deparsing expressions
4003 : *
4004 : * calls deparse_expression_pretty with all prettyPrinting disabled
4005 : */
4006 : char *
4007 53678 : deparse_expression(Node *expr, List *dpcontext,
4008 : bool forceprefix, bool showimplicit)
4009 : {
4010 53678 : return deparse_expression_pretty(expr, dpcontext, forceprefix,
4011 : showimplicit, 0, 0);
4012 : }
4013 :
4014 : /* ----------
4015 : * deparse_expression_pretty - General utility for deparsing expressions
4016 : *
4017 : * expr is the node tree to be deparsed. It must be a transformed expression
4018 : * tree (ie, not the raw output of gram.y).
4019 : *
4020 : * dpcontext is a list of deparse_namespace nodes representing the context
4021 : * for interpreting Vars in the node tree. It can be NIL if no Vars are
4022 : * expected.
4023 : *
4024 : * forceprefix is true to force all Vars to be prefixed with their table names.
4025 : *
4026 : * showimplicit is true to force all implicit casts to be shown explicitly.
4027 : *
4028 : * Tries to pretty up the output according to prettyFlags and startIndent.
4029 : *
4030 : * The result is a palloc'd string.
4031 : * ----------
4032 : */
4033 : static char *
4034 62549 : deparse_expression_pretty(Node *expr, List *dpcontext,
4035 : bool forceprefix, bool showimplicit,
4036 : int prettyFlags, int startIndent)
4037 : {
4038 : StringInfoData buf;
4039 : deparse_context context;
4040 :
4041 62549 : initStringInfo(&buf);
4042 62549 : context.buf = &buf;
4043 62549 : context.namespaces = dpcontext;
4044 62549 : context.resultDesc = NULL;
4045 62549 : context.targetList = NIL;
4046 62549 : context.windowClause = NIL;
4047 62549 : context.varprefix = forceprefix;
4048 62549 : context.prettyFlags = prettyFlags;
4049 62549 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
4050 62549 : context.indentLevel = startIndent;
4051 62549 : context.colNamesVisible = true;
4052 62549 : context.inGroupBy = false;
4053 62549 : context.varInOrderBy = false;
4054 62549 : context.appendparents = NULL;
4055 :
4056 62549 : get_rule_expr(expr, &context, showimplicit);
4057 :
4058 62549 : return buf.data;
4059 : }
4060 :
4061 : /* ----------
4062 : * deparse_context_for - Build deparse context for a single relation
4063 : *
4064 : * Given the reference name (alias) and OID of a relation, build deparsing
4065 : * context for an expression referencing only that relation (as varno 1,
4066 : * varlevelsup 0). This is sufficient for many uses of deparse_expression.
4067 : * ----------
4068 : */
4069 : List *
4070 15462 : deparse_context_for(const char *aliasname, Oid relid)
4071 : {
4072 : deparse_namespace *dpns;
4073 : RangeTblEntry *rte;
4074 :
4075 15462 : dpns = palloc0_object(deparse_namespace);
4076 :
4077 : /* Build a minimal RTE for the rel */
4078 15462 : rte = makeNode(RangeTblEntry);
4079 15462 : rte->rtekind = RTE_RELATION;
4080 15462 : rte->relid = relid;
4081 15462 : rte->relkind = RELKIND_RELATION; /* no need for exactness here */
4082 15462 : rte->rellockmode = AccessShareLock;
4083 15462 : rte->alias = makeAlias(aliasname, NIL);
4084 15462 : rte->eref = rte->alias;
4085 15462 : rte->lateral = false;
4086 15462 : rte->inh = false;
4087 15462 : rte->inFromCl = true;
4088 :
4089 : /* Build one-element rtable */
4090 15462 : dpns->rtable = list_make1(rte);
4091 15462 : dpns->subplans = NIL;
4092 15462 : dpns->ctes = NIL;
4093 15462 : dpns->appendrels = NULL;
4094 15462 : set_rtable_names(dpns, NIL, NULL);
4095 15462 : set_simple_column_names(dpns);
4096 :
4097 : /* Return a one-deep namespace stack */
4098 15462 : return list_make1(dpns);
4099 : }
4100 :
4101 : /*
4102 : * deparse_context_for_plan_tree - Build deparse context for a Plan tree
4103 : *
4104 : * When deparsing an expression in a Plan tree, we use the plan's rangetable
4105 : * to resolve names of simple Vars. The initialization of column names for
4106 : * this is rather expensive if the rangetable is large, and it'll be the same
4107 : * for every expression in the Plan tree; so we do it just once and re-use
4108 : * the result of this function for each expression. (Note that the result
4109 : * is not usable until set_deparse_context_plan() is applied to it.)
4110 : *
4111 : * In addition to the PlannedStmt, pass the per-RTE alias names
4112 : * assigned by a previous call to select_rtable_names_for_explain.
4113 : */
4114 : List *
4115 16247 : deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
4116 : {
4117 : deparse_namespace *dpns;
4118 :
4119 16247 : dpns = palloc0_object(deparse_namespace);
4120 :
4121 : /* Initialize fields that stay the same across the whole plan tree */
4122 16247 : dpns->rtable = pstmt->rtable;
4123 16247 : dpns->rtable_names = rtable_names;
4124 16247 : dpns->subplans = pstmt->subplans;
4125 16247 : dpns->ctes = NIL;
4126 16247 : if (pstmt->appendRelations)
4127 : {
4128 : /* Set up the array, indexed by child relid */
4129 2614 : int ntables = list_length(dpns->rtable);
4130 : ListCell *lc;
4131 :
4132 2614 : dpns->appendrels = (AppendRelInfo **)
4133 2614 : palloc0((ntables + 1) * sizeof(AppendRelInfo *));
4134 14517 : foreach(lc, pstmt->appendRelations)
4135 : {
4136 11903 : AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
4137 11903 : Index crelid = appinfo->child_relid;
4138 :
4139 : Assert(crelid > 0 && crelid <= ntables);
4140 : Assert(dpns->appendrels[crelid] == NULL);
4141 11903 : dpns->appendrels[crelid] = appinfo;
4142 : }
4143 : }
4144 : else
4145 13633 : dpns->appendrels = NULL; /* don't need it */
4146 :
4147 : /*
4148 : * Set up column name aliases, ignoring any join RTEs; they don't matter
4149 : * because plan trees don't contain any join alias Vars.
4150 : */
4151 16247 : set_simple_column_names(dpns);
4152 :
4153 : /* Return a one-deep namespace stack */
4154 16247 : return list_make1(dpns);
4155 : }
4156 :
4157 : /*
4158 : * set_deparse_context_plan - Specify Plan node containing expression
4159 : *
4160 : * When deparsing an expression in a Plan tree, we might have to resolve
4161 : * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
4162 : * provide the parent Plan node. Then OUTER_VAR and INNER_VAR references
4163 : * can be resolved by drilling down into the left and right child plans.
4164 : * Similarly, INDEX_VAR references can be resolved by reference to the
4165 : * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
4166 : * ForeignScan and CustomScan nodes. (Note that we don't currently support
4167 : * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
4168 : * for those, we can only deparse the indexqualorig fields, which won't
4169 : * contain INDEX_VAR Vars.)
4170 : *
4171 : * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
4172 : * the most-closely-nested first. This is needed to resolve PARAM_EXEC
4173 : * Params. Note we assume that all the Plan nodes share the same rtable.
4174 : *
4175 : * For a ModifyTable plan, we might also need to resolve references to OLD/NEW
4176 : * variables in the RETURNING list, so we copy the alias names of the OLD and
4177 : * NEW rows from the ModifyTable plan node.
4178 : *
4179 : * Once this function has been called, deparse_expression() can be called on
4180 : * subsidiary expression(s) of the specified Plan node. To deparse
4181 : * expressions of a different Plan node in the same Plan tree, re-call this
4182 : * function to identify the new parent Plan node.
4183 : *
4184 : * The result is the same List passed in; this is a notational convenience.
4185 : */
4186 : List *
4187 38691 : set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
4188 : {
4189 : deparse_namespace *dpns;
4190 :
4191 : /* Should always have one-entry namespace list for Plan deparsing */
4192 : Assert(list_length(dpcontext) == 1);
4193 38691 : dpns = (deparse_namespace *) linitial(dpcontext);
4194 :
4195 : /* Set our attention on the specific plan node passed in */
4196 38691 : dpns->ancestors = ancestors;
4197 38691 : set_deparse_plan(dpns, plan);
4198 :
4199 : /* For ModifyTable, set aliases for OLD and NEW in RETURNING */
4200 38691 : if (IsA(plan, ModifyTable))
4201 : {
4202 139 : dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias;
4203 139 : dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias;
4204 : }
4205 :
4206 38691 : return dpcontext;
4207 : }
4208 :
4209 : /*
4210 : * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
4211 : *
4212 : * Determine the relation aliases we'll use during an EXPLAIN operation.
4213 : * This is just a frontend to set_rtable_names. We have to expose the aliases
4214 : * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
4215 : */
4216 : List *
4217 16247 : select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
4218 : {
4219 : deparse_namespace dpns;
4220 :
4221 16247 : memset(&dpns, 0, sizeof(dpns));
4222 16247 : dpns.rtable = rtable;
4223 16247 : dpns.subplans = NIL;
4224 16247 : dpns.ctes = NIL;
4225 16247 : dpns.appendrels = NULL;
4226 16247 : set_rtable_names(&dpns, NIL, rels_used);
4227 : /* We needn't bother computing column aliases yet */
4228 :
4229 16247 : return dpns.rtable_names;
4230 : }
4231 :
4232 : /*
4233 : * set_rtable_names: select RTE aliases to be used in printing a query
4234 : *
4235 : * We fill in dpns->rtable_names with a list of names that is one-for-one with
4236 : * the already-filled dpns->rtable list. Each RTE name is unique among those
4237 : * in the new namespace plus any ancestor namespaces listed in
4238 : * parent_namespaces.
4239 : *
4240 : * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
4241 : *
4242 : * Note that this function is only concerned with relation names, not column
4243 : * names.
4244 : */
4245 : static void
4246 35339 : set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
4247 : Bitmapset *rels_used)
4248 : {
4249 : HASHCTL hash_ctl;
4250 : HTAB *names_hash;
4251 : NameHashEntry *hentry;
4252 : bool found;
4253 : int rtindex;
4254 : ListCell *lc;
4255 :
4256 35339 : dpns->rtable_names = NIL;
4257 : /* nothing more to do if empty rtable */
4258 35339 : if (dpns->rtable == NIL)
4259 328 : return;
4260 :
4261 : /*
4262 : * We use a hash table to hold known names, so that this process is O(N)
4263 : * not O(N^2) for N names.
4264 : */
4265 35011 : hash_ctl.keysize = NAMEDATALEN;
4266 35011 : hash_ctl.entrysize = sizeof(NameHashEntry);
4267 35011 : hash_ctl.hcxt = CurrentMemoryContext;
4268 35011 : names_hash = hash_create("set_rtable_names names",
4269 35011 : list_length(dpns->rtable),
4270 : &hash_ctl,
4271 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
4272 :
4273 : /* Preload the hash table with names appearing in parent_namespaces */
4274 36116 : foreach(lc, parent_namespaces)
4275 : {
4276 1105 : deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
4277 : ListCell *lc2;
4278 :
4279 3861 : foreach(lc2, olddpns->rtable_names)
4280 : {
4281 2756 : char *oldname = (char *) lfirst(lc2);
4282 :
4283 2756 : if (oldname == NULL)
4284 201 : continue;
4285 2555 : hentry = (NameHashEntry *) hash_search(names_hash,
4286 : oldname,
4287 : HASH_ENTER,
4288 : &found);
4289 : /* we do not complain about duplicate names in parent namespaces */
4290 2555 : hentry->counter = 0;
4291 : }
4292 : }
4293 :
4294 : /* Now we can scan the rtable */
4295 35011 : rtindex = 1;
4296 102043 : foreach(lc, dpns->rtable)
4297 : {
4298 67032 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4299 : char *refname;
4300 :
4301 : /* Just in case this takes an unreasonable amount of time ... */
4302 67032 : CHECK_FOR_INTERRUPTS();
4303 :
4304 67032 : if (rels_used && !bms_is_member(rtindex, rels_used))
4305 : {
4306 : /* Ignore unreferenced RTE */
4307 12016 : refname = NULL;
4308 : }
4309 55016 : else if (rte->alias)
4310 : {
4311 : /* If RTE has a user-defined alias, prefer that */
4312 35773 : refname = rte->alias->aliasname;
4313 : }
4314 19243 : else if (rte->rtekind == RTE_RELATION)
4315 : {
4316 : /* Use the current actual name of the relation */
4317 14724 : refname = get_rel_name(rte->relid);
4318 : }
4319 4519 : else if (rte->rtekind == RTE_JOIN)
4320 : {
4321 : /* Unnamed join has no refname */
4322 1129 : refname = NULL;
4323 : }
4324 : else
4325 : {
4326 : /* Otherwise use whatever the parser assigned */
4327 3390 : refname = rte->eref->aliasname;
4328 : }
4329 :
4330 : /*
4331 : * If the selected name isn't unique, append digits to make it so, and
4332 : * make a new hash entry for it once we've got a unique name. For a
4333 : * very long input name, we might have to truncate to stay within
4334 : * NAMEDATALEN.
4335 : */
4336 67032 : if (refname)
4337 : {
4338 53887 : hentry = (NameHashEntry *) hash_search(names_hash,
4339 : refname,
4340 : HASH_ENTER,
4341 : &found);
4342 53887 : if (found)
4343 : {
4344 : /* Name already in use, must choose a new one */
4345 10165 : int refnamelen = strlen(refname);
4346 10165 : char *modname = (char *) palloc(refnamelen + 16);
4347 : NameHashEntry *hentry2;
4348 :
4349 : do
4350 : {
4351 10169 : hentry->counter++;
4352 : for (;;)
4353 : {
4354 10177 : memcpy(modname, refname, refnamelen);
4355 10177 : sprintf(modname + refnamelen, "_%d", hentry->counter);
4356 10177 : if (strlen(modname) < NAMEDATALEN)
4357 10169 : break;
4358 : /* drop chars from refname to keep all the digits */
4359 8 : refnamelen = pg_mbcliplen(refname, refnamelen,
4360 : refnamelen - 1);
4361 : }
4362 10169 : hentry2 = (NameHashEntry *) hash_search(names_hash,
4363 : modname,
4364 : HASH_ENTER,
4365 : &found);
4366 10169 : } while (found);
4367 10165 : hentry2->counter = 0; /* init new hash entry */
4368 10165 : refname = modname;
4369 : }
4370 : else
4371 : {
4372 : /* Name not previously used, need only initialize hentry */
4373 43722 : hentry->counter = 0;
4374 : }
4375 : }
4376 :
4377 67032 : dpns->rtable_names = lappend(dpns->rtable_names, refname);
4378 67032 : rtindex++;
4379 : }
4380 :
4381 35011 : hash_destroy(names_hash);
4382 : }
4383 :
4384 : /*
4385 : * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
4386 : *
4387 : * For convenience, this is defined to initialize the deparse_namespace struct
4388 : * from scratch.
4389 : */
4390 : static void
4391 3545 : set_deparse_for_query(deparse_namespace *dpns, Query *query,
4392 : List *parent_namespaces)
4393 : {
4394 : ListCell *lc;
4395 : ListCell *lc2;
4396 :
4397 : /* Initialize *dpns and fill rtable/ctes links */
4398 3545 : memset(dpns, 0, sizeof(deparse_namespace));
4399 3545 : dpns->rtable = query->rtable;
4400 3545 : dpns->subplans = NIL;
4401 3545 : dpns->ctes = query->cteList;
4402 3545 : dpns->appendrels = NULL;
4403 3545 : dpns->ret_old_alias = query->returningOldAlias;
4404 3545 : dpns->ret_new_alias = query->returningNewAlias;
4405 :
4406 : /* Assign a unique relation alias to each RTE */
4407 3545 : set_rtable_names(dpns, parent_namespaces, NULL);
4408 :
4409 : /* Initialize dpns->rtable_columns to contain zeroed structs */
4410 3545 : dpns->rtable_columns = NIL;
4411 9914 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4412 6369 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4413 : palloc0(sizeof(deparse_columns)));
4414 :
4415 : /* If it's a utility query, it won't have a jointree */
4416 3545 : if (query->jointree)
4417 : {
4418 : /* Detect whether global uniqueness of USING names is needed */
4419 3536 : dpns->unique_using =
4420 3536 : has_dangerous_join_using(dpns, (Node *) query->jointree);
4421 :
4422 : /*
4423 : * Select names for columns merged by USING, via a recursive pass over
4424 : * the query jointree.
4425 : */
4426 3536 : set_using_names(dpns, (Node *) query->jointree, NIL);
4427 : }
4428 :
4429 : /*
4430 : * Now assign remaining column aliases for each RTE. We do this in a
4431 : * linear scan of the rtable, so as to process RTEs whether or not they
4432 : * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4433 : * etc). JOIN RTEs must be processed after their children, but this is
4434 : * okay because they appear later in the rtable list than their children
4435 : * (cf Asserts in identify_join_columns()).
4436 : */
4437 9914 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4438 : {
4439 6369 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4440 6369 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4441 :
4442 6369 : if (rte->rtekind == RTE_JOIN)
4443 940 : set_join_column_names(dpns, rte, colinfo);
4444 : else
4445 5429 : set_relation_column_names(dpns, rte, colinfo);
4446 : }
4447 3545 : }
4448 :
4449 : /*
4450 : * set_simple_column_names: fill in column aliases for non-query situations
4451 : *
4452 : * This handles EXPLAIN and cases where we only have relation RTEs. Without
4453 : * a join tree, we can't do anything smart about join RTEs, but we don't
4454 : * need to, because EXPLAIN should never see join alias Vars anyway.
4455 : * If we find a join RTE we'll just skip it, leaving its deparse_columns
4456 : * struct all-zero. If somehow we try to deparse a join alias Var, we'll
4457 : * error out cleanly because the struct's num_cols will be zero.
4458 : */
4459 : static void
4460 31794 : set_simple_column_names(deparse_namespace *dpns)
4461 : {
4462 : ListCell *lc;
4463 : ListCell *lc2;
4464 :
4465 : /* Initialize dpns->rtable_columns to contain zeroed structs */
4466 31794 : dpns->rtable_columns = NIL;
4467 92457 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4468 60663 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4469 : palloc0(sizeof(deparse_columns)));
4470 :
4471 : /* Assign unique column aliases within each non-join RTE */
4472 92457 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4473 : {
4474 60663 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4475 60663 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4476 :
4477 60663 : if (rte->rtekind != RTE_JOIN)
4478 56698 : set_relation_column_names(dpns, rte, colinfo);
4479 : }
4480 31794 : }
4481 :
4482 : /*
4483 : * has_dangerous_join_using: search jointree for unnamed JOIN USING
4484 : *
4485 : * Merged columns of a JOIN USING may act differently from either of the input
4486 : * columns, either because they are merged with COALESCE (in a FULL JOIN) or
4487 : * because an implicit coercion of the underlying input column is required.
4488 : * In such a case the column must be referenced as a column of the JOIN not as
4489 : * a column of either input. And this is problematic if the join is unnamed
4490 : * (alias-less): we cannot qualify the column's name with an RTE name, since
4491 : * there is none. (Forcibly assigning an alias to the join is not a solution,
4492 : * since that will prevent legal references to tables below the join.)
4493 : * To ensure that every column in the query is unambiguously referenceable,
4494 : * we must assign such merged columns names that are globally unique across
4495 : * the whole query, aliasing other columns out of the way as necessary.
4496 : *
4497 : * Because the ensuing re-aliasing is fairly damaging to the readability of
4498 : * the query, we don't do this unless we have to. So, we must pre-scan
4499 : * the join tree to see if we have to, before starting set_using_names().
4500 : */
4501 : static bool
4502 8436 : has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
4503 : {
4504 8436 : if (IsA(jtnode, RangeTblRef))
4505 : {
4506 : /* nothing to do here */
4507 : }
4508 4432 : else if (IsA(jtnode, FromExpr))
4509 : {
4510 3536 : FromExpr *f = (FromExpr *) jtnode;
4511 : ListCell *lc;
4512 :
4513 6696 : foreach(lc, f->fromlist)
4514 : {
4515 3212 : if (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))
4516 52 : return true;
4517 : }
4518 : }
4519 896 : else if (IsA(jtnode, JoinExpr))
4520 : {
4521 896 : JoinExpr *j = (JoinExpr *) jtnode;
4522 :
4523 : /* Is it an unnamed JOIN with USING? */
4524 896 : if (j->alias == NULL && j->usingClause)
4525 : {
4526 : /*
4527 : * Yes, so check each join alias var to see if any of them are not
4528 : * simple references to underlying columns. If so, we have a
4529 : * dangerous situation and must pick unique aliases.
4530 : */
4531 188 : RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4532 :
4533 : /* We need only examine the merged columns */
4534 388 : for (int i = 0; i < jrte->joinmergedcols; i++)
4535 : {
4536 252 : Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4537 :
4538 252 : if (!IsA(aliasvar, Var))
4539 52 : return true;
4540 : }
4541 : }
4542 :
4543 : /* Nope, but inspect children */
4544 844 : if (has_dangerous_join_using(dpns, j->larg))
4545 0 : return true;
4546 844 : if (has_dangerous_join_using(dpns, j->rarg))
4547 0 : return true;
4548 : }
4549 : else
4550 0 : elog(ERROR, "unrecognized node type: %d",
4551 : (int) nodeTag(jtnode));
4552 8332 : return false;
4553 : }
4554 :
4555 : /*
4556 : * set_using_names: select column aliases to be used for merged USING columns
4557 : *
4558 : * We do this during a recursive descent of the query jointree.
4559 : * dpns->unique_using must already be set to determine the global strategy.
4560 : *
4561 : * Column alias info is saved in the dpns->rtable_columns list, which is
4562 : * assumed to be filled with pre-zeroed deparse_columns structs.
4563 : *
4564 : * parentUsing is a list of all USING aliases assigned in parent joins of
4565 : * the current jointree node. (The passed-in list must not be modified.)
4566 : *
4567 : * Note that we do not use per-deparse_columns hash tables in this function.
4568 : * The number of names that need to be assigned should be small enough that
4569 : * we don't need to trouble with that.
4570 : */
4571 : static void
4572 8656 : set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
4573 : {
4574 8656 : if (IsA(jtnode, RangeTblRef))
4575 : {
4576 : /* nothing to do now */
4577 : }
4578 4476 : else if (IsA(jtnode, FromExpr))
4579 : {
4580 3536 : FromExpr *f = (FromExpr *) jtnode;
4581 : ListCell *lc;
4582 :
4583 6776 : foreach(lc, f->fromlist)
4584 3240 : set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4585 : }
4586 940 : else if (IsA(jtnode, JoinExpr))
4587 : {
4588 940 : JoinExpr *j = (JoinExpr *) jtnode;
4589 940 : RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4590 940 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
4591 : int *leftattnos;
4592 : int *rightattnos;
4593 : deparse_columns *leftcolinfo;
4594 : deparse_columns *rightcolinfo;
4595 : int i;
4596 : ListCell *lc;
4597 :
4598 : /* Get info about the shape of the join */
4599 940 : identify_join_columns(j, rte, colinfo);
4600 940 : leftattnos = colinfo->leftattnos;
4601 940 : rightattnos = colinfo->rightattnos;
4602 :
4603 : /* Look up the not-yet-filled-in child deparse_columns structs */
4604 940 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4605 940 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4606 :
4607 : /*
4608 : * If this join is unnamed, then we cannot substitute new aliases at
4609 : * this level, so any name requirements pushed down to here must be
4610 : * pushed down again to the children.
4611 : */
4612 940 : if (rte->alias == NULL)
4613 : {
4614 960 : for (i = 0; i < colinfo->num_cols; i++)
4615 : {
4616 92 : char *colname = colinfo->colnames[i];
4617 :
4618 92 : if (colname == NULL)
4619 16 : continue;
4620 :
4621 : /* Push down to left column, unless it's a system column */
4622 76 : if (leftattnos[i] > 0)
4623 : {
4624 68 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4625 68 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4626 : }
4627 :
4628 : /* Same on the righthand side */
4629 76 : if (rightattnos[i] > 0)
4630 : {
4631 76 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4632 76 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4633 : }
4634 : }
4635 : }
4636 :
4637 : /*
4638 : * If there's a USING clause, select the USING column names and push
4639 : * those names down to the children. We have two strategies:
4640 : *
4641 : * If dpns->unique_using is true, we force all USING names to be
4642 : * unique across the whole query level. In principle we'd only need
4643 : * the names of dangerous USING columns to be globally unique, but to
4644 : * safely assign all USING names in a single pass, we have to enforce
4645 : * the same uniqueness rule for all of them. However, if a USING
4646 : * column's name has been pushed down from the parent, we should use
4647 : * it as-is rather than making a uniqueness adjustment. This is
4648 : * necessary when we're at an unnamed join, and it creates no risk of
4649 : * ambiguity. Also, if there's a user-written output alias for a
4650 : * merged column, we prefer to use that rather than the input name;
4651 : * this simplifies the logic and seems likely to lead to less aliasing
4652 : * overall.
4653 : *
4654 : * If dpns->unique_using is false, we only need USING names to be
4655 : * unique within their own join RTE. We still need to honor
4656 : * pushed-down names, though.
4657 : *
4658 : * Though significantly different in results, these two strategies are
4659 : * implemented by the same code, with only the difference of whether
4660 : * to put assigned names into dpns->using_names.
4661 : */
4662 940 : if (j->usingClause)
4663 : {
4664 : /* Copy the input parentUsing list so we don't modify it */
4665 280 : parentUsing = list_copy(parentUsing);
4666 :
4667 : /* USING names must correspond to the first join output columns */
4668 280 : expand_colnames_array_to(colinfo, list_length(j->usingClause));
4669 280 : i = 0;
4670 664 : foreach(lc, j->usingClause)
4671 : {
4672 384 : char *colname = strVal(lfirst(lc));
4673 :
4674 : /* Assert it's a merged column */
4675 : Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4676 :
4677 : /* Adopt passed-down name if any, else select unique name */
4678 384 : if (colinfo->colnames[i] != NULL)
4679 68 : colname = colinfo->colnames[i];
4680 : else
4681 : {
4682 : /* Prefer user-written output alias if any */
4683 316 : if (rte->alias && i < list_length(rte->alias->colnames))
4684 0 : colname = strVal(list_nth(rte->alias->colnames, i));
4685 : /* Make it appropriately unique */
4686 316 : colname = make_colname_unique(colname, dpns, colinfo);
4687 316 : if (dpns->unique_using)
4688 88 : dpns->using_names = lappend(dpns->using_names,
4689 : colname);
4690 : /* Save it as output column name, too */
4691 316 : colinfo->colnames[i] = colname;
4692 : }
4693 :
4694 : /* Remember selected names for use later */
4695 384 : colinfo->usingNames = lappend(colinfo->usingNames, colname);
4696 384 : parentUsing = lappend(parentUsing, colname);
4697 :
4698 : /* Push down to left column, unless it's a system column */
4699 384 : if (leftattnos[i] > 0)
4700 : {
4701 384 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4702 384 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4703 : }
4704 :
4705 : /* Same on the righthand side */
4706 384 : if (rightattnos[i] > 0)
4707 : {
4708 384 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4709 384 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4710 : }
4711 :
4712 384 : i++;
4713 : }
4714 : }
4715 :
4716 : /* Mark child deparse_columns structs with correct parentUsing info */
4717 940 : leftcolinfo->parentUsing = parentUsing;
4718 940 : rightcolinfo->parentUsing = parentUsing;
4719 :
4720 : /* Now recursively assign USING column names in children */
4721 940 : set_using_names(dpns, j->larg, parentUsing);
4722 940 : set_using_names(dpns, j->rarg, parentUsing);
4723 : }
4724 : else
4725 0 : elog(ERROR, "unrecognized node type: %d",
4726 : (int) nodeTag(jtnode));
4727 8656 : }
4728 :
4729 : /*
4730 : * set_relation_column_names: select column aliases for a non-join RTE
4731 : *
4732 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4733 : * If any colnames entries are already filled in, those override local
4734 : * choices.
4735 : */
4736 : static void
4737 62127 : set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4738 : deparse_columns *colinfo)
4739 : {
4740 : int ncolumns;
4741 : char **real_colnames;
4742 : bool changed_any;
4743 : int noldcolumns;
4744 : int i;
4745 : int j;
4746 :
4747 : /*
4748 : * Construct an array of the current "real" column names of the RTE.
4749 : * real_colnames[] will be indexed by physical column number, with NULL
4750 : * entries for dropped columns.
4751 : */
4752 62127 : if (rte->rtekind == RTE_RELATION)
4753 : {
4754 : /* Relation --- look to the system catalogs for up-to-date info */
4755 : Relation rel;
4756 : TupleDesc tupdesc;
4757 :
4758 52587 : rel = relation_open(rte->relid, AccessShareLock);
4759 52587 : tupdesc = RelationGetDescr(rel);
4760 :
4761 52587 : ncolumns = tupdesc->natts;
4762 52587 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4763 :
4764 332225 : for (i = 0; i < ncolumns; i++)
4765 : {
4766 279638 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4767 :
4768 279638 : if (attr->attisdropped)
4769 1904 : real_colnames[i] = NULL;
4770 : else
4771 277734 : real_colnames[i] = pstrdup(NameStr(attr->attname));
4772 : }
4773 52587 : relation_close(rel, AccessShareLock);
4774 : }
4775 : else
4776 : {
4777 : /* Otherwise get the column names from eref or expandRTE() */
4778 : List *colnames;
4779 : ListCell *lc;
4780 :
4781 : /*
4782 : * Functions returning composites have the annoying property that some
4783 : * of the composite type's columns might have been dropped since the
4784 : * query was parsed. If possible, use expandRTE() to handle that
4785 : * case, since it has the tedious logic needed to find out about
4786 : * dropped columns. However, if we're explaining a plan, then we
4787 : * don't have rte->functions because the planner thinks that won't be
4788 : * needed later, and that breaks expandRTE(). So in that case we have
4789 : * to rely on rte->eref, which may lead us to report a dropped
4790 : * column's old name; that seems close enough for EXPLAIN's purposes.
4791 : *
4792 : * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4793 : * which should be sufficiently up-to-date: no other RTE types can
4794 : * have columns get dropped from under them after parsing.
4795 : */
4796 9540 : if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4797 : {
4798 : /* Since we're not creating Vars, rtindex etc. don't matter */
4799 553 : expandRTE(rte, 1, 0, VAR_RETURNING_DEFAULT, -1,
4800 : true /* include dropped */ , &colnames, NULL);
4801 : }
4802 : else
4803 8987 : colnames = rte->eref->colnames;
4804 :
4805 9540 : ncolumns = list_length(colnames);
4806 9540 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4807 :
4808 9540 : i = 0;
4809 31018 : foreach(lc, colnames)
4810 : {
4811 : /*
4812 : * If the column name we find here is an empty string, then it's a
4813 : * dropped column, so change to NULL.
4814 : */
4815 21478 : char *cname = strVal(lfirst(lc));
4816 :
4817 21478 : if (cname[0] == '\0')
4818 36 : cname = NULL;
4819 21478 : real_colnames[i] = cname;
4820 21478 : i++;
4821 : }
4822 : }
4823 :
4824 : /*
4825 : * Ensure colinfo->colnames has a slot for each column. (It could be long
4826 : * enough already, if we pushed down a name for the last column.) Note:
4827 : * it's possible that there are now more columns than there were when the
4828 : * query was parsed, ie colnames could be longer than rte->eref->colnames.
4829 : * We must assign unique aliases to the new columns too, else there could
4830 : * be unresolved conflicts when the view/rule is reloaded.
4831 : */
4832 62127 : expand_colnames_array_to(colinfo, ncolumns);
4833 : Assert(colinfo->num_cols == ncolumns);
4834 :
4835 : /*
4836 : * Make sufficiently large new_colnames and is_new_col arrays, too.
4837 : *
4838 : * Note: because we leave colinfo->num_new_cols zero until after the loop,
4839 : * colname_is_unique will not consult that array, which is fine because it
4840 : * would only be duplicate effort.
4841 : */
4842 62127 : colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4843 62127 : colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4844 :
4845 : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4846 62127 : build_colinfo_names_hash(colinfo);
4847 :
4848 : /*
4849 : * Scan the columns, select a unique alias for each one, and store it in
4850 : * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4851 : * entries for dropped columns, the latter omits them. Also mark
4852 : * new_colnames entries as to whether they are new since parse time; this
4853 : * is the case for entries beyond the length of rte->eref->colnames.
4854 : */
4855 62127 : noldcolumns = list_length(rte->eref->colnames);
4856 62127 : changed_any = false;
4857 62127 : j = 0;
4858 363243 : for (i = 0; i < ncolumns; i++)
4859 : {
4860 301116 : char *real_colname = real_colnames[i];
4861 301116 : char *colname = colinfo->colnames[i];
4862 :
4863 : /* Skip dropped columns */
4864 301116 : if (real_colname == NULL)
4865 : {
4866 : Assert(colname == NULL); /* colnames[i] is already NULL */
4867 1940 : continue;
4868 : }
4869 :
4870 : /* If alias already assigned, that's what to use */
4871 299176 : if (colname == NULL)
4872 : {
4873 : /* If user wrote an alias, prefer that over real column name */
4874 298476 : if (rte->alias && i < list_length(rte->alias->colnames))
4875 29894 : colname = strVal(list_nth(rte->alias->colnames, i));
4876 : else
4877 268582 : colname = real_colname;
4878 :
4879 : /* Unique-ify and insert into colinfo */
4880 298476 : colname = make_colname_unique(colname, dpns, colinfo);
4881 :
4882 298476 : colinfo->colnames[i] = colname;
4883 298476 : add_to_names_hash(colinfo, colname);
4884 : }
4885 :
4886 : /* Put names of non-dropped columns in new_colnames[] too */
4887 299176 : colinfo->new_colnames[j] = colname;
4888 : /* And mark them as new or not */
4889 299176 : colinfo->is_new_col[j] = (i >= noldcolumns);
4890 299176 : j++;
4891 :
4892 : /* Remember if any assigned aliases differ from "real" name */
4893 299176 : if (!changed_any && strcmp(colname, real_colname) != 0)
4894 793 : changed_any = true;
4895 : }
4896 :
4897 : /* We're now done needing the colinfo's names_hash */
4898 62127 : destroy_colinfo_names_hash(colinfo);
4899 :
4900 : /*
4901 : * Set correct length for new_colnames[] array. (Note: if columns have
4902 : * been added, colinfo->num_cols includes them, which is not really quite
4903 : * right but is harmless, since any new columns must be at the end where
4904 : * they won't affect varattnos of pre-existing columns.)
4905 : */
4906 62127 : colinfo->num_new_cols = j;
4907 :
4908 : /*
4909 : * For a relation RTE, we need only print the alias column names if any
4910 : * are different from the underlying "real" names. For a function RTE,
4911 : * always emit a complete column alias list; this is to protect against
4912 : * possible instability of the default column names (eg, from altering
4913 : * parameter names). For tablefunc RTEs, we never print aliases, because
4914 : * the column names are part of the clause itself. For other RTE types,
4915 : * print if we changed anything OR if there were user-written column
4916 : * aliases (since the latter would be part of the underlying "reality").
4917 : */
4918 62127 : if (rte->rtekind == RTE_RELATION)
4919 52587 : colinfo->printaliases = changed_any;
4920 9540 : else if (rte->rtekind == RTE_FUNCTION)
4921 960 : colinfo->printaliases = true;
4922 8580 : else if (rte->rtekind == RTE_TABLEFUNC)
4923 114 : colinfo->printaliases = false;
4924 8466 : else if (rte->alias && rte->alias->colnames != NIL)
4925 490 : colinfo->printaliases = true;
4926 : else
4927 7976 : colinfo->printaliases = changed_any;
4928 62127 : }
4929 :
4930 : /*
4931 : * set_join_column_names: select column aliases for a join RTE
4932 : *
4933 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4934 : * If any colnames entries are already filled in, those override local
4935 : * choices. Also, names for USING columns were already chosen by
4936 : * set_using_names(). We further expect that column alias selection has been
4937 : * completed for both input RTEs.
4938 : */
4939 : static void
4940 940 : set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4941 : deparse_columns *colinfo)
4942 : {
4943 : deparse_columns *leftcolinfo;
4944 : deparse_columns *rightcolinfo;
4945 : bool changed_any;
4946 : int noldcolumns;
4947 : int nnewcolumns;
4948 940 : Bitmapset *leftmerged = NULL;
4949 940 : Bitmapset *rightmerged = NULL;
4950 : int i;
4951 : int j;
4952 : int ic;
4953 : int jc;
4954 :
4955 : /* Look up the previously-filled-in child deparse_columns structs */
4956 940 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4957 940 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4958 :
4959 : /*
4960 : * Ensure colinfo->colnames has a slot for each column. (It could be long
4961 : * enough already, if we pushed down a name for the last column.) Note:
4962 : * it's possible that one or both inputs now have more columns than there
4963 : * were when the query was parsed, but we'll deal with that below. We
4964 : * only need entries in colnames for pre-existing columns.
4965 : */
4966 940 : noldcolumns = list_length(rte->eref->colnames);
4967 940 : expand_colnames_array_to(colinfo, noldcolumns);
4968 : Assert(colinfo->num_cols == noldcolumns);
4969 :
4970 : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4971 940 : build_colinfo_names_hash(colinfo);
4972 :
4973 : /*
4974 : * Scan the join output columns, select an alias for each one, and store
4975 : * it in colinfo->colnames. If there are USING columns, set_using_names()
4976 : * already selected their names, so we can start the loop at the first
4977 : * non-merged column.
4978 : */
4979 940 : changed_any = false;
4980 30493 : for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4981 : {
4982 29553 : char *colname = colinfo->colnames[i];
4983 : char *real_colname;
4984 :
4985 : /* Join column must refer to at least one input column */
4986 : Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4987 :
4988 : /* Get the child column name */
4989 29553 : if (colinfo->leftattnos[i] > 0)
4990 20721 : real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4991 8832 : else if (colinfo->rightattnos[i] > 0)
4992 8832 : real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4993 : else
4994 : {
4995 : /* We're joining system columns --- use eref name */
4996 0 : real_colname = strVal(list_nth(rte->eref->colnames, i));
4997 : }
4998 :
4999 : /* If child col has been dropped, no need to assign a join colname */
5000 29553 : if (real_colname == NULL)
5001 : {
5002 4 : colinfo->colnames[i] = NULL;
5003 4 : continue;
5004 : }
5005 :
5006 : /* In an unnamed join, just report child column names as-is */
5007 29549 : if (rte->alias == NULL)
5008 : {
5009 29297 : colinfo->colnames[i] = real_colname;
5010 29297 : add_to_names_hash(colinfo, real_colname);
5011 29297 : continue;
5012 : }
5013 :
5014 : /* If alias already assigned, that's what to use */
5015 252 : if (colname == NULL)
5016 : {
5017 : /* If user wrote an alias, prefer that over real column name */
5018 252 : if (rte->alias && i < list_length(rte->alias->colnames))
5019 64 : colname = strVal(list_nth(rte->alias->colnames, i));
5020 : else
5021 188 : colname = real_colname;
5022 :
5023 : /* Unique-ify and insert into colinfo */
5024 252 : colname = make_colname_unique(colname, dpns, colinfo);
5025 :
5026 252 : colinfo->colnames[i] = colname;
5027 252 : add_to_names_hash(colinfo, colname);
5028 : }
5029 :
5030 : /* Remember if any assigned aliases differ from "real" name */
5031 252 : if (!changed_any && strcmp(colname, real_colname) != 0)
5032 16 : changed_any = true;
5033 : }
5034 :
5035 : /*
5036 : * Calculate number of columns the join would have if it were re-parsed
5037 : * now, and create storage for the new_colnames and is_new_col arrays.
5038 : *
5039 : * Note: colname_is_unique will be consulting new_colnames[] during the
5040 : * loops below, so its not-yet-filled entries must be zeroes.
5041 : */
5042 1880 : nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
5043 940 : list_length(colinfo->usingNames);
5044 940 : colinfo->num_new_cols = nnewcolumns;
5045 940 : colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
5046 940 : colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
5047 :
5048 : /*
5049 : * Generating the new_colnames array is a bit tricky since any new columns
5050 : * added since parse time must be inserted in the right places. This code
5051 : * must match the parser, which will order a join's columns as merged
5052 : * columns first (in USING-clause order), then non-merged columns from the
5053 : * left input (in attnum order), then non-merged columns from the right
5054 : * input (ditto). If one of the inputs is itself a join, its columns will
5055 : * be ordered according to the same rule, which means newly-added columns
5056 : * might not be at the end. We can figure out what's what by consulting
5057 : * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
5058 : *
5059 : * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
5060 : * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
5061 : * meanings for the current child RTE.
5062 : */
5063 :
5064 : /* Handle merged columns; they are first and can't be new */
5065 940 : i = j = 0;
5066 940 : while (i < noldcolumns &&
5067 1324 : colinfo->leftattnos[i] != 0 &&
5068 1324 : colinfo->rightattnos[i] != 0)
5069 : {
5070 : /* column name is already determined and known unique */
5071 384 : colinfo->new_colnames[j] = colinfo->colnames[i];
5072 384 : colinfo->is_new_col[j] = false;
5073 :
5074 : /* build bitmapsets of child attnums of merged columns */
5075 384 : if (colinfo->leftattnos[i] > 0)
5076 384 : leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
5077 384 : if (colinfo->rightattnos[i] > 0)
5078 384 : rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);
5079 :
5080 384 : i++, j++;
5081 : }
5082 :
5083 : /* Handle non-merged left-child columns */
5084 940 : ic = 0;
5085 22369 : for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
5086 : {
5087 21429 : char *child_colname = leftcolinfo->new_colnames[jc];
5088 :
5089 21429 : if (!leftcolinfo->is_new_col[jc])
5090 : {
5091 : /* Advance ic to next non-dropped old column of left child */
5092 21157 : while (ic < leftcolinfo->num_cols &&
5093 21157 : leftcolinfo->colnames[ic] == NULL)
5094 56 : ic++;
5095 : Assert(ic < leftcolinfo->num_cols);
5096 21101 : ic++;
5097 : /* If it is a merged column, we already processed it */
5098 21101 : if (bms_is_member(ic, leftmerged))
5099 384 : continue;
5100 : /* Else, advance i to the corresponding existing join column */
5101 20721 : while (i < colinfo->num_cols &&
5102 20721 : colinfo->colnames[i] == NULL)
5103 4 : i++;
5104 : Assert(i < colinfo->num_cols);
5105 : Assert(ic == colinfo->leftattnos[i]);
5106 : /* Use the already-assigned name of this column */
5107 20717 : colinfo->new_colnames[j] = colinfo->colnames[i];
5108 20717 : i++;
5109 : }
5110 : else
5111 : {
5112 : /*
5113 : * Unique-ify the new child column name and assign, unless we're
5114 : * in an unnamed join, in which case just copy
5115 : */
5116 328 : if (rte->alias != NULL)
5117 : {
5118 176 : colinfo->new_colnames[j] =
5119 88 : make_colname_unique(child_colname, dpns, colinfo);
5120 88 : if (!changed_any &&
5121 72 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
5122 8 : changed_any = true;
5123 : }
5124 : else
5125 240 : colinfo->new_colnames[j] = child_colname;
5126 328 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
5127 : }
5128 :
5129 21045 : colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
5130 21045 : j++;
5131 : }
5132 :
5133 : /* Handle non-merged right-child columns in exactly the same way */
5134 940 : ic = 0;
5135 10268 : for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
5136 : {
5137 9328 : char *child_colname = rightcolinfo->new_colnames[jc];
5138 :
5139 9328 : if (!rightcolinfo->is_new_col[jc])
5140 : {
5141 : /* Advance ic to next non-dropped old column of right child */
5142 9216 : while (ic < rightcolinfo->num_cols &&
5143 9216 : rightcolinfo->colnames[ic] == NULL)
5144 0 : ic++;
5145 : Assert(ic < rightcolinfo->num_cols);
5146 9216 : ic++;
5147 : /* If it is a merged column, we already processed it */
5148 9216 : if (bms_is_member(ic, rightmerged))
5149 384 : continue;
5150 : /* Else, advance i to the corresponding existing join column */
5151 8832 : while (i < colinfo->num_cols &&
5152 8832 : colinfo->colnames[i] == NULL)
5153 0 : i++;
5154 : Assert(i < colinfo->num_cols);
5155 : Assert(ic == colinfo->rightattnos[i]);
5156 : /* Use the already-assigned name of this column */
5157 8832 : colinfo->new_colnames[j] = colinfo->colnames[i];
5158 8832 : i++;
5159 : }
5160 : else
5161 : {
5162 : /*
5163 : * Unique-ify the new child column name and assign, unless we're
5164 : * in an unnamed join, in which case just copy
5165 : */
5166 112 : if (rte->alias != NULL)
5167 : {
5168 32 : colinfo->new_colnames[j] =
5169 16 : make_colname_unique(child_colname, dpns, colinfo);
5170 16 : if (!changed_any &&
5171 16 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
5172 8 : changed_any = true;
5173 : }
5174 : else
5175 96 : colinfo->new_colnames[j] = child_colname;
5176 112 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
5177 : }
5178 :
5179 8944 : colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
5180 8944 : j++;
5181 : }
5182 :
5183 : /* Assert we processed the right number of columns */
5184 : #ifdef USE_ASSERT_CHECKING
5185 : while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
5186 : i++;
5187 : Assert(i == colinfo->num_cols);
5188 : Assert(j == nnewcolumns);
5189 : #endif
5190 :
5191 : /* We're now done needing the colinfo's names_hash */
5192 940 : destroy_colinfo_names_hash(colinfo);
5193 :
5194 : /*
5195 : * For a named join, print column aliases if we changed any from the child
5196 : * names. Unnamed joins cannot print aliases.
5197 : */
5198 940 : if (rte->alias != NULL)
5199 72 : colinfo->printaliases = changed_any;
5200 : else
5201 868 : colinfo->printaliases = false;
5202 940 : }
5203 :
5204 : /*
5205 : * colname_is_unique: is colname distinct from already-chosen column names?
5206 : *
5207 : * dpns is query-wide info, colinfo is for the column's RTE
5208 : */
5209 : static bool
5210 300715 : colname_is_unique(const char *colname, deparse_namespace *dpns,
5211 : deparse_columns *colinfo)
5212 : {
5213 : int i;
5214 : ListCell *lc;
5215 :
5216 : /*
5217 : * If we have a hash table, consult that instead of linearly scanning the
5218 : * colinfo's strings.
5219 : */
5220 300715 : if (colinfo->names_hash)
5221 : {
5222 10322 : if (hash_search(colinfo->names_hash,
5223 : colname,
5224 : HASH_FIND,
5225 : NULL) != NULL)
5226 0 : return false;
5227 : }
5228 : else
5229 : {
5230 : /* Check against already-assigned column aliases within RTE */
5231 4013086 : for (i = 0; i < colinfo->num_cols; i++)
5232 : {
5233 3724212 : char *oldname = colinfo->colnames[i];
5234 :
5235 3724212 : if (oldname && strcmp(oldname, colname) == 0)
5236 1519 : return false;
5237 : }
5238 :
5239 : /*
5240 : * If we're building a new_colnames array, check that too (this will
5241 : * be partially but not completely redundant with the previous checks)
5242 : */
5243 289722 : for (i = 0; i < colinfo->num_new_cols; i++)
5244 : {
5245 864 : char *oldname = colinfo->new_colnames[i];
5246 :
5247 864 : if (oldname && strcmp(oldname, colname) == 0)
5248 16 : return false;
5249 : }
5250 :
5251 : /*
5252 : * Also check against names already assigned for parent-join USING
5253 : * cols
5254 : */
5255 290554 : foreach(lc, colinfo->parentUsing)
5256 : {
5257 1700 : char *oldname = (char *) lfirst(lc);
5258 :
5259 1700 : if (strcmp(oldname, colname) == 0)
5260 4 : return false;
5261 : }
5262 : }
5263 :
5264 : /*
5265 : * Also check against USING-column names that must be globally unique.
5266 : * These are not hashed, but there should be few of them.
5267 : */
5268 299748 : foreach(lc, dpns->using_names)
5269 : {
5270 600 : char *oldname = (char *) lfirst(lc);
5271 :
5272 600 : if (strcmp(oldname, colname) == 0)
5273 28 : return false;
5274 : }
5275 :
5276 299148 : return true;
5277 : }
5278 :
5279 : /*
5280 : * make_colname_unique: modify colname if necessary to make it unique
5281 : *
5282 : * dpns is query-wide info, colinfo is for the column's RTE
5283 : */
5284 : static char *
5285 299148 : make_colname_unique(char *colname, deparse_namespace *dpns,
5286 : deparse_columns *colinfo)
5287 : {
5288 : /*
5289 : * If the selected name isn't unique, append digits to make it so. For a
5290 : * very long input name, we might have to truncate to stay within
5291 : * NAMEDATALEN.
5292 : */
5293 299148 : if (!colname_is_unique(colname, dpns, colinfo))
5294 : {
5295 1086 : int colnamelen = strlen(colname);
5296 1086 : char *modname = (char *) palloc(colnamelen + 16);
5297 1086 : int i = 0;
5298 :
5299 : do
5300 : {
5301 1567 : i++;
5302 : for (;;)
5303 : {
5304 1567 : memcpy(modname, colname, colnamelen);
5305 1567 : sprintf(modname + colnamelen, "_%d", i);
5306 1567 : if (strlen(modname) < NAMEDATALEN)
5307 1567 : break;
5308 : /* drop chars from colname to keep all the digits */
5309 0 : colnamelen = pg_mbcliplen(colname, colnamelen,
5310 : colnamelen - 1);
5311 : }
5312 1567 : } while (!colname_is_unique(modname, dpns, colinfo));
5313 1086 : colname = modname;
5314 : }
5315 299148 : return colname;
5316 : }
5317 :
5318 : /*
5319 : * expand_colnames_array_to: make colinfo->colnames at least n items long
5320 : *
5321 : * Any added array entries are initialized to zero.
5322 : */
5323 : static void
5324 64259 : expand_colnames_array_to(deparse_columns *colinfo, int n)
5325 : {
5326 64259 : if (n > colinfo->num_cols)
5327 : {
5328 62524 : if (colinfo->colnames == NULL)
5329 61588 : colinfo->colnames = palloc0_array(char *, n);
5330 : else
5331 936 : colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
5332 62524 : colinfo->num_cols = n;
5333 : }
5334 64259 : }
5335 :
5336 : /*
5337 : * build_colinfo_names_hash: optionally construct a hash table for colinfo
5338 : */
5339 : static void
5340 63067 : build_colinfo_names_hash(deparse_columns *colinfo)
5341 : {
5342 : HASHCTL hash_ctl;
5343 : int i;
5344 : ListCell *lc;
5345 :
5346 : /*
5347 : * Use a hash table only for RTEs with at least 32 columns. (The cutoff
5348 : * is somewhat arbitrary, but let's choose it so that this code does get
5349 : * exercised in the regression tests.)
5350 : */
5351 63067 : if (colinfo->num_cols < 32)
5352 62263 : return;
5353 :
5354 : /*
5355 : * Set up the hash table. The entries are just strings with no other
5356 : * payload.
5357 : */
5358 804 : hash_ctl.keysize = NAMEDATALEN;
5359 804 : hash_ctl.entrysize = NAMEDATALEN;
5360 804 : hash_ctl.hcxt = CurrentMemoryContext;
5361 1608 : colinfo->names_hash = hash_create("deparse_columns names",
5362 804 : colinfo->num_cols + colinfo->num_new_cols,
5363 : &hash_ctl,
5364 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
5365 :
5366 : /*
5367 : * Preload the hash table with any names already present (these would have
5368 : * come from set_using_names).
5369 : */
5370 37754 : for (i = 0; i < colinfo->num_cols; i++)
5371 : {
5372 36950 : char *oldname = colinfo->colnames[i];
5373 :
5374 36950 : if (oldname)
5375 0 : add_to_names_hash(colinfo, oldname);
5376 : }
5377 :
5378 804 : for (i = 0; i < colinfo->num_new_cols; i++)
5379 : {
5380 0 : char *oldname = colinfo->new_colnames[i];
5381 :
5382 0 : if (oldname)
5383 0 : add_to_names_hash(colinfo, oldname);
5384 : }
5385 :
5386 804 : foreach(lc, colinfo->parentUsing)
5387 : {
5388 0 : char *oldname = (char *) lfirst(lc);
5389 :
5390 0 : add_to_names_hash(colinfo, oldname);
5391 : }
5392 : }
5393 :
5394 : /*
5395 : * add_to_names_hash: add a string to the names_hash, if we're using one
5396 : */
5397 : static void
5398 328465 : add_to_names_hash(deparse_columns *colinfo, const char *name)
5399 : {
5400 328465 : if (colinfo->names_hash)
5401 36950 : (void) hash_search(colinfo->names_hash,
5402 : name,
5403 : HASH_ENTER,
5404 : NULL);
5405 328465 : }
5406 :
5407 : /*
5408 : * destroy_colinfo_names_hash: destroy hash table when done with it
5409 : */
5410 : static void
5411 63067 : destroy_colinfo_names_hash(deparse_columns *colinfo)
5412 : {
5413 63067 : if (colinfo->names_hash)
5414 : {
5415 804 : hash_destroy(colinfo->names_hash);
5416 804 : colinfo->names_hash = NULL;
5417 : }
5418 63067 : }
5419 :
5420 : /*
5421 : * identify_join_columns: figure out where columns of a join come from
5422 : *
5423 : * Fills the join-specific fields of the colinfo struct, except for
5424 : * usingNames which is filled later.
5425 : */
5426 : static void
5427 940 : identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
5428 : deparse_columns *colinfo)
5429 : {
5430 : int numjoincols;
5431 : int jcolno;
5432 : int rcolno;
5433 : ListCell *lc;
5434 :
5435 : /* Extract left/right child RT indexes */
5436 940 : if (IsA(j->larg, RangeTblRef))
5437 605 : colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
5438 335 : else if (IsA(j->larg, JoinExpr))
5439 335 : colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
5440 : else
5441 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5442 : (int) nodeTag(j->larg));
5443 940 : if (IsA(j->rarg, RangeTblRef))
5444 940 : colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
5445 0 : else if (IsA(j->rarg, JoinExpr))
5446 0 : colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
5447 : else
5448 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5449 : (int) nodeTag(j->rarg));
5450 :
5451 : /* Assert children will be processed earlier than join in second pass */
5452 : Assert(colinfo->leftrti < j->rtindex);
5453 : Assert(colinfo->rightrti < j->rtindex);
5454 :
5455 : /* Initialize result arrays with zeroes */
5456 940 : numjoincols = list_length(jrte->joinaliasvars);
5457 : Assert(numjoincols == list_length(jrte->eref->colnames));
5458 940 : colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
5459 940 : colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
5460 :
5461 : /*
5462 : * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
5463 : * Recall that the column(s) merged due to USING are the first column(s)
5464 : * of the join output. We need not do anything special while scanning
5465 : * joinleftcols, but while scanning joinrightcols we must distinguish
5466 : * merged from unmerged columns.
5467 : */
5468 940 : jcolno = 0;
5469 22045 : foreach(lc, jrte->joinleftcols)
5470 : {
5471 21105 : int leftattno = lfirst_int(lc);
5472 :
5473 21105 : colinfo->leftattnos[jcolno++] = leftattno;
5474 : }
5475 940 : rcolno = 0;
5476 10156 : foreach(lc, jrte->joinrightcols)
5477 : {
5478 9216 : int rightattno = lfirst_int(lc);
5479 :
5480 9216 : if (rcolno < jrte->joinmergedcols) /* merged column? */
5481 384 : colinfo->rightattnos[rcolno] = rightattno;
5482 : else
5483 8832 : colinfo->rightattnos[jcolno++] = rightattno;
5484 9216 : rcolno++;
5485 : }
5486 : Assert(jcolno == numjoincols);
5487 940 : }
5488 :
5489 : /*
5490 : * get_rtable_name: convenience function to get a previously assigned RTE alias
5491 : *
5492 : * The RTE must belong to the topmost namespace level in "context".
5493 : */
5494 : static char *
5495 4091 : get_rtable_name(int rtindex, deparse_context *context)
5496 : {
5497 4091 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
5498 :
5499 : Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
5500 4091 : return (char *) list_nth(dpns->rtable_names, rtindex - 1);
5501 : }
5502 :
5503 : /*
5504 : * set_deparse_plan: set up deparse_namespace to parse subexpressions
5505 : * of a given Plan node
5506 : *
5507 : * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
5508 : * and index_tlist fields. Caller must already have adjusted the ancestors
5509 : * list if necessary. Note that the rtable, subplans, and ctes fields do
5510 : * not need to change when shifting attention to different plan nodes in a
5511 : * single plan tree.
5512 : */
5513 : static void
5514 99448 : set_deparse_plan(deparse_namespace *dpns, Plan *plan)
5515 : {
5516 99448 : dpns->plan = plan;
5517 :
5518 : /*
5519 : * We special-case Append and MergeAppend to pretend that the first child
5520 : * plan is the OUTER referent; we have to interpret OUTER Vars in their
5521 : * tlists according to one of the children, and the first one is the most
5522 : * natural choice.
5523 : */
5524 99448 : if (IsA(plan, Append))
5525 2917 : dpns->outer_plan = linitial(((Append *) plan)->appendplans);
5526 96531 : else if (IsA(plan, MergeAppend))
5527 360 : dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
5528 : else
5529 96171 : dpns->outer_plan = outerPlan(plan);
5530 :
5531 99448 : if (dpns->outer_plan)
5532 48015 : dpns->outer_tlist = dpns->outer_plan->targetlist;
5533 : else
5534 51433 : dpns->outer_tlist = NIL;
5535 :
5536 : /*
5537 : * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
5538 : * use OUTER because that could someday conflict with the normal meaning.)
5539 : * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
5540 : * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
5541 : * that as INNER referent.
5542 : *
5543 : * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
5544 : * INNER referent. This is the join from the target relation to the data
5545 : * source, and all INNER_VAR Vars in other parts of the query refer to its
5546 : * targetlist.
5547 : *
5548 : * For ON CONFLICT DO SELECT/UPDATE we just need the inner tlist to point
5549 : * to the excluded expression's tlist. (Similar to the SubqueryScan we
5550 : * don't want to reuse OUTER, it's used for RETURNING in some modify table
5551 : * cases, although not INSERT .. CONFLICT).
5552 : */
5553 99448 : if (IsA(plan, SubqueryScan))
5554 453 : dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5555 98995 : else if (IsA(plan, CteScan))
5556 373 : dpns->inner_plan = list_nth(dpns->subplans,
5557 373 : ((CteScan *) plan)->ctePlanId - 1);
5558 98622 : else if (IsA(plan, WorkTableScan))
5559 116 : dpns->inner_plan = find_recursive_union(dpns,
5560 : (WorkTableScan *) plan);
5561 98506 : else if (IsA(plan, ModifyTable))
5562 : {
5563 287 : if (((ModifyTable *) plan)->operation == CMD_MERGE)
5564 40 : dpns->inner_plan = outerPlan(plan);
5565 : else
5566 247 : dpns->inner_plan = plan;
5567 : }
5568 : else
5569 98219 : dpns->inner_plan = innerPlan(plan);
5570 :
5571 99448 : if (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)
5572 141 : dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
5573 99307 : else if (dpns->inner_plan)
5574 17294 : dpns->inner_tlist = dpns->inner_plan->targetlist;
5575 : else
5576 82013 : dpns->inner_tlist = NIL;
5577 :
5578 : /* Set up referent for INDEX_VAR Vars, if needed */
5579 99448 : if (IsA(plan, IndexOnlyScan))
5580 2282 : dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5581 97166 : else if (IsA(plan, ForeignScan))
5582 1558 : dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5583 95608 : else if (IsA(plan, CustomScan))
5584 0 : dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5585 : else
5586 95608 : dpns->index_tlist = NIL;
5587 99448 : }
5588 :
5589 : /*
5590 : * Locate the ancestor plan node that is the RecursiveUnion generating
5591 : * the WorkTableScan's work table. We can match on wtParam, since that
5592 : * should be unique within the plan tree.
5593 : */
5594 : static Plan *
5595 116 : find_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)
5596 : {
5597 : ListCell *lc;
5598 :
5599 292 : foreach(lc, dpns->ancestors)
5600 : {
5601 292 : Plan *ancestor = (Plan *) lfirst(lc);
5602 :
5603 292 : if (IsA(ancestor, RecursiveUnion) &&
5604 116 : ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5605 116 : return ancestor;
5606 : }
5607 0 : elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5608 : wtscan->wtParam);
5609 : return NULL;
5610 : }
5611 :
5612 : /*
5613 : * push_child_plan: temporarily transfer deparsing attention to a child plan
5614 : *
5615 : * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
5616 : * deparse context in case the referenced expression itself uses
5617 : * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
5618 : * affecting levelsup issues (although in a Plan tree there really shouldn't
5619 : * be any).
5620 : *
5621 : * Caller must provide a local deparse_namespace variable to save the
5622 : * previous state for pop_child_plan.
5623 : */
5624 : static void
5625 57666 : push_child_plan(deparse_namespace *dpns, Plan *plan,
5626 : deparse_namespace *save_dpns)
5627 : {
5628 : /* Save state for restoration later */
5629 57666 : *save_dpns = *dpns;
5630 :
5631 : /* Link current plan node into ancestors list */
5632 57666 : dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5633 :
5634 : /* Set attention on selected child */
5635 57666 : set_deparse_plan(dpns, plan);
5636 57666 : }
5637 :
5638 : /*
5639 : * pop_child_plan: undo the effects of push_child_plan
5640 : */
5641 : static void
5642 57666 : pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5643 : {
5644 : List *ancestors;
5645 :
5646 : /* Get rid of ancestors list cell added by push_child_plan */
5647 57666 : ancestors = list_delete_first(dpns->ancestors);
5648 :
5649 : /* Restore fields changed by push_child_plan */
5650 57666 : *dpns = *save_dpns;
5651 :
5652 : /* Make sure dpns->ancestors is right (may be unnecessary) */
5653 57666 : dpns->ancestors = ancestors;
5654 57666 : }
5655 :
5656 : /*
5657 : * push_ancestor_plan: temporarily transfer deparsing attention to an
5658 : * ancestor plan
5659 : *
5660 : * When expanding a Param reference, we must adjust the deparse context
5661 : * to match the plan node that contains the expression being printed;
5662 : * otherwise we'd fail if that expression itself contains a Param or
5663 : * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
5664 : *
5665 : * The target ancestor is conveniently identified by the ListCell holding it
5666 : * in dpns->ancestors.
5667 : *
5668 : * Caller must provide a local deparse_namespace variable to save the
5669 : * previous state for pop_ancestor_plan.
5670 : */
5671 : static void
5672 3091 : push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
5673 : deparse_namespace *save_dpns)
5674 : {
5675 3091 : Plan *plan = (Plan *) lfirst(ancestor_cell);
5676 :
5677 : /* Save state for restoration later */
5678 3091 : *save_dpns = *dpns;
5679 :
5680 : /* Build a new ancestor list with just this node's ancestors */
5681 3091 : dpns->ancestors =
5682 3091 : list_copy_tail(dpns->ancestors,
5683 3091 : list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5684 :
5685 : /* Set attention on selected ancestor */
5686 3091 : set_deparse_plan(dpns, plan);
5687 3091 : }
5688 :
5689 : /*
5690 : * pop_ancestor_plan: undo the effects of push_ancestor_plan
5691 : */
5692 : static void
5693 3091 : pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5694 : {
5695 : /* Free the ancestor list made in push_ancestor_plan */
5696 3091 : list_free(dpns->ancestors);
5697 :
5698 : /* Restore fields changed by push_ancestor_plan */
5699 3091 : *dpns = *save_dpns;
5700 3091 : }
5701 :
5702 :
5703 : /* ----------
5704 : * make_ruledef - reconstruct the CREATE RULE command
5705 : * for a given pg_rewrite tuple
5706 : * ----------
5707 : */
5708 : static void
5709 309 : make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5710 : int prettyFlags)
5711 : {
5712 : char *rulename;
5713 : char ev_type;
5714 : Oid ev_class;
5715 : bool is_instead;
5716 : char *ev_qual;
5717 : char *ev_action;
5718 : List *actions;
5719 : Relation ev_relation;
5720 309 : TupleDesc viewResultDesc = NULL;
5721 : int fno;
5722 : Datum dat;
5723 : bool isnull;
5724 :
5725 : /*
5726 : * Get the attribute values from the rules tuple
5727 : */
5728 309 : fno = SPI_fnumber(rulettc, "rulename");
5729 309 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5730 : Assert(!isnull);
5731 309 : rulename = NameStr(*(DatumGetName(dat)));
5732 :
5733 309 : fno = SPI_fnumber(rulettc, "ev_type");
5734 309 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5735 : Assert(!isnull);
5736 309 : ev_type = DatumGetChar(dat);
5737 :
5738 309 : fno = SPI_fnumber(rulettc, "ev_class");
5739 309 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5740 : Assert(!isnull);
5741 309 : ev_class = DatumGetObjectId(dat);
5742 :
5743 309 : fno = SPI_fnumber(rulettc, "is_instead");
5744 309 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5745 : Assert(!isnull);
5746 309 : is_instead = DatumGetBool(dat);
5747 :
5748 309 : fno = SPI_fnumber(rulettc, "ev_qual");
5749 309 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5750 : Assert(ev_qual != NULL);
5751 :
5752 309 : fno = SPI_fnumber(rulettc, "ev_action");
5753 309 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5754 : Assert(ev_action != NULL);
5755 309 : actions = (List *) stringToNode(ev_action);
5756 309 : if (actions == NIL)
5757 0 : elog(ERROR, "invalid empty ev_action list");
5758 :
5759 309 : ev_relation = table_open(ev_class, AccessShareLock);
5760 :
5761 : /*
5762 : * Build the rules definition text
5763 : */
5764 309 : appendStringInfo(buf, "CREATE RULE %s AS",
5765 : quote_identifier(rulename));
5766 :
5767 309 : if (prettyFlags & PRETTYFLAG_INDENT)
5768 309 : appendStringInfoString(buf, "\n ON ");
5769 : else
5770 0 : appendStringInfoString(buf, " ON ");
5771 :
5772 : /* The event the rule is fired for */
5773 309 : switch (ev_type)
5774 : {
5775 4 : case '1':
5776 4 : appendStringInfoString(buf, "SELECT");
5777 4 : viewResultDesc = RelationGetDescr(ev_relation);
5778 4 : break;
5779 :
5780 84 : case '2':
5781 84 : appendStringInfoString(buf, "UPDATE");
5782 84 : break;
5783 :
5784 165 : case '3':
5785 165 : appendStringInfoString(buf, "INSERT");
5786 165 : break;
5787 :
5788 56 : case '4':
5789 56 : appendStringInfoString(buf, "DELETE");
5790 56 : break;
5791 :
5792 0 : default:
5793 0 : ereport(ERROR,
5794 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5795 : errmsg("rule \"%s\" has unsupported event type %d",
5796 : rulename, ev_type)));
5797 : break;
5798 : }
5799 :
5800 : /* The relation the rule is fired on */
5801 309 : appendStringInfo(buf, " TO %s",
5802 309 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
5803 76 : generate_relation_name(ev_class, NIL) :
5804 233 : generate_qualified_relation_name(ev_class));
5805 :
5806 : /* If the rule has an event qualification, add it */
5807 309 : if (strcmp(ev_qual, "<>") != 0)
5808 : {
5809 : Node *qual;
5810 : Query *query;
5811 : deparse_context context;
5812 : deparse_namespace dpns;
5813 :
5814 62 : if (prettyFlags & PRETTYFLAG_INDENT)
5815 62 : appendStringInfoString(buf, "\n ");
5816 62 : appendStringInfoString(buf, " WHERE ");
5817 :
5818 62 : qual = stringToNode(ev_qual);
5819 :
5820 : /*
5821 : * We need to make a context for recognizing any Vars in the qual
5822 : * (which can only be references to OLD and NEW). Use the rtable of
5823 : * the first query in the action list for this purpose.
5824 : */
5825 62 : query = (Query *) linitial(actions);
5826 :
5827 : /*
5828 : * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5829 : * into the SELECT, and that's what we need to look at. (Ugly kluge
5830 : * ... try to fix this when we redesign querytrees.)
5831 : */
5832 62 : query = getInsertSelectQuery(query, NULL);
5833 :
5834 : /* Must acquire locks right away; see notes in get_query_def() */
5835 62 : AcquireRewriteLocks(query, false, false);
5836 :
5837 62 : context.buf = buf;
5838 62 : context.namespaces = list_make1(&dpns);
5839 62 : context.resultDesc = NULL;
5840 62 : context.targetList = NIL;
5841 62 : context.windowClause = NIL;
5842 62 : context.varprefix = (list_length(query->rtable) != 1);
5843 62 : context.prettyFlags = prettyFlags;
5844 62 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
5845 62 : context.indentLevel = PRETTYINDENT_STD;
5846 62 : context.colNamesVisible = true;
5847 62 : context.inGroupBy = false;
5848 62 : context.varInOrderBy = false;
5849 62 : context.appendparents = NULL;
5850 :
5851 62 : set_deparse_for_query(&dpns, query, NIL);
5852 :
5853 62 : get_rule_expr(qual, &context, false);
5854 : }
5855 :
5856 309 : appendStringInfoString(buf, " DO ");
5857 :
5858 : /* The INSTEAD keyword (if so) */
5859 309 : if (is_instead)
5860 183 : appendStringInfoString(buf, "INSTEAD ");
5861 :
5862 : /* Finally the rules actions */
5863 309 : if (list_length(actions) > 1)
5864 : {
5865 : ListCell *action;
5866 : Query *query;
5867 :
5868 10 : appendStringInfoChar(buf, '(');
5869 30 : foreach(action, actions)
5870 : {
5871 20 : query = (Query *) lfirst(action);
5872 20 : get_query_def(query, buf, NIL, viewResultDesc, true,
5873 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5874 20 : if (prettyFlags)
5875 20 : appendStringInfoString(buf, ";\n");
5876 : else
5877 0 : appendStringInfoString(buf, "; ");
5878 : }
5879 10 : appendStringInfoString(buf, ");");
5880 : }
5881 : else
5882 : {
5883 : Query *query;
5884 :
5885 299 : query = (Query *) linitial(actions);
5886 299 : get_query_def(query, buf, NIL, viewResultDesc, true,
5887 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5888 299 : appendStringInfoChar(buf, ';');
5889 : }
5890 :
5891 309 : table_close(ev_relation, AccessShareLock);
5892 309 : }
5893 :
5894 :
5895 : /* ----------
5896 : * make_viewdef - reconstruct the SELECT part of a
5897 : * view rewrite rule
5898 : * ----------
5899 : */
5900 : static void
5901 2118 : make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5902 : int prettyFlags, int wrapColumn)
5903 : {
5904 : Query *query;
5905 : char ev_type;
5906 : Oid ev_class;
5907 : bool is_instead;
5908 : char *ev_qual;
5909 : char *ev_action;
5910 : List *actions;
5911 : Relation ev_relation;
5912 : int fno;
5913 : Datum dat;
5914 : bool isnull;
5915 :
5916 : /*
5917 : * Get the attribute values from the rules tuple
5918 : */
5919 2118 : fno = SPI_fnumber(rulettc, "ev_type");
5920 2118 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5921 : Assert(!isnull);
5922 2118 : ev_type = DatumGetChar(dat);
5923 :
5924 2118 : fno = SPI_fnumber(rulettc, "ev_class");
5925 2118 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5926 : Assert(!isnull);
5927 2118 : ev_class = DatumGetObjectId(dat);
5928 :
5929 2118 : fno = SPI_fnumber(rulettc, "is_instead");
5930 2118 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5931 : Assert(!isnull);
5932 2118 : is_instead = DatumGetBool(dat);
5933 :
5934 2118 : fno = SPI_fnumber(rulettc, "ev_qual");
5935 2118 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5936 : Assert(ev_qual != NULL);
5937 :
5938 2118 : fno = SPI_fnumber(rulettc, "ev_action");
5939 2118 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5940 : Assert(ev_action != NULL);
5941 2118 : actions = (List *) stringToNode(ev_action);
5942 :
5943 2118 : if (list_length(actions) != 1)
5944 : {
5945 : /* keep output buffer empty and leave */
5946 0 : return;
5947 : }
5948 :
5949 2118 : query = (Query *) linitial(actions);
5950 :
5951 2118 : if (ev_type != '1' || !is_instead ||
5952 2118 : strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5953 : {
5954 : /* keep output buffer empty and leave */
5955 0 : return;
5956 : }
5957 :
5958 2118 : ev_relation = table_open(ev_class, AccessShareLock);
5959 :
5960 2118 : get_query_def(query, buf, NIL, RelationGetDescr(ev_relation), true,
5961 : prettyFlags, wrapColumn, 0);
5962 2118 : appendStringInfoChar(buf, ';');
5963 :
5964 2118 : table_close(ev_relation, AccessShareLock);
5965 : }
5966 :
5967 :
5968 : /* ----------
5969 : * get_query_def - Parse back one query parsetree
5970 : *
5971 : * query: parsetree to be displayed
5972 : * buf: output text is appended to buf
5973 : * parentnamespace: list (initially empty) of outer-level deparse_namespace's
5974 : * resultDesc: if not NULL, the output tuple descriptor for the view
5975 : * represented by a SELECT query. We use the column names from it
5976 : * to label SELECT output columns, in preference to names in the query
5977 : * colNamesVisible: true if the surrounding context cares about the output
5978 : * column names at all (as, for example, an EXISTS() context does not);
5979 : * when false, we can suppress dummy column labels such as "?column?"
5980 : * prettyFlags: bitmask of PRETTYFLAG_XXX options
5981 : * wrapColumn: maximum line length, or -1 to disable wrapping
5982 : * startIndent: initial indentation amount
5983 : * ----------
5984 : */
5985 : static void
5986 3459 : get_query_def(Query *query, StringInfo buf, List *parentnamespace,
5987 : TupleDesc resultDesc, bool colNamesVisible,
5988 : int prettyFlags, int wrapColumn, int startIndent)
5989 : {
5990 : deparse_context context;
5991 : deparse_namespace dpns;
5992 : int rtable_size;
5993 :
5994 : /* Guard against excessively long or deeply-nested queries */
5995 3459 : CHECK_FOR_INTERRUPTS();
5996 3459 : check_stack_depth();
5997 :
5998 6918 : rtable_size = query->hasGroupRTE ?
5999 3459 : list_length(query->rtable) - 1 :
6000 3340 : list_length(query->rtable);
6001 :
6002 : /*
6003 : * Replace any Vars in the query's targetlist and havingQual that
6004 : * reference GROUP outputs with the underlying grouping expressions.
6005 : *
6006 : * We can safely pass NULL for the root here. Preserving varnullingrels
6007 : * makes no difference to the deparsed source text.
6008 : */
6009 3459 : if (query->hasGroupRTE)
6010 : {
6011 119 : query->targetList = (List *)
6012 119 : flatten_group_exprs(NULL, query, (Node *) query->targetList);
6013 119 : query->havingQual =
6014 119 : flatten_group_exprs(NULL, query, query->havingQual);
6015 : }
6016 :
6017 : /*
6018 : * Before we begin to examine the query, acquire locks on referenced
6019 : * relations, and fix up deleted columns in JOIN RTEs. This ensures
6020 : * consistent results. Note we assume it's OK to scribble on the passed
6021 : * querytree!
6022 : *
6023 : * We are only deparsing the query (we are not about to execute it), so we
6024 : * only need AccessShareLock on the relations it mentions.
6025 : */
6026 3459 : AcquireRewriteLocks(query, false, false);
6027 :
6028 3459 : context.buf = buf;
6029 3459 : context.namespaces = lcons(&dpns, list_copy(parentnamespace));
6030 3459 : context.resultDesc = NULL;
6031 3459 : context.targetList = NIL;
6032 3459 : context.windowClause = NIL;
6033 3459 : context.varprefix = (parentnamespace != NIL ||
6034 3459 : rtable_size != 1);
6035 3459 : context.prettyFlags = prettyFlags;
6036 3459 : context.wrapColumn = wrapColumn;
6037 3459 : context.indentLevel = startIndent;
6038 3459 : context.colNamesVisible = colNamesVisible;
6039 3459 : context.inGroupBy = false;
6040 3459 : context.varInOrderBy = false;
6041 3459 : context.appendparents = NULL;
6042 :
6043 3459 : set_deparse_for_query(&dpns, query, parentnamespace);
6044 :
6045 3459 : switch (query->commandType)
6046 : {
6047 3085 : case CMD_SELECT:
6048 : /* We set context.resultDesc only if it's a SELECT */
6049 3085 : context.resultDesc = resultDesc;
6050 3085 : get_select_query_def(query, &context);
6051 3085 : break;
6052 :
6053 94 : case CMD_UPDATE:
6054 94 : get_update_query_def(query, &context);
6055 94 : break;
6056 :
6057 194 : case CMD_INSERT:
6058 194 : get_insert_query_def(query, &context);
6059 194 : break;
6060 :
6061 47 : case CMD_DELETE:
6062 47 : get_delete_query_def(query, &context);
6063 47 : break;
6064 :
6065 8 : case CMD_MERGE:
6066 8 : get_merge_query_def(query, &context);
6067 8 : break;
6068 :
6069 22 : case CMD_NOTHING:
6070 22 : appendStringInfoString(buf, "NOTHING");
6071 22 : break;
6072 :
6073 9 : case CMD_UTILITY:
6074 9 : get_utility_query_def(query, &context);
6075 9 : break;
6076 :
6077 0 : default:
6078 0 : elog(ERROR, "unrecognized query command type: %d",
6079 : query->commandType);
6080 : break;
6081 : }
6082 3459 : }
6083 :
6084 : /* ----------
6085 : * get_values_def - Parse back a VALUES list
6086 : * ----------
6087 : */
6088 : static void
6089 177 : get_values_def(List *values_lists, deparse_context *context)
6090 : {
6091 177 : StringInfo buf = context->buf;
6092 177 : bool first_list = true;
6093 : ListCell *vtl;
6094 :
6095 177 : appendStringInfoString(buf, "VALUES ");
6096 :
6097 505 : foreach(vtl, values_lists)
6098 : {
6099 328 : List *sublist = (List *) lfirst(vtl);
6100 328 : bool first_col = true;
6101 : ListCell *lc;
6102 :
6103 328 : if (first_list)
6104 177 : first_list = false;
6105 : else
6106 151 : appendStringInfoString(buf, ", ");
6107 :
6108 328 : appendStringInfoChar(buf, '(');
6109 1260 : foreach(lc, sublist)
6110 : {
6111 932 : Node *col = (Node *) lfirst(lc);
6112 :
6113 932 : if (first_col)
6114 328 : first_col = false;
6115 : else
6116 604 : appendStringInfoChar(buf, ',');
6117 :
6118 : /*
6119 : * Print the value. Whole-row Vars need special treatment.
6120 : */
6121 932 : get_rule_expr_toplevel(col, context, false);
6122 : }
6123 328 : appendStringInfoChar(buf, ')');
6124 : }
6125 177 : }
6126 :
6127 : /* ----------
6128 : * get_with_clause - Parse back a WITH clause
6129 : * ----------
6130 : */
6131 : static void
6132 3428 : get_with_clause(Query *query, deparse_context *context)
6133 : {
6134 3428 : StringInfo buf = context->buf;
6135 : const char *sep;
6136 : ListCell *l;
6137 :
6138 3428 : if (query->cteList == NIL)
6139 3365 : return;
6140 :
6141 63 : if (PRETTY_INDENT(context))
6142 : {
6143 63 : context->indentLevel += PRETTYINDENT_STD;
6144 63 : appendStringInfoChar(buf, ' ');
6145 : }
6146 :
6147 63 : if (query->hasRecursive)
6148 34 : sep = "WITH RECURSIVE ";
6149 : else
6150 29 : sep = "WITH ";
6151 156 : foreach(l, query->cteList)
6152 : {
6153 93 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
6154 :
6155 93 : appendStringInfoString(buf, sep);
6156 93 : appendStringInfoString(buf, quote_identifier(cte->ctename));
6157 93 : if (cte->aliascolnames)
6158 : {
6159 38 : bool first = true;
6160 : ListCell *col;
6161 :
6162 38 : appendStringInfoChar(buf, '(');
6163 100 : foreach(col, cte->aliascolnames)
6164 : {
6165 62 : if (first)
6166 38 : first = false;
6167 : else
6168 24 : appendStringInfoString(buf, ", ");
6169 62 : appendStringInfoString(buf,
6170 62 : quote_identifier(strVal(lfirst(col))));
6171 : }
6172 38 : appendStringInfoChar(buf, ')');
6173 : }
6174 93 : appendStringInfoString(buf, " AS ");
6175 93 : switch (cte->ctematerialized)
6176 : {
6177 81 : case CTEMaterializeDefault:
6178 81 : break;
6179 12 : case CTEMaterializeAlways:
6180 12 : appendStringInfoString(buf, "MATERIALIZED ");
6181 12 : break;
6182 0 : case CTEMaterializeNever:
6183 0 : appendStringInfoString(buf, "NOT MATERIALIZED ");
6184 0 : break;
6185 : }
6186 93 : appendStringInfoChar(buf, '(');
6187 93 : if (PRETTY_INDENT(context))
6188 93 : appendContextKeyword(context, "", 0, 0, 0);
6189 93 : get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
6190 : true,
6191 : context->prettyFlags, context->wrapColumn,
6192 : context->indentLevel);
6193 93 : if (PRETTY_INDENT(context))
6194 93 : appendContextKeyword(context, "", 0, 0, 0);
6195 93 : appendStringInfoChar(buf, ')');
6196 :
6197 93 : if (cte->search_clause)
6198 : {
6199 4 : bool first = true;
6200 : ListCell *lc;
6201 :
6202 4 : appendStringInfo(buf, " SEARCH %s FIRST BY ",
6203 4 : cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
6204 :
6205 12 : foreach(lc, cte->search_clause->search_col_list)
6206 : {
6207 8 : if (first)
6208 4 : first = false;
6209 : else
6210 4 : appendStringInfoString(buf, ", ");
6211 8 : appendStringInfoString(buf,
6212 8 : quote_identifier(strVal(lfirst(lc))));
6213 : }
6214 :
6215 4 : appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
6216 : }
6217 :
6218 93 : if (cte->cycle_clause)
6219 : {
6220 8 : bool first = true;
6221 : ListCell *lc;
6222 :
6223 8 : appendStringInfoString(buf, " CYCLE ");
6224 :
6225 24 : foreach(lc, cte->cycle_clause->cycle_col_list)
6226 : {
6227 16 : if (first)
6228 8 : first = false;
6229 : else
6230 8 : appendStringInfoString(buf, ", ");
6231 16 : appendStringInfoString(buf,
6232 16 : quote_identifier(strVal(lfirst(lc))));
6233 : }
6234 :
6235 8 : appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
6236 :
6237 : {
6238 8 : Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
6239 8 : Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
6240 :
6241 12 : if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
6242 4 : cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
6243 : {
6244 4 : appendStringInfoString(buf, " TO ");
6245 4 : get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
6246 4 : appendStringInfoString(buf, " DEFAULT ");
6247 4 : get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
6248 : }
6249 : }
6250 :
6251 8 : appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
6252 : }
6253 :
6254 93 : sep = ", ";
6255 : }
6256 :
6257 63 : if (PRETTY_INDENT(context))
6258 : {
6259 63 : context->indentLevel -= PRETTYINDENT_STD;
6260 63 : appendContextKeyword(context, "", 0, 0, 0);
6261 : }
6262 : else
6263 0 : appendStringInfoChar(buf, ' ');
6264 : }
6265 :
6266 : /* ----------
6267 : * get_select_query_def - Parse back a SELECT parsetree
6268 : * ----------
6269 : */
6270 : static void
6271 3085 : get_select_query_def(Query *query, deparse_context *context)
6272 : {
6273 3085 : StringInfo buf = context->buf;
6274 : bool force_colno;
6275 : ListCell *l;
6276 :
6277 : /* Insert the WITH clause if given */
6278 3085 : get_with_clause(query, context);
6279 :
6280 : /* Subroutines may need to consult the SELECT targetlist and windowClause */
6281 3085 : context->targetList = query->targetList;
6282 3085 : context->windowClause = query->windowClause;
6283 :
6284 : /*
6285 : * If the Query node has a setOperations tree, then it's the top level of
6286 : * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
6287 : * fields are interesting in the top query itself.
6288 : */
6289 3085 : if (query->setOperations)
6290 : {
6291 103 : get_setop_query(query->setOperations, query, context);
6292 : /* ORDER BY clauses must be simple in this case */
6293 103 : force_colno = true;
6294 : }
6295 : else
6296 : {
6297 2982 : get_basic_select_query(query, context);
6298 2982 : force_colno = false;
6299 : }
6300 :
6301 : /* Add the ORDER BY clause if given */
6302 3085 : if (query->sortClause != NIL)
6303 : {
6304 124 : appendContextKeyword(context, " ORDER BY ",
6305 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6306 124 : get_rule_orderby(query->sortClause, query->targetList,
6307 : force_colno, context);
6308 : }
6309 :
6310 : /*
6311 : * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
6312 : * standard spelling of LIMIT.
6313 : */
6314 3085 : if (query->limitOffset != NULL)
6315 : {
6316 18 : appendContextKeyword(context, " OFFSET ",
6317 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6318 18 : get_rule_expr(query->limitOffset, context, false);
6319 : }
6320 3085 : if (query->limitCount != NULL)
6321 : {
6322 49 : if (query->limitOption == LIMIT_OPTION_WITH_TIES)
6323 : {
6324 : /*
6325 : * The limitCount arg is a c_expr, so it needs parens. Simple
6326 : * literals and function expressions would not need parens, but
6327 : * unfortunately it's hard to tell if the expression will be
6328 : * printed as a simple literal like 123 or as a typecast
6329 : * expression, like '-123'::int4. The grammar accepts the former
6330 : * without quoting, but not the latter.
6331 : */
6332 27 : appendContextKeyword(context, " FETCH FIRST ",
6333 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6334 27 : appendStringInfoChar(buf, '(');
6335 27 : get_rule_expr(query->limitCount, context, false);
6336 27 : appendStringInfoChar(buf, ')');
6337 27 : appendStringInfoString(buf, " ROWS WITH TIES");
6338 : }
6339 : else
6340 : {
6341 22 : appendContextKeyword(context, " LIMIT ",
6342 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6343 22 : if (IsA(query->limitCount, Const) &&
6344 9 : ((Const *) query->limitCount)->constisnull)
6345 9 : appendStringInfoString(buf, "ALL");
6346 : else
6347 13 : get_rule_expr(query->limitCount, context, false);
6348 : }
6349 : }
6350 :
6351 : /* Add FOR [KEY] UPDATE/SHARE clauses if present */
6352 3085 : if (query->hasForUpdate)
6353 : {
6354 8 : foreach(l, query->rowMarks)
6355 : {
6356 4 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
6357 :
6358 : /* don't print implicit clauses */
6359 4 : if (rc->pushedDown)
6360 0 : continue;
6361 :
6362 4 : appendContextKeyword(context,
6363 4 : get_lock_clause_strength(rc->strength),
6364 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6365 :
6366 4 : appendStringInfo(buf, " OF %s",
6367 4 : quote_identifier(get_rtable_name(rc->rti,
6368 : context)));
6369 4 : if (rc->waitPolicy == LockWaitError)
6370 0 : appendStringInfoString(buf, " NOWAIT");
6371 4 : else if (rc->waitPolicy == LockWaitSkip)
6372 0 : appendStringInfoString(buf, " SKIP LOCKED");
6373 : }
6374 : }
6375 3085 : }
6376 :
6377 : static char *
6378 8 : get_lock_clause_strength(LockClauseStrength strength)
6379 : {
6380 8 : switch (strength)
6381 : {
6382 0 : case LCS_NONE:
6383 : /* we intentionally throw an error for LCS_NONE */
6384 0 : elog(ERROR, "unrecognized LockClauseStrength %d",
6385 : (int) strength);
6386 : break;
6387 0 : case LCS_FORKEYSHARE:
6388 0 : return " FOR KEY SHARE";
6389 0 : case LCS_FORSHARE:
6390 0 : return " FOR SHARE";
6391 0 : case LCS_FORNOKEYUPDATE:
6392 0 : return " FOR NO KEY UPDATE";
6393 8 : case LCS_FORUPDATE:
6394 8 : return " FOR UPDATE";
6395 : }
6396 0 : return NULL; /* keep compiler quiet */
6397 : }
6398 :
6399 : /*
6400 : * Detect whether query looks like SELECT ... FROM VALUES(),
6401 : * with no need to rename the output columns of the VALUES RTE.
6402 : * If so, return the VALUES RTE. Otherwise return NULL.
6403 : */
6404 : static RangeTblEntry *
6405 2982 : get_simple_values_rte(Query *query, TupleDesc resultDesc)
6406 : {
6407 2982 : RangeTblEntry *result = NULL;
6408 : ListCell *lc;
6409 :
6410 : /*
6411 : * We want to detect a match even if the Query also contains OLD or NEW
6412 : * rule RTEs. So the idea is to scan the rtable and see if there is only
6413 : * one inFromCl RTE that is a VALUES RTE.
6414 : */
6415 3217 : foreach(lc, query->rtable)
6416 : {
6417 2727 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
6418 :
6419 2727 : if (rte->rtekind == RTE_VALUES && rte->inFromCl)
6420 : {
6421 151 : if (result)
6422 2492 : return NULL; /* multiple VALUES (probably not possible) */
6423 151 : result = rte;
6424 : }
6425 2576 : else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
6426 84 : continue; /* ignore rule entries */
6427 : else
6428 2492 : return NULL; /* something else -> not simple VALUES */
6429 : }
6430 :
6431 : /*
6432 : * We don't need to check the targetlist in any great detail, because
6433 : * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
6434 : * appear inside auto-generated sub-queries with very restricted
6435 : * structure. However, DefineView might have modified the tlist by
6436 : * injecting new column aliases, or we might have some other column
6437 : * aliases forced by a resultDesc. We can only simplify if the RTE's
6438 : * column names match the names that get_target_list() would select.
6439 : */
6440 490 : if (result)
6441 : {
6442 : ListCell *lcn;
6443 : int colno;
6444 :
6445 151 : if (list_length(query->targetList) != list_length(result->eref->colnames))
6446 0 : return NULL; /* this probably cannot happen */
6447 151 : colno = 0;
6448 556 : forboth(lc, query->targetList, lcn, result->eref->colnames)
6449 : {
6450 413 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
6451 413 : char *cname = strVal(lfirst(lcn));
6452 : char *colname;
6453 :
6454 413 : if (tle->resjunk)
6455 8 : return NULL; /* this probably cannot happen */
6456 :
6457 : /* compute name that get_target_list would use for column */
6458 413 : colno++;
6459 413 : if (resultDesc && colno <= resultDesc->natts)
6460 20 : colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6461 : else
6462 393 : colname = tle->resname;
6463 :
6464 : /* does it match the VALUES RTE? */
6465 413 : if (colname == NULL || strcmp(colname, cname) != 0)
6466 8 : return NULL; /* column name has been changed */
6467 : }
6468 : }
6469 :
6470 482 : return result;
6471 : }
6472 :
6473 : static void
6474 2982 : get_basic_select_query(Query *query, deparse_context *context)
6475 : {
6476 2982 : StringInfo buf = context->buf;
6477 : RangeTblEntry *values_rte;
6478 : char *sep;
6479 : ListCell *l;
6480 :
6481 2982 : if (PRETTY_INDENT(context))
6482 : {
6483 2955 : context->indentLevel += PRETTYINDENT_STD;
6484 2955 : appendStringInfoChar(buf, ' ');
6485 : }
6486 :
6487 : /*
6488 : * If the query looks like SELECT * FROM (VALUES ...), then print just the
6489 : * VALUES part. This reverses what transformValuesClause() did at parse
6490 : * time.
6491 : */
6492 2982 : values_rte = get_simple_values_rte(query, context->resultDesc);
6493 2982 : if (values_rte)
6494 : {
6495 143 : get_values_def(values_rte->values_lists, context);
6496 143 : return;
6497 : }
6498 :
6499 : /*
6500 : * Build up the query string - first we say SELECT
6501 : */
6502 2839 : if (query->isReturn)
6503 31 : appendStringInfoString(buf, "RETURN");
6504 : else
6505 2808 : appendStringInfoString(buf, "SELECT");
6506 :
6507 : /* Add the DISTINCT clause if given */
6508 2839 : if (query->distinctClause != NIL)
6509 : {
6510 0 : if (query->hasDistinctOn)
6511 : {
6512 0 : appendStringInfoString(buf, " DISTINCT ON (");
6513 0 : sep = "";
6514 0 : foreach(l, query->distinctClause)
6515 : {
6516 0 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6517 :
6518 0 : appendStringInfoString(buf, sep);
6519 0 : get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6520 : false, context);
6521 0 : sep = ", ";
6522 : }
6523 0 : appendStringInfoChar(buf, ')');
6524 : }
6525 : else
6526 0 : appendStringInfoString(buf, " DISTINCT");
6527 : }
6528 :
6529 : /* Then we tell what to select (the targetlist) */
6530 2839 : get_target_list(query->targetList, context);
6531 :
6532 : /* Add the FROM clause if needed */
6533 2839 : get_from_clause(query, " FROM ", context);
6534 :
6535 : /* Add the WHERE clause if given */
6536 2839 : if (query->jointree->quals != NULL)
6537 : {
6538 895 : appendContextKeyword(context, " WHERE ",
6539 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6540 895 : get_rule_expr(query->jointree->quals, context, false);
6541 : }
6542 :
6543 : /* Add the GROUP BY clause if given */
6544 2839 : if (query->groupClause != NULL || query->groupingSets != NULL)
6545 : {
6546 : bool save_ingroupby;
6547 :
6548 119 : appendContextKeyword(context, " GROUP BY ",
6549 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6550 119 : if (query->groupDistinct)
6551 0 : appendStringInfoString(buf, "DISTINCT ");
6552 :
6553 119 : save_ingroupby = context->inGroupBy;
6554 119 : context->inGroupBy = true;
6555 :
6556 119 : if (query->groupByAll)
6557 4 : appendStringInfoString(buf, "ALL");
6558 115 : else if (query->groupingSets == NIL)
6559 : {
6560 111 : sep = "";
6561 255 : foreach(l, query->groupClause)
6562 : {
6563 144 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6564 :
6565 144 : appendStringInfoString(buf, sep);
6566 144 : get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
6567 : false, context);
6568 144 : sep = ", ";
6569 : }
6570 : }
6571 : else
6572 : {
6573 4 : sep = "";
6574 8 : foreach(l, query->groupingSets)
6575 : {
6576 4 : GroupingSet *grp = lfirst(l);
6577 :
6578 4 : appendStringInfoString(buf, sep);
6579 4 : get_rule_groupingset(grp, query->targetList, true, context);
6580 4 : sep = ", ";
6581 : }
6582 : }
6583 :
6584 119 : context->inGroupBy = save_ingroupby;
6585 : }
6586 :
6587 : /* Add the HAVING clause if given */
6588 2839 : if (query->havingQual != NULL)
6589 : {
6590 5 : appendContextKeyword(context, " HAVING ",
6591 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6592 5 : get_rule_expr(query->havingQual, context, false);
6593 : }
6594 :
6595 : /* Add the WINDOW clause if needed */
6596 2839 : if (query->windowClause != NIL)
6597 32 : get_rule_windowclause(query, context);
6598 : }
6599 :
6600 : /* ----------
6601 : * get_target_list - Parse back a SELECT target list
6602 : *
6603 : * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.
6604 : * ----------
6605 : */
6606 : static void
6607 2948 : get_target_list(List *targetList, deparse_context *context)
6608 : {
6609 2948 : StringInfo buf = context->buf;
6610 : StringInfoData targetbuf;
6611 2948 : bool last_was_multiline = false;
6612 : char *sep;
6613 : int colno;
6614 : ListCell *l;
6615 :
6616 : /* we use targetbuf to hold each TLE's text temporarily */
6617 2948 : initStringInfo(&targetbuf);
6618 :
6619 2948 : sep = " ";
6620 2948 : colno = 0;
6621 15661 : foreach(l, targetList)
6622 : {
6623 12713 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6624 : char *colname;
6625 : char *attname;
6626 :
6627 12713 : if (tle->resjunk)
6628 25 : continue; /* ignore junk entries */
6629 :
6630 12688 : appendStringInfoString(buf, sep);
6631 12688 : sep = ", ";
6632 12688 : colno++;
6633 :
6634 : /*
6635 : * Put the new field text into targetbuf so we can decide after we've
6636 : * got it whether or not it needs to go on a new line.
6637 : */
6638 12688 : resetStringInfo(&targetbuf);
6639 12688 : context->buf = &targetbuf;
6640 :
6641 : /*
6642 : * We special-case Var nodes rather than using get_rule_expr. This is
6643 : * needed because get_rule_expr will display a whole-row Var as
6644 : * "foo.*", which is the preferred notation in most contexts, but at
6645 : * the top level of a SELECT list it's not right (the parser will
6646 : * expand that notation into multiple columns, yielding behavior
6647 : * different from a whole-row Var). We need to call get_variable
6648 : * directly so that we can tell it to do the right thing, and so that
6649 : * we can get the attribute name which is the default AS label.
6650 : */
6651 12688 : if (tle->expr && (IsA(tle->expr, Var)))
6652 : {
6653 9766 : attname = get_variable((Var *) tle->expr, 0, true, context);
6654 : }
6655 : else
6656 : {
6657 2922 : get_rule_expr((Node *) tle->expr, context, true);
6658 :
6659 : /*
6660 : * When colNamesVisible is true, we should always show the
6661 : * assigned column name explicitly. Otherwise, show it only if
6662 : * it's not FigureColname's fallback.
6663 : */
6664 2922 : attname = context->colNamesVisible ? NULL : "?column?";
6665 : }
6666 :
6667 : /*
6668 : * Figure out what the result column should be called. In the context
6669 : * of a view, use the view's tuple descriptor (so as to pick up the
6670 : * effects of any column RENAME that's been done on the view).
6671 : * Otherwise, just use what we can find in the TLE.
6672 : */
6673 12688 : if (context->resultDesc && colno <= context->resultDesc->natts)
6674 11533 : colname = NameStr(TupleDescAttr(context->resultDesc,
6675 : colno - 1)->attname);
6676 : else
6677 1155 : colname = tle->resname;
6678 :
6679 : /* Show AS unless the column's name is correct as-is */
6680 12688 : if (colname) /* resname could be NULL */
6681 : {
6682 12657 : if (attname == NULL || strcmp(attname, colname) != 0)
6683 4152 : appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
6684 : }
6685 :
6686 : /* Restore context's output buffer */
6687 12688 : context->buf = buf;
6688 :
6689 : /* Consider line-wrapping if enabled */
6690 12688 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
6691 : {
6692 : int leading_nl_pos;
6693 :
6694 : /* Does the new field start with a new line? */
6695 12661 : if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
6696 368 : leading_nl_pos = 0;
6697 : else
6698 12293 : leading_nl_pos = -1;
6699 :
6700 : /* If so, we shouldn't add anything */
6701 12661 : if (leading_nl_pos >= 0)
6702 : {
6703 : /* instead, remove any trailing spaces currently in buf */
6704 368 : removeStringInfoSpaces(buf);
6705 : }
6706 : else
6707 : {
6708 : char *trailing_nl;
6709 :
6710 : /* Locate the start of the current line in the output buffer */
6711 12293 : trailing_nl = strrchr(buf->data, '\n');
6712 12293 : if (trailing_nl == NULL)
6713 3628 : trailing_nl = buf->data;
6714 : else
6715 8665 : trailing_nl++;
6716 :
6717 : /*
6718 : * Add a newline, plus some indentation, if the new field is
6719 : * not the first and either the new field would cause an
6720 : * overflow or the last field used more than one line.
6721 : */
6722 12293 : if (colno > 1 &&
6723 9381 : ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
6724 : last_was_multiline))
6725 9381 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
6726 : PRETTYINDENT_STD, PRETTYINDENT_VAR);
6727 : }
6728 :
6729 : /* Remember this field's multiline status for next iteration */
6730 12661 : last_was_multiline =
6731 12661 : (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
6732 : }
6733 :
6734 : /* Add the new field */
6735 12688 : appendBinaryStringInfo(buf, targetbuf.data, targetbuf.len);
6736 : }
6737 :
6738 : /* clean up */
6739 2948 : pfree(targetbuf.data);
6740 2948 : }
6741 :
6742 : static void
6743 109 : get_returning_clause(Query *query, deparse_context *context)
6744 : {
6745 109 : StringInfo buf = context->buf;
6746 :
6747 109 : if (query->returningList)
6748 : {
6749 109 : bool have_with = false;
6750 :
6751 109 : appendContextKeyword(context, " RETURNING",
6752 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6753 :
6754 : /* Add WITH (OLD/NEW) options, if they're not the defaults */
6755 109 : if (query->returningOldAlias && strcmp(query->returningOldAlias, "old") != 0)
6756 : {
6757 12 : appendStringInfo(buf, " WITH (OLD AS %s",
6758 12 : quote_identifier(query->returningOldAlias));
6759 12 : have_with = true;
6760 : }
6761 109 : if (query->returningNewAlias && strcmp(query->returningNewAlias, "new") != 0)
6762 : {
6763 12 : if (have_with)
6764 8 : appendStringInfo(buf, ", NEW AS %s",
6765 8 : quote_identifier(query->returningNewAlias));
6766 : else
6767 : {
6768 4 : appendStringInfo(buf, " WITH (NEW AS %s",
6769 4 : quote_identifier(query->returningNewAlias));
6770 4 : have_with = true;
6771 : }
6772 : }
6773 109 : if (have_with)
6774 16 : appendStringInfoChar(buf, ')');
6775 :
6776 : /* Add the returning expressions themselves */
6777 109 : get_target_list(query->returningList, context);
6778 : }
6779 109 : }
6780 :
6781 : static void
6782 463 : get_setop_query(Node *setOp, Query *query, deparse_context *context)
6783 : {
6784 463 : StringInfo buf = context->buf;
6785 : bool need_paren;
6786 :
6787 : /* Guard against excessively long or deeply-nested queries */
6788 463 : CHECK_FOR_INTERRUPTS();
6789 463 : check_stack_depth();
6790 :
6791 463 : if (IsA(setOp, RangeTblRef))
6792 : {
6793 283 : RangeTblRef *rtr = (RangeTblRef *) setOp;
6794 283 : RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
6795 283 : Query *subquery = rte->subquery;
6796 :
6797 : Assert(subquery != NULL);
6798 :
6799 : /*
6800 : * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.
6801 : * Also add parens if the leaf query contains its own set operations.
6802 : * (That shouldn't happen unless one of the other clauses is also
6803 : * present, see transformSetOperationTree; but let's be safe.)
6804 : */
6805 849 : need_paren = (subquery->cteList ||
6806 283 : subquery->sortClause ||
6807 283 : subquery->rowMarks ||
6808 283 : subquery->limitOffset ||
6809 849 : subquery->limitCount ||
6810 283 : subquery->setOperations);
6811 283 : if (need_paren)
6812 0 : appendStringInfoChar(buf, '(');
6813 283 : get_query_def(subquery, buf, context->namespaces,
6814 283 : context->resultDesc, context->colNamesVisible,
6815 : context->prettyFlags, context->wrapColumn,
6816 : context->indentLevel);
6817 283 : if (need_paren)
6818 0 : appendStringInfoChar(buf, ')');
6819 : }
6820 180 : else if (IsA(setOp, SetOperationStmt))
6821 : {
6822 180 : SetOperationStmt *op = (SetOperationStmt *) setOp;
6823 : int subindent;
6824 : bool save_colnamesvisible;
6825 :
6826 : /*
6827 : * We force parens when nesting two SetOperationStmts, except when the
6828 : * lefthand input is another setop of the same kind. Syntactically,
6829 : * we could omit parens in rather more cases, but it seems best to use
6830 : * parens to flag cases where the setop operator changes. If we use
6831 : * parens, we also increase the indentation level for the child query.
6832 : *
6833 : * There are some cases in which parens are needed around a leaf query
6834 : * too, but those are more easily handled at the next level down (see
6835 : * code above).
6836 : */
6837 180 : if (IsA(op->larg, SetOperationStmt))
6838 : {
6839 77 : SetOperationStmt *lop = (SetOperationStmt *) op->larg;
6840 :
6841 77 : if (op->op == lop->op && op->all == lop->all)
6842 77 : need_paren = false;
6843 : else
6844 0 : need_paren = true;
6845 : }
6846 : else
6847 103 : need_paren = false;
6848 :
6849 180 : if (need_paren)
6850 : {
6851 0 : appendStringInfoChar(buf, '(');
6852 0 : subindent = PRETTYINDENT_STD;
6853 0 : appendContextKeyword(context, "", subindent, 0, 0);
6854 : }
6855 : else
6856 180 : subindent = 0;
6857 :
6858 180 : get_setop_query(op->larg, query, context);
6859 :
6860 180 : if (need_paren)
6861 0 : appendContextKeyword(context, ") ", -subindent, 0, 0);
6862 180 : else if (PRETTY_INDENT(context))
6863 180 : appendContextKeyword(context, "", -subindent, 0, 0);
6864 : else
6865 0 : appendStringInfoChar(buf, ' ');
6866 :
6867 180 : switch (op->op)
6868 : {
6869 180 : case SETOP_UNION:
6870 180 : appendStringInfoString(buf, "UNION ");
6871 180 : break;
6872 0 : case SETOP_INTERSECT:
6873 0 : appendStringInfoString(buf, "INTERSECT ");
6874 0 : break;
6875 0 : case SETOP_EXCEPT:
6876 0 : appendStringInfoString(buf, "EXCEPT ");
6877 0 : break;
6878 0 : default:
6879 0 : elog(ERROR, "unrecognized set op: %d",
6880 : (int) op->op);
6881 : }
6882 180 : if (op->all)
6883 172 : appendStringInfoString(buf, "ALL ");
6884 :
6885 : /* Always parenthesize if RHS is another setop */
6886 180 : need_paren = IsA(op->rarg, SetOperationStmt);
6887 :
6888 : /*
6889 : * The indentation code here is deliberately a bit different from that
6890 : * for the lefthand input, because we want the line breaks in
6891 : * different places.
6892 : */
6893 180 : if (need_paren)
6894 : {
6895 0 : appendStringInfoChar(buf, '(');
6896 0 : subindent = PRETTYINDENT_STD;
6897 : }
6898 : else
6899 180 : subindent = 0;
6900 180 : appendContextKeyword(context, "", subindent, 0, 0);
6901 :
6902 : /*
6903 : * The output column names of the RHS sub-select don't matter.
6904 : */
6905 180 : save_colnamesvisible = context->colNamesVisible;
6906 180 : context->colNamesVisible = false;
6907 :
6908 180 : get_setop_query(op->rarg, query, context);
6909 :
6910 180 : context->colNamesVisible = save_colnamesvisible;
6911 :
6912 180 : if (PRETTY_INDENT(context))
6913 180 : context->indentLevel -= subindent;
6914 180 : if (need_paren)
6915 0 : appendContextKeyword(context, ")", 0, 0, 0);
6916 : }
6917 : else
6918 : {
6919 0 : elog(ERROR, "unrecognized node type: %d",
6920 : (int) nodeTag(setOp));
6921 : }
6922 463 : }
6923 :
6924 : /*
6925 : * Display a sort/group clause.
6926 : *
6927 : * Also returns the expression tree, so caller need not find it again.
6928 : */
6929 : static Node *
6930 421 : get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
6931 : deparse_context *context)
6932 : {
6933 421 : StringInfo buf = context->buf;
6934 : TargetEntry *tle;
6935 : Node *expr;
6936 :
6937 421 : tle = get_sortgroupref_tle(ref, tlist);
6938 421 : expr = (Node *) tle->expr;
6939 :
6940 : /*
6941 : * Use column-number form if requested by caller. Otherwise, if
6942 : * expression is a constant, force it to be dumped with an explicit cast
6943 : * as decoration --- this is because a simple integer constant is
6944 : * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
6945 : * we dump it without any decoration. Similarly, if it's just a Var,
6946 : * there is risk of misinterpretation if the column name is reassigned in
6947 : * the SELECT list, so we may need to force table qualification. And, if
6948 : * it's anything more complex than a simple Var, then force extra parens
6949 : * around it, to ensure it can't be misinterpreted as a cube() or rollup()
6950 : * construct.
6951 : */
6952 421 : if (force_colno)
6953 : {
6954 : Assert(!tle->resjunk);
6955 7 : appendStringInfo(buf, "%d", tle->resno);
6956 : }
6957 414 : else if (!expr)
6958 : /* do nothing, probably can't happen */ ;
6959 414 : else if (IsA(expr, Const))
6960 0 : get_const_expr((Const *) expr, context, 1);
6961 414 : else if (IsA(expr, Var))
6962 : {
6963 : /* Tell get_variable to check for name conflict */
6964 397 : bool save_varinorderby = context->varInOrderBy;
6965 :
6966 397 : context->varInOrderBy = true;
6967 397 : (void) get_variable((Var *) expr, 0, false, context);
6968 397 : context->varInOrderBy = save_varinorderby;
6969 : }
6970 : else
6971 : {
6972 : /*
6973 : * We must force parens for function-like expressions even if
6974 : * PRETTY_PAREN is off, since those are the ones in danger of
6975 : * misparsing. For other expressions we need to force them only if
6976 : * PRETTY_PAREN is on, since otherwise the expression will output them
6977 : * itself. (We can't skip the parens.)
6978 : */
6979 34 : bool need_paren = (PRETTY_PAREN(context)
6980 17 : || IsA(expr, FuncExpr)
6981 15 : || IsA(expr, Aggref)
6982 15 : || IsA(expr, WindowFunc)
6983 34 : || IsA(expr, JsonConstructorExpr));
6984 :
6985 17 : if (need_paren)
6986 2 : appendStringInfoChar(context->buf, '(');
6987 17 : get_rule_expr(expr, context, true);
6988 17 : if (need_paren)
6989 2 : appendStringInfoChar(context->buf, ')');
6990 : }
6991 :
6992 421 : return expr;
6993 : }
6994 :
6995 : /*
6996 : * Display a GroupingSet
6997 : */
6998 : static void
6999 12 : get_rule_groupingset(GroupingSet *gset, List *targetlist,
7000 : bool omit_parens, deparse_context *context)
7001 : {
7002 : ListCell *l;
7003 12 : StringInfo buf = context->buf;
7004 12 : bool omit_child_parens = true;
7005 12 : char *sep = "";
7006 :
7007 12 : switch (gset->kind)
7008 : {
7009 0 : case GROUPING_SET_EMPTY:
7010 0 : appendStringInfoString(buf, "()");
7011 0 : return;
7012 :
7013 8 : case GROUPING_SET_SIMPLE:
7014 : {
7015 8 : if (!omit_parens || list_length(gset->content) != 1)
7016 8 : appendStringInfoChar(buf, '(');
7017 :
7018 28 : foreach(l, gset->content)
7019 : {
7020 20 : Index ref = lfirst_int(l);
7021 :
7022 20 : appendStringInfoString(buf, sep);
7023 20 : get_rule_sortgroupclause(ref, targetlist,
7024 : false, context);
7025 20 : sep = ", ";
7026 : }
7027 :
7028 8 : if (!omit_parens || list_length(gset->content) != 1)
7029 8 : appendStringInfoChar(buf, ')');
7030 : }
7031 8 : return;
7032 :
7033 4 : case GROUPING_SET_ROLLUP:
7034 4 : appendStringInfoString(buf, "ROLLUP(");
7035 4 : break;
7036 0 : case GROUPING_SET_CUBE:
7037 0 : appendStringInfoString(buf, "CUBE(");
7038 0 : break;
7039 0 : case GROUPING_SET_SETS:
7040 0 : appendStringInfoString(buf, "GROUPING SETS (");
7041 0 : omit_child_parens = false;
7042 0 : break;
7043 : }
7044 :
7045 12 : foreach(l, gset->content)
7046 : {
7047 8 : appendStringInfoString(buf, sep);
7048 8 : get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
7049 8 : sep = ", ";
7050 : }
7051 :
7052 4 : appendStringInfoChar(buf, ')');
7053 : }
7054 :
7055 : /*
7056 : * Display an ORDER BY list.
7057 : */
7058 : static void
7059 227 : get_rule_orderby(List *orderList, List *targetList,
7060 : bool force_colno, deparse_context *context)
7061 : {
7062 227 : StringInfo buf = context->buf;
7063 : const char *sep;
7064 : ListCell *l;
7065 :
7066 227 : sep = "";
7067 484 : foreach(l, orderList)
7068 : {
7069 257 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
7070 : Node *sortexpr;
7071 : Oid sortcoltype;
7072 : TypeCacheEntry *typentry;
7073 :
7074 257 : appendStringInfoString(buf, sep);
7075 257 : sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
7076 : force_colno, context);
7077 257 : sortcoltype = exprType(sortexpr);
7078 : /* See whether operator is default < or > for datatype */
7079 257 : typentry = lookup_type_cache(sortcoltype,
7080 : TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
7081 257 : if (srt->sortop == typentry->lt_opr)
7082 : {
7083 : /* ASC is default, so emit nothing for it */
7084 240 : if (srt->nulls_first)
7085 0 : appendStringInfoString(buf, " NULLS FIRST");
7086 : }
7087 17 : else if (srt->sortop == typentry->gt_opr)
7088 : {
7089 6 : appendStringInfoString(buf, " DESC");
7090 : /* DESC defaults to NULLS FIRST */
7091 6 : if (!srt->nulls_first)
7092 1 : appendStringInfoString(buf, " NULLS LAST");
7093 : }
7094 : else
7095 : {
7096 11 : appendStringInfo(buf, " USING %s",
7097 : generate_operator_name(srt->sortop,
7098 : sortcoltype,
7099 : sortcoltype));
7100 : /* be specific to eliminate ambiguity */
7101 11 : if (srt->nulls_first)
7102 0 : appendStringInfoString(buf, " NULLS FIRST");
7103 : else
7104 11 : appendStringInfoString(buf, " NULLS LAST");
7105 : }
7106 257 : sep = ", ";
7107 : }
7108 227 : }
7109 :
7110 : /*
7111 : * Display a WINDOW clause.
7112 : *
7113 : * Note that the windowClause list might contain only anonymous window
7114 : * specifications, in which case we should print nothing here.
7115 : */
7116 : static void
7117 32 : get_rule_windowclause(Query *query, deparse_context *context)
7118 : {
7119 32 : StringInfo buf = context->buf;
7120 : const char *sep;
7121 : ListCell *l;
7122 :
7123 32 : sep = NULL;
7124 64 : foreach(l, query->windowClause)
7125 : {
7126 32 : WindowClause *wc = (WindowClause *) lfirst(l);
7127 :
7128 32 : if (wc->name == NULL)
7129 28 : continue; /* ignore anonymous windows */
7130 :
7131 4 : if (sep == NULL)
7132 4 : appendContextKeyword(context, " WINDOW ",
7133 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7134 : else
7135 0 : appendStringInfoString(buf, sep);
7136 :
7137 4 : appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
7138 :
7139 4 : get_rule_windowspec(wc, query->targetList, context);
7140 :
7141 4 : sep = ", ";
7142 : }
7143 32 : }
7144 :
7145 : /*
7146 : * Display a window definition
7147 : */
7148 : static void
7149 32 : get_rule_windowspec(WindowClause *wc, List *targetList,
7150 : deparse_context *context)
7151 : {
7152 32 : StringInfo buf = context->buf;
7153 32 : bool needspace = false;
7154 : const char *sep;
7155 : ListCell *l;
7156 :
7157 32 : appendStringInfoChar(buf, '(');
7158 32 : if (wc->refname)
7159 : {
7160 0 : appendStringInfoString(buf, quote_identifier(wc->refname));
7161 0 : needspace = true;
7162 : }
7163 : /* partition clauses are always inherited, so only print if no refname */
7164 32 : if (wc->partitionClause && !wc->refname)
7165 : {
7166 0 : if (needspace)
7167 0 : appendStringInfoChar(buf, ' ');
7168 0 : appendStringInfoString(buf, "PARTITION BY ");
7169 0 : sep = "";
7170 0 : foreach(l, wc->partitionClause)
7171 : {
7172 0 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
7173 :
7174 0 : appendStringInfoString(buf, sep);
7175 0 : get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
7176 : false, context);
7177 0 : sep = ", ";
7178 : }
7179 0 : needspace = true;
7180 : }
7181 : /* print ordering clause only if not inherited */
7182 32 : if (wc->orderClause && !wc->copiedOrder)
7183 : {
7184 32 : if (needspace)
7185 0 : appendStringInfoChar(buf, ' ');
7186 32 : appendStringInfoString(buf, "ORDER BY ");
7187 32 : get_rule_orderby(wc->orderClause, targetList, false, context);
7188 32 : needspace = true;
7189 : }
7190 : /* framing clause is never inherited, so print unless it's default */
7191 32 : if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
7192 : {
7193 28 : if (needspace)
7194 28 : appendStringInfoChar(buf, ' ');
7195 28 : get_window_frame_options(wc->frameOptions,
7196 : wc->startOffset, wc->endOffset,
7197 : context);
7198 : }
7199 32 : appendStringInfoChar(buf, ')');
7200 32 : }
7201 :
7202 : /*
7203 : * Append the description of a window's framing options to context->buf
7204 : */
7205 : static void
7206 158 : get_window_frame_options(int frameOptions,
7207 : Node *startOffset, Node *endOffset,
7208 : deparse_context *context)
7209 : {
7210 158 : StringInfo buf = context->buf;
7211 :
7212 158 : if (frameOptions & FRAMEOPTION_NONDEFAULT)
7213 : {
7214 158 : if (frameOptions & FRAMEOPTION_RANGE)
7215 13 : appendStringInfoString(buf, "RANGE ");
7216 145 : else if (frameOptions & FRAMEOPTION_ROWS)
7217 137 : appendStringInfoString(buf, "ROWS ");
7218 8 : else if (frameOptions & FRAMEOPTION_GROUPS)
7219 8 : appendStringInfoString(buf, "GROUPS ");
7220 : else
7221 : Assert(false);
7222 158 : if (frameOptions & FRAMEOPTION_BETWEEN)
7223 61 : appendStringInfoString(buf, "BETWEEN ");
7224 158 : if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
7225 101 : appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
7226 57 : else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
7227 17 : appendStringInfoString(buf, "CURRENT ROW ");
7228 40 : else if (frameOptions & FRAMEOPTION_START_OFFSET)
7229 : {
7230 40 : get_rule_expr(startOffset, context, false);
7231 40 : if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
7232 40 : appendStringInfoString(buf, " PRECEDING ");
7233 0 : else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
7234 0 : appendStringInfoString(buf, " FOLLOWING ");
7235 : else
7236 : Assert(false);
7237 : }
7238 : else
7239 : Assert(false);
7240 158 : if (frameOptions & FRAMEOPTION_BETWEEN)
7241 : {
7242 61 : appendStringInfoString(buf, "AND ");
7243 61 : if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
7244 13 : appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
7245 48 : else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
7246 4 : appendStringInfoString(buf, "CURRENT ROW ");
7247 44 : else if (frameOptions & FRAMEOPTION_END_OFFSET)
7248 : {
7249 44 : get_rule_expr(endOffset, context, false);
7250 44 : if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
7251 0 : appendStringInfoString(buf, " PRECEDING ");
7252 44 : else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
7253 44 : appendStringInfoString(buf, " FOLLOWING ");
7254 : else
7255 : Assert(false);
7256 : }
7257 : else
7258 : Assert(false);
7259 : }
7260 158 : if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
7261 4 : appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
7262 154 : else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
7263 4 : appendStringInfoString(buf, "EXCLUDE GROUP ");
7264 150 : else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
7265 4 : appendStringInfoString(buf, "EXCLUDE TIES ");
7266 : /* we will now have a trailing space; remove it */
7267 158 : buf->data[--(buf->len)] = '\0';
7268 : }
7269 158 : }
7270 :
7271 : /*
7272 : * Return the description of a window's framing options as a palloc'd string
7273 : */
7274 : char *
7275 130 : get_window_frame_options_for_explain(int frameOptions,
7276 : Node *startOffset, Node *endOffset,
7277 : List *dpcontext, bool forceprefix)
7278 : {
7279 : StringInfoData buf;
7280 : deparse_context context;
7281 :
7282 130 : initStringInfo(&buf);
7283 130 : context.buf = &buf;
7284 130 : context.namespaces = dpcontext;
7285 130 : context.resultDesc = NULL;
7286 130 : context.targetList = NIL;
7287 130 : context.windowClause = NIL;
7288 130 : context.varprefix = forceprefix;
7289 130 : context.prettyFlags = 0;
7290 130 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
7291 130 : context.indentLevel = 0;
7292 130 : context.colNamesVisible = true;
7293 130 : context.inGroupBy = false;
7294 130 : context.varInOrderBy = false;
7295 130 : context.appendparents = NULL;
7296 :
7297 130 : get_window_frame_options(frameOptions, startOffset, endOffset, &context);
7298 :
7299 130 : return buf.data;
7300 : }
7301 :
7302 : /* ----------
7303 : * get_insert_query_def - Parse back an INSERT parsetree
7304 : * ----------
7305 : */
7306 : static void
7307 194 : get_insert_query_def(Query *query, deparse_context *context)
7308 : {
7309 194 : StringInfo buf = context->buf;
7310 194 : RangeTblEntry *select_rte = NULL;
7311 194 : RangeTblEntry *values_rte = NULL;
7312 : RangeTblEntry *rte;
7313 : char *sep;
7314 : ListCell *l;
7315 : List *strippedexprs;
7316 :
7317 : /* Insert the WITH clause if given */
7318 194 : get_with_clause(query, context);
7319 :
7320 : /*
7321 : * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
7322 : * single RTE for the SELECT or VALUES. Plain VALUES has neither.
7323 : */
7324 758 : foreach(l, query->rtable)
7325 : {
7326 564 : rte = (RangeTblEntry *) lfirst(l);
7327 :
7328 564 : if (rte->rtekind == RTE_SUBQUERY)
7329 : {
7330 30 : if (select_rte)
7331 0 : elog(ERROR, "too many subquery RTEs in INSERT");
7332 30 : select_rte = rte;
7333 : }
7334 :
7335 564 : if (rte->rtekind == RTE_VALUES)
7336 : {
7337 26 : if (values_rte)
7338 0 : elog(ERROR, "too many values RTEs in INSERT");
7339 26 : values_rte = rte;
7340 : }
7341 : }
7342 194 : if (select_rte && values_rte)
7343 0 : elog(ERROR, "both subquery and values RTEs in INSERT");
7344 :
7345 : /*
7346 : * Start the query with INSERT INTO relname
7347 : */
7348 194 : rte = rt_fetch(query->resultRelation, query->rtable);
7349 : Assert(rte->rtekind == RTE_RELATION);
7350 :
7351 194 : if (PRETTY_INDENT(context))
7352 : {
7353 194 : context->indentLevel += PRETTYINDENT_STD;
7354 194 : appendStringInfoChar(buf, ' ');
7355 : }
7356 194 : appendStringInfo(buf, "INSERT INTO %s",
7357 : generate_relation_name(rte->relid, NIL));
7358 :
7359 : /* Print the relation alias, if needed; INSERT requires explicit AS */
7360 194 : get_rte_alias(rte, query->resultRelation, true, context);
7361 :
7362 : /* always want a space here */
7363 194 : appendStringInfoChar(buf, ' ');
7364 :
7365 : /*
7366 : * Add the insert-column-names list. Any indirection decoration needed on
7367 : * the column names can be inferred from the top targetlist.
7368 : */
7369 194 : strippedexprs = NIL;
7370 194 : sep = "";
7371 194 : if (query->targetList)
7372 194 : appendStringInfoChar(buf, '(');
7373 695 : foreach(l, query->targetList)
7374 : {
7375 501 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7376 :
7377 501 : if (tle->resjunk)
7378 0 : continue; /* ignore junk entries */
7379 :
7380 501 : appendStringInfoString(buf, sep);
7381 501 : sep = ", ";
7382 :
7383 : /*
7384 : * Put out name of target column; look in the catalogs, not at
7385 : * tle->resname, since resname will fail to track RENAME.
7386 : */
7387 501 : appendStringInfoString(buf,
7388 501 : quote_identifier(get_attname(rte->relid,
7389 501 : tle->resno,
7390 : false)));
7391 :
7392 : /*
7393 : * Print any indirection needed (subfields or subscripts), and strip
7394 : * off the top-level nodes representing the indirection assignments.
7395 : * Add the stripped expressions to strippedexprs. (If it's a
7396 : * single-VALUES statement, the stripped expressions are the VALUES to
7397 : * print below. Otherwise they're just Vars and not really
7398 : * interesting.)
7399 : */
7400 501 : strippedexprs = lappend(strippedexprs,
7401 501 : processIndirection((Node *) tle->expr,
7402 : context));
7403 : }
7404 194 : if (query->targetList)
7405 194 : appendStringInfoString(buf, ") ");
7406 :
7407 194 : if (query->override)
7408 : {
7409 0 : if (query->override == OVERRIDING_SYSTEM_VALUE)
7410 0 : appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
7411 0 : else if (query->override == OVERRIDING_USER_VALUE)
7412 0 : appendStringInfoString(buf, "OVERRIDING USER VALUE ");
7413 : }
7414 :
7415 194 : if (select_rte)
7416 : {
7417 : /* Add the SELECT */
7418 30 : get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
7419 : false,
7420 : context->prettyFlags, context->wrapColumn,
7421 : context->indentLevel);
7422 : }
7423 164 : else if (values_rte)
7424 : {
7425 : /* Add the multi-VALUES expression lists */
7426 26 : get_values_def(values_rte->values_lists, context);
7427 : }
7428 138 : else if (strippedexprs)
7429 : {
7430 : /* Add the single-VALUES expression list */
7431 138 : appendContextKeyword(context, "VALUES (",
7432 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7433 138 : get_rule_list_toplevel(strippedexprs, context, false);
7434 138 : appendStringInfoChar(buf, ')');
7435 : }
7436 : else
7437 : {
7438 : /* No expressions, so it must be DEFAULT VALUES */
7439 0 : appendStringInfoString(buf, "DEFAULT VALUES");
7440 : }
7441 :
7442 : /* Add ON CONFLICT if present */
7443 194 : if (query->onConflict)
7444 : {
7445 24 : OnConflictExpr *confl = query->onConflict;
7446 :
7447 24 : appendStringInfoString(buf, " ON CONFLICT");
7448 :
7449 24 : if (confl->arbiterElems)
7450 : {
7451 : /* Add the single-VALUES expression list */
7452 20 : appendStringInfoChar(buf, '(');
7453 20 : get_rule_expr((Node *) confl->arbiterElems, context, false);
7454 20 : appendStringInfoChar(buf, ')');
7455 :
7456 : /* Add a WHERE clause (for partial indexes) if given */
7457 20 : if (confl->arbiterWhere != NULL)
7458 : {
7459 : bool save_varprefix;
7460 :
7461 : /*
7462 : * Force non-prefixing of Vars, since parser assumes that they
7463 : * belong to target relation. WHERE clause does not use
7464 : * InferenceElem, so this is separately required.
7465 : */
7466 8 : save_varprefix = context->varprefix;
7467 8 : context->varprefix = false;
7468 :
7469 8 : appendContextKeyword(context, " WHERE ",
7470 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7471 8 : get_rule_expr(confl->arbiterWhere, context, false);
7472 :
7473 8 : context->varprefix = save_varprefix;
7474 : }
7475 : }
7476 4 : else if (OidIsValid(confl->constraint))
7477 : {
7478 0 : char *constraint = get_constraint_name(confl->constraint);
7479 :
7480 0 : if (!constraint)
7481 0 : elog(ERROR, "cache lookup failed for constraint %u",
7482 : confl->constraint);
7483 0 : appendStringInfo(buf, " ON CONSTRAINT %s",
7484 : quote_identifier(constraint));
7485 : }
7486 :
7487 24 : if (confl->action == ONCONFLICT_NOTHING)
7488 : {
7489 12 : appendStringInfoString(buf, " DO NOTHING");
7490 : }
7491 12 : else if (confl->action == ONCONFLICT_UPDATE)
7492 : {
7493 8 : appendStringInfoString(buf, " DO UPDATE SET ");
7494 : /* Deparse targetlist */
7495 8 : get_update_query_targetlist_def(query, confl->onConflictSet,
7496 : context, rte);
7497 :
7498 : /* Add a WHERE clause if given */
7499 8 : if (confl->onConflictWhere != NULL)
7500 : {
7501 8 : appendContextKeyword(context, " WHERE ",
7502 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7503 8 : get_rule_expr(confl->onConflictWhere, context, false);
7504 : }
7505 : }
7506 : else
7507 : {
7508 : Assert(confl->action == ONCONFLICT_SELECT);
7509 4 : appendStringInfoString(buf, " DO SELECT");
7510 :
7511 : /* Add FOR [KEY] UPDATE/SHARE clause if present */
7512 4 : if (confl->lockStrength != LCS_NONE)
7513 4 : appendStringInfoString(buf, get_lock_clause_strength(confl->lockStrength));
7514 :
7515 : /* Add a WHERE clause if given */
7516 4 : if (confl->onConflictWhere != NULL)
7517 : {
7518 4 : appendContextKeyword(context, " WHERE ",
7519 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7520 4 : get_rule_expr(confl->onConflictWhere, context, false);
7521 : }
7522 : }
7523 : }
7524 :
7525 : /* Add RETURNING if present */
7526 194 : if (query->returningList)
7527 51 : get_returning_clause(query, context);
7528 194 : }
7529 :
7530 :
7531 : /* ----------
7532 : * get_update_query_def - Parse back an UPDATE parsetree
7533 : * ----------
7534 : */
7535 : static void
7536 94 : get_update_query_def(Query *query, deparse_context *context)
7537 : {
7538 94 : StringInfo buf = context->buf;
7539 : RangeTblEntry *rte;
7540 :
7541 : /* Insert the WITH clause if given */
7542 94 : get_with_clause(query, context);
7543 :
7544 : /*
7545 : * Start the query with UPDATE relname SET
7546 : */
7547 94 : rte = rt_fetch(query->resultRelation, query->rtable);
7548 : Assert(rte->rtekind == RTE_RELATION);
7549 94 : if (PRETTY_INDENT(context))
7550 : {
7551 94 : appendStringInfoChar(buf, ' ');
7552 94 : context->indentLevel += PRETTYINDENT_STD;
7553 : }
7554 188 : appendStringInfo(buf, "UPDATE %s%s",
7555 94 : only_marker(rte),
7556 : generate_relation_name(rte->relid, NIL));
7557 :
7558 : /* Print the FOR PORTION OF, if needed */
7559 94 : get_for_portion_of(query->forPortionOf, context);
7560 :
7561 : /* Print the relation alias, if needed */
7562 94 : get_rte_alias(rte, query->resultRelation, false, context);
7563 :
7564 94 : appendStringInfoString(buf, " SET ");
7565 :
7566 : /* Deparse targetlist */
7567 94 : get_update_query_targetlist_def(query, query->targetList, context, rte);
7568 :
7569 : /* Add the FROM clause if needed */
7570 94 : get_from_clause(query, " FROM ", context);
7571 :
7572 : /* Add a WHERE clause if given */
7573 94 : if (query->jointree->quals != NULL)
7574 : {
7575 61 : appendContextKeyword(context, " WHERE ",
7576 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7577 61 : get_rule_expr(query->jointree->quals, context, false);
7578 : }
7579 :
7580 : /* Add RETURNING if present */
7581 94 : if (query->returningList)
7582 37 : get_returning_clause(query, context);
7583 94 : }
7584 :
7585 :
7586 : /* ----------
7587 : * get_update_query_targetlist_def - Parse back an UPDATE targetlist
7588 : * ----------
7589 : */
7590 : static void
7591 118 : get_update_query_targetlist_def(Query *query, List *targetList,
7592 : deparse_context *context, RangeTblEntry *rte)
7593 : {
7594 118 : StringInfo buf = context->buf;
7595 : ListCell *l;
7596 : ListCell *next_ma_cell;
7597 : int remaining_ma_columns;
7598 : const char *sep;
7599 : SubLink *cur_ma_sublink;
7600 : List *ma_sublinks;
7601 :
7602 : /*
7603 : * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
7604 : * into a list. We expect them to appear, in ID order, in resjunk tlist
7605 : * entries.
7606 : */
7607 118 : ma_sublinks = NIL;
7608 118 : if (query->hasSubLinks) /* else there can't be any */
7609 : {
7610 28 : foreach(l, targetList)
7611 : {
7612 20 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7613 :
7614 20 : if (tle->resjunk && IsA(tle->expr, SubLink))
7615 : {
7616 4 : SubLink *sl = (SubLink *) tle->expr;
7617 :
7618 4 : if (sl->subLinkType == MULTIEXPR_SUBLINK)
7619 : {
7620 4 : ma_sublinks = lappend(ma_sublinks, sl);
7621 : Assert(sl->subLinkId == list_length(ma_sublinks));
7622 : }
7623 : }
7624 : }
7625 : }
7626 118 : next_ma_cell = list_head(ma_sublinks);
7627 118 : cur_ma_sublink = NULL;
7628 118 : remaining_ma_columns = 0;
7629 :
7630 : /* Add the comma separated list of 'attname = value' */
7631 118 : sep = "";
7632 298 : foreach(l, targetList)
7633 : {
7634 180 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7635 : Node *expr;
7636 :
7637 180 : if (tle->resjunk)
7638 4 : continue; /* ignore junk entries */
7639 :
7640 : /* Emit separator (OK whether we're in multiassignment or not) */
7641 176 : appendStringInfoString(buf, sep);
7642 176 : sep = ", ";
7643 :
7644 : /*
7645 : * Check to see if we're starting a multiassignment group: if so,
7646 : * output a left paren.
7647 : */
7648 176 : if (next_ma_cell != NULL && cur_ma_sublink == NULL)
7649 : {
7650 : /*
7651 : * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
7652 : * Param. That could be buried under FieldStores and
7653 : * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
7654 : * and underneath those there could be an implicit type coercion.
7655 : * Because we would ignore implicit type coercions anyway, we
7656 : * don't need to be as careful as processIndirection() is about
7657 : * descending past implicit CoerceToDomains.
7658 : */
7659 4 : expr = (Node *) tle->expr;
7660 8 : while (expr)
7661 : {
7662 8 : if (IsA(expr, FieldStore))
7663 : {
7664 0 : FieldStore *fstore = (FieldStore *) expr;
7665 :
7666 0 : expr = (Node *) linitial(fstore->newvals);
7667 : }
7668 8 : else if (IsA(expr, SubscriptingRef))
7669 : {
7670 4 : SubscriptingRef *sbsref = (SubscriptingRef *) expr;
7671 :
7672 4 : if (sbsref->refassgnexpr == NULL)
7673 0 : break;
7674 :
7675 4 : expr = (Node *) sbsref->refassgnexpr;
7676 : }
7677 4 : else if (IsA(expr, CoerceToDomain))
7678 : {
7679 0 : CoerceToDomain *cdomain = (CoerceToDomain *) expr;
7680 :
7681 0 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
7682 0 : break;
7683 0 : expr = (Node *) cdomain->arg;
7684 : }
7685 : else
7686 4 : break;
7687 : }
7688 4 : expr = strip_implicit_coercions(expr);
7689 :
7690 4 : if (expr && IsA(expr, Param) &&
7691 4 : ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
7692 : {
7693 4 : cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
7694 4 : next_ma_cell = lnext(ma_sublinks, next_ma_cell);
7695 4 : remaining_ma_columns = count_nonjunk_tlist_entries(((Query *) cur_ma_sublink->subselect)->targetList);
7696 : Assert(((Param *) expr)->paramid ==
7697 : ((cur_ma_sublink->subLinkId << 16) | 1));
7698 4 : appendStringInfoChar(buf, '(');
7699 : }
7700 : }
7701 :
7702 : /*
7703 : * Put out name of target column; look in the catalogs, not at
7704 : * tle->resname, since resname will fail to track RENAME.
7705 : */
7706 176 : appendStringInfoString(buf,
7707 176 : quote_identifier(get_attname(rte->relid,
7708 176 : tle->resno,
7709 : false)));
7710 :
7711 : /*
7712 : * Print any indirection needed (subfields or subscripts), and strip
7713 : * off the top-level nodes representing the indirection assignments.
7714 : */
7715 176 : expr = processIndirection((Node *) tle->expr, context);
7716 :
7717 : /*
7718 : * If we're in a multiassignment, skip printing anything more, unless
7719 : * this is the last column; in which case, what we print should be the
7720 : * sublink, not the Param.
7721 : */
7722 176 : if (cur_ma_sublink != NULL)
7723 : {
7724 12 : if (--remaining_ma_columns > 0)
7725 8 : continue; /* not the last column of multiassignment */
7726 4 : appendStringInfoChar(buf, ')');
7727 4 : expr = (Node *) cur_ma_sublink;
7728 4 : cur_ma_sublink = NULL;
7729 : }
7730 :
7731 168 : appendStringInfoString(buf, " = ");
7732 :
7733 168 : get_rule_expr(expr, context, false);
7734 : }
7735 118 : }
7736 :
7737 :
7738 : /* ----------
7739 : * get_delete_query_def - Parse back a DELETE parsetree
7740 : * ----------
7741 : */
7742 : static void
7743 47 : get_delete_query_def(Query *query, deparse_context *context)
7744 : {
7745 47 : StringInfo buf = context->buf;
7746 : RangeTblEntry *rte;
7747 :
7748 : /* Insert the WITH clause if given */
7749 47 : get_with_clause(query, context);
7750 :
7751 : /*
7752 : * Start the query with DELETE FROM relname
7753 : */
7754 47 : rte = rt_fetch(query->resultRelation, query->rtable);
7755 : Assert(rte->rtekind == RTE_RELATION);
7756 47 : if (PRETTY_INDENT(context))
7757 : {
7758 47 : appendStringInfoChar(buf, ' ');
7759 47 : context->indentLevel += PRETTYINDENT_STD;
7760 : }
7761 94 : appendStringInfo(buf, "DELETE FROM %s%s",
7762 47 : only_marker(rte),
7763 : generate_relation_name(rte->relid, NIL));
7764 :
7765 : /* Print the FOR PORTION OF, if needed */
7766 47 : get_for_portion_of(query->forPortionOf, context);
7767 :
7768 : /* Print the relation alias, if needed */
7769 47 : get_rte_alias(rte, query->resultRelation, false, context);
7770 :
7771 : /* Add the USING clause if given */
7772 47 : get_from_clause(query, " USING ", context);
7773 :
7774 : /* Add a WHERE clause if given */
7775 47 : if (query->jointree->quals != NULL)
7776 : {
7777 39 : appendContextKeyword(context, " WHERE ",
7778 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7779 39 : get_rule_expr(query->jointree->quals, context, false);
7780 : }
7781 :
7782 : /* Add RETURNING if present */
7783 47 : if (query->returningList)
7784 17 : get_returning_clause(query, context);
7785 47 : }
7786 :
7787 :
7788 : /* ----------
7789 : * get_merge_query_def - Parse back a MERGE parsetree
7790 : * ----------
7791 : */
7792 : static void
7793 8 : get_merge_query_def(Query *query, deparse_context *context)
7794 : {
7795 8 : StringInfo buf = context->buf;
7796 : RangeTblEntry *rte;
7797 : ListCell *lc;
7798 : bool haveNotMatchedBySource;
7799 :
7800 : /* Insert the WITH clause if given */
7801 8 : get_with_clause(query, context);
7802 :
7803 : /*
7804 : * Start the query with MERGE INTO relname
7805 : */
7806 8 : rte = rt_fetch(query->resultRelation, query->rtable);
7807 : Assert(rte->rtekind == RTE_RELATION);
7808 8 : if (PRETTY_INDENT(context))
7809 : {
7810 8 : appendStringInfoChar(buf, ' ');
7811 8 : context->indentLevel += PRETTYINDENT_STD;
7812 : }
7813 16 : appendStringInfo(buf, "MERGE INTO %s%s",
7814 8 : only_marker(rte),
7815 : generate_relation_name(rte->relid, NIL));
7816 :
7817 : /* Print the relation alias, if needed */
7818 8 : get_rte_alias(rte, query->resultRelation, false, context);
7819 :
7820 : /* Print the source relation and join clause */
7821 8 : get_from_clause(query, " USING ", context);
7822 8 : appendContextKeyword(context, " ON ",
7823 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7824 8 : get_rule_expr(query->mergeJoinCondition, context, false);
7825 :
7826 : /*
7827 : * Test for any NOT MATCHED BY SOURCE actions. If there are none, then
7828 : * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
7829 : * SQL standard. Otherwise, we have a non-SQL-standard query, so output
7830 : * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
7831 : * more explicit.
7832 : */
7833 8 : haveNotMatchedBySource = false;
7834 56 : foreach(lc, query->mergeActionList)
7835 : {
7836 52 : MergeAction *action = lfirst_node(MergeAction, lc);
7837 :
7838 52 : if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
7839 : {
7840 4 : haveNotMatchedBySource = true;
7841 4 : break;
7842 : }
7843 : }
7844 :
7845 : /* Print each merge action */
7846 60 : foreach(lc, query->mergeActionList)
7847 : {
7848 52 : MergeAction *action = lfirst_node(MergeAction, lc);
7849 :
7850 52 : appendContextKeyword(context, " WHEN ",
7851 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7852 52 : switch (action->matchKind)
7853 : {
7854 24 : case MERGE_WHEN_MATCHED:
7855 24 : appendStringInfoString(buf, "MATCHED");
7856 24 : break;
7857 4 : case MERGE_WHEN_NOT_MATCHED_BY_SOURCE:
7858 4 : appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
7859 4 : break;
7860 24 : case MERGE_WHEN_NOT_MATCHED_BY_TARGET:
7861 24 : if (haveNotMatchedBySource)
7862 4 : appendStringInfoString(buf, "NOT MATCHED BY TARGET");
7863 : else
7864 20 : appendStringInfoString(buf, "NOT MATCHED");
7865 24 : break;
7866 0 : default:
7867 0 : elog(ERROR, "unrecognized matchKind: %d",
7868 : (int) action->matchKind);
7869 : }
7870 :
7871 52 : if (action->qual)
7872 : {
7873 32 : appendContextKeyword(context, " AND ",
7874 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7875 32 : get_rule_expr(action->qual, context, false);
7876 : }
7877 52 : appendContextKeyword(context, " THEN ",
7878 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7879 :
7880 52 : if (action->commandType == CMD_INSERT)
7881 : {
7882 : /* This generally matches get_insert_query_def() */
7883 24 : List *strippedexprs = NIL;
7884 24 : const char *sep = "";
7885 : ListCell *lc2;
7886 :
7887 24 : appendStringInfoString(buf, "INSERT");
7888 :
7889 24 : if (action->targetList)
7890 20 : appendStringInfoString(buf, " (");
7891 68 : foreach(lc2, action->targetList)
7892 : {
7893 44 : TargetEntry *tle = (TargetEntry *) lfirst(lc2);
7894 :
7895 : Assert(!tle->resjunk);
7896 :
7897 44 : appendStringInfoString(buf, sep);
7898 44 : sep = ", ";
7899 :
7900 44 : appendStringInfoString(buf,
7901 44 : quote_identifier(get_attname(rte->relid,
7902 44 : tle->resno,
7903 : false)));
7904 44 : strippedexprs = lappend(strippedexprs,
7905 44 : processIndirection((Node *) tle->expr,
7906 : context));
7907 : }
7908 24 : if (action->targetList)
7909 20 : appendStringInfoChar(buf, ')');
7910 :
7911 24 : if (action->override)
7912 : {
7913 4 : if (action->override == OVERRIDING_SYSTEM_VALUE)
7914 0 : appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
7915 4 : else if (action->override == OVERRIDING_USER_VALUE)
7916 4 : appendStringInfoString(buf, " OVERRIDING USER VALUE");
7917 : }
7918 :
7919 24 : if (strippedexprs)
7920 : {
7921 20 : appendContextKeyword(context, " VALUES (",
7922 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);
7923 20 : get_rule_list_toplevel(strippedexprs, context, false);
7924 20 : appendStringInfoChar(buf, ')');
7925 : }
7926 : else
7927 4 : appendStringInfoString(buf, " DEFAULT VALUES");
7928 : }
7929 28 : else if (action->commandType == CMD_UPDATE)
7930 : {
7931 16 : appendStringInfoString(buf, "UPDATE SET ");
7932 16 : get_update_query_targetlist_def(query, action->targetList,
7933 : context, rte);
7934 : }
7935 12 : else if (action->commandType == CMD_DELETE)
7936 8 : appendStringInfoString(buf, "DELETE");
7937 4 : else if (action->commandType == CMD_NOTHING)
7938 4 : appendStringInfoString(buf, "DO NOTHING");
7939 : }
7940 :
7941 : /* Add RETURNING if present */
7942 8 : if (query->returningList)
7943 4 : get_returning_clause(query, context);
7944 8 : }
7945 :
7946 :
7947 : /* ----------
7948 : * get_utility_query_def - Parse back a UTILITY parsetree
7949 : * ----------
7950 : */
7951 : static void
7952 9 : get_utility_query_def(Query *query, deparse_context *context)
7953 : {
7954 9 : StringInfo buf = context->buf;
7955 :
7956 9 : if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7957 9 : {
7958 9 : NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7959 :
7960 9 : appendContextKeyword(context, "",
7961 : 0, PRETTYINDENT_STD, 1);
7962 9 : appendStringInfo(buf, "NOTIFY %s",
7963 9 : quote_identifier(stmt->conditionname));
7964 9 : if (stmt->payload)
7965 : {
7966 0 : appendStringInfoString(buf, ", ");
7967 0 : simple_quote_literal(buf, stmt->payload);
7968 : }
7969 : }
7970 : else
7971 : {
7972 : /* Currently only NOTIFY utility commands can appear in rules */
7973 0 : elog(ERROR, "unexpected utility statement type");
7974 : }
7975 9 : }
7976 :
7977 :
7978 : /*
7979 : * Parse back a graph label expression
7980 : */
7981 : static void
7982 96 : get_graph_label_expr(Node *label_expr, deparse_context *context)
7983 : {
7984 96 : StringInfo buf = context->buf;
7985 :
7986 96 : check_stack_depth();
7987 :
7988 96 : switch (nodeTag(label_expr))
7989 : {
7990 78 : case T_GraphLabelRef:
7991 : {
7992 78 : GraphLabelRef *lref = (GraphLabelRef *) label_expr;
7993 :
7994 78 : appendStringInfoString(buf, quote_identifier(get_propgraph_label_name(lref->labelid)));
7995 78 : break;
7996 : }
7997 :
7998 18 : case T_BoolExpr:
7999 : {
8000 18 : BoolExpr *be = (BoolExpr *) label_expr;
8001 : ListCell *lc;
8002 18 : bool first = true;
8003 :
8004 : Assert(be->boolop == OR_EXPR);
8005 :
8006 54 : foreach(lc, be->args)
8007 : {
8008 36 : if (!first)
8009 : {
8010 18 : if (be->boolop == OR_EXPR)
8011 18 : appendStringInfoString(buf, "|");
8012 : }
8013 : else
8014 18 : first = false;
8015 36 : get_graph_label_expr(lfirst(lc), context);
8016 : }
8017 :
8018 18 : break;
8019 : }
8020 :
8021 0 : default:
8022 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(label_expr));
8023 : break;
8024 : }
8025 96 : }
8026 :
8027 : /*
8028 : * Parse back a path pattern expression
8029 : */
8030 : static void
8031 14 : get_path_pattern_expr_def(List *path_pattern_expr, deparse_context *context)
8032 : {
8033 14 : StringInfo buf = context->buf;
8034 : ListCell *lc;
8035 :
8036 74 : foreach(lc, path_pattern_expr)
8037 : {
8038 60 : GraphElementPattern *gep = lfirst_node(GraphElementPattern, lc);
8039 60 : const char *sep = "";
8040 :
8041 60 : switch (gep->kind)
8042 : {
8043 37 : case VERTEX_PATTERN:
8044 37 : appendStringInfoString(buf, "(");
8045 37 : break;
8046 0 : case EDGE_PATTERN_LEFT:
8047 0 : appendStringInfoString(buf, "<-[");
8048 0 : break;
8049 23 : case EDGE_PATTERN_RIGHT:
8050 : case EDGE_PATTERN_ANY:
8051 23 : appendStringInfoString(buf, "-[");
8052 23 : break;
8053 0 : case PAREN_EXPR:
8054 0 : appendStringInfoString(buf, "(");
8055 0 : break;
8056 : }
8057 :
8058 60 : if (gep->variable)
8059 : {
8060 37 : appendStringInfoString(buf, quote_identifier(gep->variable));
8061 37 : sep = " ";
8062 : }
8063 :
8064 60 : if (gep->labelexpr)
8065 : {
8066 60 : appendStringInfoString(buf, sep);
8067 60 : appendStringInfoString(buf, "IS ");
8068 60 : get_graph_label_expr(gep->labelexpr, context);
8069 60 : sep = " ";
8070 : }
8071 :
8072 60 : if (gep->subexpr)
8073 : {
8074 0 : appendStringInfoString(buf, sep);
8075 0 : get_path_pattern_expr_def(gep->subexpr, context);
8076 0 : sep = " ";
8077 : }
8078 :
8079 60 : if (gep->whereClause)
8080 : {
8081 14 : appendStringInfoString(buf, sep);
8082 14 : appendStringInfoString(buf, "WHERE ");
8083 14 : get_rule_expr(gep->whereClause, context, false);
8084 : }
8085 :
8086 60 : switch (gep->kind)
8087 : {
8088 37 : case VERTEX_PATTERN:
8089 37 : appendStringInfoString(buf, ")");
8090 37 : break;
8091 0 : case EDGE_PATTERN_LEFT:
8092 : case EDGE_PATTERN_ANY:
8093 0 : appendStringInfoString(buf, "]-");
8094 0 : break;
8095 23 : case EDGE_PATTERN_RIGHT:
8096 23 : appendStringInfoString(buf, "]->");
8097 23 : break;
8098 0 : case PAREN_EXPR:
8099 0 : appendStringInfoString(buf, ")");
8100 0 : break;
8101 : }
8102 :
8103 60 : if (gep->quantifier)
8104 : {
8105 0 : int lower = linitial_int(gep->quantifier);
8106 0 : int upper = lsecond_int(gep->quantifier);
8107 :
8108 0 : appendStringInfo(buf, "{%d,%d}", lower, upper);
8109 : }
8110 : }
8111 14 : }
8112 :
8113 : /*
8114 : * Parse back a graph pattern
8115 : */
8116 : static void
8117 14 : get_graph_pattern_def(GraphPattern *graph_pattern, deparse_context *context)
8118 : {
8119 14 : StringInfo buf = context->buf;
8120 : ListCell *lc;
8121 14 : bool first = true;
8122 :
8123 28 : foreach(lc, graph_pattern->path_pattern_list)
8124 : {
8125 14 : List *path_pattern_expr = lfirst_node(List, lc);
8126 :
8127 14 : if (!first)
8128 0 : appendStringInfoString(buf, ", ");
8129 : else
8130 14 : first = false;
8131 :
8132 14 : get_path_pattern_expr_def(path_pattern_expr, context);
8133 : }
8134 :
8135 14 : if (graph_pattern->whereClause)
8136 : {
8137 0 : appendStringInfoString(buf, "WHERE ");
8138 0 : get_rule_expr(graph_pattern->whereClause, context, false);
8139 : }
8140 14 : }
8141 :
8142 : /*
8143 : * Display a Var appropriately.
8144 : *
8145 : * In some cases (currently only when recursing into an unnamed join)
8146 : * the Var's varlevelsup has to be interpreted with respect to a context
8147 : * above the current one; levelsup indicates the offset.
8148 : *
8149 : * If istoplevel is true, the Var is at the top level of a SELECT's
8150 : * targetlist, which means we need special treatment of whole-row Vars.
8151 : * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
8152 : * dirty hack to prevent "tab.*" from being expanded into multiple columns.
8153 : * (The parser will strip the useless coercion, so no inefficiency is added in
8154 : * dump and reload.) We used to print just "tab" in such cases, but that is
8155 : * ambiguous and will yield the wrong result if "tab" is also a plain column
8156 : * name in the query.
8157 : *
8158 : * Returns the attname of the Var, or NULL if the Var has no attname (because
8159 : * it is a whole-row Var or a subplan output reference).
8160 : */
8161 : static char *
8162 124352 : get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
8163 : {
8164 124352 : StringInfo buf = context->buf;
8165 : RangeTblEntry *rte;
8166 : AttrNumber attnum;
8167 : int netlevelsup;
8168 : deparse_namespace *dpns;
8169 : int varno;
8170 : AttrNumber varattno;
8171 : deparse_columns *colinfo;
8172 : char *refname;
8173 : char *attname;
8174 : bool need_prefix;
8175 :
8176 : /* Find appropriate nesting depth */
8177 124352 : netlevelsup = var->varlevelsup + levelsup;
8178 124352 : if (netlevelsup >= list_length(context->namespaces))
8179 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
8180 : var->varlevelsup, levelsup);
8181 124352 : dpns = (deparse_namespace *) list_nth(context->namespaces,
8182 : netlevelsup);
8183 :
8184 : /*
8185 : * If we have a syntactic referent for the Var, and we're working from a
8186 : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8187 : * on the semantic referent. (Forcing use of the semantic referent when
8188 : * printing plan trees is a design choice that's perhaps more motivated by
8189 : * backwards compatibility than anything else. But it does have the
8190 : * advantage of making plans more explicit.)
8191 : */
8192 124352 : if (var->varnosyn > 0 && dpns->plan == NULL)
8193 : {
8194 24543 : varno = var->varnosyn;
8195 24543 : varattno = var->varattnosyn;
8196 : }
8197 : else
8198 : {
8199 99809 : varno = var->varno;
8200 99809 : varattno = var->varattno;
8201 : }
8202 :
8203 : /*
8204 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
8205 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8206 : * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
8207 : * find the aliases previously assigned for this RTE.
8208 : */
8209 124352 : if (varno >= 1 && varno <= list_length(dpns->rtable))
8210 : {
8211 : /*
8212 : * We might have been asked to map child Vars to some parent relation.
8213 : */
8214 90543 : if (context->appendparents && dpns->appendrels)
8215 : {
8216 2514 : int pvarno = varno;
8217 2514 : AttrNumber pvarattno = varattno;
8218 2514 : AppendRelInfo *appinfo = dpns->appendrels[pvarno];
8219 2514 : bool found = false;
8220 :
8221 : /* Only map up to inheritance parents, not UNION ALL appendrels */
8222 5074 : while (appinfo &&
8223 2791 : rt_fetch(appinfo->parent_relid,
8224 2791 : dpns->rtable)->rtekind == RTE_RELATION)
8225 : {
8226 2560 : found = false;
8227 2560 : if (pvarattno > 0) /* system columns stay as-is */
8228 : {
8229 2408 : if (pvarattno > appinfo->num_child_cols)
8230 0 : break; /* safety check */
8231 2408 : pvarattno = appinfo->parent_colnos[pvarattno - 1];
8232 2408 : if (pvarattno == 0)
8233 0 : break; /* Var is local to child */
8234 : }
8235 :
8236 2560 : pvarno = appinfo->parent_relid;
8237 2560 : found = true;
8238 :
8239 : /* If the parent is itself a child, continue up. */
8240 : Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
8241 2560 : appinfo = dpns->appendrels[pvarno];
8242 : }
8243 :
8244 : /*
8245 : * If we found an ancestral rel, and that rel is included in
8246 : * appendparents, print that column not the original one.
8247 : */
8248 2514 : if (found && bms_is_member(pvarno, context->appendparents))
8249 : {
8250 2035 : varno = pvarno;
8251 2035 : varattno = pvarattno;
8252 : }
8253 : }
8254 :
8255 90543 : rte = rt_fetch(varno, dpns->rtable);
8256 :
8257 : /* might be returning old/new column value */
8258 90543 : if (var->varreturningtype == VAR_RETURNING_OLD)
8259 274 : refname = dpns->ret_old_alias;
8260 90269 : else if (var->varreturningtype == VAR_RETURNING_NEW)
8261 273 : refname = dpns->ret_new_alias;
8262 : else
8263 89996 : refname = (char *) list_nth(dpns->rtable_names, varno - 1);
8264 :
8265 90543 : colinfo = deparse_columns_fetch(varno, dpns);
8266 90543 : attnum = varattno;
8267 : }
8268 : else
8269 : {
8270 33809 : resolve_special_varno((Node *) var, context,
8271 : get_special_variable, NULL);
8272 33809 : return NULL;
8273 : }
8274 :
8275 : /*
8276 : * The planner will sometimes emit Vars referencing resjunk elements of a
8277 : * subquery's target list (this is currently only possible if it chooses
8278 : * to generate a "physical tlist" for a SubqueryScan or CteScan node).
8279 : * Although we prefer to print subquery-referencing Vars using the
8280 : * subquery's alias, that's not possible for resjunk items since they have
8281 : * no alias. So in that case, drill down to the subplan and print the
8282 : * contents of the referenced tlist item. This works because in a plan
8283 : * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
8284 : * we'll have set dpns->inner_plan to reference the child plan node.
8285 : */
8286 93542 : if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
8287 2999 : attnum > list_length(rte->eref->colnames) &&
8288 1 : dpns->inner_plan)
8289 : {
8290 : TargetEntry *tle;
8291 : deparse_namespace save_dpns;
8292 :
8293 1 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8294 1 : if (!tle)
8295 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
8296 : attnum, rte->eref->aliasname);
8297 :
8298 : Assert(netlevelsup == 0);
8299 1 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8300 :
8301 : /*
8302 : * Force parentheses because our caller probably assumed a Var is a
8303 : * simple expression.
8304 : */
8305 1 : if (!IsA(tle->expr, Var))
8306 0 : appendStringInfoChar(buf, '(');
8307 1 : get_rule_expr((Node *) tle->expr, context, true);
8308 1 : if (!IsA(tle->expr, Var))
8309 0 : appendStringInfoChar(buf, ')');
8310 :
8311 1 : pop_child_plan(dpns, &save_dpns);
8312 1 : return NULL;
8313 : }
8314 :
8315 : /*
8316 : * If it's an unnamed join, look at the expansion of the alias variable.
8317 : * If it's a simple reference to one of the input vars, then recursively
8318 : * print the name of that var instead. When it's not a simple reference,
8319 : * we have to just print the unqualified join column name. (This can only
8320 : * happen with "dangerous" merged columns in a JOIN USING; we took pains
8321 : * previously to make the unqualified column name unique in such cases.)
8322 : *
8323 : * This wouldn't work in decompiling plan trees, because we don't store
8324 : * joinaliasvars lists after planning; but a plan tree should never
8325 : * contain a join alias variable.
8326 : */
8327 90542 : if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
8328 : {
8329 72 : if (rte->joinaliasvars == NIL)
8330 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
8331 72 : if (attnum > 0)
8332 : {
8333 : Var *aliasvar;
8334 :
8335 72 : aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
8336 : /* we intentionally don't strip implicit coercions here */
8337 72 : if (aliasvar && IsA(aliasvar, Var))
8338 : {
8339 0 : return get_variable(aliasvar, var->varlevelsup + levelsup,
8340 : istoplevel, context);
8341 : }
8342 : }
8343 :
8344 : /*
8345 : * Unnamed join has no refname. (Note: since it's unnamed, there is
8346 : * no way the user could have referenced it to create a whole-row Var
8347 : * for it. So we don't have to cover that case below.)
8348 : */
8349 : Assert(refname == NULL);
8350 : }
8351 :
8352 90542 : if (attnum == InvalidAttrNumber)
8353 627 : attname = NULL;
8354 89915 : else if (attnum > 0)
8355 : {
8356 : /* Get column name to use from the colinfo struct */
8357 88695 : if (attnum > colinfo->num_cols)
8358 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
8359 : attnum, rte->eref->aliasname);
8360 88695 : attname = colinfo->colnames[attnum - 1];
8361 :
8362 : /*
8363 : * If we find a Var referencing a dropped column, it seems better to
8364 : * print something (anything) than to fail. In general this should
8365 : * not happen, but it used to be possible for some cases involving
8366 : * functions returning named composite types, and perhaps there are
8367 : * still bugs out there.
8368 : */
8369 88695 : if (attname == NULL)
8370 4 : attname = "?dropped?column?";
8371 : }
8372 : else
8373 : {
8374 : /* System column - name is fixed, get it from the catalog */
8375 1220 : attname = get_rte_attribute_name(rte, attnum);
8376 : }
8377 :
8378 133507 : need_prefix = (context->varprefix || attname == NULL ||
8379 42965 : var->varreturningtype != VAR_RETURNING_DEFAULT);
8380 :
8381 : /*
8382 : * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
8383 : * clause, we may need to add a table-name prefix to prevent
8384 : * findTargetlistEntrySQL92 from misinterpreting the name as an
8385 : * output-column name. To avoid cluttering the output with unnecessary
8386 : * prefixes, do so only if there is a name match to a SELECT tlist item
8387 : * that is different from the Var.
8388 : */
8389 90542 : if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
8390 : {
8391 173 : int colno = 0;
8392 :
8393 685 : foreach_node(TargetEntry, tle, context->targetList)
8394 : {
8395 : char *colname;
8396 :
8397 347 : if (tle->resjunk)
8398 0 : continue; /* ignore junk entries */
8399 347 : colno++;
8400 :
8401 : /* This must match colname-choosing logic in get_target_list() */
8402 347 : if (context->resultDesc && colno <= context->resultDesc->natts)
8403 347 : colname = NameStr(TupleDescAttr(context->resultDesc,
8404 : colno - 1)->attname);
8405 : else
8406 0 : colname = tle->resname;
8407 :
8408 347 : if (colname && strcmp(colname, attname) == 0 &&
8409 124 : !equal(var, tle->expr))
8410 : {
8411 8 : need_prefix = true;
8412 8 : break;
8413 : }
8414 : }
8415 : }
8416 :
8417 90542 : if (refname && need_prefix)
8418 : {
8419 47526 : appendStringInfoString(buf, quote_identifier(refname));
8420 47526 : appendStringInfoChar(buf, '.');
8421 : }
8422 90542 : if (attname)
8423 89915 : appendStringInfoString(buf, quote_identifier(attname));
8424 : else
8425 : {
8426 627 : appendStringInfoChar(buf, '*');
8427 627 : if (istoplevel)
8428 56 : appendStringInfo(buf, "::%s",
8429 : format_type_with_typemod(var->vartype,
8430 : var->vartypmod));
8431 : }
8432 :
8433 90542 : return attname;
8434 : }
8435 :
8436 : /*
8437 : * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
8438 : * routine is actually a callback for resolve_special_varno, which handles
8439 : * finding the correct TargetEntry. We get the expression contained in that
8440 : * TargetEntry and just need to deparse it, a job we can throw back on
8441 : * get_rule_expr.
8442 : */
8443 : static void
8444 33809 : get_special_variable(Node *node, deparse_context *context, void *callback_arg)
8445 : {
8446 33809 : StringInfo buf = context->buf;
8447 :
8448 : /*
8449 : * For a non-Var referent, force parentheses because our caller probably
8450 : * assumed a Var is a simple expression.
8451 : */
8452 33809 : if (!IsA(node, Var))
8453 3463 : appendStringInfoChar(buf, '(');
8454 33809 : get_rule_expr(node, context, true);
8455 33809 : if (!IsA(node, Var))
8456 3463 : appendStringInfoChar(buf, ')');
8457 33809 : }
8458 :
8459 : /*
8460 : * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
8461 : * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
8462 : * invoke the callback provided.
8463 : */
8464 : static void
8465 95298 : resolve_special_varno(Node *node, deparse_context *context,
8466 : rsv_callback callback, void *callback_arg)
8467 : {
8468 : Var *var;
8469 : deparse_namespace *dpns;
8470 :
8471 : /* This function is recursive, so let's be paranoid. */
8472 95298 : check_stack_depth();
8473 :
8474 : /* If it's not a Var, invoke the callback. */
8475 95298 : if (!IsA(node, Var))
8476 : {
8477 3983 : (*callback) (node, context, callback_arg);
8478 3983 : return;
8479 : }
8480 :
8481 : /* Find appropriate nesting depth */
8482 91315 : var = (Var *) node;
8483 91315 : dpns = (deparse_namespace *) list_nth(context->namespaces,
8484 91315 : var->varlevelsup);
8485 :
8486 : /*
8487 : * If varno is special, recurse. (Don't worry about varnosyn; if we're
8488 : * here, we already decided not to use that.)
8489 : */
8490 91315 : if (var->varno == OUTER_VAR && dpns->outer_tlist)
8491 : {
8492 : TargetEntry *tle;
8493 : deparse_namespace save_dpns;
8494 : Bitmapset *save_appendparents;
8495 :
8496 46277 : tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
8497 46277 : if (!tle)
8498 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
8499 :
8500 : /*
8501 : * If we're descending to the first child of an Append or MergeAppend,
8502 : * update appendparents. This will affect deparsing of all Vars
8503 : * appearing within the eventually-resolved subexpression.
8504 : */
8505 46277 : save_appendparents = context->appendparents;
8506 :
8507 46277 : if (IsA(dpns->plan, Append))
8508 2936 : context->appendparents = bms_union(context->appendparents,
8509 2936 : ((Append *) dpns->plan)->apprelids);
8510 43341 : else if (IsA(dpns->plan, MergeAppend))
8511 413 : context->appendparents = bms_union(context->appendparents,
8512 413 : ((MergeAppend *) dpns->plan)->apprelids);
8513 :
8514 46277 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8515 46277 : resolve_special_varno((Node *) tle->expr, context,
8516 : callback, callback_arg);
8517 46277 : pop_child_plan(dpns, &save_dpns);
8518 46277 : context->appendparents = save_appendparents;
8519 46277 : return;
8520 : }
8521 45038 : else if (var->varno == INNER_VAR && dpns->inner_tlist)
8522 : {
8523 : TargetEntry *tle;
8524 : deparse_namespace save_dpns;
8525 :
8526 11300 : tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
8527 11300 : if (!tle)
8528 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
8529 :
8530 11300 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8531 11300 : resolve_special_varno((Node *) tle->expr, context,
8532 : callback, callback_arg);
8533 11300 : pop_child_plan(dpns, &save_dpns);
8534 11300 : return;
8535 : }
8536 33738 : else if (var->varno == INDEX_VAR && dpns->index_tlist)
8537 : {
8538 : TargetEntry *tle;
8539 :
8540 3392 : tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
8541 3392 : if (!tle)
8542 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
8543 :
8544 3392 : resolve_special_varno((Node *) tle->expr, context,
8545 : callback, callback_arg);
8546 3392 : return;
8547 : }
8548 30346 : else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
8549 0 : elog(ERROR, "bogus varno: %d", var->varno);
8550 :
8551 : /* Not special. Just invoke the callback. */
8552 30346 : (*callback) (node, context, callback_arg);
8553 : }
8554 :
8555 : /*
8556 : * Get the name of a field of an expression of composite type. The
8557 : * expression is usually a Var, but we handle other cases too.
8558 : *
8559 : * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
8560 : *
8561 : * This is fairly straightforward when the expression has a named composite
8562 : * type; we need only look up the type in the catalogs. However, the type
8563 : * could also be RECORD. Since no actual table or view column is allowed to
8564 : * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
8565 : * or to a subquery output. We drill down to find the ultimate defining
8566 : * expression and attempt to infer the field name from it. We ereport if we
8567 : * can't determine the name.
8568 : *
8569 : * Similarly, a PARAM of type RECORD has to refer to some expression of
8570 : * a determinable composite type.
8571 : */
8572 : static const char *
8573 1050 : get_name_for_var_field(Var *var, int fieldno,
8574 : int levelsup, deparse_context *context)
8575 : {
8576 : RangeTblEntry *rte;
8577 : AttrNumber attnum;
8578 : int netlevelsup;
8579 : deparse_namespace *dpns;
8580 : int varno;
8581 : AttrNumber varattno;
8582 : TupleDesc tupleDesc;
8583 : Node *expr;
8584 :
8585 : /*
8586 : * If it's a RowExpr that was expanded from a whole-row Var, use the
8587 : * column names attached to it. (We could let get_expr_result_tupdesc()
8588 : * handle this, but it's much cheaper to just pull out the name we need.)
8589 : */
8590 1050 : if (IsA(var, RowExpr))
8591 : {
8592 24 : RowExpr *r = (RowExpr *) var;
8593 :
8594 24 : if (fieldno > 0 && fieldno <= list_length(r->colnames))
8595 24 : return strVal(list_nth(r->colnames, fieldno - 1));
8596 : }
8597 :
8598 : /*
8599 : * If it's a Param of type RECORD, try to find what the Param refers to.
8600 : */
8601 1026 : if (IsA(var, Param))
8602 : {
8603 12 : Param *param = (Param *) var;
8604 : ListCell *ancestor_cell;
8605 :
8606 12 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8607 12 : if (expr)
8608 : {
8609 : /* Found a match, so recurse to decipher the field name */
8610 : deparse_namespace save_dpns;
8611 : const char *result;
8612 :
8613 12 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
8614 12 : result = get_name_for_var_field((Var *) expr, fieldno,
8615 : 0, context);
8616 12 : pop_ancestor_plan(dpns, &save_dpns);
8617 12 : return result;
8618 : }
8619 : }
8620 :
8621 : /*
8622 : * If it's a Var of type RECORD, we have to find what the Var refers to;
8623 : * if not, we can use get_expr_result_tupdesc().
8624 : */
8625 1014 : if (!IsA(var, Var) ||
8626 961 : var->vartype != RECORDOID)
8627 : {
8628 850 : tupleDesc = get_expr_result_tupdesc((Node *) var, false);
8629 : /* Got the tupdesc, so we can extract the field name */
8630 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
8631 850 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
8632 : }
8633 :
8634 : /* Find appropriate nesting depth */
8635 164 : netlevelsup = var->varlevelsup + levelsup;
8636 164 : if (netlevelsup >= list_length(context->namespaces))
8637 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
8638 : var->varlevelsup, levelsup);
8639 164 : dpns = (deparse_namespace *) list_nth(context->namespaces,
8640 : netlevelsup);
8641 :
8642 : /*
8643 : * If we have a syntactic referent for the Var, and we're working from a
8644 : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8645 : * on the semantic referent. (See comments in get_variable().)
8646 : */
8647 164 : if (var->varnosyn > 0 && dpns->plan == NULL)
8648 : {
8649 64 : varno = var->varnosyn;
8650 64 : varattno = var->varattnosyn;
8651 : }
8652 : else
8653 : {
8654 100 : varno = var->varno;
8655 100 : varattno = var->varattno;
8656 : }
8657 :
8658 : /*
8659 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
8660 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8661 : * down into the subplans, or INDEX_VAR, which is resolved similarly.
8662 : *
8663 : * Note: unlike get_variable and resolve_special_varno, we need not worry
8664 : * about inheritance mapping: a child Var should have the same datatype as
8665 : * its parent, and here we're really only interested in the Var's type.
8666 : */
8667 164 : if (varno >= 1 && varno <= list_length(dpns->rtable))
8668 : {
8669 112 : rte = rt_fetch(varno, dpns->rtable);
8670 112 : attnum = varattno;
8671 : }
8672 52 : else if (varno == OUTER_VAR && dpns->outer_tlist)
8673 : {
8674 : TargetEntry *tle;
8675 : deparse_namespace save_dpns;
8676 : const char *result;
8677 :
8678 40 : tle = get_tle_by_resno(dpns->outer_tlist, varattno);
8679 40 : if (!tle)
8680 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
8681 :
8682 : Assert(netlevelsup == 0);
8683 40 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8684 :
8685 40 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8686 : levelsup, context);
8687 :
8688 40 : pop_child_plan(dpns, &save_dpns);
8689 40 : return result;
8690 : }
8691 12 : else if (varno == INNER_VAR && dpns->inner_tlist)
8692 : {
8693 : TargetEntry *tle;
8694 : deparse_namespace save_dpns;
8695 : const char *result;
8696 :
8697 12 : tle = get_tle_by_resno(dpns->inner_tlist, varattno);
8698 12 : if (!tle)
8699 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
8700 :
8701 : Assert(netlevelsup == 0);
8702 12 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8703 :
8704 12 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8705 : levelsup, context);
8706 :
8707 12 : pop_child_plan(dpns, &save_dpns);
8708 12 : return result;
8709 : }
8710 0 : else if (varno == INDEX_VAR && dpns->index_tlist)
8711 : {
8712 : TargetEntry *tle;
8713 : const char *result;
8714 :
8715 0 : tle = get_tle_by_resno(dpns->index_tlist, varattno);
8716 0 : if (!tle)
8717 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
8718 :
8719 : Assert(netlevelsup == 0);
8720 :
8721 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8722 : levelsup, context);
8723 :
8724 0 : return result;
8725 : }
8726 : else
8727 : {
8728 0 : elog(ERROR, "bogus varno: %d", varno);
8729 : return NULL; /* keep compiler quiet */
8730 : }
8731 :
8732 112 : if (attnum == InvalidAttrNumber)
8733 : {
8734 : /* Var is whole-row reference to RTE, so select the right field */
8735 16 : return get_rte_attribute_name(rte, fieldno);
8736 : }
8737 :
8738 : /*
8739 : * This part has essentially the same logic as the parser's
8740 : * expandRecordVariable() function, but we are dealing with a different
8741 : * representation of the input context, and we only need one field name
8742 : * not a TupleDesc. Also, we need special cases for finding subquery and
8743 : * CTE subplans when deparsing Plan trees.
8744 : */
8745 96 : expr = (Node *) var; /* default if we can't drill down */
8746 :
8747 96 : switch (rte->rtekind)
8748 : {
8749 0 : case RTE_RELATION:
8750 : case RTE_VALUES:
8751 : case RTE_NAMEDTUPLESTORE:
8752 : case RTE_GRAPH_TABLE:
8753 : case RTE_RESULT:
8754 :
8755 : /*
8756 : * This case should not occur: a column of a table, values list,
8757 : * or ENR shouldn't have type RECORD. Fall through and fail (most
8758 : * likely) at the bottom.
8759 : */
8760 0 : break;
8761 48 : case RTE_SUBQUERY:
8762 : /* Subselect-in-FROM: examine sub-select's output expr */
8763 : {
8764 48 : if (rte->subquery)
8765 : {
8766 28 : TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
8767 : attnum);
8768 :
8769 28 : if (ste == NULL || ste->resjunk)
8770 0 : elog(ERROR, "subquery %s does not have attribute %d",
8771 : rte->eref->aliasname, attnum);
8772 28 : expr = (Node *) ste->expr;
8773 28 : if (IsA(expr, Var))
8774 : {
8775 : /*
8776 : * Recurse into the sub-select to see what its Var
8777 : * refers to. We have to build an additional level of
8778 : * namespace to keep in step with varlevelsup in the
8779 : * subselect; furthermore, the subquery RTE might be
8780 : * from an outer query level, in which case the
8781 : * namespace for the subselect must have that outer
8782 : * level as parent namespace.
8783 : */
8784 12 : List *save_nslist = context->namespaces;
8785 : List *parent_namespaces;
8786 : deparse_namespace mydpns;
8787 : const char *result;
8788 :
8789 12 : parent_namespaces = list_copy_tail(context->namespaces,
8790 : netlevelsup);
8791 :
8792 12 : set_deparse_for_query(&mydpns, rte->subquery,
8793 : parent_namespaces);
8794 :
8795 12 : context->namespaces = lcons(&mydpns, parent_namespaces);
8796 :
8797 12 : result = get_name_for_var_field((Var *) expr, fieldno,
8798 : 0, context);
8799 :
8800 12 : context->namespaces = save_nslist;
8801 :
8802 12 : return result;
8803 : }
8804 : /* else fall through to inspect the expression */
8805 : }
8806 : else
8807 : {
8808 : /*
8809 : * We're deparsing a Plan tree so we don't have complete
8810 : * RTE entries (in particular, rte->subquery is NULL). But
8811 : * the only place we'd normally see a Var directly
8812 : * referencing a SUBQUERY RTE is in a SubqueryScan plan
8813 : * node, and we can look into the child plan's tlist
8814 : * instead. An exception occurs if the subquery was
8815 : * proven empty and optimized away: then we'd find such a
8816 : * Var in a childless Result node, and there's nothing in
8817 : * the plan tree that would let us figure out what it had
8818 : * originally referenced. In that case, fall back on
8819 : * printing "fN", analogously to the default column names
8820 : * for RowExprs.
8821 : */
8822 : TargetEntry *tle;
8823 : deparse_namespace save_dpns;
8824 : const char *result;
8825 :
8826 20 : if (!dpns->inner_plan)
8827 : {
8828 8 : char *dummy_name = palloc(32);
8829 :
8830 : Assert(dpns->plan && IsA(dpns->plan, Result));
8831 8 : snprintf(dummy_name, 32, "f%d", fieldno);
8832 8 : return dummy_name;
8833 : }
8834 : Assert(dpns->plan && IsA(dpns->plan, SubqueryScan));
8835 :
8836 12 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8837 12 : if (!tle)
8838 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8839 : attnum);
8840 : Assert(netlevelsup == 0);
8841 12 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8842 :
8843 12 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8844 : levelsup, context);
8845 :
8846 12 : pop_child_plan(dpns, &save_dpns);
8847 12 : return result;
8848 : }
8849 : }
8850 16 : break;
8851 0 : case RTE_JOIN:
8852 : /* Join RTE --- recursively inspect the alias variable */
8853 0 : if (rte->joinaliasvars == NIL)
8854 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
8855 : Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
8856 0 : expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
8857 : Assert(expr != NULL);
8858 : /* we intentionally don't strip implicit coercions here */
8859 0 : if (IsA(expr, Var))
8860 0 : return get_name_for_var_field((Var *) expr, fieldno,
8861 0 : var->varlevelsup + levelsup,
8862 : context);
8863 : /* else fall through to inspect the expression */
8864 0 : break;
8865 0 : case RTE_FUNCTION:
8866 : case RTE_TABLEFUNC:
8867 :
8868 : /*
8869 : * We couldn't get here unless a function is declared with one of
8870 : * its result columns as RECORD, which is not allowed.
8871 : */
8872 0 : break;
8873 48 : case RTE_CTE:
8874 : /* CTE reference: examine subquery's output expr */
8875 : {
8876 48 : CommonTableExpr *cte = NULL;
8877 : Index ctelevelsup;
8878 : ListCell *lc;
8879 :
8880 : /*
8881 : * Try to find the referenced CTE using the namespace stack.
8882 : */
8883 48 : ctelevelsup = rte->ctelevelsup + netlevelsup;
8884 48 : if (ctelevelsup >= list_length(context->namespaces))
8885 8 : lc = NULL;
8886 : else
8887 : {
8888 : deparse_namespace *ctedpns;
8889 :
8890 : ctedpns = (deparse_namespace *)
8891 40 : list_nth(context->namespaces, ctelevelsup);
8892 44 : foreach(lc, ctedpns->ctes)
8893 : {
8894 24 : cte = (CommonTableExpr *) lfirst(lc);
8895 24 : if (strcmp(cte->ctename, rte->ctename) == 0)
8896 20 : break;
8897 : }
8898 : }
8899 48 : if (lc != NULL)
8900 : {
8901 20 : Query *ctequery = (Query *) cte->ctequery;
8902 20 : TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
8903 : attnum);
8904 :
8905 20 : if (ste == NULL || ste->resjunk)
8906 0 : elog(ERROR, "CTE %s does not have attribute %d",
8907 : rte->eref->aliasname, attnum);
8908 20 : expr = (Node *) ste->expr;
8909 20 : if (IsA(expr, Var))
8910 : {
8911 : /*
8912 : * Recurse into the CTE to see what its Var refers to.
8913 : * We have to build an additional level of namespace
8914 : * to keep in step with varlevelsup in the CTE;
8915 : * furthermore it could be an outer CTE (compare
8916 : * SUBQUERY case above).
8917 : */
8918 12 : List *save_nslist = context->namespaces;
8919 : List *parent_namespaces;
8920 : deparse_namespace mydpns;
8921 : const char *result;
8922 :
8923 12 : parent_namespaces = list_copy_tail(context->namespaces,
8924 : ctelevelsup);
8925 :
8926 12 : set_deparse_for_query(&mydpns, ctequery,
8927 : parent_namespaces);
8928 :
8929 12 : context->namespaces = lcons(&mydpns, parent_namespaces);
8930 :
8931 12 : result = get_name_for_var_field((Var *) expr, fieldno,
8932 : 0, context);
8933 :
8934 12 : context->namespaces = save_nslist;
8935 :
8936 12 : return result;
8937 : }
8938 : /* else fall through to inspect the expression */
8939 : }
8940 : else
8941 : {
8942 : /*
8943 : * We're deparsing a Plan tree so we don't have a CTE
8944 : * list. But the only places we'd normally see a Var
8945 : * directly referencing a CTE RTE are in CteScan or
8946 : * WorkTableScan plan nodes. For those cases,
8947 : * set_deparse_plan arranged for dpns->inner_plan to be
8948 : * the plan node that emits the CTE or RecursiveUnion
8949 : * result, and we can look at its tlist instead. As
8950 : * above, this can fail if the CTE has been proven empty,
8951 : * in which case fall back to "fN".
8952 : */
8953 : TargetEntry *tle;
8954 : deparse_namespace save_dpns;
8955 : const char *result;
8956 :
8957 28 : if (!dpns->inner_plan)
8958 : {
8959 4 : char *dummy_name = palloc(32);
8960 :
8961 : Assert(dpns->plan && IsA(dpns->plan, Result));
8962 4 : snprintf(dummy_name, 32, "f%d", fieldno);
8963 4 : return dummy_name;
8964 : }
8965 : Assert(dpns->plan && (IsA(dpns->plan, CteScan) ||
8966 : IsA(dpns->plan, WorkTableScan)));
8967 :
8968 24 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8969 24 : if (!tle)
8970 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8971 : attnum);
8972 : Assert(netlevelsup == 0);
8973 24 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8974 :
8975 24 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8976 : levelsup, context);
8977 :
8978 24 : pop_child_plan(dpns, &save_dpns);
8979 24 : return result;
8980 : }
8981 : }
8982 8 : break;
8983 0 : case RTE_GROUP:
8984 :
8985 : /*
8986 : * We couldn't get here: any Vars that reference the RTE_GROUP RTE
8987 : * should have been replaced with the underlying grouping
8988 : * expressions.
8989 : */
8990 0 : break;
8991 : }
8992 :
8993 : /*
8994 : * We now have an expression we can't expand any more, so see if
8995 : * get_expr_result_tupdesc() can do anything with it.
8996 : */
8997 24 : tupleDesc = get_expr_result_tupdesc(expr, false);
8998 : /* Got the tupdesc, so we can extract the field name */
8999 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
9000 24 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
9001 : }
9002 :
9003 : /*
9004 : * Try to find the referenced expression for a PARAM_EXEC Param that might
9005 : * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
9006 : *
9007 : * If successful, return the expression and set *dpns_p and *ancestor_cell_p
9008 : * appropriately for calling push_ancestor_plan(). If no referent can be
9009 : * found, return NULL.
9010 : */
9011 : static Node *
9012 4786 : find_param_referent(Param *param, deparse_context *context,
9013 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
9014 : {
9015 : /* Initialize output parameters to prevent compiler warnings */
9016 4786 : *dpns_p = NULL;
9017 4786 : *ancestor_cell_p = NULL;
9018 :
9019 : /*
9020 : * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
9021 : * SubPlan argument. This will necessarily be in some ancestor of the
9022 : * current expression's Plan node.
9023 : */
9024 4786 : if (param->paramkind == PARAM_EXEC)
9025 : {
9026 : deparse_namespace *dpns;
9027 : Plan *child_plan;
9028 : ListCell *lc;
9029 :
9030 4197 : dpns = (deparse_namespace *) linitial(context->namespaces);
9031 4197 : child_plan = dpns->plan;
9032 :
9033 7458 : foreach(lc, dpns->ancestors)
9034 : {
9035 6352 : Node *ancestor = (Node *) lfirst(lc);
9036 : ListCell *lc2;
9037 :
9038 : /*
9039 : * NestLoops transmit params to their inner child only.
9040 : */
9041 6352 : if (IsA(ancestor, NestLoop) &&
9042 2890 : child_plan == innerPlan(ancestor))
9043 : {
9044 2768 : NestLoop *nl = (NestLoop *) ancestor;
9045 :
9046 3435 : foreach(lc2, nl->nestParams)
9047 : {
9048 3318 : NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
9049 :
9050 3318 : if (nlp->paramno == param->paramid)
9051 : {
9052 : /* Found a match, so return it */
9053 2651 : *dpns_p = dpns;
9054 2651 : *ancestor_cell_p = lc;
9055 2651 : return (Node *) nlp->paramval;
9056 : }
9057 : }
9058 : }
9059 :
9060 : /*
9061 : * If ancestor is a SubPlan, check the arguments it provides.
9062 : */
9063 3701 : if (IsA(ancestor, SubPlan))
9064 287 : {
9065 727 : SubPlan *subplan = (SubPlan *) ancestor;
9066 : ListCell *lc3;
9067 : ListCell *lc4;
9068 :
9069 950 : forboth(lc3, subplan->parParam, lc4, subplan->args)
9070 : {
9071 663 : int paramid = lfirst_int(lc3);
9072 663 : Node *arg = (Node *) lfirst(lc4);
9073 :
9074 663 : if (paramid == param->paramid)
9075 : {
9076 : /*
9077 : * Found a match, so return it. But, since Vars in
9078 : * the arg are to be evaluated in the surrounding
9079 : * context, we have to point to the next ancestor item
9080 : * that is *not* a SubPlan.
9081 : */
9082 : ListCell *rest;
9083 :
9084 440 : for_each_cell(rest, dpns->ancestors,
9085 : lnext(dpns->ancestors, lc))
9086 : {
9087 440 : Node *ancestor2 = (Node *) lfirst(rest);
9088 :
9089 440 : if (!IsA(ancestor2, SubPlan))
9090 : {
9091 440 : *dpns_p = dpns;
9092 440 : *ancestor_cell_p = rest;
9093 440 : return arg;
9094 : }
9095 : }
9096 0 : elog(ERROR, "SubPlan cannot be outermost ancestor");
9097 : }
9098 : }
9099 :
9100 : /* SubPlan isn't a kind of Plan, so skip the rest */
9101 287 : continue;
9102 : }
9103 :
9104 : /*
9105 : * We need not consider the ancestor's initPlan list, since
9106 : * initplans never have any parParams.
9107 : */
9108 :
9109 : /* No luck, crawl up to next ancestor */
9110 2974 : child_plan = (Plan *) ancestor;
9111 : }
9112 : }
9113 :
9114 : /* No referent found */
9115 1695 : return NULL;
9116 : }
9117 :
9118 : /*
9119 : * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.
9120 : *
9121 : * If successful, return the generating subplan/initplan and set *column_p
9122 : * to the subplan's 0-based output column number.
9123 : * Otherwise, return NULL.
9124 : */
9125 : static SubPlan *
9126 1695 : find_param_generator(Param *param, deparse_context *context, int *column_p)
9127 : {
9128 : /* Initialize output parameter to prevent compiler warnings */
9129 1695 : *column_p = 0;
9130 :
9131 : /*
9132 : * If it's a PARAM_EXEC parameter, search the current plan node as well as
9133 : * ancestor nodes looking for a subplan or initplan that emits the value
9134 : * for the Param. It could appear in the setParams of an initplan or
9135 : * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
9136 : */
9137 1695 : if (param->paramkind == PARAM_EXEC)
9138 : {
9139 : SubPlan *result;
9140 : deparse_namespace *dpns;
9141 : ListCell *lc;
9142 :
9143 1106 : dpns = (deparse_namespace *) linitial(context->namespaces);
9144 :
9145 : /* First check the innermost plan node's initplans */
9146 1106 : result = find_param_generator_initplan(param, dpns->plan, column_p);
9147 1106 : if (result)
9148 289 : return result;
9149 :
9150 : /*
9151 : * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
9152 : * which can be referenced by Params elsewhere in the targetlist.
9153 : * (Such Params should always be in the same targetlist, so there's no
9154 : * need to do this work at upper plan nodes.)
9155 : */
9156 4113 : foreach_node(TargetEntry, tle, dpns->plan->targetlist)
9157 : {
9158 2547 : if (tle->expr && IsA(tle->expr, SubPlan))
9159 : {
9160 66 : SubPlan *subplan = (SubPlan *) tle->expr;
9161 :
9162 66 : if (subplan->subLinkType == MULTIEXPR_SUBLINK)
9163 : {
9164 51 : foreach_int(paramid, subplan->setParam)
9165 : {
9166 51 : if (paramid == param->paramid)
9167 : {
9168 : /* Found a match, so return it. */
9169 34 : *column_p = foreach_current_index(paramid);
9170 34 : return subplan;
9171 : }
9172 : }
9173 : }
9174 : }
9175 : }
9176 :
9177 : /* No luck, so check the ancestor nodes */
9178 1018 : foreach(lc, dpns->ancestors)
9179 : {
9180 1018 : Node *ancestor = (Node *) lfirst(lc);
9181 :
9182 : /*
9183 : * If ancestor is a SubPlan, check the paramIds it provides.
9184 : */
9185 1018 : if (IsA(ancestor, SubPlan))
9186 0 : {
9187 191 : SubPlan *subplan = (SubPlan *) ancestor;
9188 :
9189 216 : foreach_int(paramid, subplan->paramIds)
9190 : {
9191 216 : if (paramid == param->paramid)
9192 : {
9193 : /* Found a match, so return it. */
9194 191 : *column_p = foreach_current_index(paramid);
9195 191 : return subplan;
9196 : }
9197 : }
9198 :
9199 : /* SubPlan isn't a kind of Plan, so skip the rest */
9200 0 : continue;
9201 : }
9202 :
9203 : /*
9204 : * Otherwise, it's some kind of Plan node, so check its initplans.
9205 : */
9206 827 : result = find_param_generator_initplan(param, (Plan *) ancestor,
9207 : column_p);
9208 827 : if (result)
9209 592 : return result;
9210 :
9211 : /* No luck, crawl up to next ancestor */
9212 : }
9213 : }
9214 :
9215 : /* No generator found */
9216 589 : return NULL;
9217 : }
9218 :
9219 : /*
9220 : * Subroutine for find_param_generator: search one Plan node's initplans
9221 : */
9222 : static SubPlan *
9223 1933 : find_param_generator_initplan(Param *param, Plan *plan, int *column_p)
9224 : {
9225 3077 : foreach_node(SubPlan, subplan, plan->initPlan)
9226 : {
9227 1161 : foreach_int(paramid, subplan->setParam)
9228 : {
9229 977 : if (paramid == param->paramid)
9230 : {
9231 : /* Found a match, so return it. */
9232 881 : *column_p = foreach_current_index(paramid);
9233 881 : return subplan;
9234 : }
9235 : }
9236 : }
9237 1052 : return NULL;
9238 : }
9239 :
9240 : /*
9241 : * Display a Param appropriately.
9242 : */
9243 : static void
9244 4774 : get_parameter(Param *param, deparse_context *context)
9245 : {
9246 : Node *expr;
9247 : deparse_namespace *dpns;
9248 : ListCell *ancestor_cell;
9249 : SubPlan *subplan;
9250 : int column;
9251 :
9252 : /*
9253 : * If it's a PARAM_EXEC parameter, try to locate the expression from which
9254 : * the parameter was computed. This stanza handles only cases in which
9255 : * the Param represents an input to the subplan we are currently in.
9256 : */
9257 4774 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
9258 4774 : if (expr)
9259 : {
9260 : /* Found a match, so print it */
9261 : deparse_namespace save_dpns;
9262 : bool save_varprefix;
9263 : bool need_paren;
9264 :
9265 : /* Switch attention to the ancestor plan node */
9266 3079 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
9267 :
9268 : /*
9269 : * Force prefixing of Vars, since they won't belong to the relation
9270 : * being scanned in the original plan node.
9271 : */
9272 3079 : save_varprefix = context->varprefix;
9273 3079 : context->varprefix = true;
9274 :
9275 : /*
9276 : * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
9277 : * upper-level Param, which wouldn't need extra parentheses.
9278 : * Otherwise, insert parens to ensure the expression looks atomic.
9279 : */
9280 3095 : need_paren = !(IsA(expr, Var) ||
9281 16 : IsA(expr, Aggref) ||
9282 12 : IsA(expr, GroupingFunc) ||
9283 8 : IsA(expr, Param));
9284 3079 : if (need_paren)
9285 0 : appendStringInfoChar(context->buf, '(');
9286 :
9287 3079 : get_rule_expr(expr, context, false);
9288 :
9289 3079 : if (need_paren)
9290 0 : appendStringInfoChar(context->buf, ')');
9291 :
9292 3079 : context->varprefix = save_varprefix;
9293 :
9294 3079 : pop_ancestor_plan(dpns, &save_dpns);
9295 :
9296 3079 : return;
9297 : }
9298 :
9299 : /*
9300 : * Alternatively, maybe it's a subplan output, which we print as a
9301 : * reference to the subplan. (We could drill down into the subplan and
9302 : * print the relevant targetlist expression, but that has been deemed too
9303 : * confusing since it would violate normal SQL scope rules. Also, we're
9304 : * relying on this reference to show that the testexpr containing the
9305 : * Param has anything to do with that subplan at all.)
9306 : */
9307 1695 : subplan = find_param_generator(param, context, &column);
9308 1695 : if (subplan)
9309 : {
9310 : const char *nameprefix;
9311 :
9312 1106 : if (subplan->isInitPlan)
9313 881 : nameprefix = "InitPlan ";
9314 : else
9315 225 : nameprefix = "SubPlan ";
9316 :
9317 1106 : appendStringInfo(context->buf, "(%s%s%s).col%d",
9318 1106 : subplan->useHashTable ? "hashed " : "",
9319 : nameprefix,
9320 : subplan->plan_name, column + 1);
9321 :
9322 1106 : return;
9323 : }
9324 :
9325 : /*
9326 : * If it's an external parameter, see if the outermost namespace provides
9327 : * function argument names.
9328 : */
9329 589 : if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
9330 : {
9331 589 : dpns = llast(context->namespaces);
9332 589 : if (dpns->argnames &&
9333 45 : param->paramid > 0 &&
9334 45 : param->paramid <= dpns->numargs)
9335 : {
9336 45 : char *argname = dpns->argnames[param->paramid - 1];
9337 :
9338 45 : if (argname)
9339 : {
9340 45 : bool should_qualify = false;
9341 : ListCell *lc;
9342 :
9343 : /*
9344 : * Qualify the parameter name if there are any other deparse
9345 : * namespaces with range tables. This avoids qualifying in
9346 : * trivial cases like "RETURN a + b", but makes it safe in all
9347 : * other cases.
9348 : */
9349 103 : foreach(lc, context->namespaces)
9350 : {
9351 78 : deparse_namespace *depns = lfirst(lc);
9352 :
9353 78 : if (depns->rtable_names != NIL)
9354 : {
9355 20 : should_qualify = true;
9356 20 : break;
9357 : }
9358 : }
9359 45 : if (should_qualify)
9360 : {
9361 20 : appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
9362 20 : appendStringInfoChar(context->buf, '.');
9363 : }
9364 :
9365 45 : appendStringInfoString(context->buf, quote_identifier(argname));
9366 45 : return;
9367 : }
9368 : }
9369 : }
9370 :
9371 : /*
9372 : * Not PARAM_EXEC, or couldn't find referent: just print $N.
9373 : *
9374 : * It's a bug if we get here for anything except PARAM_EXTERN Params, but
9375 : * in production builds printing $N seems more useful than failing.
9376 : */
9377 : Assert(param->paramkind == PARAM_EXTERN);
9378 :
9379 544 : appendStringInfo(context->buf, "$%d", param->paramid);
9380 : }
9381 :
9382 : /*
9383 : * get_simple_binary_op_name
9384 : *
9385 : * helper function for isSimpleNode
9386 : * will return single char binary operator name, or NULL if it's not
9387 : */
9388 : static const char *
9389 100 : get_simple_binary_op_name(OpExpr *expr)
9390 : {
9391 100 : List *args = expr->args;
9392 :
9393 100 : if (list_length(args) == 2)
9394 : {
9395 : /* binary operator */
9396 100 : Node *arg1 = (Node *) linitial(args);
9397 100 : Node *arg2 = (Node *) lsecond(args);
9398 : const char *op;
9399 :
9400 100 : op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
9401 100 : if (strlen(op) == 1)
9402 100 : return op;
9403 : }
9404 0 : return NULL;
9405 : }
9406 :
9407 :
9408 : /*
9409 : * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
9410 : *
9411 : * true : simple in the context of parent node's type
9412 : * false : not simple
9413 : */
9414 : static bool
9415 3945 : isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
9416 : {
9417 3945 : if (!node)
9418 0 : return false;
9419 :
9420 3945 : switch (nodeTag(node))
9421 : {
9422 3314 : case T_Var:
9423 : case T_Const:
9424 : case T_Param:
9425 : case T_CoerceToDomainValue:
9426 : case T_SetToDefault:
9427 : case T_CurrentOfExpr:
9428 : /* single words: always simple */
9429 3314 : return true;
9430 :
9431 327 : case T_SubscriptingRef:
9432 : case T_ArrayExpr:
9433 : case T_RowExpr:
9434 : case T_CoalesceExpr:
9435 : case T_MinMaxExpr:
9436 : case T_SQLValueFunction:
9437 : case T_XmlExpr:
9438 : case T_NextValueExpr:
9439 : case T_NullIfExpr:
9440 : case T_Aggref:
9441 : case T_GroupingFunc:
9442 : case T_WindowFunc:
9443 : case T_MergeSupportFunc:
9444 : case T_FuncExpr:
9445 : case T_JsonConstructorExpr:
9446 : case T_JsonExpr:
9447 : /* function-like: name(..) or name[..] */
9448 327 : return true;
9449 :
9450 : /* CASE keywords act as parentheses */
9451 0 : case T_CaseExpr:
9452 0 : return true;
9453 :
9454 48 : case T_FieldSelect:
9455 :
9456 : /*
9457 : * appears simple since . has top precedence, unless parent is
9458 : * T_FieldSelect itself!
9459 : */
9460 48 : return !IsA(parentNode, FieldSelect);
9461 :
9462 0 : case T_FieldStore:
9463 :
9464 : /*
9465 : * treat like FieldSelect (probably doesn't matter)
9466 : */
9467 0 : return !IsA(parentNode, FieldStore);
9468 :
9469 12 : case T_CoerceToDomain:
9470 : /* maybe simple, check args */
9471 12 : return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
9472 : node, prettyFlags);
9473 12 : case T_RelabelType:
9474 12 : return isSimpleNode((Node *) ((RelabelType *) node)->arg,
9475 : node, prettyFlags);
9476 16 : case T_CoerceViaIO:
9477 16 : return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
9478 : node, prettyFlags);
9479 0 : case T_ArrayCoerceExpr:
9480 0 : return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
9481 : node, prettyFlags);
9482 0 : case T_ConvertRowtypeExpr:
9483 0 : return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
9484 : node, prettyFlags);
9485 0 : case T_ReturningExpr:
9486 0 : return isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr,
9487 : node, prettyFlags);
9488 :
9489 184 : case T_OpExpr:
9490 : {
9491 : /* depends on parent node type; needs further checking */
9492 184 : if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
9493 : {
9494 : const char *op;
9495 : const char *parentOp;
9496 : bool is_lopriop;
9497 : bool is_hipriop;
9498 : bool is_lopriparent;
9499 : bool is_hipriparent;
9500 :
9501 52 : op = get_simple_binary_op_name((OpExpr *) node);
9502 52 : if (!op)
9503 0 : return false;
9504 :
9505 : /* We know only the basic operators + - and * / % */
9506 52 : is_lopriop = (strchr("+-", *op) != NULL);
9507 52 : is_hipriop = (strchr("*/%", *op) != NULL);
9508 52 : if (!(is_lopriop || is_hipriop))
9509 4 : return false;
9510 :
9511 48 : parentOp = get_simple_binary_op_name((OpExpr *) parentNode);
9512 48 : if (!parentOp)
9513 0 : return false;
9514 :
9515 48 : is_lopriparent = (strchr("+-", *parentOp) != NULL);
9516 48 : is_hipriparent = (strchr("*/%", *parentOp) != NULL);
9517 48 : if (!(is_lopriparent || is_hipriparent))
9518 0 : return false;
9519 :
9520 48 : if (is_hipriop && is_lopriparent)
9521 8 : return true; /* op binds tighter than parent */
9522 :
9523 40 : if (is_lopriop && is_hipriparent)
9524 32 : return false;
9525 :
9526 : /*
9527 : * Operators are same priority --- can skip parens only if
9528 : * we have (a - b) - c, not a - (b - c).
9529 : */
9530 8 : if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
9531 4 : return true;
9532 :
9533 4 : return false;
9534 : }
9535 : /* else do the same stuff as for T_SubLink et al. */
9536 : }
9537 : pg_fallthrough;
9538 :
9539 : case T_SubLink:
9540 : case T_NullTest:
9541 : case T_BooleanTest:
9542 : case T_DistinctExpr:
9543 : case T_JsonIsPredicate:
9544 144 : switch (nodeTag(parentNode))
9545 : {
9546 40 : case T_FuncExpr:
9547 : {
9548 : /* special handling for casts and COERCE_SQL_SYNTAX */
9549 40 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9550 :
9551 40 : if (type == COERCE_EXPLICIT_CAST ||
9552 4 : type == COERCE_IMPLICIT_CAST ||
9553 : type == COERCE_SQL_SYNTAX)
9554 40 : return false;
9555 0 : return true; /* own parentheses */
9556 : }
9557 84 : case T_BoolExpr: /* lower precedence */
9558 : case T_SubscriptingRef: /* other separators */
9559 : case T_ArrayExpr: /* other separators */
9560 : case T_RowExpr: /* other separators */
9561 : case T_CoalesceExpr: /* own parentheses */
9562 : case T_MinMaxExpr: /* own parentheses */
9563 : case T_XmlExpr: /* own parentheses */
9564 : case T_NullIfExpr: /* other separators */
9565 : case T_Aggref: /* own parentheses */
9566 : case T_GroupingFunc: /* own parentheses */
9567 : case T_WindowFunc: /* own parentheses */
9568 : case T_CaseExpr: /* other separators */
9569 84 : return true;
9570 20 : default:
9571 20 : return false;
9572 : }
9573 :
9574 12 : case T_BoolExpr:
9575 12 : switch (nodeTag(parentNode))
9576 : {
9577 12 : case T_BoolExpr:
9578 12 : if (prettyFlags & PRETTYFLAG_PAREN)
9579 : {
9580 : BoolExprType type;
9581 : BoolExprType parentType;
9582 :
9583 12 : type = ((BoolExpr *) node)->boolop;
9584 12 : parentType = ((BoolExpr *) parentNode)->boolop;
9585 12 : switch (type)
9586 : {
9587 8 : case NOT_EXPR:
9588 : case AND_EXPR:
9589 8 : if (parentType == AND_EXPR || parentType == OR_EXPR)
9590 8 : return true;
9591 0 : break;
9592 4 : case OR_EXPR:
9593 4 : if (parentType == OR_EXPR)
9594 0 : return true;
9595 4 : break;
9596 : }
9597 : }
9598 4 : return false;
9599 0 : case T_FuncExpr:
9600 : {
9601 : /* special handling for casts and COERCE_SQL_SYNTAX */
9602 0 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9603 :
9604 0 : if (type == COERCE_EXPLICIT_CAST ||
9605 0 : type == COERCE_IMPLICIT_CAST ||
9606 : type == COERCE_SQL_SYNTAX)
9607 0 : return false;
9608 0 : return true; /* own parentheses */
9609 : }
9610 0 : case T_SubscriptingRef: /* other separators */
9611 : case T_ArrayExpr: /* other separators */
9612 : case T_RowExpr: /* other separators */
9613 : case T_CoalesceExpr: /* own parentheses */
9614 : case T_MinMaxExpr: /* own parentheses */
9615 : case T_XmlExpr: /* own parentheses */
9616 : case T_NullIfExpr: /* other separators */
9617 : case T_Aggref: /* own parentheses */
9618 : case T_GroupingFunc: /* own parentheses */
9619 : case T_WindowFunc: /* own parentheses */
9620 : case T_CaseExpr: /* other separators */
9621 : case T_JsonExpr: /* own parentheses */
9622 0 : return true;
9623 0 : default:
9624 0 : return false;
9625 : }
9626 :
9627 4 : case T_JsonValueExpr:
9628 : /* maybe simple, check args */
9629 4 : return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
9630 : node, prettyFlags);
9631 :
9632 4 : default:
9633 4 : break;
9634 : }
9635 : /* those we don't know: in dubio complexo */
9636 4 : return false;
9637 : }
9638 :
9639 :
9640 : /*
9641 : * appendContextKeyword - append a keyword to buffer
9642 : *
9643 : * If prettyPrint is enabled, perform a line break, and adjust indentation.
9644 : * Otherwise, just append the keyword.
9645 : */
9646 : static void
9647 19137 : appendContextKeyword(deparse_context *context, const char *str,
9648 : int indentBefore, int indentAfter, int indentPlus)
9649 : {
9650 19137 : StringInfo buf = context->buf;
9651 :
9652 19137 : if (PRETTY_INDENT(context))
9653 : {
9654 : int indentAmount;
9655 :
9656 18516 : context->indentLevel += indentBefore;
9657 :
9658 : /* remove any trailing spaces currently in the buffer ... */
9659 18516 : removeStringInfoSpaces(buf);
9660 : /* ... then add a newline and some spaces */
9661 18516 : appendStringInfoChar(buf, '\n');
9662 :
9663 18516 : if (context->indentLevel < PRETTYINDENT_LIMIT)
9664 18516 : indentAmount = Max(context->indentLevel, 0) + indentPlus;
9665 : else
9666 : {
9667 : /*
9668 : * If we're indented more than PRETTYINDENT_LIMIT characters, try
9669 : * to conserve horizontal space by reducing the per-level
9670 : * indentation. For best results the scale factor here should
9671 : * divide all the indent amounts that get added to indentLevel
9672 : * (PRETTYINDENT_STD, etc). It's important that the indentation
9673 : * not grow unboundedly, else deeply-nested trees use O(N^2)
9674 : * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
9675 : */
9676 0 : indentAmount = PRETTYINDENT_LIMIT +
9677 0 : (context->indentLevel - PRETTYINDENT_LIMIT) /
9678 : (PRETTYINDENT_STD / 2);
9679 0 : indentAmount %= PRETTYINDENT_LIMIT;
9680 : /* scale/wrap logic affects indentLevel, but not indentPlus */
9681 0 : indentAmount += indentPlus;
9682 : }
9683 18516 : appendStringInfoSpaces(buf, indentAmount);
9684 :
9685 18516 : appendStringInfoString(buf, str);
9686 :
9687 18516 : context->indentLevel += indentAfter;
9688 18516 : if (context->indentLevel < 0)
9689 0 : context->indentLevel = 0;
9690 : }
9691 : else
9692 621 : appendStringInfoString(buf, str);
9693 19137 : }
9694 :
9695 : /*
9696 : * removeStringInfoSpaces - delete trailing spaces from a buffer.
9697 : *
9698 : * Possibly this should move to stringinfo.c at some point.
9699 : */
9700 : static void
9701 18884 : removeStringInfoSpaces(StringInfo str)
9702 : {
9703 29488 : while (str->len > 0 && str->data[str->len - 1] == ' ')
9704 10604 : str->data[--(str->len)] = '\0';
9705 18884 : }
9706 :
9707 :
9708 : /*
9709 : * get_rule_expr_paren - deparse expr using get_rule_expr,
9710 : * embracing the string with parentheses if necessary for prettyPrint.
9711 : *
9712 : * Never embrace if prettyFlags=0, because it's done in the calling node.
9713 : *
9714 : * Any node that does *not* embrace its argument node by sql syntax (with
9715 : * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
9716 : * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
9717 : * added.
9718 : */
9719 : static void
9720 108676 : get_rule_expr_paren(Node *node, deparse_context *context,
9721 : bool showimplicit, Node *parentNode)
9722 : {
9723 : bool need_paren;
9724 :
9725 112577 : need_paren = PRETTY_PAREN(context) &&
9726 3901 : !isSimpleNode(node, parentNode, context->prettyFlags);
9727 :
9728 108676 : if (need_paren)
9729 108 : appendStringInfoChar(context->buf, '(');
9730 :
9731 108676 : get_rule_expr(node, context, showimplicit);
9732 :
9733 108676 : if (need_paren)
9734 108 : appendStringInfoChar(context->buf, ')');
9735 108676 : }
9736 :
9737 : static void
9738 56 : get_json_behavior(JsonBehavior *behavior, deparse_context *context,
9739 : const char *on)
9740 : {
9741 : /*
9742 : * The order of array elements must correspond to the order of
9743 : * JsonBehaviorType members.
9744 : */
9745 56 : const char *behavior_names[] =
9746 : {
9747 : " NULL",
9748 : " ERROR",
9749 : " EMPTY",
9750 : " TRUE",
9751 : " FALSE",
9752 : " UNKNOWN",
9753 : " EMPTY ARRAY",
9754 : " EMPTY OBJECT",
9755 : " DEFAULT "
9756 : };
9757 :
9758 56 : if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
9759 0 : elog(ERROR, "invalid json behavior type: %d", behavior->btype);
9760 :
9761 56 : appendStringInfoString(context->buf, behavior_names[behavior->btype]);
9762 :
9763 56 : if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
9764 12 : get_rule_expr(behavior->expr, context, false);
9765 :
9766 56 : appendStringInfo(context->buf, " ON %s", on);
9767 56 : }
9768 :
9769 : /*
9770 : * get_json_expr_options
9771 : *
9772 : * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and
9773 : * JSON_TABLE columns.
9774 : */
9775 : static void
9776 304 : get_json_expr_options(JsonExpr *jsexpr, deparse_context *context,
9777 : JsonBehaviorType default_behavior)
9778 : {
9779 304 : if (jsexpr->op == JSON_QUERY_OP)
9780 : {
9781 140 : if (jsexpr->wrapper == JSW_CONDITIONAL)
9782 8 : appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
9783 132 : else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
9784 20 : appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
9785 : /* The default */
9786 112 : else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
9787 112 : appendStringInfoString(context->buf, " WITHOUT WRAPPER");
9788 :
9789 140 : if (jsexpr->omit_quotes)
9790 28 : appendStringInfoString(context->buf, " OMIT QUOTES");
9791 : /* The default */
9792 : else
9793 112 : appendStringInfoString(context->buf, " KEEP QUOTES");
9794 : }
9795 :
9796 304 : if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
9797 20 : get_json_behavior(jsexpr->on_empty, context, "EMPTY");
9798 :
9799 304 : if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
9800 32 : get_json_behavior(jsexpr->on_error, context, "ERROR");
9801 304 : }
9802 :
9803 : /* ----------
9804 : * get_rule_expr - Parse back an expression
9805 : *
9806 : * Note: showimplicit determines whether we display any implicit cast that
9807 : * is present at the top of the expression tree. It is a passed argument,
9808 : * not a field of the context struct, because we change the value as we
9809 : * recurse down into the expression. In general we suppress implicit casts
9810 : * when the result type is known with certainty (eg, the arguments of an
9811 : * OR must be boolean). We display implicit casts for arguments of functions
9812 : * and operators, since this is needed to be certain that the same function
9813 : * or operator will be chosen when the expression is re-parsed.
9814 : * ----------
9815 : */
9816 : static void
9817 235041 : get_rule_expr(Node *node, deparse_context *context,
9818 : bool showimplicit)
9819 : {
9820 235041 : StringInfo buf = context->buf;
9821 :
9822 235041 : if (node == NULL)
9823 60 : return;
9824 :
9825 : /* Guard against excessively long or deeply-nested queries */
9826 234981 : CHECK_FOR_INTERRUPTS();
9827 234981 : check_stack_depth();
9828 :
9829 : /*
9830 : * Each level of get_rule_expr must emit an indivisible term
9831 : * (parenthesized if necessary) to ensure result is reparsed into the same
9832 : * expression tree. The only exception is that when the input is a List,
9833 : * we emit the component items comma-separated with no surrounding
9834 : * decoration; this is convenient for most callers.
9835 : */
9836 234981 : switch (nodeTag(node))
9837 : {
9838 113395 : case T_Var:
9839 113395 : (void) get_variable((Var *) node, 0, false, context);
9840 113395 : break;
9841 :
9842 41085 : case T_Const:
9843 41085 : get_const_expr((Const *) node, context, 0);
9844 41085 : break;
9845 :
9846 4774 : case T_Param:
9847 4774 : get_parameter((Param *) node, context);
9848 4774 : break;
9849 :
9850 2570 : case T_Aggref:
9851 2570 : get_agg_expr((Aggref *) node, context, (Aggref *) node);
9852 2570 : break;
9853 :
9854 74 : case T_GroupingFunc:
9855 : {
9856 74 : GroupingFunc *gexpr = (GroupingFunc *) node;
9857 :
9858 74 : appendStringInfoString(buf, "GROUPING(");
9859 74 : get_rule_expr((Node *) gexpr->args, context, true);
9860 74 : appendStringInfoChar(buf, ')');
9861 : }
9862 74 : break;
9863 :
9864 214 : case T_WindowFunc:
9865 214 : get_windowfunc_expr((WindowFunc *) node, context);
9866 214 : break;
9867 :
9868 4 : case T_MergeSupportFunc:
9869 4 : appendStringInfoString(buf, "MERGE_ACTION()");
9870 4 : break;
9871 :
9872 232 : case T_SubscriptingRef:
9873 : {
9874 232 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
9875 : bool need_parens;
9876 :
9877 : /*
9878 : * If the argument is a CaseTestExpr, we must be inside a
9879 : * FieldStore, ie, we are assigning to an element of an array
9880 : * within a composite column. Since we already punted on
9881 : * displaying the FieldStore's target information, just punt
9882 : * here too, and display only the assignment source
9883 : * expression.
9884 : */
9885 232 : if (IsA(sbsref->refexpr, CaseTestExpr))
9886 : {
9887 : Assert(sbsref->refassgnexpr);
9888 0 : get_rule_expr((Node *) sbsref->refassgnexpr,
9889 : context, showimplicit);
9890 0 : break;
9891 : }
9892 :
9893 : /*
9894 : * Parenthesize the argument unless it's a simple Var or a
9895 : * FieldSelect. (In particular, if it's another
9896 : * SubscriptingRef, we *must* parenthesize to avoid
9897 : * confusion.)
9898 : */
9899 358 : need_parens = !IsA(sbsref->refexpr, Var) &&
9900 126 : !IsA(sbsref->refexpr, FieldSelect);
9901 232 : if (need_parens)
9902 56 : appendStringInfoChar(buf, '(');
9903 232 : get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
9904 232 : if (need_parens)
9905 56 : appendStringInfoChar(buf, ')');
9906 :
9907 : /*
9908 : * If there's a refassgnexpr, we want to print the node in the
9909 : * format "container[subscripts] := refassgnexpr". This is
9910 : * not legal SQL, so decompilation of INSERT or UPDATE
9911 : * statements should always use processIndirection as part of
9912 : * the statement-level syntax. We should only see this when
9913 : * EXPLAIN tries to print the targetlist of a plan resulting
9914 : * from such a statement.
9915 : */
9916 232 : if (sbsref->refassgnexpr)
9917 : {
9918 : Node *refassgnexpr;
9919 :
9920 : /*
9921 : * Use processIndirection to print this node's subscripts
9922 : * as well as any additional field selections or
9923 : * subscripting in immediate descendants. It returns the
9924 : * RHS expr that is actually being "assigned".
9925 : */
9926 8 : refassgnexpr = processIndirection(node, context);
9927 8 : appendStringInfoString(buf, " := ");
9928 8 : get_rule_expr(refassgnexpr, context, showimplicit);
9929 : }
9930 : else
9931 : {
9932 : /* Just an ordinary container fetch, so print subscripts */
9933 224 : printSubscripts(sbsref, context);
9934 : }
9935 : }
9936 232 : break;
9937 :
9938 8127 : case T_FuncExpr:
9939 8127 : get_func_expr((FuncExpr *) node, context, showimplicit);
9940 8127 : break;
9941 :
9942 28 : case T_NamedArgExpr:
9943 : {
9944 28 : NamedArgExpr *na = (NamedArgExpr *) node;
9945 :
9946 28 : appendStringInfo(buf, "%s => ", quote_identifier(na->name));
9947 28 : get_rule_expr((Node *) na->arg, context, showimplicit);
9948 : }
9949 28 : break;
9950 :
9951 40578 : case T_OpExpr:
9952 40578 : get_oper_expr((OpExpr *) node, context);
9953 40578 : break;
9954 :
9955 16 : case T_DistinctExpr:
9956 : {
9957 16 : DistinctExpr *expr = (DistinctExpr *) node;
9958 16 : List *args = expr->args;
9959 16 : Node *arg1 = (Node *) linitial(args);
9960 16 : Node *arg2 = (Node *) lsecond(args);
9961 :
9962 16 : if (!PRETTY_PAREN(context))
9963 12 : appendStringInfoChar(buf, '(');
9964 16 : get_rule_expr_paren(arg1, context, true, node);
9965 16 : appendStringInfoString(buf, " IS DISTINCT FROM ");
9966 16 : get_rule_expr_paren(arg2, context, true, node);
9967 16 : if (!PRETTY_PAREN(context))
9968 12 : appendStringInfoChar(buf, ')');
9969 : }
9970 16 : break;
9971 :
9972 140 : case T_NullIfExpr:
9973 : {
9974 140 : NullIfExpr *nullifexpr = (NullIfExpr *) node;
9975 :
9976 140 : appendStringInfoString(buf, "NULLIF(");
9977 140 : get_rule_expr((Node *) nullifexpr->args, context, true);
9978 140 : appendStringInfoChar(buf, ')');
9979 : }
9980 140 : break;
9981 :
9982 2018 : case T_ScalarArrayOpExpr:
9983 : {
9984 2018 : ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
9985 2018 : List *args = expr->args;
9986 2018 : Node *arg1 = (Node *) linitial(args);
9987 2018 : Node *arg2 = (Node *) lsecond(args);
9988 :
9989 2018 : if (!PRETTY_PAREN(context))
9990 2010 : appendStringInfoChar(buf, '(');
9991 2018 : get_rule_expr_paren(arg1, context, true, node);
9992 2018 : appendStringInfo(buf, " %s %s (",
9993 : generate_operator_name(expr->opno,
9994 : exprType(arg1),
9995 : get_base_element_type(exprType(arg2))),
9996 2018 : expr->useOr ? "ANY" : "ALL");
9997 2018 : get_rule_expr_paren(arg2, context, true, node);
9998 :
9999 : /*
10000 : * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
10001 : * a bare sub-SELECT. Since we're here, the sub-SELECT must
10002 : * be meant as a scalar sub-SELECT yielding an array value to
10003 : * be used in ScalarArrayOpExpr; but the grammar will
10004 : * preferentially interpret such a construct as an ANY/ALL
10005 : * SubLink. To prevent misparsing the output that way, insert
10006 : * a dummy coercion (which will be stripped by parse analysis,
10007 : * so no inefficiency is added in dump and reload). This is
10008 : * indeed most likely what the user wrote to get the construct
10009 : * accepted in the first place.
10010 : */
10011 2018 : if (IsA(arg2, SubLink) &&
10012 4 : ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
10013 4 : appendStringInfo(buf, "::%s",
10014 : format_type_with_typemod(exprType(arg2),
10015 : exprTypmod(arg2)));
10016 2018 : appendStringInfoChar(buf, ')');
10017 2018 : if (!PRETTY_PAREN(context))
10018 2010 : appendStringInfoChar(buf, ')');
10019 : }
10020 2018 : break;
10021 :
10022 7467 : case T_BoolExpr:
10023 : {
10024 7467 : BoolExpr *expr = (BoolExpr *) node;
10025 7467 : Node *first_arg = linitial(expr->args);
10026 : ListCell *arg;
10027 :
10028 7467 : switch (expr->boolop)
10029 : {
10030 5927 : case AND_EXPR:
10031 5927 : if (!PRETTY_PAREN(context))
10032 5891 : appendStringInfoChar(buf, '(');
10033 5927 : get_rule_expr_paren(first_arg, context,
10034 : false, node);
10035 13523 : for_each_from(arg, expr->args, 1)
10036 : {
10037 7596 : appendStringInfoString(buf, " AND ");
10038 7596 : get_rule_expr_paren((Node *) lfirst(arg), context,
10039 : false, node);
10040 : }
10041 5927 : if (!PRETTY_PAREN(context))
10042 5891 : appendStringInfoChar(buf, ')');
10043 5927 : break;
10044 :
10045 1244 : case OR_EXPR:
10046 1244 : if (!PRETTY_PAREN(context))
10047 1236 : appendStringInfoChar(buf, '(');
10048 1244 : get_rule_expr_paren(first_arg, context,
10049 : false, node);
10050 2963 : for_each_from(arg, expr->args, 1)
10051 : {
10052 1719 : appendStringInfoString(buf, " OR ");
10053 1719 : get_rule_expr_paren((Node *) lfirst(arg), context,
10054 : false, node);
10055 : }
10056 1244 : if (!PRETTY_PAREN(context))
10057 1236 : appendStringInfoChar(buf, ')');
10058 1244 : break;
10059 :
10060 296 : case NOT_EXPR:
10061 296 : if (!PRETTY_PAREN(context))
10062 288 : appendStringInfoChar(buf, '(');
10063 296 : appendStringInfoString(buf, "NOT ");
10064 296 : get_rule_expr_paren(first_arg, context,
10065 : false, node);
10066 296 : if (!PRETTY_PAREN(context))
10067 288 : appendStringInfoChar(buf, ')');
10068 296 : break;
10069 :
10070 0 : default:
10071 0 : elog(ERROR, "unrecognized boolop: %d",
10072 : (int) expr->boolop);
10073 : }
10074 : }
10075 7467 : break;
10076 :
10077 294 : case T_SubLink:
10078 294 : get_sublink_expr((SubLink *) node, context);
10079 294 : break;
10080 :
10081 525 : case T_SubPlan:
10082 : {
10083 525 : SubPlan *subplan = (SubPlan *) node;
10084 :
10085 : /*
10086 : * We cannot see an already-planned subplan in rule deparsing,
10087 : * only while EXPLAINing a query plan. We don't try to
10088 : * reconstruct the original SQL, just reference the subplan
10089 : * that appears elsewhere in EXPLAIN's result. It does seem
10090 : * useful to show the subLinkType and testexpr (if any), and
10091 : * we also note whether the subplan will be hashed.
10092 : */
10093 525 : switch (subplan->subLinkType)
10094 : {
10095 68 : case EXISTS_SUBLINK:
10096 68 : appendStringInfoString(buf, "EXISTS(");
10097 : Assert(subplan->testexpr == NULL);
10098 68 : break;
10099 4 : case ALL_SUBLINK:
10100 4 : appendStringInfoString(buf, "(ALL ");
10101 : Assert(subplan->testexpr != NULL);
10102 4 : break;
10103 154 : case ANY_SUBLINK:
10104 154 : appendStringInfoString(buf, "(ANY ");
10105 : Assert(subplan->testexpr != NULL);
10106 154 : break;
10107 4 : case ROWCOMPARE_SUBLINK:
10108 : /* Parenthesizing the testexpr seems sufficient */
10109 4 : appendStringInfoChar(buf, '(');
10110 : Assert(subplan->testexpr != NULL);
10111 4 : break;
10112 270 : case EXPR_SUBLINK:
10113 : /* No need to decorate these subplan references */
10114 270 : appendStringInfoChar(buf, '(');
10115 : Assert(subplan->testexpr == NULL);
10116 270 : break;
10117 17 : case MULTIEXPR_SUBLINK:
10118 : /* MULTIEXPR isn't executed in the normal way */
10119 17 : appendStringInfoString(buf, "(rescan ");
10120 : Assert(subplan->testexpr == NULL);
10121 17 : break;
10122 8 : case ARRAY_SUBLINK:
10123 8 : appendStringInfoString(buf, "ARRAY(");
10124 : Assert(subplan->testexpr == NULL);
10125 8 : break;
10126 0 : case CTE_SUBLINK:
10127 : /* This case is unreachable within expressions */
10128 0 : appendStringInfoString(buf, "CTE(");
10129 : Assert(subplan->testexpr == NULL);
10130 0 : break;
10131 : }
10132 :
10133 525 : if (subplan->testexpr != NULL)
10134 : {
10135 : deparse_namespace *dpns;
10136 :
10137 : /*
10138 : * Push SubPlan into ancestors list while deparsing
10139 : * testexpr, so that we can handle PARAM_EXEC references
10140 : * to the SubPlan's paramIds. (This makes it look like
10141 : * the SubPlan is an "ancestor" of the current plan node,
10142 : * which is a little weird, but it does no harm.) In this
10143 : * path, we don't need to mention the SubPlan explicitly,
10144 : * because the referencing Params will show its existence.
10145 : */
10146 162 : dpns = (deparse_namespace *) linitial(context->namespaces);
10147 162 : dpns->ancestors = lcons(subplan, dpns->ancestors);
10148 :
10149 162 : get_rule_expr(subplan->testexpr, context, showimplicit);
10150 162 : appendStringInfoChar(buf, ')');
10151 :
10152 162 : dpns->ancestors = list_delete_first(dpns->ancestors);
10153 : }
10154 : else
10155 : {
10156 : const char *nameprefix;
10157 :
10158 : /* No referencing Params, so show the SubPlan's name */
10159 363 : if (subplan->isInitPlan)
10160 0 : nameprefix = "InitPlan ";
10161 : else
10162 363 : nameprefix = "SubPlan ";
10163 363 : if (subplan->useHashTable)
10164 0 : appendStringInfo(buf, "hashed %s%s)",
10165 : nameprefix, subplan->plan_name);
10166 : else
10167 363 : appendStringInfo(buf, "%s%s)",
10168 : nameprefix, subplan->plan_name);
10169 : }
10170 : }
10171 525 : break;
10172 :
10173 0 : case T_AlternativeSubPlan:
10174 : {
10175 0 : AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
10176 : ListCell *lc;
10177 :
10178 : /*
10179 : * This case cannot be reached in normal usage, since no
10180 : * AlternativeSubPlan can appear either in parsetrees or
10181 : * finished plan trees. We keep it just in case somebody
10182 : * wants to use this code to print planner data structures.
10183 : */
10184 0 : appendStringInfoString(buf, "(alternatives: ");
10185 0 : foreach(lc, asplan->subplans)
10186 : {
10187 0 : SubPlan *splan = lfirst_node(SubPlan, lc);
10188 : const char *nameprefix;
10189 :
10190 0 : if (splan->isInitPlan)
10191 0 : nameprefix = "InitPlan ";
10192 : else
10193 0 : nameprefix = "SubPlan ";
10194 0 : if (splan->useHashTable)
10195 0 : appendStringInfo(buf, "hashed %s%s", nameprefix,
10196 : splan->plan_name);
10197 : else
10198 0 : appendStringInfo(buf, "%s%s", nameprefix,
10199 : splan->plan_name);
10200 0 : if (lnext(asplan->subplans, lc))
10201 0 : appendStringInfoString(buf, " or ");
10202 : }
10203 0 : appendStringInfoChar(buf, ')');
10204 : }
10205 0 : break;
10206 :
10207 926 : case T_FieldSelect:
10208 : {
10209 926 : FieldSelect *fselect = (FieldSelect *) node;
10210 926 : Node *arg = (Node *) fselect->arg;
10211 926 : int fno = fselect->fieldnum;
10212 : const char *fieldname;
10213 : bool need_parens;
10214 :
10215 : /*
10216 : * Parenthesize the argument unless it's a SubscriptingRef or
10217 : * another FieldSelect. Note in particular that it would be
10218 : * WRONG to not parenthesize a Var argument; simplicity is not
10219 : * the issue here, having the right number of names is.
10220 : */
10221 1828 : need_parens = !IsA(arg, SubscriptingRef) &&
10222 902 : !IsA(arg, FieldSelect);
10223 926 : if (need_parens)
10224 902 : appendStringInfoChar(buf, '(');
10225 926 : get_rule_expr(arg, context, true);
10226 926 : if (need_parens)
10227 902 : appendStringInfoChar(buf, ')');
10228 :
10229 : /*
10230 : * Get and print the field name.
10231 : */
10232 926 : fieldname = get_name_for_var_field((Var *) arg, fno,
10233 : 0, context);
10234 926 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
10235 : }
10236 926 : break;
10237 :
10238 4 : case T_FieldStore:
10239 : {
10240 4 : FieldStore *fstore = (FieldStore *) node;
10241 : bool need_parens;
10242 :
10243 : /*
10244 : * There is no good way to represent a FieldStore as real SQL,
10245 : * so decompilation of INSERT or UPDATE statements should
10246 : * always use processIndirection as part of the
10247 : * statement-level syntax. We should only get here when
10248 : * EXPLAIN tries to print the targetlist of a plan resulting
10249 : * from such a statement. The plan case is even harder than
10250 : * ordinary rules would be, because the planner tries to
10251 : * collapse multiple assignments to the same field or subfield
10252 : * into one FieldStore; so we can see a list of target fields
10253 : * not just one, and the arguments could be FieldStores
10254 : * themselves. We don't bother to try to print the target
10255 : * field names; we just print the source arguments, with a
10256 : * ROW() around them if there's more than one. This isn't
10257 : * terribly complete, but it's probably good enough for
10258 : * EXPLAIN's purposes; especially since anything more would be
10259 : * either hopelessly confusing or an even poorer
10260 : * representation of what the plan is actually doing.
10261 : */
10262 4 : need_parens = (list_length(fstore->newvals) != 1);
10263 4 : if (need_parens)
10264 4 : appendStringInfoString(buf, "ROW(");
10265 4 : get_rule_expr((Node *) fstore->newvals, context, showimplicit);
10266 4 : if (need_parens)
10267 4 : appendStringInfoChar(buf, ')');
10268 : }
10269 4 : break;
10270 :
10271 1929 : case T_RelabelType:
10272 : {
10273 1929 : RelabelType *relabel = (RelabelType *) node;
10274 1929 : Node *arg = (Node *) relabel->arg;
10275 :
10276 1929 : if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
10277 1743 : !showimplicit)
10278 : {
10279 : /* don't show the implicit cast */
10280 45 : get_rule_expr_paren(arg, context, false, node);
10281 : }
10282 : else
10283 : {
10284 1884 : get_coercion_expr(arg, context,
10285 : relabel->resulttype,
10286 : relabel->resulttypmod,
10287 : node);
10288 : }
10289 : }
10290 1929 : break;
10291 :
10292 497 : case T_CoerceViaIO:
10293 : {
10294 497 : CoerceViaIO *iocoerce = (CoerceViaIO *) node;
10295 497 : Node *arg = (Node *) iocoerce->arg;
10296 :
10297 497 : if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
10298 24 : !showimplicit)
10299 : {
10300 : /* don't show the implicit cast */
10301 24 : get_rule_expr_paren(arg, context, false, node);
10302 : }
10303 : else
10304 : {
10305 473 : get_coercion_expr(arg, context,
10306 : iocoerce->resulttype,
10307 : -1,
10308 : node);
10309 : }
10310 : }
10311 497 : break;
10312 :
10313 32 : case T_ArrayCoerceExpr:
10314 : {
10315 32 : ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
10316 32 : Node *arg = (Node *) acoerce->arg;
10317 :
10318 32 : if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
10319 32 : !showimplicit)
10320 : {
10321 : /* don't show the implicit cast */
10322 0 : get_rule_expr_paren(arg, context, false, node);
10323 : }
10324 : else
10325 : {
10326 32 : get_coercion_expr(arg, context,
10327 : acoerce->resulttype,
10328 : acoerce->resulttypmod,
10329 : node);
10330 : }
10331 : }
10332 32 : break;
10333 :
10334 53 : case T_ConvertRowtypeExpr:
10335 : {
10336 53 : ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
10337 53 : Node *arg = (Node *) convert->arg;
10338 :
10339 53 : if (convert->convertformat == COERCE_IMPLICIT_CAST &&
10340 49 : !showimplicit)
10341 : {
10342 : /* don't show the implicit cast */
10343 12 : get_rule_expr_paren(arg, context, false, node);
10344 : }
10345 : else
10346 : {
10347 41 : get_coercion_expr(arg, context,
10348 : convert->resulttype, -1,
10349 : node);
10350 : }
10351 : }
10352 53 : break;
10353 :
10354 122 : case T_CollateExpr:
10355 : {
10356 122 : CollateExpr *collate = (CollateExpr *) node;
10357 122 : Node *arg = (Node *) collate->arg;
10358 :
10359 122 : if (!PRETTY_PAREN(context))
10360 118 : appendStringInfoChar(buf, '(');
10361 122 : get_rule_expr_paren(arg, context, showimplicit, node);
10362 122 : appendStringInfo(buf, " COLLATE %s",
10363 : generate_collation_name(collate->collOid));
10364 122 : if (!PRETTY_PAREN(context))
10365 118 : appendStringInfoChar(buf, ')');
10366 : }
10367 122 : break;
10368 :
10369 457 : case T_CaseExpr:
10370 : {
10371 457 : CaseExpr *caseexpr = (CaseExpr *) node;
10372 : ListCell *temp;
10373 :
10374 457 : appendContextKeyword(context, "CASE",
10375 : 0, PRETTYINDENT_VAR, 0);
10376 457 : if (caseexpr->arg)
10377 : {
10378 172 : appendStringInfoChar(buf, ' ');
10379 172 : get_rule_expr((Node *) caseexpr->arg, context, true);
10380 : }
10381 1927 : foreach(temp, caseexpr->args)
10382 : {
10383 1470 : CaseWhen *when = (CaseWhen *) lfirst(temp);
10384 1470 : Node *w = (Node *) when->expr;
10385 :
10386 1470 : if (caseexpr->arg)
10387 : {
10388 : /*
10389 : * The parser should have produced WHEN clauses of the
10390 : * form "CaseTestExpr = RHS", possibly with an
10391 : * implicit coercion inserted above the CaseTestExpr.
10392 : * For accurate decompilation of rules it's essential
10393 : * that we show just the RHS. However in an
10394 : * expression that's been through the optimizer, the
10395 : * WHEN clause could be almost anything (since the
10396 : * equality operator could have been expanded into an
10397 : * inline function). If we don't recognize the form
10398 : * of the WHEN clause, just punt and display it as-is.
10399 : */
10400 590 : if (IsA(w, OpExpr))
10401 : {
10402 590 : List *args = ((OpExpr *) w)->args;
10403 :
10404 590 : if (list_length(args) == 2 &&
10405 590 : IsA(strip_implicit_coercions(linitial(args)),
10406 : CaseTestExpr))
10407 590 : w = (Node *) lsecond(args);
10408 : }
10409 : }
10410 :
10411 1470 : if (!PRETTY_INDENT(context))
10412 80 : appendStringInfoChar(buf, ' ');
10413 1470 : appendContextKeyword(context, "WHEN ",
10414 : 0, 0, 0);
10415 1470 : get_rule_expr(w, context, false);
10416 1470 : appendStringInfoString(buf, " THEN ");
10417 1470 : get_rule_expr((Node *) when->result, context, true);
10418 : }
10419 457 : if (!PRETTY_INDENT(context))
10420 75 : appendStringInfoChar(buf, ' ');
10421 457 : appendContextKeyword(context, "ELSE ",
10422 : 0, 0, 0);
10423 457 : get_rule_expr((Node *) caseexpr->defresult, context, true);
10424 457 : if (!PRETTY_INDENT(context))
10425 75 : appendStringInfoChar(buf, ' ');
10426 457 : appendContextKeyword(context, "END",
10427 : -PRETTYINDENT_VAR, 0, 0);
10428 : }
10429 457 : break;
10430 :
10431 0 : case T_CaseTestExpr:
10432 : {
10433 : /*
10434 : * Normally we should never get here, since for expressions
10435 : * that can contain this node type we attempt to avoid
10436 : * recursing to it. But in an optimized expression we might
10437 : * be unable to avoid that (see comments for CaseExpr). If we
10438 : * do see one, print it as CASE_TEST_EXPR.
10439 : */
10440 0 : appendStringInfoString(buf, "CASE_TEST_EXPR");
10441 : }
10442 0 : break;
10443 :
10444 367 : case T_ArrayExpr:
10445 : {
10446 367 : ArrayExpr *arrayexpr = (ArrayExpr *) node;
10447 :
10448 367 : appendStringInfoString(buf, "ARRAY[");
10449 367 : get_rule_expr((Node *) arrayexpr->elements, context, true);
10450 367 : appendStringInfoChar(buf, ']');
10451 :
10452 : /*
10453 : * If the array isn't empty, we assume its elements are
10454 : * coerced to the desired type. If it's empty, though, we
10455 : * need an explicit coercion to the array type.
10456 : */
10457 367 : if (arrayexpr->elements == NIL)
10458 4 : appendStringInfo(buf, "::%s",
10459 : format_type_with_typemod(arrayexpr->array_typeid, -1));
10460 : }
10461 367 : break;
10462 :
10463 142 : case T_RowExpr:
10464 : {
10465 142 : RowExpr *rowexpr = (RowExpr *) node;
10466 142 : TupleDesc tupdesc = NULL;
10467 : ListCell *arg;
10468 : int i;
10469 : char *sep;
10470 :
10471 : /*
10472 : * If it's a named type and not RECORD, we may have to skip
10473 : * dropped columns and/or claim there are NULLs for added
10474 : * columns.
10475 : */
10476 142 : if (rowexpr->row_typeid != RECORDOID)
10477 : {
10478 44 : tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
10479 : Assert(list_length(rowexpr->args) <= tupdesc->natts);
10480 : }
10481 :
10482 : /*
10483 : * SQL99 allows "ROW" to be omitted when there is more than
10484 : * one column, but for simplicity we always print it.
10485 : */
10486 142 : appendStringInfoString(buf, "ROW(");
10487 142 : sep = "";
10488 142 : i = 0;
10489 432 : foreach(arg, rowexpr->args)
10490 : {
10491 290 : Node *e = (Node *) lfirst(arg);
10492 :
10493 290 : if (tupdesc == NULL ||
10494 104 : !TupleDescCompactAttr(tupdesc, i)->attisdropped)
10495 : {
10496 290 : appendStringInfoString(buf, sep);
10497 : /* Whole-row Vars need special treatment here */
10498 290 : get_rule_expr_toplevel(e, context, true);
10499 290 : sep = ", ";
10500 : }
10501 290 : i++;
10502 : }
10503 142 : if (tupdesc != NULL)
10504 : {
10505 44 : while (i < tupdesc->natts)
10506 : {
10507 0 : if (!TupleDescCompactAttr(tupdesc, i)->attisdropped)
10508 : {
10509 0 : appendStringInfoString(buf, sep);
10510 0 : appendStringInfoString(buf, "NULL");
10511 0 : sep = ", ";
10512 : }
10513 0 : i++;
10514 : }
10515 :
10516 44 : ReleaseTupleDesc(tupdesc);
10517 : }
10518 142 : appendStringInfoChar(buf, ')');
10519 142 : if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
10520 24 : appendStringInfo(buf, "::%s",
10521 : format_type_with_typemod(rowexpr->row_typeid, -1));
10522 : }
10523 142 : break;
10524 :
10525 80 : case T_RowCompareExpr:
10526 : {
10527 80 : RowCompareExpr *rcexpr = (RowCompareExpr *) node;
10528 :
10529 : /*
10530 : * SQL99 allows "ROW" to be omitted when there is more than
10531 : * one column, but for simplicity we always print it. Within
10532 : * a ROW expression, whole-row Vars need special treatment, so
10533 : * use get_rule_list_toplevel.
10534 : */
10535 80 : appendStringInfoString(buf, "(ROW(");
10536 80 : get_rule_list_toplevel(rcexpr->largs, context, true);
10537 :
10538 : /*
10539 : * We assume that the name of the first-column operator will
10540 : * do for all the rest too. This is definitely open to
10541 : * failure, eg if some but not all operators were renamed
10542 : * since the construct was parsed, but there seems no way to
10543 : * be perfect.
10544 : */
10545 80 : appendStringInfo(buf, ") %s ROW(",
10546 80 : generate_operator_name(linitial_oid(rcexpr->opnos),
10547 80 : exprType(linitial(rcexpr->largs)),
10548 80 : exprType(linitial(rcexpr->rargs))));
10549 80 : get_rule_list_toplevel(rcexpr->rargs, context, true);
10550 80 : appendStringInfoString(buf, "))");
10551 : }
10552 80 : break;
10553 :
10554 817 : case T_CoalesceExpr:
10555 : {
10556 817 : CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
10557 :
10558 817 : appendStringInfoString(buf, "COALESCE(");
10559 817 : get_rule_expr((Node *) coalesceexpr->args, context, true);
10560 817 : appendStringInfoChar(buf, ')');
10561 : }
10562 817 : break;
10563 :
10564 28 : case T_MinMaxExpr:
10565 : {
10566 28 : MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
10567 :
10568 28 : switch (minmaxexpr->op)
10569 : {
10570 8 : case IS_GREATEST:
10571 8 : appendStringInfoString(buf, "GREATEST(");
10572 8 : break;
10573 20 : case IS_LEAST:
10574 20 : appendStringInfoString(buf, "LEAST(");
10575 20 : break;
10576 : }
10577 28 : get_rule_expr((Node *) minmaxexpr->args, context, true);
10578 28 : appendStringInfoChar(buf, ')');
10579 : }
10580 28 : break;
10581 :
10582 449 : case T_SQLValueFunction:
10583 : {
10584 449 : SQLValueFunction *svf = (SQLValueFunction *) node;
10585 :
10586 : /*
10587 : * Note: this code knows that typmod for time, timestamp, and
10588 : * timestamptz just prints as integer.
10589 : */
10590 449 : switch (svf->op)
10591 : {
10592 69 : case SVFOP_CURRENT_DATE:
10593 69 : appendStringInfoString(buf, "CURRENT_DATE");
10594 69 : break;
10595 8 : case SVFOP_CURRENT_TIME:
10596 8 : appendStringInfoString(buf, "CURRENT_TIME");
10597 8 : break;
10598 8 : case SVFOP_CURRENT_TIME_N:
10599 8 : appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
10600 8 : break;
10601 8 : case SVFOP_CURRENT_TIMESTAMP:
10602 8 : appendStringInfoString(buf, "CURRENT_TIMESTAMP");
10603 8 : break;
10604 75 : case SVFOP_CURRENT_TIMESTAMP_N:
10605 75 : appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
10606 : svf->typmod);
10607 75 : break;
10608 8 : case SVFOP_LOCALTIME:
10609 8 : appendStringInfoString(buf, "LOCALTIME");
10610 8 : break;
10611 8 : case SVFOP_LOCALTIME_N:
10612 8 : appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
10613 8 : break;
10614 20 : case SVFOP_LOCALTIMESTAMP:
10615 20 : appendStringInfoString(buf, "LOCALTIMESTAMP");
10616 20 : break;
10617 12 : case SVFOP_LOCALTIMESTAMP_N:
10618 12 : appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
10619 : svf->typmod);
10620 12 : break;
10621 8 : case SVFOP_CURRENT_ROLE:
10622 8 : appendStringInfoString(buf, "CURRENT_ROLE");
10623 8 : break;
10624 181 : case SVFOP_CURRENT_USER:
10625 181 : appendStringInfoString(buf, "CURRENT_USER");
10626 181 : break;
10627 8 : case SVFOP_USER:
10628 8 : appendStringInfoString(buf, "USER");
10629 8 : break;
10630 20 : case SVFOP_SESSION_USER:
10631 20 : appendStringInfoString(buf, "SESSION_USER");
10632 20 : break;
10633 8 : case SVFOP_CURRENT_CATALOG:
10634 8 : appendStringInfoString(buf, "CURRENT_CATALOG");
10635 8 : break;
10636 8 : case SVFOP_CURRENT_SCHEMA:
10637 8 : appendStringInfoString(buf, "CURRENT_SCHEMA");
10638 8 : break;
10639 : }
10640 : }
10641 449 : break;
10642 :
10643 99 : case T_XmlExpr:
10644 : {
10645 99 : XmlExpr *xexpr = (XmlExpr *) node;
10646 99 : bool needcomma = false;
10647 : ListCell *arg;
10648 : ListCell *narg;
10649 : Const *con;
10650 :
10651 99 : switch (xexpr->op)
10652 : {
10653 9 : case IS_XMLCONCAT:
10654 9 : appendStringInfoString(buf, "XMLCONCAT(");
10655 9 : break;
10656 18 : case IS_XMLELEMENT:
10657 18 : appendStringInfoString(buf, "XMLELEMENT(");
10658 18 : break;
10659 9 : case IS_XMLFOREST:
10660 9 : appendStringInfoString(buf, "XMLFOREST(");
10661 9 : break;
10662 9 : case IS_XMLPARSE:
10663 9 : appendStringInfoString(buf, "XMLPARSE(");
10664 9 : break;
10665 9 : case IS_XMLPI:
10666 9 : appendStringInfoString(buf, "XMLPI(");
10667 9 : break;
10668 9 : case IS_XMLROOT:
10669 9 : appendStringInfoString(buf, "XMLROOT(");
10670 9 : break;
10671 36 : case IS_XMLSERIALIZE:
10672 36 : appendStringInfoString(buf, "XMLSERIALIZE(");
10673 36 : break;
10674 0 : case IS_DOCUMENT:
10675 0 : break;
10676 : }
10677 99 : if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
10678 : {
10679 45 : if (xexpr->xmloption == XMLOPTION_DOCUMENT)
10680 18 : appendStringInfoString(buf, "DOCUMENT ");
10681 : else
10682 27 : appendStringInfoString(buf, "CONTENT ");
10683 : }
10684 99 : if (xexpr->name)
10685 : {
10686 27 : appendStringInfo(buf, "NAME %s",
10687 27 : quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));
10688 27 : needcomma = true;
10689 : }
10690 99 : if (xexpr->named_args)
10691 : {
10692 18 : if (xexpr->op != IS_XMLFOREST)
10693 : {
10694 9 : if (needcomma)
10695 9 : appendStringInfoString(buf, ", ");
10696 9 : appendStringInfoString(buf, "XMLATTRIBUTES(");
10697 9 : needcomma = false;
10698 : }
10699 63 : forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
10700 : {
10701 45 : Node *e = (Node *) lfirst(arg);
10702 45 : char *argname = strVal(lfirst(narg));
10703 :
10704 45 : if (needcomma)
10705 27 : appendStringInfoString(buf, ", ");
10706 45 : get_rule_expr(e, context, true);
10707 45 : appendStringInfo(buf, " AS %s",
10708 45 : quote_identifier(map_xml_name_to_sql_identifier(argname)));
10709 45 : needcomma = true;
10710 : }
10711 18 : if (xexpr->op != IS_XMLFOREST)
10712 9 : appendStringInfoChar(buf, ')');
10713 : }
10714 99 : if (xexpr->args)
10715 : {
10716 90 : if (needcomma)
10717 27 : appendStringInfoString(buf, ", ");
10718 90 : switch (xexpr->op)
10719 : {
10720 72 : case IS_XMLCONCAT:
10721 : case IS_XMLELEMENT:
10722 : case IS_XMLFOREST:
10723 : case IS_XMLPI:
10724 : case IS_XMLSERIALIZE:
10725 : /* no extra decoration needed */
10726 72 : get_rule_expr((Node *) xexpr->args, context, true);
10727 72 : break;
10728 9 : case IS_XMLPARSE:
10729 : Assert(list_length(xexpr->args) == 2);
10730 :
10731 9 : get_rule_expr((Node *) linitial(xexpr->args),
10732 : context, true);
10733 :
10734 9 : con = lsecond_node(Const, xexpr->args);
10735 : Assert(!con->constisnull);
10736 9 : if (DatumGetBool(con->constvalue))
10737 0 : appendStringInfoString(buf,
10738 : " PRESERVE WHITESPACE");
10739 : else
10740 9 : appendStringInfoString(buf,
10741 : " STRIP WHITESPACE");
10742 9 : break;
10743 9 : case IS_XMLROOT:
10744 : Assert(list_length(xexpr->args) == 3);
10745 :
10746 9 : get_rule_expr((Node *) linitial(xexpr->args),
10747 : context, true);
10748 :
10749 9 : appendStringInfoString(buf, ", VERSION ");
10750 9 : con = (Const *) lsecond(xexpr->args);
10751 9 : if (IsA(con, Const) &&
10752 9 : con->constisnull)
10753 9 : appendStringInfoString(buf, "NO VALUE");
10754 : else
10755 0 : get_rule_expr((Node *) con, context, false);
10756 :
10757 9 : con = lthird_node(Const, xexpr->args);
10758 9 : if (con->constisnull)
10759 : /* suppress STANDALONE NO VALUE */ ;
10760 : else
10761 : {
10762 9 : switch (DatumGetInt32(con->constvalue))
10763 : {
10764 9 : case XML_STANDALONE_YES:
10765 9 : appendStringInfoString(buf,
10766 : ", STANDALONE YES");
10767 9 : break;
10768 0 : case XML_STANDALONE_NO:
10769 0 : appendStringInfoString(buf,
10770 : ", STANDALONE NO");
10771 0 : break;
10772 0 : case XML_STANDALONE_NO_VALUE:
10773 0 : appendStringInfoString(buf,
10774 : ", STANDALONE NO VALUE");
10775 0 : break;
10776 0 : default:
10777 0 : break;
10778 : }
10779 : }
10780 9 : break;
10781 0 : case IS_DOCUMENT:
10782 0 : get_rule_expr_paren((Node *) xexpr->args, context, false, node);
10783 0 : break;
10784 : }
10785 : }
10786 99 : if (xexpr->op == IS_XMLSERIALIZE)
10787 : {
10788 36 : appendStringInfo(buf, " AS %s",
10789 : format_type_with_typemod(xexpr->type,
10790 : xexpr->typmod));
10791 36 : if (xexpr->indent)
10792 9 : appendStringInfoString(buf, " INDENT");
10793 : else
10794 27 : appendStringInfoString(buf, " NO INDENT");
10795 : }
10796 :
10797 99 : if (xexpr->op == IS_DOCUMENT)
10798 0 : appendStringInfoString(buf, " IS DOCUMENT");
10799 : else
10800 99 : appendStringInfoChar(buf, ')');
10801 : }
10802 99 : break;
10803 :
10804 1830 : case T_NullTest:
10805 : {
10806 1830 : NullTest *ntest = (NullTest *) node;
10807 :
10808 1830 : if (!PRETTY_PAREN(context))
10809 1790 : appendStringInfoChar(buf, '(');
10810 1830 : get_rule_expr_paren((Node *) ntest->arg, context, true, node);
10811 :
10812 : /*
10813 : * For scalar inputs, we prefer to print as IS [NOT] NULL,
10814 : * which is shorter and traditional. If it's a rowtype input
10815 : * but we're applying a scalar test, must print IS [NOT]
10816 : * DISTINCT FROM NULL to be semantically correct.
10817 : */
10818 1830 : if (ntest->argisrow ||
10819 1792 : !type_is_rowtype(exprType((Node *) ntest->arg)))
10820 : {
10821 1810 : switch (ntest->nulltesttype)
10822 : {
10823 576 : case IS_NULL:
10824 576 : appendStringInfoString(buf, " IS NULL");
10825 576 : break;
10826 1234 : case IS_NOT_NULL:
10827 1234 : appendStringInfoString(buf, " IS NOT NULL");
10828 1234 : break;
10829 0 : default:
10830 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10831 : (int) ntest->nulltesttype);
10832 : }
10833 : }
10834 : else
10835 : {
10836 20 : switch (ntest->nulltesttype)
10837 : {
10838 8 : case IS_NULL:
10839 8 : appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
10840 8 : break;
10841 12 : case IS_NOT_NULL:
10842 12 : appendStringInfoString(buf, " IS DISTINCT FROM NULL");
10843 12 : break;
10844 0 : default:
10845 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10846 : (int) ntest->nulltesttype);
10847 : }
10848 : }
10849 1830 : if (!PRETTY_PAREN(context))
10850 1790 : appendStringInfoChar(buf, ')');
10851 : }
10852 1830 : break;
10853 :
10854 208 : case T_BooleanTest:
10855 : {
10856 208 : BooleanTest *btest = (BooleanTest *) node;
10857 :
10858 208 : if (!PRETTY_PAREN(context))
10859 208 : appendStringInfoChar(buf, '(');
10860 208 : get_rule_expr_paren((Node *) btest->arg, context, false, node);
10861 208 : switch (btest->booltesttype)
10862 : {
10863 24 : case IS_TRUE:
10864 24 : appendStringInfoString(buf, " IS TRUE");
10865 24 : break;
10866 92 : case IS_NOT_TRUE:
10867 92 : appendStringInfoString(buf, " IS NOT TRUE");
10868 92 : break;
10869 0 : case IS_FALSE:
10870 0 : appendStringInfoString(buf, " IS FALSE");
10871 0 : break;
10872 36 : case IS_NOT_FALSE:
10873 36 : appendStringInfoString(buf, " IS NOT FALSE");
10874 36 : break;
10875 20 : case IS_UNKNOWN:
10876 20 : appendStringInfoString(buf, " IS UNKNOWN");
10877 20 : break;
10878 36 : case IS_NOT_UNKNOWN:
10879 36 : appendStringInfoString(buf, " IS NOT UNKNOWN");
10880 36 : break;
10881 0 : default:
10882 0 : elog(ERROR, "unrecognized booltesttype: %d",
10883 : (int) btest->booltesttype);
10884 : }
10885 208 : if (!PRETTY_PAREN(context))
10886 208 : appendStringInfoChar(buf, ')');
10887 : }
10888 208 : break;
10889 :
10890 79 : case T_CoerceToDomain:
10891 : {
10892 79 : CoerceToDomain *ctest = (CoerceToDomain *) node;
10893 79 : Node *arg = (Node *) ctest->arg;
10894 :
10895 79 : if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
10896 36 : !showimplicit)
10897 : {
10898 : /* don't show the implicit cast */
10899 28 : get_rule_expr(arg, context, false);
10900 : }
10901 : else
10902 : {
10903 51 : get_coercion_expr(arg, context,
10904 : ctest->resulttype,
10905 : ctest->resulttypmod,
10906 : node);
10907 : }
10908 : }
10909 79 : break;
10910 :
10911 256 : case T_CoerceToDomainValue:
10912 256 : appendStringInfoString(buf, "VALUE");
10913 256 : break;
10914 :
10915 44 : case T_SetToDefault:
10916 44 : appendStringInfoString(buf, "DEFAULT");
10917 44 : break;
10918 :
10919 16 : case T_CurrentOfExpr:
10920 : {
10921 16 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
10922 :
10923 16 : if (cexpr->cursor_name)
10924 16 : appendStringInfo(buf, "CURRENT OF %s",
10925 16 : quote_identifier(cexpr->cursor_name));
10926 : else
10927 0 : appendStringInfo(buf, "CURRENT OF $%d",
10928 : cexpr->cursor_param);
10929 : }
10930 16 : break;
10931 :
10932 0 : case T_NextValueExpr:
10933 : {
10934 0 : NextValueExpr *nvexpr = (NextValueExpr *) node;
10935 :
10936 : /*
10937 : * This isn't exactly nextval(), but that seems close enough
10938 : * for EXPLAIN's purposes.
10939 : */
10940 0 : appendStringInfoString(buf, "nextval(");
10941 0 : simple_quote_literal(buf,
10942 0 : generate_relation_name(nvexpr->seqid,
10943 : NIL));
10944 0 : appendStringInfoChar(buf, ')');
10945 : }
10946 0 : break;
10947 :
10948 20 : case T_InferenceElem:
10949 : {
10950 20 : InferenceElem *iexpr = (InferenceElem *) node;
10951 : bool save_varprefix;
10952 : bool need_parens;
10953 :
10954 : /*
10955 : * InferenceElem can only refer to target relation, so a
10956 : * prefix is not useful, and indeed would cause parse errors.
10957 : */
10958 20 : save_varprefix = context->varprefix;
10959 20 : context->varprefix = false;
10960 :
10961 : /*
10962 : * Parenthesize the element unless it's a simple Var or a bare
10963 : * function call. Follows pg_get_indexdef_worker().
10964 : */
10965 20 : need_parens = !IsA(iexpr->expr, Var);
10966 20 : if (IsA(iexpr->expr, FuncExpr) &&
10967 0 : ((FuncExpr *) iexpr->expr)->funcformat ==
10968 : COERCE_EXPLICIT_CALL)
10969 0 : need_parens = false;
10970 :
10971 20 : if (need_parens)
10972 0 : appendStringInfoChar(buf, '(');
10973 20 : get_rule_expr((Node *) iexpr->expr,
10974 : context, false);
10975 20 : if (need_parens)
10976 0 : appendStringInfoChar(buf, ')');
10977 :
10978 20 : context->varprefix = save_varprefix;
10979 :
10980 20 : if (iexpr->infercollid)
10981 8 : appendStringInfo(buf, " COLLATE %s",
10982 : generate_collation_name(iexpr->infercollid));
10983 :
10984 : /* Add the operator class name, if not default */
10985 20 : if (iexpr->inferopclass)
10986 : {
10987 8 : Oid inferopclass = iexpr->inferopclass;
10988 8 : Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
10989 :
10990 8 : get_opclass_name(inferopclass, inferopcinputtype, buf);
10991 : }
10992 : }
10993 20 : break;
10994 :
10995 8 : case T_ReturningExpr:
10996 : {
10997 8 : ReturningExpr *retExpr = (ReturningExpr *) node;
10998 :
10999 : /*
11000 : * We cannot see a ReturningExpr in rule deparsing, only while
11001 : * EXPLAINing a query plan (ReturningExpr nodes are only ever
11002 : * adding during query rewriting). Just display the expression
11003 : * returned (an expanded view column).
11004 : */
11005 8 : get_rule_expr((Node *) retExpr->retexpr, context, showimplicit);
11006 : }
11007 8 : break;
11008 :
11009 2746 : case T_PartitionBoundSpec:
11010 : {
11011 2746 : PartitionBoundSpec *spec = (PartitionBoundSpec *) node;
11012 : ListCell *cell;
11013 : char *sep;
11014 :
11015 2746 : if (spec->is_default)
11016 : {
11017 161 : appendStringInfoString(buf, "DEFAULT");
11018 161 : break;
11019 : }
11020 :
11021 2585 : switch (spec->strategy)
11022 : {
11023 161 : case PARTITION_STRATEGY_HASH:
11024 : Assert(spec->modulus > 0 && spec->remainder >= 0);
11025 : Assert(spec->modulus > spec->remainder);
11026 :
11027 161 : appendStringInfoString(buf, "FOR VALUES");
11028 161 : appendStringInfo(buf, " WITH (modulus %d, remainder %d)",
11029 : spec->modulus, spec->remainder);
11030 161 : break;
11031 :
11032 865 : case PARTITION_STRATEGY_LIST:
11033 : Assert(spec->listdatums != NIL);
11034 :
11035 865 : appendStringInfoString(buf, "FOR VALUES IN (");
11036 865 : sep = "";
11037 2358 : foreach(cell, spec->listdatums)
11038 : {
11039 1493 : Const *val = lfirst_node(Const, cell);
11040 :
11041 1493 : appendStringInfoString(buf, sep);
11042 1493 : get_const_expr(val, context, -1);
11043 1493 : sep = ", ";
11044 : }
11045 :
11046 865 : appendStringInfoChar(buf, ')');
11047 865 : break;
11048 :
11049 1559 : case PARTITION_STRATEGY_RANGE:
11050 : Assert(spec->lowerdatums != NIL &&
11051 : spec->upperdatums != NIL &&
11052 : list_length(spec->lowerdatums) ==
11053 : list_length(spec->upperdatums));
11054 :
11055 1559 : appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
11056 : get_range_partbound_string(spec->lowerdatums),
11057 : get_range_partbound_string(spec->upperdatums));
11058 1559 : break;
11059 :
11060 0 : default:
11061 0 : elog(ERROR, "unrecognized partition strategy: %d",
11062 : (int) spec->strategy);
11063 : break;
11064 : }
11065 : }
11066 2585 : break;
11067 :
11068 104 : case T_JsonValueExpr:
11069 : {
11070 104 : JsonValueExpr *jve = (JsonValueExpr *) node;
11071 :
11072 104 : get_rule_expr((Node *) jve->raw_expr, context, false);
11073 104 : get_json_format(jve->format, context->buf);
11074 : }
11075 104 : break;
11076 :
11077 124 : case T_JsonConstructorExpr:
11078 124 : get_json_constructor((JsonConstructorExpr *) node, context, false);
11079 124 : break;
11080 :
11081 52 : case T_JsonIsPredicate:
11082 : {
11083 52 : JsonIsPredicate *pred = (JsonIsPredicate *) node;
11084 :
11085 52 : if (!PRETTY_PAREN(context))
11086 20 : appendStringInfoChar(context->buf, '(');
11087 :
11088 52 : get_rule_expr_paren(pred->expr, context, true, node);
11089 :
11090 52 : appendStringInfoString(context->buf, " IS JSON");
11091 :
11092 : /* TODO: handle FORMAT clause */
11093 :
11094 52 : switch (pred->item_type)
11095 : {
11096 8 : case JS_TYPE_SCALAR:
11097 8 : appendStringInfoString(context->buf, " SCALAR");
11098 8 : break;
11099 8 : case JS_TYPE_ARRAY:
11100 8 : appendStringInfoString(context->buf, " ARRAY");
11101 8 : break;
11102 8 : case JS_TYPE_OBJECT:
11103 8 : appendStringInfoString(context->buf, " OBJECT");
11104 8 : break;
11105 28 : default:
11106 28 : break;
11107 : }
11108 :
11109 52 : if (pred->unique_keys)
11110 20 : appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
11111 :
11112 52 : if (!PRETTY_PAREN(context))
11113 20 : appendStringInfoChar(context->buf, ')');
11114 : }
11115 52 : break;
11116 :
11117 40 : case T_JsonExpr:
11118 : {
11119 40 : JsonExpr *jexpr = (JsonExpr *) node;
11120 :
11121 40 : switch (jexpr->op)
11122 : {
11123 8 : case JSON_EXISTS_OP:
11124 8 : appendStringInfoString(buf, "JSON_EXISTS(");
11125 8 : break;
11126 24 : case JSON_QUERY_OP:
11127 24 : appendStringInfoString(buf, "JSON_QUERY(");
11128 24 : break;
11129 8 : case JSON_VALUE_OP:
11130 8 : appendStringInfoString(buf, "JSON_VALUE(");
11131 8 : break;
11132 0 : default:
11133 0 : elog(ERROR, "unrecognized JsonExpr op: %d",
11134 : (int) jexpr->op);
11135 : }
11136 :
11137 40 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
11138 :
11139 40 : appendStringInfoString(buf, ", ");
11140 :
11141 40 : get_json_path_spec(jexpr->path_spec, context, showimplicit);
11142 :
11143 40 : if (jexpr->passing_values)
11144 : {
11145 : ListCell *lc1,
11146 : *lc2;
11147 8 : bool needcomma = false;
11148 :
11149 8 : appendStringInfoString(buf, " PASSING ");
11150 :
11151 32 : forboth(lc1, jexpr->passing_names,
11152 : lc2, jexpr->passing_values)
11153 : {
11154 24 : if (needcomma)
11155 16 : appendStringInfoString(buf, ", ");
11156 24 : needcomma = true;
11157 :
11158 24 : get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
11159 24 : appendStringInfo(buf, " AS %s",
11160 24 : quote_identifier(lfirst_node(String, lc1)->sval));
11161 : }
11162 : }
11163 :
11164 40 : if (jexpr->op != JSON_EXISTS_OP ||
11165 8 : jexpr->returning->typid != BOOLOID)
11166 32 : get_json_returning(jexpr->returning, context->buf,
11167 32 : jexpr->op == JSON_QUERY_OP);
11168 :
11169 40 : get_json_expr_options(jexpr, context,
11170 40 : jexpr->op != JSON_EXISTS_OP ?
11171 : JSON_BEHAVIOR_NULL :
11172 : JSON_BEHAVIOR_FALSE);
11173 :
11174 40 : appendStringInfoChar(buf, ')');
11175 : }
11176 40 : break;
11177 :
11178 1826 : case T_List:
11179 : {
11180 : char *sep;
11181 : ListCell *l;
11182 :
11183 1826 : sep = "";
11184 5180 : foreach(l, (List *) node)
11185 : {
11186 3354 : appendStringInfoString(buf, sep);
11187 3354 : get_rule_expr((Node *) lfirst(l), context, showimplicit);
11188 3354 : sep = ", ";
11189 : }
11190 : }
11191 1826 : break;
11192 :
11193 48 : case T_TableFunc:
11194 48 : get_tablefunc((TableFunc *) node, context, showimplicit);
11195 48 : break;
11196 :
11197 37 : case T_GraphPropertyRef:
11198 : {
11199 37 : GraphPropertyRef *gpr = (GraphPropertyRef *) node;
11200 :
11201 37 : appendStringInfo(buf, "%s.%s", quote_identifier(gpr->elvarname), quote_identifier(get_propgraph_property_name(gpr->propid)));
11202 37 : break;
11203 : }
11204 :
11205 0 : default:
11206 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
11207 : break;
11208 : }
11209 : }
11210 :
11211 : /*
11212 : * get_rule_expr_toplevel - Parse back a toplevel expression
11213 : *
11214 : * Same as get_rule_expr(), except that if the expr is just a Var, we pass
11215 : * istoplevel = true not false to get_variable(). This causes whole-row Vars
11216 : * to get printed with decoration that will prevent expansion of "*".
11217 : * We need to use this in contexts such as ROW() and VALUES(), where the
11218 : * parser would expand "foo.*" appearing at top level. (In principle we'd
11219 : * use this in get_target_list() too, but that has additional worries about
11220 : * whether to print AS, so it needs to invoke get_variable() directly anyway.)
11221 : */
11222 : static void
11223 1967 : get_rule_expr_toplevel(Node *node, deparse_context *context,
11224 : bool showimplicit)
11225 : {
11226 1967 : if (node && IsA(node, Var))
11227 794 : (void) get_variable((Var *) node, 0, true, context);
11228 : else
11229 1173 : get_rule_expr(node, context, showimplicit);
11230 1967 : }
11231 :
11232 : /*
11233 : * get_rule_list_toplevel - Parse back a list of toplevel expressions
11234 : *
11235 : * Apply get_rule_expr_toplevel() to each element of a List.
11236 : *
11237 : * This adds commas between the expressions, but caller is responsible
11238 : * for printing surrounding decoration.
11239 : */
11240 : static void
11241 318 : get_rule_list_toplevel(List *lst, deparse_context *context,
11242 : bool showimplicit)
11243 : {
11244 : const char *sep;
11245 : ListCell *lc;
11246 :
11247 318 : sep = "";
11248 1063 : foreach(lc, lst)
11249 : {
11250 745 : Node *e = (Node *) lfirst(lc);
11251 :
11252 745 : appendStringInfoString(context->buf, sep);
11253 745 : get_rule_expr_toplevel(e, context, showimplicit);
11254 745 : sep = ", ";
11255 : }
11256 318 : }
11257 :
11258 : /*
11259 : * get_rule_expr_funccall - Parse back a function-call expression
11260 : *
11261 : * Same as get_rule_expr(), except that we guarantee that the output will
11262 : * look like a function call, or like one of the things the grammar treats as
11263 : * equivalent to a function call (see the func_expr_windowless production).
11264 : * This is needed in places where the grammar uses func_expr_windowless and
11265 : * you can't substitute a parenthesized a_expr. If what we have isn't going
11266 : * to look like a function call, wrap it in a dummy CAST() expression, which
11267 : * will satisfy the grammar --- and, indeed, is likely what the user wrote to
11268 : * produce such a thing.
11269 : */
11270 : static void
11271 565 : get_rule_expr_funccall(Node *node, deparse_context *context,
11272 : bool showimplicit)
11273 : {
11274 565 : if (looks_like_function(node))
11275 557 : get_rule_expr(node, context, showimplicit);
11276 : else
11277 : {
11278 8 : StringInfo buf = context->buf;
11279 :
11280 8 : appendStringInfoString(buf, "CAST(");
11281 : /* no point in showing any top-level implicit cast */
11282 8 : get_rule_expr(node, context, false);
11283 8 : appendStringInfo(buf, " AS %s)",
11284 : format_type_with_typemod(exprType(node),
11285 : exprTypmod(node)));
11286 : }
11287 565 : }
11288 :
11289 : /*
11290 : * Helper function to identify node types that satisfy func_expr_windowless.
11291 : * If in doubt, "false" is always a safe answer.
11292 : */
11293 : static bool
11294 1294 : looks_like_function(Node *node)
11295 : {
11296 1294 : if (node == NULL)
11297 0 : return false; /* probably shouldn't happen */
11298 1294 : switch (nodeTag(node))
11299 : {
11300 577 : case T_FuncExpr:
11301 : /* OK, unless it's going to deparse as a cast */
11302 589 : return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
11303 12 : ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
11304 72 : case T_NullIfExpr:
11305 : case T_CoalesceExpr:
11306 : case T_MinMaxExpr:
11307 : case T_SQLValueFunction:
11308 : case T_XmlExpr:
11309 : case T_JsonExpr:
11310 : /* these are all accepted by func_expr_common_subexpr */
11311 72 : return true;
11312 645 : default:
11313 645 : break;
11314 : }
11315 645 : return false;
11316 : }
11317 :
11318 :
11319 : /*
11320 : * get_oper_expr - Parse back an OpExpr node
11321 : */
11322 : static void
11323 40578 : get_oper_expr(OpExpr *expr, deparse_context *context)
11324 : {
11325 40578 : StringInfo buf = context->buf;
11326 40578 : Oid opno = expr->opno;
11327 40578 : List *args = expr->args;
11328 :
11329 40578 : if (!PRETTY_PAREN(context))
11330 38966 : appendStringInfoChar(buf, '(');
11331 40578 : if (list_length(args) == 2)
11332 : {
11333 : /* binary operator */
11334 40558 : Node *arg1 = (Node *) linitial(args);
11335 40558 : Node *arg2 = (Node *) lsecond(args);
11336 :
11337 40558 : get_rule_expr_paren(arg1, context, true, (Node *) expr);
11338 40558 : appendStringInfo(buf, " %s ",
11339 : generate_operator_name(opno,
11340 : exprType(arg1),
11341 : exprType(arg2)));
11342 40558 : get_rule_expr_paren(arg2, context, true, (Node *) expr);
11343 : }
11344 : else
11345 : {
11346 : /* prefix operator */
11347 20 : Node *arg = (Node *) linitial(args);
11348 :
11349 20 : appendStringInfo(buf, "%s ",
11350 : generate_operator_name(opno,
11351 : InvalidOid,
11352 : exprType(arg)));
11353 20 : get_rule_expr_paren(arg, context, true, (Node *) expr);
11354 : }
11355 40578 : if (!PRETTY_PAREN(context))
11356 38966 : appendStringInfoChar(buf, ')');
11357 40578 : }
11358 :
11359 : /*
11360 : * get_func_expr - Parse back a FuncExpr node
11361 : */
11362 : static void
11363 8127 : get_func_expr(FuncExpr *expr, deparse_context *context,
11364 : bool showimplicit)
11365 : {
11366 8127 : StringInfo buf = context->buf;
11367 8127 : Oid funcoid = expr->funcid;
11368 : Oid argtypes[FUNC_MAX_ARGS];
11369 : int nargs;
11370 : List *argnames;
11371 : bool use_variadic;
11372 : ListCell *l;
11373 :
11374 : /*
11375 : * If the function call came from an implicit coercion, then just show the
11376 : * first argument --- unless caller wants to see implicit coercions.
11377 : */
11378 8127 : if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
11379 : {
11380 765 : get_rule_expr_paren((Node *) linitial(expr->args), context,
11381 : false, (Node *) expr);
11382 2021 : return;
11383 : }
11384 :
11385 : /*
11386 : * If the function call came from a cast, then show the first argument
11387 : * plus an explicit cast operation.
11388 : */
11389 7362 : if (expr->funcformat == COERCE_EXPLICIT_CAST ||
11390 6905 : expr->funcformat == COERCE_IMPLICIT_CAST)
11391 : {
11392 1140 : Node *arg = linitial(expr->args);
11393 1140 : Oid rettype = expr->funcresulttype;
11394 : int32 coercedTypmod;
11395 :
11396 : /* Get the typmod if this is a length-coercion function */
11397 1140 : (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
11398 :
11399 1140 : get_coercion_expr(arg, context,
11400 : rettype, coercedTypmod,
11401 : (Node *) expr);
11402 :
11403 1140 : return;
11404 : }
11405 :
11406 : /*
11407 : * If the function was called using one of the SQL spec's random special
11408 : * syntaxes, try to reproduce that. If we don't recognize the function,
11409 : * fall through.
11410 : */
11411 6222 : if (expr->funcformat == COERCE_SQL_SYNTAX)
11412 : {
11413 120 : if (get_func_sql_syntax(expr, context))
11414 116 : return;
11415 : }
11416 :
11417 : /*
11418 : * Normal function: display as proname(args). First we need to extract
11419 : * the argument datatypes.
11420 : */
11421 6106 : if (list_length(expr->args) > FUNC_MAX_ARGS)
11422 0 : ereport(ERROR,
11423 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
11424 : errmsg("too many arguments")));
11425 6106 : nargs = 0;
11426 6106 : argnames = NIL;
11427 12792 : foreach(l, expr->args)
11428 : {
11429 6686 : Node *arg = (Node *) lfirst(l);
11430 :
11431 6686 : if (IsA(arg, NamedArgExpr))
11432 28 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
11433 6686 : argtypes[nargs] = exprType(arg);
11434 6686 : nargs++;
11435 : }
11436 :
11437 6106 : appendStringInfo(buf, "%s(",
11438 : generate_function_name(funcoid, nargs,
11439 : argnames, argtypes,
11440 6106 : expr->funcvariadic,
11441 : &use_variadic,
11442 6106 : context->inGroupBy));
11443 6106 : nargs = 0;
11444 12792 : foreach(l, expr->args)
11445 : {
11446 6686 : if (nargs++ > 0)
11447 1326 : appendStringInfoString(buf, ", ");
11448 6686 : if (use_variadic && lnext(expr->args, l) == NULL)
11449 7 : appendStringInfoString(buf, "VARIADIC ");
11450 6686 : get_rule_expr((Node *) lfirst(l), context, true);
11451 : }
11452 6106 : appendStringInfoChar(buf, ')');
11453 : }
11454 :
11455 : /*
11456 : * get_agg_expr - Parse back an Aggref node
11457 : */
11458 : static void
11459 3090 : get_agg_expr(Aggref *aggref, deparse_context *context,
11460 : Aggref *original_aggref)
11461 : {
11462 3090 : get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
11463 : false);
11464 3090 : }
11465 :
11466 : /*
11467 : * get_agg_expr_helper - subroutine for get_agg_expr and
11468 : * get_json_agg_constructor
11469 : */
11470 : static void
11471 3126 : get_agg_expr_helper(Aggref *aggref, deparse_context *context,
11472 : Aggref *original_aggref, const char *funcname,
11473 : const char *options, bool is_json_objectagg)
11474 : {
11475 3126 : StringInfo buf = context->buf;
11476 : Oid argtypes[FUNC_MAX_ARGS];
11477 : int nargs;
11478 3126 : bool use_variadic = false;
11479 :
11480 : /*
11481 : * For a combining aggregate, we look up and deparse the corresponding
11482 : * partial aggregate instead. This is necessary because our input
11483 : * argument list has been replaced; the new argument list always has just
11484 : * one element, which will point to a partial Aggref that supplies us with
11485 : * transition states to combine.
11486 : */
11487 3126 : if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
11488 : {
11489 : TargetEntry *tle;
11490 :
11491 : Assert(list_length(aggref->args) == 1);
11492 520 : tle = linitial_node(TargetEntry, aggref->args);
11493 520 : resolve_special_varno((Node *) tle->expr, context,
11494 : get_agg_combine_expr, original_aggref);
11495 520 : return;
11496 : }
11497 :
11498 : /*
11499 : * Mark as PARTIAL, if appropriate. We look to the original aggref so as
11500 : * to avoid printing this when recursing from the code just above.
11501 : */
11502 2606 : if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))
11503 1148 : appendStringInfoString(buf, "PARTIAL ");
11504 :
11505 : /* Extract the argument types as seen by the parser */
11506 2606 : nargs = get_aggregate_argtypes(aggref, argtypes);
11507 :
11508 2606 : if (!funcname)
11509 2570 : funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
11510 2570 : argtypes, aggref->aggvariadic,
11511 : &use_variadic,
11512 2570 : context->inGroupBy);
11513 :
11514 : /* Print the aggregate name, schema-qualified if needed */
11515 2606 : appendStringInfo(buf, "%s(%s", funcname,
11516 2606 : (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
11517 :
11518 2606 : if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
11519 : {
11520 : /*
11521 : * Ordered-set aggregates do not use "*" syntax. Also, we needn't
11522 : * worry about inserting VARIADIC. So we can just dump the direct
11523 : * args as-is.
11524 : */
11525 : Assert(!aggref->aggvariadic);
11526 17 : get_rule_expr((Node *) aggref->aggdirectargs, context, true);
11527 : Assert(aggref->aggorder != NIL);
11528 17 : appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
11529 17 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
11530 : }
11531 : else
11532 : {
11533 : /* aggstar can be set only in zero-argument aggregates */
11534 2589 : if (aggref->aggstar)
11535 775 : appendStringInfoChar(buf, '*');
11536 : else
11537 : {
11538 : ListCell *l;
11539 : int i;
11540 :
11541 1814 : i = 0;
11542 3751 : foreach(l, aggref->args)
11543 : {
11544 1937 : TargetEntry *tle = (TargetEntry *) lfirst(l);
11545 1937 : Node *arg = (Node *) tle->expr;
11546 :
11547 : Assert(!IsA(arg, NamedArgExpr));
11548 1937 : if (tle->resjunk)
11549 31 : continue;
11550 1906 : if (i++ > 0)
11551 : {
11552 92 : if (is_json_objectagg)
11553 : {
11554 : /*
11555 : * the ABSENT ON NULL and WITH UNIQUE args are printed
11556 : * separately, so ignore them here
11557 : */
11558 20 : if (i > 2)
11559 0 : break;
11560 :
11561 20 : appendStringInfoString(buf, " : ");
11562 : }
11563 : else
11564 72 : appendStringInfoString(buf, ", ");
11565 : }
11566 1906 : if (use_variadic && i == nargs)
11567 4 : appendStringInfoString(buf, "VARIADIC ");
11568 1906 : get_rule_expr(arg, context, true);
11569 : }
11570 : }
11571 :
11572 2589 : if (aggref->aggorder != NIL)
11573 : {
11574 54 : appendStringInfoString(buf, " ORDER BY ");
11575 54 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
11576 : }
11577 : }
11578 :
11579 2606 : if (options)
11580 36 : appendStringInfoString(buf, options);
11581 :
11582 2606 : if (aggref->aggfilter != NULL)
11583 : {
11584 28 : appendStringInfoString(buf, ") FILTER (WHERE ");
11585 28 : get_rule_expr((Node *) aggref->aggfilter, context, false);
11586 : }
11587 :
11588 2606 : appendStringInfoChar(buf, ')');
11589 : }
11590 :
11591 : /*
11592 : * This is a helper function for get_agg_expr(). It's used when we deparse
11593 : * a combining Aggref; resolve_special_varno locates the corresponding partial
11594 : * Aggref and then calls this.
11595 : */
11596 : static void
11597 520 : get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
11598 : {
11599 : Aggref *aggref;
11600 520 : Aggref *original_aggref = callback_arg;
11601 :
11602 520 : if (!IsA(node, Aggref))
11603 0 : elog(ERROR, "combining Aggref does not point to an Aggref");
11604 :
11605 520 : aggref = (Aggref *) node;
11606 520 : get_agg_expr(aggref, context, original_aggref);
11607 520 : }
11608 :
11609 : /*
11610 : * get_windowfunc_expr - Parse back a WindowFunc node
11611 : */
11612 : static void
11613 214 : get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
11614 : {
11615 214 : get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
11616 214 : }
11617 :
11618 :
11619 : /*
11620 : * get_windowfunc_expr_helper - subroutine for get_windowfunc_expr and
11621 : * get_json_agg_constructor
11622 : */
11623 : static void
11624 222 : get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
11625 : const char *funcname, const char *options,
11626 : bool is_json_objectagg)
11627 : {
11628 222 : StringInfo buf = context->buf;
11629 : Oid argtypes[FUNC_MAX_ARGS];
11630 : int nargs;
11631 : List *argnames;
11632 : ListCell *l;
11633 :
11634 222 : if (list_length(wfunc->args) > FUNC_MAX_ARGS)
11635 0 : ereport(ERROR,
11636 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
11637 : errmsg("too many arguments")));
11638 222 : nargs = 0;
11639 222 : argnames = NIL;
11640 376 : foreach(l, wfunc->args)
11641 : {
11642 154 : Node *arg = (Node *) lfirst(l);
11643 :
11644 154 : if (IsA(arg, NamedArgExpr))
11645 0 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
11646 154 : argtypes[nargs] = exprType(arg);
11647 154 : nargs++;
11648 : }
11649 :
11650 222 : if (!funcname)
11651 214 : funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
11652 : argtypes, false, NULL,
11653 214 : context->inGroupBy);
11654 :
11655 222 : appendStringInfo(buf, "%s(", funcname);
11656 :
11657 : /* winstar can be set only in zero-argument aggregates */
11658 222 : if (wfunc->winstar)
11659 16 : appendStringInfoChar(buf, '*');
11660 : else
11661 : {
11662 206 : if (is_json_objectagg)
11663 : {
11664 4 : get_rule_expr((Node *) linitial(wfunc->args), context, false);
11665 4 : appendStringInfoString(buf, " : ");
11666 4 : get_rule_expr((Node *) lsecond(wfunc->args), context, false);
11667 : }
11668 : else
11669 202 : get_rule_expr((Node *) wfunc->args, context, true);
11670 : }
11671 :
11672 222 : if (options)
11673 8 : appendStringInfoString(buf, options);
11674 :
11675 222 : if (wfunc->aggfilter != NULL)
11676 : {
11677 0 : appendStringInfoString(buf, ") FILTER (WHERE ");
11678 0 : get_rule_expr((Node *) wfunc->aggfilter, context, false);
11679 : }
11680 :
11681 222 : appendStringInfoString(buf, ") ");
11682 :
11683 222 : if (wfunc->ignore_nulls == PARSER_IGNORE_NULLS)
11684 4 : appendStringInfoString(buf, "IGNORE NULLS ");
11685 :
11686 222 : appendStringInfoString(buf, "OVER ");
11687 :
11688 222 : if (context->windowClause)
11689 : {
11690 : /* Query-decompilation case: search the windowClause list */
11691 40 : foreach(l, context->windowClause)
11692 : {
11693 40 : WindowClause *wc = (WindowClause *) lfirst(l);
11694 :
11695 40 : if (wc->winref == wfunc->winref)
11696 : {
11697 40 : if (wc->name)
11698 12 : appendStringInfoString(buf, quote_identifier(wc->name));
11699 : else
11700 28 : get_rule_windowspec(wc, context->targetList, context);
11701 40 : break;
11702 : }
11703 : }
11704 40 : if (l == NULL)
11705 0 : elog(ERROR, "could not find window clause for winref %u",
11706 : wfunc->winref);
11707 : }
11708 : else
11709 : {
11710 : /*
11711 : * In EXPLAIN, search the namespace stack for a matching WindowAgg
11712 : * node (probably it's always the first entry), and print winname.
11713 : */
11714 182 : foreach(l, context->namespaces)
11715 : {
11716 182 : deparse_namespace *dpns = (deparse_namespace *) lfirst(l);
11717 :
11718 182 : if (dpns->plan && IsA(dpns->plan, WindowAgg))
11719 : {
11720 182 : WindowAgg *wagg = (WindowAgg *) dpns->plan;
11721 :
11722 182 : if (wagg->winref == wfunc->winref)
11723 : {
11724 182 : appendStringInfoString(buf, quote_identifier(wagg->winname));
11725 182 : break;
11726 : }
11727 : }
11728 : }
11729 182 : if (l == NULL)
11730 0 : elog(ERROR, "could not find window clause for winref %u",
11731 : wfunc->winref);
11732 : }
11733 222 : }
11734 :
11735 : /*
11736 : * get_func_sql_syntax - Parse back a SQL-syntax function call
11737 : *
11738 : * Returns true if we successfully deparsed, false if we did not
11739 : * recognize the function.
11740 : */
11741 : static bool
11742 120 : get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
11743 : {
11744 120 : StringInfo buf = context->buf;
11745 120 : Oid funcoid = expr->funcid;
11746 :
11747 120 : switch (funcoid)
11748 : {
11749 16 : case F_TIMEZONE_INTERVAL_TIMESTAMP:
11750 : case F_TIMEZONE_INTERVAL_TIMESTAMPTZ:
11751 : case F_TIMEZONE_INTERVAL_TIMETZ:
11752 : case F_TIMEZONE_TEXT_TIMESTAMP:
11753 : case F_TIMEZONE_TEXT_TIMESTAMPTZ:
11754 : case F_TIMEZONE_TEXT_TIMETZ:
11755 : /* AT TIME ZONE ... note reversed argument order */
11756 16 : appendStringInfoChar(buf, '(');
11757 16 : get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
11758 : (Node *) expr);
11759 16 : appendStringInfoString(buf, " AT TIME ZONE ");
11760 16 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11761 : (Node *) expr);
11762 16 : appendStringInfoChar(buf, ')');
11763 16 : return true;
11764 :
11765 12 : case F_TIMEZONE_TIMESTAMP:
11766 : case F_TIMEZONE_TIMESTAMPTZ:
11767 : case F_TIMEZONE_TIMETZ:
11768 : /* AT LOCAL */
11769 12 : appendStringInfoChar(buf, '(');
11770 12 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11771 : (Node *) expr);
11772 12 : appendStringInfoString(buf, " AT LOCAL)");
11773 12 : return true;
11774 :
11775 4 : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL:
11776 : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ:
11777 : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL:
11778 : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ:
11779 : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_INTERVAL:
11780 : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_TIMESTAMP:
11781 : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_INTERVAL:
11782 : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_TIMESTAMP:
11783 : case F_OVERLAPS_TIMETZ_TIMETZ_TIMETZ_TIMETZ:
11784 : case F_OVERLAPS_TIME_INTERVAL_TIME_INTERVAL:
11785 : case F_OVERLAPS_TIME_INTERVAL_TIME_TIME:
11786 : case F_OVERLAPS_TIME_TIME_TIME_INTERVAL:
11787 : case F_OVERLAPS_TIME_TIME_TIME_TIME:
11788 : /* (x1, x2) OVERLAPS (y1, y2) */
11789 4 : appendStringInfoString(buf, "((");
11790 4 : get_rule_expr((Node *) linitial(expr->args), context, false);
11791 4 : appendStringInfoString(buf, ", ");
11792 4 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11793 4 : appendStringInfoString(buf, ") OVERLAPS (");
11794 4 : get_rule_expr((Node *) lthird(expr->args), context, false);
11795 4 : appendStringInfoString(buf, ", ");
11796 4 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11797 4 : appendStringInfoString(buf, "))");
11798 4 : return true;
11799 :
11800 12 : case F_EXTRACT_TEXT_DATE:
11801 : case F_EXTRACT_TEXT_TIME:
11802 : case F_EXTRACT_TEXT_TIMETZ:
11803 : case F_EXTRACT_TEXT_TIMESTAMP:
11804 : case F_EXTRACT_TEXT_TIMESTAMPTZ:
11805 : case F_EXTRACT_TEXT_INTERVAL:
11806 : /* EXTRACT (x FROM y) */
11807 12 : appendStringInfoString(buf, "EXTRACT(");
11808 : {
11809 12 : Const *con = (Const *) linitial(expr->args);
11810 :
11811 : Assert(IsA(con, Const) &&
11812 : con->consttype == TEXTOID &&
11813 : !con->constisnull);
11814 12 : appendStringInfoString(buf, TextDatumGetCString(con->constvalue));
11815 : }
11816 12 : appendStringInfoString(buf, " FROM ");
11817 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11818 12 : appendStringInfoChar(buf, ')');
11819 12 : return true;
11820 :
11821 8 : case F_IS_NORMALIZED:
11822 : /* IS xxx NORMALIZED */
11823 8 : appendStringInfoChar(buf, '(');
11824 8 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11825 : (Node *) expr);
11826 8 : appendStringInfoString(buf, " IS");
11827 8 : if (list_length(expr->args) == 2)
11828 : {
11829 4 : Const *con = (Const *) lsecond(expr->args);
11830 :
11831 : Assert(IsA(con, Const) &&
11832 : con->consttype == TEXTOID &&
11833 : !con->constisnull);
11834 4 : appendStringInfo(buf, " %s",
11835 4 : TextDatumGetCString(con->constvalue));
11836 : }
11837 8 : appendStringInfoString(buf, " NORMALIZED)");
11838 8 : return true;
11839 :
11840 4 : case F_PG_COLLATION_FOR:
11841 : /* COLLATION FOR */
11842 4 : appendStringInfoString(buf, "COLLATION FOR (");
11843 4 : get_rule_expr((Node *) linitial(expr->args), context, false);
11844 4 : appendStringInfoChar(buf, ')');
11845 4 : return true;
11846 :
11847 8 : case F_NORMALIZE:
11848 : /* NORMALIZE() */
11849 8 : appendStringInfoString(buf, "NORMALIZE(");
11850 8 : get_rule_expr((Node *) linitial(expr->args), context, false);
11851 8 : if (list_length(expr->args) == 2)
11852 : {
11853 4 : Const *con = (Const *) lsecond(expr->args);
11854 :
11855 : Assert(IsA(con, Const) &&
11856 : con->consttype == TEXTOID &&
11857 : !con->constisnull);
11858 4 : appendStringInfo(buf, ", %s",
11859 4 : TextDatumGetCString(con->constvalue));
11860 : }
11861 8 : appendStringInfoChar(buf, ')');
11862 8 : return true;
11863 :
11864 8 : case F_OVERLAY_BIT_BIT_INT4:
11865 : case F_OVERLAY_BIT_BIT_INT4_INT4:
11866 : case F_OVERLAY_BYTEA_BYTEA_INT4:
11867 : case F_OVERLAY_BYTEA_BYTEA_INT4_INT4:
11868 : case F_OVERLAY_TEXT_TEXT_INT4:
11869 : case F_OVERLAY_TEXT_TEXT_INT4_INT4:
11870 : /* OVERLAY() */
11871 8 : appendStringInfoString(buf, "OVERLAY(");
11872 8 : get_rule_expr((Node *) linitial(expr->args), context, false);
11873 8 : appendStringInfoString(buf, " PLACING ");
11874 8 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11875 8 : appendStringInfoString(buf, " FROM ");
11876 8 : get_rule_expr((Node *) lthird(expr->args), context, false);
11877 8 : if (list_length(expr->args) == 4)
11878 : {
11879 4 : appendStringInfoString(buf, " FOR ");
11880 4 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11881 : }
11882 8 : appendStringInfoChar(buf, ')');
11883 8 : return true;
11884 :
11885 4 : case F_POSITION_BIT_BIT:
11886 : case F_POSITION_BYTEA_BYTEA:
11887 : case F_POSITION_TEXT_TEXT:
11888 : /* POSITION() ... extra parens since args are b_expr not a_expr */
11889 4 : appendStringInfoString(buf, "POSITION((");
11890 4 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11891 4 : appendStringInfoString(buf, ") IN (");
11892 4 : get_rule_expr((Node *) linitial(expr->args), context, false);
11893 4 : appendStringInfoString(buf, "))");
11894 4 : return true;
11895 :
11896 4 : case F_SUBSTRING_BIT_INT4:
11897 : case F_SUBSTRING_BIT_INT4_INT4:
11898 : case F_SUBSTRING_BYTEA_INT4:
11899 : case F_SUBSTRING_BYTEA_INT4_INT4:
11900 : case F_SUBSTRING_TEXT_INT4:
11901 : case F_SUBSTRING_TEXT_INT4_INT4:
11902 : /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
11903 4 : appendStringInfoString(buf, "SUBSTRING(");
11904 4 : get_rule_expr((Node *) linitial(expr->args), context, false);
11905 4 : appendStringInfoString(buf, " FROM ");
11906 4 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11907 4 : if (list_length(expr->args) == 3)
11908 : {
11909 4 : appendStringInfoString(buf, " FOR ");
11910 4 : get_rule_expr((Node *) lthird(expr->args), context, false);
11911 : }
11912 4 : appendStringInfoChar(buf, ')');
11913 4 : return true;
11914 :
11915 4 : case F_SUBSTRING_TEXT_TEXT_TEXT:
11916 : /* SUBSTRING SIMILAR/ESCAPE */
11917 4 : appendStringInfoString(buf, "SUBSTRING(");
11918 4 : get_rule_expr((Node *) linitial(expr->args), context, false);
11919 4 : appendStringInfoString(buf, " SIMILAR ");
11920 4 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11921 4 : appendStringInfoString(buf, " ESCAPE ");
11922 4 : get_rule_expr((Node *) lthird(expr->args), context, false);
11923 4 : appendStringInfoChar(buf, ')');
11924 4 : return true;
11925 :
11926 8 : case F_BTRIM_BYTEA_BYTEA:
11927 : case F_BTRIM_TEXT:
11928 : case F_BTRIM_TEXT_TEXT:
11929 : /* TRIM() */
11930 8 : appendStringInfoString(buf, "TRIM(BOTH");
11931 8 : if (list_length(expr->args) == 2)
11932 : {
11933 8 : appendStringInfoChar(buf, ' ');
11934 8 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11935 : }
11936 8 : appendStringInfoString(buf, " FROM ");
11937 8 : get_rule_expr((Node *) linitial(expr->args), context, false);
11938 8 : appendStringInfoChar(buf, ')');
11939 8 : return true;
11940 :
11941 8 : case F_LTRIM_BYTEA_BYTEA:
11942 : case F_LTRIM_TEXT:
11943 : case F_LTRIM_TEXT_TEXT:
11944 : /* TRIM() */
11945 8 : appendStringInfoString(buf, "TRIM(LEADING");
11946 8 : if (list_length(expr->args) == 2)
11947 : {
11948 8 : appendStringInfoChar(buf, ' ');
11949 8 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11950 : }
11951 8 : appendStringInfoString(buf, " FROM ");
11952 8 : get_rule_expr((Node *) linitial(expr->args), context, false);
11953 8 : appendStringInfoChar(buf, ')');
11954 8 : return true;
11955 :
11956 8 : case F_RTRIM_BYTEA_BYTEA:
11957 : case F_RTRIM_TEXT:
11958 : case F_RTRIM_TEXT_TEXT:
11959 : /* TRIM() */
11960 8 : appendStringInfoString(buf, "TRIM(TRAILING");
11961 8 : if (list_length(expr->args) == 2)
11962 : {
11963 4 : appendStringInfoChar(buf, ' ');
11964 4 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11965 : }
11966 8 : appendStringInfoString(buf, " FROM ");
11967 8 : get_rule_expr((Node *) linitial(expr->args), context, false);
11968 8 : appendStringInfoChar(buf, ')');
11969 8 : return true;
11970 :
11971 8 : case F_SYSTEM_USER:
11972 8 : appendStringInfoString(buf, "SYSTEM_USER");
11973 8 : return true;
11974 :
11975 0 : case F_XMLEXISTS:
11976 : /* XMLEXISTS ... extra parens because args are c_expr */
11977 0 : appendStringInfoString(buf, "XMLEXISTS((");
11978 0 : get_rule_expr((Node *) linitial(expr->args), context, false);
11979 0 : appendStringInfoString(buf, ") PASSING (");
11980 0 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11981 0 : appendStringInfoString(buf, "))");
11982 0 : return true;
11983 : }
11984 4 : return false;
11985 : }
11986 :
11987 : /* ----------
11988 : * get_coercion_expr
11989 : *
11990 : * Make a string representation of a value coerced to a specific type
11991 : * ----------
11992 : */
11993 : static void
11994 3621 : get_coercion_expr(Node *arg, deparse_context *context,
11995 : Oid resulttype, int32 resulttypmod,
11996 : Node *parentNode)
11997 : {
11998 3621 : StringInfo buf = context->buf;
11999 :
12000 : /*
12001 : * Since parse_coerce.c doesn't immediately collapse application of
12002 : * length-coercion functions to constants, what we'll typically see in
12003 : * such cases is a Const with typmod -1 and a length-coercion function
12004 : * right above it. Avoid generating redundant output. However, beware of
12005 : * suppressing casts when the user actually wrote something like
12006 : * 'foo'::text::char(3).
12007 : *
12008 : * Note: it might seem that we are missing the possibility of needing to
12009 : * print a COLLATE clause for such a Const. However, a Const could only
12010 : * have nondefault collation in a post-constant-folding tree, in which the
12011 : * length coercion would have been folded too. See also the special
12012 : * handling of CollateExpr in coerce_to_target_type(): any collation
12013 : * marking will be above the coercion node, not below it.
12014 : */
12015 3621 : if (arg && IsA(arg, Const) &&
12016 430 : ((Const *) arg)->consttype == resulttype &&
12017 41 : ((Const *) arg)->consttypmod == -1)
12018 : {
12019 : /* Show the constant without normal ::typename decoration */
12020 41 : get_const_expr((Const *) arg, context, -1);
12021 : }
12022 : else
12023 : {
12024 3580 : if (!PRETTY_PAREN(context))
12025 3315 : appendStringInfoChar(buf, '(');
12026 3580 : get_rule_expr_paren(arg, context, false, parentNode);
12027 3580 : if (!PRETTY_PAREN(context))
12028 3315 : appendStringInfoChar(buf, ')');
12029 : }
12030 :
12031 : /*
12032 : * Never emit resulttype(arg) functional notation. A pg_proc entry could
12033 : * take precedence, and a resulttype in pg_temp would require schema
12034 : * qualification that format_type_with_typemod() would usually omit. We've
12035 : * standardized on arg::resulttype, but CAST(arg AS resulttype) notation
12036 : * would work fine.
12037 : */
12038 3621 : appendStringInfo(buf, "::%s",
12039 : format_type_with_typemod(resulttype, resulttypmod));
12040 3621 : }
12041 :
12042 : /* ----------
12043 : * get_const_expr
12044 : *
12045 : * Make a string representation of a Const
12046 : *
12047 : * showtype can be -1 to never show "::typename" decoration, or +1 to always
12048 : * show it, or 0 to show it only if the constant wouldn't be assumed to be
12049 : * the right type by default.
12050 : *
12051 : * If the Const's collation isn't default for its type, show that too.
12052 : * We mustn't do this when showtype is -1 (since that means the caller will
12053 : * print "::typename", and we can't put a COLLATE clause in between). It's
12054 : * caller's responsibility that collation isn't missed in such cases.
12055 : * ----------
12056 : */
12057 : static void
12058 46435 : get_const_expr(Const *constval, deparse_context *context, int showtype)
12059 : {
12060 46435 : StringInfo buf = context->buf;
12061 : Oid typoutput;
12062 : bool typIsVarlena;
12063 : char *extval;
12064 46435 : bool needlabel = false;
12065 :
12066 46435 : if (constval->constisnull)
12067 : {
12068 : /*
12069 : * Always label the type of a NULL constant to prevent misdecisions
12070 : * about type when reparsing.
12071 : */
12072 797 : appendStringInfoString(buf, "NULL");
12073 797 : if (showtype >= 0)
12074 : {
12075 766 : appendStringInfo(buf, "::%s",
12076 : format_type_with_typemod(constval->consttype,
12077 : constval->consttypmod));
12078 766 : get_const_collation(constval, context);
12079 : }
12080 6116 : return;
12081 : }
12082 :
12083 45638 : getTypeOutputInfo(constval->consttype,
12084 : &typoutput, &typIsVarlena);
12085 :
12086 45638 : extval = OidOutputFunctionCall(typoutput, constval->constvalue);
12087 :
12088 45638 : switch (constval->consttype)
12089 : {
12090 26160 : case INT4OID:
12091 :
12092 : /*
12093 : * INT4 can be printed without any decoration, unless it is
12094 : * negative; in that case print it as '-nnn'::integer to ensure
12095 : * that the output will re-parse as a constant, not as a constant
12096 : * plus operator. In most cases we could get away with printing
12097 : * (-nnn) instead, because of the way that gram.y handles negative
12098 : * literals; but that doesn't work for INT_MIN, and it doesn't
12099 : * seem that much prettier anyway.
12100 : */
12101 26160 : if (extval[0] != '-')
12102 25843 : appendStringInfoString(buf, extval);
12103 : else
12104 : {
12105 317 : appendStringInfo(buf, "'%s'", extval);
12106 317 : needlabel = true; /* we must attach a cast */
12107 : }
12108 26160 : break;
12109 :
12110 720 : case NUMERICOID:
12111 :
12112 : /*
12113 : * NUMERIC can be printed without quotes if it looks like a float
12114 : * constant (not an integer, and not Infinity or NaN) and doesn't
12115 : * have a leading sign (for the same reason as for INT4).
12116 : */
12117 720 : if (isdigit((unsigned char) extval[0]) &&
12118 720 : strcspn(extval, "eE.") != strlen(extval))
12119 : {
12120 251 : appendStringInfoString(buf, extval);
12121 : }
12122 : else
12123 : {
12124 469 : appendStringInfo(buf, "'%s'", extval);
12125 469 : needlabel = true; /* we must attach a cast */
12126 : }
12127 720 : break;
12128 :
12129 1049 : case BOOLOID:
12130 1049 : if (strcmp(extval, "t") == 0)
12131 407 : appendStringInfoString(buf, "true");
12132 : else
12133 642 : appendStringInfoString(buf, "false");
12134 1049 : break;
12135 :
12136 17709 : default:
12137 17709 : simple_quote_literal(buf, extval);
12138 17709 : break;
12139 : }
12140 :
12141 45638 : pfree(extval);
12142 :
12143 45638 : if (showtype < 0)
12144 5319 : return;
12145 :
12146 : /*
12147 : * For showtype == 0, append ::typename unless the constant will be
12148 : * implicitly typed as the right type when it is read in.
12149 : *
12150 : * XXX this code has to be kept in sync with the behavior of the parser,
12151 : * especially make_const.
12152 : */
12153 40319 : switch (constval->consttype)
12154 : {
12155 1107 : case BOOLOID:
12156 : case UNKNOWNOID:
12157 : /* These types can be left unlabeled */
12158 1107 : needlabel = false;
12159 1107 : break;
12160 23401 : case INT4OID:
12161 : /* We determined above whether a label is needed */
12162 23401 : break;
12163 720 : case NUMERICOID:
12164 :
12165 : /*
12166 : * Float-looking constants will be typed as numeric, which we
12167 : * checked above; but if there's a nondefault typmod we need to
12168 : * show it.
12169 : */
12170 720 : needlabel |= (constval->consttypmod >= 0);
12171 720 : break;
12172 15091 : default:
12173 15091 : needlabel = true;
12174 15091 : break;
12175 : }
12176 40319 : if (needlabel || showtype > 0)
12177 15871 : appendStringInfo(buf, "::%s",
12178 : format_type_with_typemod(constval->consttype,
12179 : constval->consttypmod));
12180 :
12181 40319 : get_const_collation(constval, context);
12182 : }
12183 :
12184 : /*
12185 : * helper for get_const_expr: append COLLATE if needed
12186 : */
12187 : static void
12188 41085 : get_const_collation(Const *constval, deparse_context *context)
12189 : {
12190 41085 : StringInfo buf = context->buf;
12191 :
12192 41085 : if (OidIsValid(constval->constcollid))
12193 : {
12194 5916 : Oid typcollation = get_typcollation(constval->consttype);
12195 :
12196 5916 : if (constval->constcollid != typcollation)
12197 : {
12198 48 : appendStringInfo(buf, " COLLATE %s",
12199 : generate_collation_name(constval->constcollid));
12200 : }
12201 : }
12202 41085 : }
12203 :
12204 : /*
12205 : * get_json_path_spec - Parse back a JSON path specification
12206 : */
12207 : static void
12208 304 : get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
12209 : {
12210 304 : if (IsA(path_spec, Const))
12211 304 : get_const_expr((Const *) path_spec, context, -1);
12212 : else
12213 0 : get_rule_expr(path_spec, context, showimplicit);
12214 304 : }
12215 :
12216 : /*
12217 : * get_json_format - Parse back a JsonFormat node
12218 : */
12219 : static void
12220 128 : get_json_format(JsonFormat *format, StringInfo buf)
12221 : {
12222 128 : if (format->format_type == JS_FORMAT_DEFAULT)
12223 76 : return;
12224 :
12225 52 : appendStringInfoString(buf,
12226 52 : format->format_type == JS_FORMAT_JSONB ?
12227 : " FORMAT JSONB" : " FORMAT JSON");
12228 :
12229 52 : if (format->encoding != JS_ENC_DEFAULT)
12230 : {
12231 : const char *encoding;
12232 :
12233 4 : encoding =
12234 8 : format->encoding == JS_ENC_UTF16 ? "UTF16" :
12235 4 : format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
12236 :
12237 4 : appendStringInfo(buf, " ENCODING %s", encoding);
12238 : }
12239 : }
12240 :
12241 : /*
12242 : * get_json_returning - Parse back a JsonReturning structure
12243 : */
12244 : static void
12245 120 : get_json_returning(JsonReturning *returning, StringInfo buf,
12246 : bool json_format_by_default)
12247 : {
12248 120 : if (!OidIsValid(returning->typid))
12249 0 : return;
12250 :
12251 120 : appendStringInfo(buf, " RETURNING %s",
12252 : format_type_with_typemod(returning->typid,
12253 : returning->typmod));
12254 :
12255 232 : if (!json_format_by_default ||
12256 112 : returning->format->format_type !=
12257 112 : (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
12258 24 : get_json_format(returning->format, buf);
12259 : }
12260 :
12261 : /*
12262 : * get_json_constructor - Parse back a JsonConstructorExpr node
12263 : */
12264 : static void
12265 124 : get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
12266 : bool showimplicit)
12267 : {
12268 124 : StringInfo buf = context->buf;
12269 : const char *funcname;
12270 : bool is_json_object;
12271 : int curridx;
12272 : ListCell *lc;
12273 :
12274 124 : if (ctor->type == JSCTOR_JSON_OBJECTAGG)
12275 : {
12276 24 : get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
12277 24 : return;
12278 : }
12279 100 : else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
12280 : {
12281 20 : get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
12282 20 : return;
12283 : }
12284 :
12285 80 : switch (ctor->type)
12286 : {
12287 20 : case JSCTOR_JSON_OBJECT:
12288 20 : funcname = "JSON_OBJECT";
12289 20 : break;
12290 16 : case JSCTOR_JSON_ARRAY:
12291 16 : funcname = "JSON_ARRAY";
12292 16 : break;
12293 28 : case JSCTOR_JSON_PARSE:
12294 28 : funcname = "JSON";
12295 28 : break;
12296 8 : case JSCTOR_JSON_SCALAR:
12297 8 : funcname = "JSON_SCALAR";
12298 8 : break;
12299 8 : case JSCTOR_JSON_SERIALIZE:
12300 8 : funcname = "JSON_SERIALIZE";
12301 8 : break;
12302 0 : default:
12303 0 : elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
12304 : }
12305 :
12306 80 : appendStringInfo(buf, "%s(", funcname);
12307 :
12308 80 : is_json_object = ctor->type == JSCTOR_JSON_OBJECT;
12309 212 : foreach(lc, ctor->args)
12310 : {
12311 132 : curridx = foreach_current_index(lc);
12312 132 : if (curridx > 0)
12313 : {
12314 : const char *sep;
12315 :
12316 52 : sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
12317 52 : appendStringInfoString(buf, sep);
12318 : }
12319 :
12320 132 : get_rule_expr((Node *) lfirst(lc), context, true);
12321 : }
12322 :
12323 80 : get_json_constructor_options(ctor, buf);
12324 80 : appendStringInfoChar(buf, ')');
12325 : }
12326 :
12327 : /*
12328 : * Append options, if any, to the JSON constructor being deparsed
12329 : */
12330 : static void
12331 124 : get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
12332 : {
12333 124 : if (ctor->absent_on_null)
12334 : {
12335 24 : if (ctor->type == JSCTOR_JSON_OBJECT ||
12336 24 : ctor->type == JSCTOR_JSON_OBJECTAGG)
12337 0 : appendStringInfoString(buf, " ABSENT ON NULL");
12338 : }
12339 : else
12340 : {
12341 100 : if (ctor->type == JSCTOR_JSON_ARRAY ||
12342 100 : ctor->type == JSCTOR_JSON_ARRAYAGG)
12343 12 : appendStringInfoString(buf, " NULL ON NULL");
12344 : }
12345 :
12346 124 : if (ctor->unique)
12347 16 : appendStringInfoString(buf, " WITH UNIQUE KEYS");
12348 :
12349 : /*
12350 : * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
12351 : * support one.
12352 : */
12353 124 : if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
12354 88 : get_json_returning(ctor->returning, buf, true);
12355 124 : }
12356 :
12357 : /*
12358 : * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
12359 : */
12360 : static void
12361 44 : get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
12362 : const char *funcname, bool is_json_objectagg)
12363 : {
12364 : StringInfoData options;
12365 :
12366 44 : initStringInfo(&options);
12367 44 : get_json_constructor_options(ctor, &options);
12368 :
12369 44 : if (IsA(ctor->func, Aggref))
12370 36 : get_agg_expr_helper((Aggref *) ctor->func, context,
12371 36 : (Aggref *) ctor->func,
12372 36 : funcname, options.data, is_json_objectagg);
12373 8 : else if (IsA(ctor->func, WindowFunc))
12374 8 : get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
12375 8 : funcname, options.data,
12376 : is_json_objectagg);
12377 : else
12378 0 : elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
12379 : nodeTag(ctor->func));
12380 44 : }
12381 :
12382 : /*
12383 : * simple_quote_literal - Format a string as a SQL literal, append to buf
12384 : */
12385 : static void
12386 18242 : simple_quote_literal(StringInfo buf, const char *val)
12387 : {
12388 : const char *valptr;
12389 :
12390 : /*
12391 : * We always form the string literal according to standard SQL rules.
12392 : */
12393 18242 : appendStringInfoChar(buf, '\'');
12394 184944 : for (valptr = val; *valptr; valptr++)
12395 : {
12396 166702 : char ch = *valptr;
12397 :
12398 166702 : if (SQL_STR_DOUBLE(ch, false))
12399 204 : appendStringInfoChar(buf, ch);
12400 166702 : appendStringInfoChar(buf, ch);
12401 : }
12402 18242 : appendStringInfoChar(buf, '\'');
12403 18242 : }
12404 :
12405 :
12406 : /* ----------
12407 : * get_sublink_expr - Parse back a sublink
12408 : * ----------
12409 : */
12410 : static void
12411 294 : get_sublink_expr(SubLink *sublink, deparse_context *context)
12412 : {
12413 294 : StringInfo buf = context->buf;
12414 294 : Query *query = (Query *) (sublink->subselect);
12415 294 : char *opname = NULL;
12416 : bool need_paren;
12417 :
12418 294 : if (sublink->subLinkType == ARRAY_SUBLINK)
12419 14 : appendStringInfoString(buf, "ARRAY(");
12420 : else
12421 280 : appendStringInfoChar(buf, '(');
12422 :
12423 : /*
12424 : * Note that we print the name of only the first operator, when there are
12425 : * multiple combining operators. This is an approximation that could go
12426 : * wrong in various scenarios (operators in different schemas, renamed
12427 : * operators, etc) but there is not a whole lot we can do about it, since
12428 : * the syntax allows only one operator to be shown.
12429 : */
12430 294 : if (sublink->testexpr)
12431 : {
12432 12 : if (IsA(sublink->testexpr, OpExpr))
12433 : {
12434 : /* single combining operator */
12435 4 : OpExpr *opexpr = (OpExpr *) sublink->testexpr;
12436 :
12437 4 : get_rule_expr(linitial(opexpr->args), context, true);
12438 4 : opname = generate_operator_name(opexpr->opno,
12439 4 : exprType(linitial(opexpr->args)),
12440 4 : exprType(lsecond(opexpr->args)));
12441 : }
12442 8 : else if (IsA(sublink->testexpr, BoolExpr))
12443 : {
12444 : /* multiple combining operators, = or <> cases */
12445 : char *sep;
12446 : ListCell *l;
12447 :
12448 4 : appendStringInfoChar(buf, '(');
12449 4 : sep = "";
12450 12 : foreach(l, ((BoolExpr *) sublink->testexpr)->args)
12451 : {
12452 8 : OpExpr *opexpr = lfirst_node(OpExpr, l);
12453 :
12454 8 : appendStringInfoString(buf, sep);
12455 8 : get_rule_expr(linitial(opexpr->args), context, true);
12456 8 : if (!opname)
12457 4 : opname = generate_operator_name(opexpr->opno,
12458 4 : exprType(linitial(opexpr->args)),
12459 4 : exprType(lsecond(opexpr->args)));
12460 8 : sep = ", ";
12461 : }
12462 4 : appendStringInfoChar(buf, ')');
12463 : }
12464 4 : else if (IsA(sublink->testexpr, RowCompareExpr))
12465 : {
12466 : /* multiple combining operators, < <= > >= cases */
12467 4 : RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
12468 :
12469 4 : appendStringInfoChar(buf, '(');
12470 4 : get_rule_expr((Node *) rcexpr->largs, context, true);
12471 4 : opname = generate_operator_name(linitial_oid(rcexpr->opnos),
12472 4 : exprType(linitial(rcexpr->largs)),
12473 4 : exprType(linitial(rcexpr->rargs)));
12474 4 : appendStringInfoChar(buf, ')');
12475 : }
12476 : else
12477 0 : elog(ERROR, "unrecognized testexpr type: %d",
12478 : (int) nodeTag(sublink->testexpr));
12479 : }
12480 :
12481 294 : need_paren = true;
12482 :
12483 294 : switch (sublink->subLinkType)
12484 : {
12485 113 : case EXISTS_SUBLINK:
12486 113 : appendStringInfoString(buf, "EXISTS ");
12487 113 : break;
12488 :
12489 8 : case ANY_SUBLINK:
12490 8 : if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
12491 4 : appendStringInfoString(buf, " IN ");
12492 : else
12493 4 : appendStringInfo(buf, " %s ANY ", opname);
12494 8 : break;
12495 :
12496 4 : case ALL_SUBLINK:
12497 4 : appendStringInfo(buf, " %s ALL ", opname);
12498 4 : break;
12499 :
12500 0 : case ROWCOMPARE_SUBLINK:
12501 0 : appendStringInfo(buf, " %s ", opname);
12502 0 : break;
12503 :
12504 169 : case EXPR_SUBLINK:
12505 : case MULTIEXPR_SUBLINK:
12506 : case ARRAY_SUBLINK:
12507 169 : need_paren = false;
12508 169 : break;
12509 :
12510 0 : case CTE_SUBLINK: /* shouldn't occur in a SubLink */
12511 : default:
12512 0 : elog(ERROR, "unrecognized sublink type: %d",
12513 : (int) sublink->subLinkType);
12514 : break;
12515 : }
12516 :
12517 294 : if (need_paren)
12518 125 : appendStringInfoChar(buf, '(');
12519 :
12520 294 : get_query_def(query, buf, context->namespaces, NULL, false,
12521 : context->prettyFlags, context->wrapColumn,
12522 : context->indentLevel);
12523 :
12524 294 : if (need_paren)
12525 125 : appendStringInfoString(buf, "))");
12526 : else
12527 169 : appendStringInfoChar(buf, ')');
12528 294 : }
12529 :
12530 :
12531 : /* ----------
12532 : * get_xmltable - Parse back a XMLTABLE function
12533 : * ----------
12534 : */
12535 : static void
12536 38 : get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
12537 : {
12538 38 : StringInfo buf = context->buf;
12539 :
12540 38 : appendStringInfoString(buf, "XMLTABLE(");
12541 :
12542 38 : if (tf->ns_uris != NIL)
12543 : {
12544 : ListCell *lc1,
12545 : *lc2;
12546 9 : bool first = true;
12547 :
12548 9 : appendStringInfoString(buf, "XMLNAMESPACES (");
12549 18 : forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
12550 : {
12551 9 : Node *expr = (Node *) lfirst(lc1);
12552 9 : String *ns_node = lfirst_node(String, lc2);
12553 :
12554 9 : if (!first)
12555 0 : appendStringInfoString(buf, ", ");
12556 : else
12557 9 : first = false;
12558 :
12559 9 : if (ns_node != NULL)
12560 : {
12561 9 : get_rule_expr(expr, context, showimplicit);
12562 9 : appendStringInfo(buf, " AS %s",
12563 9 : quote_identifier(strVal(ns_node)));
12564 : }
12565 : else
12566 : {
12567 0 : appendStringInfoString(buf, "DEFAULT ");
12568 0 : get_rule_expr(expr, context, showimplicit);
12569 : }
12570 : }
12571 9 : appendStringInfoString(buf, "), ");
12572 : }
12573 :
12574 38 : appendStringInfoChar(buf, '(');
12575 38 : get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
12576 38 : appendStringInfoString(buf, ") PASSING (");
12577 38 : get_rule_expr((Node *) tf->docexpr, context, showimplicit);
12578 38 : appendStringInfoChar(buf, ')');
12579 :
12580 38 : if (tf->colexprs != NIL)
12581 : {
12582 : ListCell *l1;
12583 : ListCell *l2;
12584 : ListCell *l3;
12585 : ListCell *l4;
12586 : ListCell *l5;
12587 38 : int colnum = 0;
12588 :
12589 38 : appendStringInfoString(buf, " COLUMNS ");
12590 231 : forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
12591 : l4, tf->colexprs, l5, tf->coldefexprs)
12592 : {
12593 193 : char *colname = strVal(lfirst(l1));
12594 193 : Oid typid = lfirst_oid(l2);
12595 193 : int32 typmod = lfirst_int(l3);
12596 193 : Node *colexpr = (Node *) lfirst(l4);
12597 193 : Node *coldefexpr = (Node *) lfirst(l5);
12598 193 : bool ordinality = (tf->ordinalitycol == colnum);
12599 193 : bool notnull = bms_is_member(colnum, tf->notnulls);
12600 :
12601 193 : if (colnum > 0)
12602 155 : appendStringInfoString(buf, ", ");
12603 193 : colnum++;
12604 :
12605 365 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
12606 : ordinality ? "FOR ORDINALITY" :
12607 172 : format_type_with_typemod(typid, typmod));
12608 193 : if (ordinality)
12609 21 : continue;
12610 :
12611 172 : if (coldefexpr != NULL)
12612 : {
12613 21 : appendStringInfoString(buf, " DEFAULT (");
12614 21 : get_rule_expr((Node *) coldefexpr, context, showimplicit);
12615 21 : appendStringInfoChar(buf, ')');
12616 : }
12617 172 : if (colexpr != NULL)
12618 : {
12619 156 : appendStringInfoString(buf, " PATH (");
12620 156 : get_rule_expr((Node *) colexpr, context, showimplicit);
12621 156 : appendStringInfoChar(buf, ')');
12622 : }
12623 172 : if (notnull)
12624 21 : appendStringInfoString(buf, " NOT NULL");
12625 : }
12626 : }
12627 :
12628 38 : appendStringInfoChar(buf, ')');
12629 38 : }
12630 :
12631 : /*
12632 : * get_json_table_nested_columns - Parse back nested JSON_TABLE columns
12633 : */
12634 : static void
12635 68 : get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
12636 : deparse_context *context, bool showimplicit,
12637 : bool needcomma)
12638 : {
12639 68 : if (IsA(plan, JsonTablePathScan))
12640 : {
12641 48 : JsonTablePathScan *scan = castNode(JsonTablePathScan, plan);
12642 :
12643 48 : if (needcomma)
12644 32 : appendStringInfoChar(context->buf, ',');
12645 :
12646 48 : appendStringInfoChar(context->buf, ' ');
12647 48 : appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
12648 48 : get_const_expr(scan->path->value, context, -1);
12649 48 : appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
12650 48 : get_json_table_columns(tf, scan, context, showimplicit);
12651 : }
12652 20 : else if (IsA(plan, JsonTableSiblingJoin))
12653 : {
12654 20 : JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
12655 :
12656 20 : get_json_table_nested_columns(tf, join->lplan, context, showimplicit,
12657 : needcomma);
12658 20 : get_json_table_nested_columns(tf, join->rplan, context, showimplicit,
12659 : true);
12660 : }
12661 68 : }
12662 :
12663 : /*
12664 : * get_json_table_columns - Parse back JSON_TABLE columns
12665 : */
12666 : static void
12667 120 : get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
12668 : deparse_context *context,
12669 : bool showimplicit)
12670 : {
12671 120 : StringInfo buf = context->buf;
12672 : ListCell *lc_colname;
12673 : ListCell *lc_coltype;
12674 : ListCell *lc_coltypmod;
12675 : ListCell *lc_colvalexpr;
12676 120 : int colnum = 0;
12677 :
12678 120 : appendStringInfoChar(buf, ' ');
12679 120 : appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12680 :
12681 120 : if (PRETTY_INDENT(context))
12682 92 : context->indentLevel += PRETTYINDENT_VAR;
12683 :
12684 572 : forfour(lc_colname, tf->colnames,
12685 : lc_coltype, tf->coltypes,
12686 : lc_coltypmod, tf->coltypmods,
12687 : lc_colvalexpr, tf->colvalexprs)
12688 : {
12689 484 : char *colname = strVal(lfirst(lc_colname));
12690 : JsonExpr *colexpr;
12691 : Oid typid;
12692 : int32 typmod;
12693 : bool ordinality;
12694 : JsonBehaviorType default_behavior;
12695 :
12696 484 : typid = lfirst_oid(lc_coltype);
12697 484 : typmod = lfirst_int(lc_coltypmod);
12698 484 : colexpr = castNode(JsonExpr, lfirst(lc_colvalexpr));
12699 :
12700 : /* Skip columns that don't belong to this scan. */
12701 484 : if (scan->colMin < 0 || colnum < scan->colMin)
12702 : {
12703 176 : colnum++;
12704 176 : continue;
12705 : }
12706 308 : if (colnum > scan->colMax)
12707 32 : break;
12708 :
12709 276 : if (colnum > scan->colMin)
12710 172 : appendStringInfoString(buf, ", ");
12711 :
12712 276 : colnum++;
12713 :
12714 276 : ordinality = !colexpr;
12715 :
12716 276 : appendContextKeyword(context, "", 0, 0, 0);
12717 :
12718 540 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
12719 : ordinality ? "FOR ORDINALITY" :
12720 264 : format_type_with_typemod(typid, typmod));
12721 276 : if (ordinality)
12722 12 : continue;
12723 :
12724 : /*
12725 : * Set default_behavior to guide get_json_expr_options() on whether to
12726 : * emit the ON ERROR / EMPTY clauses.
12727 : */
12728 264 : if (colexpr->op == JSON_EXISTS_OP)
12729 : {
12730 24 : appendStringInfoString(buf, " EXISTS");
12731 24 : default_behavior = JSON_BEHAVIOR_FALSE;
12732 : }
12733 : else
12734 : {
12735 240 : if (colexpr->op == JSON_QUERY_OP)
12736 : {
12737 : char typcategory;
12738 : bool typispreferred;
12739 :
12740 116 : get_type_category_preferred(typid, &typcategory, &typispreferred);
12741 :
12742 116 : if (typcategory == TYPCATEGORY_STRING)
12743 24 : appendStringInfoString(buf,
12744 24 : colexpr->format->format_type == JS_FORMAT_JSONB ?
12745 : " FORMAT JSONB" : " FORMAT JSON");
12746 : }
12747 :
12748 240 : default_behavior = JSON_BEHAVIOR_NULL;
12749 : }
12750 :
12751 264 : appendStringInfoString(buf, " PATH ");
12752 :
12753 264 : get_json_path_spec(colexpr->path_spec, context, showimplicit);
12754 :
12755 264 : get_json_expr_options(colexpr, context, default_behavior);
12756 : }
12757 :
12758 120 : if (scan->child)
12759 28 : get_json_table_nested_columns(tf, scan->child, context, showimplicit,
12760 28 : scan->colMin >= 0);
12761 :
12762 120 : if (PRETTY_INDENT(context))
12763 92 : context->indentLevel -= PRETTYINDENT_VAR;
12764 :
12765 120 : appendContextKeyword(context, ")", 0, 0, 0);
12766 120 : }
12767 :
12768 : /* ----------
12769 : * get_json_table - Parse back a JSON_TABLE function
12770 : * ----------
12771 : */
12772 : static void
12773 72 : get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
12774 : {
12775 72 : StringInfo buf = context->buf;
12776 72 : JsonExpr *jexpr = castNode(JsonExpr, tf->docexpr);
12777 72 : JsonTablePathScan *root = castNode(JsonTablePathScan, tf->plan);
12778 :
12779 72 : appendStringInfoString(buf, "JSON_TABLE(");
12780 :
12781 72 : if (PRETTY_INDENT(context))
12782 44 : context->indentLevel += PRETTYINDENT_VAR;
12783 :
12784 72 : appendContextKeyword(context, "", 0, 0, 0);
12785 :
12786 72 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12787 :
12788 72 : appendStringInfoString(buf, ", ");
12789 :
12790 72 : get_const_expr(root->path->value, context, -1);
12791 :
12792 72 : appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12793 :
12794 72 : if (jexpr->passing_values)
12795 : {
12796 : ListCell *lc1,
12797 : *lc2;
12798 56 : bool needcomma = false;
12799 :
12800 56 : appendStringInfoChar(buf, ' ');
12801 56 : appendContextKeyword(context, "PASSING ", 0, 0, 0);
12802 :
12803 56 : if (PRETTY_INDENT(context))
12804 28 : context->indentLevel += PRETTYINDENT_VAR;
12805 :
12806 168 : forboth(lc1, jexpr->passing_names,
12807 : lc2, jexpr->passing_values)
12808 : {
12809 112 : if (needcomma)
12810 56 : appendStringInfoString(buf, ", ");
12811 112 : needcomma = true;
12812 :
12813 112 : appendContextKeyword(context, "", 0, 0, 0);
12814 :
12815 112 : get_rule_expr((Node *) lfirst(lc2), context, false);
12816 112 : appendStringInfo(buf, " AS %s",
12817 112 : quote_identifier((lfirst_node(String, lc1))->sval)
12818 : );
12819 : }
12820 :
12821 56 : if (PRETTY_INDENT(context))
12822 28 : context->indentLevel -= PRETTYINDENT_VAR;
12823 : }
12824 :
12825 72 : get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12826 : showimplicit);
12827 :
12828 72 : if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12829 4 : get_json_behavior(jexpr->on_error, context, "ERROR");
12830 :
12831 72 : if (PRETTY_INDENT(context))
12832 44 : context->indentLevel -= PRETTYINDENT_VAR;
12833 :
12834 72 : appendContextKeyword(context, ")", 0, 0, 0);
12835 72 : }
12836 :
12837 : /* ----------
12838 : * get_tablefunc - Parse back a table function
12839 : * ----------
12840 : */
12841 : static void
12842 110 : get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
12843 : {
12844 : /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12845 :
12846 110 : if (tf->functype == TFT_XMLTABLE)
12847 38 : get_xmltable(tf, context, showimplicit);
12848 72 : else if (tf->functype == TFT_JSON_TABLE)
12849 72 : get_json_table(tf, context, showimplicit);
12850 110 : }
12851 :
12852 : /* ----------
12853 : * get_from_clause - Parse back a FROM clause
12854 : *
12855 : * "prefix" is the keyword that denotes the start of the list of FROM
12856 : * elements. It is FROM when used to parse back SELECT and UPDATE, but
12857 : * is USING when parsing back DELETE.
12858 : * ----------
12859 : */
12860 : static void
12861 2988 : get_from_clause(Query *query, const char *prefix, deparse_context *context)
12862 : {
12863 2988 : StringInfo buf = context->buf;
12864 2988 : bool first = true;
12865 : ListCell *l;
12866 :
12867 : /*
12868 : * We use the query's jointree as a guide to what to print. However, we
12869 : * must ignore auto-added RTEs that are marked not inFromCl. (These can
12870 : * only appear at the top level of the jointree, so it's sufficient to
12871 : * check here.) This check also ensures we ignore the rule pseudo-RTEs
12872 : * for NEW and OLD.
12873 : */
12874 5950 : foreach(l, query->jointree->fromlist)
12875 : {
12876 2962 : Node *jtnode = (Node *) lfirst(l);
12877 :
12878 2962 : if (IsA(jtnode, RangeTblRef))
12879 : {
12880 2357 : int varno = ((RangeTblRef *) jtnode)->rtindex;
12881 2357 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12882 :
12883 2357 : if (!rte->inFromCl)
12884 230 : continue;
12885 : }
12886 :
12887 2732 : if (first)
12888 : {
12889 2508 : appendContextKeyword(context, prefix,
12890 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
12891 2508 : first = false;
12892 :
12893 2508 : get_from_clause_item(jtnode, query, context);
12894 : }
12895 : else
12896 : {
12897 : StringInfoData itembuf;
12898 :
12899 224 : appendStringInfoString(buf, ", ");
12900 :
12901 : /*
12902 : * Put the new FROM item's text into itembuf so we can decide
12903 : * after we've got it whether or not it needs to go on a new line.
12904 : */
12905 224 : initStringInfo(&itembuf);
12906 224 : context->buf = &itembuf;
12907 :
12908 224 : get_from_clause_item(jtnode, query, context);
12909 :
12910 : /* Restore context's output buffer */
12911 224 : context->buf = buf;
12912 :
12913 : /* Consider line-wrapping if enabled */
12914 224 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
12915 : {
12916 : /* Does the new item start with a new line? */
12917 224 : if (itembuf.len > 0 && itembuf.data[0] == '\n')
12918 : {
12919 : /* If so, we shouldn't add anything */
12920 : /* instead, remove any trailing spaces currently in buf */
12921 0 : removeStringInfoSpaces(buf);
12922 : }
12923 : else
12924 : {
12925 : char *trailing_nl;
12926 :
12927 : /* Locate the start of the current line in the buffer */
12928 224 : trailing_nl = strrchr(buf->data, '\n');
12929 224 : if (trailing_nl == NULL)
12930 0 : trailing_nl = buf->data;
12931 : else
12932 224 : trailing_nl++;
12933 :
12934 : /*
12935 : * Add a newline, plus some indentation, if the new item
12936 : * would cause an overflow.
12937 : */
12938 224 : if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
12939 224 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
12940 : PRETTYINDENT_STD,
12941 : PRETTYINDENT_VAR);
12942 : }
12943 : }
12944 :
12945 : /* Add the new item */
12946 224 : appendBinaryStringInfo(buf, itembuf.data, itembuf.len);
12947 :
12948 : /* clean up */
12949 224 : pfree(itembuf.data);
12950 : }
12951 : }
12952 2988 : }
12953 :
12954 : static void
12955 4612 : get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
12956 : {
12957 4612 : StringInfo buf = context->buf;
12958 4612 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
12959 :
12960 4612 : if (IsA(jtnode, RangeTblRef))
12961 : {
12962 3672 : int varno = ((RangeTblRef *) jtnode)->rtindex;
12963 3672 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12964 3672 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
12965 3672 : RangeTblFunction *rtfunc1 = NULL;
12966 :
12967 3672 : if (rte->lateral)
12968 73 : appendStringInfoString(buf, "LATERAL ");
12969 :
12970 : /* Print the FROM item proper */
12971 3672 : switch (rte->rtekind)
12972 : {
12973 2732 : case RTE_RELATION:
12974 : /* Normal relation RTE */
12975 5464 : appendStringInfo(buf, "%s%s",
12976 2732 : only_marker(rte),
12977 : generate_relation_name(rte->relid,
12978 : context->namespaces));
12979 2732 : break;
12980 188 : case RTE_SUBQUERY:
12981 : /* Subquery RTE */
12982 188 : appendStringInfoChar(buf, '(');
12983 188 : get_query_def(rte->subquery, buf, context->namespaces, NULL,
12984 : true,
12985 : context->prettyFlags, context->wrapColumn,
12986 : context->indentLevel);
12987 188 : appendStringInfoChar(buf, ')');
12988 188 : break;
12989 553 : case RTE_FUNCTION:
12990 : /* Function RTE */
12991 553 : rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
12992 :
12993 : /*
12994 : * Omit ROWS FROM() syntax for just one function, unless it
12995 : * has both a coldeflist and WITH ORDINALITY. If it has both,
12996 : * we must use ROWS FROM() syntax to avoid ambiguity about
12997 : * whether the coldeflist includes the ordinality column.
12998 : */
12999 553 : if (list_length(rte->functions) == 1 &&
13000 533 : (rtfunc1->funccolnames == NIL || !rte->funcordinality))
13001 : {
13002 533 : get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
13003 : /* we'll print the coldeflist below, if it has one */
13004 : }
13005 : else
13006 : {
13007 : bool all_unnest;
13008 : ListCell *lc;
13009 :
13010 : /*
13011 : * If all the function calls in the list are to unnest,
13012 : * and none need a coldeflist, then collapse the list back
13013 : * down to UNNEST(args). (If we had more than one
13014 : * built-in unnest function, this would get more
13015 : * difficult.)
13016 : *
13017 : * XXX This is pretty ugly, since it makes not-terribly-
13018 : * future-proof assumptions about what the parser would do
13019 : * with the output; but the alternative is to emit our
13020 : * nonstandard ROWS FROM() notation for what might have
13021 : * been a perfectly spec-compliant multi-argument
13022 : * UNNEST().
13023 : */
13024 20 : all_unnest = true;
13025 52 : foreach(lc, rte->functions)
13026 : {
13027 44 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
13028 :
13029 44 : if (!IsA(rtfunc->funcexpr, FuncExpr) ||
13030 44 : ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
13031 32 : rtfunc->funccolnames != NIL)
13032 : {
13033 12 : all_unnest = false;
13034 12 : break;
13035 : }
13036 : }
13037 :
13038 20 : if (all_unnest)
13039 : {
13040 8 : List *allargs = NIL;
13041 :
13042 32 : foreach(lc, rte->functions)
13043 : {
13044 24 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
13045 24 : List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
13046 :
13047 24 : allargs = list_concat(allargs, args);
13048 : }
13049 :
13050 8 : appendStringInfoString(buf, "UNNEST(");
13051 8 : get_rule_expr((Node *) allargs, context, true);
13052 8 : appendStringInfoChar(buf, ')');
13053 : }
13054 : else
13055 : {
13056 12 : int funcno = 0;
13057 :
13058 12 : appendStringInfoString(buf, "ROWS FROM(");
13059 44 : foreach(lc, rte->functions)
13060 : {
13061 32 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
13062 :
13063 32 : if (funcno > 0)
13064 20 : appendStringInfoString(buf, ", ");
13065 32 : get_rule_expr_funccall(rtfunc->funcexpr, context, true);
13066 32 : if (rtfunc->funccolnames != NIL)
13067 : {
13068 : /* Reconstruct the column definition list */
13069 4 : appendStringInfoString(buf, " AS ");
13070 4 : get_from_clause_coldeflist(rtfunc,
13071 : NULL,
13072 : context);
13073 : }
13074 32 : funcno++;
13075 : }
13076 12 : appendStringInfoChar(buf, ')');
13077 : }
13078 : /* prevent printing duplicate coldeflist below */
13079 20 : rtfunc1 = NULL;
13080 : }
13081 553 : if (rte->funcordinality)
13082 12 : appendStringInfoString(buf, " WITH ORDINALITY");
13083 553 : break;
13084 62 : case RTE_TABLEFUNC:
13085 62 : get_tablefunc(rte->tablefunc, context, true);
13086 62 : break;
13087 14 : case RTE_GRAPH_TABLE:
13088 14 : appendStringInfoString(buf, "GRAPH_TABLE (");
13089 14 : appendStringInfoString(buf, generate_relation_name(rte->relid, context->namespaces));
13090 14 : appendStringInfoString(buf, " MATCH ");
13091 14 : get_graph_pattern_def(rte->graph_pattern, context);
13092 14 : appendStringInfoString(buf, " COLUMNS (");
13093 : {
13094 : ListCell *lc;
13095 14 : bool first = true;
13096 :
13097 37 : foreach(lc, rte->graph_table_columns)
13098 : {
13099 23 : TargetEntry *te = lfirst_node(TargetEntry, lc);
13100 23 : deparse_context context = {0};
13101 :
13102 23 : if (!first)
13103 9 : appendStringInfoString(buf, ", ");
13104 : else
13105 14 : first = false;
13106 :
13107 23 : context.buf = buf;
13108 :
13109 23 : get_rule_expr((Node *) te->expr, &context, false);
13110 23 : appendStringInfoString(buf, " AS ");
13111 23 : appendStringInfoString(buf, quote_identifier(te->resname));
13112 : }
13113 : }
13114 14 : appendStringInfoString(buf, ")");
13115 14 : appendStringInfoString(buf, ")");
13116 14 : break;
13117 8 : case RTE_VALUES:
13118 : /* Values list RTE */
13119 8 : appendStringInfoChar(buf, '(');
13120 8 : get_values_def(rte->values_lists, context);
13121 8 : appendStringInfoChar(buf, ')');
13122 8 : break;
13123 115 : case RTE_CTE:
13124 115 : appendStringInfoString(buf, quote_identifier(rte->ctename));
13125 115 : break;
13126 0 : default:
13127 0 : elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
13128 : break;
13129 : }
13130 :
13131 : /* Print the relation alias, if needed */
13132 3672 : get_rte_alias(rte, varno, false, context);
13133 :
13134 : /* Print the column definitions or aliases, if needed */
13135 3672 : if (rtfunc1 && rtfunc1->funccolnames != NIL)
13136 : {
13137 : /* Reconstruct the columndef list, which is also the aliases */
13138 0 : get_from_clause_coldeflist(rtfunc1, colinfo, context);
13139 : }
13140 : else
13141 : {
13142 : /* Else print column aliases as needed */
13143 3672 : get_column_alias_list(colinfo, context);
13144 : }
13145 :
13146 : /* Tablesample clause must go after any alias */
13147 3672 : if (rte->rtekind == RTE_RELATION && rte->tablesample)
13148 18 : get_tablesample_def(rte->tablesample, context);
13149 : }
13150 940 : else if (IsA(jtnode, JoinExpr))
13151 : {
13152 940 : JoinExpr *j = (JoinExpr *) jtnode;
13153 940 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
13154 : bool need_paren_on_right;
13155 :
13156 2172 : need_paren_on_right = PRETTY_PAREN(context) &&
13157 940 : !IsA(j->rarg, RangeTblRef) &&
13158 0 : !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
13159 :
13160 940 : if (!PRETTY_PAREN(context) || j->alias != NULL)
13161 720 : appendStringInfoChar(buf, '(');
13162 :
13163 940 : get_from_clause_item(j->larg, query, context);
13164 :
13165 940 : switch (j->jointype)
13166 : {
13167 517 : case JOIN_INNER:
13168 517 : if (j->quals)
13169 489 : appendContextKeyword(context, " JOIN ",
13170 : -PRETTYINDENT_STD,
13171 : PRETTYINDENT_STD,
13172 : PRETTYINDENT_JOIN);
13173 : else
13174 28 : appendContextKeyword(context, " CROSS JOIN ",
13175 : -PRETTYINDENT_STD,
13176 : PRETTYINDENT_STD,
13177 : PRETTYINDENT_JOIN);
13178 517 : break;
13179 355 : case JOIN_LEFT:
13180 355 : appendContextKeyword(context, " LEFT JOIN ",
13181 : -PRETTYINDENT_STD,
13182 : PRETTYINDENT_STD,
13183 : PRETTYINDENT_JOIN);
13184 355 : break;
13185 68 : case JOIN_FULL:
13186 68 : appendContextKeyword(context, " FULL JOIN ",
13187 : -PRETTYINDENT_STD,
13188 : PRETTYINDENT_STD,
13189 : PRETTYINDENT_JOIN);
13190 68 : break;
13191 0 : case JOIN_RIGHT:
13192 0 : appendContextKeyword(context, " RIGHT JOIN ",
13193 : -PRETTYINDENT_STD,
13194 : PRETTYINDENT_STD,
13195 : PRETTYINDENT_JOIN);
13196 0 : break;
13197 0 : default:
13198 0 : elog(ERROR, "unrecognized join type: %d",
13199 : (int) j->jointype);
13200 : }
13201 :
13202 940 : if (need_paren_on_right)
13203 0 : appendStringInfoChar(buf, '(');
13204 940 : get_from_clause_item(j->rarg, query, context);
13205 940 : if (need_paren_on_right)
13206 0 : appendStringInfoChar(buf, ')');
13207 :
13208 940 : if (j->usingClause)
13209 : {
13210 : ListCell *lc;
13211 280 : bool first = true;
13212 :
13213 280 : appendStringInfoString(buf, " USING (");
13214 : /* Use the assigned names, not what's in usingClause */
13215 664 : foreach(lc, colinfo->usingNames)
13216 : {
13217 384 : char *colname = (char *) lfirst(lc);
13218 :
13219 384 : if (first)
13220 280 : first = false;
13221 : else
13222 104 : appendStringInfoString(buf, ", ");
13223 384 : appendStringInfoString(buf, quote_identifier(colname));
13224 : }
13225 280 : appendStringInfoChar(buf, ')');
13226 :
13227 280 : if (j->join_using_alias)
13228 8 : appendStringInfo(buf, " AS %s",
13229 8 : quote_identifier(j->join_using_alias->aliasname));
13230 : }
13231 660 : else if (j->quals)
13232 : {
13233 628 : appendStringInfoString(buf, " ON ");
13234 628 : if (!PRETTY_PAREN(context))
13235 624 : appendStringInfoChar(buf, '(');
13236 628 : get_rule_expr(j->quals, context, false);
13237 628 : if (!PRETTY_PAREN(context))
13238 624 : appendStringInfoChar(buf, ')');
13239 : }
13240 32 : else if (j->jointype != JOIN_INNER)
13241 : {
13242 : /* If we didn't say CROSS JOIN above, we must provide an ON */
13243 4 : appendStringInfoString(buf, " ON TRUE");
13244 : }
13245 :
13246 940 : if (!PRETTY_PAREN(context) || j->alias != NULL)
13247 720 : appendStringInfoChar(buf, ')');
13248 :
13249 : /* Yes, it's correct to put alias after the right paren ... */
13250 940 : if (j->alias != NULL)
13251 : {
13252 : /*
13253 : * Note that it's correct to emit an alias clause if and only if
13254 : * there was one originally. Otherwise we'd be converting a named
13255 : * join to unnamed or vice versa, which creates semantic
13256 : * subtleties we don't want. However, we might print a different
13257 : * alias name than was there originally.
13258 : */
13259 72 : appendStringInfo(buf, " %s",
13260 72 : quote_identifier(get_rtable_name(j->rtindex,
13261 : context)));
13262 72 : get_column_alias_list(colinfo, context);
13263 : }
13264 : }
13265 : else
13266 0 : elog(ERROR, "unrecognized node type: %d",
13267 : (int) nodeTag(jtnode));
13268 4612 : }
13269 :
13270 : /*
13271 : * get_rte_alias - print the relation's alias, if needed
13272 : *
13273 : * If printed, the alias is preceded by a space, or by " AS " if use_as is true.
13274 : */
13275 : static void
13276 4015 : get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
13277 : deparse_context *context)
13278 : {
13279 4015 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
13280 4015 : char *refname = get_rtable_name(varno, context);
13281 4015 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
13282 4015 : bool printalias = false;
13283 :
13284 4015 : if (rte->alias != NULL)
13285 : {
13286 : /* Always print alias if user provided one */
13287 1884 : printalias = true;
13288 : }
13289 2131 : else if (colinfo->printaliases)
13290 : {
13291 : /* Always print alias if we need to print column aliases */
13292 210 : printalias = true;
13293 : }
13294 1921 : else if (rte->rtekind == RTE_RELATION)
13295 : {
13296 : /*
13297 : * No need to print alias if it's same as relation name (this would
13298 : * normally be the case, but not if set_rtable_names had to resolve a
13299 : * conflict).
13300 : */
13301 1736 : if (strcmp(refname, get_relation_name(rte->relid)) != 0)
13302 52 : printalias = true;
13303 : }
13304 185 : else if (rte->rtekind == RTE_FUNCTION)
13305 : {
13306 : /*
13307 : * For a function RTE, always print alias. This covers possible
13308 : * renaming of the function and/or instability of the FigureColname
13309 : * rules for things that aren't simple functions. Note we'd need to
13310 : * force it anyway for the columndef list case.
13311 : */
13312 0 : printalias = true;
13313 : }
13314 185 : else if (rte->rtekind == RTE_SUBQUERY ||
13315 169 : rte->rtekind == RTE_VALUES)
13316 : {
13317 : /*
13318 : * For a subquery, always print alias. This makes the output
13319 : * SQL-spec-compliant, even though we allow such aliases to be omitted
13320 : * on input.
13321 : */
13322 24 : printalias = true;
13323 : }
13324 161 : else if (rte->rtekind == RTE_CTE)
13325 : {
13326 : /*
13327 : * No need to print alias if it's same as CTE name (this would
13328 : * normally be the case, but not if set_rtable_names had to resolve a
13329 : * conflict).
13330 : */
13331 89 : if (strcmp(refname, rte->ctename) != 0)
13332 12 : printalias = true;
13333 : }
13334 :
13335 4015 : if (printalias)
13336 2182 : appendStringInfo(context->buf, "%s%s",
13337 : use_as ? " AS " : " ",
13338 : quote_identifier(refname));
13339 4015 : }
13340 :
13341 : /*
13342 : * get_for_portion_of - print FOR PORTION OF if needed
13343 : * XXX: Newlines would help here, at least when pretty-printing. But then the
13344 : * alias and SET will be on their own line with a leading space.
13345 : */
13346 : static void
13347 141 : get_for_portion_of(ForPortionOfExpr *forPortionOf, deparse_context *context)
13348 : {
13349 141 : if (forPortionOf)
13350 : {
13351 16 : appendStringInfo(context->buf, " FOR PORTION OF %s",
13352 16 : quote_identifier(forPortionOf->range_name));
13353 :
13354 : /*
13355 : * Try to write it as FROM ... TO ... if we received it that way,
13356 : * otherwise (targetExpr).
13357 : */
13358 16 : if (forPortionOf->targetFrom && forPortionOf->targetTo)
13359 : {
13360 8 : appendStringInfoString(context->buf, " FROM ");
13361 8 : get_rule_expr(forPortionOf->targetFrom, context, false);
13362 8 : appendStringInfoString(context->buf, " TO ");
13363 8 : get_rule_expr(forPortionOf->targetTo, context, false);
13364 : }
13365 : else
13366 : {
13367 8 : appendStringInfoString(context->buf, " (");
13368 8 : get_rule_expr(forPortionOf->targetRange, context, false);
13369 8 : appendStringInfoString(context->buf, ")");
13370 : }
13371 : }
13372 141 : }
13373 :
13374 : /*
13375 : * get_column_alias_list - print column alias list for an RTE
13376 : *
13377 : * Caller must already have printed the relation's alias name.
13378 : */
13379 : static void
13380 3744 : get_column_alias_list(deparse_columns *colinfo, deparse_context *context)
13381 : {
13382 3744 : StringInfo buf = context->buf;
13383 : int i;
13384 3744 : bool first = true;
13385 :
13386 : /* Don't print aliases if not needed */
13387 3744 : if (!colinfo->printaliases)
13388 2951 : return;
13389 :
13390 6298 : for (i = 0; i < colinfo->num_new_cols; i++)
13391 : {
13392 5505 : char *colname = colinfo->new_colnames[i];
13393 :
13394 5505 : if (first)
13395 : {
13396 793 : appendStringInfoChar(buf, '(');
13397 793 : first = false;
13398 : }
13399 : else
13400 4712 : appendStringInfoString(buf, ", ");
13401 5505 : appendStringInfoString(buf, quote_identifier(colname));
13402 : }
13403 793 : if (!first)
13404 793 : appendStringInfoChar(buf, ')');
13405 : }
13406 :
13407 : /*
13408 : * get_from_clause_coldeflist - reproduce FROM clause coldeflist
13409 : *
13410 : * When printing a top-level coldeflist (which is syntactically also the
13411 : * relation's column alias list), use column names from colinfo. But when
13412 : * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
13413 : * original coldeflist's names, which are available in rtfunc->funccolnames.
13414 : * Pass NULL for colinfo to select the latter behavior.
13415 : *
13416 : * The coldeflist is appended immediately (no space) to buf. Caller is
13417 : * responsible for ensuring that an alias or AS is present before it.
13418 : */
13419 : static void
13420 4 : get_from_clause_coldeflist(RangeTblFunction *rtfunc,
13421 : deparse_columns *colinfo,
13422 : deparse_context *context)
13423 : {
13424 4 : StringInfo buf = context->buf;
13425 : ListCell *l1;
13426 : ListCell *l2;
13427 : ListCell *l3;
13428 : ListCell *l4;
13429 : int i;
13430 :
13431 4 : appendStringInfoChar(buf, '(');
13432 :
13433 4 : i = 0;
13434 16 : forfour(l1, rtfunc->funccoltypes,
13435 : l2, rtfunc->funccoltypmods,
13436 : l3, rtfunc->funccolcollations,
13437 : l4, rtfunc->funccolnames)
13438 : {
13439 12 : Oid atttypid = lfirst_oid(l1);
13440 12 : int32 atttypmod = lfirst_int(l2);
13441 12 : Oid attcollation = lfirst_oid(l3);
13442 : char *attname;
13443 :
13444 12 : if (colinfo)
13445 0 : attname = colinfo->colnames[i];
13446 : else
13447 12 : attname = strVal(lfirst(l4));
13448 :
13449 : Assert(attname); /* shouldn't be any dropped columns here */
13450 :
13451 12 : if (i > 0)
13452 8 : appendStringInfoString(buf, ", ");
13453 12 : appendStringInfo(buf, "%s %s",
13454 : quote_identifier(attname),
13455 : format_type_with_typemod(atttypid, atttypmod));
13456 16 : if (OidIsValid(attcollation) &&
13457 4 : attcollation != get_typcollation(atttypid))
13458 0 : appendStringInfo(buf, " COLLATE %s",
13459 : generate_collation_name(attcollation));
13460 :
13461 12 : i++;
13462 : }
13463 :
13464 4 : appendStringInfoChar(buf, ')');
13465 4 : }
13466 :
13467 : /*
13468 : * get_tablesample_def - print a TableSampleClause
13469 : */
13470 : static void
13471 18 : get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
13472 : {
13473 18 : StringInfo buf = context->buf;
13474 : Oid argtypes[1];
13475 : int nargs;
13476 : ListCell *l;
13477 :
13478 : /*
13479 : * We should qualify the handler's function name if it wouldn't be
13480 : * resolved by lookup in the current search path.
13481 : */
13482 18 : argtypes[0] = INTERNALOID;
13483 18 : appendStringInfo(buf, " TABLESAMPLE %s (",
13484 : generate_function_name(tablesample->tsmhandler, 1,
13485 : NIL, argtypes,
13486 : false, NULL, false));
13487 :
13488 18 : nargs = 0;
13489 36 : foreach(l, tablesample->args)
13490 : {
13491 18 : if (nargs++ > 0)
13492 0 : appendStringInfoString(buf, ", ");
13493 18 : get_rule_expr((Node *) lfirst(l), context, false);
13494 : }
13495 18 : appendStringInfoChar(buf, ')');
13496 :
13497 18 : if (tablesample->repeatable != NULL)
13498 : {
13499 9 : appendStringInfoString(buf, " REPEATABLE (");
13500 9 : get_rule_expr((Node *) tablesample->repeatable, context, false);
13501 9 : appendStringInfoChar(buf, ')');
13502 : }
13503 18 : }
13504 :
13505 : /*
13506 : * get_opclass_name - fetch name of an index operator class
13507 : *
13508 : * The opclass name is appended (after a space) to buf.
13509 : *
13510 : * Output is suppressed if the opclass is the default for the given
13511 : * actual_datatype. (If you don't want this behavior, just pass
13512 : * InvalidOid for actual_datatype.)
13513 : */
13514 : static void
13515 7175 : get_opclass_name(Oid opclass, Oid actual_datatype,
13516 : StringInfo buf)
13517 : {
13518 : HeapTuple ht_opc;
13519 : Form_pg_opclass opcrec;
13520 : char *opcname;
13521 : char *nspname;
13522 :
13523 7175 : ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
13524 7175 : if (!HeapTupleIsValid(ht_opc))
13525 0 : elog(ERROR, "cache lookup failed for opclass %u", opclass);
13526 7175 : opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
13527 :
13528 14324 : if (!OidIsValid(actual_datatype) ||
13529 7149 : GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
13530 : {
13531 : /* Okay, we need the opclass name. Do we need to qualify it? */
13532 293 : opcname = NameStr(opcrec->opcname);
13533 293 : if (OpclassIsVisible(opclass))
13534 293 : appendStringInfo(buf, " %s", quote_identifier(opcname));
13535 : else
13536 : {
13537 0 : nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
13538 0 : appendStringInfo(buf, " %s.%s",
13539 : quote_identifier(nspname),
13540 : quote_identifier(opcname));
13541 : }
13542 : }
13543 7175 : ReleaseSysCache(ht_opc);
13544 7175 : }
13545 :
13546 : /*
13547 : * generate_opclass_name
13548 : * Compute the name to display for an opclass specified by OID
13549 : *
13550 : * The result includes all necessary quoting and schema-prefixing.
13551 : */
13552 : char *
13553 4 : generate_opclass_name(Oid opclass)
13554 : {
13555 : StringInfoData buf;
13556 :
13557 4 : initStringInfo(&buf);
13558 4 : get_opclass_name(opclass, InvalidOid, &buf);
13559 :
13560 4 : return &buf.data[1]; /* get_opclass_name() prepends space */
13561 : }
13562 :
13563 : /*
13564 : * processIndirection - take care of array and subfield assignment
13565 : *
13566 : * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
13567 : * appear in the input, printing them as decoration for the base column
13568 : * name (which we assume the caller just printed). We might also need to
13569 : * strip CoerceToDomain nodes, but only ones that appear above assignment
13570 : * nodes.
13571 : *
13572 : * Returns the subexpression that's to be assigned.
13573 : */
13574 : static Node *
13575 729 : processIndirection(Node *node, deparse_context *context)
13576 : {
13577 729 : StringInfo buf = context->buf;
13578 729 : CoerceToDomain *cdomain = NULL;
13579 :
13580 : for (;;)
13581 : {
13582 933 : if (node == NULL)
13583 0 : break;
13584 933 : if (IsA(node, FieldStore))
13585 : {
13586 72 : FieldStore *fstore = (FieldStore *) node;
13587 : Oid typrelid;
13588 : char *fieldname;
13589 :
13590 : /* lookup tuple type */
13591 72 : typrelid = get_typ_typrelid(fstore->resulttype);
13592 72 : if (!OidIsValid(typrelid))
13593 0 : elog(ERROR, "argument type %s of FieldStore is not a tuple type",
13594 : format_type_be(fstore->resulttype));
13595 :
13596 : /*
13597 : * Print the field name. There should only be one target field in
13598 : * stored rules. There could be more than that in executable
13599 : * target lists, but this function cannot be used for that case.
13600 : */
13601 : Assert(list_length(fstore->fieldnums) == 1);
13602 72 : fieldname = get_attname(typrelid,
13603 72 : linitial_int(fstore->fieldnums), false);
13604 72 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
13605 :
13606 : /*
13607 : * We ignore arg since it should be an uninteresting reference to
13608 : * the target column or subcolumn.
13609 : */
13610 72 : node = (Node *) linitial(fstore->newvals);
13611 : }
13612 861 : else if (IsA(node, SubscriptingRef))
13613 : {
13614 92 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
13615 :
13616 92 : if (sbsref->refassgnexpr == NULL)
13617 0 : break;
13618 :
13619 92 : printSubscripts(sbsref, context);
13620 :
13621 : /*
13622 : * We ignore refexpr since it should be an uninteresting reference
13623 : * to the target column or subcolumn.
13624 : */
13625 92 : node = (Node *) sbsref->refassgnexpr;
13626 : }
13627 769 : else if (IsA(node, CoerceToDomain))
13628 : {
13629 40 : cdomain = (CoerceToDomain *) node;
13630 : /* If it's an explicit domain coercion, we're done */
13631 40 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
13632 0 : break;
13633 : /* Tentatively descend past the CoerceToDomain */
13634 40 : node = (Node *) cdomain->arg;
13635 : }
13636 : else
13637 729 : break;
13638 : }
13639 :
13640 : /*
13641 : * If we descended past a CoerceToDomain whose argument turned out not to
13642 : * be a FieldStore or array assignment, back up to the CoerceToDomain.
13643 : * (This is not enough to be fully correct if there are nested implicit
13644 : * CoerceToDomains, but such cases shouldn't ever occur.)
13645 : */
13646 729 : if (cdomain && node == (Node *) cdomain->arg)
13647 0 : node = (Node *) cdomain;
13648 :
13649 729 : return node;
13650 : }
13651 :
13652 : static void
13653 316 : printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
13654 : {
13655 316 : StringInfo buf = context->buf;
13656 : ListCell *lowlist_item;
13657 : ListCell *uplist_item;
13658 :
13659 316 : lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
13660 632 : foreach(uplist_item, sbsref->refupperindexpr)
13661 : {
13662 316 : appendStringInfoChar(buf, '[');
13663 316 : if (lowlist_item)
13664 : {
13665 : /* If subexpression is NULL, get_rule_expr prints nothing */
13666 0 : get_rule_expr((Node *) lfirst(lowlist_item), context, false);
13667 0 : appendStringInfoChar(buf, ':');
13668 0 : lowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item);
13669 : }
13670 : /* If subexpression is NULL, get_rule_expr prints nothing */
13671 316 : get_rule_expr((Node *) lfirst(uplist_item), context, false);
13672 316 : appendStringInfoChar(buf, ']');
13673 : }
13674 316 : }
13675 :
13676 : /*
13677 : * quote_identifier - Quote an identifier only if needed
13678 : *
13679 : * When quotes are needed, we palloc the required space; slightly
13680 : * space-wasteful but well worth it for notational simplicity.
13681 : */
13682 : const char *
13683 1889503 : quote_identifier(const char *ident)
13684 : {
13685 : /*
13686 : * Can avoid quoting if ident starts with a lowercase letter or underscore
13687 : * and contains only lowercase letters, digits, and underscores, *and* is
13688 : * not any SQL keyword. Otherwise, supply quotes.
13689 : */
13690 1889503 : int nquotes = 0;
13691 : bool safe;
13692 : const char *ptr;
13693 : char *result;
13694 : char *optr;
13695 :
13696 : /*
13697 : * would like to use <ctype.h> macros here, but they might yield unwanted
13698 : * locale-specific results...
13699 : */
13700 1889503 : safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
13701 :
13702 16819247 : for (ptr = ident; *ptr; ptr++)
13703 : {
13704 14929744 : char ch = *ptr;
13705 :
13706 14929744 : if ((ch >= 'a' && ch <= 'z') ||
13707 2282421 : (ch >= '0' && ch <= '9') ||
13708 : (ch == '_'))
13709 : {
13710 : /* okay */
13711 : }
13712 : else
13713 : {
13714 359293 : safe = false;
13715 359293 : if (ch == '"')
13716 83 : nquotes++;
13717 : }
13718 : }
13719 :
13720 1889503 : if (quote_all_identifiers)
13721 7726 : safe = false;
13722 :
13723 1889503 : if (safe)
13724 : {
13725 : /*
13726 : * Check for keyword. We quote keywords except for unreserved ones.
13727 : * (In some cases we could avoid quoting a col_name or type_func_name
13728 : * keyword, but it seems much harder than it's worth to tell that.)
13729 : *
13730 : * Note: ScanKeywordLookup() does case-insensitive comparison, but
13731 : * that's fine, since we already know we have all-lower-case.
13732 : */
13733 1827306 : int kwnum = ScanKeywordLookup(ident, &ScanKeywords);
13734 :
13735 1827306 : if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
13736 2390 : safe = false;
13737 : }
13738 :
13739 1889503 : if (safe)
13740 1824916 : return ident; /* no change needed */
13741 :
13742 64587 : result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13743 :
13744 64587 : optr = result;
13745 64587 : *optr++ = '"';
13746 515803 : for (ptr = ident; *ptr; ptr++)
13747 : {
13748 451216 : char ch = *ptr;
13749 :
13750 451216 : if (ch == '"')
13751 83 : *optr++ = '"';
13752 451216 : *optr++ = ch;
13753 : }
13754 64587 : *optr++ = '"';
13755 64587 : *optr = '\0';
13756 :
13757 64587 : return result;
13758 : }
13759 :
13760 : /*
13761 : * quote_qualified_identifier - Quote a possibly-qualified identifier
13762 : *
13763 : * Return a name of the form qualifier.ident, or just ident if qualifier
13764 : * is NULL, quoting each component if necessary. The result is palloc'd.
13765 : */
13766 : char *
13767 744182 : quote_qualified_identifier(const char *qualifier,
13768 : const char *ident)
13769 : {
13770 : StringInfoData buf;
13771 :
13772 744182 : initStringInfo(&buf);
13773 744182 : if (qualifier)
13774 260728 : appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
13775 744182 : appendStringInfoString(&buf, quote_identifier(ident));
13776 744182 : return buf.data;
13777 : }
13778 :
13779 : /*
13780 : * get_relation_name
13781 : * Get the unqualified name of a relation specified by OID
13782 : *
13783 : * This differs from the underlying get_rel_name() function in that it will
13784 : * throw error instead of silently returning NULL if the OID is bad.
13785 : */
13786 : static char *
13787 10436 : get_relation_name(Oid relid)
13788 : {
13789 10436 : char *relname = get_rel_name(relid);
13790 :
13791 10436 : if (!relname)
13792 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13793 10436 : return relname;
13794 : }
13795 :
13796 : /*
13797 : * generate_relation_name
13798 : * Compute the name to display for a relation specified by OID
13799 : *
13800 : * The result includes all necessary quoting and schema-prefixing.
13801 : *
13802 : * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
13803 : * We will forcibly qualify the relation name if it equals any CTE name
13804 : * visible in the namespace list.
13805 : */
13806 : static char *
13807 5442 : generate_relation_name(Oid relid, List *namespaces)
13808 : {
13809 : HeapTuple tp;
13810 : Form_pg_class reltup;
13811 : bool need_qual;
13812 : ListCell *nslist;
13813 : char *relname;
13814 : char *nspname;
13815 : char *result;
13816 :
13817 5442 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13818 5442 : if (!HeapTupleIsValid(tp))
13819 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13820 5442 : reltup = (Form_pg_class) GETSTRUCT(tp);
13821 5442 : relname = NameStr(reltup->relname);
13822 :
13823 : /* Check for conflicting CTE name */
13824 5442 : need_qual = false;
13825 8946 : foreach(nslist, namespaces)
13826 : {
13827 3504 : deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
13828 : ListCell *ctlist;
13829 :
13830 3592 : foreach(ctlist, dpns->ctes)
13831 : {
13832 88 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);
13833 :
13834 88 : if (strcmp(cte->ctename, relname) == 0)
13835 : {
13836 0 : need_qual = true;
13837 0 : break;
13838 : }
13839 : }
13840 3504 : if (need_qual)
13841 0 : break;
13842 : }
13843 :
13844 : /* Otherwise, qualify the name if not visible in search path */
13845 5442 : if (!need_qual)
13846 5442 : need_qual = !RelationIsVisible(relid);
13847 :
13848 5442 : if (need_qual)
13849 1617 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
13850 : else
13851 3825 : nspname = NULL;
13852 :
13853 5442 : result = quote_qualified_identifier(nspname, relname);
13854 :
13855 5442 : ReleaseSysCache(tp);
13856 :
13857 5442 : return result;
13858 : }
13859 :
13860 : /*
13861 : * generate_qualified_relation_name
13862 : * Compute the name to display for a relation specified by OID
13863 : *
13864 : * As above, but unconditionally schema-qualify the name.
13865 : */
13866 : static char *
13867 4569 : generate_qualified_relation_name(Oid relid)
13868 : {
13869 : HeapTuple tp;
13870 : Form_pg_class reltup;
13871 : char *relname;
13872 : char *nspname;
13873 : char *result;
13874 :
13875 4569 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13876 4569 : if (!HeapTupleIsValid(tp))
13877 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13878 4569 : reltup = (Form_pg_class) GETSTRUCT(tp);
13879 4569 : relname = NameStr(reltup->relname);
13880 :
13881 4569 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
13882 4569 : if (!nspname)
13883 0 : elog(ERROR, "cache lookup failed for namespace %u",
13884 : reltup->relnamespace);
13885 :
13886 4569 : result = quote_qualified_identifier(nspname, relname);
13887 :
13888 4569 : ReleaseSysCache(tp);
13889 :
13890 4569 : return result;
13891 : }
13892 :
13893 : /*
13894 : * generate_function_name
13895 : * Compute the name to display for a function specified by OID,
13896 : * given that it is being called with the specified actual arg names and
13897 : * types. (Those matter because of ambiguous-function resolution rules.)
13898 : *
13899 : * If we're dealing with a potentially variadic function (in practice, this
13900 : * means a FuncExpr or Aggref, not some other way of calling a function), then
13901 : * has_variadic must specify whether variadic arguments have been merged,
13902 : * and *use_variadic_p will be set to indicate whether to print VARIADIC in
13903 : * the output. For non-FuncExpr cases, has_variadic should be false and
13904 : * use_variadic_p can be NULL.
13905 : *
13906 : * inGroupBy must be true if we're deparsing a GROUP BY clause.
13907 : *
13908 : * The result includes all necessary quoting and schema-prefixing.
13909 : */
13910 : static char *
13911 9653 : generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
13912 : bool has_variadic, bool *use_variadic_p,
13913 : bool inGroupBy)
13914 : {
13915 : char *result;
13916 : HeapTuple proctup;
13917 : Form_pg_proc procform;
13918 : char *proname;
13919 : bool use_variadic;
13920 : char *nspname;
13921 : FuncDetailCode p_result;
13922 : int fgc_flags;
13923 : Oid p_funcid;
13924 : Oid p_rettype;
13925 : bool p_retset;
13926 : int p_nvargs;
13927 : Oid p_vatype;
13928 : Oid *p_true_typeids;
13929 9653 : bool force_qualify = false;
13930 :
13931 9653 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
13932 9653 : if (!HeapTupleIsValid(proctup))
13933 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
13934 9653 : procform = (Form_pg_proc) GETSTRUCT(proctup);
13935 9653 : proname = NameStr(procform->proname);
13936 :
13937 : /*
13938 : * Due to parser hacks to avoid needing to reserve CUBE, we need to force
13939 : * qualification of some function names within GROUP BY.
13940 : */
13941 9653 : if (inGroupBy)
13942 : {
13943 0 : if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
13944 0 : force_qualify = true;
13945 : }
13946 :
13947 : /*
13948 : * Determine whether VARIADIC should be printed. We must do this first
13949 : * since it affects the lookup rules in func_get_detail().
13950 : *
13951 : * We always print VARIADIC if the function has a merged variadic-array
13952 : * argument. Note that this is always the case for functions taking a
13953 : * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
13954 : * and printed the array elements as separate arguments, the call could
13955 : * match a newer non-VARIADIC function.
13956 : */
13957 9653 : if (use_variadic_p)
13958 : {
13959 : /* Parser should not have set funcvariadic unless fn is variadic */
13960 : Assert(!has_variadic || OidIsValid(procform->provariadic));
13961 8676 : use_variadic = has_variadic;
13962 8676 : *use_variadic_p = use_variadic;
13963 : }
13964 : else
13965 : {
13966 : Assert(!has_variadic);
13967 977 : use_variadic = false;
13968 : }
13969 :
13970 : /*
13971 : * The idea here is to schema-qualify only if the parser would fail to
13972 : * resolve the correct function given the unqualified func name with the
13973 : * specified argtypes and VARIADIC flag. But if we already decided to
13974 : * force qualification, then we can skip the lookup and pretend we didn't
13975 : * find it.
13976 : */
13977 9653 : if (!force_qualify)
13978 9653 : p_result = func_get_detail(list_make1(makeString(proname)),
13979 : NIL, argnames, nargs, argtypes,
13980 : !use_variadic, true, false,
13981 : &fgc_flags,
13982 : &p_funcid, &p_rettype,
13983 : &p_retset, &p_nvargs, &p_vatype,
13984 9653 : &p_true_typeids, NULL);
13985 : else
13986 : {
13987 0 : p_result = FUNCDETAIL_NOTFOUND;
13988 0 : p_funcid = InvalidOid;
13989 : }
13990 :
13991 9653 : if ((p_result == FUNCDETAIL_NORMAL ||
13992 664 : p_result == FUNCDETAIL_AGGREGATE ||
13993 9073 : p_result == FUNCDETAIL_WINDOWFUNC) &&
13994 9073 : p_funcid == funcid)
13995 9073 : nspname = NULL;
13996 : else
13997 580 : nspname = get_namespace_name_or_temp(procform->pronamespace);
13998 :
13999 9653 : result = quote_qualified_identifier(nspname, proname);
14000 :
14001 9653 : ReleaseSysCache(proctup);
14002 :
14003 9653 : return result;
14004 : }
14005 :
14006 : /*
14007 : * generate_operator_name
14008 : * Compute the name to display for an operator specified by OID,
14009 : * given that it is being called with the specified actual arg types.
14010 : * (Arg types matter because of ambiguous-operator resolution rules.
14011 : * Pass InvalidOid for unused arg of a unary operator.)
14012 : *
14013 : * The result includes all necessary quoting and schema-prefixing,
14014 : * plus the OPERATOR() decoration needed to use a qualified operator name
14015 : * in an expression.
14016 : */
14017 : static char *
14018 42875 : generate_operator_name(Oid operid, Oid arg1, Oid arg2)
14019 : {
14020 : StringInfoData buf;
14021 : HeapTuple opertup;
14022 : Form_pg_operator operform;
14023 : char *oprname;
14024 : char *nspname;
14025 : Operator p_result;
14026 :
14027 42875 : initStringInfo(&buf);
14028 :
14029 42875 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
14030 42875 : if (!HeapTupleIsValid(opertup))
14031 0 : elog(ERROR, "cache lookup failed for operator %u", operid);
14032 42875 : operform = (Form_pg_operator) GETSTRUCT(opertup);
14033 42875 : oprname = NameStr(operform->oprname);
14034 :
14035 : /*
14036 : * The idea here is to schema-qualify only if the parser would fail to
14037 : * resolve the correct operator given the unqualified op name with the
14038 : * specified argtypes.
14039 : */
14040 42875 : switch (operform->oprkind)
14041 : {
14042 42855 : case 'b':
14043 42855 : p_result = oper(NULL, list_make1(makeString(oprname)), arg1, arg2,
14044 : true, -1);
14045 42855 : break;
14046 20 : case 'l':
14047 20 : p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2,
14048 : true, -1);
14049 20 : break;
14050 0 : default:
14051 0 : elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
14052 : p_result = NULL; /* keep compiler quiet */
14053 : break;
14054 : }
14055 :
14056 42875 : if (p_result != NULL && oprid(p_result) == operid)
14057 42870 : nspname = NULL;
14058 : else
14059 : {
14060 5 : nspname = get_namespace_name_or_temp(operform->oprnamespace);
14061 5 : appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
14062 : }
14063 :
14064 42875 : appendStringInfoString(&buf, oprname);
14065 :
14066 42875 : if (nspname)
14067 5 : appendStringInfoChar(&buf, ')');
14068 :
14069 42875 : if (p_result != NULL)
14070 42870 : ReleaseSysCache(p_result);
14071 :
14072 42875 : ReleaseSysCache(opertup);
14073 :
14074 42875 : return buf.data;
14075 : }
14076 :
14077 : /*
14078 : * generate_operator_clause --- generate a binary-operator WHERE clause
14079 : *
14080 : * This is used for internally-generated-and-executed SQL queries, where
14081 : * precision is essential and readability is secondary. The basic
14082 : * requirement is to append "leftop op rightop" to buf, where leftop and
14083 : * rightop are given as strings and are assumed to yield types leftoptype
14084 : * and rightoptype; the operator is identified by OID. The complexity
14085 : * comes from needing to be sure that the parser will select the desired
14086 : * operator when the query is parsed. We always name the operator using
14087 : * OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
14088 : * We have to emit casts too, if either input isn't already the input type
14089 : * of the operator; else we are at the mercy of the parser's heuristics for
14090 : * ambiguous-operator resolution. The caller must ensure that leftop and
14091 : * rightop are suitable arguments for a cast operation; it's best to insert
14092 : * parentheses if they aren't just variables or parameters.
14093 : */
14094 : void
14095 3160 : generate_operator_clause(StringInfo buf,
14096 : const char *leftop, Oid leftoptype,
14097 : Oid opoid,
14098 : const char *rightop, Oid rightoptype)
14099 : {
14100 : HeapTuple opertup;
14101 : Form_pg_operator operform;
14102 : char *oprname;
14103 : char *nspname;
14104 :
14105 3160 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
14106 3160 : if (!HeapTupleIsValid(opertup))
14107 0 : elog(ERROR, "cache lookup failed for operator %u", opoid);
14108 3160 : operform = (Form_pg_operator) GETSTRUCT(opertup);
14109 : Assert(operform->oprkind == 'b');
14110 3160 : oprname = NameStr(operform->oprname);
14111 :
14112 3160 : nspname = get_namespace_name(operform->oprnamespace);
14113 :
14114 3160 : appendStringInfoString(buf, leftop);
14115 3160 : if (leftoptype != operform->oprleft)
14116 767 : add_cast_to(buf, operform->oprleft);
14117 3160 : appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
14118 3160 : appendStringInfoString(buf, oprname);
14119 3160 : appendStringInfo(buf, ") %s", rightop);
14120 3160 : if (rightoptype != operform->oprright)
14121 613 : add_cast_to(buf, operform->oprright);
14122 :
14123 3160 : ReleaseSysCache(opertup);
14124 3160 : }
14125 :
14126 : /*
14127 : * Add a cast specification to buf. We spell out the type name the hard way,
14128 : * intentionally not using format_type_be(). This is to avoid corner cases
14129 : * for CHARACTER, BIT, and perhaps other types, where specifying the type
14130 : * using SQL-standard syntax results in undesirable data truncation. By
14131 : * doing it this way we can be certain that the cast will have default (-1)
14132 : * target typmod.
14133 : */
14134 : static void
14135 1380 : add_cast_to(StringInfo buf, Oid typid)
14136 : {
14137 : HeapTuple typetup;
14138 : Form_pg_type typform;
14139 : char *typname;
14140 : char *nspname;
14141 :
14142 1380 : typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
14143 1380 : if (!HeapTupleIsValid(typetup))
14144 0 : elog(ERROR, "cache lookup failed for type %u", typid);
14145 1380 : typform = (Form_pg_type) GETSTRUCT(typetup);
14146 :
14147 1380 : typname = NameStr(typform->typname);
14148 1380 : nspname = get_namespace_name_or_temp(typform->typnamespace);
14149 :
14150 1380 : appendStringInfo(buf, "::%s.%s",
14151 : quote_identifier(nspname), quote_identifier(typname));
14152 :
14153 1380 : ReleaseSysCache(typetup);
14154 1380 : }
14155 :
14156 : /*
14157 : * generate_qualified_type_name
14158 : * Compute the name to display for a type specified by OID
14159 : *
14160 : * This is different from format_type_be() in that we unconditionally
14161 : * schema-qualify the name. That also means no special syntax for
14162 : * SQL-standard type names ... although in current usage, this should
14163 : * only get used for domains, so such cases wouldn't occur anyway.
14164 : */
14165 : static char *
14166 9 : generate_qualified_type_name(Oid typid)
14167 : {
14168 : HeapTuple tp;
14169 : Form_pg_type typtup;
14170 : char *typname;
14171 : char *nspname;
14172 : char *result;
14173 :
14174 9 : tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
14175 9 : if (!HeapTupleIsValid(tp))
14176 0 : elog(ERROR, "cache lookup failed for type %u", typid);
14177 9 : typtup = (Form_pg_type) GETSTRUCT(tp);
14178 9 : typname = NameStr(typtup->typname);
14179 :
14180 9 : nspname = get_namespace_name_or_temp(typtup->typnamespace);
14181 9 : if (!nspname)
14182 0 : elog(ERROR, "cache lookup failed for namespace %u",
14183 : typtup->typnamespace);
14184 :
14185 9 : result = quote_qualified_identifier(nspname, typname);
14186 :
14187 9 : ReleaseSysCache(tp);
14188 :
14189 9 : return result;
14190 : }
14191 :
14192 : /*
14193 : * generate_collation_name
14194 : * Compute the name to display for a collation specified by OID
14195 : *
14196 : * The result includes all necessary quoting and schema-prefixing.
14197 : */
14198 : char *
14199 256 : generate_collation_name(Oid collid)
14200 : {
14201 : HeapTuple tp;
14202 : Form_pg_collation colltup;
14203 : char *collname;
14204 : char *nspname;
14205 : char *result;
14206 :
14207 256 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
14208 256 : if (!HeapTupleIsValid(tp))
14209 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
14210 256 : colltup = (Form_pg_collation) GETSTRUCT(tp);
14211 256 : collname = NameStr(colltup->collname);
14212 :
14213 256 : if (!CollationIsVisible(collid))
14214 0 : nspname = get_namespace_name_or_temp(colltup->collnamespace);
14215 : else
14216 256 : nspname = NULL;
14217 :
14218 256 : result = quote_qualified_identifier(nspname, collname);
14219 :
14220 256 : ReleaseSysCache(tp);
14221 :
14222 256 : return result;
14223 : }
14224 :
14225 : /*
14226 : * Given a C string, produce a TEXT datum.
14227 : *
14228 : * We assume that the input was palloc'd and may be freed.
14229 : */
14230 : static text *
14231 26108 : string_to_text(char *str)
14232 : {
14233 : text *result;
14234 :
14235 26108 : result = cstring_to_text(str);
14236 26108 : pfree(str);
14237 26108 : return result;
14238 : }
14239 :
14240 : /*
14241 : * Generate a C string representing a relation options from text[] datum.
14242 : */
14243 : void
14244 139 : get_reloptions(StringInfo buf, Datum reloptions)
14245 : {
14246 : Datum *options;
14247 : int noptions;
14248 : int i;
14249 :
14250 139 : deconstruct_array_builtin(DatumGetArrayTypeP(reloptions), TEXTOID,
14251 : &options, NULL, &noptions);
14252 :
14253 324 : for (i = 0; i < noptions; i++)
14254 : {
14255 185 : char *option = TextDatumGetCString(options[i]);
14256 : char *name;
14257 : char *separator;
14258 : char *value;
14259 :
14260 : /*
14261 : * Each array element should have the form name=value. If the "=" is
14262 : * missing for some reason, treat it like an empty value.
14263 : */
14264 185 : name = option;
14265 185 : separator = strchr(option, '=');
14266 185 : if (separator)
14267 : {
14268 185 : *separator = '\0';
14269 185 : value = separator + 1;
14270 : }
14271 : else
14272 0 : value = "";
14273 :
14274 185 : if (i > 0)
14275 46 : appendStringInfoString(buf, ", ");
14276 185 : appendStringInfo(buf, "%s=", quote_identifier(name));
14277 :
14278 : /*
14279 : * In general we need to quote the value; but to avoid unnecessary
14280 : * clutter, do not quote if it is an identifier that would not need
14281 : * quoting. (We could also allow numbers, but that is a bit trickier
14282 : * than it looks --- for example, are leading zeroes significant? We
14283 : * don't want to assume very much here about what custom reloptions
14284 : * might mean.)
14285 : */
14286 185 : if (quote_identifier(value) == value)
14287 4 : appendStringInfoString(buf, value);
14288 : else
14289 181 : simple_quote_literal(buf, value);
14290 :
14291 185 : pfree(option);
14292 : }
14293 139 : }
14294 :
14295 : /*
14296 : * Generate a C string representing a relation's reloptions, or NULL if none.
14297 : */
14298 : static char *
14299 4499 : flatten_reloptions(Oid relid)
14300 : {
14301 4499 : char *result = NULL;
14302 : HeapTuple tuple;
14303 : Datum reloptions;
14304 : bool isnull;
14305 :
14306 4499 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14307 4499 : if (!HeapTupleIsValid(tuple))
14308 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
14309 :
14310 4499 : reloptions = SysCacheGetAttr(RELOID, tuple,
14311 : Anum_pg_class_reloptions, &isnull);
14312 4499 : if (!isnull)
14313 : {
14314 : StringInfoData buf;
14315 :
14316 105 : initStringInfo(&buf);
14317 105 : get_reloptions(&buf, reloptions);
14318 :
14319 105 : result = buf.data;
14320 : }
14321 :
14322 4499 : ReleaseSysCache(tuple);
14323 :
14324 4499 : return result;
14325 : }
14326 :
14327 : /*
14328 : * get_range_partbound_string
14329 : * A C string representation of one range partition bound
14330 : */
14331 : char *
14332 3142 : get_range_partbound_string(List *bound_datums)
14333 : {
14334 : deparse_context context;
14335 : StringInfoData buf;
14336 : ListCell *cell;
14337 : char *sep;
14338 :
14339 3142 : initStringInfo(&buf);
14340 3142 : memset(&context, 0, sizeof(deparse_context));
14341 3142 : context.buf = &buf;
14342 :
14343 3142 : appendStringInfoChar(&buf, '(');
14344 3142 : sep = "";
14345 6762 : foreach(cell, bound_datums)
14346 : {
14347 3620 : PartitionRangeDatum *datum =
14348 : lfirst_node(PartitionRangeDatum, cell);
14349 :
14350 3620 : appendStringInfoString(&buf, sep);
14351 3620 : if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
14352 148 : appendStringInfoString(&buf, "MINVALUE");
14353 3472 : else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
14354 80 : appendStringInfoString(&buf, "MAXVALUE");
14355 : else
14356 : {
14357 3392 : Const *val = castNode(Const, datum->value);
14358 :
14359 3392 : get_const_expr(val, &context, -1);
14360 : }
14361 3620 : sep = ", ";
14362 : }
14363 3142 : appendStringInfoChar(&buf, ')');
14364 :
14365 3142 : return buf.data;
14366 : }
|