Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * ruleutils.c
4 : * Functions to convert stored expressions/querytrees back to
5 : * source text
6 : *
7 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : *
11 : * IDENTIFICATION
12 : * src/backend/utils/adt/ruleutils.c
13 : *
14 : *-------------------------------------------------------------------------
15 : */
16 : #include "postgres.h"
17 :
18 : #include <ctype.h>
19 : #include <unistd.h>
20 : #include <fcntl.h>
21 :
22 : #include "access/amapi.h"
23 : #include "access/htup_details.h"
24 : #include "access/relation.h"
25 : #include "access/table.h"
26 : #include "catalog/pg_aggregate.h"
27 : #include "catalog/pg_am.h"
28 : #include "catalog/pg_authid.h"
29 : #include "catalog/pg_collation.h"
30 : #include "catalog/pg_constraint.h"
31 : #include "catalog/pg_depend.h"
32 : #include "catalog/pg_language.h"
33 : #include "catalog/pg_opclass.h"
34 : #include "catalog/pg_operator.h"
35 : #include "catalog/pg_partitioned_table.h"
36 : #include "catalog/pg_proc.h"
37 : #include "catalog/pg_statistic_ext.h"
38 : #include "catalog/pg_trigger.h"
39 : #include "catalog/pg_type.h"
40 : #include "commands/defrem.h"
41 : #include "commands/tablespace.h"
42 : #include "common/keywords.h"
43 : #include "executor/spi.h"
44 : #include "funcapi.h"
45 : #include "mb/pg_wchar.h"
46 : #include "miscadmin.h"
47 : #include "nodes/makefuncs.h"
48 : #include "nodes/nodeFuncs.h"
49 : #include "nodes/pathnodes.h"
50 : #include "optimizer/optimizer.h"
51 : #include "parser/parse_agg.h"
52 : #include "parser/parse_func.h"
53 : #include "parser/parse_oper.h"
54 : #include "parser/parse_relation.h"
55 : #include "parser/parser.h"
56 : #include "parser/parsetree.h"
57 : #include "rewrite/rewriteHandler.h"
58 : #include "rewrite/rewriteManip.h"
59 : #include "rewrite/rewriteSupport.h"
60 : #include "utils/array.h"
61 : #include "utils/builtins.h"
62 : #include "utils/fmgroids.h"
63 : #include "utils/guc.h"
64 : #include "utils/hsearch.h"
65 : #include "utils/lsyscache.h"
66 : #include "utils/partcache.h"
67 : #include "utils/rel.h"
68 : #include "utils/ruleutils.h"
69 : #include "utils/snapmgr.h"
70 : #include "utils/syscache.h"
71 : #include "utils/typcache.h"
72 : #include "utils/varlena.h"
73 : #include "utils/xml.h"
74 :
75 : /* ----------
76 : * Pretty formatting constants
77 : * ----------
78 : */
79 :
80 : /* Indent counts */
81 : #define PRETTYINDENT_STD 8
82 : #define PRETTYINDENT_JOIN 4
83 : #define PRETTYINDENT_VAR 4
84 :
85 : #define PRETTYINDENT_LIMIT 40 /* wrap limit */
86 :
87 : /* Pretty flags */
88 : #define PRETTYFLAG_PAREN 0x0001
89 : #define PRETTYFLAG_INDENT 0x0002
90 : #define PRETTYFLAG_SCHEMA 0x0004
91 :
92 : /* Standard conversion of a "bool pretty" option to detailed flags */
93 : #define GET_PRETTY_FLAGS(pretty) \
94 : ((pretty) ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) \
95 : : PRETTYFLAG_INDENT)
96 :
97 : /* Default line length for pretty-print wrapping: 0 means wrap always */
98 : #define WRAP_COLUMN_DEFAULT 0
99 :
100 : /* macros to test if pretty action needed */
101 : #define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
102 : #define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
103 : #define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
104 :
105 :
106 : /* ----------
107 : * Local data types
108 : * ----------
109 : */
110 :
111 : /* Context info needed for invoking a recursive querytree display routine */
112 : typedef struct
113 : {
114 : StringInfo buf; /* output buffer to append to */
115 : List *namespaces; /* List of deparse_namespace nodes */
116 : TupleDesc resultDesc; /* if top level of a view, the view's tupdesc */
117 : List *targetList; /* Current query level's SELECT targetlist */
118 : List *windowClause; /* Current query level's WINDOW clause */
119 : int prettyFlags; /* enabling of pretty-print functions */
120 : int wrapColumn; /* max line length, or -1 for no limit */
121 : int indentLevel; /* current indent level for pretty-print */
122 : bool varprefix; /* true to print prefixes on Vars */
123 : bool colNamesVisible; /* do we care about output column names? */
124 : bool inGroupBy; /* deparsing GROUP BY clause? */
125 : bool varInOrderBy; /* deparsing simple Var in ORDER BY? */
126 : Bitmapset *appendparents; /* if not null, map child Vars of these relids
127 : * back to the parent rel */
128 : } deparse_context;
129 :
130 : /*
131 : * Each level of query context around a subtree needs a level of Var namespace.
132 : * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
133 : * the current context's namespaces list.
134 : *
135 : * rtable is the list of actual RTEs from the Query or PlannedStmt.
136 : * rtable_names holds the alias name to be used for each RTE (either a C
137 : * string, or NULL for nameless RTEs such as unnamed joins).
138 : * rtable_columns holds the column alias names to be used for each RTE.
139 : *
140 : * subplans is a list of Plan trees for SubPlans and CTEs (it's only used
141 : * in the PlannedStmt case).
142 : * ctes is a list of CommonTableExpr nodes (only used in the Query case).
143 : * appendrels, if not null (it's only used in the PlannedStmt case), is an
144 : * array of AppendRelInfo nodes, indexed by child relid. We use that to map
145 : * child-table Vars to their inheritance parents.
146 : *
147 : * In some cases we need to make names of merged JOIN USING columns unique
148 : * across the whole query, not only per-RTE. If so, unique_using is true
149 : * and using_names is a list of C strings representing names already assigned
150 : * to USING columns.
151 : *
152 : * When deparsing plan trees, there is always just a single item in the
153 : * deparse_namespace list (since a plan tree never contains Vars with
154 : * varlevelsup > 0). We store the Plan node that is the immediate
155 : * parent of the expression to be deparsed, as well as a list of that
156 : * Plan's ancestors. In addition, we store its outer and inner subplan nodes,
157 : * as well as their targetlists, and the index tlist if the current plan node
158 : * might contain INDEX_VAR Vars. (These fields could be derived on-the-fly
159 : * from the current Plan node, but it seems notationally clearer to set them
160 : * up as separate fields.)
161 : */
162 : typedef struct
163 : {
164 : List *rtable; /* List of RangeTblEntry nodes */
165 : List *rtable_names; /* Parallel list of names for RTEs */
166 : List *rtable_columns; /* Parallel list of deparse_columns structs */
167 : List *subplans; /* List of Plan trees for SubPlans */
168 : List *ctes; /* List of CommonTableExpr nodes */
169 : AppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */
170 : char *ret_old_alias; /* alias for OLD in RETURNING list */
171 : char *ret_new_alias; /* alias for NEW in RETURNING list */
172 : /* Workspace for column alias assignment: */
173 : bool unique_using; /* Are we making USING names globally unique */
174 : List *using_names; /* List of assigned names for USING columns */
175 : /* Remaining fields are used only when deparsing a Plan tree: */
176 : Plan *plan; /* immediate parent of current expression */
177 : List *ancestors; /* ancestors of plan */
178 : Plan *outer_plan; /* outer subnode, or NULL if none */
179 : Plan *inner_plan; /* inner subnode, or NULL if none */
180 : List *outer_tlist; /* referent for OUTER_VAR Vars */
181 : List *inner_tlist; /* referent for INNER_VAR Vars */
182 : List *index_tlist; /* referent for INDEX_VAR Vars */
183 : /* Special namespace representing a function signature: */
184 : char *funcname;
185 : int numargs;
186 : char **argnames;
187 : } deparse_namespace;
188 :
189 : /*
190 : * Per-relation data about column alias names.
191 : *
192 : * Selecting aliases is unreasonably complicated because of the need to dump
193 : * rules/views whose underlying tables may have had columns added, deleted, or
194 : * renamed since the query was parsed. We must nonetheless print the rule/view
195 : * in a form that can be reloaded and will produce the same results as before.
196 : *
197 : * For each RTE used in the query, we must assign column aliases that are
198 : * unique within that RTE. SQL does not require this of the original query,
199 : * but due to factors such as *-expansion we need to be able to uniquely
200 : * reference every column in a decompiled query. As long as we qualify all
201 : * column references, per-RTE uniqueness is sufficient for that.
202 : *
203 : * However, we can't ensure per-column name uniqueness for unnamed join RTEs,
204 : * since they just inherit column names from their input RTEs, and we can't
205 : * rename the columns at the join level. Most of the time this isn't an issue
206 : * because we don't need to reference the join's output columns as such; we
207 : * can reference the input columns instead. That approach can fail for merged
208 : * JOIN USING columns, however, so when we have one of those in an unnamed
209 : * join, we have to make that column's alias globally unique across the whole
210 : * query to ensure it can be referenced unambiguously.
211 : *
212 : * Another problem is that a JOIN USING clause requires the columns to be
213 : * merged to have the same aliases in both input RTEs, and that no other
214 : * columns in those RTEs or their children conflict with the USING names.
215 : * To handle that, we do USING-column alias assignment in a recursive
216 : * traversal of the query's jointree. When descending through a JOIN with
217 : * USING, we preassign the USING column names to the child columns, overriding
218 : * other rules for column alias assignment. We also mark each RTE with a list
219 : * of all USING column names selected for joins containing that RTE, so that
220 : * when we assign other columns' aliases later, we can avoid conflicts.
221 : *
222 : * Another problem is that if a JOIN's input tables have had columns added or
223 : * deleted since the query was parsed, we must generate a column alias list
224 : * for the join that matches the current set of input columns --- otherwise, a
225 : * change in the number of columns in the left input would throw off matching
226 : * of aliases to columns of the right input. Thus, positions in the printable
227 : * column alias list are not necessarily one-for-one with varattnos of the
228 : * JOIN, so we need a separate new_colnames[] array for printing purposes.
229 : *
230 : * Finally, when dealing with wide tables we risk O(N^2) costs in assigning
231 : * non-duplicate column names. We ameliorate that by using a hash table that
232 : * holds all the strings appearing in colnames, new_colnames, and parentUsing.
233 : */
234 : typedef struct
235 : {
236 : /*
237 : * colnames is an array containing column aliases to use for columns that
238 : * existed when the query was parsed. Dropped columns have NULL entries.
239 : * This array can be directly indexed by varattno to get a Var's name.
240 : *
241 : * Non-NULL entries are guaranteed unique within the RTE, *except* when
242 : * this is for an unnamed JOIN RTE. In that case we merely copy up names
243 : * from the two input RTEs.
244 : *
245 : * During the recursive descent in set_using_names(), forcible assignment
246 : * of a child RTE's column name is represented by pre-setting that element
247 : * of the child's colnames array. So at that stage, NULL entries in this
248 : * array just mean that no name has been preassigned, not necessarily that
249 : * the column is dropped.
250 : */
251 : int num_cols; /* length of colnames[] array */
252 : char **colnames; /* array of C strings and NULLs */
253 :
254 : /*
255 : * new_colnames is an array containing column aliases to use for columns
256 : * that would exist if the query was re-parsed against the current
257 : * definitions of its base tables. This is what to print as the column
258 : * alias list for the RTE. This array does not include dropped columns,
259 : * but it will include columns added since original parsing. Indexes in
260 : * it therefore have little to do with current varattno values. As above,
261 : * entries are unique unless this is for an unnamed JOIN RTE. (In such an
262 : * RTE, we never actually print this array, but we must compute it anyway
263 : * for possible use in computing column names of upper joins.) The
264 : * parallel array is_new_col marks which of these columns are new since
265 : * original parsing. Entries with is_new_col false must match the
266 : * non-NULL colnames entries one-for-one.
267 : */
268 : int num_new_cols; /* length of new_colnames[] array */
269 : char **new_colnames; /* array of C strings */
270 : bool *is_new_col; /* array of bool flags */
271 :
272 : /* This flag tells whether we should actually print a column alias list */
273 : bool printaliases;
274 :
275 : /* This list has all names used as USING names in joins above this RTE */
276 : List *parentUsing; /* names assigned to parent merged columns */
277 :
278 : /*
279 : * If this struct is for a JOIN RTE, we fill these fields during the
280 : * set_using_names() pass to describe its relationship to its child RTEs.
281 : *
282 : * leftattnos and rightattnos are arrays with one entry per existing
283 : * output column of the join (hence, indexable by join varattno). For a
284 : * simple reference to a column of the left child, leftattnos[i] is the
285 : * child RTE's attno and rightattnos[i] is zero; and conversely for a
286 : * column of the right child. But for merged columns produced by JOIN
287 : * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.
288 : * Note that a simple reference might be to a child RTE column that's been
289 : * dropped; but that's OK since the column could not be used in the query.
290 : *
291 : * If it's a JOIN USING, usingNames holds the alias names selected for the
292 : * merged columns (these might be different from the original USING list,
293 : * if we had to modify names to achieve uniqueness).
294 : */
295 : int leftrti; /* rangetable index of left child */
296 : int rightrti; /* rangetable index of right child */
297 : int *leftattnos; /* left-child varattnos of join cols, or 0 */
298 : int *rightattnos; /* right-child varattnos of join cols, or 0 */
299 : List *usingNames; /* names assigned to merged columns */
300 :
301 : /*
302 : * Hash table holding copies of all the strings appearing in this struct's
303 : * colnames, new_colnames, and parentUsing. We use a hash table only for
304 : * sufficiently wide relations, and only during the colname-assignment
305 : * functions set_relation_column_names and set_join_column_names;
306 : * otherwise, names_hash is NULL.
307 : */
308 : HTAB *names_hash; /* entries are just strings */
309 : } deparse_columns;
310 :
311 : /* This macro is analogous to rt_fetch(), but for deparse_columns structs */
312 : #define deparse_columns_fetch(rangetable_index, dpns) \
313 : ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
314 :
315 : /*
316 : * Entry in set_rtable_names' hash table
317 : */
318 : typedef struct
319 : {
320 : char name[NAMEDATALEN]; /* Hash key --- must be first */
321 : int counter; /* Largest addition used so far for name */
322 : } NameHashEntry;
323 :
324 : /* Callback signature for resolve_special_varno() */
325 : typedef void (*rsv_callback) (Node *node, deparse_context *context,
326 : void *callback_arg);
327 :
328 :
329 : /* ----------
330 : * Global data
331 : * ----------
332 : */
333 : static SPIPlanPtr plan_getrulebyoid = NULL;
334 : static const char *const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
335 : static SPIPlanPtr plan_getviewrule = NULL;
336 : static const char *const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
337 :
338 : /* GUC parameters */
339 : bool quote_all_identifiers = false;
340 :
341 :
342 : /* ----------
343 : * Local functions
344 : *
345 : * Most of these functions used to use fixed-size buffers to build their
346 : * results. Now, they take an (already initialized) StringInfo object
347 : * as a parameter, and append their text output to its contents.
348 : * ----------
349 : */
350 : static char *deparse_expression_pretty(Node *expr, List *dpcontext,
351 : bool forceprefix, bool showimplicit,
352 : int prettyFlags, int startIndent);
353 : static char *pg_get_viewdef_worker(Oid viewoid,
354 : int prettyFlags, int wrapColumn);
355 : static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
356 : static int decompile_column_index_array(Datum column_index_array, Oid relId,
357 : bool withPeriod, StringInfo buf);
358 : static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
359 : static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
360 : const Oid *excludeOps,
361 : bool attrsOnly, bool keysOnly,
362 : bool showTblSpc, bool inherits,
363 : int prettyFlags, bool missing_ok);
364 : static char *pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
365 : bool missing_ok);
366 : static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
367 : bool attrsOnly, bool missing_ok);
368 : static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
369 : int prettyFlags, bool missing_ok);
370 : static text *pg_get_expr_worker(text *expr, Oid relid, int prettyFlags);
371 : static int print_function_arguments(StringInfo buf, HeapTuple proctup,
372 : bool print_table_args, bool print_defaults);
373 : static void print_function_rettype(StringInfo buf, HeapTuple proctup);
374 : static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
375 : static void print_function_sqlbody(StringInfo buf, HeapTuple proctup);
376 : static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
377 : Bitmapset *rels_used);
378 : static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
379 : List *parent_namespaces);
380 : static void set_simple_column_names(deparse_namespace *dpns);
381 : static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
382 : static void set_using_names(deparse_namespace *dpns, Node *jtnode,
383 : List *parentUsing);
384 : static void set_relation_column_names(deparse_namespace *dpns,
385 : RangeTblEntry *rte,
386 : deparse_columns *colinfo);
387 : static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
388 : deparse_columns *colinfo);
389 : static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
390 : deparse_columns *colinfo);
391 : static char *make_colname_unique(char *colname, deparse_namespace *dpns,
392 : deparse_columns *colinfo);
393 : static void expand_colnames_array_to(deparse_columns *colinfo, int n);
394 : static void build_colinfo_names_hash(deparse_columns *colinfo);
395 : static void add_to_names_hash(deparse_columns *colinfo, const char *name);
396 : static void destroy_colinfo_names_hash(deparse_columns *colinfo);
397 : static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
398 : deparse_columns *colinfo);
399 : static char *get_rtable_name(int rtindex, deparse_context *context);
400 : static void set_deparse_plan(deparse_namespace *dpns, Plan *plan);
401 : static Plan *find_recursive_union(deparse_namespace *dpns,
402 : WorkTableScan *wtscan);
403 : static void push_child_plan(deparse_namespace *dpns, Plan *plan,
404 : deparse_namespace *save_dpns);
405 : static void pop_child_plan(deparse_namespace *dpns,
406 : deparse_namespace *save_dpns);
407 : static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
408 : deparse_namespace *save_dpns);
409 : static void pop_ancestor_plan(deparse_namespace *dpns,
410 : deparse_namespace *save_dpns);
411 : static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
412 : int prettyFlags);
413 : static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
414 : int prettyFlags, int wrapColumn);
415 : static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
416 : TupleDesc resultDesc, bool colNamesVisible,
417 : int prettyFlags, int wrapColumn, int startIndent);
418 : static void get_values_def(List *values_lists, deparse_context *context);
419 : static void get_with_clause(Query *query, deparse_context *context);
420 : static void get_select_query_def(Query *query, deparse_context *context);
421 : static void get_insert_query_def(Query *query, deparse_context *context);
422 : static void get_update_query_def(Query *query, deparse_context *context);
423 : static void get_update_query_targetlist_def(Query *query, List *targetList,
424 : deparse_context *context,
425 : RangeTblEntry *rte);
426 : static void get_delete_query_def(Query *query, deparse_context *context);
427 : static void get_merge_query_def(Query *query, deparse_context *context);
428 : static void get_utility_query_def(Query *query, deparse_context *context);
429 : static void get_basic_select_query(Query *query, deparse_context *context);
430 : static void get_target_list(List *targetList, deparse_context *context);
431 : static void get_returning_clause(Query *query, deparse_context *context);
432 : static void get_setop_query(Node *setOp, Query *query,
433 : deparse_context *context);
434 : static Node *get_rule_sortgroupclause(Index ref, List *tlist,
435 : bool force_colno,
436 : deparse_context *context);
437 : static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
438 : bool omit_parens, deparse_context *context);
439 : static void get_rule_orderby(List *orderList, List *targetList,
440 : bool force_colno, deparse_context *context);
441 : static void get_rule_windowclause(Query *query, deparse_context *context);
442 : static void get_rule_windowspec(WindowClause *wc, List *targetList,
443 : deparse_context *context);
444 : static char *get_variable(Var *var, int levelsup, bool istoplevel,
445 : deparse_context *context);
446 : static void get_special_variable(Node *node, deparse_context *context,
447 : void *callback_arg);
448 : static void resolve_special_varno(Node *node, deparse_context *context,
449 : rsv_callback callback, void *callback_arg);
450 : static Node *find_param_referent(Param *param, deparse_context *context,
451 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
452 : static SubPlan *find_param_generator(Param *param, deparse_context *context,
453 : int *column_p);
454 : static SubPlan *find_param_generator_initplan(Param *param, Plan *plan,
455 : int *column_p);
456 : static void get_parameter(Param *param, deparse_context *context);
457 : static const char *get_simple_binary_op_name(OpExpr *expr);
458 : static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
459 : static void appendContextKeyword(deparse_context *context, const char *str,
460 : int indentBefore, int indentAfter, int indentPlus);
461 : static void removeStringInfoSpaces(StringInfo str);
462 : static void get_rule_expr(Node *node, deparse_context *context,
463 : bool showimplicit);
464 : static void get_rule_expr_toplevel(Node *node, deparse_context *context,
465 : bool showimplicit);
466 : static void get_rule_list_toplevel(List *lst, deparse_context *context,
467 : bool showimplicit);
468 : static void get_rule_expr_funccall(Node *node, deparse_context *context,
469 : bool showimplicit);
470 : static bool looks_like_function(Node *node);
471 : static void get_oper_expr(OpExpr *expr, deparse_context *context);
472 : static void get_func_expr(FuncExpr *expr, deparse_context *context,
473 : bool showimplicit);
474 : static void get_agg_expr(Aggref *aggref, deparse_context *context,
475 : Aggref *original_aggref);
476 : static void get_agg_expr_helper(Aggref *aggref, deparse_context *context,
477 : Aggref *original_aggref, const char *funcname,
478 : const char *options, bool is_json_objectagg);
479 : static void get_agg_combine_expr(Node *node, deparse_context *context,
480 : void *callback_arg);
481 : static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
482 : static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
483 : const char *funcname, const char *options,
484 : bool is_json_objectagg);
485 : static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
486 : static void get_coercion_expr(Node *arg, deparse_context *context,
487 : Oid resulttype, int32 resulttypmod,
488 : Node *parentNode);
489 : static void get_const_expr(Const *constval, deparse_context *context,
490 : int showtype);
491 : static void get_const_collation(Const *constval, deparse_context *context);
492 : static void get_json_format(JsonFormat *format, StringInfo buf);
493 : static void get_json_returning(JsonReturning *returning, StringInfo buf,
494 : bool json_format_by_default);
495 : static void get_json_constructor(JsonConstructorExpr *ctor,
496 : deparse_context *context, bool showimplicit);
497 : static void get_json_constructor_options(JsonConstructorExpr *ctor,
498 : StringInfo buf);
499 : static void get_json_agg_constructor(JsonConstructorExpr *ctor,
500 : deparse_context *context,
501 : const char *funcname,
502 : bool is_json_objectagg);
503 : static void simple_quote_literal(StringInfo buf, const char *val);
504 : static void get_sublink_expr(SubLink *sublink, deparse_context *context);
505 : static void get_tablefunc(TableFunc *tf, deparse_context *context,
506 : bool showimplicit);
507 : static void get_from_clause(Query *query, const char *prefix,
508 : deparse_context *context);
509 : static void get_from_clause_item(Node *jtnode, Query *query,
510 : deparse_context *context);
511 : static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
512 : deparse_context *context);
513 : static void get_column_alias_list(deparse_columns *colinfo,
514 : deparse_context *context);
515 : static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
516 : deparse_columns *colinfo,
517 : deparse_context *context);
518 : static void get_tablesample_def(TableSampleClause *tablesample,
519 : deparse_context *context);
520 : static void get_opclass_name(Oid opclass, Oid actual_datatype,
521 : StringInfo buf);
522 : static Node *processIndirection(Node *node, deparse_context *context);
523 : static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
524 : static char *get_relation_name(Oid relid);
525 : static char *generate_relation_name(Oid relid, List *namespaces);
526 : static char *generate_qualified_relation_name(Oid relid);
527 : static char *generate_function_name(Oid funcid, int nargs,
528 : List *argnames, Oid *argtypes,
529 : bool has_variadic, bool *use_variadic_p,
530 : bool inGroupBy);
531 : static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
532 : static void add_cast_to(StringInfo buf, Oid typid);
533 : static char *generate_qualified_type_name(Oid typid);
534 : static text *string_to_text(char *str);
535 : static char *flatten_reloptions(Oid relid);
536 : static void get_reloptions(StringInfo buf, Datum reloptions);
537 : static void get_json_path_spec(Node *path_spec, deparse_context *context,
538 : bool showimplicit);
539 : static void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
540 : deparse_context *context,
541 : bool showimplicit);
542 : static void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
543 : deparse_context *context,
544 : bool showimplicit,
545 : bool needcomma);
546 :
547 : #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
548 :
549 :
550 : /* ----------
551 : * pg_get_ruledef - Do it all and return a text
552 : * that could be used as a statement
553 : * to recreate the rule
554 : * ----------
555 : */
556 : Datum
557 450 : pg_get_ruledef(PG_FUNCTION_ARGS)
558 : {
559 450 : Oid ruleoid = PG_GETARG_OID(0);
560 : int prettyFlags;
561 : char *res;
562 :
563 450 : prettyFlags = PRETTYFLAG_INDENT;
564 :
565 450 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
566 :
567 450 : if (res == NULL)
568 6 : PG_RETURN_NULL();
569 :
570 444 : PG_RETURN_TEXT_P(string_to_text(res));
571 : }
572 :
573 :
574 : Datum
575 114 : pg_get_ruledef_ext(PG_FUNCTION_ARGS)
576 : {
577 114 : Oid ruleoid = PG_GETARG_OID(0);
578 114 : bool pretty = PG_GETARG_BOOL(1);
579 : int prettyFlags;
580 : char *res;
581 :
582 114 : prettyFlags = GET_PRETTY_FLAGS(pretty);
583 :
584 114 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
585 :
586 114 : if (res == NULL)
587 0 : PG_RETURN_NULL();
588 :
589 114 : PG_RETURN_TEXT_P(string_to_text(res));
590 : }
591 :
592 :
593 : static char *
594 564 : pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
595 : {
596 : Datum args[1];
597 : char nulls[1];
598 : int spirc;
599 : HeapTuple ruletup;
600 : TupleDesc rulettc;
601 : StringInfoData buf;
602 :
603 : /*
604 : * Do this first so that string is alloc'd in outer context not SPI's.
605 : */
606 564 : initStringInfo(&buf);
607 :
608 : /*
609 : * Connect to SPI manager
610 : */
611 564 : SPI_connect();
612 :
613 : /*
614 : * On the first call prepare the plan to lookup pg_rewrite. We read
615 : * pg_rewrite over the SPI manager instead of using the syscache to be
616 : * checked for read access on pg_rewrite.
617 : */
618 564 : if (plan_getrulebyoid == NULL)
619 : {
620 : Oid argtypes[1];
621 : SPIPlanPtr plan;
622 :
623 40 : argtypes[0] = OIDOID;
624 40 : plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
625 40 : if (plan == NULL)
626 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
627 40 : SPI_keepplan(plan);
628 40 : plan_getrulebyoid = plan;
629 : }
630 :
631 : /*
632 : * Get the pg_rewrite tuple for this rule
633 : */
634 564 : args[0] = ObjectIdGetDatum(ruleoid);
635 564 : nulls[0] = ' ';
636 564 : spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
637 564 : if (spirc != SPI_OK_SELECT)
638 0 : elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
639 564 : if (SPI_processed != 1)
640 : {
641 : /*
642 : * There is no tuple data available here, just keep the output buffer
643 : * empty.
644 : */
645 : }
646 : else
647 : {
648 : /*
649 : * Get the rule's definition and put it into executor's memory
650 : */
651 558 : ruletup = SPI_tuptable->vals[0];
652 558 : rulettc = SPI_tuptable->tupdesc;
653 558 : make_ruledef(&buf, ruletup, rulettc, prettyFlags);
654 : }
655 :
656 : /*
657 : * Disconnect from SPI manager
658 : */
659 564 : if (SPI_finish() != SPI_OK_FINISH)
660 0 : elog(ERROR, "SPI_finish failed");
661 :
662 564 : if (buf.len == 0)
663 6 : return NULL;
664 :
665 558 : return buf.data;
666 : }
667 :
668 :
669 : /* ----------
670 : * pg_get_viewdef - Mainly the same thing, but we
671 : * only return the SELECT part of a view
672 : * ----------
673 : */
674 : Datum
675 2378 : pg_get_viewdef(PG_FUNCTION_ARGS)
676 : {
677 : /* By OID */
678 2378 : Oid viewoid = PG_GETARG_OID(0);
679 : int prettyFlags;
680 : char *res;
681 :
682 2378 : prettyFlags = PRETTYFLAG_INDENT;
683 :
684 2378 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
685 :
686 2378 : if (res == NULL)
687 6 : PG_RETURN_NULL();
688 :
689 2372 : PG_RETURN_TEXT_P(string_to_text(res));
690 : }
691 :
692 :
693 : Datum
694 556 : pg_get_viewdef_ext(PG_FUNCTION_ARGS)
695 : {
696 : /* By OID */
697 556 : Oid viewoid = PG_GETARG_OID(0);
698 556 : bool pretty = PG_GETARG_BOOL(1);
699 : int prettyFlags;
700 : char *res;
701 :
702 556 : prettyFlags = GET_PRETTY_FLAGS(pretty);
703 :
704 556 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
705 :
706 556 : if (res == NULL)
707 0 : PG_RETURN_NULL();
708 :
709 556 : PG_RETURN_TEXT_P(string_to_text(res));
710 : }
711 :
712 : Datum
713 6 : pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
714 : {
715 : /* By OID */
716 6 : Oid viewoid = PG_GETARG_OID(0);
717 6 : int wrap = PG_GETARG_INT32(1);
718 : int prettyFlags;
719 : char *res;
720 :
721 : /* calling this implies we want pretty printing */
722 6 : prettyFlags = GET_PRETTY_FLAGS(true);
723 :
724 6 : res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
725 :
726 6 : if (res == NULL)
727 0 : PG_RETURN_NULL();
728 :
729 6 : PG_RETURN_TEXT_P(string_to_text(res));
730 : }
731 :
732 : Datum
733 72 : pg_get_viewdef_name(PG_FUNCTION_ARGS)
734 : {
735 : /* By qualified name */
736 72 : text *viewname = PG_GETARG_TEXT_PP(0);
737 : int prettyFlags;
738 : RangeVar *viewrel;
739 : Oid viewoid;
740 : char *res;
741 :
742 72 : prettyFlags = PRETTYFLAG_INDENT;
743 :
744 : /* Look up view name. Can't lock it - we might not have privileges. */
745 72 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
746 72 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
747 :
748 72 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
749 :
750 72 : if (res == NULL)
751 0 : PG_RETURN_NULL();
752 :
753 72 : PG_RETURN_TEXT_P(string_to_text(res));
754 : }
755 :
756 :
757 : Datum
758 402 : pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
759 : {
760 : /* By qualified name */
761 402 : text *viewname = PG_GETARG_TEXT_PP(0);
762 402 : bool pretty = PG_GETARG_BOOL(1);
763 : int prettyFlags;
764 : RangeVar *viewrel;
765 : Oid viewoid;
766 : char *res;
767 :
768 402 : prettyFlags = GET_PRETTY_FLAGS(pretty);
769 :
770 : /* Look up view name. Can't lock it - we might not have privileges. */
771 402 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
772 402 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
773 :
774 402 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
775 :
776 402 : if (res == NULL)
777 0 : PG_RETURN_NULL();
778 :
779 402 : PG_RETURN_TEXT_P(string_to_text(res));
780 : }
781 :
782 : /*
783 : * Common code for by-OID and by-name variants of pg_get_viewdef
784 : */
785 : static char *
786 3414 : pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
787 : {
788 : Datum args[2];
789 : char nulls[2];
790 : int spirc;
791 : HeapTuple ruletup;
792 : TupleDesc rulettc;
793 : StringInfoData buf;
794 :
795 : /*
796 : * Do this first so that string is alloc'd in outer context not SPI's.
797 : */
798 3414 : initStringInfo(&buf);
799 :
800 : /*
801 : * Connect to SPI manager
802 : */
803 3414 : SPI_connect();
804 :
805 : /*
806 : * On the first call prepare the plan to lookup pg_rewrite. We read
807 : * pg_rewrite over the SPI manager instead of using the syscache to be
808 : * checked for read access on pg_rewrite.
809 : */
810 3414 : if (plan_getviewrule == NULL)
811 : {
812 : Oid argtypes[2];
813 : SPIPlanPtr plan;
814 :
815 234 : argtypes[0] = OIDOID;
816 234 : argtypes[1] = NAMEOID;
817 234 : plan = SPI_prepare(query_getviewrule, 2, argtypes);
818 234 : if (plan == NULL)
819 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
820 234 : SPI_keepplan(plan);
821 234 : plan_getviewrule = plan;
822 : }
823 :
824 : /*
825 : * Get the pg_rewrite tuple for the view's SELECT rule
826 : */
827 3414 : args[0] = ObjectIdGetDatum(viewoid);
828 3414 : args[1] = DirectFunctionCall1(namein, CStringGetDatum(ViewSelectRuleName));
829 3414 : nulls[0] = ' ';
830 3414 : nulls[1] = ' ';
831 3414 : spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
832 3414 : if (spirc != SPI_OK_SELECT)
833 0 : elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
834 3414 : if (SPI_processed != 1)
835 : {
836 : /*
837 : * There is no tuple data available here, just keep the output buffer
838 : * empty.
839 : */
840 : }
841 : else
842 : {
843 : /*
844 : * Get the rule's definition and put it into executor's memory
845 : */
846 3408 : ruletup = SPI_tuptable->vals[0];
847 3408 : rulettc = SPI_tuptable->tupdesc;
848 3408 : make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
849 : }
850 :
851 : /*
852 : * Disconnect from SPI manager
853 : */
854 3414 : if (SPI_finish() != SPI_OK_FINISH)
855 0 : elog(ERROR, "SPI_finish failed");
856 :
857 3414 : if (buf.len == 0)
858 6 : return NULL;
859 :
860 3408 : return buf.data;
861 : }
862 :
863 : /* ----------
864 : * pg_get_triggerdef - Get the definition of a trigger
865 : * ----------
866 : */
867 : Datum
868 204 : pg_get_triggerdef(PG_FUNCTION_ARGS)
869 : {
870 204 : Oid trigid = PG_GETARG_OID(0);
871 : char *res;
872 :
873 204 : res = pg_get_triggerdef_worker(trigid, false);
874 :
875 204 : if (res == NULL)
876 6 : PG_RETURN_NULL();
877 :
878 198 : PG_RETURN_TEXT_P(string_to_text(res));
879 : }
880 :
881 : Datum
882 1130 : pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
883 : {
884 1130 : Oid trigid = PG_GETARG_OID(0);
885 1130 : bool pretty = PG_GETARG_BOOL(1);
886 : char *res;
887 :
888 1130 : res = pg_get_triggerdef_worker(trigid, pretty);
889 :
890 1130 : if (res == NULL)
891 0 : PG_RETURN_NULL();
892 :
893 1130 : PG_RETURN_TEXT_P(string_to_text(res));
894 : }
895 :
896 : static char *
897 1334 : pg_get_triggerdef_worker(Oid trigid, bool pretty)
898 : {
899 : HeapTuple ht_trig;
900 : Form_pg_trigger trigrec;
901 : StringInfoData buf;
902 : Relation tgrel;
903 : ScanKeyData skey[1];
904 : SysScanDesc tgscan;
905 1334 : int findx = 0;
906 : char *tgname;
907 : char *tgoldtable;
908 : char *tgnewtable;
909 : Datum value;
910 : bool isnull;
911 :
912 : /*
913 : * Fetch the pg_trigger tuple by the Oid of the trigger
914 : */
915 1334 : tgrel = table_open(TriggerRelationId, AccessShareLock);
916 :
917 1334 : ScanKeyInit(&skey[0],
918 : Anum_pg_trigger_oid,
919 : BTEqualStrategyNumber, F_OIDEQ,
920 : ObjectIdGetDatum(trigid));
921 :
922 1334 : tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
923 : NULL, 1, skey);
924 :
925 1334 : ht_trig = systable_getnext(tgscan);
926 :
927 1334 : if (!HeapTupleIsValid(ht_trig))
928 : {
929 6 : systable_endscan(tgscan);
930 6 : table_close(tgrel, AccessShareLock);
931 6 : return NULL;
932 : }
933 :
934 1328 : trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
935 :
936 : /*
937 : * Start the trigger definition. Note that the trigger's name should never
938 : * be schema-qualified, but the trigger rel's name may be.
939 : */
940 1328 : initStringInfo(&buf);
941 :
942 1328 : tgname = NameStr(trigrec->tgname);
943 2656 : appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
944 1328 : OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
945 : quote_identifier(tgname));
946 :
947 1328 : if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
948 520 : appendStringInfoString(&buf, "BEFORE");
949 808 : else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
950 784 : appendStringInfoString(&buf, "AFTER");
951 24 : else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
952 24 : appendStringInfoString(&buf, "INSTEAD OF");
953 : else
954 0 : elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
955 :
956 1328 : if (TRIGGER_FOR_INSERT(trigrec->tgtype))
957 : {
958 864 : appendStringInfoString(&buf, " INSERT");
959 864 : findx++;
960 : }
961 1328 : if (TRIGGER_FOR_DELETE(trigrec->tgtype))
962 : {
963 226 : if (findx > 0)
964 90 : appendStringInfoString(&buf, " OR DELETE");
965 : else
966 136 : appendStringInfoString(&buf, " DELETE");
967 226 : findx++;
968 : }
969 1328 : if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
970 : {
971 648 : if (findx > 0)
972 320 : appendStringInfoString(&buf, " OR UPDATE");
973 : else
974 328 : appendStringInfoString(&buf, " UPDATE");
975 648 : findx++;
976 : /* tgattr is first var-width field, so OK to access directly */
977 648 : if (trigrec->tgattr.dim1 > 0)
978 : {
979 : int i;
980 :
981 76 : appendStringInfoString(&buf, " OF ");
982 168 : for (i = 0; i < trigrec->tgattr.dim1; i++)
983 : {
984 : char *attname;
985 :
986 92 : if (i > 0)
987 16 : appendStringInfoString(&buf, ", ");
988 92 : attname = get_attname(trigrec->tgrelid,
989 92 : trigrec->tgattr.values[i], false);
990 92 : appendStringInfoString(&buf, quote_identifier(attname));
991 : }
992 : }
993 : }
994 1328 : if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
995 : {
996 0 : if (findx > 0)
997 0 : appendStringInfoString(&buf, " OR TRUNCATE");
998 : else
999 0 : appendStringInfoString(&buf, " TRUNCATE");
1000 0 : findx++;
1001 : }
1002 :
1003 : /*
1004 : * In non-pretty mode, always schema-qualify the target table name for
1005 : * safety. In pretty mode, schema-qualify only if not visible.
1006 : */
1007 2656 : appendStringInfo(&buf, " ON %s ",
1008 : pretty ?
1009 138 : generate_relation_name(trigrec->tgrelid, NIL) :
1010 1190 : generate_qualified_relation_name(trigrec->tgrelid));
1011 :
1012 1328 : if (OidIsValid(trigrec->tgconstraint))
1013 : {
1014 0 : if (OidIsValid(trigrec->tgconstrrelid))
1015 0 : appendStringInfo(&buf, "FROM %s ",
1016 : generate_relation_name(trigrec->tgconstrrelid, NIL));
1017 0 : if (!trigrec->tgdeferrable)
1018 0 : appendStringInfoString(&buf, "NOT ");
1019 0 : appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
1020 0 : if (trigrec->tginitdeferred)
1021 0 : appendStringInfoString(&buf, "DEFERRED ");
1022 : else
1023 0 : appendStringInfoString(&buf, "IMMEDIATE ");
1024 : }
1025 :
1026 1328 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
1027 : tgrel->rd_att, &isnull);
1028 1328 : if (!isnull)
1029 98 : tgoldtable = NameStr(*DatumGetName(value));
1030 : else
1031 1230 : tgoldtable = NULL;
1032 1328 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
1033 : tgrel->rd_att, &isnull);
1034 1328 : if (!isnull)
1035 108 : tgnewtable = NameStr(*DatumGetName(value));
1036 : else
1037 1220 : tgnewtable = NULL;
1038 1328 : if (tgoldtable != NULL || tgnewtable != NULL)
1039 : {
1040 152 : appendStringInfoString(&buf, "REFERENCING ");
1041 152 : if (tgoldtable != NULL)
1042 98 : appendStringInfo(&buf, "OLD TABLE AS %s ",
1043 : quote_identifier(tgoldtable));
1044 152 : if (tgnewtable != NULL)
1045 108 : appendStringInfo(&buf, "NEW TABLE AS %s ",
1046 : quote_identifier(tgnewtable));
1047 : }
1048 :
1049 1328 : if (TRIGGER_FOR_ROW(trigrec->tgtype))
1050 1010 : appendStringInfoString(&buf, "FOR EACH ROW ");
1051 : else
1052 318 : appendStringInfoString(&buf, "FOR EACH STATEMENT ");
1053 :
1054 : /* If the trigger has a WHEN qualification, add that */
1055 1328 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
1056 : tgrel->rd_att, &isnull);
1057 1328 : if (!isnull)
1058 : {
1059 : Node *qual;
1060 : char relkind;
1061 : deparse_context context;
1062 : deparse_namespace dpns;
1063 : RangeTblEntry *oldrte;
1064 : RangeTblEntry *newrte;
1065 :
1066 138 : appendStringInfoString(&buf, "WHEN (");
1067 :
1068 138 : qual = stringToNode(TextDatumGetCString(value));
1069 :
1070 138 : relkind = get_rel_relkind(trigrec->tgrelid);
1071 :
1072 : /* Build minimal OLD and NEW RTEs for the rel */
1073 138 : oldrte = makeNode(RangeTblEntry);
1074 138 : oldrte->rtekind = RTE_RELATION;
1075 138 : oldrte->relid = trigrec->tgrelid;
1076 138 : oldrte->relkind = relkind;
1077 138 : oldrte->rellockmode = AccessShareLock;
1078 138 : oldrte->alias = makeAlias("old", NIL);
1079 138 : oldrte->eref = oldrte->alias;
1080 138 : oldrte->lateral = false;
1081 138 : oldrte->inh = false;
1082 138 : oldrte->inFromCl = true;
1083 :
1084 138 : newrte = makeNode(RangeTblEntry);
1085 138 : newrte->rtekind = RTE_RELATION;
1086 138 : newrte->relid = trigrec->tgrelid;
1087 138 : newrte->relkind = relkind;
1088 138 : newrte->rellockmode = AccessShareLock;
1089 138 : newrte->alias = makeAlias("new", NIL);
1090 138 : newrte->eref = newrte->alias;
1091 138 : newrte->lateral = false;
1092 138 : newrte->inh = false;
1093 138 : newrte->inFromCl = true;
1094 :
1095 : /* Build two-element rtable */
1096 138 : memset(&dpns, 0, sizeof(dpns));
1097 138 : dpns.rtable = list_make2(oldrte, newrte);
1098 138 : dpns.subplans = NIL;
1099 138 : dpns.ctes = NIL;
1100 138 : dpns.appendrels = NULL;
1101 138 : set_rtable_names(&dpns, NIL, NULL);
1102 138 : set_simple_column_names(&dpns);
1103 :
1104 : /* Set up context with one-deep namespace stack */
1105 138 : context.buf = &buf;
1106 138 : context.namespaces = list_make1(&dpns);
1107 138 : context.resultDesc = NULL;
1108 138 : context.targetList = NIL;
1109 138 : context.windowClause = NIL;
1110 138 : context.varprefix = true;
1111 138 : context.prettyFlags = GET_PRETTY_FLAGS(pretty);
1112 138 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
1113 138 : context.indentLevel = PRETTYINDENT_STD;
1114 138 : context.colNamesVisible = true;
1115 138 : context.inGroupBy = false;
1116 138 : context.varInOrderBy = false;
1117 138 : context.appendparents = NULL;
1118 :
1119 138 : get_rule_expr(qual, &context, false);
1120 :
1121 138 : appendStringInfoString(&buf, ") ");
1122 : }
1123 :
1124 1328 : appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
1125 : generate_function_name(trigrec->tgfoid, 0,
1126 : NIL, NULL,
1127 : false, NULL, false));
1128 :
1129 1328 : if (trigrec->tgnargs > 0)
1130 : {
1131 : char *p;
1132 : int i;
1133 :
1134 450 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
1135 : tgrel->rd_att, &isnull);
1136 450 : if (isnull)
1137 0 : elog(ERROR, "tgargs is null for trigger %u", trigid);
1138 450 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
1139 1196 : for (i = 0; i < trigrec->tgnargs; i++)
1140 : {
1141 746 : if (i > 0)
1142 296 : appendStringInfoString(&buf, ", ");
1143 746 : simple_quote_literal(&buf, p);
1144 : /* advance p to next string embedded in tgargs */
1145 6756 : while (*p)
1146 6010 : p++;
1147 746 : p++;
1148 : }
1149 : }
1150 :
1151 : /* We deliberately do not put semi-colon at end */
1152 1328 : appendStringInfoChar(&buf, ')');
1153 :
1154 : /* Clean up */
1155 1328 : systable_endscan(tgscan);
1156 :
1157 1328 : table_close(tgrel, AccessShareLock);
1158 :
1159 1328 : return buf.data;
1160 : }
1161 :
1162 : /* ----------
1163 : * pg_get_indexdef - Get the definition of an index
1164 : *
1165 : * In the extended version, there is a colno argument as well as pretty bool.
1166 : * if colno == 0, we want a complete index definition.
1167 : * if colno > 0, we only want the Nth index key's variable or expression.
1168 : *
1169 : * Note that the SQL-function versions of this omit any info about the
1170 : * index tablespace; this is intentional because pg_dump wants it that way.
1171 : * However pg_get_indexdef_string() includes the index tablespace.
1172 : * ----------
1173 : */
1174 : Datum
1175 5214 : pg_get_indexdef(PG_FUNCTION_ARGS)
1176 : {
1177 5214 : Oid indexrelid = PG_GETARG_OID(0);
1178 : int prettyFlags;
1179 : char *res;
1180 :
1181 5214 : prettyFlags = PRETTYFLAG_INDENT;
1182 :
1183 5214 : res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1184 : false, false,
1185 : false, false,
1186 : prettyFlags, true);
1187 :
1188 5214 : if (res == NULL)
1189 6 : PG_RETURN_NULL();
1190 :
1191 5208 : PG_RETURN_TEXT_P(string_to_text(res));
1192 : }
1193 :
1194 : Datum
1195 1946 : pg_get_indexdef_ext(PG_FUNCTION_ARGS)
1196 : {
1197 1946 : Oid indexrelid = PG_GETARG_OID(0);
1198 1946 : int32 colno = PG_GETARG_INT32(1);
1199 1946 : bool pretty = PG_GETARG_BOOL(2);
1200 : int prettyFlags;
1201 : char *res;
1202 :
1203 1946 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1204 :
1205 1946 : res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1206 : colno != 0, false,
1207 : false, false,
1208 : prettyFlags, true);
1209 :
1210 1946 : if (res == NULL)
1211 0 : PG_RETURN_NULL();
1212 :
1213 1946 : PG_RETURN_TEXT_P(string_to_text(res));
1214 : }
1215 :
1216 : /*
1217 : * Internal version for use by ALTER TABLE.
1218 : * Includes a tablespace clause in the result.
1219 : * Returns a palloc'd C string; no pretty-printing.
1220 : */
1221 : char *
1222 216 : pg_get_indexdef_string(Oid indexrelid)
1223 : {
1224 216 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1225 : false, false,
1226 : true, true,
1227 : 0, false);
1228 : }
1229 :
1230 : /* Internal version that just reports the key-column definitions */
1231 : char *
1232 934 : pg_get_indexdef_columns(Oid indexrelid, bool pretty)
1233 : {
1234 : int prettyFlags;
1235 :
1236 934 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1237 :
1238 934 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1239 : true, true,
1240 : false, false,
1241 : prettyFlags, false);
1242 : }
1243 :
1244 : /* Internal version, extensible with flags to control its behavior */
1245 : char *
1246 8 : pg_get_indexdef_columns_extended(Oid indexrelid, bits16 flags)
1247 : {
1248 8 : bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1249 8 : bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1250 : int prettyFlags;
1251 :
1252 8 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1253 :
1254 8 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1255 : true, keys_only,
1256 : false, false,
1257 : prettyFlags, false);
1258 : }
1259 :
1260 : /*
1261 : * Internal workhorse to decompile an index definition.
1262 : *
1263 : * This is now used for exclusion constraints as well: if excludeOps is not
1264 : * NULL then it points to an array of exclusion operator OIDs.
1265 : */
1266 : static char *
1267 8422 : pg_get_indexdef_worker(Oid indexrelid, int colno,
1268 : const Oid *excludeOps,
1269 : bool attrsOnly, bool keysOnly,
1270 : bool showTblSpc, bool inherits,
1271 : int prettyFlags, bool missing_ok)
1272 : {
1273 : /* might want a separate isConstraint parameter later */
1274 8422 : bool isConstraint = (excludeOps != NULL);
1275 : HeapTuple ht_idx;
1276 : HeapTuple ht_idxrel;
1277 : HeapTuple ht_am;
1278 : Form_pg_index idxrec;
1279 : Form_pg_class idxrelrec;
1280 : Form_pg_am amrec;
1281 : IndexAmRoutine *amroutine;
1282 : List *indexprs;
1283 : ListCell *indexpr_item;
1284 : List *context;
1285 : Oid indrelid;
1286 : int keyno;
1287 : Datum indcollDatum;
1288 : Datum indclassDatum;
1289 : Datum indoptionDatum;
1290 : oidvector *indcollation;
1291 : oidvector *indclass;
1292 : int2vector *indoption;
1293 : StringInfoData buf;
1294 : char *str;
1295 : char *sep;
1296 :
1297 : /*
1298 : * Fetch the pg_index tuple by the Oid of the index
1299 : */
1300 8422 : ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
1301 8422 : if (!HeapTupleIsValid(ht_idx))
1302 : {
1303 6 : if (missing_ok)
1304 6 : return NULL;
1305 0 : elog(ERROR, "cache lookup failed for index %u", indexrelid);
1306 : }
1307 8416 : idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
1308 :
1309 8416 : indrelid = idxrec->indrelid;
1310 : Assert(indexrelid == idxrec->indexrelid);
1311 :
1312 : /* Must get indcollation, indclass, and indoption the hard way */
1313 8416 : indcollDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1314 : Anum_pg_index_indcollation);
1315 8416 : indcollation = (oidvector *) DatumGetPointer(indcollDatum);
1316 :
1317 8416 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1318 : Anum_pg_index_indclass);
1319 8416 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
1320 :
1321 8416 : indoptionDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1322 : Anum_pg_index_indoption);
1323 8416 : indoption = (int2vector *) DatumGetPointer(indoptionDatum);
1324 :
1325 : /*
1326 : * Fetch the pg_class tuple of the index relation
1327 : */
1328 8416 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
1329 8416 : if (!HeapTupleIsValid(ht_idxrel))
1330 0 : elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1331 8416 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
1332 :
1333 : /*
1334 : * Fetch the pg_am tuple of the index' access method
1335 : */
1336 8416 : ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
1337 8416 : if (!HeapTupleIsValid(ht_am))
1338 0 : elog(ERROR, "cache lookup failed for access method %u",
1339 : idxrelrec->relam);
1340 8416 : amrec = (Form_pg_am) GETSTRUCT(ht_am);
1341 :
1342 : /* Fetch the index AM's API struct */
1343 8416 : amroutine = GetIndexAmRoutine(amrec->amhandler);
1344 :
1345 : /*
1346 : * Get the index expressions, if any. (NOTE: we do not use the relcache
1347 : * versions of the expressions and predicate, because we want to display
1348 : * non-const-folded expressions.)
1349 : */
1350 8416 : if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs, NULL))
1351 : {
1352 : Datum exprsDatum;
1353 : char *exprsString;
1354 :
1355 550 : exprsDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1356 : Anum_pg_index_indexprs);
1357 550 : exprsString = TextDatumGetCString(exprsDatum);
1358 550 : indexprs = (List *) stringToNode(exprsString);
1359 550 : pfree(exprsString);
1360 : }
1361 : else
1362 7866 : indexprs = NIL;
1363 :
1364 8416 : indexpr_item = list_head(indexprs);
1365 :
1366 8416 : context = deparse_context_for(get_relation_name(indrelid), indrelid);
1367 :
1368 : /*
1369 : * Start the index definition. Note that the index's name should never be
1370 : * schema-qualified, but the indexed rel's name may be.
1371 : */
1372 8416 : initStringInfo(&buf);
1373 :
1374 8416 : if (!attrsOnly)
1375 : {
1376 7024 : if (!isConstraint)
1377 13840 : appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
1378 6920 : idxrec->indisunique ? "UNIQUE " : "",
1379 6920 : quote_identifier(NameStr(idxrelrec->relname)),
1380 6920 : idxrelrec->relkind == RELKIND_PARTITIONED_INDEX
1381 662 : && !inherits ? "ONLY " : "",
1382 6920 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
1383 1496 : generate_relation_name(indrelid, NIL) :
1384 5424 : generate_qualified_relation_name(indrelid),
1385 6920 : quote_identifier(NameStr(amrec->amname)));
1386 : else /* currently, must be EXCLUDE constraint */
1387 104 : appendStringInfo(&buf, "EXCLUDE USING %s (",
1388 104 : quote_identifier(NameStr(amrec->amname)));
1389 : }
1390 :
1391 : /*
1392 : * Report the indexed attributes
1393 : */
1394 8416 : sep = "";
1395 21246 : for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1396 : {
1397 12928 : AttrNumber attnum = idxrec->indkey.values[keyno];
1398 : Oid keycoltype;
1399 : Oid keycolcollation;
1400 :
1401 : /*
1402 : * Ignore non-key attributes if told to.
1403 : */
1404 12928 : if (keysOnly && keyno >= idxrec->indnkeyatts)
1405 98 : break;
1406 :
1407 : /* Otherwise, print INCLUDE to divide key and non-key attrs. */
1408 12830 : if (!colno && keyno == idxrec->indnkeyatts)
1409 : {
1410 250 : appendStringInfoString(&buf, ") INCLUDE (");
1411 250 : sep = "";
1412 : }
1413 :
1414 12830 : if (!colno)
1415 12200 : appendStringInfoString(&buf, sep);
1416 12830 : sep = ", ";
1417 :
1418 12830 : if (attnum != 0)
1419 : {
1420 : /* Simple index column */
1421 : char *attname;
1422 : int32 keycoltypmod;
1423 :
1424 12134 : attname = get_attname(indrelid, attnum, false);
1425 12134 : if (!colno || colno == keyno + 1)
1426 11966 : appendStringInfoString(&buf, quote_identifier(attname));
1427 12134 : get_atttypetypmodcoll(indrelid, attnum,
1428 : &keycoltype, &keycoltypmod,
1429 : &keycolcollation);
1430 : }
1431 : else
1432 : {
1433 : /* expressional index */
1434 : Node *indexkey;
1435 :
1436 696 : if (indexpr_item == NULL)
1437 0 : elog(ERROR, "too few entries in indexprs list");
1438 696 : indexkey = (Node *) lfirst(indexpr_item);
1439 696 : indexpr_item = lnext(indexprs, indexpr_item);
1440 : /* Deparse */
1441 696 : str = deparse_expression_pretty(indexkey, context, false, false,
1442 : prettyFlags, 0);
1443 696 : if (!colno || colno == keyno + 1)
1444 : {
1445 : /* Need parens if it's not a bare function call */
1446 684 : if (looks_like_function(indexkey))
1447 52 : appendStringInfoString(&buf, str);
1448 : else
1449 632 : appendStringInfo(&buf, "(%s)", str);
1450 : }
1451 696 : keycoltype = exprType(indexkey);
1452 696 : keycolcollation = exprCollation(indexkey);
1453 : }
1454 :
1455 : /* Print additional decoration for (selected) key columns */
1456 12830 : if (!attrsOnly && keyno < idxrec->indnkeyatts &&
1457 0 : (!colno || colno == keyno + 1))
1458 : {
1459 10468 : int16 opt = indoption->values[keyno];
1460 10468 : Oid indcoll = indcollation->values[keyno];
1461 10468 : Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1462 10468 : bool has_options = attoptions != (Datum) 0;
1463 :
1464 : /* Add collation, if not default for column */
1465 10468 : if (OidIsValid(indcoll) && indcoll != keycolcollation)
1466 94 : appendStringInfo(&buf, " COLLATE %s",
1467 : generate_collation_name((indcoll)));
1468 :
1469 : /* Add the operator class name, if not default */
1470 10468 : get_opclass_name(indclass->values[keyno],
1471 : has_options ? InvalidOid : keycoltype, &buf);
1472 :
1473 10468 : if (has_options)
1474 : {
1475 34 : appendStringInfoString(&buf, " (");
1476 34 : get_reloptions(&buf, attoptions);
1477 34 : appendStringInfoChar(&buf, ')');
1478 : }
1479 :
1480 : /* Add options if relevant */
1481 10468 : if (amroutine->amcanorder)
1482 : {
1483 : /* if it supports sort ordering, report DESC and NULLS opts */
1484 8388 : if (opt & INDOPTION_DESC)
1485 : {
1486 0 : appendStringInfoString(&buf, " DESC");
1487 : /* NULLS FIRST is the default in this case */
1488 0 : if (!(opt & INDOPTION_NULLS_FIRST))
1489 0 : appendStringInfoString(&buf, " NULLS LAST");
1490 : }
1491 : else
1492 : {
1493 8388 : if (opt & INDOPTION_NULLS_FIRST)
1494 0 : appendStringInfoString(&buf, " NULLS FIRST");
1495 : }
1496 : }
1497 :
1498 : /* Add the exclusion operator if relevant */
1499 10468 : if (excludeOps != NULL)
1500 124 : appendStringInfo(&buf, " WITH %s",
1501 124 : generate_operator_name(excludeOps[keyno],
1502 : keycoltype,
1503 : keycoltype));
1504 : }
1505 : }
1506 :
1507 8416 : if (!attrsOnly)
1508 : {
1509 7024 : appendStringInfoChar(&buf, ')');
1510 :
1511 7024 : if (idxrec->indnullsnotdistinct)
1512 12 : appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1513 :
1514 : /*
1515 : * If it has options, append "WITH (options)"
1516 : */
1517 7024 : str = flatten_reloptions(indexrelid);
1518 7024 : if (str)
1519 : {
1520 210 : appendStringInfo(&buf, " WITH (%s)", str);
1521 210 : pfree(str);
1522 : }
1523 :
1524 : /*
1525 : * Print tablespace, but only if requested
1526 : */
1527 7024 : if (showTblSpc)
1528 : {
1529 : Oid tblspc;
1530 :
1531 216 : tblspc = get_rel_tablespace(indexrelid);
1532 216 : if (OidIsValid(tblspc))
1533 : {
1534 54 : if (isConstraint)
1535 0 : appendStringInfoString(&buf, " USING INDEX");
1536 54 : appendStringInfo(&buf, " TABLESPACE %s",
1537 54 : quote_identifier(get_tablespace_name(tblspc)));
1538 : }
1539 : }
1540 :
1541 : /*
1542 : * If it's a partial index, decompile and append the predicate
1543 : */
1544 7024 : if (!heap_attisnull(ht_idx, Anum_pg_index_indpred, NULL))
1545 : {
1546 : Node *node;
1547 : Datum predDatum;
1548 : char *predString;
1549 :
1550 : /* Convert text string to node tree */
1551 314 : predDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1552 : Anum_pg_index_indpred);
1553 314 : predString = TextDatumGetCString(predDatum);
1554 314 : node = (Node *) stringToNode(predString);
1555 314 : pfree(predString);
1556 :
1557 : /* Deparse */
1558 314 : str = deparse_expression_pretty(node, context, false, false,
1559 : prettyFlags, 0);
1560 314 : if (isConstraint)
1561 42 : appendStringInfo(&buf, " WHERE (%s)", str);
1562 : else
1563 272 : appendStringInfo(&buf, " WHERE %s", str);
1564 : }
1565 : }
1566 :
1567 : /* Clean up */
1568 8416 : ReleaseSysCache(ht_idx);
1569 8416 : ReleaseSysCache(ht_idxrel);
1570 8416 : ReleaseSysCache(ht_am);
1571 :
1572 8416 : return buf.data;
1573 : }
1574 :
1575 : /* ----------
1576 : * pg_get_querydef
1577 : *
1578 : * Public entry point to deparse one query parsetree.
1579 : * The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
1580 : *
1581 : * The result is a palloc'd C string.
1582 : * ----------
1583 : */
1584 : char *
1585 0 : pg_get_querydef(Query *query, bool pretty)
1586 : {
1587 : StringInfoData buf;
1588 : int prettyFlags;
1589 :
1590 0 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1591 :
1592 0 : initStringInfo(&buf);
1593 :
1594 0 : get_query_def(query, &buf, NIL, NULL, true,
1595 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1596 :
1597 0 : return buf.data;
1598 : }
1599 :
1600 : /*
1601 : * pg_get_statisticsobjdef
1602 : * Get the definition of an extended statistics object
1603 : */
1604 : Datum
1605 248 : pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
1606 : {
1607 248 : Oid statextid = PG_GETARG_OID(0);
1608 : char *res;
1609 :
1610 248 : res = pg_get_statisticsobj_worker(statextid, false, true);
1611 :
1612 248 : if (res == NULL)
1613 6 : PG_RETURN_NULL();
1614 :
1615 242 : PG_RETURN_TEXT_P(string_to_text(res));
1616 : }
1617 :
1618 : /*
1619 : * Internal version for use by ALTER TABLE.
1620 : * Includes a tablespace clause in the result.
1621 : * Returns a palloc'd C string; no pretty-printing.
1622 : */
1623 : char *
1624 14 : pg_get_statisticsobjdef_string(Oid statextid)
1625 : {
1626 14 : return pg_get_statisticsobj_worker(statextid, false, false);
1627 : }
1628 :
1629 : /*
1630 : * pg_get_statisticsobjdef_columns
1631 : * Get columns and expressions for an extended statistics object
1632 : */
1633 : Datum
1634 402 : pg_get_statisticsobjdef_columns(PG_FUNCTION_ARGS)
1635 : {
1636 402 : Oid statextid = PG_GETARG_OID(0);
1637 : char *res;
1638 :
1639 402 : res = pg_get_statisticsobj_worker(statextid, true, true);
1640 :
1641 402 : if (res == NULL)
1642 0 : PG_RETURN_NULL();
1643 :
1644 402 : PG_RETURN_TEXT_P(string_to_text(res));
1645 : }
1646 :
1647 : /*
1648 : * Internal workhorse to decompile an extended statistics object.
1649 : */
1650 : static char *
1651 664 : pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
1652 : {
1653 : Form_pg_statistic_ext statextrec;
1654 : HeapTuple statexttup;
1655 : StringInfoData buf;
1656 : int colno;
1657 : char *nsp;
1658 : ArrayType *arr;
1659 : char *enabled;
1660 : Datum datum;
1661 : bool ndistinct_enabled;
1662 : bool dependencies_enabled;
1663 : bool mcv_enabled;
1664 : int i;
1665 : List *context;
1666 : ListCell *lc;
1667 664 : List *exprs = NIL;
1668 : bool has_exprs;
1669 : int ncolumns;
1670 :
1671 664 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1672 :
1673 664 : if (!HeapTupleIsValid(statexttup))
1674 : {
1675 6 : if (missing_ok)
1676 6 : return NULL;
1677 0 : elog(ERROR, "cache lookup failed for statistics object %u", statextid);
1678 : }
1679 :
1680 : /* has the statistics expressions? */
1681 658 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
1682 :
1683 658 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1684 :
1685 : /*
1686 : * Get the statistics expressions, if any. (NOTE: we do not use the
1687 : * relcache versions of the expressions, because we want to display
1688 : * non-const-folded expressions.)
1689 : */
1690 658 : if (has_exprs)
1691 : {
1692 : Datum exprsDatum;
1693 : char *exprsString;
1694 :
1695 142 : exprsDatum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1696 : Anum_pg_statistic_ext_stxexprs);
1697 142 : exprsString = TextDatumGetCString(exprsDatum);
1698 142 : exprs = (List *) stringToNode(exprsString);
1699 142 : pfree(exprsString);
1700 : }
1701 : else
1702 516 : exprs = NIL;
1703 :
1704 : /* count the number of columns (attributes and expressions) */
1705 658 : ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
1706 :
1707 658 : initStringInfo(&buf);
1708 :
1709 658 : if (!columns_only)
1710 : {
1711 256 : nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
1712 256 : appendStringInfo(&buf, "CREATE STATISTICS %s",
1713 : quote_qualified_identifier(nsp,
1714 256 : NameStr(statextrec->stxname)));
1715 :
1716 : /*
1717 : * Decode the stxkind column so that we know which stats types to
1718 : * print.
1719 : */
1720 256 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1721 : Anum_pg_statistic_ext_stxkind);
1722 256 : arr = DatumGetArrayTypeP(datum);
1723 256 : if (ARR_NDIM(arr) != 1 ||
1724 256 : ARR_HASNULL(arr) ||
1725 256 : ARR_ELEMTYPE(arr) != CHAROID)
1726 0 : elog(ERROR, "stxkind is not a 1-D char array");
1727 256 : enabled = (char *) ARR_DATA_PTR(arr);
1728 :
1729 256 : ndistinct_enabled = false;
1730 256 : dependencies_enabled = false;
1731 256 : mcv_enabled = false;
1732 :
1733 662 : for (i = 0; i < ARR_DIMS(arr)[0]; i++)
1734 : {
1735 406 : if (enabled[i] == STATS_EXT_NDISTINCT)
1736 136 : ndistinct_enabled = true;
1737 270 : else if (enabled[i] == STATS_EXT_DEPENDENCIES)
1738 88 : dependencies_enabled = true;
1739 182 : else if (enabled[i] == STATS_EXT_MCV)
1740 106 : mcv_enabled = true;
1741 :
1742 : /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
1743 : }
1744 :
1745 : /*
1746 : * If any option is disabled, then we'll need to append the types
1747 : * clause to show which options are enabled. We omit the types clause
1748 : * on purpose when all options are enabled, so a pg_dump/pg_restore
1749 : * will create all statistics types on a newer postgres version, if
1750 : * the statistics had all options enabled on the original version.
1751 : *
1752 : * But if the statistics is defined on just a single column, it has to
1753 : * be an expression statistics. In that case we don't need to specify
1754 : * kinds.
1755 : */
1756 256 : if ((!ndistinct_enabled || !dependencies_enabled || !mcv_enabled) &&
1757 : (ncolumns > 1))
1758 : {
1759 120 : bool gotone = false;
1760 :
1761 120 : appendStringInfoString(&buf, " (");
1762 :
1763 120 : if (ndistinct_enabled)
1764 : {
1765 66 : appendStringInfoString(&buf, "ndistinct");
1766 66 : gotone = true;
1767 : }
1768 :
1769 120 : if (dependencies_enabled)
1770 : {
1771 18 : appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
1772 18 : gotone = true;
1773 : }
1774 :
1775 120 : if (mcv_enabled)
1776 36 : appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
1777 :
1778 120 : appendStringInfoChar(&buf, ')');
1779 : }
1780 :
1781 256 : appendStringInfoString(&buf, " ON ");
1782 : }
1783 :
1784 : /* decode simple column references */
1785 1870 : for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
1786 : {
1787 1212 : AttrNumber attnum = statextrec->stxkeys.values[colno];
1788 : char *attname;
1789 :
1790 1212 : if (colno > 0)
1791 684 : appendStringInfoString(&buf, ", ");
1792 :
1793 1212 : attname = get_attname(statextrec->stxrelid, attnum, false);
1794 :
1795 1212 : appendStringInfoString(&buf, quote_identifier(attname));
1796 : }
1797 :
1798 658 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
1799 : statextrec->stxrelid);
1800 :
1801 886 : foreach(lc, exprs)
1802 : {
1803 228 : Node *expr = (Node *) lfirst(lc);
1804 : char *str;
1805 228 : int prettyFlags = PRETTYFLAG_PAREN;
1806 :
1807 228 : str = deparse_expression_pretty(expr, context, false, false,
1808 : prettyFlags, 0);
1809 :
1810 228 : if (colno > 0)
1811 98 : appendStringInfoString(&buf, ", ");
1812 :
1813 : /* Need parens if it's not a bare function call */
1814 228 : if (looks_like_function(expr))
1815 34 : appendStringInfoString(&buf, str);
1816 : else
1817 194 : appendStringInfo(&buf, "(%s)", str);
1818 :
1819 228 : colno++;
1820 : }
1821 :
1822 658 : if (!columns_only)
1823 256 : appendStringInfo(&buf, " FROM %s",
1824 : generate_relation_name(statextrec->stxrelid, NIL));
1825 :
1826 658 : ReleaseSysCache(statexttup);
1827 :
1828 658 : return buf.data;
1829 : }
1830 :
1831 : /*
1832 : * Generate text array of expressions for statistics object.
1833 : */
1834 : Datum
1835 24 : pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
1836 : {
1837 24 : Oid statextid = PG_GETARG_OID(0);
1838 : Form_pg_statistic_ext statextrec;
1839 : HeapTuple statexttup;
1840 : Datum datum;
1841 : List *context;
1842 : ListCell *lc;
1843 24 : List *exprs = NIL;
1844 : bool has_exprs;
1845 : char *tmp;
1846 24 : ArrayBuildState *astate = NULL;
1847 :
1848 24 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1849 :
1850 24 : if (!HeapTupleIsValid(statexttup))
1851 0 : PG_RETURN_NULL();
1852 :
1853 : /* Does the stats object have expressions? */
1854 24 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
1855 :
1856 : /* no expressions? we're done */
1857 24 : if (!has_exprs)
1858 : {
1859 12 : ReleaseSysCache(statexttup);
1860 12 : PG_RETURN_NULL();
1861 : }
1862 :
1863 12 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1864 :
1865 : /*
1866 : * Get the statistics expressions, and deparse them into text values.
1867 : */
1868 12 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1869 : Anum_pg_statistic_ext_stxexprs);
1870 12 : tmp = TextDatumGetCString(datum);
1871 12 : exprs = (List *) stringToNode(tmp);
1872 12 : pfree(tmp);
1873 :
1874 12 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
1875 : statextrec->stxrelid);
1876 :
1877 36 : foreach(lc, exprs)
1878 : {
1879 24 : Node *expr = (Node *) lfirst(lc);
1880 : char *str;
1881 24 : int prettyFlags = PRETTYFLAG_INDENT;
1882 :
1883 24 : str = deparse_expression_pretty(expr, context, false, false,
1884 : prettyFlags, 0);
1885 :
1886 24 : astate = accumArrayResult(astate,
1887 24 : PointerGetDatum(cstring_to_text(str)),
1888 : false,
1889 : TEXTOID,
1890 : CurrentMemoryContext);
1891 : }
1892 :
1893 12 : ReleaseSysCache(statexttup);
1894 :
1895 12 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1896 : }
1897 :
1898 : /*
1899 : * pg_get_partkeydef
1900 : *
1901 : * Returns the partition key specification, ie, the following:
1902 : *
1903 : * { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
1904 : */
1905 : Datum
1906 1308 : pg_get_partkeydef(PG_FUNCTION_ARGS)
1907 : {
1908 1308 : Oid relid = PG_GETARG_OID(0);
1909 : char *res;
1910 :
1911 1308 : res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
1912 :
1913 1308 : if (res == NULL)
1914 6 : PG_RETURN_NULL();
1915 :
1916 1302 : PG_RETURN_TEXT_P(string_to_text(res));
1917 : }
1918 :
1919 : /* Internal version that just reports the column definitions */
1920 : char *
1921 142 : pg_get_partkeydef_columns(Oid relid, bool pretty)
1922 : {
1923 : int prettyFlags;
1924 :
1925 142 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1926 :
1927 142 : return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
1928 : }
1929 :
1930 : /*
1931 : * Internal workhorse to decompile a partition key definition.
1932 : */
1933 : static char *
1934 1450 : pg_get_partkeydef_worker(Oid relid, int prettyFlags,
1935 : bool attrsOnly, bool missing_ok)
1936 : {
1937 : Form_pg_partitioned_table form;
1938 : HeapTuple tuple;
1939 : oidvector *partclass;
1940 : oidvector *partcollation;
1941 : List *partexprs;
1942 : ListCell *partexpr_item;
1943 : List *context;
1944 : Datum datum;
1945 : StringInfoData buf;
1946 : int keyno;
1947 : char *str;
1948 : char *sep;
1949 :
1950 1450 : tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
1951 1450 : if (!HeapTupleIsValid(tuple))
1952 : {
1953 6 : if (missing_ok)
1954 6 : return NULL;
1955 0 : elog(ERROR, "cache lookup failed for partition key of %u", relid);
1956 : }
1957 :
1958 1444 : form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
1959 :
1960 : Assert(form->partrelid == relid);
1961 :
1962 : /* Must get partclass and partcollation the hard way */
1963 1444 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1964 : Anum_pg_partitioned_table_partclass);
1965 1444 : partclass = (oidvector *) DatumGetPointer(datum);
1966 :
1967 1444 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1968 : Anum_pg_partitioned_table_partcollation);
1969 1444 : partcollation = (oidvector *) DatumGetPointer(datum);
1970 :
1971 :
1972 : /*
1973 : * Get the expressions, if any. (NOTE: we do not use the relcache
1974 : * versions of the expressions, because we want to display
1975 : * non-const-folded expressions.)
1976 : */
1977 1444 : if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprs, NULL))
1978 : {
1979 : Datum exprsDatum;
1980 : char *exprsString;
1981 :
1982 146 : exprsDatum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1983 : Anum_pg_partitioned_table_partexprs);
1984 146 : exprsString = TextDatumGetCString(exprsDatum);
1985 146 : partexprs = (List *) stringToNode(exprsString);
1986 :
1987 146 : if (!IsA(partexprs, List))
1988 0 : elog(ERROR, "unexpected node type found in partexprs: %d",
1989 : (int) nodeTag(partexprs));
1990 :
1991 146 : pfree(exprsString);
1992 : }
1993 : else
1994 1298 : partexprs = NIL;
1995 :
1996 1444 : partexpr_item = list_head(partexprs);
1997 1444 : context = deparse_context_for(get_relation_name(relid), relid);
1998 :
1999 1444 : initStringInfo(&buf);
2000 :
2001 1444 : switch (form->partstrat)
2002 : {
2003 54 : case PARTITION_STRATEGY_HASH:
2004 54 : if (!attrsOnly)
2005 54 : appendStringInfoString(&buf, "HASH");
2006 54 : break;
2007 556 : case PARTITION_STRATEGY_LIST:
2008 556 : if (!attrsOnly)
2009 516 : appendStringInfoString(&buf, "LIST");
2010 556 : break;
2011 834 : case PARTITION_STRATEGY_RANGE:
2012 834 : if (!attrsOnly)
2013 732 : appendStringInfoString(&buf, "RANGE");
2014 834 : break;
2015 0 : default:
2016 0 : elog(ERROR, "unexpected partition strategy: %d",
2017 : (int) form->partstrat);
2018 : }
2019 :
2020 1444 : if (!attrsOnly)
2021 1302 : appendStringInfoString(&buf, " (");
2022 1444 : sep = "";
2023 3040 : for (keyno = 0; keyno < form->partnatts; keyno++)
2024 : {
2025 1596 : AttrNumber attnum = form->partattrs.values[keyno];
2026 : Oid keycoltype;
2027 : Oid keycolcollation;
2028 : Oid partcoll;
2029 :
2030 1596 : appendStringInfoString(&buf, sep);
2031 1596 : sep = ", ";
2032 1596 : if (attnum != 0)
2033 : {
2034 : /* Simple attribute reference */
2035 : char *attname;
2036 : int32 keycoltypmod;
2037 :
2038 1438 : attname = get_attname(relid, attnum, false);
2039 1438 : appendStringInfoString(&buf, quote_identifier(attname));
2040 1438 : get_atttypetypmodcoll(relid, attnum,
2041 : &keycoltype, &keycoltypmod,
2042 : &keycolcollation);
2043 : }
2044 : else
2045 : {
2046 : /* Expression */
2047 : Node *partkey;
2048 :
2049 158 : if (partexpr_item == NULL)
2050 0 : elog(ERROR, "too few entries in partexprs list");
2051 158 : partkey = (Node *) lfirst(partexpr_item);
2052 158 : partexpr_item = lnext(partexprs, partexpr_item);
2053 :
2054 : /* Deparse */
2055 158 : str = deparse_expression_pretty(partkey, context, false, false,
2056 : prettyFlags, 0);
2057 : /* Need parens if it's not a bare function call */
2058 158 : if (looks_like_function(partkey))
2059 56 : appendStringInfoString(&buf, str);
2060 : else
2061 102 : appendStringInfo(&buf, "(%s)", str);
2062 :
2063 158 : keycoltype = exprType(partkey);
2064 158 : keycolcollation = exprCollation(partkey);
2065 : }
2066 :
2067 : /* Add collation, if not default for column */
2068 1596 : partcoll = partcollation->values[keyno];
2069 1596 : if (!attrsOnly && OidIsValid(partcoll) && partcoll != keycolcollation)
2070 6 : appendStringInfo(&buf, " COLLATE %s",
2071 : generate_collation_name((partcoll)));
2072 :
2073 : /* Add the operator class name, if not default */
2074 1596 : if (!attrsOnly)
2075 1400 : get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2076 : }
2077 :
2078 1444 : if (!attrsOnly)
2079 1302 : appendStringInfoChar(&buf, ')');
2080 :
2081 : /* Clean up */
2082 1444 : ReleaseSysCache(tuple);
2083 :
2084 1444 : return buf.data;
2085 : }
2086 :
2087 : /*
2088 : * pg_get_partition_constraintdef
2089 : *
2090 : * Returns partition constraint expression as a string for the input relation
2091 : */
2092 : Datum
2093 170 : pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
2094 : {
2095 170 : Oid relationId = PG_GETARG_OID(0);
2096 : Expr *constr_expr;
2097 : int prettyFlags;
2098 : List *context;
2099 : char *consrc;
2100 :
2101 170 : constr_expr = get_partition_qual_relid(relationId);
2102 :
2103 : /* Quick exit if no partition constraint */
2104 170 : if (constr_expr == NULL)
2105 18 : PG_RETURN_NULL();
2106 :
2107 : /*
2108 : * Deparse and return the constraint expression.
2109 : */
2110 152 : prettyFlags = PRETTYFLAG_INDENT;
2111 152 : context = deparse_context_for(get_relation_name(relationId), relationId);
2112 152 : consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2113 : false, prettyFlags, 0);
2114 :
2115 152 : PG_RETURN_TEXT_P(string_to_text(consrc));
2116 : }
2117 :
2118 : /*
2119 : * pg_get_partconstrdef_string
2120 : *
2121 : * Returns the partition constraint as a C-string for the input relation, with
2122 : * the given alias. No pretty-printing.
2123 : */
2124 : char *
2125 86 : pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
2126 : {
2127 : Expr *constr_expr;
2128 : List *context;
2129 :
2130 86 : constr_expr = get_partition_qual_relid(partitionId);
2131 86 : context = deparse_context_for(aliasname, partitionId);
2132 :
2133 86 : return deparse_expression((Node *) constr_expr, context, true, false);
2134 : }
2135 :
2136 : /*
2137 : * pg_get_constraintdef
2138 : *
2139 : * Returns the definition for the constraint, ie, everything that needs to
2140 : * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
2141 : */
2142 : Datum
2143 1836 : pg_get_constraintdef(PG_FUNCTION_ARGS)
2144 : {
2145 1836 : Oid constraintId = PG_GETARG_OID(0);
2146 : int prettyFlags;
2147 : char *res;
2148 :
2149 1836 : prettyFlags = PRETTYFLAG_INDENT;
2150 :
2151 1836 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2152 :
2153 1836 : if (res == NULL)
2154 6 : PG_RETURN_NULL();
2155 :
2156 1830 : PG_RETURN_TEXT_P(string_to_text(res));
2157 : }
2158 :
2159 : Datum
2160 4370 : pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
2161 : {
2162 4370 : Oid constraintId = PG_GETARG_OID(0);
2163 4370 : bool pretty = PG_GETARG_BOOL(1);
2164 : int prettyFlags;
2165 : char *res;
2166 :
2167 4370 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2168 :
2169 4370 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2170 :
2171 4370 : if (res == NULL)
2172 0 : PG_RETURN_NULL();
2173 :
2174 4370 : PG_RETURN_TEXT_P(string_to_text(res));
2175 : }
2176 :
2177 : /*
2178 : * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
2179 : */
2180 : char *
2181 596 : pg_get_constraintdef_command(Oid constraintId)
2182 : {
2183 596 : return pg_get_constraintdef_worker(constraintId, true, 0, false);
2184 : }
2185 :
2186 : /*
2187 : * As of 9.4, we now use an MVCC snapshot for this.
2188 : */
2189 : static char *
2190 6802 : pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
2191 : int prettyFlags, bool missing_ok)
2192 : {
2193 : HeapTuple tup;
2194 : Form_pg_constraint conForm;
2195 : StringInfoData buf;
2196 : SysScanDesc scandesc;
2197 : ScanKeyData scankey[1];
2198 6802 : Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());
2199 6802 : Relation relation = table_open(ConstraintRelationId, AccessShareLock);
2200 :
2201 6802 : ScanKeyInit(&scankey[0],
2202 : Anum_pg_constraint_oid,
2203 : BTEqualStrategyNumber, F_OIDEQ,
2204 : ObjectIdGetDatum(constraintId));
2205 :
2206 6802 : scandesc = systable_beginscan(relation,
2207 : ConstraintOidIndexId,
2208 : true,
2209 : snapshot,
2210 : 1,
2211 : scankey);
2212 :
2213 : /*
2214 : * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2215 : * via SearchSysCache, which works fine.
2216 : */
2217 6802 : tup = systable_getnext(scandesc);
2218 :
2219 6802 : UnregisterSnapshot(snapshot);
2220 :
2221 6802 : if (!HeapTupleIsValid(tup))
2222 : {
2223 6 : if (missing_ok)
2224 : {
2225 6 : systable_endscan(scandesc);
2226 6 : table_close(relation, AccessShareLock);
2227 6 : return NULL;
2228 : }
2229 0 : elog(ERROR, "could not find tuple for constraint %u", constraintId);
2230 : }
2231 :
2232 6796 : conForm = (Form_pg_constraint) GETSTRUCT(tup);
2233 :
2234 6796 : initStringInfo(&buf);
2235 :
2236 6796 : if (fullCommand)
2237 : {
2238 596 : if (OidIsValid(conForm->conrelid))
2239 : {
2240 : /*
2241 : * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2242 : * constraints, and other types of constraints don't inherit
2243 : * anyway so it doesn't matter whether we say ONLY or not. Someday
2244 : * we might need to let callers specify whether to put ONLY in the
2245 : * command.
2246 : */
2247 582 : appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2248 : generate_qualified_relation_name(conForm->conrelid),
2249 582 : quote_identifier(NameStr(conForm->conname)));
2250 : }
2251 : else
2252 : {
2253 : /* Must be a domain constraint */
2254 : Assert(OidIsValid(conForm->contypid));
2255 14 : appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2256 : generate_qualified_type_name(conForm->contypid),
2257 14 : quote_identifier(NameStr(conForm->conname)));
2258 : }
2259 : }
2260 :
2261 6796 : switch (conForm->contype)
2262 : {
2263 726 : case CONSTRAINT_FOREIGN:
2264 : {
2265 : Datum val;
2266 : bool isnull;
2267 : const char *string;
2268 :
2269 : /* Start off the constraint definition */
2270 726 : appendStringInfoString(&buf, "FOREIGN KEY (");
2271 :
2272 : /* Fetch and build referencing-column list */
2273 726 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2274 : Anum_pg_constraint_conkey);
2275 :
2276 : /* If it is a temporal foreign key then it uses PERIOD. */
2277 726 : decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
2278 :
2279 : /* add foreign relation name */
2280 726 : appendStringInfo(&buf, ") REFERENCES %s(",
2281 : generate_relation_name(conForm->confrelid,
2282 : NIL));
2283 :
2284 : /* Fetch and build referenced-column list */
2285 726 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2286 : Anum_pg_constraint_confkey);
2287 :
2288 726 : decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
2289 :
2290 726 : appendStringInfoChar(&buf, ')');
2291 :
2292 : /* Add match type */
2293 726 : switch (conForm->confmatchtype)
2294 : {
2295 34 : case FKCONSTR_MATCH_FULL:
2296 34 : string = " MATCH FULL";
2297 34 : break;
2298 0 : case FKCONSTR_MATCH_PARTIAL:
2299 0 : string = " MATCH PARTIAL";
2300 0 : break;
2301 692 : case FKCONSTR_MATCH_SIMPLE:
2302 692 : string = "";
2303 692 : break;
2304 0 : default:
2305 0 : elog(ERROR, "unrecognized confmatchtype: %d",
2306 : conForm->confmatchtype);
2307 : string = ""; /* keep compiler quiet */
2308 : break;
2309 : }
2310 726 : appendStringInfoString(&buf, string);
2311 :
2312 : /* Add ON UPDATE and ON DELETE clauses, if needed */
2313 726 : switch (conForm->confupdtype)
2314 : {
2315 602 : case FKCONSTR_ACTION_NOACTION:
2316 602 : string = NULL; /* suppress default */
2317 602 : break;
2318 0 : case FKCONSTR_ACTION_RESTRICT:
2319 0 : string = "RESTRICT";
2320 0 : break;
2321 96 : case FKCONSTR_ACTION_CASCADE:
2322 96 : string = "CASCADE";
2323 96 : break;
2324 28 : case FKCONSTR_ACTION_SETNULL:
2325 28 : string = "SET NULL";
2326 28 : break;
2327 0 : case FKCONSTR_ACTION_SETDEFAULT:
2328 0 : string = "SET DEFAULT";
2329 0 : break;
2330 0 : default:
2331 0 : elog(ERROR, "unrecognized confupdtype: %d",
2332 : conForm->confupdtype);
2333 : string = NULL; /* keep compiler quiet */
2334 : break;
2335 : }
2336 726 : if (string)
2337 124 : appendStringInfo(&buf, " ON UPDATE %s", string);
2338 :
2339 726 : switch (conForm->confdeltype)
2340 : {
2341 586 : case FKCONSTR_ACTION_NOACTION:
2342 586 : string = NULL; /* suppress default */
2343 586 : break;
2344 20 : case FKCONSTR_ACTION_RESTRICT:
2345 20 : string = "RESTRICT";
2346 20 : break;
2347 96 : case FKCONSTR_ACTION_CASCADE:
2348 96 : string = "CASCADE";
2349 96 : break;
2350 18 : case FKCONSTR_ACTION_SETNULL:
2351 18 : string = "SET NULL";
2352 18 : break;
2353 6 : case FKCONSTR_ACTION_SETDEFAULT:
2354 6 : string = "SET DEFAULT";
2355 6 : break;
2356 0 : default:
2357 0 : elog(ERROR, "unrecognized confdeltype: %d",
2358 : conForm->confdeltype);
2359 : string = NULL; /* keep compiler quiet */
2360 : break;
2361 : }
2362 726 : if (string)
2363 140 : appendStringInfo(&buf, " ON DELETE %s", string);
2364 :
2365 : /*
2366 : * Add columns specified to SET NULL or SET DEFAULT if
2367 : * provided.
2368 : */
2369 726 : val = SysCacheGetAttr(CONSTROID, tup,
2370 : Anum_pg_constraint_confdelsetcols, &isnull);
2371 726 : if (!isnull)
2372 : {
2373 12 : appendStringInfoString(&buf, " (");
2374 12 : decompile_column_index_array(val, conForm->conrelid, false, &buf);
2375 12 : appendStringInfoChar(&buf, ')');
2376 : }
2377 :
2378 726 : break;
2379 : }
2380 3636 : case CONSTRAINT_PRIMARY:
2381 : case CONSTRAINT_UNIQUE:
2382 : {
2383 : Datum val;
2384 : Oid indexId;
2385 : int keyatts;
2386 : HeapTuple indtup;
2387 :
2388 : /* Start off the constraint definition */
2389 3636 : if (conForm->contype == CONSTRAINT_PRIMARY)
2390 2926 : appendStringInfoString(&buf, "PRIMARY KEY ");
2391 : else
2392 710 : appendStringInfoString(&buf, "UNIQUE ");
2393 :
2394 3636 : indexId = conForm->conindid;
2395 :
2396 3636 : indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
2397 3636 : if (!HeapTupleIsValid(indtup))
2398 0 : elog(ERROR, "cache lookup failed for index %u", indexId);
2399 3636 : if (conForm->contype == CONSTRAINT_UNIQUE &&
2400 710 : ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
2401 0 : appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2402 :
2403 3636 : appendStringInfoChar(&buf, '(');
2404 :
2405 : /* Fetch and build target column list */
2406 3636 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2407 : Anum_pg_constraint_conkey);
2408 :
2409 3636 : keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
2410 3636 : if (conForm->conperiod)
2411 374 : appendStringInfoString(&buf, " WITHOUT OVERLAPS");
2412 :
2413 3636 : appendStringInfoChar(&buf, ')');
2414 :
2415 : /* Build including column list (from pg_index.indkeys) */
2416 3636 : val = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2417 : Anum_pg_index_indnatts);
2418 3636 : if (DatumGetInt32(val) > keyatts)
2419 : {
2420 : Datum cols;
2421 : Datum *keys;
2422 : int nKeys;
2423 : int j;
2424 :
2425 82 : appendStringInfoString(&buf, " INCLUDE (");
2426 :
2427 82 : cols = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2428 : Anum_pg_index_indkey);
2429 :
2430 82 : deconstruct_array_builtin(DatumGetArrayTypeP(cols), INT2OID,
2431 : &keys, NULL, &nKeys);
2432 :
2433 246 : for (j = keyatts; j < nKeys; j++)
2434 : {
2435 : char *colName;
2436 :
2437 164 : colName = get_attname(conForm->conrelid,
2438 164 : DatumGetInt16(keys[j]), false);
2439 164 : if (j > keyatts)
2440 82 : appendStringInfoString(&buf, ", ");
2441 164 : appendStringInfoString(&buf, quote_identifier(colName));
2442 : }
2443 :
2444 82 : appendStringInfoChar(&buf, ')');
2445 : }
2446 3636 : ReleaseSysCache(indtup);
2447 :
2448 : /* XXX why do we only print these bits if fullCommand? */
2449 3636 : if (fullCommand && OidIsValid(indexId))
2450 : {
2451 204 : char *options = flatten_reloptions(indexId);
2452 : Oid tblspc;
2453 :
2454 204 : if (options)
2455 : {
2456 0 : appendStringInfo(&buf, " WITH (%s)", options);
2457 0 : pfree(options);
2458 : }
2459 :
2460 : /*
2461 : * Print the tablespace, unless it's the database default.
2462 : * This is to help ALTER TABLE usage of this facility,
2463 : * which needs this behavior to recreate exact catalog
2464 : * state.
2465 : */
2466 204 : tblspc = get_rel_tablespace(indexId);
2467 204 : if (OidIsValid(tblspc))
2468 24 : appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2469 24 : quote_identifier(get_tablespace_name(tblspc)));
2470 : }
2471 :
2472 3636 : break;
2473 : }
2474 2126 : case CONSTRAINT_CHECK:
2475 : {
2476 : Datum val;
2477 : char *conbin;
2478 : char *consrc;
2479 : Node *expr;
2480 : List *context;
2481 :
2482 : /* Fetch constraint expression in parsetree form */
2483 2126 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2484 : Anum_pg_constraint_conbin);
2485 :
2486 2126 : conbin = TextDatumGetCString(val);
2487 2126 : expr = stringToNode(conbin);
2488 :
2489 : /* Set up deparsing context for Var nodes in constraint */
2490 2126 : if (conForm->conrelid != InvalidOid)
2491 : {
2492 : /* relation constraint */
2493 1918 : context = deparse_context_for(get_relation_name(conForm->conrelid),
2494 : conForm->conrelid);
2495 : }
2496 : else
2497 : {
2498 : /* domain constraint --- can't have Vars */
2499 208 : context = NIL;
2500 : }
2501 :
2502 2126 : consrc = deparse_expression_pretty(expr, context, false, false,
2503 : prettyFlags, 0);
2504 :
2505 : /*
2506 : * Now emit the constraint definition, adding NO INHERIT if
2507 : * necessary.
2508 : *
2509 : * There are cases where the constraint expression will be
2510 : * fully parenthesized and we don't need the outer parens ...
2511 : * but there are other cases where we do need 'em. Be
2512 : * conservative for now.
2513 : *
2514 : * Note that simply checking for leading '(' and trailing ')'
2515 : * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2516 : */
2517 2126 : appendStringInfo(&buf, "CHECK (%s)%s",
2518 : consrc,
2519 2126 : conForm->connoinherit ? " NO INHERIT" : "");
2520 2126 : break;
2521 : }
2522 204 : case CONSTRAINT_NOTNULL:
2523 : {
2524 204 : if (conForm->conrelid)
2525 : {
2526 : AttrNumber attnum;
2527 :
2528 198 : attnum = extractNotNullColumn(tup);
2529 :
2530 198 : appendStringInfo(&buf, "NOT NULL %s",
2531 198 : quote_identifier(get_attname(conForm->conrelid,
2532 : attnum, false)));
2533 198 : if (((Form_pg_constraint) GETSTRUCT(tup))->connoinherit)
2534 0 : appendStringInfoString(&buf, " NO INHERIT");
2535 : }
2536 6 : else if (conForm->contypid)
2537 : {
2538 : /* conkey is null for domain not-null constraints */
2539 6 : appendStringInfoString(&buf, "NOT NULL");
2540 : }
2541 204 : break;
2542 : }
2543 :
2544 0 : case CONSTRAINT_TRIGGER:
2545 :
2546 : /*
2547 : * There isn't an ALTER TABLE syntax for creating a user-defined
2548 : * constraint trigger, but it seems better to print something than
2549 : * throw an error; if we throw error then this function couldn't
2550 : * safely be applied to all rows of pg_constraint.
2551 : */
2552 0 : appendStringInfoString(&buf, "TRIGGER");
2553 0 : break;
2554 104 : case CONSTRAINT_EXCLUSION:
2555 : {
2556 104 : Oid indexOid = conForm->conindid;
2557 : Datum val;
2558 : Datum *elems;
2559 : int nElems;
2560 : int i;
2561 : Oid *operators;
2562 :
2563 : /* Extract operator OIDs from the pg_constraint tuple */
2564 104 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2565 : Anum_pg_constraint_conexclop);
2566 :
2567 104 : deconstruct_array_builtin(DatumGetArrayTypeP(val), OIDOID,
2568 : &elems, NULL, &nElems);
2569 :
2570 104 : operators = (Oid *) palloc(nElems * sizeof(Oid));
2571 228 : for (i = 0; i < nElems; i++)
2572 124 : operators[i] = DatumGetObjectId(elems[i]);
2573 :
2574 : /* pg_get_indexdef_worker does the rest */
2575 : /* suppress tablespace because pg_dump wants it that way */
2576 104 : appendStringInfoString(&buf,
2577 104 : pg_get_indexdef_worker(indexOid,
2578 : 0,
2579 : operators,
2580 : false,
2581 : false,
2582 : false,
2583 : false,
2584 : prettyFlags,
2585 : false));
2586 104 : break;
2587 : }
2588 0 : default:
2589 0 : elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2590 : break;
2591 : }
2592 :
2593 6796 : if (conForm->condeferrable)
2594 120 : appendStringInfoString(&buf, " DEFERRABLE");
2595 6796 : if (conForm->condeferred)
2596 48 : appendStringInfoString(&buf, " INITIALLY DEFERRED");
2597 :
2598 : /* Validated status is irrelevant when the constraint is NOT ENFORCED. */
2599 6796 : if (!conForm->conenforced)
2600 86 : appendStringInfoString(&buf, " NOT ENFORCED");
2601 6710 : else if (!conForm->convalidated)
2602 94 : appendStringInfoString(&buf, " NOT VALID");
2603 :
2604 : /* Cleanup */
2605 6796 : systable_endscan(scandesc);
2606 6796 : table_close(relation, AccessShareLock);
2607 :
2608 6796 : return buf.data;
2609 : }
2610 :
2611 :
2612 : /*
2613 : * Convert an int16[] Datum into a comma-separated list of column names
2614 : * for the indicated relation; append the list to buf. Returns the number
2615 : * of keys.
2616 : */
2617 : static int
2618 5100 : decompile_column_index_array(Datum column_index_array, Oid relId,
2619 : bool withPeriod, StringInfo buf)
2620 : {
2621 : Datum *keys;
2622 : int nKeys;
2623 : int j;
2624 :
2625 : /* Extract data from array of int16 */
2626 5100 : deconstruct_array_builtin(DatumGetArrayTypeP(column_index_array), INT2OID,
2627 : &keys, NULL, &nKeys);
2628 :
2629 12444 : for (j = 0; j < nKeys; j++)
2630 : {
2631 : char *colName;
2632 :
2633 7344 : colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2634 :
2635 7344 : if (j == 0)
2636 5100 : appendStringInfoString(buf, quote_identifier(colName));
2637 : else
2638 2492 : appendStringInfo(buf, ", %s%s",
2639 248 : (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
2640 : quote_identifier(colName));
2641 : }
2642 :
2643 5100 : return nKeys;
2644 : }
2645 :
2646 :
2647 : /* ----------
2648 : * pg_get_expr - Decompile an expression tree
2649 : *
2650 : * Input: an expression tree in nodeToString form, and a relation OID
2651 : *
2652 : * Output: reverse-listed expression
2653 : *
2654 : * Currently, the expression can only refer to a single relation, namely
2655 : * the one specified by the second parameter. This is sufficient for
2656 : * partial indexes, column default expressions, etc. We also support
2657 : * Var-free expressions, for which the OID can be InvalidOid.
2658 : *
2659 : * If the OID is nonzero but not actually valid, don't throw an error,
2660 : * just return NULL. This is a bit questionable, but it's what we've
2661 : * done historically, and it can help avoid unwanted failures when
2662 : * examining catalog entries for just-deleted relations.
2663 : *
2664 : * We expect this function to work, or throw a reasonably clean error,
2665 : * for any node tree that can appear in a catalog pg_node_tree column.
2666 : * Query trees, such as those appearing in pg_rewrite.ev_action, are
2667 : * not supported. Nor are expressions in more than one relation, which
2668 : * can appear in places like pg_rewrite.ev_qual.
2669 : * ----------
2670 : */
2671 : Datum
2672 7232 : pg_get_expr(PG_FUNCTION_ARGS)
2673 : {
2674 7232 : text *expr = PG_GETARG_TEXT_PP(0);
2675 7232 : Oid relid = PG_GETARG_OID(1);
2676 : text *result;
2677 : int prettyFlags;
2678 :
2679 7232 : prettyFlags = PRETTYFLAG_INDENT;
2680 :
2681 7232 : result = pg_get_expr_worker(expr, relid, prettyFlags);
2682 7232 : if (result)
2683 7230 : PG_RETURN_TEXT_P(result);
2684 : else
2685 2 : PG_RETURN_NULL();
2686 : }
2687 :
2688 : Datum
2689 484 : pg_get_expr_ext(PG_FUNCTION_ARGS)
2690 : {
2691 484 : text *expr = PG_GETARG_TEXT_PP(0);
2692 484 : Oid relid = PG_GETARG_OID(1);
2693 484 : bool pretty = PG_GETARG_BOOL(2);
2694 : text *result;
2695 : int prettyFlags;
2696 :
2697 484 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2698 :
2699 484 : result = pg_get_expr_worker(expr, relid, prettyFlags);
2700 484 : if (result)
2701 484 : PG_RETURN_TEXT_P(result);
2702 : else
2703 0 : PG_RETURN_NULL();
2704 : }
2705 :
2706 : static text *
2707 7716 : pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
2708 : {
2709 : Node *node;
2710 : Node *tst;
2711 : Relids relids;
2712 : List *context;
2713 : char *exprstr;
2714 7716 : Relation rel = NULL;
2715 : char *str;
2716 :
2717 : /* Convert input pg_node_tree (really TEXT) object to C string */
2718 7716 : exprstr = text_to_cstring(expr);
2719 :
2720 : /* Convert expression to node tree */
2721 7716 : node = (Node *) stringToNode(exprstr);
2722 :
2723 7716 : pfree(exprstr);
2724 :
2725 : /*
2726 : * Throw error if the input is a querytree rather than an expression tree.
2727 : * While we could support queries here, there seems no very good reason
2728 : * to. In most such catalog columns, we'll see a List of Query nodes, or
2729 : * even nested Lists, so drill down to a non-List node before checking.
2730 : */
2731 7716 : tst = node;
2732 7716 : while (tst && IsA(tst, List))
2733 0 : tst = linitial((List *) tst);
2734 7716 : if (tst && IsA(tst, Query))
2735 0 : ereport(ERROR,
2736 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2737 : errmsg("input is a query, not an expression")));
2738 :
2739 : /*
2740 : * Throw error if the expression contains Vars we won't be able to
2741 : * deparse.
2742 : */
2743 7716 : relids = pull_varnos(NULL, node);
2744 7716 : if (OidIsValid(relid))
2745 : {
2746 7654 : if (!bms_is_subset(relids, bms_make_singleton(1)))
2747 0 : ereport(ERROR,
2748 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2749 : errmsg("expression contains variables of more than one relation")));
2750 : }
2751 : else
2752 : {
2753 62 : if (!bms_is_empty(relids))
2754 0 : ereport(ERROR,
2755 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2756 : errmsg("expression contains variables")));
2757 : }
2758 :
2759 : /*
2760 : * Prepare deparse context if needed. If we are deparsing with a relid,
2761 : * we need to transiently open and lock the rel, to make sure it won't go
2762 : * away underneath us. (set_relation_column_names would lock it anyway,
2763 : * so this isn't really introducing any new behavior.)
2764 : */
2765 7716 : if (OidIsValid(relid))
2766 : {
2767 7654 : rel = try_relation_open(relid, AccessShareLock);
2768 7654 : if (rel == NULL)
2769 2 : return NULL;
2770 7652 : context = deparse_context_for(RelationGetRelationName(rel), relid);
2771 : }
2772 : else
2773 62 : context = NIL;
2774 :
2775 : /* Deparse */
2776 7714 : str = deparse_expression_pretty(node, context, false, false,
2777 : prettyFlags, 0);
2778 :
2779 7714 : if (rel != NULL)
2780 7652 : relation_close(rel, AccessShareLock);
2781 :
2782 7714 : return string_to_text(str);
2783 : }
2784 :
2785 :
2786 : /* ----------
2787 : * pg_get_userbyid - Get a user name by roleid and
2788 : * fallback to 'unknown (OID=n)'
2789 : * ----------
2790 : */
2791 : Datum
2792 1682 : pg_get_userbyid(PG_FUNCTION_ARGS)
2793 : {
2794 1682 : Oid roleid = PG_GETARG_OID(0);
2795 : Name result;
2796 : HeapTuple roletup;
2797 : Form_pg_authid role_rec;
2798 :
2799 : /*
2800 : * Allocate space for the result
2801 : */
2802 1682 : result = (Name) palloc(NAMEDATALEN);
2803 1682 : memset(NameStr(*result), 0, NAMEDATALEN);
2804 :
2805 : /*
2806 : * Get the pg_authid entry and print the result
2807 : */
2808 1682 : roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
2809 1682 : if (HeapTupleIsValid(roletup))
2810 : {
2811 1682 : role_rec = (Form_pg_authid) GETSTRUCT(roletup);
2812 1682 : *result = role_rec->rolname;
2813 1682 : ReleaseSysCache(roletup);
2814 : }
2815 : else
2816 0 : sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
2817 :
2818 1682 : PG_RETURN_NAME(result);
2819 : }
2820 :
2821 :
2822 : /*
2823 : * pg_get_serial_sequence
2824 : * Get the name of the sequence used by an identity or serial column,
2825 : * formatted suitably for passing to setval, nextval or currval.
2826 : * First parameter is not treated as double-quoted, second parameter
2827 : * is --- see documentation for reason.
2828 : */
2829 : Datum
2830 12 : pg_get_serial_sequence(PG_FUNCTION_ARGS)
2831 : {
2832 12 : text *tablename = PG_GETARG_TEXT_PP(0);
2833 12 : text *columnname = PG_GETARG_TEXT_PP(1);
2834 : RangeVar *tablerv;
2835 : Oid tableOid;
2836 : char *column;
2837 : AttrNumber attnum;
2838 12 : Oid sequenceId = InvalidOid;
2839 : Relation depRel;
2840 : ScanKeyData key[3];
2841 : SysScanDesc scan;
2842 : HeapTuple tup;
2843 :
2844 : /* Look up table name. Can't lock it - we might not have privileges. */
2845 12 : tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
2846 12 : tableOid = RangeVarGetRelid(tablerv, NoLock, false);
2847 :
2848 : /* Get the number of the column */
2849 12 : column = text_to_cstring(columnname);
2850 :
2851 12 : attnum = get_attnum(tableOid, column);
2852 12 : if (attnum == InvalidAttrNumber)
2853 0 : ereport(ERROR,
2854 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2855 : errmsg("column \"%s\" of relation \"%s\" does not exist",
2856 : column, tablerv->relname)));
2857 :
2858 : /* Search the dependency table for the dependent sequence */
2859 12 : depRel = table_open(DependRelationId, AccessShareLock);
2860 :
2861 12 : ScanKeyInit(&key[0],
2862 : Anum_pg_depend_refclassid,
2863 : BTEqualStrategyNumber, F_OIDEQ,
2864 : ObjectIdGetDatum(RelationRelationId));
2865 12 : ScanKeyInit(&key[1],
2866 : Anum_pg_depend_refobjid,
2867 : BTEqualStrategyNumber, F_OIDEQ,
2868 : ObjectIdGetDatum(tableOid));
2869 12 : ScanKeyInit(&key[2],
2870 : Anum_pg_depend_refobjsubid,
2871 : BTEqualStrategyNumber, F_INT4EQ,
2872 : Int32GetDatum(attnum));
2873 :
2874 12 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
2875 : NULL, 3, key);
2876 :
2877 30 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
2878 : {
2879 30 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
2880 :
2881 : /*
2882 : * Look for an auto dependency (serial column) or internal dependency
2883 : * (identity column) of a sequence on a column. (We need the relkind
2884 : * test because indexes can also have auto dependencies on columns.)
2885 : */
2886 30 : if (deprec->classid == RelationRelationId &&
2887 12 : deprec->objsubid == 0 &&
2888 12 : (deprec->deptype == DEPENDENCY_AUTO ||
2889 18 : deprec->deptype == DEPENDENCY_INTERNAL) &&
2890 12 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
2891 : {
2892 12 : sequenceId = deprec->objid;
2893 12 : break;
2894 : }
2895 : }
2896 :
2897 12 : systable_endscan(scan);
2898 12 : table_close(depRel, AccessShareLock);
2899 :
2900 12 : if (OidIsValid(sequenceId))
2901 : {
2902 : char *result;
2903 :
2904 12 : result = generate_qualified_relation_name(sequenceId);
2905 :
2906 12 : PG_RETURN_TEXT_P(string_to_text(result));
2907 : }
2908 :
2909 0 : PG_RETURN_NULL();
2910 : }
2911 :
2912 :
2913 : /*
2914 : * pg_get_functiondef
2915 : * Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
2916 : * the specified function.
2917 : *
2918 : * Note: if you change the output format of this function, be careful not
2919 : * to break psql's rules (in \ef and \sf) for identifying the start of the
2920 : * function body. To wit: the function body starts on a line that begins with
2921 : * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
2922 : */
2923 : Datum
2924 172 : pg_get_functiondef(PG_FUNCTION_ARGS)
2925 : {
2926 172 : Oid funcid = PG_GETARG_OID(0);
2927 : StringInfoData buf;
2928 : StringInfoData dq;
2929 : HeapTuple proctup;
2930 : Form_pg_proc proc;
2931 : bool isfunction;
2932 : Datum tmp;
2933 : bool isnull;
2934 : const char *prosrc;
2935 : const char *name;
2936 : const char *nsp;
2937 : float4 procost;
2938 : int oldlen;
2939 :
2940 172 : initStringInfo(&buf);
2941 :
2942 : /* Look up the function */
2943 172 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
2944 172 : if (!HeapTupleIsValid(proctup))
2945 6 : PG_RETURN_NULL();
2946 :
2947 166 : proc = (Form_pg_proc) GETSTRUCT(proctup);
2948 166 : name = NameStr(proc->proname);
2949 :
2950 166 : if (proc->prokind == PROKIND_AGGREGATE)
2951 0 : ereport(ERROR,
2952 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2953 : errmsg("\"%s\" is an aggregate function", name)));
2954 :
2955 166 : isfunction = (proc->prokind != PROKIND_PROCEDURE);
2956 :
2957 : /*
2958 : * We always qualify the function name, to ensure the right function gets
2959 : * replaced.
2960 : */
2961 166 : nsp = get_namespace_name_or_temp(proc->pronamespace);
2962 166 : appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
2963 : isfunction ? "FUNCTION" : "PROCEDURE",
2964 : quote_qualified_identifier(nsp, name));
2965 166 : (void) print_function_arguments(&buf, proctup, false, true);
2966 166 : appendStringInfoString(&buf, ")\n");
2967 166 : if (isfunction)
2968 : {
2969 146 : appendStringInfoString(&buf, " RETURNS ");
2970 146 : print_function_rettype(&buf, proctup);
2971 146 : appendStringInfoChar(&buf, '\n');
2972 : }
2973 :
2974 166 : print_function_trftypes(&buf, proctup);
2975 :
2976 166 : appendStringInfo(&buf, " LANGUAGE %s\n",
2977 166 : quote_identifier(get_language_name(proc->prolang, false)));
2978 :
2979 : /* Emit some miscellaneous options on one line */
2980 166 : oldlen = buf.len;
2981 :
2982 166 : if (proc->prokind == PROKIND_WINDOW)
2983 0 : appendStringInfoString(&buf, " WINDOW");
2984 166 : switch (proc->provolatile)
2985 : {
2986 12 : case PROVOLATILE_IMMUTABLE:
2987 12 : appendStringInfoString(&buf, " IMMUTABLE");
2988 12 : break;
2989 30 : case PROVOLATILE_STABLE:
2990 30 : appendStringInfoString(&buf, " STABLE");
2991 30 : break;
2992 124 : case PROVOLATILE_VOLATILE:
2993 124 : break;
2994 : }
2995 :
2996 166 : switch (proc->proparallel)
2997 : {
2998 28 : case PROPARALLEL_SAFE:
2999 28 : appendStringInfoString(&buf, " PARALLEL SAFE");
3000 28 : break;
3001 0 : case PROPARALLEL_RESTRICTED:
3002 0 : appendStringInfoString(&buf, " PARALLEL RESTRICTED");
3003 0 : break;
3004 138 : case PROPARALLEL_UNSAFE:
3005 138 : break;
3006 : }
3007 :
3008 166 : if (proc->proisstrict)
3009 50 : appendStringInfoString(&buf, " STRICT");
3010 166 : if (proc->prosecdef)
3011 6 : appendStringInfoString(&buf, " SECURITY DEFINER");
3012 166 : if (proc->proleakproof)
3013 0 : appendStringInfoString(&buf, " LEAKPROOF");
3014 :
3015 : /* This code for the default cost and rows should match functioncmds.c */
3016 166 : if (proc->prolang == INTERNALlanguageId ||
3017 166 : proc->prolang == ClanguageId)
3018 10 : procost = 1;
3019 : else
3020 156 : procost = 100;
3021 166 : if (proc->procost != procost)
3022 6 : appendStringInfo(&buf, " COST %g", proc->procost);
3023 :
3024 166 : if (proc->prorows > 0 && proc->prorows != 1000)
3025 0 : appendStringInfo(&buf, " ROWS %g", proc->prorows);
3026 :
3027 166 : if (proc->prosupport)
3028 : {
3029 : Oid argtypes[1];
3030 :
3031 : /*
3032 : * We should qualify the support function's name if it wouldn't be
3033 : * resolved by lookup in the current search path.
3034 : */
3035 0 : argtypes[0] = INTERNALOID;
3036 0 : appendStringInfo(&buf, " SUPPORT %s",
3037 : generate_function_name(proc->prosupport, 1,
3038 : NIL, argtypes,
3039 : false, NULL, false));
3040 : }
3041 :
3042 166 : if (oldlen != buf.len)
3043 64 : appendStringInfoChar(&buf, '\n');
3044 :
3045 : /* Emit any proconfig options, one per line */
3046 166 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
3047 166 : if (!isnull)
3048 : {
3049 6 : ArrayType *a = DatumGetArrayTypeP(tmp);
3050 : int i;
3051 :
3052 : Assert(ARR_ELEMTYPE(a) == TEXTOID);
3053 : Assert(ARR_NDIM(a) == 1);
3054 : Assert(ARR_LBOUND(a)[0] == 1);
3055 :
3056 36 : for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3057 : {
3058 : Datum d;
3059 :
3060 30 : d = array_ref(a, 1, &i,
3061 : -1 /* varlenarray */ ,
3062 : -1 /* TEXT's typlen */ ,
3063 : false /* TEXT's typbyval */ ,
3064 : TYPALIGN_INT /* TEXT's typalign */ ,
3065 : &isnull);
3066 30 : if (!isnull)
3067 : {
3068 30 : char *configitem = TextDatumGetCString(d);
3069 : char *pos;
3070 :
3071 30 : pos = strchr(configitem, '=');
3072 30 : if (pos == NULL)
3073 0 : continue;
3074 30 : *pos++ = '\0';
3075 :
3076 30 : appendStringInfo(&buf, " SET %s TO ",
3077 : quote_identifier(configitem));
3078 :
3079 : /*
3080 : * Variables that are marked GUC_LIST_QUOTE were already fully
3081 : * quoted by flatten_set_variable_args() before they were put
3082 : * into the proconfig array. However, because the quoting
3083 : * rules used there aren't exactly like SQL's, we have to
3084 : * break the list value apart and then quote the elements as
3085 : * string literals. (The elements may be double-quoted as-is,
3086 : * but we can't just feed them to the SQL parser; it would do
3087 : * the wrong thing with elements that are zero-length or
3088 : * longer than NAMEDATALEN.)
3089 : *
3090 : * Variables that are not so marked should just be emitted as
3091 : * simple string literals. If the variable is not known to
3092 : * guc.c, we'll do that; this makes it unsafe to use
3093 : * GUC_LIST_QUOTE for extension variables.
3094 : */
3095 30 : if (GetConfigOptionFlags(configitem, true) & GUC_LIST_QUOTE)
3096 : {
3097 : List *namelist;
3098 : ListCell *lc;
3099 :
3100 : /* Parse string into list of identifiers */
3101 12 : if (!SplitGUCList(pos, ',', &namelist))
3102 : {
3103 : /* this shouldn't fail really */
3104 0 : elog(ERROR, "invalid list syntax in proconfig item");
3105 : }
3106 42 : foreach(lc, namelist)
3107 : {
3108 30 : char *curname = (char *) lfirst(lc);
3109 :
3110 30 : simple_quote_literal(&buf, curname);
3111 30 : if (lnext(namelist, lc))
3112 18 : appendStringInfoString(&buf, ", ");
3113 : }
3114 : }
3115 : else
3116 18 : simple_quote_literal(&buf, pos);
3117 30 : appendStringInfoChar(&buf, '\n');
3118 : }
3119 : }
3120 : }
3121 :
3122 : /* And finally the function definition ... */
3123 166 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3124 166 : if (proc->prolang == SQLlanguageId && !isnull)
3125 : {
3126 114 : print_function_sqlbody(&buf, proctup);
3127 : }
3128 : else
3129 : {
3130 52 : appendStringInfoString(&buf, "AS ");
3131 :
3132 52 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
3133 52 : if (!isnull)
3134 : {
3135 10 : simple_quote_literal(&buf, TextDatumGetCString(tmp));
3136 10 : appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3137 : }
3138 :
3139 52 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosrc);
3140 52 : prosrc = TextDatumGetCString(tmp);
3141 :
3142 : /*
3143 : * We always use dollar quoting. Figure out a suitable delimiter.
3144 : *
3145 : * Since the user is likely to be editing the function body string, we
3146 : * shouldn't use a short delimiter that he might easily create a
3147 : * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3148 : * if needed.
3149 : */
3150 52 : initStringInfo(&dq);
3151 52 : appendStringInfoChar(&dq, '$');
3152 52 : appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3153 52 : while (strstr(prosrc, dq.data) != NULL)
3154 0 : appendStringInfoChar(&dq, 'x');
3155 52 : appendStringInfoChar(&dq, '$');
3156 :
3157 52 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3158 52 : appendStringInfoString(&buf, prosrc);
3159 52 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3160 : }
3161 :
3162 166 : appendStringInfoChar(&buf, '\n');
3163 :
3164 166 : ReleaseSysCache(proctup);
3165 :
3166 166 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3167 : }
3168 :
3169 : /*
3170 : * pg_get_function_arguments
3171 : * Get a nicely-formatted list of arguments for a function.
3172 : * This is everything that would go between the parentheses in
3173 : * CREATE FUNCTION.
3174 : */
3175 : Datum
3176 4612 : pg_get_function_arguments(PG_FUNCTION_ARGS)
3177 : {
3178 4612 : Oid funcid = PG_GETARG_OID(0);
3179 : StringInfoData buf;
3180 : HeapTuple proctup;
3181 :
3182 4612 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3183 4612 : if (!HeapTupleIsValid(proctup))
3184 6 : PG_RETURN_NULL();
3185 :
3186 4606 : initStringInfo(&buf);
3187 :
3188 4606 : (void) print_function_arguments(&buf, proctup, false, true);
3189 :
3190 4606 : ReleaseSysCache(proctup);
3191 :
3192 4606 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3193 : }
3194 :
3195 : /*
3196 : * pg_get_function_identity_arguments
3197 : * Get a formatted list of arguments for a function.
3198 : * This is everything that would go between the parentheses in
3199 : * ALTER FUNCTION, etc. In particular, don't print defaults.
3200 : */
3201 : Datum
3202 4080 : pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
3203 : {
3204 4080 : Oid funcid = PG_GETARG_OID(0);
3205 : StringInfoData buf;
3206 : HeapTuple proctup;
3207 :
3208 4080 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3209 4080 : if (!HeapTupleIsValid(proctup))
3210 6 : PG_RETURN_NULL();
3211 :
3212 4074 : initStringInfo(&buf);
3213 :
3214 4074 : (void) print_function_arguments(&buf, proctup, false, false);
3215 :
3216 4074 : ReleaseSysCache(proctup);
3217 :
3218 4074 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3219 : }
3220 :
3221 : /*
3222 : * pg_get_function_result
3223 : * Get a nicely-formatted version of the result type of a function.
3224 : * This is what would appear after RETURNS in CREATE FUNCTION.
3225 : */
3226 : Datum
3227 4026 : pg_get_function_result(PG_FUNCTION_ARGS)
3228 : {
3229 4026 : Oid funcid = PG_GETARG_OID(0);
3230 : StringInfoData buf;
3231 : HeapTuple proctup;
3232 :
3233 4026 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3234 4026 : if (!HeapTupleIsValid(proctup))
3235 6 : PG_RETURN_NULL();
3236 :
3237 4020 : if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
3238 : {
3239 240 : ReleaseSysCache(proctup);
3240 240 : PG_RETURN_NULL();
3241 : }
3242 :
3243 3780 : initStringInfo(&buf);
3244 :
3245 3780 : print_function_rettype(&buf, proctup);
3246 :
3247 3780 : ReleaseSysCache(proctup);
3248 :
3249 3780 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3250 : }
3251 :
3252 : /*
3253 : * Guts of pg_get_function_result: append the function's return type
3254 : * to the specified buffer.
3255 : */
3256 : static void
3257 3926 : print_function_rettype(StringInfo buf, HeapTuple proctup)
3258 : {
3259 3926 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3260 3926 : int ntabargs = 0;
3261 : StringInfoData rbuf;
3262 :
3263 3926 : initStringInfo(&rbuf);
3264 :
3265 3926 : if (proc->proretset)
3266 : {
3267 : /* It might be a table function; try to print the arguments */
3268 406 : appendStringInfoString(&rbuf, "TABLE(");
3269 406 : ntabargs = print_function_arguments(&rbuf, proctup, true, false);
3270 406 : if (ntabargs > 0)
3271 76 : appendStringInfoChar(&rbuf, ')');
3272 : else
3273 330 : resetStringInfo(&rbuf);
3274 : }
3275 :
3276 3926 : if (ntabargs == 0)
3277 : {
3278 : /* Not a table function, so do the normal thing */
3279 3850 : if (proc->proretset)
3280 330 : appendStringInfoString(&rbuf, "SETOF ");
3281 3850 : appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3282 : }
3283 :
3284 3926 : appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
3285 3926 : }
3286 :
3287 : /*
3288 : * Common code for pg_get_function_arguments and pg_get_function_result:
3289 : * append the desired subset of arguments to buf. We print only TABLE
3290 : * arguments when print_table_args is true, and all the others when it's false.
3291 : * We print argument defaults only if print_defaults is true.
3292 : * Function return value is the number of arguments printed.
3293 : */
3294 : static int
3295 9252 : print_function_arguments(StringInfo buf, HeapTuple proctup,
3296 : bool print_table_args, bool print_defaults)
3297 : {
3298 9252 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3299 : int numargs;
3300 : Oid *argtypes;
3301 : char **argnames;
3302 : char *argmodes;
3303 9252 : int insertorderbyat = -1;
3304 : int argsprinted;
3305 : int inputargno;
3306 : int nlackdefaults;
3307 9252 : List *argdefaults = NIL;
3308 9252 : ListCell *nextargdefault = NULL;
3309 : int i;
3310 :
3311 9252 : numargs = get_func_arg_info(proctup,
3312 : &argtypes, &argnames, &argmodes);
3313 :
3314 9252 : nlackdefaults = numargs;
3315 9252 : if (print_defaults && proc->pronargdefaults > 0)
3316 : {
3317 : Datum proargdefaults;
3318 : bool isnull;
3319 :
3320 38 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3321 : Anum_pg_proc_proargdefaults,
3322 : &isnull);
3323 38 : if (!isnull)
3324 : {
3325 : char *str;
3326 :
3327 38 : str = TextDatumGetCString(proargdefaults);
3328 38 : argdefaults = castNode(List, stringToNode(str));
3329 38 : pfree(str);
3330 38 : nextargdefault = list_head(argdefaults);
3331 : /* nlackdefaults counts only *input* arguments lacking defaults */
3332 38 : nlackdefaults = proc->pronargs - list_length(argdefaults);
3333 : }
3334 : }
3335 :
3336 : /* Check for special treatment of ordered-set aggregates */
3337 9252 : if (proc->prokind == PROKIND_AGGREGATE)
3338 : {
3339 : HeapTuple aggtup;
3340 : Form_pg_aggregate agg;
3341 :
3342 1174 : aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(proc->oid));
3343 1174 : if (!HeapTupleIsValid(aggtup))
3344 0 : elog(ERROR, "cache lookup failed for aggregate %u",
3345 : proc->oid);
3346 1174 : agg = (Form_pg_aggregate) GETSTRUCT(aggtup);
3347 1174 : if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3348 52 : insertorderbyat = agg->aggnumdirectargs;
3349 1174 : ReleaseSysCache(aggtup);
3350 : }
3351 :
3352 9252 : argsprinted = 0;
3353 9252 : inputargno = 0;
3354 18682 : for (i = 0; i < numargs; i++)
3355 : {
3356 9430 : Oid argtype = argtypes[i];
3357 9430 : char *argname = argnames ? argnames[i] : NULL;
3358 9430 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
3359 : const char *modename;
3360 : bool isinput;
3361 :
3362 9430 : switch (argmode)
3363 : {
3364 7752 : case PROARGMODE_IN:
3365 :
3366 : /*
3367 : * For procedures, explicitly mark all argument modes, so as
3368 : * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3369 : */
3370 7752 : if (proc->prokind == PROKIND_PROCEDURE)
3371 536 : modename = "IN ";
3372 : else
3373 7216 : modename = "";
3374 7752 : isinput = true;
3375 7752 : break;
3376 100 : case PROARGMODE_INOUT:
3377 100 : modename = "INOUT ";
3378 100 : isinput = true;
3379 100 : break;
3380 956 : case PROARGMODE_OUT:
3381 956 : modename = "OUT ";
3382 956 : isinput = false;
3383 956 : break;
3384 178 : case PROARGMODE_VARIADIC:
3385 178 : modename = "VARIADIC ";
3386 178 : isinput = true;
3387 178 : break;
3388 444 : case PROARGMODE_TABLE:
3389 444 : modename = "";
3390 444 : isinput = false;
3391 444 : break;
3392 0 : default:
3393 0 : elog(ERROR, "invalid parameter mode '%c'", argmode);
3394 : modename = NULL; /* keep compiler quiet */
3395 : isinput = false;
3396 : break;
3397 : }
3398 9430 : if (isinput)
3399 8030 : inputargno++; /* this is a 1-based counter */
3400 :
3401 9430 : if (print_table_args != (argmode == PROARGMODE_TABLE))
3402 764 : continue;
3403 :
3404 8666 : if (argsprinted == insertorderbyat)
3405 : {
3406 52 : if (argsprinted)
3407 52 : appendStringInfoChar(buf, ' ');
3408 52 : appendStringInfoString(buf, "ORDER BY ");
3409 : }
3410 8614 : else if (argsprinted)
3411 2776 : appendStringInfoString(buf, ", ");
3412 :
3413 8666 : appendStringInfoString(buf, modename);
3414 8666 : if (argname && argname[0])
3415 3110 : appendStringInfo(buf, "%s ", quote_identifier(argname));
3416 8666 : appendStringInfoString(buf, format_type_be(argtype));
3417 8666 : if (print_defaults && isinput && inputargno > nlackdefaults)
3418 : {
3419 : Node *expr;
3420 :
3421 : Assert(nextargdefault != NULL);
3422 58 : expr = (Node *) lfirst(nextargdefault);
3423 58 : nextargdefault = lnext(argdefaults, nextargdefault);
3424 :
3425 58 : appendStringInfo(buf, " DEFAULT %s",
3426 : deparse_expression(expr, NIL, false, false));
3427 : }
3428 8666 : argsprinted++;
3429 :
3430 : /* nasty hack: print the last arg twice for variadic ordered-set agg */
3431 8666 : if (argsprinted == insertorderbyat && i == numargs - 1)
3432 : {
3433 26 : i--;
3434 : /* aggs shouldn't have defaults anyway, but just to be sure ... */
3435 26 : print_defaults = false;
3436 : }
3437 : }
3438 :
3439 9252 : return argsprinted;
3440 : }
3441 :
3442 : static bool
3443 96 : is_input_argument(int nth, const char *argmodes)
3444 : {
3445 : return (!argmodes
3446 42 : || argmodes[nth] == PROARGMODE_IN
3447 18 : || argmodes[nth] == PROARGMODE_INOUT
3448 138 : || argmodes[nth] == PROARGMODE_VARIADIC);
3449 : }
3450 :
3451 : /*
3452 : * Append used transformed types to specified buffer
3453 : */
3454 : static void
3455 166 : print_function_trftypes(StringInfo buf, HeapTuple proctup)
3456 : {
3457 : Oid *trftypes;
3458 : int ntypes;
3459 :
3460 166 : ntypes = get_func_trftypes(proctup, &trftypes);
3461 166 : if (ntypes > 0)
3462 : {
3463 : int i;
3464 :
3465 6 : appendStringInfoString(buf, " TRANSFORM ");
3466 16 : for (i = 0; i < ntypes; i++)
3467 : {
3468 10 : if (i != 0)
3469 4 : appendStringInfoString(buf, ", ");
3470 10 : appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3471 : }
3472 6 : appendStringInfoChar(buf, '\n');
3473 : }
3474 166 : }
3475 :
3476 : /*
3477 : * Get textual representation of a function argument's default value. The
3478 : * second argument of this function is the argument number among all arguments
3479 : * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
3480 : * how information_schema.sql uses it.
3481 : */
3482 : Datum
3483 54 : pg_get_function_arg_default(PG_FUNCTION_ARGS)
3484 : {
3485 54 : Oid funcid = PG_GETARG_OID(0);
3486 54 : int32 nth_arg = PG_GETARG_INT32(1);
3487 : HeapTuple proctup;
3488 : Form_pg_proc proc;
3489 : int numargs;
3490 : Oid *argtypes;
3491 : char **argnames;
3492 : char *argmodes;
3493 : int i;
3494 : List *argdefaults;
3495 : Node *node;
3496 : char *str;
3497 : int nth_inputarg;
3498 : Datum proargdefaults;
3499 : bool isnull;
3500 : int nth_default;
3501 :
3502 54 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3503 54 : if (!HeapTupleIsValid(proctup))
3504 12 : PG_RETURN_NULL();
3505 :
3506 42 : numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3507 42 : if (nth_arg < 1 || nth_arg > numargs || !is_input_argument(nth_arg - 1, argmodes))
3508 : {
3509 12 : ReleaseSysCache(proctup);
3510 12 : PG_RETURN_NULL();
3511 : }
3512 :
3513 30 : nth_inputarg = 0;
3514 84 : for (i = 0; i < nth_arg; i++)
3515 54 : if (is_input_argument(i, argmodes))
3516 48 : nth_inputarg++;
3517 :
3518 30 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3519 : Anum_pg_proc_proargdefaults,
3520 : &isnull);
3521 30 : if (isnull)
3522 : {
3523 0 : ReleaseSysCache(proctup);
3524 0 : PG_RETURN_NULL();
3525 : }
3526 :
3527 30 : str = TextDatumGetCString(proargdefaults);
3528 30 : argdefaults = castNode(List, stringToNode(str));
3529 30 : pfree(str);
3530 :
3531 30 : proc = (Form_pg_proc) GETSTRUCT(proctup);
3532 :
3533 : /*
3534 : * Calculate index into proargdefaults: proargdefaults corresponds to the
3535 : * last N input arguments, where N = pronargdefaults.
3536 : */
3537 30 : nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3538 :
3539 30 : if (nth_default < 0 || nth_default >= list_length(argdefaults))
3540 : {
3541 6 : ReleaseSysCache(proctup);
3542 6 : PG_RETURN_NULL();
3543 : }
3544 24 : node = list_nth(argdefaults, nth_default);
3545 24 : str = deparse_expression(node, NIL, false, false);
3546 :
3547 24 : ReleaseSysCache(proctup);
3548 :
3549 24 : PG_RETURN_TEXT_P(string_to_text(str));
3550 : }
3551 :
3552 : static void
3553 212 : print_function_sqlbody(StringInfo buf, HeapTuple proctup)
3554 : {
3555 : int numargs;
3556 : Oid *argtypes;
3557 : char **argnames;
3558 : char *argmodes;
3559 212 : deparse_namespace dpns = {0};
3560 : Datum tmp;
3561 : Node *n;
3562 :
3563 212 : dpns.funcname = pstrdup(NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname));
3564 212 : numargs = get_func_arg_info(proctup,
3565 : &argtypes, &argnames, &argmodes);
3566 212 : dpns.numargs = numargs;
3567 212 : dpns.argnames = argnames;
3568 :
3569 212 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosqlbody);
3570 212 : n = stringToNode(TextDatumGetCString(tmp));
3571 :
3572 212 : if (IsA(n, List))
3573 : {
3574 : List *stmts;
3575 : ListCell *lc;
3576 :
3577 166 : stmts = linitial(castNode(List, n));
3578 :
3579 166 : appendStringInfoString(buf, "BEGIN ATOMIC\n");
3580 :
3581 322 : foreach(lc, stmts)
3582 : {
3583 156 : Query *query = lfirst_node(Query, lc);
3584 :
3585 : /* It seems advisable to get at least AccessShareLock on rels */
3586 156 : AcquireRewriteLocks(query, false, false);
3587 156 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3588 : PRETTYFLAG_INDENT, WRAP_COLUMN_DEFAULT, 1);
3589 156 : appendStringInfoChar(buf, ';');
3590 156 : appendStringInfoChar(buf, '\n');
3591 : }
3592 :
3593 166 : appendStringInfoString(buf, "END");
3594 : }
3595 : else
3596 : {
3597 46 : Query *query = castNode(Query, n);
3598 :
3599 : /* It seems advisable to get at least AccessShareLock on rels */
3600 46 : AcquireRewriteLocks(query, false, false);
3601 46 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3602 : 0, WRAP_COLUMN_DEFAULT, 0);
3603 : }
3604 212 : }
3605 :
3606 : Datum
3607 3502 : pg_get_function_sqlbody(PG_FUNCTION_ARGS)
3608 : {
3609 3502 : Oid funcid = PG_GETARG_OID(0);
3610 : StringInfoData buf;
3611 : HeapTuple proctup;
3612 : bool isnull;
3613 :
3614 3502 : initStringInfo(&buf);
3615 :
3616 : /* Look up the function */
3617 3502 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3618 3502 : if (!HeapTupleIsValid(proctup))
3619 0 : PG_RETURN_NULL();
3620 :
3621 3502 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3622 3502 : if (isnull)
3623 : {
3624 3404 : ReleaseSysCache(proctup);
3625 3404 : PG_RETURN_NULL();
3626 : }
3627 :
3628 98 : print_function_sqlbody(&buf, proctup);
3629 :
3630 98 : ReleaseSysCache(proctup);
3631 :
3632 98 : PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
3633 : }
3634 :
3635 :
3636 : /*
3637 : * deparse_expression - General utility for deparsing expressions
3638 : *
3639 : * calls deparse_expression_pretty with all prettyPrinting disabled
3640 : */
3641 : char *
3642 67322 : deparse_expression(Node *expr, List *dpcontext,
3643 : bool forceprefix, bool showimplicit)
3644 : {
3645 67322 : return deparse_expression_pretty(expr, dpcontext, forceprefix,
3646 : showimplicit, 0, 0);
3647 : }
3648 :
3649 : /* ----------
3650 : * deparse_expression_pretty - General utility for deparsing expressions
3651 : *
3652 : * expr is the node tree to be deparsed. It must be a transformed expression
3653 : * tree (ie, not the raw output of gram.y).
3654 : *
3655 : * dpcontext is a list of deparse_namespace nodes representing the context
3656 : * for interpreting Vars in the node tree. It can be NIL if no Vars are
3657 : * expected.
3658 : *
3659 : * forceprefix is true to force all Vars to be prefixed with their table names.
3660 : *
3661 : * showimplicit is true to force all implicit casts to be shown explicitly.
3662 : *
3663 : * Tries to pretty up the output according to prettyFlags and startIndent.
3664 : *
3665 : * The result is a palloc'd string.
3666 : * ----------
3667 : */
3668 : static char *
3669 78734 : deparse_expression_pretty(Node *expr, List *dpcontext,
3670 : bool forceprefix, bool showimplicit,
3671 : int prettyFlags, int startIndent)
3672 : {
3673 : StringInfoData buf;
3674 : deparse_context context;
3675 :
3676 78734 : initStringInfo(&buf);
3677 78734 : context.buf = &buf;
3678 78734 : context.namespaces = dpcontext;
3679 78734 : context.resultDesc = NULL;
3680 78734 : context.targetList = NIL;
3681 78734 : context.windowClause = NIL;
3682 78734 : context.varprefix = forceprefix;
3683 78734 : context.prettyFlags = prettyFlags;
3684 78734 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
3685 78734 : context.indentLevel = startIndent;
3686 78734 : context.colNamesVisible = true;
3687 78734 : context.inGroupBy = false;
3688 78734 : context.varInOrderBy = false;
3689 78734 : context.appendparents = NULL;
3690 :
3691 78734 : get_rule_expr(expr, &context, showimplicit);
3692 :
3693 78734 : return buf.data;
3694 : }
3695 :
3696 : /* ----------
3697 : * deparse_context_for - Build deparse context for a single relation
3698 : *
3699 : * Given the reference name (alias) and OID of a relation, build deparsing
3700 : * context for an expression referencing only that relation (as varno 1,
3701 : * varlevelsup 0). This is sufficient for many uses of deparse_expression.
3702 : * ----------
3703 : */
3704 : List *
3705 21288 : deparse_context_for(const char *aliasname, Oid relid)
3706 : {
3707 : deparse_namespace *dpns;
3708 : RangeTblEntry *rte;
3709 :
3710 21288 : dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
3711 :
3712 : /* Build a minimal RTE for the rel */
3713 21288 : rte = makeNode(RangeTblEntry);
3714 21288 : rte->rtekind = RTE_RELATION;
3715 21288 : rte->relid = relid;
3716 21288 : rte->relkind = RELKIND_RELATION; /* no need for exactness here */
3717 21288 : rte->rellockmode = AccessShareLock;
3718 21288 : rte->alias = makeAlias(aliasname, NIL);
3719 21288 : rte->eref = rte->alias;
3720 21288 : rte->lateral = false;
3721 21288 : rte->inh = false;
3722 21288 : rte->inFromCl = true;
3723 :
3724 : /* Build one-element rtable */
3725 21288 : dpns->rtable = list_make1(rte);
3726 21288 : dpns->subplans = NIL;
3727 21288 : dpns->ctes = NIL;
3728 21288 : dpns->appendrels = NULL;
3729 21288 : set_rtable_names(dpns, NIL, NULL);
3730 21288 : set_simple_column_names(dpns);
3731 :
3732 : /* Return a one-deep namespace stack */
3733 21288 : return list_make1(dpns);
3734 : }
3735 :
3736 : /*
3737 : * deparse_context_for_plan_tree - Build deparse context for a Plan tree
3738 : *
3739 : * When deparsing an expression in a Plan tree, we use the plan's rangetable
3740 : * to resolve names of simple Vars. The initialization of column names for
3741 : * this is rather expensive if the rangetable is large, and it'll be the same
3742 : * for every expression in the Plan tree; so we do it just once and re-use
3743 : * the result of this function for each expression. (Note that the result
3744 : * is not usable until set_deparse_context_plan() is applied to it.)
3745 : *
3746 : * In addition to the PlannedStmt, pass the per-RTE alias names
3747 : * assigned by a previous call to select_rtable_names_for_explain.
3748 : */
3749 : List *
3750 22682 : deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
3751 : {
3752 : deparse_namespace *dpns;
3753 :
3754 22682 : dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
3755 :
3756 : /* Initialize fields that stay the same across the whole plan tree */
3757 22682 : dpns->rtable = pstmt->rtable;
3758 22682 : dpns->rtable_names = rtable_names;
3759 22682 : dpns->subplans = pstmt->subplans;
3760 22682 : dpns->ctes = NIL;
3761 22682 : if (pstmt->appendRelations)
3762 : {
3763 : /* Set up the array, indexed by child relid */
3764 3696 : int ntables = list_length(dpns->rtable);
3765 : ListCell *lc;
3766 :
3767 3696 : dpns->appendrels = (AppendRelInfo **)
3768 3696 : palloc0((ntables + 1) * sizeof(AppendRelInfo *));
3769 20140 : foreach(lc, pstmt->appendRelations)
3770 : {
3771 16444 : AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
3772 16444 : Index crelid = appinfo->child_relid;
3773 :
3774 : Assert(crelid > 0 && crelid <= ntables);
3775 : Assert(dpns->appendrels[crelid] == NULL);
3776 16444 : dpns->appendrels[crelid] = appinfo;
3777 : }
3778 : }
3779 : else
3780 18986 : dpns->appendrels = NULL; /* don't need it */
3781 :
3782 : /*
3783 : * Set up column name aliases, ignoring any join RTEs; they don't matter
3784 : * because plan trees don't contain any join alias Vars.
3785 : */
3786 22682 : set_simple_column_names(dpns);
3787 :
3788 : /* Return a one-deep namespace stack */
3789 22682 : return list_make1(dpns);
3790 : }
3791 :
3792 : /*
3793 : * set_deparse_context_plan - Specify Plan node containing expression
3794 : *
3795 : * When deparsing an expression in a Plan tree, we might have to resolve
3796 : * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
3797 : * provide the parent Plan node. Then OUTER_VAR and INNER_VAR references
3798 : * can be resolved by drilling down into the left and right child plans.
3799 : * Similarly, INDEX_VAR references can be resolved by reference to the
3800 : * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
3801 : * ForeignScan and CustomScan nodes. (Note that we don't currently support
3802 : * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
3803 : * for those, we can only deparse the indexqualorig fields, which won't
3804 : * contain INDEX_VAR Vars.)
3805 : *
3806 : * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
3807 : * the most-closely-nested first. This is needed to resolve PARAM_EXEC
3808 : * Params. Note we assume that all the Plan nodes share the same rtable.
3809 : *
3810 : * For a ModifyTable plan, we might also need to resolve references to OLD/NEW
3811 : * variables in the RETURNING list, so we copy the alias names of the OLD and
3812 : * NEW rows from the ModifyTable plan node.
3813 : *
3814 : * Once this function has been called, deparse_expression() can be called on
3815 : * subsidiary expression(s) of the specified Plan node. To deparse
3816 : * expressions of a different Plan node in the same Plan tree, re-call this
3817 : * function to identify the new parent Plan node.
3818 : *
3819 : * The result is the same List passed in; this is a notational convenience.
3820 : */
3821 : List *
3822 49728 : set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
3823 : {
3824 : deparse_namespace *dpns;
3825 :
3826 : /* Should always have one-entry namespace list for Plan deparsing */
3827 : Assert(list_length(dpcontext) == 1);
3828 49728 : dpns = (deparse_namespace *) linitial(dpcontext);
3829 :
3830 : /* Set our attention on the specific plan node passed in */
3831 49728 : dpns->ancestors = ancestors;
3832 49728 : set_deparse_plan(dpns, plan);
3833 :
3834 : /* For ModifyTable, set aliases for OLD and NEW in RETURNING */
3835 49728 : if (IsA(plan, ModifyTable))
3836 : {
3837 192 : dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias;
3838 192 : dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias;
3839 : }
3840 :
3841 49728 : return dpcontext;
3842 : }
3843 :
3844 : /*
3845 : * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
3846 : *
3847 : * Determine the relation aliases we'll use during an EXPLAIN operation.
3848 : * This is just a frontend to set_rtable_names. We have to expose the aliases
3849 : * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
3850 : */
3851 : List *
3852 22682 : select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
3853 : {
3854 : deparse_namespace dpns;
3855 :
3856 22682 : memset(&dpns, 0, sizeof(dpns));
3857 22682 : dpns.rtable = rtable;
3858 22682 : dpns.subplans = NIL;
3859 22682 : dpns.ctes = NIL;
3860 22682 : dpns.appendrels = NULL;
3861 22682 : set_rtable_names(&dpns, NIL, rels_used);
3862 : /* We needn't bother computing column aliases yet */
3863 :
3864 22682 : return dpns.rtable_names;
3865 : }
3866 :
3867 : /*
3868 : * set_rtable_names: select RTE aliases to be used in printing a query
3869 : *
3870 : * We fill in dpns->rtable_names with a list of names that is one-for-one with
3871 : * the already-filled dpns->rtable list. Each RTE name is unique among those
3872 : * in the new namespace plus any ancestor namespaces listed in
3873 : * parent_namespaces.
3874 : *
3875 : * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
3876 : *
3877 : * Note that this function is only concerned with relation names, not column
3878 : * names.
3879 : */
3880 : static void
3881 49862 : set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
3882 : Bitmapset *rels_used)
3883 : {
3884 : HASHCTL hash_ctl;
3885 : HTAB *names_hash;
3886 : NameHashEntry *hentry;
3887 : bool found;
3888 : int rtindex;
3889 : ListCell *lc;
3890 :
3891 49862 : dpns->rtable_names = NIL;
3892 : /* nothing more to do if empty rtable */
3893 49862 : if (dpns->rtable == NIL)
3894 524 : return;
3895 :
3896 : /*
3897 : * We use a hash table to hold known names, so that this process is O(N)
3898 : * not O(N^2) for N names.
3899 : */
3900 49338 : hash_ctl.keysize = NAMEDATALEN;
3901 49338 : hash_ctl.entrysize = sizeof(NameHashEntry);
3902 49338 : hash_ctl.hcxt = CurrentMemoryContext;
3903 49338 : names_hash = hash_create("set_rtable_names names",
3904 49338 : list_length(dpns->rtable),
3905 : &hash_ctl,
3906 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
3907 :
3908 : /* Preload the hash table with names appearing in parent_namespaces */
3909 51076 : foreach(lc, parent_namespaces)
3910 : {
3911 1738 : deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
3912 : ListCell *lc2;
3913 :
3914 6300 : foreach(lc2, olddpns->rtable_names)
3915 : {
3916 4562 : char *oldname = (char *) lfirst(lc2);
3917 :
3918 4562 : if (oldname == NULL)
3919 336 : continue;
3920 4226 : hentry = (NameHashEntry *) hash_search(names_hash,
3921 : oldname,
3922 : HASH_ENTER,
3923 : &found);
3924 : /* we do not complain about duplicate names in parent namespaces */
3925 4226 : hentry->counter = 0;
3926 : }
3927 : }
3928 :
3929 : /* Now we can scan the rtable */
3930 49338 : rtindex = 1;
3931 142014 : foreach(lc, dpns->rtable)
3932 : {
3933 92676 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
3934 : char *refname;
3935 :
3936 : /* Just in case this takes an unreasonable amount of time ... */
3937 92676 : CHECK_FOR_INTERRUPTS();
3938 :
3939 92676 : if (rels_used && !bms_is_member(rtindex, rels_used))
3940 : {
3941 : /* Ignore unreferenced RTE */
3942 16158 : refname = NULL;
3943 : }
3944 76518 : else if (rte->alias)
3945 : {
3946 : /* If RTE has a user-defined alias, prefer that */
3947 50682 : refname = rte->alias->aliasname;
3948 : }
3949 25836 : else if (rte->rtekind == RTE_RELATION)
3950 : {
3951 : /* Use the current actual name of the relation */
3952 21284 : refname = get_rel_name(rte->relid);
3953 : }
3954 4552 : else if (rte->rtekind == RTE_JOIN)
3955 : {
3956 : /* Unnamed join has no refname */
3957 1818 : refname = NULL;
3958 : }
3959 : else
3960 : {
3961 : /* Otherwise use whatever the parser assigned */
3962 2734 : refname = rte->eref->aliasname;
3963 : }
3964 :
3965 : /*
3966 : * If the selected name isn't unique, append digits to make it so, and
3967 : * make a new hash entry for it once we've got a unique name. For a
3968 : * very long input name, we might have to truncate to stay within
3969 : * NAMEDATALEN.
3970 : */
3971 92676 : if (refname)
3972 : {
3973 74700 : hentry = (NameHashEntry *) hash_search(names_hash,
3974 : refname,
3975 : HASH_ENTER,
3976 : &found);
3977 74700 : if (found)
3978 : {
3979 : /* Name already in use, must choose a new one */
3980 13484 : int refnamelen = strlen(refname);
3981 13484 : char *modname = (char *) palloc(refnamelen + 16);
3982 : NameHashEntry *hentry2;
3983 :
3984 : do
3985 : {
3986 13490 : hentry->counter++;
3987 : for (;;)
3988 : {
3989 13502 : memcpy(modname, refname, refnamelen);
3990 13502 : sprintf(modname + refnamelen, "_%d", hentry->counter);
3991 13502 : if (strlen(modname) < NAMEDATALEN)
3992 13490 : break;
3993 : /* drop chars from refname to keep all the digits */
3994 12 : refnamelen = pg_mbcliplen(refname, refnamelen,
3995 : refnamelen - 1);
3996 : }
3997 13490 : hentry2 = (NameHashEntry *) hash_search(names_hash,
3998 : modname,
3999 : HASH_ENTER,
4000 : &found);
4001 13490 : } while (found);
4002 13484 : hentry2->counter = 0; /* init new hash entry */
4003 13484 : refname = modname;
4004 : }
4005 : else
4006 : {
4007 : /* Name not previously used, need only initialize hentry */
4008 61216 : hentry->counter = 0;
4009 : }
4010 : }
4011 :
4012 92676 : dpns->rtable_names = lappend(dpns->rtable_names, refname);
4013 92676 : rtindex++;
4014 : }
4015 :
4016 49338 : hash_destroy(names_hash);
4017 : }
4018 :
4019 : /*
4020 : * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
4021 : *
4022 : * For convenience, this is defined to initialize the deparse_namespace struct
4023 : * from scratch.
4024 : */
4025 : static void
4026 5754 : set_deparse_for_query(deparse_namespace *dpns, Query *query,
4027 : List *parent_namespaces)
4028 : {
4029 : ListCell *lc;
4030 : ListCell *lc2;
4031 :
4032 : /* Initialize *dpns and fill rtable/ctes links */
4033 5754 : memset(dpns, 0, sizeof(deparse_namespace));
4034 5754 : dpns->rtable = query->rtable;
4035 5754 : dpns->subplans = NIL;
4036 5754 : dpns->ctes = query->cteList;
4037 5754 : dpns->appendrels = NULL;
4038 5754 : dpns->ret_old_alias = query->returningOldAlias;
4039 5754 : dpns->ret_new_alias = query->returningNewAlias;
4040 :
4041 : /* Assign a unique relation alias to each RTE */
4042 5754 : set_rtable_names(dpns, parent_namespaces, NULL);
4043 :
4044 : /* Initialize dpns->rtable_columns to contain zeroed structs */
4045 5754 : dpns->rtable_columns = NIL;
4046 16200 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4047 10446 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4048 : palloc0(sizeof(deparse_columns)));
4049 :
4050 : /* If it's a utility query, it won't have a jointree */
4051 5754 : if (query->jointree)
4052 : {
4053 : /* Detect whether global uniqueness of USING names is needed */
4054 5738 : dpns->unique_using =
4055 5738 : has_dangerous_join_using(dpns, (Node *) query->jointree);
4056 :
4057 : /*
4058 : * Select names for columns merged by USING, via a recursive pass over
4059 : * the query jointree.
4060 : */
4061 5738 : set_using_names(dpns, (Node *) query->jointree, NIL);
4062 : }
4063 :
4064 : /*
4065 : * Now assign remaining column aliases for each RTE. We do this in a
4066 : * linear scan of the rtable, so as to process RTEs whether or not they
4067 : * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4068 : * etc). JOIN RTEs must be processed after their children, but this is
4069 : * okay because they appear later in the rtable list than their children
4070 : * (cf Asserts in identify_join_columns()).
4071 : */
4072 16200 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4073 : {
4074 10446 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4075 10446 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4076 :
4077 10446 : if (rte->rtekind == RTE_JOIN)
4078 1498 : set_join_column_names(dpns, rte, colinfo);
4079 : else
4080 8948 : set_relation_column_names(dpns, rte, colinfo);
4081 : }
4082 5754 : }
4083 :
4084 : /*
4085 : * set_simple_column_names: fill in column aliases for non-query situations
4086 : *
4087 : * This handles EXPLAIN and cases where we only have relation RTEs. Without
4088 : * a join tree, we can't do anything smart about join RTEs, but we don't
4089 : * need to, because EXPLAIN should never see join alias Vars anyway.
4090 : * If we find a join RTE we'll just skip it, leaving its deparse_columns
4091 : * struct all-zero. If somehow we try to deparse a join alias Var, we'll
4092 : * error out cleanly because the struct's num_cols will be zero.
4093 : */
4094 : static void
4095 44108 : set_simple_column_names(deparse_namespace *dpns)
4096 : {
4097 : ListCell *lc;
4098 : ListCell *lc2;
4099 :
4100 : /* Initialize dpns->rtable_columns to contain zeroed structs */
4101 44108 : dpns->rtable_columns = NIL;
4102 126338 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4103 82230 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4104 : palloc0(sizeof(deparse_columns)));
4105 :
4106 : /* Assign unique column aliases within each non-join RTE */
4107 126338 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4108 : {
4109 82230 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4110 82230 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4111 :
4112 82230 : if (rte->rtekind != RTE_JOIN)
4113 77100 : set_relation_column_names(dpns, rte, colinfo);
4114 : }
4115 44108 : }
4116 :
4117 : /*
4118 : * has_dangerous_join_using: search jointree for unnamed JOIN USING
4119 : *
4120 : * Merged columns of a JOIN USING may act differently from either of the input
4121 : * columns, either because they are merged with COALESCE (in a FULL JOIN) or
4122 : * because an implicit coercion of the underlying input column is required.
4123 : * In such a case the column must be referenced as a column of the JOIN not as
4124 : * a column of either input. And this is problematic if the join is unnamed
4125 : * (alias-less): we cannot qualify the column's name with an RTE name, since
4126 : * there is none. (Forcibly assigning an alias to the join is not a solution,
4127 : * since that will prevent legal references to tables below the join.)
4128 : * To ensure that every column in the query is unambiguously referenceable,
4129 : * we must assign such merged columns names that are globally unique across
4130 : * the whole query, aliasing other columns out of the way as necessary.
4131 : *
4132 : * Because the ensuing re-aliasing is fairly damaging to the readability of
4133 : * the query, we don't do this unless we have to. So, we must pre-scan
4134 : * the join tree to see if we have to, before starting set_using_names().
4135 : */
4136 : static bool
4137 13658 : has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
4138 : {
4139 13658 : if (IsA(jtnode, RangeTblRef))
4140 : {
4141 : /* nothing to do here */
4142 : }
4143 7170 : else if (IsA(jtnode, FromExpr))
4144 : {
4145 5738 : FromExpr *f = (FromExpr *) jtnode;
4146 : ListCell *lc;
4147 :
4148 10866 : foreach(lc, f->fromlist)
4149 : {
4150 5200 : if (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))
4151 72 : return true;
4152 : }
4153 : }
4154 1432 : else if (IsA(jtnode, JoinExpr))
4155 : {
4156 1432 : JoinExpr *j = (JoinExpr *) jtnode;
4157 :
4158 : /* Is it an unnamed JOIN with USING? */
4159 1432 : if (j->alias == NULL && j->usingClause)
4160 : {
4161 : /*
4162 : * Yes, so check each join alias var to see if any of them are not
4163 : * simple references to underlying columns. If so, we have a
4164 : * dangerous situation and must pick unique aliases.
4165 : */
4166 286 : RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4167 :
4168 : /* We need only examine the merged columns */
4169 596 : for (int i = 0; i < jrte->joinmergedcols; i++)
4170 : {
4171 382 : Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4172 :
4173 382 : if (!IsA(aliasvar, Var))
4174 72 : return true;
4175 : }
4176 : }
4177 :
4178 : /* Nope, but inspect children */
4179 1360 : if (has_dangerous_join_using(dpns, j->larg))
4180 0 : return true;
4181 1360 : if (has_dangerous_join_using(dpns, j->rarg))
4182 0 : return true;
4183 : }
4184 : else
4185 0 : elog(ERROR, "unrecognized node type: %d",
4186 : (int) nodeTag(jtnode));
4187 13514 : return false;
4188 : }
4189 :
4190 : /*
4191 : * set_using_names: select column aliases to be used for merged USING columns
4192 : *
4193 : * We do this during a recursive descent of the query jointree.
4194 : * dpns->unique_using must already be set to determine the global strategy.
4195 : *
4196 : * Column alias info is saved in the dpns->rtable_columns list, which is
4197 : * assumed to be filled with pre-zeroed deparse_columns structs.
4198 : *
4199 : * parentUsing is a list of all USING aliases assigned in parent joins of
4200 : * the current jointree node. (The passed-in list must not be modified.)
4201 : *
4202 : * Note that we do not use per-deparse_columns hash tables in this function.
4203 : * The number of names that need to be assigned should be small enough that
4204 : * we don't need to trouble with that.
4205 : */
4206 : static void
4207 13976 : set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
4208 : {
4209 13976 : if (IsA(jtnode, RangeTblRef))
4210 : {
4211 : /* nothing to do now */
4212 : }
4213 7236 : else if (IsA(jtnode, FromExpr))
4214 : {
4215 5738 : FromExpr *f = (FromExpr *) jtnode;
4216 : ListCell *lc;
4217 :
4218 10980 : foreach(lc, f->fromlist)
4219 5242 : set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4220 : }
4221 1498 : else if (IsA(jtnode, JoinExpr))
4222 : {
4223 1498 : JoinExpr *j = (JoinExpr *) jtnode;
4224 1498 : RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4225 1498 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
4226 : int *leftattnos;
4227 : int *rightattnos;
4228 : deparse_columns *leftcolinfo;
4229 : deparse_columns *rightcolinfo;
4230 : int i;
4231 : ListCell *lc;
4232 :
4233 : /* Get info about the shape of the join */
4234 1498 : identify_join_columns(j, rte, colinfo);
4235 1498 : leftattnos = colinfo->leftattnos;
4236 1498 : rightattnos = colinfo->rightattnos;
4237 :
4238 : /* Look up the not-yet-filled-in child deparse_columns structs */
4239 1498 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4240 1498 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4241 :
4242 : /*
4243 : * If this join is unnamed, then we cannot substitute new aliases at
4244 : * this level, so any name requirements pushed down to here must be
4245 : * pushed down again to the children.
4246 : */
4247 1498 : if (rte->alias == NULL)
4248 : {
4249 1528 : for (i = 0; i < colinfo->num_cols; i++)
4250 : {
4251 138 : char *colname = colinfo->colnames[i];
4252 :
4253 138 : if (colname == NULL)
4254 24 : continue;
4255 :
4256 : /* Push down to left column, unless it's a system column */
4257 114 : if (leftattnos[i] > 0)
4258 : {
4259 102 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4260 102 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4261 : }
4262 :
4263 : /* Same on the righthand side */
4264 114 : if (rightattnos[i] > 0)
4265 : {
4266 114 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4267 114 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4268 : }
4269 : }
4270 : }
4271 :
4272 : /*
4273 : * If there's a USING clause, select the USING column names and push
4274 : * those names down to the children. We have two strategies:
4275 : *
4276 : * If dpns->unique_using is true, we force all USING names to be
4277 : * unique across the whole query level. In principle we'd only need
4278 : * the names of dangerous USING columns to be globally unique, but to
4279 : * safely assign all USING names in a single pass, we have to enforce
4280 : * the same uniqueness rule for all of them. However, if a USING
4281 : * column's name has been pushed down from the parent, we should use
4282 : * it as-is rather than making a uniqueness adjustment. This is
4283 : * necessary when we're at an unnamed join, and it creates no risk of
4284 : * ambiguity. Also, if there's a user-written output alias for a
4285 : * merged column, we prefer to use that rather than the input name;
4286 : * this simplifies the logic and seems likely to lead to less aliasing
4287 : * overall.
4288 : *
4289 : * If dpns->unique_using is false, we only need USING names to be
4290 : * unique within their own join RTE. We still need to honor
4291 : * pushed-down names, though.
4292 : *
4293 : * Though significantly different in results, these two strategies are
4294 : * implemented by the same code, with only the difference of whether
4295 : * to put assigned names into dpns->using_names.
4296 : */
4297 1498 : if (j->usingClause)
4298 : {
4299 : /* Copy the input parentUsing list so we don't modify it */
4300 424 : parentUsing = list_copy(parentUsing);
4301 :
4302 : /* USING names must correspond to the first join output columns */
4303 424 : expand_colnames_array_to(colinfo, list_length(j->usingClause));
4304 424 : i = 0;
4305 1004 : foreach(lc, j->usingClause)
4306 : {
4307 580 : char *colname = strVal(lfirst(lc));
4308 :
4309 : /* Assert it's a merged column */
4310 : Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4311 :
4312 : /* Adopt passed-down name if any, else select unique name */
4313 580 : if (colinfo->colnames[i] != NULL)
4314 102 : colname = colinfo->colnames[i];
4315 : else
4316 : {
4317 : /* Prefer user-written output alias if any */
4318 478 : if (rte->alias && i < list_length(rte->alias->colnames))
4319 0 : colname = strVal(list_nth(rte->alias->colnames, i));
4320 : /* Make it appropriately unique */
4321 478 : colname = make_colname_unique(colname, dpns, colinfo);
4322 478 : if (dpns->unique_using)
4323 126 : dpns->using_names = lappend(dpns->using_names,
4324 : colname);
4325 : /* Save it as output column name, too */
4326 478 : colinfo->colnames[i] = colname;
4327 : }
4328 :
4329 : /* Remember selected names for use later */
4330 580 : colinfo->usingNames = lappend(colinfo->usingNames, colname);
4331 580 : parentUsing = lappend(parentUsing, colname);
4332 :
4333 : /* Push down to left column, unless it's a system column */
4334 580 : if (leftattnos[i] > 0)
4335 : {
4336 580 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4337 580 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4338 : }
4339 :
4340 : /* Same on the righthand side */
4341 580 : if (rightattnos[i] > 0)
4342 : {
4343 580 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4344 580 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4345 : }
4346 :
4347 580 : i++;
4348 : }
4349 : }
4350 :
4351 : /* Mark child deparse_columns structs with correct parentUsing info */
4352 1498 : leftcolinfo->parentUsing = parentUsing;
4353 1498 : rightcolinfo->parentUsing = parentUsing;
4354 :
4355 : /* Now recursively assign USING column names in children */
4356 1498 : set_using_names(dpns, j->larg, parentUsing);
4357 1498 : set_using_names(dpns, j->rarg, parentUsing);
4358 : }
4359 : else
4360 0 : elog(ERROR, "unrecognized node type: %d",
4361 : (int) nodeTag(jtnode));
4362 13976 : }
4363 :
4364 : /*
4365 : * set_relation_column_names: select column aliases for a non-join RTE
4366 : *
4367 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4368 : * If any colnames entries are already filled in, those override local
4369 : * choices.
4370 : */
4371 : static void
4372 86048 : set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4373 : deparse_columns *colinfo)
4374 : {
4375 : int ncolumns;
4376 : char **real_colnames;
4377 : bool changed_any;
4378 : int noldcolumns;
4379 : int i;
4380 : int j;
4381 :
4382 : /*
4383 : * Construct an array of the current "real" column names of the RTE.
4384 : * real_colnames[] will be indexed by physical column number, with NULL
4385 : * entries for dropped columns.
4386 : */
4387 86048 : if (rte->rtekind == RTE_RELATION)
4388 : {
4389 : /* Relation --- look to the system catalogs for up-to-date info */
4390 : Relation rel;
4391 : TupleDesc tupdesc;
4392 :
4393 73188 : rel = relation_open(rte->relid, AccessShareLock);
4394 73188 : tupdesc = RelationGetDescr(rel);
4395 :
4396 73188 : ncolumns = tupdesc->natts;
4397 73188 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4398 :
4399 476598 : for (i = 0; i < ncolumns; i++)
4400 : {
4401 403410 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4402 :
4403 403410 : if (attr->attisdropped)
4404 3094 : real_colnames[i] = NULL;
4405 : else
4406 400316 : real_colnames[i] = pstrdup(NameStr(attr->attname));
4407 : }
4408 73188 : relation_close(rel, AccessShareLock);
4409 : }
4410 : else
4411 : {
4412 : /* Otherwise get the column names from eref or expandRTE() */
4413 : List *colnames;
4414 : ListCell *lc;
4415 :
4416 : /*
4417 : * Functions returning composites have the annoying property that some
4418 : * of the composite type's columns might have been dropped since the
4419 : * query was parsed. If possible, use expandRTE() to handle that
4420 : * case, since it has the tedious logic needed to find out about
4421 : * dropped columns. However, if we're explaining a plan, then we
4422 : * don't have rte->functions because the planner thinks that won't be
4423 : * needed later, and that breaks expandRTE(). So in that case we have
4424 : * to rely on rte->eref, which may lead us to report a dropped
4425 : * column's old name; that seems close enough for EXPLAIN's purposes.
4426 : *
4427 : * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4428 : * which should be sufficiently up-to-date: no other RTE types can
4429 : * have columns get dropped from under them after parsing.
4430 : */
4431 12860 : if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4432 : {
4433 : /* Since we're not creating Vars, rtindex etc. don't matter */
4434 810 : expandRTE(rte, 1, 0, VAR_RETURNING_DEFAULT, -1,
4435 : true /* include dropped */ , &colnames, NULL);
4436 : }
4437 : else
4438 12050 : colnames = rte->eref->colnames;
4439 :
4440 12860 : ncolumns = list_length(colnames);
4441 12860 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4442 :
4443 12860 : i = 0;
4444 43358 : foreach(lc, colnames)
4445 : {
4446 : /*
4447 : * If the column name we find here is an empty string, then it's a
4448 : * dropped column, so change to NULL.
4449 : */
4450 30498 : char *cname = strVal(lfirst(lc));
4451 :
4452 30498 : if (cname[0] == '\0')
4453 54 : cname = NULL;
4454 30498 : real_colnames[i] = cname;
4455 30498 : i++;
4456 : }
4457 : }
4458 :
4459 : /*
4460 : * Ensure colinfo->colnames has a slot for each column. (It could be long
4461 : * enough already, if we pushed down a name for the last column.) Note:
4462 : * it's possible that there are now more columns than there were when the
4463 : * query was parsed, ie colnames could be longer than rte->eref->colnames.
4464 : * We must assign unique aliases to the new columns too, else there could
4465 : * be unresolved conflicts when the view/rule is reloaded.
4466 : */
4467 86048 : expand_colnames_array_to(colinfo, ncolumns);
4468 : Assert(colinfo->num_cols == ncolumns);
4469 :
4470 : /*
4471 : * Make sufficiently large new_colnames and is_new_col arrays, too.
4472 : *
4473 : * Note: because we leave colinfo->num_new_cols zero until after the loop,
4474 : * colname_is_unique will not consult that array, which is fine because it
4475 : * would only be duplicate effort.
4476 : */
4477 86048 : colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4478 86048 : colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4479 :
4480 : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4481 86048 : build_colinfo_names_hash(colinfo);
4482 :
4483 : /*
4484 : * Scan the columns, select a unique alias for each one, and store it in
4485 : * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4486 : * entries for dropped columns, the latter omits them. Also mark
4487 : * new_colnames entries as to whether they are new since parse time; this
4488 : * is the case for entries beyond the length of rte->eref->colnames.
4489 : */
4490 86048 : noldcolumns = list_length(rte->eref->colnames);
4491 86048 : changed_any = false;
4492 86048 : j = 0;
4493 519956 : for (i = 0; i < ncolumns; i++)
4494 : {
4495 433908 : char *real_colname = real_colnames[i];
4496 433908 : char *colname = colinfo->colnames[i];
4497 :
4498 : /* Skip dropped columns */
4499 433908 : if (real_colname == NULL)
4500 : {
4501 : Assert(colname == NULL); /* colnames[i] is already NULL */
4502 3148 : continue;
4503 : }
4504 :
4505 : /* If alias already assigned, that's what to use */
4506 430760 : if (colname == NULL)
4507 : {
4508 : /* If user wrote an alias, prefer that over real column name */
4509 429702 : if (rte->alias && i < list_length(rte->alias->colnames))
4510 41494 : colname = strVal(list_nth(rte->alias->colnames, i));
4511 : else
4512 388208 : colname = real_colname;
4513 :
4514 : /* Unique-ify and insert into colinfo */
4515 429702 : colname = make_colname_unique(colname, dpns, colinfo);
4516 :
4517 429702 : colinfo->colnames[i] = colname;
4518 429702 : add_to_names_hash(colinfo, colname);
4519 : }
4520 :
4521 : /* Put names of non-dropped columns in new_colnames[] too */
4522 430760 : colinfo->new_colnames[j] = colname;
4523 : /* And mark them as new or not */
4524 430760 : colinfo->is_new_col[j] = (i >= noldcolumns);
4525 430760 : j++;
4526 :
4527 : /* Remember if any assigned aliases differ from "real" name */
4528 430760 : if (!changed_any && strcmp(colname, real_colname) != 0)
4529 1156 : changed_any = true;
4530 : }
4531 :
4532 : /* We're now done needing the colinfo's names_hash */
4533 86048 : destroy_colinfo_names_hash(colinfo);
4534 :
4535 : /*
4536 : * Set correct length for new_colnames[] array. (Note: if columns have
4537 : * been added, colinfo->num_cols includes them, which is not really quite
4538 : * right but is harmless, since any new columns must be at the end where
4539 : * they won't affect varattnos of pre-existing columns.)
4540 : */
4541 86048 : colinfo->num_new_cols = j;
4542 :
4543 : /*
4544 : * For a relation RTE, we need only print the alias column names if any
4545 : * are different from the underlying "real" names. For a function RTE,
4546 : * always emit a complete column alias list; this is to protect against
4547 : * possible instability of the default column names (eg, from altering
4548 : * parameter names). For tablefunc RTEs, we never print aliases, because
4549 : * the column names are part of the clause itself. For other RTE types,
4550 : * print if we changed anything OR if there were user-written column
4551 : * aliases (since the latter would be part of the underlying "reality").
4552 : */
4553 86048 : if (rte->rtekind == RTE_RELATION)
4554 73188 : colinfo->printaliases = changed_any;
4555 12860 : else if (rte->rtekind == RTE_FUNCTION)
4556 1356 : colinfo->printaliases = true;
4557 11504 : else if (rte->rtekind == RTE_TABLEFUNC)
4558 176 : colinfo->printaliases = false;
4559 11328 : else if (rte->alias && rte->alias->colnames != NIL)
4560 768 : colinfo->printaliases = true;
4561 : else
4562 10560 : colinfo->printaliases = changed_any;
4563 86048 : }
4564 :
4565 : /*
4566 : * set_join_column_names: select column aliases for a join RTE
4567 : *
4568 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4569 : * If any colnames entries are already filled in, those override local
4570 : * choices. Also, names for USING columns were already chosen by
4571 : * set_using_names(). We further expect that column alias selection has been
4572 : * completed for both input RTEs.
4573 : */
4574 : static void
4575 1498 : set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4576 : deparse_columns *colinfo)
4577 : {
4578 : deparse_columns *leftcolinfo;
4579 : deparse_columns *rightcolinfo;
4580 : bool changed_any;
4581 : int noldcolumns;
4582 : int nnewcolumns;
4583 1498 : Bitmapset *leftmerged = NULL;
4584 1498 : Bitmapset *rightmerged = NULL;
4585 : int i;
4586 : int j;
4587 : int ic;
4588 : int jc;
4589 :
4590 : /* Look up the previously-filled-in child deparse_columns structs */
4591 1498 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4592 1498 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4593 :
4594 : /*
4595 : * Ensure colinfo->colnames has a slot for each column. (It could be long
4596 : * enough already, if we pushed down a name for the last column.) Note:
4597 : * it's possible that one or both inputs now have more columns than there
4598 : * were when the query was parsed, but we'll deal with that below. We
4599 : * only need entries in colnames for pre-existing columns.
4600 : */
4601 1498 : noldcolumns = list_length(rte->eref->colnames);
4602 1498 : expand_colnames_array_to(colinfo, noldcolumns);
4603 : Assert(colinfo->num_cols == noldcolumns);
4604 :
4605 : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4606 1498 : build_colinfo_names_hash(colinfo);
4607 :
4608 : /*
4609 : * Scan the join output columns, select an alias for each one, and store
4610 : * it in colinfo->colnames. If there are USING columns, set_using_names()
4611 : * already selected their names, so we can start the loop at the first
4612 : * non-merged column.
4613 : */
4614 1498 : changed_any = false;
4615 49036 : for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4616 : {
4617 47538 : char *colname = colinfo->colnames[i];
4618 : char *real_colname;
4619 :
4620 : /* Join column must refer to at least one input column */
4621 : Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4622 :
4623 : /* Get the child column name */
4624 47538 : if (colinfo->leftattnos[i] > 0)
4625 33430 : real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4626 14108 : else if (colinfo->rightattnos[i] > 0)
4627 14108 : real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4628 : else
4629 : {
4630 : /* We're joining system columns --- use eref name */
4631 0 : real_colname = strVal(list_nth(rte->eref->colnames, i));
4632 : }
4633 :
4634 : /* If child col has been dropped, no need to assign a join colname */
4635 47538 : if (real_colname == NULL)
4636 : {
4637 6 : colinfo->colnames[i] = NULL;
4638 6 : continue;
4639 : }
4640 :
4641 : /* In an unnamed join, just report child column names as-is */
4642 47532 : if (rte->alias == NULL)
4643 : {
4644 47154 : colinfo->colnames[i] = real_colname;
4645 47154 : add_to_names_hash(colinfo, real_colname);
4646 47154 : continue;
4647 : }
4648 :
4649 : /* If alias already assigned, that's what to use */
4650 378 : if (colname == NULL)
4651 : {
4652 : /* If user wrote an alias, prefer that over real column name */
4653 378 : if (rte->alias && i < list_length(rte->alias->colnames))
4654 96 : colname = strVal(list_nth(rte->alias->colnames, i));
4655 : else
4656 282 : colname = real_colname;
4657 :
4658 : /* Unique-ify and insert into colinfo */
4659 378 : colname = make_colname_unique(colname, dpns, colinfo);
4660 :
4661 378 : colinfo->colnames[i] = colname;
4662 378 : add_to_names_hash(colinfo, colname);
4663 : }
4664 :
4665 : /* Remember if any assigned aliases differ from "real" name */
4666 378 : if (!changed_any && strcmp(colname, real_colname) != 0)
4667 24 : changed_any = true;
4668 : }
4669 :
4670 : /*
4671 : * Calculate number of columns the join would have if it were re-parsed
4672 : * now, and create storage for the new_colnames and is_new_col arrays.
4673 : *
4674 : * Note: colname_is_unique will be consulting new_colnames[] during the
4675 : * loops below, so its not-yet-filled entries must be zeroes.
4676 : */
4677 2996 : nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
4678 1498 : list_length(colinfo->usingNames);
4679 1498 : colinfo->num_new_cols = nnewcolumns;
4680 1498 : colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
4681 1498 : colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
4682 :
4683 : /*
4684 : * Generating the new_colnames array is a bit tricky since any new columns
4685 : * added since parse time must be inserted in the right places. This code
4686 : * must match the parser, which will order a join's columns as merged
4687 : * columns first (in USING-clause order), then non-merged columns from the
4688 : * left input (in attnum order), then non-merged columns from the right
4689 : * input (ditto). If one of the inputs is itself a join, its columns will
4690 : * be ordered according to the same rule, which means newly-added columns
4691 : * might not be at the end. We can figure out what's what by consulting
4692 : * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
4693 : *
4694 : * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
4695 : * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
4696 : * meanings for the current child RTE.
4697 : */
4698 :
4699 : /* Handle merged columns; they are first and can't be new */
4700 1498 : i = j = 0;
4701 2078 : while (i < noldcolumns &&
4702 2078 : colinfo->leftattnos[i] != 0 &&
4703 2078 : colinfo->rightattnos[i] != 0)
4704 : {
4705 : /* column name is already determined and known unique */
4706 580 : colinfo->new_colnames[j] = colinfo->colnames[i];
4707 580 : colinfo->is_new_col[j] = false;
4708 :
4709 : /* build bitmapsets of child attnums of merged columns */
4710 580 : if (colinfo->leftattnos[i] > 0)
4711 580 : leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
4712 580 : if (colinfo->rightattnos[i] > 0)
4713 580 : rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);
4714 :
4715 580 : i++, j++;
4716 : }
4717 :
4718 : /* Handle non-merged left-child columns */
4719 1498 : ic = 0;
4720 35994 : for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
4721 : {
4722 34496 : char *child_colname = leftcolinfo->new_colnames[jc];
4723 :
4724 34496 : if (!leftcolinfo->is_new_col[jc])
4725 : {
4726 : /* Advance ic to next non-dropped old column of left child */
4727 34088 : while (ic < leftcolinfo->num_cols &&
4728 34088 : leftcolinfo->colnames[ic] == NULL)
4729 84 : ic++;
4730 : Assert(ic < leftcolinfo->num_cols);
4731 34004 : ic++;
4732 : /* If it is a merged column, we already processed it */
4733 34004 : if (bms_is_member(ic, leftmerged))
4734 580 : continue;
4735 : /* Else, advance i to the corresponding existing join column */
4736 33430 : while (i < colinfo->num_cols &&
4737 33430 : colinfo->colnames[i] == NULL)
4738 6 : i++;
4739 : Assert(i < colinfo->num_cols);
4740 : Assert(ic == colinfo->leftattnos[i]);
4741 : /* Use the already-assigned name of this column */
4742 33424 : colinfo->new_colnames[j] = colinfo->colnames[i];
4743 33424 : i++;
4744 : }
4745 : else
4746 : {
4747 : /*
4748 : * Unique-ify the new child column name and assign, unless we're
4749 : * in an unnamed join, in which case just copy
4750 : */
4751 492 : if (rte->alias != NULL)
4752 : {
4753 264 : colinfo->new_colnames[j] =
4754 132 : make_colname_unique(child_colname, dpns, colinfo);
4755 132 : if (!changed_any &&
4756 108 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4757 12 : changed_any = true;
4758 : }
4759 : else
4760 360 : colinfo->new_colnames[j] = child_colname;
4761 492 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
4762 : }
4763 :
4764 33916 : colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
4765 33916 : j++;
4766 : }
4767 :
4768 : /* Handle non-merged right-child columns in exactly the same way */
4769 1498 : ic = 0;
4770 16354 : for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
4771 : {
4772 14856 : char *child_colname = rightcolinfo->new_colnames[jc];
4773 :
4774 14856 : if (!rightcolinfo->is_new_col[jc])
4775 : {
4776 : /* Advance ic to next non-dropped old column of right child */
4777 14688 : while (ic < rightcolinfo->num_cols &&
4778 14688 : rightcolinfo->colnames[ic] == NULL)
4779 0 : ic++;
4780 : Assert(ic < rightcolinfo->num_cols);
4781 14688 : ic++;
4782 : /* If it is a merged column, we already processed it */
4783 14688 : if (bms_is_member(ic, rightmerged))
4784 580 : continue;
4785 : /* Else, advance i to the corresponding existing join column */
4786 14108 : while (i < colinfo->num_cols &&
4787 14108 : colinfo->colnames[i] == NULL)
4788 0 : i++;
4789 : Assert(i < colinfo->num_cols);
4790 : Assert(ic == colinfo->rightattnos[i]);
4791 : /* Use the already-assigned name of this column */
4792 14108 : colinfo->new_colnames[j] = colinfo->colnames[i];
4793 14108 : i++;
4794 : }
4795 : else
4796 : {
4797 : /*
4798 : * Unique-ify the new child column name and assign, unless we're
4799 : * in an unnamed join, in which case just copy
4800 : */
4801 168 : if (rte->alias != NULL)
4802 : {
4803 48 : colinfo->new_colnames[j] =
4804 24 : make_colname_unique(child_colname, dpns, colinfo);
4805 24 : if (!changed_any &&
4806 24 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4807 12 : changed_any = true;
4808 : }
4809 : else
4810 144 : colinfo->new_colnames[j] = child_colname;
4811 168 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
4812 : }
4813 :
4814 14276 : colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
4815 14276 : j++;
4816 : }
4817 :
4818 : /* Assert we processed the right number of columns */
4819 : #ifdef USE_ASSERT_CHECKING
4820 : while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
4821 : i++;
4822 : Assert(i == colinfo->num_cols);
4823 : Assert(j == nnewcolumns);
4824 : #endif
4825 :
4826 : /* We're now done needing the colinfo's names_hash */
4827 1498 : destroy_colinfo_names_hash(colinfo);
4828 :
4829 : /*
4830 : * For a named join, print column aliases if we changed any from the child
4831 : * names. Unnamed joins cannot print aliases.
4832 : */
4833 1498 : if (rte->alias != NULL)
4834 108 : colinfo->printaliases = changed_any;
4835 : else
4836 1390 : colinfo->printaliases = false;
4837 1498 : }
4838 :
4839 : /*
4840 : * colname_is_unique: is colname distinct from already-chosen column names?
4841 : *
4842 : * dpns is query-wide info, colinfo is for the column's RTE
4843 : */
4844 : static bool
4845 433028 : colname_is_unique(const char *colname, deparse_namespace *dpns,
4846 : deparse_columns *colinfo)
4847 : {
4848 : int i;
4849 : ListCell *lc;
4850 :
4851 : /*
4852 : * If we have a hash table, consult that instead of linearly scanning the
4853 : * colinfo's strings.
4854 : */
4855 433028 : if (colinfo->names_hash)
4856 : {
4857 17248 : if (hash_search(colinfo->names_hash,
4858 : colname,
4859 : HASH_FIND,
4860 : NULL) != NULL)
4861 0 : return false;
4862 : }
4863 : else
4864 : {
4865 : /* Check against already-assigned column aliases within RTE */
4866 5878856 : for (i = 0; i < colinfo->num_cols; i++)
4867 : {
4868 5465318 : char *oldname = colinfo->colnames[i];
4869 :
4870 5465318 : if (oldname && strcmp(oldname, colname) == 0)
4871 2242 : return false;
4872 : }
4873 :
4874 : /*
4875 : * If we're building a new_colnames array, check that too (this will
4876 : * be partially but not completely redundant with the previous checks)
4877 : */
4878 414810 : for (i = 0; i < colinfo->num_new_cols; i++)
4879 : {
4880 1296 : char *oldname = colinfo->new_colnames[i];
4881 :
4882 1296 : if (oldname && strcmp(oldname, colname) == 0)
4883 24 : return false;
4884 : }
4885 :
4886 : /*
4887 : * Also check against names already assigned for parent-join USING
4888 : * cols
4889 : */
4890 416106 : foreach(lc, colinfo->parentUsing)
4891 : {
4892 2598 : char *oldname = (char *) lfirst(lc);
4893 :
4894 2598 : if (strcmp(oldname, colname) == 0)
4895 6 : return false;
4896 : }
4897 : }
4898 :
4899 : /*
4900 : * Also check against USING-column names that must be globally unique.
4901 : * These are not hashed, but there should be few of them.
4902 : */
4903 431596 : foreach(lc, dpns->using_names)
4904 : {
4905 882 : char *oldname = (char *) lfirst(lc);
4906 :
4907 882 : if (strcmp(oldname, colname) == 0)
4908 42 : return false;
4909 : }
4910 :
4911 430714 : return true;
4912 : }
4913 :
4914 : /*
4915 : * make_colname_unique: modify colname if necessary to make it unique
4916 : *
4917 : * dpns is query-wide info, colinfo is for the column's RTE
4918 : */
4919 : static char *
4920 430714 : make_colname_unique(char *colname, deparse_namespace *dpns,
4921 : deparse_columns *colinfo)
4922 : {
4923 : /*
4924 : * If the selected name isn't unique, append digits to make it so. For a
4925 : * very long input name, we might have to truncate to stay within
4926 : * NAMEDATALEN.
4927 : */
4928 430714 : if (!colname_is_unique(colname, dpns, colinfo))
4929 : {
4930 1602 : int colnamelen = strlen(colname);
4931 1602 : char *modname = (char *) palloc(colnamelen + 16);
4932 1602 : int i = 0;
4933 :
4934 : do
4935 : {
4936 2314 : i++;
4937 : for (;;)
4938 : {
4939 2314 : memcpy(modname, colname, colnamelen);
4940 2314 : sprintf(modname + colnamelen, "_%d", i);
4941 2314 : if (strlen(modname) < NAMEDATALEN)
4942 2314 : break;
4943 : /* drop chars from colname to keep all the digits */
4944 0 : colnamelen = pg_mbcliplen(colname, colnamelen,
4945 : colnamelen - 1);
4946 : }
4947 2314 : } while (!colname_is_unique(modname, dpns, colinfo));
4948 1602 : colname = modname;
4949 : }
4950 430714 : return colname;
4951 : }
4952 :
4953 : /*
4954 : * expand_colnames_array_to: make colinfo->colnames at least n items long
4955 : *
4956 : * Any added array entries are initialized to zero.
4957 : */
4958 : static void
4959 89346 : expand_colnames_array_to(deparse_columns *colinfo, int n)
4960 : {
4961 89346 : if (n > colinfo->num_cols)
4962 : {
4963 86920 : if (colinfo->colnames == NULL)
4964 85504 : colinfo->colnames = palloc0_array(char *, n);
4965 : else
4966 1416 : colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
4967 86920 : colinfo->num_cols = n;
4968 : }
4969 89346 : }
4970 :
4971 : /*
4972 : * build_colinfo_names_hash: optionally construct a hash table for colinfo
4973 : */
4974 : static void
4975 87546 : build_colinfo_names_hash(deparse_columns *colinfo)
4976 : {
4977 : HASHCTL hash_ctl;
4978 : int i;
4979 : ListCell *lc;
4980 :
4981 : /*
4982 : * Use a hash table only for RTEs with at least 32 columns. (The cutoff
4983 : * is somewhat arbitrary, but let's choose it so that this code does get
4984 : * exercised in the regression tests.)
4985 : */
4986 87546 : if (colinfo->num_cols < 32)
4987 86228 : return;
4988 :
4989 : /*
4990 : * Set up the hash table. The entries are just strings with no other
4991 : * payload.
4992 : */
4993 1318 : hash_ctl.keysize = NAMEDATALEN;
4994 1318 : hash_ctl.entrysize = NAMEDATALEN;
4995 1318 : hash_ctl.hcxt = CurrentMemoryContext;
4996 2636 : colinfo->names_hash = hash_create("deparse_columns names",
4997 1318 : colinfo->num_cols + colinfo->num_new_cols,
4998 : &hash_ctl,
4999 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
5000 :
5001 : /*
5002 : * Preload the hash table with any names already present (these would have
5003 : * come from set_using_names).
5004 : */
5005 61022 : for (i = 0; i < colinfo->num_cols; i++)
5006 : {
5007 59704 : char *oldname = colinfo->colnames[i];
5008 :
5009 59704 : if (oldname)
5010 0 : add_to_names_hash(colinfo, oldname);
5011 : }
5012 :
5013 1318 : for (i = 0; i < colinfo->num_new_cols; i++)
5014 : {
5015 0 : char *oldname = colinfo->new_colnames[i];
5016 :
5017 0 : if (oldname)
5018 0 : add_to_names_hash(colinfo, oldname);
5019 : }
5020 :
5021 1318 : foreach(lc, colinfo->parentUsing)
5022 : {
5023 0 : char *oldname = (char *) lfirst(lc);
5024 :
5025 0 : add_to_names_hash(colinfo, oldname);
5026 : }
5027 : }
5028 :
5029 : /*
5030 : * add_to_names_hash: add a string to the names_hash, if we're using one
5031 : */
5032 : static void
5033 477894 : add_to_names_hash(deparse_columns *colinfo, const char *name)
5034 : {
5035 477894 : if (colinfo->names_hash)
5036 59704 : (void) hash_search(colinfo->names_hash,
5037 : name,
5038 : HASH_ENTER,
5039 : NULL);
5040 477894 : }
5041 :
5042 : /*
5043 : * destroy_colinfo_names_hash: destroy hash table when done with it
5044 : */
5045 : static void
5046 87546 : destroy_colinfo_names_hash(deparse_columns *colinfo)
5047 : {
5048 87546 : if (colinfo->names_hash)
5049 : {
5050 1318 : hash_destroy(colinfo->names_hash);
5051 1318 : colinfo->names_hash = NULL;
5052 : }
5053 87546 : }
5054 :
5055 : /*
5056 : * identify_join_columns: figure out where columns of a join come from
5057 : *
5058 : * Fills the join-specific fields of the colinfo struct, except for
5059 : * usingNames which is filled later.
5060 : */
5061 : static void
5062 1498 : identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
5063 : deparse_columns *colinfo)
5064 : {
5065 : int numjoincols;
5066 : int jcolno;
5067 : int rcolno;
5068 : ListCell *lc;
5069 :
5070 : /* Extract left/right child RT indexes */
5071 1498 : if (IsA(j->larg, RangeTblRef))
5072 952 : colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
5073 546 : else if (IsA(j->larg, JoinExpr))
5074 546 : colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
5075 : else
5076 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5077 : (int) nodeTag(j->larg));
5078 1498 : if (IsA(j->rarg, RangeTblRef))
5079 1498 : colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
5080 0 : else if (IsA(j->rarg, JoinExpr))
5081 0 : colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
5082 : else
5083 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5084 : (int) nodeTag(j->rarg));
5085 :
5086 : /* Assert children will be processed earlier than join in second pass */
5087 : Assert(colinfo->leftrti < j->rtindex);
5088 : Assert(colinfo->rightrti < j->rtindex);
5089 :
5090 : /* Initialize result arrays with zeroes */
5091 1498 : numjoincols = list_length(jrte->joinaliasvars);
5092 : Assert(numjoincols == list_length(jrte->eref->colnames));
5093 1498 : colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
5094 1498 : colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
5095 :
5096 : /*
5097 : * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
5098 : * Recall that the column(s) merged due to USING are the first column(s)
5099 : * of the join output. We need not do anything special while scanning
5100 : * joinleftcols, but while scanning joinrightcols we must distinguish
5101 : * merged from unmerged columns.
5102 : */
5103 1498 : jcolno = 0;
5104 35508 : foreach(lc, jrte->joinleftcols)
5105 : {
5106 34010 : int leftattno = lfirst_int(lc);
5107 :
5108 34010 : colinfo->leftattnos[jcolno++] = leftattno;
5109 : }
5110 1498 : rcolno = 0;
5111 16186 : foreach(lc, jrte->joinrightcols)
5112 : {
5113 14688 : int rightattno = lfirst_int(lc);
5114 :
5115 14688 : if (rcolno < jrte->joinmergedcols) /* merged column? */
5116 580 : colinfo->rightattnos[rcolno] = rightattno;
5117 : else
5118 14108 : colinfo->rightattnos[jcolno++] = rightattno;
5119 14688 : rcolno++;
5120 : }
5121 : Assert(jcolno == numjoincols);
5122 1498 : }
5123 :
5124 : /*
5125 : * get_rtable_name: convenience function to get a previously assigned RTE alias
5126 : *
5127 : * The RTE must belong to the topmost namespace level in "context".
5128 : */
5129 : static char *
5130 6586 : get_rtable_name(int rtindex, deparse_context *context)
5131 : {
5132 6586 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
5133 :
5134 : Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
5135 6586 : return (char *) list_nth(dpns->rtable_names, rtindex - 1);
5136 : }
5137 :
5138 : /*
5139 : * set_deparse_plan: set up deparse_namespace to parse subexpressions
5140 : * of a given Plan node
5141 : *
5142 : * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
5143 : * and index_tlist fields. Caller must already have adjusted the ancestors
5144 : * list if necessary. Note that the rtable, subplans, and ctes fields do
5145 : * not need to change when shifting attention to different plan nodes in a
5146 : * single plan tree.
5147 : */
5148 : static void
5149 118044 : set_deparse_plan(deparse_namespace *dpns, Plan *plan)
5150 : {
5151 118044 : dpns->plan = plan;
5152 :
5153 : /*
5154 : * We special-case Append and MergeAppend to pretend that the first child
5155 : * plan is the OUTER referent; we have to interpret OUTER Vars in their
5156 : * tlists according to one of the children, and the first one is the most
5157 : * natural choice.
5158 : */
5159 118044 : if (IsA(plan, Append))
5160 3988 : dpns->outer_plan = linitial(((Append *) plan)->appendplans);
5161 114056 : else if (IsA(plan, MergeAppend))
5162 486 : dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
5163 : else
5164 113570 : dpns->outer_plan = outerPlan(plan);
5165 :
5166 118044 : if (dpns->outer_plan)
5167 53614 : dpns->outer_tlist = dpns->outer_plan->targetlist;
5168 : else
5169 64430 : dpns->outer_tlist = NIL;
5170 :
5171 : /*
5172 : * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
5173 : * use OUTER because that could someday conflict with the normal meaning.)
5174 : * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
5175 : * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
5176 : * that as INNER referent.
5177 : *
5178 : * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
5179 : * INNER referent. This is the join from the target relation to the data
5180 : * source, and all INNER_VAR Vars in other parts of the query refer to its
5181 : * targetlist.
5182 : *
5183 : * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the
5184 : * excluded expression's tlist. (Similar to the SubqueryScan we don't want
5185 : * to reuse OUTER, it's used for RETURNING in some modify table cases,
5186 : * although not INSERT .. CONFLICT).
5187 : */
5188 118044 : if (IsA(plan, SubqueryScan))
5189 644 : dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5190 117400 : else if (IsA(plan, CteScan))
5191 552 : dpns->inner_plan = list_nth(dpns->subplans,
5192 552 : ((CteScan *) plan)->ctePlanId - 1);
5193 116848 : else if (IsA(plan, WorkTableScan))
5194 174 : dpns->inner_plan = find_recursive_union(dpns,
5195 : (WorkTableScan *) plan);
5196 116674 : else if (IsA(plan, ModifyTable))
5197 : {
5198 384 : if (((ModifyTable *) plan)->operation == CMD_MERGE)
5199 60 : dpns->inner_plan = outerPlan(plan);
5200 : else
5201 324 : dpns->inner_plan = plan;
5202 : }
5203 : else
5204 116290 : dpns->inner_plan = innerPlan(plan);
5205 :
5206 118044 : if (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)
5207 170 : dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
5208 117874 : else if (dpns->inner_plan)
5209 20018 : dpns->inner_tlist = dpns->inner_plan->targetlist;
5210 : else
5211 97856 : dpns->inner_tlist = NIL;
5212 :
5213 : /* Set up referent for INDEX_VAR Vars, if needed */
5214 118044 : if (IsA(plan, IndexOnlyScan))
5215 2942 : dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5216 115102 : else if (IsA(plan, ForeignScan))
5217 2828 : dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5218 112274 : else if (IsA(plan, CustomScan))
5219 0 : dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5220 : else
5221 112274 : dpns->index_tlist = NIL;
5222 118044 : }
5223 :
5224 : /*
5225 : * Locate the ancestor plan node that is the RecursiveUnion generating
5226 : * the WorkTableScan's work table. We can match on wtParam, since that
5227 : * should be unique within the plan tree.
5228 : */
5229 : static Plan *
5230 174 : find_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)
5231 : {
5232 : ListCell *lc;
5233 :
5234 438 : foreach(lc, dpns->ancestors)
5235 : {
5236 438 : Plan *ancestor = (Plan *) lfirst(lc);
5237 :
5238 438 : if (IsA(ancestor, RecursiveUnion) &&
5239 174 : ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5240 174 : return ancestor;
5241 : }
5242 0 : elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5243 : wtscan->wtParam);
5244 : return NULL;
5245 : }
5246 :
5247 : /*
5248 : * push_child_plan: temporarily transfer deparsing attention to a child plan
5249 : *
5250 : * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
5251 : * deparse context in case the referenced expression itself uses
5252 : * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
5253 : * affecting levelsup issues (although in a Plan tree there really shouldn't
5254 : * be any).
5255 : *
5256 : * Caller must provide a local deparse_namespace variable to save the
5257 : * previous state for pop_child_plan.
5258 : */
5259 : static void
5260 64358 : push_child_plan(deparse_namespace *dpns, Plan *plan,
5261 : deparse_namespace *save_dpns)
5262 : {
5263 : /* Save state for restoration later */
5264 64358 : *save_dpns = *dpns;
5265 :
5266 : /* Link current plan node into ancestors list */
5267 64358 : dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5268 :
5269 : /* Set attention on selected child */
5270 64358 : set_deparse_plan(dpns, plan);
5271 64358 : }
5272 :
5273 : /*
5274 : * pop_child_plan: undo the effects of push_child_plan
5275 : */
5276 : static void
5277 64358 : pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5278 : {
5279 : List *ancestors;
5280 :
5281 : /* Get rid of ancestors list cell added by push_child_plan */
5282 64358 : ancestors = list_delete_first(dpns->ancestors);
5283 :
5284 : /* Restore fields changed by push_child_plan */
5285 64358 : *dpns = *save_dpns;
5286 :
5287 : /* Make sure dpns->ancestors is right (may be unnecessary) */
5288 64358 : dpns->ancestors = ancestors;
5289 64358 : }
5290 :
5291 : /*
5292 : * push_ancestor_plan: temporarily transfer deparsing attention to an
5293 : * ancestor plan
5294 : *
5295 : * When expanding a Param reference, we must adjust the deparse context
5296 : * to match the plan node that contains the expression being printed;
5297 : * otherwise we'd fail if that expression itself contains a Param or
5298 : * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
5299 : *
5300 : * The target ancestor is conveniently identified by the ListCell holding it
5301 : * in dpns->ancestors.
5302 : *
5303 : * Caller must provide a local deparse_namespace variable to save the
5304 : * previous state for pop_ancestor_plan.
5305 : */
5306 : static void
5307 3958 : push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
5308 : deparse_namespace *save_dpns)
5309 : {
5310 3958 : Plan *plan = (Plan *) lfirst(ancestor_cell);
5311 :
5312 : /* Save state for restoration later */
5313 3958 : *save_dpns = *dpns;
5314 :
5315 : /* Build a new ancestor list with just this node's ancestors */
5316 3958 : dpns->ancestors =
5317 3958 : list_copy_tail(dpns->ancestors,
5318 3958 : list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5319 :
5320 : /* Set attention on selected ancestor */
5321 3958 : set_deparse_plan(dpns, plan);
5322 3958 : }
5323 :
5324 : /*
5325 : * pop_ancestor_plan: undo the effects of push_ancestor_plan
5326 : */
5327 : static void
5328 3958 : pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5329 : {
5330 : /* Free the ancestor list made in push_ancestor_plan */
5331 3958 : list_free(dpns->ancestors);
5332 :
5333 : /* Restore fields changed by push_ancestor_plan */
5334 3958 : *dpns = *save_dpns;
5335 3958 : }
5336 :
5337 :
5338 : /* ----------
5339 : * make_ruledef - reconstruct the CREATE RULE command
5340 : * for a given pg_rewrite tuple
5341 : * ----------
5342 : */
5343 : static void
5344 558 : make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5345 : int prettyFlags)
5346 : {
5347 : char *rulename;
5348 : char ev_type;
5349 : Oid ev_class;
5350 : bool is_instead;
5351 : char *ev_qual;
5352 : char *ev_action;
5353 : List *actions;
5354 : Relation ev_relation;
5355 558 : TupleDesc viewResultDesc = NULL;
5356 : int fno;
5357 : Datum dat;
5358 : bool isnull;
5359 :
5360 : /*
5361 : * Get the attribute values from the rules tuple
5362 : */
5363 558 : fno = SPI_fnumber(rulettc, "rulename");
5364 558 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5365 : Assert(!isnull);
5366 558 : rulename = NameStr(*(DatumGetName(dat)));
5367 :
5368 558 : fno = SPI_fnumber(rulettc, "ev_type");
5369 558 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5370 : Assert(!isnull);
5371 558 : ev_type = DatumGetChar(dat);
5372 :
5373 558 : fno = SPI_fnumber(rulettc, "ev_class");
5374 558 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5375 : Assert(!isnull);
5376 558 : ev_class = DatumGetObjectId(dat);
5377 :
5378 558 : fno = SPI_fnumber(rulettc, "is_instead");
5379 558 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5380 : Assert(!isnull);
5381 558 : is_instead = DatumGetBool(dat);
5382 :
5383 558 : fno = SPI_fnumber(rulettc, "ev_qual");
5384 558 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5385 : Assert(ev_qual != NULL);
5386 :
5387 558 : fno = SPI_fnumber(rulettc, "ev_action");
5388 558 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5389 : Assert(ev_action != NULL);
5390 558 : actions = (List *) stringToNode(ev_action);
5391 558 : if (actions == NIL)
5392 0 : elog(ERROR, "invalid empty ev_action list");
5393 :
5394 558 : ev_relation = table_open(ev_class, AccessShareLock);
5395 :
5396 : /*
5397 : * Build the rules definition text
5398 : */
5399 558 : appendStringInfo(buf, "CREATE RULE %s AS",
5400 : quote_identifier(rulename));
5401 :
5402 558 : if (prettyFlags & PRETTYFLAG_INDENT)
5403 558 : appendStringInfoString(buf, "\n ON ");
5404 : else
5405 0 : appendStringInfoString(buf, " ON ");
5406 :
5407 : /* The event the rule is fired for */
5408 558 : switch (ev_type)
5409 : {
5410 6 : case '1':
5411 6 : appendStringInfoString(buf, "SELECT");
5412 6 : viewResultDesc = RelationGetDescr(ev_relation);
5413 6 : break;
5414 :
5415 154 : case '2':
5416 154 : appendStringInfoString(buf, "UPDATE");
5417 154 : break;
5418 :
5419 294 : case '3':
5420 294 : appendStringInfoString(buf, "INSERT");
5421 294 : break;
5422 :
5423 104 : case '4':
5424 104 : appendStringInfoString(buf, "DELETE");
5425 104 : break;
5426 :
5427 0 : default:
5428 0 : ereport(ERROR,
5429 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5430 : errmsg("rule \"%s\" has unsupported event type %d",
5431 : rulename, ev_type)));
5432 : break;
5433 : }
5434 :
5435 : /* The relation the rule is fired on */
5436 558 : appendStringInfo(buf, " TO %s",
5437 558 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
5438 114 : generate_relation_name(ev_class, NIL) :
5439 444 : generate_qualified_relation_name(ev_class));
5440 :
5441 : /* If the rule has an event qualification, add it */
5442 558 : if (strcmp(ev_qual, "<>") != 0)
5443 : {
5444 : Node *qual;
5445 : Query *query;
5446 : deparse_context context;
5447 : deparse_namespace dpns;
5448 :
5449 122 : if (prettyFlags & PRETTYFLAG_INDENT)
5450 122 : appendStringInfoString(buf, "\n ");
5451 122 : appendStringInfoString(buf, " WHERE ");
5452 :
5453 122 : qual = stringToNode(ev_qual);
5454 :
5455 : /*
5456 : * We need to make a context for recognizing any Vars in the qual
5457 : * (which can only be references to OLD and NEW). Use the rtable of
5458 : * the first query in the action list for this purpose.
5459 : */
5460 122 : query = (Query *) linitial(actions);
5461 :
5462 : /*
5463 : * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5464 : * into the SELECT, and that's what we need to look at. (Ugly kluge
5465 : * ... try to fix this when we redesign querytrees.)
5466 : */
5467 122 : query = getInsertSelectQuery(query, NULL);
5468 :
5469 : /* Must acquire locks right away; see notes in get_query_def() */
5470 122 : AcquireRewriteLocks(query, false, false);
5471 :
5472 122 : context.buf = buf;
5473 122 : context.namespaces = list_make1(&dpns);
5474 122 : context.resultDesc = NULL;
5475 122 : context.targetList = NIL;
5476 122 : context.windowClause = NIL;
5477 122 : context.varprefix = (list_length(query->rtable) != 1);
5478 122 : context.prettyFlags = prettyFlags;
5479 122 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
5480 122 : context.indentLevel = PRETTYINDENT_STD;
5481 122 : context.colNamesVisible = true;
5482 122 : context.inGroupBy = false;
5483 122 : context.varInOrderBy = false;
5484 122 : context.appendparents = NULL;
5485 :
5486 122 : set_deparse_for_query(&dpns, query, NIL);
5487 :
5488 122 : get_rule_expr(qual, &context, false);
5489 : }
5490 :
5491 558 : appendStringInfoString(buf, " DO ");
5492 :
5493 : /* The INSTEAD keyword (if so) */
5494 558 : if (is_instead)
5495 330 : appendStringInfoString(buf, "INSTEAD ");
5496 :
5497 : /* Finally the rules actions */
5498 558 : if (list_length(actions) > 1)
5499 : {
5500 : ListCell *action;
5501 : Query *query;
5502 :
5503 20 : appendStringInfoChar(buf, '(');
5504 60 : foreach(action, actions)
5505 : {
5506 40 : query = (Query *) lfirst(action);
5507 40 : get_query_def(query, buf, NIL, viewResultDesc, true,
5508 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5509 40 : if (prettyFlags)
5510 40 : appendStringInfoString(buf, ";\n");
5511 : else
5512 0 : appendStringInfoString(buf, "; ");
5513 : }
5514 20 : appendStringInfoString(buf, ");");
5515 : }
5516 : else
5517 : {
5518 : Query *query;
5519 :
5520 538 : query = (Query *) linitial(actions);
5521 538 : get_query_def(query, buf, NIL, viewResultDesc, true,
5522 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5523 538 : appendStringInfoChar(buf, ';');
5524 : }
5525 :
5526 558 : table_close(ev_relation, AccessShareLock);
5527 558 : }
5528 :
5529 :
5530 : /* ----------
5531 : * make_viewdef - reconstruct the SELECT part of a
5532 : * view rewrite rule
5533 : * ----------
5534 : */
5535 : static void
5536 3408 : make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5537 : int prettyFlags, int wrapColumn)
5538 : {
5539 : Query *query;
5540 : char ev_type;
5541 : Oid ev_class;
5542 : bool is_instead;
5543 : char *ev_qual;
5544 : char *ev_action;
5545 : List *actions;
5546 : Relation ev_relation;
5547 : int fno;
5548 : Datum dat;
5549 : bool isnull;
5550 :
5551 : /*
5552 : * Get the attribute values from the rules tuple
5553 : */
5554 3408 : fno = SPI_fnumber(rulettc, "ev_type");
5555 3408 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5556 : Assert(!isnull);
5557 3408 : ev_type = DatumGetChar(dat);
5558 :
5559 3408 : fno = SPI_fnumber(rulettc, "ev_class");
5560 3408 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5561 : Assert(!isnull);
5562 3408 : ev_class = DatumGetObjectId(dat);
5563 :
5564 3408 : fno = SPI_fnumber(rulettc, "is_instead");
5565 3408 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5566 : Assert(!isnull);
5567 3408 : is_instead = DatumGetBool(dat);
5568 :
5569 3408 : fno = SPI_fnumber(rulettc, "ev_qual");
5570 3408 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5571 : Assert(ev_qual != NULL);
5572 :
5573 3408 : fno = SPI_fnumber(rulettc, "ev_action");
5574 3408 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5575 : Assert(ev_action != NULL);
5576 3408 : actions = (List *) stringToNode(ev_action);
5577 :
5578 3408 : if (list_length(actions) != 1)
5579 : {
5580 : /* keep output buffer empty and leave */
5581 0 : return;
5582 : }
5583 :
5584 3408 : query = (Query *) linitial(actions);
5585 :
5586 3408 : if (ev_type != '1' || !is_instead ||
5587 3408 : strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5588 : {
5589 : /* keep output buffer empty and leave */
5590 0 : return;
5591 : }
5592 :
5593 3408 : ev_relation = table_open(ev_class, AccessShareLock);
5594 :
5595 3408 : get_query_def(query, buf, NIL, RelationGetDescr(ev_relation), true,
5596 : prettyFlags, wrapColumn, 0);
5597 3408 : appendStringInfoChar(buf, ';');
5598 :
5599 3408 : table_close(ev_relation, AccessShareLock);
5600 : }
5601 :
5602 :
5603 : /* ----------
5604 : * get_query_def - Parse back one query parsetree
5605 : *
5606 : * query: parsetree to be displayed
5607 : * buf: output text is appended to buf
5608 : * parentnamespace: list (initially empty) of outer-level deparse_namespace's
5609 : * resultDesc: if not NULL, the output tuple descriptor for the view
5610 : * represented by a SELECT query. We use the column names from it
5611 : * to label SELECT output columns, in preference to names in the query
5612 : * colNamesVisible: true if the surrounding context cares about the output
5613 : * column names at all (as, for example, an EXISTS() context does not);
5614 : * when false, we can suppress dummy column labels such as "?column?"
5615 : * prettyFlags: bitmask of PRETTYFLAG_XXX options
5616 : * wrapColumn: maximum line length, or -1 to disable wrapping
5617 : * startIndent: initial indentation amount
5618 : * ----------
5619 : */
5620 : static void
5621 5596 : get_query_def(Query *query, StringInfo buf, List *parentnamespace,
5622 : TupleDesc resultDesc, bool colNamesVisible,
5623 : int prettyFlags, int wrapColumn, int startIndent)
5624 : {
5625 : deparse_context context;
5626 : deparse_namespace dpns;
5627 : int rtable_size;
5628 :
5629 : /* Guard against excessively long or deeply-nested queries */
5630 5596 : CHECK_FOR_INTERRUPTS();
5631 5596 : check_stack_depth();
5632 :
5633 11192 : rtable_size = query->hasGroupRTE ?
5634 5596 : list_length(query->rtable) - 1 :
5635 5378 : list_length(query->rtable);
5636 :
5637 : /*
5638 : * Replace any Vars in the query's targetlist and havingQual that
5639 : * reference GROUP outputs with the underlying grouping expressions.
5640 : */
5641 5596 : if (query->hasGroupRTE)
5642 : {
5643 218 : query->targetList = (List *)
5644 218 : flatten_group_exprs(NULL, query, (Node *) query->targetList);
5645 218 : query->havingQual =
5646 218 : flatten_group_exprs(NULL, query, query->havingQual);
5647 : }
5648 :
5649 : /*
5650 : * Before we begin to examine the query, acquire locks on referenced
5651 : * relations, and fix up deleted columns in JOIN RTEs. This ensures
5652 : * consistent results. Note we assume it's OK to scribble on the passed
5653 : * querytree!
5654 : *
5655 : * We are only deparsing the query (we are not about to execute it), so we
5656 : * only need AccessShareLock on the relations it mentions.
5657 : */
5658 5596 : AcquireRewriteLocks(query, false, false);
5659 :
5660 5596 : context.buf = buf;
5661 5596 : context.namespaces = lcons(&dpns, list_copy(parentnamespace));
5662 5596 : context.resultDesc = NULL;
5663 5596 : context.targetList = NIL;
5664 5596 : context.windowClause = NIL;
5665 5596 : context.varprefix = (parentnamespace != NIL ||
5666 : rtable_size != 1);
5667 5596 : context.prettyFlags = prettyFlags;
5668 5596 : context.wrapColumn = wrapColumn;
5669 5596 : context.indentLevel = startIndent;
5670 5596 : context.colNamesVisible = colNamesVisible;
5671 5596 : context.inGroupBy = false;
5672 5596 : context.varInOrderBy = false;
5673 5596 : context.appendparents = NULL;
5674 :
5675 5596 : set_deparse_for_query(&dpns, query, parentnamespace);
5676 :
5677 5596 : switch (query->commandType)
5678 : {
5679 4956 : case CMD_SELECT:
5680 : /* We set context.resultDesc only if it's a SELECT */
5681 4956 : context.resultDesc = resultDesc;
5682 4956 : get_select_query_def(query, &context);
5683 4956 : break;
5684 :
5685 154 : case CMD_UPDATE:
5686 154 : get_update_query_def(query, &context);
5687 154 : break;
5688 :
5689 340 : case CMD_INSERT:
5690 340 : get_insert_query_def(query, &context);
5691 340 : break;
5692 :
5693 76 : case CMD_DELETE:
5694 76 : get_delete_query_def(query, &context);
5695 76 : break;
5696 :
5697 12 : case CMD_MERGE:
5698 12 : get_merge_query_def(query, &context);
5699 12 : break;
5700 :
5701 42 : case CMD_NOTHING:
5702 42 : appendStringInfoString(buf, "NOTHING");
5703 42 : break;
5704 :
5705 16 : case CMD_UTILITY:
5706 16 : get_utility_query_def(query, &context);
5707 16 : break;
5708 :
5709 0 : default:
5710 0 : elog(ERROR, "unrecognized query command type: %d",
5711 : query->commandType);
5712 : break;
5713 : }
5714 5596 : }
5715 :
5716 : /* ----------
5717 : * get_values_def - Parse back a VALUES list
5718 : * ----------
5719 : */
5720 : static void
5721 272 : get_values_def(List *values_lists, deparse_context *context)
5722 : {
5723 272 : StringInfo buf = context->buf;
5724 272 : bool first_list = true;
5725 : ListCell *vtl;
5726 :
5727 272 : appendStringInfoString(buf, "VALUES ");
5728 :
5729 778 : foreach(vtl, values_lists)
5730 : {
5731 506 : List *sublist = (List *) lfirst(vtl);
5732 506 : bool first_col = true;
5733 : ListCell *lc;
5734 :
5735 506 : if (first_list)
5736 272 : first_list = false;
5737 : else
5738 234 : appendStringInfoString(buf, ", ");
5739 :
5740 506 : appendStringInfoChar(buf, '(');
5741 1958 : foreach(lc, sublist)
5742 : {
5743 1452 : Node *col = (Node *) lfirst(lc);
5744 :
5745 1452 : if (first_col)
5746 506 : first_col = false;
5747 : else
5748 946 : appendStringInfoChar(buf, ',');
5749 :
5750 : /*
5751 : * Print the value. Whole-row Vars need special treatment.
5752 : */
5753 1452 : get_rule_expr_toplevel(col, context, false);
5754 : }
5755 506 : appendStringInfoChar(buf, ')');
5756 : }
5757 272 : }
5758 :
5759 : /* ----------
5760 : * get_with_clause - Parse back a WITH clause
5761 : * ----------
5762 : */
5763 : static void
5764 5538 : get_with_clause(Query *query, deparse_context *context)
5765 : {
5766 5538 : StringInfo buf = context->buf;
5767 : const char *sep;
5768 : ListCell *l;
5769 :
5770 5538 : if (query->cteList == NIL)
5771 5442 : return;
5772 :
5773 96 : if (PRETTY_INDENT(context))
5774 : {
5775 96 : context->indentLevel += PRETTYINDENT_STD;
5776 96 : appendStringInfoChar(buf, ' ');
5777 : }
5778 :
5779 96 : if (query->hasRecursive)
5780 56 : sep = "WITH RECURSIVE ";
5781 : else
5782 40 : sep = "WITH ";
5783 242 : foreach(l, query->cteList)
5784 : {
5785 146 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
5786 :
5787 146 : appendStringInfoString(buf, sep);
5788 146 : appendStringInfoString(buf, quote_identifier(cte->ctename));
5789 146 : if (cte->aliascolnames)
5790 : {
5791 56 : bool first = true;
5792 : ListCell *col;
5793 :
5794 56 : appendStringInfoChar(buf, '(');
5795 148 : foreach(col, cte->aliascolnames)
5796 : {
5797 92 : if (first)
5798 56 : first = false;
5799 : else
5800 36 : appendStringInfoString(buf, ", ");
5801 92 : appendStringInfoString(buf,
5802 92 : quote_identifier(strVal(lfirst(col))));
5803 : }
5804 56 : appendStringInfoChar(buf, ')');
5805 : }
5806 146 : appendStringInfoString(buf, " AS ");
5807 146 : switch (cte->ctematerialized)
5808 : {
5809 128 : case CTEMaterializeDefault:
5810 128 : break;
5811 18 : case CTEMaterializeAlways:
5812 18 : appendStringInfoString(buf, "MATERIALIZED ");
5813 18 : break;
5814 0 : case CTEMaterializeNever:
5815 0 : appendStringInfoString(buf, "NOT MATERIALIZED ");
5816 0 : break;
5817 : }
5818 146 : appendStringInfoChar(buf, '(');
5819 146 : if (PRETTY_INDENT(context))
5820 146 : appendContextKeyword(context, "", 0, 0, 0);
5821 146 : get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
5822 : true,
5823 : context->prettyFlags, context->wrapColumn,
5824 : context->indentLevel);
5825 146 : if (PRETTY_INDENT(context))
5826 146 : appendContextKeyword(context, "", 0, 0, 0);
5827 146 : appendStringInfoChar(buf, ')');
5828 :
5829 146 : if (cte->search_clause)
5830 : {
5831 6 : bool first = true;
5832 : ListCell *lc;
5833 :
5834 6 : appendStringInfo(buf, " SEARCH %s FIRST BY ",
5835 6 : cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
5836 :
5837 18 : foreach(lc, cte->search_clause->search_col_list)
5838 : {
5839 12 : if (first)
5840 6 : first = false;
5841 : else
5842 6 : appendStringInfoString(buf, ", ");
5843 12 : appendStringInfoString(buf,
5844 12 : quote_identifier(strVal(lfirst(lc))));
5845 : }
5846 :
5847 6 : appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
5848 : }
5849 :
5850 146 : if (cte->cycle_clause)
5851 : {
5852 12 : bool first = true;
5853 : ListCell *lc;
5854 :
5855 12 : appendStringInfoString(buf, " CYCLE ");
5856 :
5857 36 : foreach(lc, cte->cycle_clause->cycle_col_list)
5858 : {
5859 24 : if (first)
5860 12 : first = false;
5861 : else
5862 12 : appendStringInfoString(buf, ", ");
5863 24 : appendStringInfoString(buf,
5864 24 : quote_identifier(strVal(lfirst(lc))));
5865 : }
5866 :
5867 12 : appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
5868 :
5869 : {
5870 12 : Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
5871 12 : Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
5872 :
5873 18 : if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
5874 6 : cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
5875 : {
5876 6 : appendStringInfoString(buf, " TO ");
5877 6 : get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
5878 6 : appendStringInfoString(buf, " DEFAULT ");
5879 6 : get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
5880 : }
5881 : }
5882 :
5883 12 : appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
5884 : }
5885 :
5886 146 : sep = ", ";
5887 : }
5888 :
5889 96 : if (PRETTY_INDENT(context))
5890 : {
5891 96 : context->indentLevel -= PRETTYINDENT_STD;
5892 96 : appendContextKeyword(context, "", 0, 0, 0);
5893 : }
5894 : else
5895 0 : appendStringInfoChar(buf, ' ');
5896 : }
5897 :
5898 : /* ----------
5899 : * get_select_query_def - Parse back a SELECT parsetree
5900 : * ----------
5901 : */
5902 : static void
5903 4956 : get_select_query_def(Query *query, deparse_context *context)
5904 : {
5905 4956 : StringInfo buf = context->buf;
5906 : bool force_colno;
5907 : ListCell *l;
5908 :
5909 : /* Insert the WITH clause if given */
5910 4956 : get_with_clause(query, context);
5911 :
5912 : /* Subroutines may need to consult the SELECT targetlist and windowClause */
5913 4956 : context->targetList = query->targetList;
5914 4956 : context->windowClause = query->windowClause;
5915 :
5916 : /*
5917 : * If the Query node has a setOperations tree, then it's the top level of
5918 : * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
5919 : * fields are interesting in the top query itself.
5920 : */
5921 4956 : if (query->setOperations)
5922 : {
5923 164 : get_setop_query(query->setOperations, query, context);
5924 : /* ORDER BY clauses must be simple in this case */
5925 164 : force_colno = true;
5926 : }
5927 : else
5928 : {
5929 4792 : get_basic_select_query(query, context);
5930 4792 : force_colno = false;
5931 : }
5932 :
5933 : /* Add the ORDER BY clause if given */
5934 4956 : if (query->sortClause != NIL)
5935 : {
5936 164 : appendContextKeyword(context, " ORDER BY ",
5937 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5938 164 : get_rule_orderby(query->sortClause, query->targetList,
5939 : force_colno, context);
5940 : }
5941 :
5942 : /*
5943 : * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
5944 : * standard spelling of LIMIT.
5945 : */
5946 4956 : if (query->limitOffset != NULL)
5947 : {
5948 32 : appendContextKeyword(context, " OFFSET ",
5949 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5950 32 : get_rule_expr(query->limitOffset, context, false);
5951 : }
5952 4956 : if (query->limitCount != NULL)
5953 : {
5954 70 : if (query->limitOption == LIMIT_OPTION_WITH_TIES)
5955 : {
5956 32 : appendContextKeyword(context, " FETCH FIRST ",
5957 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5958 32 : get_rule_expr(query->limitCount, context, false);
5959 32 : appendStringInfoString(buf, " ROWS WITH TIES");
5960 : }
5961 : else
5962 : {
5963 38 : appendContextKeyword(context, " LIMIT ",
5964 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5965 38 : if (IsA(query->limitCount, Const) &&
5966 16 : ((Const *) query->limitCount)->constisnull)
5967 16 : appendStringInfoString(buf, "ALL");
5968 : else
5969 22 : get_rule_expr(query->limitCount, context, false);
5970 : }
5971 : }
5972 :
5973 : /* Add FOR [KEY] UPDATE/SHARE clauses if present */
5974 4956 : if (query->hasForUpdate)
5975 : {
5976 12 : foreach(l, query->rowMarks)
5977 : {
5978 6 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
5979 :
5980 : /* don't print implicit clauses */
5981 6 : if (rc->pushedDown)
5982 0 : continue;
5983 :
5984 6 : switch (rc->strength)
5985 : {
5986 0 : case LCS_NONE:
5987 : /* we intentionally throw an error for LCS_NONE */
5988 0 : elog(ERROR, "unrecognized LockClauseStrength %d",
5989 : (int) rc->strength);
5990 : break;
5991 0 : case LCS_FORKEYSHARE:
5992 0 : appendContextKeyword(context, " FOR KEY SHARE",
5993 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5994 0 : break;
5995 0 : case LCS_FORSHARE:
5996 0 : appendContextKeyword(context, " FOR SHARE",
5997 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5998 0 : break;
5999 0 : case LCS_FORNOKEYUPDATE:
6000 0 : appendContextKeyword(context, " FOR NO KEY UPDATE",
6001 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6002 0 : break;
6003 6 : case LCS_FORUPDATE:
6004 6 : appendContextKeyword(context, " FOR UPDATE",
6005 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6006 6 : break;
6007 : }
6008 :
6009 6 : appendStringInfo(buf, " OF %s",
6010 6 : quote_identifier(get_rtable_name(rc->rti,
6011 : context)));
6012 6 : if (rc->waitPolicy == LockWaitError)
6013 0 : appendStringInfoString(buf, " NOWAIT");
6014 6 : else if (rc->waitPolicy == LockWaitSkip)
6015 0 : appendStringInfoString(buf, " SKIP LOCKED");
6016 : }
6017 : }
6018 4956 : }
6019 :
6020 : /*
6021 : * Detect whether query looks like SELECT ... FROM VALUES(),
6022 : * with no need to rename the output columns of the VALUES RTE.
6023 : * If so, return the VALUES RTE. Otherwise return NULL.
6024 : */
6025 : static RangeTblEntry *
6026 4792 : get_simple_values_rte(Query *query, TupleDesc resultDesc)
6027 : {
6028 4792 : RangeTblEntry *result = NULL;
6029 : ListCell *lc;
6030 :
6031 : /*
6032 : * We want to detect a match even if the Query also contains OLD or NEW
6033 : * rule RTEs. So the idea is to scan the rtable and see if there is only
6034 : * one inFromCl RTE that is a VALUES RTE.
6035 : */
6036 5164 : foreach(lc, query->rtable)
6037 : {
6038 4394 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
6039 :
6040 4394 : if (rte->rtekind == RTE_VALUES && rte->inFromCl)
6041 : {
6042 228 : if (result)
6043 4022 : return NULL; /* multiple VALUES (probably not possible) */
6044 228 : result = rte;
6045 : }
6046 4166 : else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
6047 144 : continue; /* ignore rule entries */
6048 : else
6049 4022 : return NULL; /* something else -> not simple VALUES */
6050 : }
6051 :
6052 : /*
6053 : * We don't need to check the targetlist in any great detail, because
6054 : * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
6055 : * appear inside auto-generated sub-queries with very restricted
6056 : * structure. However, DefineView might have modified the tlist by
6057 : * injecting new column aliases, or we might have some other column
6058 : * aliases forced by a resultDesc. We can only simplify if the RTE's
6059 : * column names match the names that get_target_list() would select.
6060 : */
6061 770 : if (result)
6062 : {
6063 : ListCell *lcn;
6064 : int colno;
6065 :
6066 228 : if (list_length(query->targetList) != list_length(result->eref->colnames))
6067 0 : return NULL; /* this probably cannot happen */
6068 228 : colno = 0;
6069 842 : forboth(lc, query->targetList, lcn, result->eref->colnames)
6070 : {
6071 626 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
6072 626 : char *cname = strVal(lfirst(lcn));
6073 : char *colname;
6074 :
6075 626 : if (tle->resjunk)
6076 12 : return NULL; /* this probably cannot happen */
6077 :
6078 : /* compute name that get_target_list would use for column */
6079 626 : colno++;
6080 626 : if (resultDesc && colno <= resultDesc->natts)
6081 30 : colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6082 : else
6083 596 : colname = tle->resname;
6084 :
6085 : /* does it match the VALUES RTE? */
6086 626 : if (colname == NULL || strcmp(colname, cname) != 0)
6087 12 : return NULL; /* column name has been changed */
6088 : }
6089 : }
6090 :
6091 758 : return result;
6092 : }
6093 :
6094 : static void
6095 4792 : get_basic_select_query(Query *query, deparse_context *context)
6096 : {
6097 4792 : StringInfo buf = context->buf;
6098 : RangeTblEntry *values_rte;
6099 : char *sep;
6100 : ListCell *l;
6101 :
6102 4792 : if (PRETTY_INDENT(context))
6103 : {
6104 4746 : context->indentLevel += PRETTYINDENT_STD;
6105 4746 : appendStringInfoChar(buf, ' ');
6106 : }
6107 :
6108 : /*
6109 : * If the query looks like SELECT * FROM (VALUES ...), then print just the
6110 : * VALUES part. This reverses what transformValuesClause() did at parse
6111 : * time.
6112 : */
6113 4792 : values_rte = get_simple_values_rte(query, context->resultDesc);
6114 4792 : if (values_rte)
6115 : {
6116 216 : get_values_def(values_rte->values_lists, context);
6117 216 : return;
6118 : }
6119 :
6120 : /*
6121 : * Build up the query string - first we say SELECT
6122 : */
6123 4576 : if (query->isReturn)
6124 52 : appendStringInfoString(buf, "RETURN");
6125 : else
6126 4524 : appendStringInfoString(buf, "SELECT");
6127 :
6128 : /* Add the DISTINCT clause if given */
6129 4576 : if (query->distinctClause != NIL)
6130 : {
6131 0 : if (query->hasDistinctOn)
6132 : {
6133 0 : appendStringInfoString(buf, " DISTINCT ON (");
6134 0 : sep = "";
6135 0 : foreach(l, query->distinctClause)
6136 : {
6137 0 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6138 :
6139 0 : appendStringInfoString(buf, sep);
6140 0 : get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6141 : false, context);
6142 0 : sep = ", ";
6143 : }
6144 0 : appendStringInfoChar(buf, ')');
6145 : }
6146 : else
6147 0 : appendStringInfoString(buf, " DISTINCT");
6148 : }
6149 :
6150 : /* Then we tell what to select (the targetlist) */
6151 4576 : get_target_list(query->targetList, context);
6152 :
6153 : /* Add the FROM clause if needed */
6154 4576 : get_from_clause(query, " FROM ", context);
6155 :
6156 : /* Add the WHERE clause if given */
6157 4576 : if (query->jointree->quals != NULL)
6158 : {
6159 1454 : appendContextKeyword(context, " WHERE ",
6160 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6161 1454 : get_rule_expr(query->jointree->quals, context, false);
6162 : }
6163 :
6164 : /* Add the GROUP BY clause if given */
6165 4576 : if (query->groupClause != NULL || query->groupingSets != NULL)
6166 : {
6167 : bool save_ingroupby;
6168 :
6169 218 : appendContextKeyword(context, " GROUP BY ",
6170 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6171 218 : if (query->groupDistinct)
6172 0 : appendStringInfoString(buf, "DISTINCT ");
6173 :
6174 218 : save_ingroupby = context->inGroupBy;
6175 218 : context->inGroupBy = true;
6176 :
6177 218 : if (query->groupingSets == NIL)
6178 : {
6179 212 : sep = "";
6180 482 : foreach(l, query->groupClause)
6181 : {
6182 270 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6183 :
6184 270 : appendStringInfoString(buf, sep);
6185 270 : get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
6186 : false, context);
6187 270 : sep = ", ";
6188 : }
6189 : }
6190 : else
6191 : {
6192 6 : sep = "";
6193 12 : foreach(l, query->groupingSets)
6194 : {
6195 6 : GroupingSet *grp = lfirst(l);
6196 :
6197 6 : appendStringInfoString(buf, sep);
6198 6 : get_rule_groupingset(grp, query->targetList, true, context);
6199 6 : sep = ", ";
6200 : }
6201 : }
6202 :
6203 218 : context->inGroupBy = save_ingroupby;
6204 : }
6205 :
6206 : /* Add the HAVING clause if given */
6207 4576 : if (query->havingQual != NULL)
6208 : {
6209 10 : appendContextKeyword(context, " HAVING ",
6210 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6211 10 : get_rule_expr(query->havingQual, context, false);
6212 : }
6213 :
6214 : /* Add the WINDOW clause if needed */
6215 4576 : if (query->windowClause != NIL)
6216 42 : get_rule_windowclause(query, context);
6217 : }
6218 :
6219 : /* ----------
6220 : * get_target_list - Parse back a SELECT target list
6221 : *
6222 : * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.
6223 : * ----------
6224 : */
6225 : static void
6226 4722 : get_target_list(List *targetList, deparse_context *context)
6227 : {
6228 4722 : StringInfo buf = context->buf;
6229 : StringInfoData targetbuf;
6230 4722 : bool last_was_multiline = false;
6231 : char *sep;
6232 : int colno;
6233 : ListCell *l;
6234 :
6235 : /* we use targetbuf to hold each TLE's text temporarily */
6236 4722 : initStringInfo(&targetbuf);
6237 :
6238 4722 : sep = " ";
6239 4722 : colno = 0;
6240 24222 : foreach(l, targetList)
6241 : {
6242 19500 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6243 : char *colname;
6244 : char *attname;
6245 :
6246 19500 : if (tle->resjunk)
6247 34 : continue; /* ignore junk entries */
6248 :
6249 19466 : appendStringInfoString(buf, sep);
6250 19466 : sep = ", ";
6251 19466 : colno++;
6252 :
6253 : /*
6254 : * Put the new field text into targetbuf so we can decide after we've
6255 : * got it whether or not it needs to go on a new line.
6256 : */
6257 19466 : resetStringInfo(&targetbuf);
6258 19466 : context->buf = &targetbuf;
6259 :
6260 : /*
6261 : * We special-case Var nodes rather than using get_rule_expr. This is
6262 : * needed because get_rule_expr will display a whole-row Var as
6263 : * "foo.*", which is the preferred notation in most contexts, but at
6264 : * the top level of a SELECT list it's not right (the parser will
6265 : * expand that notation into multiple columns, yielding behavior
6266 : * different from a whole-row Var). We need to call get_variable
6267 : * directly so that we can tell it to do the right thing, and so that
6268 : * we can get the attribute name which is the default AS label.
6269 : */
6270 19466 : if (tle->expr && (IsA(tle->expr, Var)))
6271 : {
6272 15064 : attname = get_variable((Var *) tle->expr, 0, true, context);
6273 : }
6274 : else
6275 : {
6276 4402 : get_rule_expr((Node *) tle->expr, context, true);
6277 :
6278 : /*
6279 : * When colNamesVisible is true, we should always show the
6280 : * assigned column name explicitly. Otherwise, show it only if
6281 : * it's not FigureColname's fallback.
6282 : */
6283 4402 : attname = context->colNamesVisible ? NULL : "?column?";
6284 : }
6285 :
6286 : /*
6287 : * Figure out what the result column should be called. In the context
6288 : * of a view, use the view's tuple descriptor (so as to pick up the
6289 : * effects of any column RENAME that's been done on the view).
6290 : * Otherwise, just use what we can find in the TLE.
6291 : */
6292 19466 : if (context->resultDesc && colno <= context->resultDesc->natts)
6293 17650 : colname = NameStr(TupleDescAttr(context->resultDesc,
6294 : colno - 1)->attname);
6295 : else
6296 1816 : colname = tle->resname;
6297 :
6298 : /* Show AS unless the column's name is correct as-is */
6299 19466 : if (colname) /* resname could be NULL */
6300 : {
6301 19414 : if (attname == NULL || strcmp(attname, colname) != 0)
6302 6324 : appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
6303 : }
6304 :
6305 : /* Restore context's output buffer */
6306 19466 : context->buf = buf;
6307 :
6308 : /* Consider line-wrapping if enabled */
6309 19466 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
6310 : {
6311 : int leading_nl_pos;
6312 :
6313 : /* Does the new field start with a new line? */
6314 19420 : if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
6315 462 : leading_nl_pos = 0;
6316 : else
6317 18958 : leading_nl_pos = -1;
6318 :
6319 : /* If so, we shouldn't add anything */
6320 19420 : if (leading_nl_pos >= 0)
6321 : {
6322 : /* instead, remove any trailing spaces currently in buf */
6323 462 : removeStringInfoSpaces(buf);
6324 : }
6325 : else
6326 : {
6327 : char *trailing_nl;
6328 :
6329 : /* Locate the start of the current line in the output buffer */
6330 18958 : trailing_nl = strrchr(buf->data, '\n');
6331 18958 : if (trailing_nl == NULL)
6332 5696 : trailing_nl = buf->data;
6333 : else
6334 13262 : trailing_nl++;
6335 :
6336 : /*
6337 : * Add a newline, plus some indentation, if the new field is
6338 : * not the first and either the new field would cause an
6339 : * overflow or the last field used more than one line.
6340 : */
6341 18958 : if (colno > 1 &&
6342 14298 : ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
6343 : last_was_multiline))
6344 14298 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
6345 : PRETTYINDENT_STD, PRETTYINDENT_VAR);
6346 : }
6347 :
6348 : /* Remember this field's multiline status for next iteration */
6349 19420 : last_was_multiline =
6350 19420 : (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
6351 : }
6352 :
6353 : /* Add the new field */
6354 19466 : appendBinaryStringInfo(buf, targetbuf.data, targetbuf.len);
6355 : }
6356 :
6357 : /* clean up */
6358 4722 : pfree(targetbuf.data);
6359 4722 : }
6360 :
6361 : static void
6362 146 : get_returning_clause(Query *query, deparse_context *context)
6363 : {
6364 146 : StringInfo buf = context->buf;
6365 :
6366 146 : if (query->returningList)
6367 : {
6368 146 : bool have_with = false;
6369 :
6370 146 : appendContextKeyword(context, " RETURNING",
6371 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6372 :
6373 : /* Add WITH (OLD/NEW) options, if they're not the defaults */
6374 146 : if (query->returningOldAlias && strcmp(query->returningOldAlias, "old") != 0)
6375 : {
6376 18 : appendStringInfo(buf, " WITH (OLD AS %s",
6377 18 : quote_identifier(query->returningOldAlias));
6378 18 : have_with = true;
6379 : }
6380 146 : if (query->returningNewAlias && strcmp(query->returningNewAlias, "new") != 0)
6381 : {
6382 18 : if (have_with)
6383 12 : appendStringInfo(buf, ", NEW AS %s",
6384 12 : quote_identifier(query->returningNewAlias));
6385 : else
6386 : {
6387 6 : appendStringInfo(buf, " WITH (NEW AS %s",
6388 6 : quote_identifier(query->returningNewAlias));
6389 6 : have_with = true;
6390 : }
6391 : }
6392 146 : if (have_with)
6393 24 : appendStringInfoChar(buf, ')');
6394 :
6395 : /* Add the returning expressions themselves */
6396 146 : get_target_list(query->returningList, context);
6397 : }
6398 146 : }
6399 :
6400 : static void
6401 756 : get_setop_query(Node *setOp, Query *query, deparse_context *context)
6402 : {
6403 756 : StringInfo buf = context->buf;
6404 : bool need_paren;
6405 :
6406 : /* Guard against excessively long or deeply-nested queries */
6407 756 : CHECK_FOR_INTERRUPTS();
6408 756 : check_stack_depth();
6409 :
6410 756 : if (IsA(setOp, RangeTblRef))
6411 : {
6412 460 : RangeTblRef *rtr = (RangeTblRef *) setOp;
6413 460 : RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
6414 460 : Query *subquery = rte->subquery;
6415 :
6416 : Assert(subquery != NULL);
6417 :
6418 : /*
6419 : * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.
6420 : * Also add parens if the leaf query contains its own set operations.
6421 : * (That shouldn't happen unless one of the other clauses is also
6422 : * present, see transformSetOperationTree; but let's be safe.)
6423 : */
6424 1380 : need_paren = (subquery->cteList ||
6425 460 : subquery->sortClause ||
6426 460 : subquery->rowMarks ||
6427 460 : subquery->limitOffset ||
6428 1380 : subquery->limitCount ||
6429 460 : subquery->setOperations);
6430 460 : if (need_paren)
6431 0 : appendStringInfoChar(buf, '(');
6432 460 : get_query_def(subquery, buf, context->namespaces,
6433 460 : context->resultDesc, context->colNamesVisible,
6434 : context->prettyFlags, context->wrapColumn,
6435 : context->indentLevel);
6436 460 : if (need_paren)
6437 0 : appendStringInfoChar(buf, ')');
6438 : }
6439 296 : else if (IsA(setOp, SetOperationStmt))
6440 : {
6441 296 : SetOperationStmt *op = (SetOperationStmt *) setOp;
6442 : int subindent;
6443 : bool save_colnamesvisible;
6444 :
6445 : /*
6446 : * We force parens when nesting two SetOperationStmts, except when the
6447 : * lefthand input is another setop of the same kind. Syntactically,
6448 : * we could omit parens in rather more cases, but it seems best to use
6449 : * parens to flag cases where the setop operator changes. If we use
6450 : * parens, we also increase the indentation level for the child query.
6451 : *
6452 : * There are some cases in which parens are needed around a leaf query
6453 : * too, but those are more easily handled at the next level down (see
6454 : * code above).
6455 : */
6456 296 : if (IsA(op->larg, SetOperationStmt))
6457 : {
6458 132 : SetOperationStmt *lop = (SetOperationStmt *) op->larg;
6459 :
6460 132 : if (op->op == lop->op && op->all == lop->all)
6461 132 : need_paren = false;
6462 : else
6463 0 : need_paren = true;
6464 : }
6465 : else
6466 164 : need_paren = false;
6467 :
6468 296 : if (need_paren)
6469 : {
6470 0 : appendStringInfoChar(buf, '(');
6471 0 : subindent = PRETTYINDENT_STD;
6472 0 : appendContextKeyword(context, "", subindent, 0, 0);
6473 : }
6474 : else
6475 296 : subindent = 0;
6476 :
6477 296 : get_setop_query(op->larg, query, context);
6478 :
6479 296 : if (need_paren)
6480 0 : appendContextKeyword(context, ") ", -subindent, 0, 0);
6481 296 : else if (PRETTY_INDENT(context))
6482 296 : appendContextKeyword(context, "", -subindent, 0, 0);
6483 : else
6484 0 : appendStringInfoChar(buf, ' ');
6485 :
6486 296 : switch (op->op)
6487 : {
6488 296 : case SETOP_UNION:
6489 296 : appendStringInfoString(buf, "UNION ");
6490 296 : break;
6491 0 : case SETOP_INTERSECT:
6492 0 : appendStringInfoString(buf, "INTERSECT ");
6493 0 : break;
6494 0 : case SETOP_EXCEPT:
6495 0 : appendStringInfoString(buf, "EXCEPT ");
6496 0 : break;
6497 0 : default:
6498 0 : elog(ERROR, "unrecognized set op: %d",
6499 : (int) op->op);
6500 : }
6501 296 : if (op->all)
6502 284 : appendStringInfoString(buf, "ALL ");
6503 :
6504 : /* Always parenthesize if RHS is another setop */
6505 296 : need_paren = IsA(op->rarg, SetOperationStmt);
6506 :
6507 : /*
6508 : * The indentation code here is deliberately a bit different from that
6509 : * for the lefthand input, because we want the line breaks in
6510 : * different places.
6511 : */
6512 296 : if (need_paren)
6513 : {
6514 0 : appendStringInfoChar(buf, '(');
6515 0 : subindent = PRETTYINDENT_STD;
6516 : }
6517 : else
6518 296 : subindent = 0;
6519 296 : appendContextKeyword(context, "", subindent, 0, 0);
6520 :
6521 : /*
6522 : * The output column names of the RHS sub-select don't matter.
6523 : */
6524 296 : save_colnamesvisible = context->colNamesVisible;
6525 296 : context->colNamesVisible = false;
6526 :
6527 296 : get_setop_query(op->rarg, query, context);
6528 :
6529 296 : context->colNamesVisible = save_colnamesvisible;
6530 :
6531 296 : if (PRETTY_INDENT(context))
6532 296 : context->indentLevel -= subindent;
6533 296 : if (need_paren)
6534 0 : appendContextKeyword(context, ")", 0, 0, 0);
6535 : }
6536 : else
6537 : {
6538 0 : elog(ERROR, "unrecognized node type: %d",
6539 : (int) nodeTag(setOp));
6540 : }
6541 756 : }
6542 :
6543 : /*
6544 : * Display a sort/group clause.
6545 : *
6546 : * Also returns the expression tree, so caller need not find it again.
6547 : */
6548 : static Node *
6549 648 : get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
6550 : deparse_context *context)
6551 : {
6552 648 : StringInfo buf = context->buf;
6553 : TargetEntry *tle;
6554 : Node *expr;
6555 :
6556 648 : tle = get_sortgroupref_tle(ref, tlist);
6557 648 : expr = (Node *) tle->expr;
6558 :
6559 : /*
6560 : * Use column-number form if requested by caller. Otherwise, if
6561 : * expression is a constant, force it to be dumped with an explicit cast
6562 : * as decoration --- this is because a simple integer constant is
6563 : * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
6564 : * we dump it without any decoration. Similarly, if it's just a Var,
6565 : * there is risk of misinterpretation if the column name is reassigned in
6566 : * the SELECT list, so we may need to force table qualification. And, if
6567 : * it's anything more complex than a simple Var, then force extra parens
6568 : * around it, to ensure it can't be misinterpreted as a cube() or rollup()
6569 : * construct.
6570 : */
6571 648 : if (force_colno)
6572 : {
6573 : Assert(!tle->resjunk);
6574 12 : appendStringInfo(buf, "%d", tle->resno);
6575 : }
6576 636 : else if (!expr)
6577 : /* do nothing, probably can't happen */ ;
6578 636 : else if (IsA(expr, Const))
6579 0 : get_const_expr((Const *) expr, context, 1);
6580 636 : else if (IsA(expr, Var))
6581 : {
6582 : /* Tell get_variable to check for name conflict */
6583 608 : bool save_varinorderby = context->varInOrderBy;
6584 :
6585 608 : context->varInOrderBy = true;
6586 608 : (void) get_variable((Var *) expr, 0, false, context);
6587 608 : context->varInOrderBy = save_varinorderby;
6588 : }
6589 : else
6590 : {
6591 : /*
6592 : * We must force parens for function-like expressions even if
6593 : * PRETTY_PAREN is off, since those are the ones in danger of
6594 : * misparsing. For other expressions we need to force them only if
6595 : * PRETTY_PAREN is on, since otherwise the expression will output them
6596 : * itself. (We can't skip the parens.)
6597 : */
6598 56 : bool need_paren = (PRETTY_PAREN(context)
6599 28 : || IsA(expr, FuncExpr)
6600 24 : || IsA(expr, Aggref)
6601 24 : || IsA(expr, WindowFunc)
6602 56 : || IsA(expr, JsonConstructorExpr));
6603 :
6604 28 : if (need_paren)
6605 4 : appendStringInfoChar(context->buf, '(');
6606 28 : get_rule_expr(expr, context, true);
6607 28 : if (need_paren)
6608 4 : appendStringInfoChar(context->buf, ')');
6609 : }
6610 :
6611 648 : return expr;
6612 : }
6613 :
6614 : /*
6615 : * Display a GroupingSet
6616 : */
6617 : static void
6618 18 : get_rule_groupingset(GroupingSet *gset, List *targetlist,
6619 : bool omit_parens, deparse_context *context)
6620 : {
6621 : ListCell *l;
6622 18 : StringInfo buf = context->buf;
6623 18 : bool omit_child_parens = true;
6624 18 : char *sep = "";
6625 :
6626 18 : switch (gset->kind)
6627 : {
6628 0 : case GROUPING_SET_EMPTY:
6629 0 : appendStringInfoString(buf, "()");
6630 0 : return;
6631 :
6632 12 : case GROUPING_SET_SIMPLE:
6633 : {
6634 12 : if (!omit_parens || list_length(gset->content) != 1)
6635 12 : appendStringInfoChar(buf, '(');
6636 :
6637 42 : foreach(l, gset->content)
6638 : {
6639 30 : Index ref = lfirst_int(l);
6640 :
6641 30 : appendStringInfoString(buf, sep);
6642 30 : get_rule_sortgroupclause(ref, targetlist,
6643 : false, context);
6644 30 : sep = ", ";
6645 : }
6646 :
6647 12 : if (!omit_parens || list_length(gset->content) != 1)
6648 12 : appendStringInfoChar(buf, ')');
6649 : }
6650 12 : return;
6651 :
6652 6 : case GROUPING_SET_ROLLUP:
6653 6 : appendStringInfoString(buf, "ROLLUP(");
6654 6 : break;
6655 0 : case GROUPING_SET_CUBE:
6656 0 : appendStringInfoString(buf, "CUBE(");
6657 0 : break;
6658 0 : case GROUPING_SET_SETS:
6659 0 : appendStringInfoString(buf, "GROUPING SETS (");
6660 0 : omit_child_parens = false;
6661 0 : break;
6662 : }
6663 :
6664 18 : foreach(l, gset->content)
6665 : {
6666 12 : appendStringInfoString(buf, sep);
6667 12 : get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
6668 12 : sep = ", ";
6669 : }
6670 :
6671 6 : appendStringInfoChar(buf, ')');
6672 : }
6673 :
6674 : /*
6675 : * Display an ORDER BY list.
6676 : */
6677 : static void
6678 316 : get_rule_orderby(List *orderList, List *targetList,
6679 : bool force_colno, deparse_context *context)
6680 : {
6681 316 : StringInfo buf = context->buf;
6682 : const char *sep;
6683 : ListCell *l;
6684 :
6685 316 : sep = "";
6686 664 : foreach(l, orderList)
6687 : {
6688 348 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6689 : Node *sortexpr;
6690 : Oid sortcoltype;
6691 : TypeCacheEntry *typentry;
6692 :
6693 348 : appendStringInfoString(buf, sep);
6694 348 : sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
6695 : force_colno, context);
6696 348 : sortcoltype = exprType(sortexpr);
6697 : /* See whether operator is default < or > for datatype */
6698 348 : typentry = lookup_type_cache(sortcoltype,
6699 : TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
6700 348 : if (srt->sortop == typentry->lt_opr)
6701 : {
6702 : /* ASC is default, so emit nothing for it */
6703 320 : if (srt->nulls_first)
6704 0 : appendStringInfoString(buf, " NULLS FIRST");
6705 : }
6706 28 : else if (srt->sortop == typentry->gt_opr)
6707 : {
6708 10 : appendStringInfoString(buf, " DESC");
6709 : /* DESC defaults to NULLS FIRST */
6710 10 : if (!srt->nulls_first)
6711 2 : appendStringInfoString(buf, " NULLS LAST");
6712 : }
6713 : else
6714 : {
6715 18 : appendStringInfo(buf, " USING %s",
6716 : generate_operator_name(srt->sortop,
6717 : sortcoltype,
6718 : sortcoltype));
6719 : /* be specific to eliminate ambiguity */
6720 18 : if (srt->nulls_first)
6721 0 : appendStringInfoString(buf, " NULLS FIRST");
6722 : else
6723 18 : appendStringInfoString(buf, " NULLS LAST");
6724 : }
6725 348 : sep = ", ";
6726 : }
6727 316 : }
6728 :
6729 : /*
6730 : * Display a WINDOW clause.
6731 : *
6732 : * Note that the windowClause list might contain only anonymous window
6733 : * specifications, in which case we should print nothing here.
6734 : */
6735 : static void
6736 42 : get_rule_windowclause(Query *query, deparse_context *context)
6737 : {
6738 42 : StringInfo buf = context->buf;
6739 : const char *sep;
6740 : ListCell *l;
6741 :
6742 42 : sep = NULL;
6743 84 : foreach(l, query->windowClause)
6744 : {
6745 42 : WindowClause *wc = (WindowClause *) lfirst(l);
6746 :
6747 42 : if (wc->name == NULL)
6748 42 : continue; /* ignore anonymous windows */
6749 :
6750 0 : if (sep == NULL)
6751 0 : appendContextKeyword(context, " WINDOW ",
6752 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6753 : else
6754 0 : appendStringInfoString(buf, sep);
6755 :
6756 0 : appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
6757 :
6758 0 : get_rule_windowspec(wc, query->targetList, context);
6759 :
6760 0 : sep = ", ";
6761 : }
6762 42 : }
6763 :
6764 : /*
6765 : * Display a window definition
6766 : */
6767 : static void
6768 42 : get_rule_windowspec(WindowClause *wc, List *targetList,
6769 : deparse_context *context)
6770 : {
6771 42 : StringInfo buf = context->buf;
6772 42 : bool needspace = false;
6773 : const char *sep;
6774 : ListCell *l;
6775 :
6776 42 : appendStringInfoChar(buf, '(');
6777 42 : if (wc->refname)
6778 : {
6779 0 : appendStringInfoString(buf, quote_identifier(wc->refname));
6780 0 : needspace = true;
6781 : }
6782 : /* partition clauses are always inherited, so only print if no refname */
6783 42 : if (wc->partitionClause && !wc->refname)
6784 : {
6785 0 : if (needspace)
6786 0 : appendStringInfoChar(buf, ' ');
6787 0 : appendStringInfoString(buf, "PARTITION BY ");
6788 0 : sep = "";
6789 0 : foreach(l, wc->partitionClause)
6790 : {
6791 0 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6792 :
6793 0 : appendStringInfoString(buf, sep);
6794 0 : get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
6795 : false, context);
6796 0 : sep = ", ";
6797 : }
6798 0 : needspace = true;
6799 : }
6800 : /* print ordering clause only if not inherited */
6801 42 : if (wc->orderClause && !wc->copiedOrder)
6802 : {
6803 42 : if (needspace)
6804 0 : appendStringInfoChar(buf, ' ');
6805 42 : appendStringInfoString(buf, "ORDER BY ");
6806 42 : get_rule_orderby(wc->orderClause, targetList, false, context);
6807 42 : needspace = true;
6808 : }
6809 : /* framing clause is never inherited, so print unless it's default */
6810 42 : if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
6811 : {
6812 42 : if (needspace)
6813 42 : appendStringInfoChar(buf, ' ');
6814 42 : if (wc->frameOptions & FRAMEOPTION_RANGE)
6815 6 : appendStringInfoString(buf, "RANGE ");
6816 36 : else if (wc->frameOptions & FRAMEOPTION_ROWS)
6817 30 : appendStringInfoString(buf, "ROWS ");
6818 6 : else if (wc->frameOptions & FRAMEOPTION_GROUPS)
6819 6 : appendStringInfoString(buf, "GROUPS ");
6820 : else
6821 : Assert(false);
6822 42 : if (wc->frameOptions & FRAMEOPTION_BETWEEN)
6823 42 : appendStringInfoString(buf, "BETWEEN ");
6824 42 : if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
6825 0 : appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
6826 42 : else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW)
6827 0 : appendStringInfoString(buf, "CURRENT ROW ");
6828 42 : else if (wc->frameOptions & FRAMEOPTION_START_OFFSET)
6829 : {
6830 42 : get_rule_expr(wc->startOffset, context, false);
6831 42 : if (wc->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
6832 42 : appendStringInfoString(buf, " PRECEDING ");
6833 0 : else if (wc->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
6834 0 : appendStringInfoString(buf, " FOLLOWING ");
6835 : else
6836 : Assert(false);
6837 : }
6838 : else
6839 : Assert(false);
6840 42 : if (wc->frameOptions & FRAMEOPTION_BETWEEN)
6841 : {
6842 42 : appendStringInfoString(buf, "AND ");
6843 42 : if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
6844 0 : appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
6845 42 : else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW)
6846 0 : appendStringInfoString(buf, "CURRENT ROW ");
6847 42 : else if (wc->frameOptions & FRAMEOPTION_END_OFFSET)
6848 : {
6849 42 : get_rule_expr(wc->endOffset, context, false);
6850 42 : if (wc->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
6851 0 : appendStringInfoString(buf, " PRECEDING ");
6852 42 : else if (wc->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
6853 42 : appendStringInfoString(buf, " FOLLOWING ");
6854 : else
6855 : Assert(false);
6856 : }
6857 : else
6858 : Assert(false);
6859 : }
6860 42 : if (wc->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
6861 6 : appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
6862 36 : else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
6863 6 : appendStringInfoString(buf, "EXCLUDE GROUP ");
6864 30 : else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES)
6865 6 : appendStringInfoString(buf, "EXCLUDE TIES ");
6866 : /* we will now have a trailing space; remove it */
6867 42 : buf->len--;
6868 : }
6869 42 : appendStringInfoChar(buf, ')');
6870 42 : }
6871 :
6872 : /* ----------
6873 : * get_insert_query_def - Parse back an INSERT parsetree
6874 : * ----------
6875 : */
6876 : static void
6877 340 : get_insert_query_def(Query *query, deparse_context *context)
6878 : {
6879 340 : StringInfo buf = context->buf;
6880 340 : RangeTblEntry *select_rte = NULL;
6881 340 : RangeTblEntry *values_rte = NULL;
6882 : RangeTblEntry *rte;
6883 : char *sep;
6884 : ListCell *l;
6885 : List *strippedexprs;
6886 :
6887 : /* Insert the WITH clause if given */
6888 340 : get_with_clause(query, context);
6889 :
6890 : /*
6891 : * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
6892 : * single RTE for the SELECT or VALUES. Plain VALUES has neither.
6893 : */
6894 1322 : foreach(l, query->rtable)
6895 : {
6896 982 : rte = (RangeTblEntry *) lfirst(l);
6897 :
6898 982 : if (rte->rtekind == RTE_SUBQUERY)
6899 : {
6900 50 : if (select_rte)
6901 0 : elog(ERROR, "too many subquery RTEs in INSERT");
6902 50 : select_rte = rte;
6903 : }
6904 :
6905 982 : if (rte->rtekind == RTE_VALUES)
6906 : {
6907 44 : if (values_rte)
6908 0 : elog(ERROR, "too many values RTEs in INSERT");
6909 44 : values_rte = rte;
6910 : }
6911 : }
6912 340 : if (select_rte && values_rte)
6913 0 : elog(ERROR, "both subquery and values RTEs in INSERT");
6914 :
6915 : /*
6916 : * Start the query with INSERT INTO relname
6917 : */
6918 340 : rte = rt_fetch(query->resultRelation, query->rtable);
6919 : Assert(rte->rtekind == RTE_RELATION);
6920 :
6921 340 : if (PRETTY_INDENT(context))
6922 : {
6923 340 : context->indentLevel += PRETTYINDENT_STD;
6924 340 : appendStringInfoChar(buf, ' ');
6925 : }
6926 340 : appendStringInfo(buf, "INSERT INTO %s",
6927 : generate_relation_name(rte->relid, NIL));
6928 :
6929 : /* Print the relation alias, if needed; INSERT requires explicit AS */
6930 340 : get_rte_alias(rte, query->resultRelation, true, context);
6931 :
6932 : /* always want a space here */
6933 340 : appendStringInfoChar(buf, ' ');
6934 :
6935 : /*
6936 : * Add the insert-column-names list. Any indirection decoration needed on
6937 : * the column names can be inferred from the top targetlist.
6938 : */
6939 340 : strippedexprs = NIL;
6940 340 : sep = "";
6941 340 : if (query->targetList)
6942 340 : appendStringInfoChar(buf, '(');
6943 1242 : foreach(l, query->targetList)
6944 : {
6945 902 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6946 :
6947 902 : if (tle->resjunk)
6948 0 : continue; /* ignore junk entries */
6949 :
6950 902 : appendStringInfoString(buf, sep);
6951 902 : sep = ", ";
6952 :
6953 : /*
6954 : * Put out name of target column; look in the catalogs, not at
6955 : * tle->resname, since resname will fail to track RENAME.
6956 : */
6957 902 : appendStringInfoString(buf,
6958 902 : quote_identifier(get_attname(rte->relid,
6959 902 : tle->resno,
6960 : false)));
6961 :
6962 : /*
6963 : * Print any indirection needed (subfields or subscripts), and strip
6964 : * off the top-level nodes representing the indirection assignments.
6965 : * Add the stripped expressions to strippedexprs. (If it's a
6966 : * single-VALUES statement, the stripped expressions are the VALUES to
6967 : * print below. Otherwise they're just Vars and not really
6968 : * interesting.)
6969 : */
6970 902 : strippedexprs = lappend(strippedexprs,
6971 902 : processIndirection((Node *) tle->expr,
6972 : context));
6973 : }
6974 340 : if (query->targetList)
6975 340 : appendStringInfoString(buf, ") ");
6976 :
6977 340 : if (query->override)
6978 : {
6979 0 : if (query->override == OVERRIDING_SYSTEM_VALUE)
6980 0 : appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
6981 0 : else if (query->override == OVERRIDING_USER_VALUE)
6982 0 : appendStringInfoString(buf, "OVERRIDING USER VALUE ");
6983 : }
6984 :
6985 340 : if (select_rte)
6986 : {
6987 : /* Add the SELECT */
6988 50 : get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
6989 : false,
6990 : context->prettyFlags, context->wrapColumn,
6991 : context->indentLevel);
6992 : }
6993 290 : else if (values_rte)
6994 : {
6995 : /* Add the multi-VALUES expression lists */
6996 44 : get_values_def(values_rte->values_lists, context);
6997 : }
6998 246 : else if (strippedexprs)
6999 : {
7000 : /* Add the single-VALUES expression list */
7001 246 : appendContextKeyword(context, "VALUES (",
7002 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7003 246 : get_rule_list_toplevel(strippedexprs, context, false);
7004 246 : appendStringInfoChar(buf, ')');
7005 : }
7006 : else
7007 : {
7008 : /* No expressions, so it must be DEFAULT VALUES */
7009 0 : appendStringInfoString(buf, "DEFAULT VALUES");
7010 : }
7011 :
7012 : /* Add ON CONFLICT if present */
7013 340 : if (query->onConflict)
7014 : {
7015 30 : OnConflictExpr *confl = query->onConflict;
7016 :
7017 30 : appendStringInfoString(buf, " ON CONFLICT");
7018 :
7019 30 : if (confl->arbiterElems)
7020 : {
7021 : /* Add the single-VALUES expression list */
7022 24 : appendStringInfoChar(buf, '(');
7023 24 : get_rule_expr((Node *) confl->arbiterElems, context, false);
7024 24 : appendStringInfoChar(buf, ')');
7025 :
7026 : /* Add a WHERE clause (for partial indexes) if given */
7027 24 : if (confl->arbiterWhere != NULL)
7028 : {
7029 : bool save_varprefix;
7030 :
7031 : /*
7032 : * Force non-prefixing of Vars, since parser assumes that they
7033 : * belong to target relation. WHERE clause does not use
7034 : * InferenceElem, so this is separately required.
7035 : */
7036 12 : save_varprefix = context->varprefix;
7037 12 : context->varprefix = false;
7038 :
7039 12 : appendContextKeyword(context, " WHERE ",
7040 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7041 12 : get_rule_expr(confl->arbiterWhere, context, false);
7042 :
7043 12 : context->varprefix = save_varprefix;
7044 : }
7045 : }
7046 6 : else if (OidIsValid(confl->constraint))
7047 : {
7048 0 : char *constraint = get_constraint_name(confl->constraint);
7049 :
7050 0 : if (!constraint)
7051 0 : elog(ERROR, "cache lookup failed for constraint %u",
7052 : confl->constraint);
7053 0 : appendStringInfo(buf, " ON CONSTRAINT %s",
7054 : quote_identifier(constraint));
7055 : }
7056 :
7057 30 : if (confl->action == ONCONFLICT_NOTHING)
7058 : {
7059 18 : appendStringInfoString(buf, " DO NOTHING");
7060 : }
7061 : else
7062 : {
7063 12 : appendStringInfoString(buf, " DO UPDATE SET ");
7064 : /* Deparse targetlist */
7065 12 : get_update_query_targetlist_def(query, confl->onConflictSet,
7066 : context, rte);
7067 :
7068 : /* Add a WHERE clause if given */
7069 12 : if (confl->onConflictWhere != NULL)
7070 : {
7071 12 : appendContextKeyword(context, " WHERE ",
7072 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7073 12 : get_rule_expr(confl->onConflictWhere, context, false);
7074 : }
7075 : }
7076 : }
7077 :
7078 : /* Add RETURNING if present */
7079 340 : if (query->returningList)
7080 78 : get_returning_clause(query, context);
7081 340 : }
7082 :
7083 :
7084 : /* ----------
7085 : * get_update_query_def - Parse back an UPDATE parsetree
7086 : * ----------
7087 : */
7088 : static void
7089 154 : get_update_query_def(Query *query, deparse_context *context)
7090 : {
7091 154 : StringInfo buf = context->buf;
7092 : RangeTblEntry *rte;
7093 :
7094 : /* Insert the WITH clause if given */
7095 154 : get_with_clause(query, context);
7096 :
7097 : /*
7098 : * Start the query with UPDATE relname SET
7099 : */
7100 154 : rte = rt_fetch(query->resultRelation, query->rtable);
7101 : Assert(rte->rtekind == RTE_RELATION);
7102 154 : if (PRETTY_INDENT(context))
7103 : {
7104 154 : appendStringInfoChar(buf, ' ');
7105 154 : context->indentLevel += PRETTYINDENT_STD;
7106 : }
7107 308 : appendStringInfo(buf, "UPDATE %s%s",
7108 154 : only_marker(rte),
7109 : generate_relation_name(rte->relid, NIL));
7110 :
7111 : /* Print the relation alias, if needed */
7112 154 : get_rte_alias(rte, query->resultRelation, false, context);
7113 :
7114 154 : appendStringInfoString(buf, " SET ");
7115 :
7116 : /* Deparse targetlist */
7117 154 : get_update_query_targetlist_def(query, query->targetList, context, rte);
7118 :
7119 : /* Add the FROM clause if needed */
7120 154 : get_from_clause(query, " FROM ", context);
7121 :
7122 : /* Add a WHERE clause if given */
7123 154 : if (query->jointree->quals != NULL)
7124 : {
7125 114 : appendContextKeyword(context, " WHERE ",
7126 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7127 114 : get_rule_expr(query->jointree->quals, context, false);
7128 : }
7129 :
7130 : /* Add RETURNING if present */
7131 154 : if (query->returningList)
7132 46 : get_returning_clause(query, context);
7133 154 : }
7134 :
7135 :
7136 : /* ----------
7137 : * get_update_query_targetlist_def - Parse back an UPDATE targetlist
7138 : * ----------
7139 : */
7140 : static void
7141 190 : get_update_query_targetlist_def(Query *query, List *targetList,
7142 : deparse_context *context, RangeTblEntry *rte)
7143 : {
7144 190 : StringInfo buf = context->buf;
7145 : ListCell *l;
7146 : ListCell *next_ma_cell;
7147 : int remaining_ma_columns;
7148 : const char *sep;
7149 : SubLink *cur_ma_sublink;
7150 : List *ma_sublinks;
7151 :
7152 : /*
7153 : * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
7154 : * into a list. We expect them to appear, in ID order, in resjunk tlist
7155 : * entries.
7156 : */
7157 190 : ma_sublinks = NIL;
7158 190 : if (query->hasSubLinks) /* else there can't be any */
7159 : {
7160 42 : foreach(l, targetList)
7161 : {
7162 30 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7163 :
7164 30 : if (tle->resjunk && IsA(tle->expr, SubLink))
7165 : {
7166 6 : SubLink *sl = (SubLink *) tle->expr;
7167 :
7168 6 : if (sl->subLinkType == MULTIEXPR_SUBLINK)
7169 : {
7170 6 : ma_sublinks = lappend(ma_sublinks, sl);
7171 : Assert(sl->subLinkId == list_length(ma_sublinks));
7172 : }
7173 : }
7174 : }
7175 : }
7176 190 : next_ma_cell = list_head(ma_sublinks);
7177 190 : cur_ma_sublink = NULL;
7178 190 : remaining_ma_columns = 0;
7179 :
7180 : /* Add the comma separated list of 'attname = value' */
7181 190 : sep = "";
7182 488 : foreach(l, targetList)
7183 : {
7184 298 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7185 : Node *expr;
7186 :
7187 298 : if (tle->resjunk)
7188 6 : continue; /* ignore junk entries */
7189 :
7190 : /* Emit separator (OK whether we're in multiassignment or not) */
7191 292 : appendStringInfoString(buf, sep);
7192 292 : sep = ", ";
7193 :
7194 : /*
7195 : * Check to see if we're starting a multiassignment group: if so,
7196 : * output a left paren.
7197 : */
7198 292 : if (next_ma_cell != NULL && cur_ma_sublink == NULL)
7199 : {
7200 : /*
7201 : * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
7202 : * Param. That could be buried under FieldStores and
7203 : * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
7204 : * and underneath those there could be an implicit type coercion.
7205 : * Because we would ignore implicit type coercions anyway, we
7206 : * don't need to be as careful as processIndirection() is about
7207 : * descending past implicit CoerceToDomains.
7208 : */
7209 6 : expr = (Node *) tle->expr;
7210 12 : while (expr)
7211 : {
7212 12 : if (IsA(expr, FieldStore))
7213 : {
7214 0 : FieldStore *fstore = (FieldStore *) expr;
7215 :
7216 0 : expr = (Node *) linitial(fstore->newvals);
7217 : }
7218 12 : else if (IsA(expr, SubscriptingRef))
7219 : {
7220 6 : SubscriptingRef *sbsref = (SubscriptingRef *) expr;
7221 :
7222 6 : if (sbsref->refassgnexpr == NULL)
7223 0 : break;
7224 :
7225 6 : expr = (Node *) sbsref->refassgnexpr;
7226 : }
7227 6 : else if (IsA(expr, CoerceToDomain))
7228 : {
7229 0 : CoerceToDomain *cdomain = (CoerceToDomain *) expr;
7230 :
7231 0 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
7232 0 : break;
7233 0 : expr = (Node *) cdomain->arg;
7234 : }
7235 : else
7236 6 : break;
7237 : }
7238 6 : expr = strip_implicit_coercions(expr);
7239 :
7240 6 : if (expr && IsA(expr, Param) &&
7241 6 : ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
7242 : {
7243 6 : cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
7244 6 : next_ma_cell = lnext(ma_sublinks, next_ma_cell);
7245 6 : remaining_ma_columns = count_nonjunk_tlist_entries(((Query *) cur_ma_sublink->subselect)->targetList);
7246 : Assert(((Param *) expr)->paramid ==
7247 : ((cur_ma_sublink->subLinkId << 16) | 1));
7248 6 : appendStringInfoChar(buf, '(');
7249 : }
7250 : }
7251 :
7252 : /*
7253 : * Put out name of target column; look in the catalogs, not at
7254 : * tle->resname, since resname will fail to track RENAME.
7255 : */
7256 292 : appendStringInfoString(buf,
7257 292 : quote_identifier(get_attname(rte->relid,
7258 292 : tle->resno,
7259 : false)));
7260 :
7261 : /*
7262 : * Print any indirection needed (subfields or subscripts), and strip
7263 : * off the top-level nodes representing the indirection assignments.
7264 : */
7265 292 : expr = processIndirection((Node *) tle->expr, context);
7266 :
7267 : /*
7268 : * If we're in a multiassignment, skip printing anything more, unless
7269 : * this is the last column; in which case, what we print should be the
7270 : * sublink, not the Param.
7271 : */
7272 292 : if (cur_ma_sublink != NULL)
7273 : {
7274 18 : if (--remaining_ma_columns > 0)
7275 12 : continue; /* not the last column of multiassignment */
7276 6 : appendStringInfoChar(buf, ')');
7277 6 : expr = (Node *) cur_ma_sublink;
7278 6 : cur_ma_sublink = NULL;
7279 : }
7280 :
7281 280 : appendStringInfoString(buf, " = ");
7282 :
7283 280 : get_rule_expr(expr, context, false);
7284 : }
7285 190 : }
7286 :
7287 :
7288 : /* ----------
7289 : * get_delete_query_def - Parse back a DELETE parsetree
7290 : * ----------
7291 : */
7292 : static void
7293 76 : get_delete_query_def(Query *query, deparse_context *context)
7294 : {
7295 76 : StringInfo buf = context->buf;
7296 : RangeTblEntry *rte;
7297 :
7298 : /* Insert the WITH clause if given */
7299 76 : get_with_clause(query, context);
7300 :
7301 : /*
7302 : * Start the query with DELETE FROM relname
7303 : */
7304 76 : rte = rt_fetch(query->resultRelation, query->rtable);
7305 : Assert(rte->rtekind == RTE_RELATION);
7306 76 : if (PRETTY_INDENT(context))
7307 : {
7308 76 : appendStringInfoChar(buf, ' ');
7309 76 : context->indentLevel += PRETTYINDENT_STD;
7310 : }
7311 152 : appendStringInfo(buf, "DELETE FROM %s%s",
7312 76 : only_marker(rte),
7313 : generate_relation_name(rte->relid, NIL));
7314 :
7315 : /* Print the relation alias, if needed */
7316 76 : get_rte_alias(rte, query->resultRelation, false, context);
7317 :
7318 : /* Add the USING clause if given */
7319 76 : get_from_clause(query, " USING ", context);
7320 :
7321 : /* Add a WHERE clause if given */
7322 76 : if (query->jointree->quals != NULL)
7323 : {
7324 76 : appendContextKeyword(context, " WHERE ",
7325 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7326 76 : get_rule_expr(query->jointree->quals, context, false);
7327 : }
7328 :
7329 : /* Add RETURNING if present */
7330 76 : if (query->returningList)
7331 16 : get_returning_clause(query, context);
7332 76 : }
7333 :
7334 :
7335 : /* ----------
7336 : * get_merge_query_def - Parse back a MERGE parsetree
7337 : * ----------
7338 : */
7339 : static void
7340 12 : get_merge_query_def(Query *query, deparse_context *context)
7341 : {
7342 12 : StringInfo buf = context->buf;
7343 : RangeTblEntry *rte;
7344 : ListCell *lc;
7345 : bool haveNotMatchedBySource;
7346 :
7347 : /* Insert the WITH clause if given */
7348 12 : get_with_clause(query, context);
7349 :
7350 : /*
7351 : * Start the query with MERGE INTO relname
7352 : */
7353 12 : rte = rt_fetch(query->resultRelation, query->rtable);
7354 : Assert(rte->rtekind == RTE_RELATION);
7355 12 : if (PRETTY_INDENT(context))
7356 : {
7357 12 : appendStringInfoChar(buf, ' ');
7358 12 : context->indentLevel += PRETTYINDENT_STD;
7359 : }
7360 24 : appendStringInfo(buf, "MERGE INTO %s%s",
7361 12 : only_marker(rte),
7362 : generate_relation_name(rte->relid, NIL));
7363 :
7364 : /* Print the relation alias, if needed */
7365 12 : get_rte_alias(rte, query->resultRelation, false, context);
7366 :
7367 : /* Print the source relation and join clause */
7368 12 : get_from_clause(query, " USING ", context);
7369 12 : appendContextKeyword(context, " ON ",
7370 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7371 12 : get_rule_expr(query->mergeJoinCondition, context, false);
7372 :
7373 : /*
7374 : * Test for any NOT MATCHED BY SOURCE actions. If there are none, then
7375 : * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
7376 : * SQL standard. Otherwise, we have a non-SQL-standard query, so output
7377 : * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
7378 : * more explicit.
7379 : */
7380 12 : haveNotMatchedBySource = false;
7381 84 : foreach(lc, query->mergeActionList)
7382 : {
7383 78 : MergeAction *action = lfirst_node(MergeAction, lc);
7384 :
7385 78 : if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
7386 : {
7387 6 : haveNotMatchedBySource = true;
7388 6 : break;
7389 : }
7390 : }
7391 :
7392 : /* Print each merge action */
7393 90 : foreach(lc, query->mergeActionList)
7394 : {
7395 78 : MergeAction *action = lfirst_node(MergeAction, lc);
7396 :
7397 78 : appendContextKeyword(context, " WHEN ",
7398 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7399 78 : switch (action->matchKind)
7400 : {
7401 36 : case MERGE_WHEN_MATCHED:
7402 36 : appendStringInfoString(buf, "MATCHED");
7403 36 : break;
7404 6 : case MERGE_WHEN_NOT_MATCHED_BY_SOURCE:
7405 6 : appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
7406 6 : break;
7407 36 : case MERGE_WHEN_NOT_MATCHED_BY_TARGET:
7408 36 : if (haveNotMatchedBySource)
7409 6 : appendStringInfoString(buf, "NOT MATCHED BY TARGET");
7410 : else
7411 30 : appendStringInfoString(buf, "NOT MATCHED");
7412 36 : break;
7413 0 : default:
7414 0 : elog(ERROR, "unrecognized matchKind: %d",
7415 : (int) action->matchKind);
7416 : }
7417 :
7418 78 : if (action->qual)
7419 : {
7420 48 : appendContextKeyword(context, " AND ",
7421 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7422 48 : get_rule_expr(action->qual, context, false);
7423 : }
7424 78 : appendContextKeyword(context, " THEN ",
7425 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7426 :
7427 78 : if (action->commandType == CMD_INSERT)
7428 : {
7429 : /* This generally matches get_insert_query_def() */
7430 36 : List *strippedexprs = NIL;
7431 36 : const char *sep = "";
7432 : ListCell *lc2;
7433 :
7434 36 : appendStringInfoString(buf, "INSERT");
7435 :
7436 36 : if (action->targetList)
7437 30 : appendStringInfoString(buf, " (");
7438 102 : foreach(lc2, action->targetList)
7439 : {
7440 66 : TargetEntry *tle = (TargetEntry *) lfirst(lc2);
7441 :
7442 : Assert(!tle->resjunk);
7443 :
7444 66 : appendStringInfoString(buf, sep);
7445 66 : sep = ", ";
7446 :
7447 66 : appendStringInfoString(buf,
7448 66 : quote_identifier(get_attname(rte->relid,
7449 66 : tle->resno,
7450 : false)));
7451 66 : strippedexprs = lappend(strippedexprs,
7452 66 : processIndirection((Node *) tle->expr,
7453 : context));
7454 : }
7455 36 : if (action->targetList)
7456 30 : appendStringInfoChar(buf, ')');
7457 :
7458 36 : if (action->override)
7459 : {
7460 6 : if (action->override == OVERRIDING_SYSTEM_VALUE)
7461 0 : appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
7462 6 : else if (action->override == OVERRIDING_USER_VALUE)
7463 6 : appendStringInfoString(buf, " OVERRIDING USER VALUE");
7464 : }
7465 :
7466 36 : if (strippedexprs)
7467 : {
7468 30 : appendContextKeyword(context, " VALUES (",
7469 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);
7470 30 : get_rule_list_toplevel(strippedexprs, context, false);
7471 30 : appendStringInfoChar(buf, ')');
7472 : }
7473 : else
7474 6 : appendStringInfoString(buf, " DEFAULT VALUES");
7475 : }
7476 42 : else if (action->commandType == CMD_UPDATE)
7477 : {
7478 24 : appendStringInfoString(buf, "UPDATE SET ");
7479 24 : get_update_query_targetlist_def(query, action->targetList,
7480 : context, rte);
7481 : }
7482 18 : else if (action->commandType == CMD_DELETE)
7483 12 : appendStringInfoString(buf, "DELETE");
7484 6 : else if (action->commandType == CMD_NOTHING)
7485 6 : appendStringInfoString(buf, "DO NOTHING");
7486 : }
7487 :
7488 : /* Add RETURNING if present */
7489 12 : if (query->returningList)
7490 6 : get_returning_clause(query, context);
7491 12 : }
7492 :
7493 :
7494 : /* ----------
7495 : * get_utility_query_def - Parse back a UTILITY parsetree
7496 : * ----------
7497 : */
7498 : static void
7499 16 : get_utility_query_def(Query *query, deparse_context *context)
7500 : {
7501 16 : StringInfo buf = context->buf;
7502 :
7503 16 : if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7504 16 : {
7505 16 : NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7506 :
7507 16 : appendContextKeyword(context, "",
7508 : 0, PRETTYINDENT_STD, 1);
7509 16 : appendStringInfo(buf, "NOTIFY %s",
7510 16 : quote_identifier(stmt->conditionname));
7511 16 : if (stmt->payload)
7512 : {
7513 0 : appendStringInfoString(buf, ", ");
7514 0 : simple_quote_literal(buf, stmt->payload);
7515 : }
7516 : }
7517 : else
7518 : {
7519 : /* Currently only NOTIFY utility commands can appear in rules */
7520 0 : elog(ERROR, "unexpected utility statement type");
7521 : }
7522 16 : }
7523 :
7524 : /*
7525 : * Display a Var appropriately.
7526 : *
7527 : * In some cases (currently only when recursing into an unnamed join)
7528 : * the Var's varlevelsup has to be interpreted with respect to a context
7529 : * above the current one; levelsup indicates the offset.
7530 : *
7531 : * If istoplevel is true, the Var is at the top level of a SELECT's
7532 : * targetlist, which means we need special treatment of whole-row Vars.
7533 : * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
7534 : * dirty hack to prevent "tab.*" from being expanded into multiple columns.
7535 : * (The parser will strip the useless coercion, so no inefficiency is added in
7536 : * dump and reload.) We used to print just "tab" in such cases, but that is
7537 : * ambiguous and will yield the wrong result if "tab" is also a plain column
7538 : * name in the query.
7539 : *
7540 : * Returns the attname of the Var, or NULL if the Var has no attname (because
7541 : * it is a whole-row Var or a subplan output reference).
7542 : */
7543 : static char *
7544 160246 : get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
7545 : {
7546 160246 : StringInfo buf = context->buf;
7547 : RangeTblEntry *rte;
7548 : AttrNumber attnum;
7549 : int netlevelsup;
7550 : deparse_namespace *dpns;
7551 : int varno;
7552 : AttrNumber varattno;
7553 : deparse_columns *colinfo;
7554 : char *refname;
7555 : char *attname;
7556 : bool need_prefix;
7557 :
7558 : /* Find appropriate nesting depth */
7559 160246 : netlevelsup = var->varlevelsup + levelsup;
7560 160246 : if (netlevelsup >= list_length(context->namespaces))
7561 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
7562 : var->varlevelsup, levelsup);
7563 160246 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7564 : netlevelsup);
7565 :
7566 : /*
7567 : * If we have a syntactic referent for the Var, and we're working from a
7568 : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
7569 : * on the semantic referent. (Forcing use of the semantic referent when
7570 : * printing plan trees is a design choice that's perhaps more motivated by
7571 : * backwards compatibility than anything else. But it does have the
7572 : * advantage of making plans more explicit.)
7573 : */
7574 160246 : if (var->varnosyn > 0 && dpns->plan == NULL)
7575 : {
7576 35916 : varno = var->varnosyn;
7577 35916 : varattno = var->varattnosyn;
7578 : }
7579 : else
7580 : {
7581 124330 : varno = var->varno;
7582 124330 : varattno = var->varattno;
7583 : }
7584 :
7585 : /*
7586 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
7587 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
7588 : * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
7589 : * find the aliases previously assigned for this RTE.
7590 : */
7591 160246 : if (varno >= 1 && varno <= list_length(dpns->rtable))
7592 : {
7593 : /*
7594 : * We might have been asked to map child Vars to some parent relation.
7595 : */
7596 120670 : if (context->appendparents && dpns->appendrels)
7597 : {
7598 3668 : int pvarno = varno;
7599 3668 : AttrNumber pvarattno = varattno;
7600 3668 : AppendRelInfo *appinfo = dpns->appendrels[pvarno];
7601 3668 : bool found = false;
7602 :
7603 : /* Only map up to inheritance parents, not UNION ALL appendrels */
7604 7458 : while (appinfo &&
7605 4080 : rt_fetch(appinfo->parent_relid,
7606 4080 : dpns->rtable)->rtekind == RTE_RELATION)
7607 : {
7608 3790 : found = false;
7609 3790 : if (pvarattno > 0) /* system columns stay as-is */
7610 : {
7611 3516 : if (pvarattno > appinfo->num_child_cols)
7612 0 : break; /* safety check */
7613 3516 : pvarattno = appinfo->parent_colnos[pvarattno - 1];
7614 3516 : if (pvarattno == 0)
7615 0 : break; /* Var is local to child */
7616 : }
7617 :
7618 3790 : pvarno = appinfo->parent_relid;
7619 3790 : found = true;
7620 :
7621 : /* If the parent is itself a child, continue up. */
7622 : Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
7623 3790 : appinfo = dpns->appendrels[pvarno];
7624 : }
7625 :
7626 : /*
7627 : * If we found an ancestral rel, and that rel is included in
7628 : * appendparents, print that column not the original one.
7629 : */
7630 3668 : if (found && bms_is_member(pvarno, context->appendparents))
7631 : {
7632 3024 : varno = pvarno;
7633 3024 : varattno = pvarattno;
7634 : }
7635 : }
7636 :
7637 120670 : rte = rt_fetch(varno, dpns->rtable);
7638 :
7639 : /* might be returning old/new column value */
7640 120670 : if (var->varreturningtype == VAR_RETURNING_OLD)
7641 416 : refname = dpns->ret_old_alias;
7642 120254 : else if (var->varreturningtype == VAR_RETURNING_NEW)
7643 414 : refname = dpns->ret_new_alias;
7644 : else
7645 119840 : refname = (char *) list_nth(dpns->rtable_names, varno - 1);
7646 :
7647 120670 : colinfo = deparse_columns_fetch(varno, dpns);
7648 120670 : attnum = varattno;
7649 : }
7650 : else
7651 : {
7652 39576 : resolve_special_varno((Node *) var, context,
7653 : get_special_variable, NULL);
7654 39576 : return NULL;
7655 : }
7656 :
7657 : /*
7658 : * The planner will sometimes emit Vars referencing resjunk elements of a
7659 : * subquery's target list (this is currently only possible if it chooses
7660 : * to generate a "physical tlist" for a SubqueryScan or CteScan node).
7661 : * Although we prefer to print subquery-referencing Vars using the
7662 : * subquery's alias, that's not possible for resjunk items since they have
7663 : * no alias. So in that case, drill down to the subplan and print the
7664 : * contents of the referenced tlist item. This works because in a plan
7665 : * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
7666 : * we'll have set dpns->inner_plan to reference the child plan node.
7667 : */
7668 124888 : if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
7669 4218 : attnum > list_length(rte->eref->colnames) &&
7670 2 : dpns->inner_plan)
7671 : {
7672 : TargetEntry *tle;
7673 : deparse_namespace save_dpns;
7674 :
7675 2 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
7676 2 : if (!tle)
7677 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
7678 : attnum, rte->eref->aliasname);
7679 :
7680 : Assert(netlevelsup == 0);
7681 2 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7682 :
7683 : /*
7684 : * Force parentheses because our caller probably assumed a Var is a
7685 : * simple expression.
7686 : */
7687 2 : if (!IsA(tle->expr, Var))
7688 0 : appendStringInfoChar(buf, '(');
7689 2 : get_rule_expr((Node *) tle->expr, context, true);
7690 2 : if (!IsA(tle->expr, Var))
7691 0 : appendStringInfoChar(buf, ')');
7692 :
7693 2 : pop_child_plan(dpns, &save_dpns);
7694 2 : return NULL;
7695 : }
7696 :
7697 : /*
7698 : * If it's an unnamed join, look at the expansion of the alias variable.
7699 : * If it's a simple reference to one of the input vars, then recursively
7700 : * print the name of that var instead. When it's not a simple reference,
7701 : * we have to just print the unqualified join column name. (This can only
7702 : * happen with "dangerous" merged columns in a JOIN USING; we took pains
7703 : * previously to make the unqualified column name unique in such cases.)
7704 : *
7705 : * This wouldn't work in decompiling plan trees, because we don't store
7706 : * joinaliasvars lists after planning; but a plan tree should never
7707 : * contain a join alias variable.
7708 : */
7709 120668 : if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
7710 : {
7711 96 : if (rte->joinaliasvars == NIL)
7712 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
7713 96 : if (attnum > 0)
7714 : {
7715 : Var *aliasvar;
7716 :
7717 96 : aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
7718 : /* we intentionally don't strip implicit coercions here */
7719 96 : if (aliasvar && IsA(aliasvar, Var))
7720 : {
7721 0 : return get_variable(aliasvar, var->varlevelsup + levelsup,
7722 : istoplevel, context);
7723 : }
7724 : }
7725 :
7726 : /*
7727 : * Unnamed join has no refname. (Note: since it's unnamed, there is
7728 : * no way the user could have referenced it to create a whole-row Var
7729 : * for it. So we don't have to cover that case below.)
7730 : */
7731 : Assert(refname == NULL);
7732 : }
7733 :
7734 120668 : if (attnum == InvalidAttrNumber)
7735 920 : attname = NULL;
7736 119748 : else if (attnum > 0)
7737 : {
7738 : /* Get column name to use from the colinfo struct */
7739 117952 : if (attnum > colinfo->num_cols)
7740 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
7741 : attnum, rte->eref->aliasname);
7742 117952 : attname = colinfo->colnames[attnum - 1];
7743 :
7744 : /*
7745 : * If we find a Var referencing a dropped column, it seems better to
7746 : * print something (anything) than to fail. In general this should
7747 : * not happen, but it used to be possible for some cases involving
7748 : * functions returning named composite types, and perhaps there are
7749 : * still bugs out there.
7750 : */
7751 117952 : if (attname == NULL)
7752 6 : attname = "?dropped?column?";
7753 : }
7754 : else
7755 : {
7756 : /* System column - name is fixed, get it from the catalog */
7757 1796 : attname = get_rte_attribute_name(rte, attnum);
7758 : }
7759 :
7760 181080 : need_prefix = (context->varprefix || attname == NULL ||
7761 60412 : var->varreturningtype != VAR_RETURNING_DEFAULT);
7762 :
7763 : /*
7764 : * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
7765 : * clause, we may need to add a table-name prefix to prevent
7766 : * findTargetlistEntrySQL92 from misinterpreting the name as an
7767 : * output-column name. To avoid cluttering the output with unnecessary
7768 : * prefixes, do so only if there is a name match to a SELECT tlist item
7769 : * that is different from the Var.
7770 : */
7771 120668 : if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
7772 : {
7773 224 : int colno = 0;
7774 :
7775 874 : foreach_node(TargetEntry, tle, context->targetList)
7776 : {
7777 : char *colname;
7778 :
7779 438 : if (tle->resjunk)
7780 0 : continue; /* ignore junk entries */
7781 438 : colno++;
7782 :
7783 : /* This must match colname-choosing logic in get_target_list() */
7784 438 : if (context->resultDesc && colno <= context->resultDesc->natts)
7785 438 : colname = NameStr(TupleDescAttr(context->resultDesc,
7786 : colno - 1)->attname);
7787 : else
7788 0 : colname = tle->resname;
7789 :
7790 438 : if (colname && strcmp(colname, attname) == 0 &&
7791 152 : !equal(var, tle->expr))
7792 : {
7793 12 : need_prefix = true;
7794 12 : break;
7795 : }
7796 : }
7797 : }
7798 :
7799 120668 : if (refname && need_prefix)
7800 : {
7801 60110 : appendStringInfoString(buf, quote_identifier(refname));
7802 60110 : appendStringInfoChar(buf, '.');
7803 : }
7804 120668 : if (attname)
7805 119748 : appendStringInfoString(buf, quote_identifier(attname));
7806 : else
7807 : {
7808 920 : appendStringInfoChar(buf, '*');
7809 920 : if (istoplevel)
7810 84 : appendStringInfo(buf, "::%s",
7811 : format_type_with_typemod(var->vartype,
7812 : var->vartypmod));
7813 : }
7814 :
7815 120668 : return attname;
7816 : }
7817 :
7818 : /*
7819 : * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
7820 : * routine is actually a callback for resolve_special_varno, which handles
7821 : * finding the correct TargetEntry. We get the expression contained in that
7822 : * TargetEntry and just need to deparse it, a job we can throw back on
7823 : * get_rule_expr.
7824 : */
7825 : static void
7826 39576 : get_special_variable(Node *node, deparse_context *context, void *callback_arg)
7827 : {
7828 39576 : StringInfo buf = context->buf;
7829 :
7830 : /*
7831 : * For a non-Var referent, force parentheses because our caller probably
7832 : * assumed a Var is a simple expression.
7833 : */
7834 39576 : if (!IsA(node, Var))
7835 3458 : appendStringInfoChar(buf, '(');
7836 39576 : get_rule_expr(node, context, true);
7837 39576 : if (!IsA(node, Var))
7838 3458 : appendStringInfoChar(buf, ')');
7839 39576 : }
7840 :
7841 : /*
7842 : * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
7843 : * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
7844 : * invoke the callback provided.
7845 : */
7846 : static void
7847 108788 : resolve_special_varno(Node *node, deparse_context *context,
7848 : rsv_callback callback, void *callback_arg)
7849 : {
7850 : Var *var;
7851 : deparse_namespace *dpns;
7852 :
7853 : /* This function is recursive, so let's be paranoid. */
7854 108788 : check_stack_depth();
7855 :
7856 : /* If it's not a Var, invoke the callback. */
7857 108788 : if (!IsA(node, Var))
7858 : {
7859 3718 : (*callback) (node, context, callback_arg);
7860 3718 : return;
7861 : }
7862 :
7863 : /* Find appropriate nesting depth */
7864 105070 : var = (Var *) node;
7865 105070 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7866 105070 : var->varlevelsup);
7867 :
7868 : /*
7869 : * If varno is special, recurse. (Don't worry about varnosyn; if we're
7870 : * here, we already decided not to use that.)
7871 : */
7872 105070 : if (var->varno == OUTER_VAR && dpns->outer_tlist)
7873 : {
7874 : TargetEntry *tle;
7875 : deparse_namespace save_dpns;
7876 : Bitmapset *save_appendparents;
7877 :
7878 51430 : tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
7879 51430 : if (!tle)
7880 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
7881 :
7882 : /*
7883 : * If we're descending to the first child of an Append or MergeAppend,
7884 : * update appendparents. This will affect deparsing of all Vars
7885 : * appearing within the eventually-resolved subexpression.
7886 : */
7887 51430 : save_appendparents = context->appendparents;
7888 :
7889 51430 : if (IsA(dpns->plan, Append))
7890 4030 : context->appendparents = bms_union(context->appendparents,
7891 4030 : ((Append *) dpns->plan)->apprelids);
7892 47400 : else if (IsA(dpns->plan, MergeAppend))
7893 560 : context->appendparents = bms_union(context->appendparents,
7894 560 : ((MergeAppend *) dpns->plan)->apprelids);
7895 :
7896 51430 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
7897 51430 : resolve_special_varno((Node *) tle->expr, context,
7898 : callback, callback_arg);
7899 51430 : pop_child_plan(dpns, &save_dpns);
7900 51430 : context->appendparents = save_appendparents;
7901 51430 : return;
7902 : }
7903 53640 : else if (var->varno == INNER_VAR && dpns->inner_tlist)
7904 : {
7905 : TargetEntry *tle;
7906 : deparse_namespace save_dpns;
7907 :
7908 12794 : tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
7909 12794 : if (!tle)
7910 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
7911 :
7912 12794 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7913 12794 : resolve_special_varno((Node *) tle->expr, context,
7914 : callback, callback_arg);
7915 12794 : pop_child_plan(dpns, &save_dpns);
7916 12794 : return;
7917 : }
7918 40846 : else if (var->varno == INDEX_VAR && dpns->index_tlist)
7919 : {
7920 : TargetEntry *tle;
7921 :
7922 4728 : tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
7923 4728 : if (!tle)
7924 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
7925 :
7926 4728 : resolve_special_varno((Node *) tle->expr, context,
7927 : callback, callback_arg);
7928 4728 : return;
7929 : }
7930 36118 : else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
7931 0 : elog(ERROR, "bogus varno: %d", var->varno);
7932 :
7933 : /* Not special. Just invoke the callback. */
7934 36118 : (*callback) (node, context, callback_arg);
7935 : }
7936 :
7937 : /*
7938 : * Get the name of a field of an expression of composite type. The
7939 : * expression is usually a Var, but we handle other cases too.
7940 : *
7941 : * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
7942 : *
7943 : * This is fairly straightforward when the expression has a named composite
7944 : * type; we need only look up the type in the catalogs. However, the type
7945 : * could also be RECORD. Since no actual table or view column is allowed to
7946 : * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
7947 : * or to a subquery output. We drill down to find the ultimate defining
7948 : * expression and attempt to infer the field name from it. We ereport if we
7949 : * can't determine the name.
7950 : *
7951 : * Similarly, a PARAM of type RECORD has to refer to some expression of
7952 : * a determinable composite type.
7953 : */
7954 : static const char *
7955 1304 : get_name_for_var_field(Var *var, int fieldno,
7956 : int levelsup, deparse_context *context)
7957 : {
7958 : RangeTblEntry *rte;
7959 : AttrNumber attnum;
7960 : int netlevelsup;
7961 : deparse_namespace *dpns;
7962 : int varno;
7963 : AttrNumber varattno;
7964 : TupleDesc tupleDesc;
7965 : Node *expr;
7966 :
7967 : /*
7968 : * If it's a RowExpr that was expanded from a whole-row Var, use the
7969 : * column names attached to it. (We could let get_expr_result_tupdesc()
7970 : * handle this, but it's much cheaper to just pull out the name we need.)
7971 : */
7972 1304 : if (IsA(var, RowExpr))
7973 : {
7974 36 : RowExpr *r = (RowExpr *) var;
7975 :
7976 36 : if (fieldno > 0 && fieldno <= list_length(r->colnames))
7977 36 : return strVal(list_nth(r->colnames, fieldno - 1));
7978 : }
7979 :
7980 : /*
7981 : * If it's a Param of type RECORD, try to find what the Param refers to.
7982 : */
7983 1268 : if (IsA(var, Param))
7984 : {
7985 18 : Param *param = (Param *) var;
7986 : ListCell *ancestor_cell;
7987 :
7988 18 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
7989 18 : if (expr)
7990 : {
7991 : /* Found a match, so recurse to decipher the field name */
7992 : deparse_namespace save_dpns;
7993 : const char *result;
7994 :
7995 18 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
7996 18 : result = get_name_for_var_field((Var *) expr, fieldno,
7997 : 0, context);
7998 18 : pop_ancestor_plan(dpns, &save_dpns);
7999 18 : return result;
8000 : }
8001 : }
8002 :
8003 : /*
8004 : * If it's a Var of type RECORD, we have to find what the Var refers to;
8005 : * if not, we can use get_expr_result_tupdesc().
8006 : */
8007 1250 : if (!IsA(var, Var) ||
8008 1170 : var->vartype != RECORDOID)
8009 : {
8010 1004 : tupleDesc = get_expr_result_tupdesc((Node *) var, false);
8011 : /* Got the tupdesc, so we can extract the field name */
8012 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
8013 1004 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
8014 : }
8015 :
8016 : /* Find appropriate nesting depth */
8017 246 : netlevelsup = var->varlevelsup + levelsup;
8018 246 : if (netlevelsup >= list_length(context->namespaces))
8019 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
8020 : var->varlevelsup, levelsup);
8021 246 : dpns = (deparse_namespace *) list_nth(context->namespaces,
8022 : netlevelsup);
8023 :
8024 : /*
8025 : * If we have a syntactic referent for the Var, and we're working from a
8026 : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8027 : * on the semantic referent. (See comments in get_variable().)
8028 : */
8029 246 : if (var->varnosyn > 0 && dpns->plan == NULL)
8030 : {
8031 96 : varno = var->varnosyn;
8032 96 : varattno = var->varattnosyn;
8033 : }
8034 : else
8035 : {
8036 150 : varno = var->varno;
8037 150 : varattno = var->varattno;
8038 : }
8039 :
8040 : /*
8041 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
8042 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8043 : * down into the subplans, or INDEX_VAR, which is resolved similarly.
8044 : *
8045 : * Note: unlike get_variable and resolve_special_varno, we need not worry
8046 : * about inheritance mapping: a child Var should have the same datatype as
8047 : * its parent, and here we're really only interested in the Var's type.
8048 : */
8049 246 : if (varno >= 1 && varno <= list_length(dpns->rtable))
8050 : {
8051 168 : rte = rt_fetch(varno, dpns->rtable);
8052 168 : attnum = varattno;
8053 : }
8054 78 : else if (varno == OUTER_VAR && dpns->outer_tlist)
8055 : {
8056 : TargetEntry *tle;
8057 : deparse_namespace save_dpns;
8058 : const char *result;
8059 :
8060 60 : tle = get_tle_by_resno(dpns->outer_tlist, varattno);
8061 60 : if (!tle)
8062 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
8063 :
8064 : Assert(netlevelsup == 0);
8065 60 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8066 :
8067 60 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8068 : levelsup, context);
8069 :
8070 60 : pop_child_plan(dpns, &save_dpns);
8071 60 : return result;
8072 : }
8073 18 : else if (varno == INNER_VAR && dpns->inner_tlist)
8074 : {
8075 : TargetEntry *tle;
8076 : deparse_namespace save_dpns;
8077 : const char *result;
8078 :
8079 18 : tle = get_tle_by_resno(dpns->inner_tlist, varattno);
8080 18 : if (!tle)
8081 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
8082 :
8083 : Assert(netlevelsup == 0);
8084 18 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8085 :
8086 18 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8087 : levelsup, context);
8088 :
8089 18 : pop_child_plan(dpns, &save_dpns);
8090 18 : return result;
8091 : }
8092 0 : else if (varno == INDEX_VAR && dpns->index_tlist)
8093 : {
8094 : TargetEntry *tle;
8095 : const char *result;
8096 :
8097 0 : tle = get_tle_by_resno(dpns->index_tlist, varattno);
8098 0 : if (!tle)
8099 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
8100 :
8101 : Assert(netlevelsup == 0);
8102 :
8103 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8104 : levelsup, context);
8105 :
8106 0 : return result;
8107 : }
8108 : else
8109 : {
8110 0 : elog(ERROR, "bogus varno: %d", varno);
8111 : return NULL; /* keep compiler quiet */
8112 : }
8113 :
8114 168 : if (attnum == InvalidAttrNumber)
8115 : {
8116 : /* Var is whole-row reference to RTE, so select the right field */
8117 24 : return get_rte_attribute_name(rte, fieldno);
8118 : }
8119 :
8120 : /*
8121 : * This part has essentially the same logic as the parser's
8122 : * expandRecordVariable() function, but we are dealing with a different
8123 : * representation of the input context, and we only need one field name
8124 : * not a TupleDesc. Also, we need special cases for finding subquery and
8125 : * CTE subplans when deparsing Plan trees.
8126 : */
8127 144 : expr = (Node *) var; /* default if we can't drill down */
8128 :
8129 144 : switch (rte->rtekind)
8130 : {
8131 0 : case RTE_RELATION:
8132 : case RTE_VALUES:
8133 : case RTE_NAMEDTUPLESTORE:
8134 : case RTE_RESULT:
8135 :
8136 : /*
8137 : * This case should not occur: a column of a table, values list,
8138 : * or ENR shouldn't have type RECORD. Fall through and fail (most
8139 : * likely) at the bottom.
8140 : */
8141 0 : break;
8142 72 : case RTE_SUBQUERY:
8143 : /* Subselect-in-FROM: examine sub-select's output expr */
8144 : {
8145 72 : if (rte->subquery)
8146 : {
8147 42 : TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
8148 : attnum);
8149 :
8150 42 : if (ste == NULL || ste->resjunk)
8151 0 : elog(ERROR, "subquery %s does not have attribute %d",
8152 : rte->eref->aliasname, attnum);
8153 42 : expr = (Node *) ste->expr;
8154 42 : if (IsA(expr, Var))
8155 : {
8156 : /*
8157 : * Recurse into the sub-select to see what its Var
8158 : * refers to. We have to build an additional level of
8159 : * namespace to keep in step with varlevelsup in the
8160 : * subselect; furthermore, the subquery RTE might be
8161 : * from an outer query level, in which case the
8162 : * namespace for the subselect must have that outer
8163 : * level as parent namespace.
8164 : */
8165 18 : List *save_nslist = context->namespaces;
8166 : List *parent_namespaces;
8167 : deparse_namespace mydpns;
8168 : const char *result;
8169 :
8170 18 : parent_namespaces = list_copy_tail(context->namespaces,
8171 : netlevelsup);
8172 :
8173 18 : set_deparse_for_query(&mydpns, rte->subquery,
8174 : parent_namespaces);
8175 :
8176 18 : context->namespaces = lcons(&mydpns, parent_namespaces);
8177 :
8178 18 : result = get_name_for_var_field((Var *) expr, fieldno,
8179 : 0, context);
8180 :
8181 18 : context->namespaces = save_nslist;
8182 :
8183 18 : return result;
8184 : }
8185 : /* else fall through to inspect the expression */
8186 : }
8187 : else
8188 : {
8189 : /*
8190 : * We're deparsing a Plan tree so we don't have complete
8191 : * RTE entries (in particular, rte->subquery is NULL). But
8192 : * the only place we'd normally see a Var directly
8193 : * referencing a SUBQUERY RTE is in a SubqueryScan plan
8194 : * node, and we can look into the child plan's tlist
8195 : * instead. An exception occurs if the subquery was
8196 : * proven empty and optimized away: then we'd find such a
8197 : * Var in a childless Result node, and there's nothing in
8198 : * the plan tree that would let us figure out what it had
8199 : * originally referenced. In that case, fall back on
8200 : * printing "fN", analogously to the default column names
8201 : * for RowExprs.
8202 : */
8203 : TargetEntry *tle;
8204 : deparse_namespace save_dpns;
8205 : const char *result;
8206 :
8207 30 : if (!dpns->inner_plan)
8208 : {
8209 12 : char *dummy_name = palloc(32);
8210 :
8211 : Assert(dpns->plan && IsA(dpns->plan, Result));
8212 12 : snprintf(dummy_name, 32, "f%d", fieldno);
8213 12 : return dummy_name;
8214 : }
8215 : Assert(dpns->plan && IsA(dpns->plan, SubqueryScan));
8216 :
8217 18 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8218 18 : if (!tle)
8219 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8220 : attnum);
8221 : Assert(netlevelsup == 0);
8222 18 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8223 :
8224 18 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8225 : levelsup, context);
8226 :
8227 18 : pop_child_plan(dpns, &save_dpns);
8228 18 : return result;
8229 : }
8230 : }
8231 24 : break;
8232 0 : case RTE_JOIN:
8233 : /* Join RTE --- recursively inspect the alias variable */
8234 0 : if (rte->joinaliasvars == NIL)
8235 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
8236 : Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
8237 0 : expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
8238 : Assert(expr != NULL);
8239 : /* we intentionally don't strip implicit coercions here */
8240 0 : if (IsA(expr, Var))
8241 0 : return get_name_for_var_field((Var *) expr, fieldno,
8242 0 : var->varlevelsup + levelsup,
8243 : context);
8244 : /* else fall through to inspect the expression */
8245 0 : break;
8246 0 : case RTE_FUNCTION:
8247 : case RTE_TABLEFUNC:
8248 :
8249 : /*
8250 : * We couldn't get here unless a function is declared with one of
8251 : * its result columns as RECORD, which is not allowed.
8252 : */
8253 0 : break;
8254 72 : case RTE_CTE:
8255 : /* CTE reference: examine subquery's output expr */
8256 : {
8257 72 : CommonTableExpr *cte = NULL;
8258 : Index ctelevelsup;
8259 : ListCell *lc;
8260 :
8261 : /*
8262 : * Try to find the referenced CTE using the namespace stack.
8263 : */
8264 72 : ctelevelsup = rte->ctelevelsup + netlevelsup;
8265 72 : if (ctelevelsup >= list_length(context->namespaces))
8266 12 : lc = NULL;
8267 : else
8268 : {
8269 : deparse_namespace *ctedpns;
8270 :
8271 : ctedpns = (deparse_namespace *)
8272 60 : list_nth(context->namespaces, ctelevelsup);
8273 66 : foreach(lc, ctedpns->ctes)
8274 : {
8275 36 : cte = (CommonTableExpr *) lfirst(lc);
8276 36 : if (strcmp(cte->ctename, rte->ctename) == 0)
8277 30 : break;
8278 : }
8279 : }
8280 72 : if (lc != NULL)
8281 : {
8282 30 : Query *ctequery = (Query *) cte->ctequery;
8283 30 : TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
8284 : attnum);
8285 :
8286 30 : if (ste == NULL || ste->resjunk)
8287 0 : elog(ERROR, "CTE %s does not have attribute %d",
8288 : rte->eref->aliasname, attnum);
8289 30 : expr = (Node *) ste->expr;
8290 30 : if (IsA(expr, Var))
8291 : {
8292 : /*
8293 : * Recurse into the CTE to see what its Var refers to.
8294 : * We have to build an additional level of namespace
8295 : * to keep in step with varlevelsup in the CTE;
8296 : * furthermore it could be an outer CTE (compare
8297 : * SUBQUERY case above).
8298 : */
8299 18 : List *save_nslist = context->namespaces;
8300 : List *parent_namespaces;
8301 : deparse_namespace mydpns;
8302 : const char *result;
8303 :
8304 18 : parent_namespaces = list_copy_tail(context->namespaces,
8305 : ctelevelsup);
8306 :
8307 18 : set_deparse_for_query(&mydpns, ctequery,
8308 : parent_namespaces);
8309 :
8310 18 : context->namespaces = lcons(&mydpns, parent_namespaces);
8311 :
8312 18 : result = get_name_for_var_field((Var *) expr, fieldno,
8313 : 0, context);
8314 :
8315 18 : context->namespaces = save_nslist;
8316 :
8317 18 : return result;
8318 : }
8319 : /* else fall through to inspect the expression */
8320 : }
8321 : else
8322 : {
8323 : /*
8324 : * We're deparsing a Plan tree so we don't have a CTE
8325 : * list. But the only places we'd normally see a Var
8326 : * directly referencing a CTE RTE are in CteScan or
8327 : * WorkTableScan plan nodes. For those cases,
8328 : * set_deparse_plan arranged for dpns->inner_plan to be
8329 : * the plan node that emits the CTE or RecursiveUnion
8330 : * result, and we can look at its tlist instead. As
8331 : * above, this can fail if the CTE has been proven empty,
8332 : * in which case fall back to "fN".
8333 : */
8334 : TargetEntry *tle;
8335 : deparse_namespace save_dpns;
8336 : const char *result;
8337 :
8338 42 : if (!dpns->inner_plan)
8339 : {
8340 6 : char *dummy_name = palloc(32);
8341 :
8342 : Assert(dpns->plan && IsA(dpns->plan, Result));
8343 6 : snprintf(dummy_name, 32, "f%d", fieldno);
8344 6 : return dummy_name;
8345 : }
8346 : Assert(dpns->plan && (IsA(dpns->plan, CteScan) ||
8347 : IsA(dpns->plan, WorkTableScan)));
8348 :
8349 36 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8350 36 : if (!tle)
8351 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8352 : attnum);
8353 : Assert(netlevelsup == 0);
8354 36 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8355 :
8356 36 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8357 : levelsup, context);
8358 :
8359 36 : pop_child_plan(dpns, &save_dpns);
8360 36 : return result;
8361 : }
8362 : }
8363 12 : break;
8364 0 : case RTE_GROUP:
8365 :
8366 : /*
8367 : * We couldn't get here: any Vars that reference the RTE_GROUP RTE
8368 : * should have been replaced with the underlying grouping
8369 : * expressions.
8370 : */
8371 0 : break;
8372 : }
8373 :
8374 : /*
8375 : * We now have an expression we can't expand any more, so see if
8376 : * get_expr_result_tupdesc() can do anything with it.
8377 : */
8378 36 : tupleDesc = get_expr_result_tupdesc(expr, false);
8379 : /* Got the tupdesc, so we can extract the field name */
8380 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
8381 36 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
8382 : }
8383 :
8384 : /*
8385 : * Try to find the referenced expression for a PARAM_EXEC Param that might
8386 : * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
8387 : *
8388 : * If successful, return the expression and set *dpns_p and *ancestor_cell_p
8389 : * appropriately for calling push_ancestor_plan(). If no referent can be
8390 : * found, return NULL.
8391 : */
8392 : static Node *
8393 6416 : find_param_referent(Param *param, deparse_context *context,
8394 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
8395 : {
8396 : /* Initialize output parameters to prevent compiler warnings */
8397 6416 : *dpns_p = NULL;
8398 6416 : *ancestor_cell_p = NULL;
8399 :
8400 : /*
8401 : * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
8402 : * SubPlan argument. This will necessarily be in some ancestor of the
8403 : * current expression's Plan node.
8404 : */
8405 6416 : if (param->paramkind == PARAM_EXEC)
8406 : {
8407 : deparse_namespace *dpns;
8408 : Plan *child_plan;
8409 : ListCell *lc;
8410 :
8411 5554 : dpns = (deparse_namespace *) linitial(context->namespaces);
8412 5554 : child_plan = dpns->plan;
8413 :
8414 10012 : foreach(lc, dpns->ancestors)
8415 : {
8416 8416 : Node *ancestor = (Node *) lfirst(lc);
8417 : ListCell *lc2;
8418 :
8419 : /*
8420 : * NestLoops transmit params to their inner child only.
8421 : */
8422 8416 : if (IsA(ancestor, NestLoop) &&
8423 3548 : child_plan == innerPlan(ancestor))
8424 : {
8425 3470 : NestLoop *nl = (NestLoop *) ancestor;
8426 :
8427 4390 : foreach(lc2, nl->nestParams)
8428 : {
8429 4258 : NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
8430 :
8431 4258 : if (nlp->paramno == param->paramid)
8432 : {
8433 : /* Found a match, so return it */
8434 3338 : *dpns_p = dpns;
8435 3338 : *ancestor_cell_p = lc;
8436 3338 : return (Node *) nlp->paramval;
8437 : }
8438 : }
8439 : }
8440 :
8441 : /*
8442 : * If ancestor is a SubPlan, check the arguments it provides.
8443 : */
8444 5078 : if (IsA(ancestor, SubPlan))
8445 : {
8446 974 : SubPlan *subplan = (SubPlan *) ancestor;
8447 : ListCell *lc3;
8448 : ListCell *lc4;
8449 :
8450 1298 : forboth(lc3, subplan->parParam, lc4, subplan->args)
8451 : {
8452 944 : int paramid = lfirst_int(lc3);
8453 944 : Node *arg = (Node *) lfirst(lc4);
8454 :
8455 944 : if (paramid == param->paramid)
8456 : {
8457 : /*
8458 : * Found a match, so return it. But, since Vars in
8459 : * the arg are to be evaluated in the surrounding
8460 : * context, we have to point to the next ancestor item
8461 : * that is *not* a SubPlan.
8462 : */
8463 : ListCell *rest;
8464 :
8465 620 : for_each_cell(rest, dpns->ancestors,
8466 : lnext(dpns->ancestors, lc))
8467 : {
8468 620 : Node *ancestor2 = (Node *) lfirst(rest);
8469 :
8470 620 : if (!IsA(ancestor2, SubPlan))
8471 : {
8472 620 : *dpns_p = dpns;
8473 620 : *ancestor_cell_p = rest;
8474 620 : return arg;
8475 : }
8476 : }
8477 0 : elog(ERROR, "SubPlan cannot be outermost ancestor");
8478 : }
8479 : }
8480 :
8481 : /* SubPlan isn't a kind of Plan, so skip the rest */
8482 354 : continue;
8483 : }
8484 :
8485 : /*
8486 : * We need not consider the ancestor's initPlan list, since
8487 : * initplans never have any parParams.
8488 : */
8489 :
8490 : /* No luck, crawl up to next ancestor */
8491 4104 : child_plan = (Plan *) ancestor;
8492 : }
8493 : }
8494 :
8495 : /* No referent found */
8496 2458 : return NULL;
8497 : }
8498 :
8499 : /*
8500 : * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.
8501 : *
8502 : * If successful, return the generating subplan/initplan and set *column_p
8503 : * to the subplan's 0-based output column number.
8504 : * Otherwise, return NULL.
8505 : */
8506 : static SubPlan *
8507 2458 : find_param_generator(Param *param, deparse_context *context, int *column_p)
8508 : {
8509 : /* Initialize output parameter to prevent compiler warnings */
8510 2458 : *column_p = 0;
8511 :
8512 : /*
8513 : * If it's a PARAM_EXEC parameter, search the current plan node as well as
8514 : * ancestor nodes looking for a subplan or initplan that emits the value
8515 : * for the Param. It could appear in the setParams of an initplan or
8516 : * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
8517 : */
8518 2458 : if (param->paramkind == PARAM_EXEC)
8519 : {
8520 : SubPlan *result;
8521 : deparse_namespace *dpns;
8522 : ListCell *lc;
8523 :
8524 1596 : dpns = (deparse_namespace *) linitial(context->namespaces);
8525 :
8526 : /* First check the innermost plan node's initplans */
8527 1596 : result = find_param_generator_initplan(param, dpns->plan, column_p);
8528 1596 : if (result)
8529 458 : return result;
8530 :
8531 : /*
8532 : * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
8533 : * which can be referenced by Params elsewhere in the targetlist.
8534 : * (Such Params should always be in the same targetlist, so there's no
8535 : * need to do this work at upper plan nodes.)
8536 : */
8537 5836 : foreach_node(TargetEntry, tle, dpns->plan->targetlist)
8538 : {
8539 3664 : if (tle->expr && IsA(tle->expr, SubPlan))
8540 : {
8541 100 : SubPlan *subplan = (SubPlan *) tle->expr;
8542 :
8543 100 : if (subplan->subLinkType == MULTIEXPR_SUBLINK)
8544 : {
8545 78 : foreach_int(paramid, subplan->setParam)
8546 : {
8547 78 : if (paramid == param->paramid)
8548 : {
8549 : /* Found a match, so return it. */
8550 52 : *column_p = foreach_current_index(paramid);
8551 52 : return subplan;
8552 : }
8553 : }
8554 : }
8555 : }
8556 : }
8557 :
8558 : /* No luck, so check the ancestor nodes */
8559 1428 : foreach(lc, dpns->ancestors)
8560 : {
8561 1428 : Node *ancestor = (Node *) lfirst(lc);
8562 :
8563 : /*
8564 : * If ancestor is a SubPlan, check the paramIds it provides.
8565 : */
8566 1428 : if (IsA(ancestor, SubPlan))
8567 : {
8568 210 : SubPlan *subplan = (SubPlan *) ancestor;
8569 :
8570 236 : foreach_int(paramid, subplan->paramIds)
8571 : {
8572 236 : if (paramid == param->paramid)
8573 : {
8574 : /* Found a match, so return it. */
8575 210 : *column_p = foreach_current_index(paramid);
8576 210 : return subplan;
8577 : }
8578 : }
8579 :
8580 : /* SubPlan isn't a kind of Plan, so skip the rest */
8581 0 : continue;
8582 : }
8583 :
8584 : /*
8585 : * Otherwise, it's some kind of Plan node, so check its initplans.
8586 : */
8587 1218 : result = find_param_generator_initplan(param, (Plan *) ancestor,
8588 : column_p);
8589 1218 : if (result)
8590 876 : return result;
8591 :
8592 : /* No luck, crawl up to next ancestor */
8593 : }
8594 : }
8595 :
8596 : /* No generator found */
8597 862 : return NULL;
8598 : }
8599 :
8600 : /*
8601 : * Subroutine for find_param_generator: search one Plan node's initplans
8602 : */
8603 : static SubPlan *
8604 2814 : find_param_generator_initplan(Param *param, Plan *plan, int *column_p)
8605 : {
8606 4434 : foreach_node(SubPlan, subplan, plan->initPlan)
8607 : {
8608 1760 : foreach_int(paramid, subplan->setParam)
8609 : {
8610 1480 : if (paramid == param->paramid)
8611 : {
8612 : /* Found a match, so return it. */
8613 1334 : *column_p = foreach_current_index(paramid);
8614 1334 : return subplan;
8615 : }
8616 : }
8617 : }
8618 1480 : return NULL;
8619 : }
8620 :
8621 : /*
8622 : * Display a Param appropriately.
8623 : */
8624 : static void
8625 6398 : get_parameter(Param *param, deparse_context *context)
8626 : {
8627 : Node *expr;
8628 : deparse_namespace *dpns;
8629 : ListCell *ancestor_cell;
8630 : SubPlan *subplan;
8631 : int column;
8632 :
8633 : /*
8634 : * If it's a PARAM_EXEC parameter, try to locate the expression from which
8635 : * the parameter was computed. This stanza handles only cases in which
8636 : * the Param represents an input to the subplan we are currently in.
8637 : */
8638 6398 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8639 6398 : if (expr)
8640 : {
8641 : /* Found a match, so print it */
8642 : deparse_namespace save_dpns;
8643 : bool save_varprefix;
8644 : bool need_paren;
8645 :
8646 : /* Switch attention to the ancestor plan node */
8647 3940 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
8648 :
8649 : /*
8650 : * Force prefixing of Vars, since they won't belong to the relation
8651 : * being scanned in the original plan node.
8652 : */
8653 3940 : save_varprefix = context->varprefix;
8654 3940 : context->varprefix = true;
8655 :
8656 : /*
8657 : * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
8658 : * upper-level Param, which wouldn't need extra parentheses.
8659 : * Otherwise, insert parens to ensure the expression looks atomic.
8660 : */
8661 3952 : need_paren = !(IsA(expr, Var) ||
8662 12 : IsA(expr, Aggref) ||
8663 12 : IsA(expr, GroupingFunc) ||
8664 6 : IsA(expr, Param));
8665 3940 : if (need_paren)
8666 0 : appendStringInfoChar(context->buf, '(');
8667 :
8668 3940 : get_rule_expr(expr, context, false);
8669 :
8670 3940 : if (need_paren)
8671 0 : appendStringInfoChar(context->buf, ')');
8672 :
8673 3940 : context->varprefix = save_varprefix;
8674 :
8675 3940 : pop_ancestor_plan(dpns, &save_dpns);
8676 :
8677 3940 : return;
8678 : }
8679 :
8680 : /*
8681 : * Alternatively, maybe it's a subplan output, which we print as a
8682 : * reference to the subplan. (We could drill down into the subplan and
8683 : * print the relevant targetlist expression, but that has been deemed too
8684 : * confusing since it would violate normal SQL scope rules. Also, we're
8685 : * relying on this reference to show that the testexpr containing the
8686 : * Param has anything to do with that subplan at all.)
8687 : */
8688 2458 : subplan = find_param_generator(param, context, &column);
8689 2458 : if (subplan)
8690 : {
8691 1596 : appendStringInfo(context->buf, "(%s%s).col%d",
8692 1596 : subplan->useHashTable ? "hashed " : "",
8693 : subplan->plan_name, column + 1);
8694 :
8695 1596 : return;
8696 : }
8697 :
8698 : /*
8699 : * If it's an external parameter, see if the outermost namespace provides
8700 : * function argument names.
8701 : */
8702 862 : if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
8703 : {
8704 862 : dpns = llast(context->namespaces);
8705 862 : if (dpns->argnames &&
8706 68 : param->paramid > 0 &&
8707 68 : param->paramid <= dpns->numargs)
8708 : {
8709 68 : char *argname = dpns->argnames[param->paramid - 1];
8710 :
8711 68 : if (argname)
8712 : {
8713 68 : bool should_qualify = false;
8714 : ListCell *lc;
8715 :
8716 : /*
8717 : * Qualify the parameter name if there are any other deparse
8718 : * namespaces with range tables. This avoids qualifying in
8719 : * trivial cases like "RETURN a + b", but makes it safe in all
8720 : * other cases.
8721 : */
8722 156 : foreach(lc, context->namespaces)
8723 : {
8724 118 : deparse_namespace *depns = lfirst(lc);
8725 :
8726 118 : if (depns->rtable_names != NIL)
8727 : {
8728 30 : should_qualify = true;
8729 30 : break;
8730 : }
8731 : }
8732 68 : if (should_qualify)
8733 : {
8734 30 : appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
8735 30 : appendStringInfoChar(context->buf, '.');
8736 : }
8737 :
8738 68 : appendStringInfoString(context->buf, quote_identifier(argname));
8739 68 : return;
8740 : }
8741 : }
8742 : }
8743 :
8744 : /*
8745 : * Not PARAM_EXEC, or couldn't find referent: just print $N.
8746 : *
8747 : * It's a bug if we get here for anything except PARAM_EXTERN Params, but
8748 : * in production builds printing $N seems more useful than failing.
8749 : */
8750 : Assert(param->paramkind == PARAM_EXTERN);
8751 :
8752 794 : appendStringInfo(context->buf, "$%d", param->paramid);
8753 : }
8754 :
8755 : /*
8756 : * get_simple_binary_op_name
8757 : *
8758 : * helper function for isSimpleNode
8759 : * will return single char binary operator name, or NULL if it's not
8760 : */
8761 : static const char *
8762 114 : get_simple_binary_op_name(OpExpr *expr)
8763 : {
8764 114 : List *args = expr->args;
8765 :
8766 114 : if (list_length(args) == 2)
8767 : {
8768 : /* binary operator */
8769 114 : Node *arg1 = (Node *) linitial(args);
8770 114 : Node *arg2 = (Node *) lsecond(args);
8771 : const char *op;
8772 :
8773 114 : op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
8774 114 : if (strlen(op) == 1)
8775 114 : return op;
8776 : }
8777 0 : return NULL;
8778 : }
8779 :
8780 :
8781 : /*
8782 : * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
8783 : *
8784 : * true : simple in the context of parent node's type
8785 : * false : not simple
8786 : */
8787 : static bool
8788 4954 : isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
8789 : {
8790 4954 : if (!node)
8791 0 : return false;
8792 :
8793 4954 : switch (nodeTag(node))
8794 : {
8795 4140 : case T_Var:
8796 : case T_Const:
8797 : case T_Param:
8798 : case T_CoerceToDomainValue:
8799 : case T_SetToDefault:
8800 : case T_CurrentOfExpr:
8801 : /* single words: always simple */
8802 4140 : return true;
8803 :
8804 448 : case T_SubscriptingRef:
8805 : case T_ArrayExpr:
8806 : case T_RowExpr:
8807 : case T_CoalesceExpr:
8808 : case T_MinMaxExpr:
8809 : case T_SQLValueFunction:
8810 : case T_XmlExpr:
8811 : case T_NextValueExpr:
8812 : case T_NullIfExpr:
8813 : case T_Aggref:
8814 : case T_GroupingFunc:
8815 : case T_WindowFunc:
8816 : case T_MergeSupportFunc:
8817 : case T_FuncExpr:
8818 : case T_JsonConstructorExpr:
8819 : case T_JsonExpr:
8820 : /* function-like: name(..) or name[..] */
8821 448 : return true;
8822 :
8823 : /* CASE keywords act as parentheses */
8824 0 : case T_CaseExpr:
8825 0 : return true;
8826 :
8827 54 : case T_FieldSelect:
8828 :
8829 : /*
8830 : * appears simple since . has top precedence, unless parent is
8831 : * T_FieldSelect itself!
8832 : */
8833 54 : return !IsA(parentNode, FieldSelect);
8834 :
8835 0 : case T_FieldStore:
8836 :
8837 : /*
8838 : * treat like FieldSelect (probably doesn't matter)
8839 : */
8840 0 : return !IsA(parentNode, FieldStore);
8841 :
8842 0 : case T_CoerceToDomain:
8843 : /* maybe simple, check args */
8844 0 : return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
8845 : node, prettyFlags);
8846 6 : case T_RelabelType:
8847 6 : return isSimpleNode((Node *) ((RelabelType *) node)->arg,
8848 : node, prettyFlags);
8849 0 : case T_CoerceViaIO:
8850 0 : return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
8851 : node, prettyFlags);
8852 0 : case T_ArrayCoerceExpr:
8853 0 : return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
8854 : node, prettyFlags);
8855 0 : case T_ConvertRowtypeExpr:
8856 0 : return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
8857 : node, prettyFlags);
8858 0 : case T_ReturningExpr:
8859 0 : return isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr,
8860 : node, prettyFlags);
8861 :
8862 252 : case T_OpExpr:
8863 : {
8864 : /* depends on parent node type; needs further checking */
8865 252 : if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
8866 : {
8867 : const char *op;
8868 : const char *parentOp;
8869 : bool is_lopriop;
8870 : bool is_hipriop;
8871 : bool is_lopriparent;
8872 : bool is_hipriparent;
8873 :
8874 60 : op = get_simple_binary_op_name((OpExpr *) node);
8875 60 : if (!op)
8876 0 : return false;
8877 :
8878 : /* We know only the basic operators + - and * / % */
8879 60 : is_lopriop = (strchr("+-", *op) != NULL);
8880 60 : is_hipriop = (strchr("*/%", *op) != NULL);
8881 60 : if (!(is_lopriop || is_hipriop))
8882 6 : return false;
8883 :
8884 54 : parentOp = get_simple_binary_op_name((OpExpr *) parentNode);
8885 54 : if (!parentOp)
8886 0 : return false;
8887 :
8888 54 : is_lopriparent = (strchr("+-", *parentOp) != NULL);
8889 54 : is_hipriparent = (strchr("*/%", *parentOp) != NULL);
8890 54 : if (!(is_lopriparent || is_hipriparent))
8891 0 : return false;
8892 :
8893 54 : if (is_hipriop && is_lopriparent)
8894 12 : return true; /* op binds tighter than parent */
8895 :
8896 42 : if (is_lopriop && is_hipriparent)
8897 30 : return false;
8898 :
8899 : /*
8900 : * Operators are same priority --- can skip parens only if
8901 : * we have (a - b) - c, not a - (b - c).
8902 : */
8903 12 : if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
8904 6 : return true;
8905 :
8906 6 : return false;
8907 : }
8908 : /* else do the same stuff as for T_SubLink et al. */
8909 : }
8910 : /* FALLTHROUGH */
8911 :
8912 : case T_SubLink:
8913 : case T_NullTest:
8914 : case T_BooleanTest:
8915 : case T_DistinctExpr:
8916 : case T_JsonIsPredicate:
8917 222 : switch (nodeTag(parentNode))
8918 : {
8919 30 : case T_FuncExpr:
8920 : {
8921 : /* special handling for casts and COERCE_SQL_SYNTAX */
8922 30 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
8923 :
8924 30 : if (type == COERCE_EXPLICIT_CAST ||
8925 6 : type == COERCE_IMPLICIT_CAST ||
8926 : type == COERCE_SQL_SYNTAX)
8927 30 : return false;
8928 0 : return true; /* own parentheses */
8929 : }
8930 162 : case T_BoolExpr: /* lower precedence */
8931 : case T_SubscriptingRef: /* other separators */
8932 : case T_ArrayExpr: /* other separators */
8933 : case T_RowExpr: /* other separators */
8934 : case T_CoalesceExpr: /* own parentheses */
8935 : case T_MinMaxExpr: /* own parentheses */
8936 : case T_XmlExpr: /* own parentheses */
8937 : case T_NullIfExpr: /* other separators */
8938 : case T_Aggref: /* own parentheses */
8939 : case T_GroupingFunc: /* own parentheses */
8940 : case T_WindowFunc: /* own parentheses */
8941 : case T_CaseExpr: /* other separators */
8942 162 : return true;
8943 30 : default:
8944 30 : return false;
8945 : }
8946 :
8947 18 : case T_BoolExpr:
8948 18 : switch (nodeTag(parentNode))
8949 : {
8950 18 : case T_BoolExpr:
8951 18 : if (prettyFlags & PRETTYFLAG_PAREN)
8952 : {
8953 : BoolExprType type;
8954 : BoolExprType parentType;
8955 :
8956 18 : type = ((BoolExpr *) node)->boolop;
8957 18 : parentType = ((BoolExpr *) parentNode)->boolop;
8958 : switch (type)
8959 : {
8960 12 : case NOT_EXPR:
8961 : case AND_EXPR:
8962 12 : if (parentType == AND_EXPR || parentType == OR_EXPR)
8963 12 : return true;
8964 0 : break;
8965 6 : case OR_EXPR:
8966 6 : if (parentType == OR_EXPR)
8967 0 : return true;
8968 6 : break;
8969 : }
8970 0 : }
8971 6 : return false;
8972 0 : case T_FuncExpr:
8973 : {
8974 : /* special handling for casts and COERCE_SQL_SYNTAX */
8975 0 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
8976 :
8977 0 : if (type == COERCE_EXPLICIT_CAST ||
8978 0 : type == COERCE_IMPLICIT_CAST ||
8979 : type == COERCE_SQL_SYNTAX)
8980 0 : return false;
8981 0 : return true; /* own parentheses */
8982 : }
8983 0 : case T_SubscriptingRef: /* other separators */
8984 : case T_ArrayExpr: /* other separators */
8985 : case T_RowExpr: /* other separators */
8986 : case T_CoalesceExpr: /* own parentheses */
8987 : case T_MinMaxExpr: /* own parentheses */
8988 : case T_XmlExpr: /* own parentheses */
8989 : case T_NullIfExpr: /* other separators */
8990 : case T_Aggref: /* own parentheses */
8991 : case T_GroupingFunc: /* own parentheses */
8992 : case T_WindowFunc: /* own parentheses */
8993 : case T_CaseExpr: /* other separators */
8994 : case T_JsonExpr: /* own parentheses */
8995 0 : return true;
8996 0 : default:
8997 0 : return false;
8998 : }
8999 :
9000 0 : case T_JsonValueExpr:
9001 : /* maybe simple, check args */
9002 0 : return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
9003 : node, prettyFlags);
9004 :
9005 6 : default:
9006 6 : break;
9007 : }
9008 : /* those we don't know: in dubio complexo */
9009 6 : return false;
9010 : }
9011 :
9012 :
9013 : /*
9014 : * appendContextKeyword - append a keyword to buffer
9015 : *
9016 : * If prettyPrint is enabled, perform a line break, and adjust indentation.
9017 : * Otherwise, just append the keyword.
9018 : */
9019 : static void
9020 29118 : appendContextKeyword(deparse_context *context, const char *str,
9021 : int indentBefore, int indentAfter, int indentPlus)
9022 : {
9023 29118 : StringInfo buf = context->buf;
9024 :
9025 29118 : if (PRETTY_INDENT(context))
9026 : {
9027 : int indentAmount;
9028 :
9029 28202 : context->indentLevel += indentBefore;
9030 :
9031 : /* remove any trailing spaces currently in the buffer ... */
9032 28202 : removeStringInfoSpaces(buf);
9033 : /* ... then add a newline and some spaces */
9034 28202 : appendStringInfoChar(buf, '\n');
9035 :
9036 28202 : if (context->indentLevel < PRETTYINDENT_LIMIT)
9037 28202 : indentAmount = Max(context->indentLevel, 0) + indentPlus;
9038 : else
9039 : {
9040 : /*
9041 : * If we're indented more than PRETTYINDENT_LIMIT characters, try
9042 : * to conserve horizontal space by reducing the per-level
9043 : * indentation. For best results the scale factor here should
9044 : * divide all the indent amounts that get added to indentLevel
9045 : * (PRETTYINDENT_STD, etc). It's important that the indentation
9046 : * not grow unboundedly, else deeply-nested trees use O(N^2)
9047 : * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
9048 : */
9049 0 : indentAmount = PRETTYINDENT_LIMIT +
9050 0 : (context->indentLevel - PRETTYINDENT_LIMIT) /
9051 : (PRETTYINDENT_STD / 2);
9052 0 : indentAmount %= PRETTYINDENT_LIMIT;
9053 : /* scale/wrap logic affects indentLevel, but not indentPlus */
9054 0 : indentAmount += indentPlus;
9055 : }
9056 28202 : appendStringInfoSpaces(buf, indentAmount);
9057 :
9058 28202 : appendStringInfoString(buf, str);
9059 :
9060 28202 : context->indentLevel += indentAfter;
9061 28202 : if (context->indentLevel < 0)
9062 0 : context->indentLevel = 0;
9063 : }
9064 : else
9065 916 : appendStringInfoString(buf, str);
9066 29118 : }
9067 :
9068 : /*
9069 : * removeStringInfoSpaces - delete trailing spaces from a buffer.
9070 : *
9071 : * Possibly this should move to stringinfo.c at some point.
9072 : */
9073 : static void
9074 28664 : removeStringInfoSpaces(StringInfo str)
9075 : {
9076 44784 : while (str->len > 0 && str->data[str->len - 1] == ' ')
9077 16120 : str->data[--(str->len)] = '\0';
9078 28664 : }
9079 :
9080 :
9081 : /*
9082 : * get_rule_expr_paren - deparse expr using get_rule_expr,
9083 : * embracing the string with parentheses if necessary for prettyPrint.
9084 : *
9085 : * Never embrace if prettyFlags=0, because it's done in the calling node.
9086 : *
9087 : * Any node that does *not* embrace its argument node by sql syntax (with
9088 : * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
9089 : * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
9090 : * added.
9091 : */
9092 : static void
9093 152744 : get_rule_expr_paren(Node *node, deparse_context *context,
9094 : bool showimplicit, Node *parentNode)
9095 : {
9096 : bool need_paren;
9097 :
9098 157692 : need_paren = PRETTY_PAREN(context) &&
9099 4948 : !isSimpleNode(node, parentNode, context->prettyFlags);
9100 :
9101 152744 : if (need_paren)
9102 114 : appendStringInfoChar(context->buf, '(');
9103 :
9104 152744 : get_rule_expr(node, context, showimplicit);
9105 :
9106 152744 : if (need_paren)
9107 114 : appendStringInfoChar(context->buf, ')');
9108 152744 : }
9109 :
9110 : static void
9111 84 : get_json_behavior(JsonBehavior *behavior, deparse_context *context,
9112 : const char *on)
9113 : {
9114 : /*
9115 : * The order of array elements must correspond to the order of
9116 : * JsonBehaviorType members.
9117 : */
9118 84 : const char *behavior_names[] =
9119 : {
9120 : " NULL",
9121 : " ERROR",
9122 : " EMPTY",
9123 : " TRUE",
9124 : " FALSE",
9125 : " UNKNOWN",
9126 : " EMPTY ARRAY",
9127 : " EMPTY OBJECT",
9128 : " DEFAULT "
9129 : };
9130 :
9131 84 : if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
9132 0 : elog(ERROR, "invalid json behavior type: %d", behavior->btype);
9133 :
9134 84 : appendStringInfoString(context->buf, behavior_names[behavior->btype]);
9135 :
9136 84 : if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
9137 18 : get_rule_expr(behavior->expr, context, false);
9138 :
9139 84 : appendStringInfo(context->buf, " ON %s", on);
9140 84 : }
9141 :
9142 : /*
9143 : * get_json_expr_options
9144 : *
9145 : * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and
9146 : * JSON_TABLE columns.
9147 : */
9148 : static void
9149 456 : get_json_expr_options(JsonExpr *jsexpr, deparse_context *context,
9150 : JsonBehaviorType default_behavior)
9151 : {
9152 456 : if (jsexpr->op == JSON_QUERY_OP)
9153 : {
9154 210 : if (jsexpr->wrapper == JSW_CONDITIONAL)
9155 12 : appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
9156 198 : else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
9157 30 : appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
9158 : /* The default */
9159 168 : else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
9160 168 : appendStringInfoString(context->buf, " WITHOUT WRAPPER");
9161 :
9162 210 : if (jsexpr->omit_quotes)
9163 42 : appendStringInfoString(context->buf, " OMIT QUOTES");
9164 : /* The default */
9165 : else
9166 168 : appendStringInfoString(context->buf, " KEEP QUOTES");
9167 : }
9168 :
9169 456 : if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
9170 30 : get_json_behavior(jsexpr->on_empty, context, "EMPTY");
9171 :
9172 456 : if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
9173 48 : get_json_behavior(jsexpr->on_error, context, "ERROR");
9174 456 : }
9175 :
9176 : /* ----------
9177 : * get_rule_expr - Parse back an expression
9178 : *
9179 : * Note: showimplicit determines whether we display any implicit cast that
9180 : * is present at the top of the expression tree. It is a passed argument,
9181 : * not a field of the context struct, because we change the value as we
9182 : * recurse down into the expression. In general we suppress implicit casts
9183 : * when the result type is known with certainty (eg, the arguments of an
9184 : * OR must be boolean). We display implicit casts for arguments of functions
9185 : * and operators, since this is needed to be certain that the same function
9186 : * or operator will be chosen when the expression is re-parsed.
9187 : * ----------
9188 : */
9189 : static void
9190 311584 : get_rule_expr(Node *node, deparse_context *context,
9191 : bool showimplicit)
9192 : {
9193 311584 : StringInfo buf = context->buf;
9194 :
9195 311584 : if (node == NULL)
9196 90 : return;
9197 :
9198 : /* Guard against excessively long or deeply-nested queries */
9199 311494 : CHECK_FOR_INTERRUPTS();
9200 311494 : check_stack_depth();
9201 :
9202 : /*
9203 : * Each level of get_rule_expr must emit an indivisible term
9204 : * (parenthesized if necessary) to ensure result is reparsed into the same
9205 : * expression tree. The only exception is that when the input is a List,
9206 : * we emit the component items comma-separated with no surrounding
9207 : * decoration; this is convenient for most callers.
9208 : */
9209 311494 : switch (nodeTag(node))
9210 : {
9211 143396 : case T_Var:
9212 143396 : (void) get_variable((Var *) node, 0, false, context);
9213 143396 : break;
9214 :
9215 58214 : case T_Const:
9216 58214 : get_const_expr((Const *) node, context, 0);
9217 58214 : break;
9218 :
9219 6398 : case T_Param:
9220 6398 : get_parameter((Param *) node, context);
9221 6398 : break;
9222 :
9223 1704 : case T_Aggref:
9224 1704 : get_agg_expr((Aggref *) node, context, (Aggref *) node);
9225 1704 : break;
9226 :
9227 112 : case T_GroupingFunc:
9228 : {
9229 112 : GroupingFunc *gexpr = (GroupingFunc *) node;
9230 :
9231 112 : appendStringInfoString(buf, "GROUPING(");
9232 112 : get_rule_expr((Node *) gexpr->args, context, true);
9233 112 : appendStringInfoChar(buf, ')');
9234 : }
9235 112 : break;
9236 :
9237 240 : case T_WindowFunc:
9238 240 : get_windowfunc_expr((WindowFunc *) node, context);
9239 240 : break;
9240 :
9241 6 : case T_MergeSupportFunc:
9242 6 : appendStringInfoString(buf, "MERGE_ACTION()");
9243 6 : break;
9244 :
9245 328 : case T_SubscriptingRef:
9246 : {
9247 328 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
9248 : bool need_parens;
9249 :
9250 : /*
9251 : * If the argument is a CaseTestExpr, we must be inside a
9252 : * FieldStore, ie, we are assigning to an element of an array
9253 : * within a composite column. Since we already punted on
9254 : * displaying the FieldStore's target information, just punt
9255 : * here too, and display only the assignment source
9256 : * expression.
9257 : */
9258 328 : if (IsA(sbsref->refexpr, CaseTestExpr))
9259 : {
9260 : Assert(sbsref->refassgnexpr);
9261 0 : get_rule_expr((Node *) sbsref->refassgnexpr,
9262 : context, showimplicit);
9263 0 : break;
9264 : }
9265 :
9266 : /*
9267 : * Parenthesize the argument unless it's a simple Var or a
9268 : * FieldSelect. (In particular, if it's another
9269 : * SubscriptingRef, we *must* parenthesize to avoid
9270 : * confusion.)
9271 : */
9272 482 : need_parens = !IsA(sbsref->refexpr, Var) &&
9273 154 : !IsA(sbsref->refexpr, FieldSelect);
9274 328 : if (need_parens)
9275 94 : appendStringInfoChar(buf, '(');
9276 328 : get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
9277 328 : if (need_parens)
9278 94 : appendStringInfoChar(buf, ')');
9279 :
9280 : /*
9281 : * If there's a refassgnexpr, we want to print the node in the
9282 : * format "container[subscripts] := refassgnexpr". This is
9283 : * not legal SQL, so decompilation of INSERT or UPDATE
9284 : * statements should always use processIndirection as part of
9285 : * the statement-level syntax. We should only see this when
9286 : * EXPLAIN tries to print the targetlist of a plan resulting
9287 : * from such a statement.
9288 : */
9289 328 : if (sbsref->refassgnexpr)
9290 : {
9291 : Node *refassgnexpr;
9292 :
9293 : /*
9294 : * Use processIndirection to print this node's subscripts
9295 : * as well as any additional field selections or
9296 : * subscripting in immediate descendants. It returns the
9297 : * RHS expr that is actually being "assigned".
9298 : */
9299 12 : refassgnexpr = processIndirection(node, context);
9300 12 : appendStringInfoString(buf, " := ");
9301 12 : get_rule_expr(refassgnexpr, context, showimplicit);
9302 : }
9303 : else
9304 : {
9305 : /* Just an ordinary container fetch, so print subscripts */
9306 316 : printSubscripts(sbsref, context);
9307 : }
9308 : }
9309 328 : break;
9310 :
9311 11754 : case T_FuncExpr:
9312 11754 : get_func_expr((FuncExpr *) node, context, showimplicit);
9313 11754 : break;
9314 :
9315 18 : case T_NamedArgExpr:
9316 : {
9317 18 : NamedArgExpr *na = (NamedArgExpr *) node;
9318 :
9319 18 : appendStringInfo(buf, "%s => ", quote_identifier(na->name));
9320 18 : get_rule_expr((Node *) na->arg, context, showimplicit);
9321 : }
9322 18 : break;
9323 :
9324 56884 : case T_OpExpr:
9325 56884 : get_oper_expr((OpExpr *) node, context);
9326 56884 : break;
9327 :
9328 18 : case T_DistinctExpr:
9329 : {
9330 18 : DistinctExpr *expr = (DistinctExpr *) node;
9331 18 : List *args = expr->args;
9332 18 : Node *arg1 = (Node *) linitial(args);
9333 18 : Node *arg2 = (Node *) lsecond(args);
9334 :
9335 18 : if (!PRETTY_PAREN(context))
9336 12 : appendStringInfoChar(buf, '(');
9337 18 : get_rule_expr_paren(arg1, context, true, node);
9338 18 : appendStringInfoString(buf, " IS DISTINCT FROM ");
9339 18 : get_rule_expr_paren(arg2, context, true, node);
9340 18 : if (!PRETTY_PAREN(context))
9341 12 : appendStringInfoChar(buf, ')');
9342 : }
9343 18 : break;
9344 :
9345 20 : case T_NullIfExpr:
9346 : {
9347 20 : NullIfExpr *nullifexpr = (NullIfExpr *) node;
9348 :
9349 20 : appendStringInfoString(buf, "NULLIF(");
9350 20 : get_rule_expr((Node *) nullifexpr->args, context, true);
9351 20 : appendStringInfoChar(buf, ')');
9352 : }
9353 20 : break;
9354 :
9355 2852 : case T_ScalarArrayOpExpr:
9356 : {
9357 2852 : ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
9358 2852 : List *args = expr->args;
9359 2852 : Node *arg1 = (Node *) linitial(args);
9360 2852 : Node *arg2 = (Node *) lsecond(args);
9361 :
9362 2852 : if (!PRETTY_PAREN(context))
9363 2840 : appendStringInfoChar(buf, '(');
9364 2852 : get_rule_expr_paren(arg1, context, true, node);
9365 2852 : appendStringInfo(buf, " %s %s (",
9366 : generate_operator_name(expr->opno,
9367 : exprType(arg1),
9368 : get_base_element_type(exprType(arg2))),
9369 2852 : expr->useOr ? "ANY" : "ALL");
9370 2852 : get_rule_expr_paren(arg2, context, true, node);
9371 :
9372 : /*
9373 : * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
9374 : * a bare sub-SELECT. Since we're here, the sub-SELECT must
9375 : * be meant as a scalar sub-SELECT yielding an array value to
9376 : * be used in ScalarArrayOpExpr; but the grammar will
9377 : * preferentially interpret such a construct as an ANY/ALL
9378 : * SubLink. To prevent misparsing the output that way, insert
9379 : * a dummy coercion (which will be stripped by parse analysis,
9380 : * so no inefficiency is added in dump and reload). This is
9381 : * indeed most likely what the user wrote to get the construct
9382 : * accepted in the first place.
9383 : */
9384 2852 : if (IsA(arg2, SubLink) &&
9385 6 : ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
9386 6 : appendStringInfo(buf, "::%s",
9387 : format_type_with_typemod(exprType(arg2),
9388 : exprTypmod(arg2)));
9389 2852 : appendStringInfoChar(buf, ')');
9390 2852 : if (!PRETTY_PAREN(context))
9391 2840 : appendStringInfoChar(buf, ')');
9392 : }
9393 2852 : break;
9394 :
9395 10868 : case T_BoolExpr:
9396 : {
9397 10868 : BoolExpr *expr = (BoolExpr *) node;
9398 10868 : Node *first_arg = linitial(expr->args);
9399 : ListCell *arg;
9400 :
9401 10868 : switch (expr->boolop)
9402 : {
9403 8644 : case AND_EXPR:
9404 8644 : if (!PRETTY_PAREN(context))
9405 8578 : appendStringInfoChar(buf, '(');
9406 8644 : get_rule_expr_paren(first_arg, context,
9407 : false, node);
9408 19648 : for_each_from(arg, expr->args, 1)
9409 : {
9410 11004 : appendStringInfoString(buf, " AND ");
9411 11004 : get_rule_expr_paren((Node *) lfirst(arg), context,
9412 : false, node);
9413 : }
9414 8644 : if (!PRETTY_PAREN(context))
9415 8578 : appendStringInfoChar(buf, ')');
9416 8644 : break;
9417 :
9418 1858 : case OR_EXPR:
9419 1858 : if (!PRETTY_PAREN(context))
9420 1846 : appendStringInfoChar(buf, '(');
9421 1858 : get_rule_expr_paren(first_arg, context,
9422 : false, node);
9423 4430 : for_each_from(arg, expr->args, 1)
9424 : {
9425 2572 : appendStringInfoString(buf, " OR ");
9426 2572 : get_rule_expr_paren((Node *) lfirst(arg), context,
9427 : false, node);
9428 : }
9429 1858 : if (!PRETTY_PAREN(context))
9430 1846 : appendStringInfoChar(buf, ')');
9431 1858 : break;
9432 :
9433 366 : case NOT_EXPR:
9434 366 : if (!PRETTY_PAREN(context))
9435 354 : appendStringInfoChar(buf, '(');
9436 366 : appendStringInfoString(buf, "NOT ");
9437 366 : get_rule_expr_paren(first_arg, context,
9438 : false, node);
9439 366 : if (!PRETTY_PAREN(context))
9440 354 : appendStringInfoChar(buf, ')');
9441 366 : break;
9442 :
9443 0 : default:
9444 0 : elog(ERROR, "unrecognized boolop: %d",
9445 : (int) expr->boolop);
9446 : }
9447 : }
9448 10868 : break;
9449 :
9450 460 : case T_SubLink:
9451 460 : get_sublink_expr((SubLink *) node, context);
9452 460 : break;
9453 :
9454 706 : case T_SubPlan:
9455 : {
9456 706 : SubPlan *subplan = (SubPlan *) node;
9457 :
9458 : /*
9459 : * We cannot see an already-planned subplan in rule deparsing,
9460 : * only while EXPLAINing a query plan. We don't try to
9461 : * reconstruct the original SQL, just reference the subplan
9462 : * that appears elsewhere in EXPLAIN's result. It does seem
9463 : * useful to show the subLinkType and testexpr (if any), and
9464 : * we also note whether the subplan will be hashed.
9465 : */
9466 706 : switch (subplan->subLinkType)
9467 : {
9468 90 : case EXISTS_SUBLINK:
9469 90 : appendStringInfoString(buf, "EXISTS(");
9470 : Assert(subplan->testexpr == NULL);
9471 90 : break;
9472 6 : case ALL_SUBLINK:
9473 6 : appendStringInfoString(buf, "(ALL ");
9474 : Assert(subplan->testexpr != NULL);
9475 6 : break;
9476 166 : case ANY_SUBLINK:
9477 166 : appendStringInfoString(buf, "(ANY ");
9478 : Assert(subplan->testexpr != NULL);
9479 166 : break;
9480 6 : case ROWCOMPARE_SUBLINK:
9481 : /* Parenthesizing the testexpr seems sufficient */
9482 6 : appendStringInfoChar(buf, '(');
9483 : Assert(subplan->testexpr != NULL);
9484 6 : break;
9485 400 : case EXPR_SUBLINK:
9486 : /* No need to decorate these subplan references */
9487 400 : appendStringInfoChar(buf, '(');
9488 : Assert(subplan->testexpr == NULL);
9489 400 : break;
9490 26 : case MULTIEXPR_SUBLINK:
9491 : /* MULTIEXPR isn't executed in the normal way */
9492 26 : appendStringInfoString(buf, "(rescan ");
9493 : Assert(subplan->testexpr == NULL);
9494 26 : break;
9495 12 : case ARRAY_SUBLINK:
9496 12 : appendStringInfoString(buf, "ARRAY(");
9497 : Assert(subplan->testexpr == NULL);
9498 12 : break;
9499 0 : case CTE_SUBLINK:
9500 : /* This case is unreachable within expressions */
9501 0 : appendStringInfoString(buf, "CTE(");
9502 : Assert(subplan->testexpr == NULL);
9503 0 : break;
9504 : }
9505 :
9506 706 : if (subplan->testexpr != NULL)
9507 : {
9508 : deparse_namespace *dpns;
9509 :
9510 : /*
9511 : * Push SubPlan into ancestors list while deparsing
9512 : * testexpr, so that we can handle PARAM_EXEC references
9513 : * to the SubPlan's paramIds. (This makes it look like
9514 : * the SubPlan is an "ancestor" of the current plan node,
9515 : * which is a little weird, but it does no harm.) In this
9516 : * path, we don't need to mention the SubPlan explicitly,
9517 : * because the referencing Params will show its existence.
9518 : */
9519 178 : dpns = (deparse_namespace *) linitial(context->namespaces);
9520 178 : dpns->ancestors = lcons(subplan, dpns->ancestors);
9521 :
9522 178 : get_rule_expr(subplan->testexpr, context, showimplicit);
9523 178 : appendStringInfoChar(buf, ')');
9524 :
9525 178 : dpns->ancestors = list_delete_first(dpns->ancestors);
9526 : }
9527 : else
9528 : {
9529 : /* No referencing Params, so show the SubPlan's name */
9530 528 : if (subplan->useHashTable)
9531 0 : appendStringInfo(buf, "hashed %s)", subplan->plan_name);
9532 : else
9533 528 : appendStringInfo(buf, "%s)", subplan->plan_name);
9534 : }
9535 : }
9536 706 : break;
9537 :
9538 0 : case T_AlternativeSubPlan:
9539 : {
9540 0 : AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
9541 : ListCell *lc;
9542 :
9543 : /*
9544 : * This case cannot be reached in normal usage, since no
9545 : * AlternativeSubPlan can appear either in parsetrees or
9546 : * finished plan trees. We keep it just in case somebody
9547 : * wants to use this code to print planner data structures.
9548 : */
9549 0 : appendStringInfoString(buf, "(alternatives: ");
9550 0 : foreach(lc, asplan->subplans)
9551 : {
9552 0 : SubPlan *splan = lfirst_node(SubPlan, lc);
9553 :
9554 0 : if (splan->useHashTable)
9555 0 : appendStringInfo(buf, "hashed %s", splan->plan_name);
9556 : else
9557 0 : appendStringInfoString(buf, splan->plan_name);
9558 0 : if (lnext(asplan->subplans, lc))
9559 0 : appendStringInfoString(buf, " or ");
9560 : }
9561 0 : appendStringInfoChar(buf, ')');
9562 : }
9563 0 : break;
9564 :
9565 1118 : case T_FieldSelect:
9566 : {
9567 1118 : FieldSelect *fselect = (FieldSelect *) node;
9568 1118 : Node *arg = (Node *) fselect->arg;
9569 1118 : int fno = fselect->fieldnum;
9570 : const char *fieldname;
9571 : bool need_parens;
9572 :
9573 : /*
9574 : * Parenthesize the argument unless it's an SubscriptingRef or
9575 : * another FieldSelect. Note in particular that it would be
9576 : * WRONG to not parenthesize a Var argument; simplicity is not
9577 : * the issue here, having the right number of names is.
9578 : */
9579 2200 : need_parens = !IsA(arg, SubscriptingRef) &&
9580 1082 : !IsA(arg, FieldSelect);
9581 1118 : if (need_parens)
9582 1082 : appendStringInfoChar(buf, '(');
9583 1118 : get_rule_expr(arg, context, true);
9584 1118 : if (need_parens)
9585 1082 : appendStringInfoChar(buf, ')');
9586 :
9587 : /*
9588 : * Get and print the field name.
9589 : */
9590 1118 : fieldname = get_name_for_var_field((Var *) arg, fno,
9591 : 0, context);
9592 1118 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
9593 : }
9594 1118 : break;
9595 :
9596 6 : case T_FieldStore:
9597 : {
9598 6 : FieldStore *fstore = (FieldStore *) node;
9599 : bool need_parens;
9600 :
9601 : /*
9602 : * There is no good way to represent a FieldStore as real SQL,
9603 : * so decompilation of INSERT or UPDATE statements should
9604 : * always use processIndirection as part of the
9605 : * statement-level syntax. We should only get here when
9606 : * EXPLAIN tries to print the targetlist of a plan resulting
9607 : * from such a statement. The plan case is even harder than
9608 : * ordinary rules would be, because the planner tries to
9609 : * collapse multiple assignments to the same field or subfield
9610 : * into one FieldStore; so we can see a list of target fields
9611 : * not just one, and the arguments could be FieldStores
9612 : * themselves. We don't bother to try to print the target
9613 : * field names; we just print the source arguments, with a
9614 : * ROW() around them if there's more than one. This isn't
9615 : * terribly complete, but it's probably good enough for
9616 : * EXPLAIN's purposes; especially since anything more would be
9617 : * either hopelessly confusing or an even poorer
9618 : * representation of what the plan is actually doing.
9619 : */
9620 6 : need_parens = (list_length(fstore->newvals) != 1);
9621 6 : if (need_parens)
9622 6 : appendStringInfoString(buf, "ROW(");
9623 6 : get_rule_expr((Node *) fstore->newvals, context, showimplicit);
9624 6 : if (need_parens)
9625 6 : appendStringInfoChar(buf, ')');
9626 : }
9627 6 : break;
9628 :
9629 2504 : case T_RelabelType:
9630 : {
9631 2504 : RelabelType *relabel = (RelabelType *) node;
9632 2504 : Node *arg = (Node *) relabel->arg;
9633 :
9634 2504 : if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
9635 2318 : !showimplicit)
9636 : {
9637 : /* don't show the implicit cast */
9638 64 : get_rule_expr_paren(arg, context, false, node);
9639 : }
9640 : else
9641 : {
9642 2440 : get_coercion_expr(arg, context,
9643 : relabel->resulttype,
9644 : relabel->resulttypmod,
9645 : node);
9646 : }
9647 : }
9648 2504 : break;
9649 :
9650 656 : case T_CoerceViaIO:
9651 : {
9652 656 : CoerceViaIO *iocoerce = (CoerceViaIO *) node;
9653 656 : Node *arg = (Node *) iocoerce->arg;
9654 :
9655 656 : if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
9656 24 : !showimplicit)
9657 : {
9658 : /* don't show the implicit cast */
9659 24 : get_rule_expr_paren(arg, context, false, node);
9660 : }
9661 : else
9662 : {
9663 632 : get_coercion_expr(arg, context,
9664 : iocoerce->resulttype,
9665 : -1,
9666 : node);
9667 : }
9668 : }
9669 656 : break;
9670 :
9671 48 : case T_ArrayCoerceExpr:
9672 : {
9673 48 : ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
9674 48 : Node *arg = (Node *) acoerce->arg;
9675 :
9676 48 : if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
9677 48 : !showimplicit)
9678 : {
9679 : /* don't show the implicit cast */
9680 0 : get_rule_expr_paren(arg, context, false, node);
9681 : }
9682 : else
9683 : {
9684 48 : get_coercion_expr(arg, context,
9685 : acoerce->resulttype,
9686 : acoerce->resulttypmod,
9687 : node);
9688 : }
9689 : }
9690 48 : break;
9691 :
9692 88 : case T_ConvertRowtypeExpr:
9693 : {
9694 88 : ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
9695 88 : Node *arg = (Node *) convert->arg;
9696 :
9697 88 : if (convert->convertformat == COERCE_IMPLICIT_CAST &&
9698 82 : !showimplicit)
9699 : {
9700 : /* don't show the implicit cast */
9701 24 : get_rule_expr_paren(arg, context, false, node);
9702 : }
9703 : else
9704 : {
9705 64 : get_coercion_expr(arg, context,
9706 : convert->resulttype, -1,
9707 : node);
9708 : }
9709 : }
9710 88 : break;
9711 :
9712 90 : case T_CollateExpr:
9713 : {
9714 90 : CollateExpr *collate = (CollateExpr *) node;
9715 90 : Node *arg = (Node *) collate->arg;
9716 :
9717 90 : if (!PRETTY_PAREN(context))
9718 84 : appendStringInfoChar(buf, '(');
9719 90 : get_rule_expr_paren(arg, context, showimplicit, node);
9720 90 : appendStringInfo(buf, " COLLATE %s",
9721 : generate_collation_name(collate->collOid));
9722 90 : if (!PRETTY_PAREN(context))
9723 84 : appendStringInfoChar(buf, ')');
9724 : }
9725 90 : break;
9726 :
9727 594 : case T_CaseExpr:
9728 : {
9729 594 : CaseExpr *caseexpr = (CaseExpr *) node;
9730 : ListCell *temp;
9731 :
9732 594 : appendContextKeyword(context, "CASE",
9733 : 0, PRETTYINDENT_VAR, 0);
9734 594 : if (caseexpr->arg)
9735 : {
9736 174 : appendStringInfoChar(buf, ' ');
9737 174 : get_rule_expr((Node *) caseexpr->arg, context, true);
9738 : }
9739 2626 : foreach(temp, caseexpr->args)
9740 : {
9741 2032 : CaseWhen *when = (CaseWhen *) lfirst(temp);
9742 2032 : Node *w = (Node *) when->expr;
9743 :
9744 2032 : if (caseexpr->arg)
9745 : {
9746 : /*
9747 : * The parser should have produced WHEN clauses of the
9748 : * form "CaseTestExpr = RHS", possibly with an
9749 : * implicit coercion inserted above the CaseTestExpr.
9750 : * For accurate decompilation of rules it's essential
9751 : * that we show just the RHS. However in an
9752 : * expression that's been through the optimizer, the
9753 : * WHEN clause could be almost anything (since the
9754 : * equality operator could have been expanded into an
9755 : * inline function). If we don't recognize the form
9756 : * of the WHEN clause, just punt and display it as-is.
9757 : */
9758 748 : if (IsA(w, OpExpr))
9759 : {
9760 748 : List *args = ((OpExpr *) w)->args;
9761 :
9762 748 : if (list_length(args) == 2 &&
9763 748 : IsA(strip_implicit_coercions(linitial(args)),
9764 : CaseTestExpr))
9765 748 : w = (Node *) lsecond(args);
9766 : }
9767 : }
9768 :
9769 2032 : if (!PRETTY_INDENT(context))
9770 118 : appendStringInfoChar(buf, ' ');
9771 2032 : appendContextKeyword(context, "WHEN ",
9772 : 0, 0, 0);
9773 2032 : get_rule_expr(w, context, false);
9774 2032 : appendStringInfoString(buf, " THEN ");
9775 2032 : get_rule_expr((Node *) when->result, context, true);
9776 : }
9777 594 : if (!PRETTY_INDENT(context))
9778 108 : appendStringInfoChar(buf, ' ');
9779 594 : appendContextKeyword(context, "ELSE ",
9780 : 0, 0, 0);
9781 594 : get_rule_expr((Node *) caseexpr->defresult, context, true);
9782 594 : if (!PRETTY_INDENT(context))
9783 108 : appendStringInfoChar(buf, ' ');
9784 594 : appendContextKeyword(context, "END",
9785 : -PRETTYINDENT_VAR, 0, 0);
9786 : }
9787 594 : break;
9788 :
9789 0 : case T_CaseTestExpr:
9790 : {
9791 : /*
9792 : * Normally we should never get here, since for expressions
9793 : * that can contain this node type we attempt to avoid
9794 : * recursing to it. But in an optimized expression we might
9795 : * be unable to avoid that (see comments for CaseExpr). If we
9796 : * do see one, print it as CASE_TEST_EXPR.
9797 : */
9798 0 : appendStringInfoString(buf, "CASE_TEST_EXPR");
9799 : }
9800 0 : break;
9801 :
9802 536 : case T_ArrayExpr:
9803 : {
9804 536 : ArrayExpr *arrayexpr = (ArrayExpr *) node;
9805 :
9806 536 : appendStringInfoString(buf, "ARRAY[");
9807 536 : get_rule_expr((Node *) arrayexpr->elements, context, true);
9808 536 : appendStringInfoChar(buf, ']');
9809 :
9810 : /*
9811 : * If the array isn't empty, we assume its elements are
9812 : * coerced to the desired type. If it's empty, though, we
9813 : * need an explicit coercion to the array type.
9814 : */
9815 536 : if (arrayexpr->elements == NIL)
9816 6 : appendStringInfo(buf, "::%s",
9817 : format_type_with_typemod(arrayexpr->array_typeid, -1));
9818 : }
9819 536 : break;
9820 :
9821 192 : case T_RowExpr:
9822 : {
9823 192 : RowExpr *rowexpr = (RowExpr *) node;
9824 192 : TupleDesc tupdesc = NULL;
9825 : ListCell *arg;
9826 : int i;
9827 : char *sep;
9828 :
9829 : /*
9830 : * If it's a named type and not RECORD, we may have to skip
9831 : * dropped columns and/or claim there are NULLs for added
9832 : * columns.
9833 : */
9834 192 : if (rowexpr->row_typeid != RECORDOID)
9835 : {
9836 54 : tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
9837 : Assert(list_length(rowexpr->args) <= tupdesc->natts);
9838 : }
9839 :
9840 : /*
9841 : * SQL99 allows "ROW" to be omitted when there is more than
9842 : * one column, but for simplicity we always print it.
9843 : */
9844 192 : appendStringInfoString(buf, "ROW(");
9845 192 : sep = "";
9846 192 : i = 0;
9847 570 : foreach(arg, rowexpr->args)
9848 : {
9849 378 : Node *e = (Node *) lfirst(arg);
9850 :
9851 378 : if (tupdesc == NULL ||
9852 120 : !TupleDescAttr(tupdesc, i)->attisdropped)
9853 : {
9854 378 : appendStringInfoString(buf, sep);
9855 : /* Whole-row Vars need special treatment here */
9856 378 : get_rule_expr_toplevel(e, context, true);
9857 378 : sep = ", ";
9858 : }
9859 378 : i++;
9860 : }
9861 192 : if (tupdesc != NULL)
9862 : {
9863 54 : while (i < tupdesc->natts)
9864 : {
9865 0 : if (!TupleDescAttr(tupdesc, i)->attisdropped)
9866 : {
9867 0 : appendStringInfoString(buf, sep);
9868 0 : appendStringInfoString(buf, "NULL");
9869 0 : sep = ", ";
9870 : }
9871 0 : i++;
9872 : }
9873 :
9874 54 : ReleaseTupleDesc(tupdesc);
9875 : }
9876 192 : appendStringInfoChar(buf, ')');
9877 192 : if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
9878 36 : appendStringInfo(buf, "::%s",
9879 : format_type_with_typemod(rowexpr->row_typeid, -1));
9880 : }
9881 192 : break;
9882 :
9883 96 : case T_RowCompareExpr:
9884 : {
9885 96 : RowCompareExpr *rcexpr = (RowCompareExpr *) node;
9886 :
9887 : /*
9888 : * SQL99 allows "ROW" to be omitted when there is more than
9889 : * one column, but for simplicity we always print it. Within
9890 : * a ROW expression, whole-row Vars need special treatment, so
9891 : * use get_rule_list_toplevel.
9892 : */
9893 96 : appendStringInfoString(buf, "(ROW(");
9894 96 : get_rule_list_toplevel(rcexpr->largs, context, true);
9895 :
9896 : /*
9897 : * We assume that the name of the first-column operator will
9898 : * do for all the rest too. This is definitely open to
9899 : * failure, eg if some but not all operators were renamed
9900 : * since the construct was parsed, but there seems no way to
9901 : * be perfect.
9902 : */
9903 96 : appendStringInfo(buf, ") %s ROW(",
9904 96 : generate_operator_name(linitial_oid(rcexpr->opnos),
9905 96 : exprType(linitial(rcexpr->largs)),
9906 96 : exprType(linitial(rcexpr->rargs))));
9907 96 : get_rule_list_toplevel(rcexpr->rargs, context, true);
9908 96 : appendStringInfoString(buf, "))");
9909 : }
9910 96 : break;
9911 :
9912 1104 : case T_CoalesceExpr:
9913 : {
9914 1104 : CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
9915 :
9916 1104 : appendStringInfoString(buf, "COALESCE(");
9917 1104 : get_rule_expr((Node *) coalesceexpr->args, context, true);
9918 1104 : appendStringInfoChar(buf, ')');
9919 : }
9920 1104 : break;
9921 :
9922 36 : case T_MinMaxExpr:
9923 : {
9924 36 : MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
9925 :
9926 36 : switch (minmaxexpr->op)
9927 : {
9928 6 : case IS_GREATEST:
9929 6 : appendStringInfoString(buf, "GREATEST(");
9930 6 : break;
9931 30 : case IS_LEAST:
9932 30 : appendStringInfoString(buf, "LEAST(");
9933 30 : break;
9934 : }
9935 36 : get_rule_expr((Node *) minmaxexpr->args, context, true);
9936 36 : appendStringInfoChar(buf, ')');
9937 : }
9938 36 : break;
9939 :
9940 694 : case T_SQLValueFunction:
9941 : {
9942 694 : SQLValueFunction *svf = (SQLValueFunction *) node;
9943 :
9944 : /*
9945 : * Note: this code knows that typmod for time, timestamp, and
9946 : * timestamptz just prints as integer.
9947 : */
9948 694 : switch (svf->op)
9949 : {
9950 104 : case SVFOP_CURRENT_DATE:
9951 104 : appendStringInfoString(buf, "CURRENT_DATE");
9952 104 : break;
9953 12 : case SVFOP_CURRENT_TIME:
9954 12 : appendStringInfoString(buf, "CURRENT_TIME");
9955 12 : break;
9956 12 : case SVFOP_CURRENT_TIME_N:
9957 12 : appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
9958 12 : break;
9959 12 : case SVFOP_CURRENT_TIMESTAMP:
9960 12 : appendStringInfoString(buf, "CURRENT_TIMESTAMP");
9961 12 : break;
9962 104 : case SVFOP_CURRENT_TIMESTAMP_N:
9963 104 : appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
9964 : svf->typmod);
9965 104 : break;
9966 12 : case SVFOP_LOCALTIME:
9967 12 : appendStringInfoString(buf, "LOCALTIME");
9968 12 : break;
9969 12 : case SVFOP_LOCALTIME_N:
9970 12 : appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
9971 12 : break;
9972 30 : case SVFOP_LOCALTIMESTAMP:
9973 30 : appendStringInfoString(buf, "LOCALTIMESTAMP");
9974 30 : break;
9975 18 : case SVFOP_LOCALTIMESTAMP_N:
9976 18 : appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
9977 : svf->typmod);
9978 18 : break;
9979 12 : case SVFOP_CURRENT_ROLE:
9980 12 : appendStringInfoString(buf, "CURRENT_ROLE");
9981 12 : break;
9982 296 : case SVFOP_CURRENT_USER:
9983 296 : appendStringInfoString(buf, "CURRENT_USER");
9984 296 : break;
9985 12 : case SVFOP_USER:
9986 12 : appendStringInfoString(buf, "USER");
9987 12 : break;
9988 34 : case SVFOP_SESSION_USER:
9989 34 : appendStringInfoString(buf, "SESSION_USER");
9990 34 : break;
9991 12 : case SVFOP_CURRENT_CATALOG:
9992 12 : appendStringInfoString(buf, "CURRENT_CATALOG");
9993 12 : break;
9994 12 : case SVFOP_CURRENT_SCHEMA:
9995 12 : appendStringInfoString(buf, "CURRENT_SCHEMA");
9996 12 : break;
9997 : }
9998 694 : }
9999 694 : break;
10000 :
10001 144 : case T_XmlExpr:
10002 : {
10003 144 : XmlExpr *xexpr = (XmlExpr *) node;
10004 144 : bool needcomma = false;
10005 : ListCell *arg;
10006 : ListCell *narg;
10007 : Const *con;
10008 :
10009 144 : switch (xexpr->op)
10010 : {
10011 16 : case IS_XMLCONCAT:
10012 16 : appendStringInfoString(buf, "XMLCONCAT(");
10013 16 : break;
10014 32 : case IS_XMLELEMENT:
10015 32 : appendStringInfoString(buf, "XMLELEMENT(");
10016 32 : break;
10017 16 : case IS_XMLFOREST:
10018 16 : appendStringInfoString(buf, "XMLFOREST(");
10019 16 : break;
10020 16 : case IS_XMLPARSE:
10021 16 : appendStringInfoString(buf, "XMLPARSE(");
10022 16 : break;
10023 16 : case IS_XMLPI:
10024 16 : appendStringInfoString(buf, "XMLPI(");
10025 16 : break;
10026 16 : case IS_XMLROOT:
10027 16 : appendStringInfoString(buf, "XMLROOT(");
10028 16 : break;
10029 32 : case IS_XMLSERIALIZE:
10030 32 : appendStringInfoString(buf, "XMLSERIALIZE(");
10031 32 : break;
10032 0 : case IS_DOCUMENT:
10033 0 : break;
10034 : }
10035 144 : if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
10036 : {
10037 48 : if (xexpr->xmloption == XMLOPTION_DOCUMENT)
10038 0 : appendStringInfoString(buf, "DOCUMENT ");
10039 : else
10040 48 : appendStringInfoString(buf, "CONTENT ");
10041 : }
10042 144 : if (xexpr->name)
10043 : {
10044 48 : appendStringInfo(buf, "NAME %s",
10045 48 : quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));
10046 48 : needcomma = true;
10047 : }
10048 144 : if (xexpr->named_args)
10049 : {
10050 32 : if (xexpr->op != IS_XMLFOREST)
10051 : {
10052 16 : if (needcomma)
10053 16 : appendStringInfoString(buf, ", ");
10054 16 : appendStringInfoString(buf, "XMLATTRIBUTES(");
10055 16 : needcomma = false;
10056 : }
10057 112 : forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
10058 : {
10059 80 : Node *e = (Node *) lfirst(arg);
10060 80 : char *argname = strVal(lfirst(narg));
10061 :
10062 80 : if (needcomma)
10063 48 : appendStringInfoString(buf, ", ");
10064 80 : get_rule_expr((Node *) e, context, true);
10065 80 : appendStringInfo(buf, " AS %s",
10066 80 : quote_identifier(map_xml_name_to_sql_identifier(argname)));
10067 80 : needcomma = true;
10068 : }
10069 32 : if (xexpr->op != IS_XMLFOREST)
10070 16 : appendStringInfoChar(buf, ')');
10071 : }
10072 144 : if (xexpr->args)
10073 : {
10074 128 : if (needcomma)
10075 48 : appendStringInfoString(buf, ", ");
10076 128 : switch (xexpr->op)
10077 : {
10078 96 : case IS_XMLCONCAT:
10079 : case IS_XMLELEMENT:
10080 : case IS_XMLFOREST:
10081 : case IS_XMLPI:
10082 : case IS_XMLSERIALIZE:
10083 : /* no extra decoration needed */
10084 96 : get_rule_expr((Node *) xexpr->args, context, true);
10085 96 : break;
10086 16 : case IS_XMLPARSE:
10087 : Assert(list_length(xexpr->args) == 2);
10088 :
10089 16 : get_rule_expr((Node *) linitial(xexpr->args),
10090 : context, true);
10091 :
10092 16 : con = lsecond_node(Const, xexpr->args);
10093 : Assert(!con->constisnull);
10094 16 : if (DatumGetBool(con->constvalue))
10095 0 : appendStringInfoString(buf,
10096 : " PRESERVE WHITESPACE");
10097 : else
10098 16 : appendStringInfoString(buf,
10099 : " STRIP WHITESPACE");
10100 16 : break;
10101 16 : case IS_XMLROOT:
10102 : Assert(list_length(xexpr->args) == 3);
10103 :
10104 16 : get_rule_expr((Node *) linitial(xexpr->args),
10105 : context, true);
10106 :
10107 16 : appendStringInfoString(buf, ", VERSION ");
10108 16 : con = (Const *) lsecond(xexpr->args);
10109 16 : if (IsA(con, Const) &&
10110 16 : con->constisnull)
10111 16 : appendStringInfoString(buf, "NO VALUE");
10112 : else
10113 0 : get_rule_expr((Node *) con, context, false);
10114 :
10115 16 : con = lthird_node(Const, xexpr->args);
10116 16 : if (con->constisnull)
10117 : /* suppress STANDALONE NO VALUE */ ;
10118 : else
10119 : {
10120 16 : switch (DatumGetInt32(con->constvalue))
10121 : {
10122 16 : case XML_STANDALONE_YES:
10123 16 : appendStringInfoString(buf,
10124 : ", STANDALONE YES");
10125 16 : break;
10126 0 : case XML_STANDALONE_NO:
10127 0 : appendStringInfoString(buf,
10128 : ", STANDALONE NO");
10129 0 : break;
10130 0 : case XML_STANDALONE_NO_VALUE:
10131 0 : appendStringInfoString(buf,
10132 : ", STANDALONE NO VALUE");
10133 0 : break;
10134 0 : default:
10135 0 : break;
10136 : }
10137 : }
10138 16 : break;
10139 0 : case IS_DOCUMENT:
10140 0 : get_rule_expr_paren((Node *) xexpr->args, context, false, node);
10141 0 : break;
10142 : }
10143 16 : }
10144 144 : if (xexpr->op == IS_XMLSERIALIZE)
10145 32 : appendStringInfo(buf, " AS %s",
10146 : format_type_with_typemod(xexpr->type,
10147 : xexpr->typmod));
10148 144 : if (xexpr->op == IS_DOCUMENT)
10149 0 : appendStringInfoString(buf, " IS DOCUMENT");
10150 : else
10151 144 : appendStringInfoChar(buf, ')');
10152 : }
10153 144 : break;
10154 :
10155 2210 : case T_NullTest:
10156 : {
10157 2210 : NullTest *ntest = (NullTest *) node;
10158 :
10159 2210 : if (!PRETTY_PAREN(context))
10160 2156 : appendStringInfoChar(buf, '(');
10161 2210 : get_rule_expr_paren((Node *) ntest->arg, context, true, node);
10162 :
10163 : /*
10164 : * For scalar inputs, we prefer to print as IS [NOT] NULL,
10165 : * which is shorter and traditional. If it's a rowtype input
10166 : * but we're applying a scalar test, must print IS [NOT]
10167 : * DISTINCT FROM NULL to be semantically correct.
10168 : */
10169 2210 : if (ntest->argisrow ||
10170 2180 : !type_is_rowtype(exprType((Node *) ntest->arg)))
10171 : {
10172 4384 : switch (ntest->nulltesttype)
10173 : {
10174 798 : case IS_NULL:
10175 798 : appendStringInfoString(buf, " IS NULL");
10176 798 : break;
10177 1394 : case IS_NOT_NULL:
10178 1394 : appendStringInfoString(buf, " IS NOT NULL");
10179 1394 : break;
10180 0 : default:
10181 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10182 : (int) ntest->nulltesttype);
10183 : }
10184 : }
10185 : else
10186 : {
10187 18 : switch (ntest->nulltesttype)
10188 : {
10189 6 : case IS_NULL:
10190 6 : appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
10191 6 : break;
10192 12 : case IS_NOT_NULL:
10193 12 : appendStringInfoString(buf, " IS DISTINCT FROM NULL");
10194 12 : break;
10195 0 : default:
10196 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10197 : (int) ntest->nulltesttype);
10198 : }
10199 : }
10200 2210 : if (!PRETTY_PAREN(context))
10201 2156 : appendStringInfoChar(buf, ')');
10202 : }
10203 2210 : break;
10204 :
10205 306 : case T_BooleanTest:
10206 : {
10207 306 : BooleanTest *btest = (BooleanTest *) node;
10208 :
10209 306 : if (!PRETTY_PAREN(context))
10210 306 : appendStringInfoChar(buf, '(');
10211 306 : get_rule_expr_paren((Node *) btest->arg, context, false, node);
10212 306 : switch (btest->booltesttype)
10213 : {
10214 36 : case IS_TRUE:
10215 36 : appendStringInfoString(buf, " IS TRUE");
10216 36 : break;
10217 138 : case IS_NOT_TRUE:
10218 138 : appendStringInfoString(buf, " IS NOT TRUE");
10219 138 : break;
10220 0 : case IS_FALSE:
10221 0 : appendStringInfoString(buf, " IS FALSE");
10222 0 : break;
10223 54 : case IS_NOT_FALSE:
10224 54 : appendStringInfoString(buf, " IS NOT FALSE");
10225 54 : break;
10226 24 : case IS_UNKNOWN:
10227 24 : appendStringInfoString(buf, " IS UNKNOWN");
10228 24 : break;
10229 54 : case IS_NOT_UNKNOWN:
10230 54 : appendStringInfoString(buf, " IS NOT UNKNOWN");
10231 54 : break;
10232 0 : default:
10233 0 : elog(ERROR, "unrecognized booltesttype: %d",
10234 : (int) btest->booltesttype);
10235 : }
10236 306 : if (!PRETTY_PAREN(context))
10237 306 : appendStringInfoChar(buf, ')');
10238 : }
10239 306 : break;
10240 :
10241 32 : case T_CoerceToDomain:
10242 : {
10243 32 : CoerceToDomain *ctest = (CoerceToDomain *) node;
10244 32 : Node *arg = (Node *) ctest->arg;
10245 :
10246 32 : if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
10247 22 : !showimplicit)
10248 : {
10249 : /* don't show the implicit cast */
10250 22 : get_rule_expr(arg, context, false);
10251 : }
10252 : else
10253 : {
10254 10 : get_coercion_expr(arg, context,
10255 : ctest->resulttype,
10256 : ctest->resulttypmod,
10257 : node);
10258 : }
10259 : }
10260 32 : break;
10261 :
10262 368 : case T_CoerceToDomainValue:
10263 368 : appendStringInfoString(buf, "VALUE");
10264 368 : break;
10265 :
10266 76 : case T_SetToDefault:
10267 76 : appendStringInfoString(buf, "DEFAULT");
10268 76 : break;
10269 :
10270 24 : case T_CurrentOfExpr:
10271 : {
10272 24 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
10273 :
10274 24 : if (cexpr->cursor_name)
10275 24 : appendStringInfo(buf, "CURRENT OF %s",
10276 24 : quote_identifier(cexpr->cursor_name));
10277 : else
10278 0 : appendStringInfo(buf, "CURRENT OF $%d",
10279 : cexpr->cursor_param);
10280 : }
10281 24 : break;
10282 :
10283 0 : case T_NextValueExpr:
10284 : {
10285 0 : NextValueExpr *nvexpr = (NextValueExpr *) node;
10286 :
10287 : /*
10288 : * This isn't exactly nextval(), but that seems close enough
10289 : * for EXPLAIN's purposes.
10290 : */
10291 0 : appendStringInfoString(buf, "nextval(");
10292 0 : simple_quote_literal(buf,
10293 0 : generate_relation_name(nvexpr->seqid,
10294 : NIL));
10295 0 : appendStringInfoChar(buf, ')');
10296 : }
10297 0 : break;
10298 :
10299 24 : case T_InferenceElem:
10300 : {
10301 24 : InferenceElem *iexpr = (InferenceElem *) node;
10302 : bool save_varprefix;
10303 : bool need_parens;
10304 :
10305 : /*
10306 : * InferenceElem can only refer to target relation, so a
10307 : * prefix is not useful, and indeed would cause parse errors.
10308 : */
10309 24 : save_varprefix = context->varprefix;
10310 24 : context->varprefix = false;
10311 :
10312 : /*
10313 : * Parenthesize the element unless it's a simple Var or a bare
10314 : * function call. Follows pg_get_indexdef_worker().
10315 : */
10316 24 : need_parens = !IsA(iexpr->expr, Var);
10317 24 : if (IsA(iexpr->expr, FuncExpr) &&
10318 0 : ((FuncExpr *) iexpr->expr)->funcformat ==
10319 : COERCE_EXPLICIT_CALL)
10320 0 : need_parens = false;
10321 :
10322 24 : if (need_parens)
10323 0 : appendStringInfoChar(buf, '(');
10324 24 : get_rule_expr((Node *) iexpr->expr,
10325 : context, false);
10326 24 : if (need_parens)
10327 0 : appendStringInfoChar(buf, ')');
10328 :
10329 24 : context->varprefix = save_varprefix;
10330 :
10331 24 : if (iexpr->infercollid)
10332 12 : appendStringInfo(buf, " COLLATE %s",
10333 : generate_collation_name(iexpr->infercollid));
10334 :
10335 : /* Add the operator class name, if not default */
10336 24 : if (iexpr->inferopclass)
10337 : {
10338 12 : Oid inferopclass = iexpr->inferopclass;
10339 12 : Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
10340 :
10341 12 : get_opclass_name(inferopclass, inferopcinputtype, buf);
10342 : }
10343 : }
10344 24 : break;
10345 :
10346 12 : case T_ReturningExpr:
10347 : {
10348 12 : ReturningExpr *retExpr = (ReturningExpr *) node;
10349 :
10350 : /*
10351 : * We cannot see a ReturningExpr in rule deparsing, only while
10352 : * EXPLAINing a query plan (ReturningExpr nodes are only ever
10353 : * adding during query rewriting). Just display the expression
10354 : * returned (an expanded view column).
10355 : */
10356 12 : get_rule_expr((Node *) retExpr->retexpr, context, showimplicit);
10357 : }
10358 12 : break;
10359 :
10360 3754 : case T_PartitionBoundSpec:
10361 : {
10362 3754 : PartitionBoundSpec *spec = (PartitionBoundSpec *) node;
10363 : ListCell *cell;
10364 : char *sep;
10365 :
10366 3754 : if (spec->is_default)
10367 : {
10368 146 : appendStringInfoString(buf, "DEFAULT");
10369 146 : break;
10370 : }
10371 :
10372 3608 : switch (spec->strategy)
10373 : {
10374 182 : case PARTITION_STRATEGY_HASH:
10375 : Assert(spec->modulus > 0 && spec->remainder >= 0);
10376 : Assert(spec->modulus > spec->remainder);
10377 :
10378 182 : appendStringInfoString(buf, "FOR VALUES");
10379 182 : appendStringInfo(buf, " WITH (modulus %d, remainder %d)",
10380 : spec->modulus, spec->remainder);
10381 182 : break;
10382 :
10383 1296 : case PARTITION_STRATEGY_LIST:
10384 : Assert(spec->listdatums != NIL);
10385 :
10386 1296 : appendStringInfoString(buf, "FOR VALUES IN (");
10387 1296 : sep = "";
10388 3456 : foreach(cell, spec->listdatums)
10389 : {
10390 2160 : Const *val = lfirst_node(Const, cell);
10391 :
10392 2160 : appendStringInfoString(buf, sep);
10393 2160 : get_const_expr(val, context, -1);
10394 2160 : sep = ", ";
10395 : }
10396 :
10397 1296 : appendStringInfoChar(buf, ')');
10398 1296 : break;
10399 :
10400 2130 : case PARTITION_STRATEGY_RANGE:
10401 : Assert(spec->lowerdatums != NIL &&
10402 : spec->upperdatums != NIL &&
10403 : list_length(spec->lowerdatums) ==
10404 : list_length(spec->upperdatums));
10405 :
10406 2130 : appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
10407 : get_range_partbound_string(spec->lowerdatums),
10408 : get_range_partbound_string(spec->upperdatums));
10409 2130 : break;
10410 :
10411 0 : default:
10412 0 : elog(ERROR, "unrecognized partition strategy: %d",
10413 : (int) spec->strategy);
10414 : break;
10415 : }
10416 : }
10417 3608 : break;
10418 :
10419 150 : case T_JsonValueExpr:
10420 : {
10421 150 : JsonValueExpr *jve = (JsonValueExpr *) node;
10422 :
10423 150 : get_rule_expr((Node *) jve->raw_expr, context, false);
10424 150 : get_json_format(jve->format, context->buf);
10425 : }
10426 150 : break;
10427 :
10428 174 : case T_JsonConstructorExpr:
10429 174 : get_json_constructor((JsonConstructorExpr *) node, context, false);
10430 174 : break;
10431 :
10432 60 : case T_JsonIsPredicate:
10433 : {
10434 60 : JsonIsPredicate *pred = (JsonIsPredicate *) node;
10435 :
10436 60 : if (!PRETTY_PAREN(context))
10437 30 : appendStringInfoChar(context->buf, '(');
10438 :
10439 60 : get_rule_expr_paren(pred->expr, context, true, node);
10440 :
10441 60 : appendStringInfoString(context->buf, " IS JSON");
10442 :
10443 : /* TODO: handle FORMAT clause */
10444 :
10445 60 : switch (pred->item_type)
10446 : {
10447 12 : case JS_TYPE_SCALAR:
10448 12 : appendStringInfoString(context->buf, " SCALAR");
10449 12 : break;
10450 12 : case JS_TYPE_ARRAY:
10451 12 : appendStringInfoString(context->buf, " ARRAY");
10452 12 : break;
10453 12 : case JS_TYPE_OBJECT:
10454 12 : appendStringInfoString(context->buf, " OBJECT");
10455 12 : break;
10456 24 : default:
10457 24 : break;
10458 : }
10459 :
10460 60 : if (pred->unique_keys)
10461 12 : appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
10462 :
10463 60 : if (!PRETTY_PAREN(context))
10464 30 : appendStringInfoChar(context->buf, ')');
10465 : }
10466 60 : break;
10467 :
10468 60 : case T_JsonExpr:
10469 : {
10470 60 : JsonExpr *jexpr = (JsonExpr *) node;
10471 :
10472 60 : switch (jexpr->op)
10473 : {
10474 12 : case JSON_EXISTS_OP:
10475 12 : appendStringInfoString(buf, "JSON_EXISTS(");
10476 12 : break;
10477 36 : case JSON_QUERY_OP:
10478 36 : appendStringInfoString(buf, "JSON_QUERY(");
10479 36 : break;
10480 12 : case JSON_VALUE_OP:
10481 12 : appendStringInfoString(buf, "JSON_VALUE(");
10482 12 : break;
10483 0 : default:
10484 0 : elog(ERROR, "unrecognized JsonExpr op: %d",
10485 : (int) jexpr->op);
10486 : }
10487 :
10488 60 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
10489 :
10490 60 : appendStringInfoString(buf, ", ");
10491 :
10492 60 : get_json_path_spec(jexpr->path_spec, context, showimplicit);
10493 :
10494 60 : if (jexpr->passing_values)
10495 : {
10496 : ListCell *lc1,
10497 : *lc2;
10498 12 : bool needcomma = false;
10499 :
10500 12 : appendStringInfoString(buf, " PASSING ");
10501 :
10502 48 : forboth(lc1, jexpr->passing_names,
10503 : lc2, jexpr->passing_values)
10504 : {
10505 36 : if (needcomma)
10506 24 : appendStringInfoString(buf, ", ");
10507 36 : needcomma = true;
10508 :
10509 36 : get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
10510 36 : appendStringInfo(buf, " AS %s",
10511 36 : quote_identifier(lfirst_node(String, lc1)->sval));
10512 : }
10513 : }
10514 :
10515 60 : if (jexpr->op != JSON_EXISTS_OP ||
10516 12 : jexpr->returning->typid != BOOLOID)
10517 48 : get_json_returning(jexpr->returning, context->buf,
10518 48 : jexpr->op == JSON_QUERY_OP);
10519 :
10520 60 : get_json_expr_options(jexpr, context,
10521 60 : jexpr->op != JSON_EXISTS_OP ?
10522 : JSON_BEHAVIOR_NULL :
10523 : JSON_BEHAVIOR_FALSE);
10524 :
10525 60 : appendStringInfoChar(buf, ')');
10526 : }
10527 60 : break;
10528 :
10529 2288 : case T_List:
10530 : {
10531 : char *sep;
10532 : ListCell *l;
10533 :
10534 2288 : sep = "";
10535 6406 : foreach(l, (List *) node)
10536 : {
10537 4118 : appendStringInfoString(buf, sep);
10538 4118 : get_rule_expr((Node *) lfirst(l), context, showimplicit);
10539 4118 : sep = ", ";
10540 : }
10541 : }
10542 2288 : break;
10543 :
10544 72 : case T_TableFunc:
10545 72 : get_tablefunc((TableFunc *) node, context, showimplicit);
10546 72 : break;
10547 :
10548 0 : default:
10549 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
10550 : break;
10551 : }
10552 : }
10553 :
10554 : /*
10555 : * get_rule_expr_toplevel - Parse back a toplevel expression
10556 : *
10557 : * Same as get_rule_expr(), except that if the expr is just a Var, we pass
10558 : * istoplevel = true not false to get_variable(). This causes whole-row Vars
10559 : * to get printed with decoration that will prevent expansion of "*".
10560 : * We need to use this in contexts such as ROW() and VALUES(), where the
10561 : * parser would expand "foo.*" appearing at top level. (In principle we'd
10562 : * use this in get_target_list() too, but that has additional worries about
10563 : * whether to print AS, so it needs to invoke get_variable() directly anyway.)
10564 : */
10565 : static void
10566 2972 : get_rule_expr_toplevel(Node *node, deparse_context *context,
10567 : bool showimplicit)
10568 : {
10569 2972 : if (node && IsA(node, Var))
10570 1178 : (void) get_variable((Var *) node, 0, true, context);
10571 : else
10572 1794 : get_rule_expr(node, context, showimplicit);
10573 2972 : }
10574 :
10575 : /*
10576 : * get_rule_list_toplevel - Parse back a list of toplevel expressions
10577 : *
10578 : * Apply get_rule_expr_toplevel() to each element of a List.
10579 : *
10580 : * This adds commas between the expressions, but caller is responsible
10581 : * for printing surrounding decoration.
10582 : */
10583 : static void
10584 468 : get_rule_list_toplevel(List *lst, deparse_context *context,
10585 : bool showimplicit)
10586 : {
10587 : const char *sep;
10588 : ListCell *lc;
10589 :
10590 468 : sep = "";
10591 1610 : foreach(lc, lst)
10592 : {
10593 1142 : Node *e = (Node *) lfirst(lc);
10594 :
10595 1142 : appendStringInfoString(context->buf, sep);
10596 1142 : get_rule_expr_toplevel(e, context, showimplicit);
10597 1142 : sep = ", ";
10598 : }
10599 468 : }
10600 :
10601 : /*
10602 : * get_rule_expr_funccall - Parse back a function-call expression
10603 : *
10604 : * Same as get_rule_expr(), except that we guarantee that the output will
10605 : * look like a function call, or like one of the things the grammar treats as
10606 : * equivalent to a function call (see the func_expr_windowless production).
10607 : * This is needed in places where the grammar uses func_expr_windowless and
10608 : * you can't substitute a parenthesized a_expr. If what we have isn't going
10609 : * to look like a function call, wrap it in a dummy CAST() expression, which
10610 : * will satisfy the grammar --- and, indeed, is likely what the user wrote to
10611 : * produce such a thing.
10612 : */
10613 : static void
10614 828 : get_rule_expr_funccall(Node *node, deparse_context *context,
10615 : bool showimplicit)
10616 : {
10617 828 : if (looks_like_function(node))
10618 816 : get_rule_expr(node, context, showimplicit);
10619 : else
10620 : {
10621 12 : StringInfo buf = context->buf;
10622 :
10623 12 : appendStringInfoString(buf, "CAST(");
10624 : /* no point in showing any top-level implicit cast */
10625 12 : get_rule_expr(node, context, false);
10626 12 : appendStringInfo(buf, " AS %s)",
10627 : format_type_with_typemod(exprType(node),
10628 : exprTypmod(node)));
10629 : }
10630 828 : }
10631 :
10632 : /*
10633 : * Helper function to identify node types that satisfy func_expr_windowless.
10634 : * If in doubt, "false" is always a safe answer.
10635 : */
10636 : static bool
10637 1898 : looks_like_function(Node *node)
10638 : {
10639 1898 : if (node == NULL)
10640 0 : return false; /* probably shouldn't happen */
10641 1898 : switch (nodeTag(node))
10642 : {
10643 856 : case T_FuncExpr:
10644 : /* OK, unless it's going to deparse as a cast */
10645 874 : return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
10646 18 : ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
10647 108 : case T_NullIfExpr:
10648 : case T_CoalesceExpr:
10649 : case T_MinMaxExpr:
10650 : case T_SQLValueFunction:
10651 : case T_XmlExpr:
10652 : case T_JsonExpr:
10653 : /* these are all accepted by func_expr_common_subexpr */
10654 108 : return true;
10655 934 : default:
10656 934 : break;
10657 : }
10658 934 : return false;
10659 : }
10660 :
10661 :
10662 : /*
10663 : * get_oper_expr - Parse back an OpExpr node
10664 : */
10665 : static void
10666 56884 : get_oper_expr(OpExpr *expr, deparse_context *context)
10667 : {
10668 56884 : StringInfo buf = context->buf;
10669 56884 : Oid opno = expr->opno;
10670 56884 : List *args = expr->args;
10671 :
10672 56884 : if (!PRETTY_PAREN(context))
10673 54872 : appendStringInfoChar(buf, '(');
10674 56884 : if (list_length(args) == 2)
10675 : {
10676 : /* binary operator */
10677 56854 : Node *arg1 = (Node *) linitial(args);
10678 56854 : Node *arg2 = (Node *) lsecond(args);
10679 :
10680 56854 : get_rule_expr_paren(arg1, context, true, (Node *) expr);
10681 56854 : appendStringInfo(buf, " %s ",
10682 : generate_operator_name(opno,
10683 : exprType(arg1),
10684 : exprType(arg2)));
10685 56854 : get_rule_expr_paren(arg2, context, true, (Node *) expr);
10686 : }
10687 : else
10688 : {
10689 : /* prefix operator */
10690 30 : Node *arg = (Node *) linitial(args);
10691 :
10692 30 : appendStringInfo(buf, "%s ",
10693 : generate_operator_name(opno,
10694 : InvalidOid,
10695 : exprType(arg)));
10696 30 : get_rule_expr_paren(arg, context, true, (Node *) expr);
10697 : }
10698 56884 : if (!PRETTY_PAREN(context))
10699 54872 : appendStringInfoChar(buf, ')');
10700 56884 : }
10701 :
10702 : /*
10703 : * get_func_expr - Parse back a FuncExpr node
10704 : */
10705 : static void
10706 11754 : get_func_expr(FuncExpr *expr, deparse_context *context,
10707 : bool showimplicit)
10708 : {
10709 11754 : StringInfo buf = context->buf;
10710 11754 : Oid funcoid = expr->funcid;
10711 : Oid argtypes[FUNC_MAX_ARGS];
10712 : int nargs;
10713 : List *argnames;
10714 : bool use_variadic;
10715 : ListCell *l;
10716 :
10717 : /*
10718 : * If the function call came from an implicit coercion, then just show the
10719 : * first argument --- unless caller wants to see implicit coercions.
10720 : */
10721 11754 : if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
10722 : {
10723 1206 : get_rule_expr_paren((Node *) linitial(expr->args), context,
10724 : false, (Node *) expr);
10725 2958 : return;
10726 : }
10727 :
10728 : /*
10729 : * If the function call came from a cast, then show the first argument
10730 : * plus an explicit cast operation.
10731 : */
10732 10548 : if (expr->funcformat == COERCE_EXPLICIT_CAST ||
10733 9928 : expr->funcformat == COERCE_IMPLICIT_CAST)
10734 : {
10735 1590 : Node *arg = linitial(expr->args);
10736 1590 : Oid rettype = expr->funcresulttype;
10737 : int32 coercedTypmod;
10738 :
10739 : /* Get the typmod if this is a length-coercion function */
10740 1590 : (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
10741 :
10742 1590 : get_coercion_expr(arg, context,
10743 : rettype, coercedTypmod,
10744 : (Node *) expr);
10745 :
10746 1590 : return;
10747 : }
10748 :
10749 : /*
10750 : * If the function was called using one of the SQL spec's random special
10751 : * syntaxes, try to reproduce that. If we don't recognize the function,
10752 : * fall through.
10753 : */
10754 8958 : if (expr->funcformat == COERCE_SQL_SYNTAX)
10755 : {
10756 168 : if (get_func_sql_syntax(expr, context))
10757 162 : return;
10758 : }
10759 :
10760 : /*
10761 : * Normal function: display as proname(args). First we need to extract
10762 : * the argument datatypes.
10763 : */
10764 8796 : if (list_length(expr->args) > FUNC_MAX_ARGS)
10765 0 : ereport(ERROR,
10766 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
10767 : errmsg("too many arguments")));
10768 8796 : nargs = 0;
10769 8796 : argnames = NIL;
10770 18340 : foreach(l, expr->args)
10771 : {
10772 9544 : Node *arg = (Node *) lfirst(l);
10773 :
10774 9544 : if (IsA(arg, NamedArgExpr))
10775 18 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
10776 9544 : argtypes[nargs] = exprType(arg);
10777 9544 : nargs++;
10778 : }
10779 :
10780 8796 : appendStringInfo(buf, "%s(",
10781 : generate_function_name(funcoid, nargs,
10782 : argnames, argtypes,
10783 8796 : expr->funcvariadic,
10784 : &use_variadic,
10785 8796 : context->inGroupBy));
10786 8796 : nargs = 0;
10787 18340 : foreach(l, expr->args)
10788 : {
10789 9544 : if (nargs++ > 0)
10790 1758 : appendStringInfoString(buf, ", ");
10791 9544 : if (use_variadic && lnext(expr->args, l) == NULL)
10792 12 : appendStringInfoString(buf, "VARIADIC ");
10793 9544 : get_rule_expr((Node *) lfirst(l), context, true);
10794 : }
10795 8796 : appendStringInfoChar(buf, ')');
10796 : }
10797 :
10798 : /*
10799 : * get_agg_expr - Parse back an Aggref node
10800 : */
10801 : static void
10802 1964 : get_agg_expr(Aggref *aggref, deparse_context *context,
10803 : Aggref *original_aggref)
10804 : {
10805 1964 : get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
10806 : false);
10807 1964 : }
10808 :
10809 : /*
10810 : * get_agg_expr_helper - subroutine for get_agg_expr and
10811 : * get_json_agg_constructor
10812 : */
10813 : static void
10814 2018 : get_agg_expr_helper(Aggref *aggref, deparse_context *context,
10815 : Aggref *original_aggref, const char *funcname,
10816 : const char *options, bool is_json_objectagg)
10817 : {
10818 2018 : StringInfo buf = context->buf;
10819 : Oid argtypes[FUNC_MAX_ARGS];
10820 : int nargs;
10821 2018 : bool use_variadic = false;
10822 :
10823 : /*
10824 : * For a combining aggregate, we look up and deparse the corresponding
10825 : * partial aggregate instead. This is necessary because our input
10826 : * argument list has been replaced; the new argument list always has just
10827 : * one element, which will point to a partial Aggref that supplies us with
10828 : * transition states to combine.
10829 : */
10830 2018 : if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
10831 : {
10832 : TargetEntry *tle;
10833 :
10834 : Assert(list_length(aggref->args) == 1);
10835 260 : tle = linitial_node(TargetEntry, aggref->args);
10836 260 : resolve_special_varno((Node *) tle->expr, context,
10837 : get_agg_combine_expr, original_aggref);
10838 260 : return;
10839 : }
10840 :
10841 : /*
10842 : * Mark as PARTIAL, if appropriate. We look to the original aggref so as
10843 : * to avoid printing this when recursing from the code just above.
10844 : */
10845 1758 : if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))
10846 60 : appendStringInfoString(buf, "PARTIAL ");
10847 :
10848 : /* Extract the argument types as seen by the parser */
10849 1758 : nargs = get_aggregate_argtypes(aggref, argtypes);
10850 :
10851 1758 : if (!funcname)
10852 1704 : funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
10853 1704 : argtypes, aggref->aggvariadic,
10854 : &use_variadic,
10855 1704 : context->inGroupBy);
10856 :
10857 : /* Print the aggregate name, schema-qualified if needed */
10858 1758 : appendStringInfo(buf, "%s(%s", funcname,
10859 1758 : (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
10860 :
10861 1758 : if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
10862 : {
10863 : /*
10864 : * Ordered-set aggregates do not use "*" syntax. Also, we needn't
10865 : * worry about inserting VARIADIC. So we can just dump the direct
10866 : * args as-is.
10867 : */
10868 : Assert(!aggref->aggvariadic);
10869 28 : get_rule_expr((Node *) aggref->aggdirectargs, context, true);
10870 : Assert(aggref->aggorder != NIL);
10871 28 : appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
10872 28 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
10873 : }
10874 : else
10875 : {
10876 : /* aggstar can be set only in zero-argument aggregates */
10877 1730 : if (aggref->aggstar)
10878 262 : appendStringInfoChar(buf, '*');
10879 : else
10880 : {
10881 : ListCell *l;
10882 : int i;
10883 :
10884 1468 : i = 0;
10885 3124 : foreach(l, aggref->args)
10886 : {
10887 1656 : TargetEntry *tle = (TargetEntry *) lfirst(l);
10888 1656 : Node *arg = (Node *) tle->expr;
10889 :
10890 : Assert(!IsA(arg, NamedArgExpr));
10891 1656 : if (tle->resjunk)
10892 50 : continue;
10893 1606 : if (i++ > 0)
10894 : {
10895 138 : if (is_json_objectagg)
10896 : {
10897 : /*
10898 : * the ABSENT ON NULL and WITH UNIQUE args are printed
10899 : * separately, so ignore them here
10900 : */
10901 30 : if (i > 2)
10902 0 : break;
10903 :
10904 30 : appendStringInfoString(buf, " : ");
10905 : }
10906 : else
10907 108 : appendStringInfoString(buf, ", ");
10908 : }
10909 1606 : if (use_variadic && i == nargs)
10910 8 : appendStringInfoString(buf, "VARIADIC ");
10911 1606 : get_rule_expr(arg, context, true);
10912 : }
10913 : }
10914 :
10915 1730 : if (aggref->aggorder != NIL)
10916 : {
10917 82 : appendStringInfoString(buf, " ORDER BY ");
10918 82 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
10919 : }
10920 : }
10921 :
10922 1758 : if (options)
10923 54 : appendStringInfoString(buf, options);
10924 :
10925 1758 : if (aggref->aggfilter != NULL)
10926 : {
10927 46 : appendStringInfoString(buf, ") FILTER (WHERE ");
10928 46 : get_rule_expr((Node *) aggref->aggfilter, context, false);
10929 : }
10930 :
10931 1758 : appendStringInfoChar(buf, ')');
10932 : }
10933 :
10934 : /*
10935 : * This is a helper function for get_agg_expr(). It's used when we deparse
10936 : * a combining Aggref; resolve_special_varno locates the corresponding partial
10937 : * Aggref and then calls this.
10938 : */
10939 : static void
10940 260 : get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
10941 : {
10942 : Aggref *aggref;
10943 260 : Aggref *original_aggref = callback_arg;
10944 :
10945 260 : if (!IsA(node, Aggref))
10946 0 : elog(ERROR, "combining Aggref does not point to an Aggref");
10947 :
10948 260 : aggref = (Aggref *) node;
10949 260 : get_agg_expr(aggref, context, original_aggref);
10950 260 : }
10951 :
10952 : /*
10953 : * get_windowfunc_expr - Parse back a WindowFunc node
10954 : */
10955 : static void
10956 240 : get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
10957 : {
10958 240 : get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
10959 240 : }
10960 :
10961 :
10962 : /*
10963 : * get_windowfunc_expr_helper - subroutine for get_windowfunc_expr and
10964 : * get_json_agg_constructor
10965 : */
10966 : static void
10967 252 : get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
10968 : const char *funcname, const char *options,
10969 : bool is_json_objectagg)
10970 : {
10971 252 : StringInfo buf = context->buf;
10972 : Oid argtypes[FUNC_MAX_ARGS];
10973 : int nargs;
10974 : List *argnames;
10975 : ListCell *l;
10976 :
10977 252 : if (list_length(wfunc->args) > FUNC_MAX_ARGS)
10978 0 : ereport(ERROR,
10979 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
10980 : errmsg("too many arguments")));
10981 252 : nargs = 0;
10982 252 : argnames = NIL;
10983 402 : foreach(l, wfunc->args)
10984 : {
10985 150 : Node *arg = (Node *) lfirst(l);
10986 :
10987 150 : if (IsA(arg, NamedArgExpr))
10988 0 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
10989 150 : argtypes[nargs] = exprType(arg);
10990 150 : nargs++;
10991 : }
10992 :
10993 252 : if (!funcname)
10994 240 : funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
10995 : argtypes, false, NULL,
10996 240 : context->inGroupBy);
10997 :
10998 252 : appendStringInfo(buf, "%s(", funcname);
10999 :
11000 : /* winstar can be set only in zero-argument aggregates */
11001 252 : if (wfunc->winstar)
11002 24 : appendStringInfoChar(buf, '*');
11003 : else
11004 : {
11005 228 : if (is_json_objectagg)
11006 : {
11007 6 : get_rule_expr((Node *) linitial(wfunc->args), context, false);
11008 6 : appendStringInfoString(buf, " : ");
11009 6 : get_rule_expr((Node *) lsecond(wfunc->args), context, false);
11010 : }
11011 : else
11012 222 : get_rule_expr((Node *) wfunc->args, context, true);
11013 : }
11014 :
11015 252 : if (options)
11016 12 : appendStringInfoString(buf, options);
11017 :
11018 252 : if (wfunc->aggfilter != NULL)
11019 : {
11020 0 : appendStringInfoString(buf, ") FILTER (WHERE ");
11021 0 : get_rule_expr((Node *) wfunc->aggfilter, context, false);
11022 : }
11023 :
11024 252 : appendStringInfoString(buf, ") OVER ");
11025 :
11026 252 : foreach(l, context->windowClause)
11027 : {
11028 42 : WindowClause *wc = (WindowClause *) lfirst(l);
11029 :
11030 42 : if (wc->winref == wfunc->winref)
11031 : {
11032 42 : if (wc->name)
11033 0 : appendStringInfoString(buf, quote_identifier(wc->name));
11034 : else
11035 42 : get_rule_windowspec(wc, context->targetList, context);
11036 42 : break;
11037 : }
11038 : }
11039 252 : if (l == NULL)
11040 : {
11041 210 : if (context->windowClause)
11042 0 : elog(ERROR, "could not find window clause for winref %u",
11043 : wfunc->winref);
11044 :
11045 : /*
11046 : * In EXPLAIN, we don't have window context information available, so
11047 : * we have to settle for this:
11048 : */
11049 210 : appendStringInfoString(buf, "(?)");
11050 : }
11051 252 : }
11052 :
11053 : /*
11054 : * get_func_sql_syntax - Parse back a SQL-syntax function call
11055 : *
11056 : * Returns true if we successfully deparsed, false if we did not
11057 : * recognize the function.
11058 : */
11059 : static bool
11060 168 : get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
11061 : {
11062 168 : StringInfo buf = context->buf;
11063 168 : Oid funcoid = expr->funcid;
11064 :
11065 168 : switch (funcoid)
11066 : {
11067 24 : case F_TIMEZONE_INTERVAL_TIMESTAMP:
11068 : case F_TIMEZONE_INTERVAL_TIMESTAMPTZ:
11069 : case F_TIMEZONE_INTERVAL_TIMETZ:
11070 : case F_TIMEZONE_TEXT_TIMESTAMP:
11071 : case F_TIMEZONE_TEXT_TIMESTAMPTZ:
11072 : case F_TIMEZONE_TEXT_TIMETZ:
11073 : /* AT TIME ZONE ... note reversed argument order */
11074 24 : appendStringInfoChar(buf, '(');
11075 24 : get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
11076 : (Node *) expr);
11077 24 : appendStringInfoString(buf, " AT TIME ZONE ");
11078 24 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11079 : (Node *) expr);
11080 24 : appendStringInfoChar(buf, ')');
11081 24 : return true;
11082 :
11083 18 : case F_TIMEZONE_TIMESTAMP:
11084 : case F_TIMEZONE_TIMESTAMPTZ:
11085 : case F_TIMEZONE_TIMETZ:
11086 : /* AT LOCAL */
11087 18 : appendStringInfoChar(buf, '(');
11088 18 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11089 : (Node *) expr);
11090 18 : appendStringInfoString(buf, " AT LOCAL)");
11091 18 : return true;
11092 :
11093 6 : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL:
11094 : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ:
11095 : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL:
11096 : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ:
11097 : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_INTERVAL:
11098 : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_TIMESTAMP:
11099 : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_INTERVAL:
11100 : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_TIMESTAMP:
11101 : case F_OVERLAPS_TIMETZ_TIMETZ_TIMETZ_TIMETZ:
11102 : case F_OVERLAPS_TIME_INTERVAL_TIME_INTERVAL:
11103 : case F_OVERLAPS_TIME_INTERVAL_TIME_TIME:
11104 : case F_OVERLAPS_TIME_TIME_TIME_INTERVAL:
11105 : case F_OVERLAPS_TIME_TIME_TIME_TIME:
11106 : /* (x1, x2) OVERLAPS (y1, y2) */
11107 6 : appendStringInfoString(buf, "((");
11108 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11109 6 : appendStringInfoString(buf, ", ");
11110 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11111 6 : appendStringInfoString(buf, ") OVERLAPS (");
11112 6 : get_rule_expr((Node *) lthird(expr->args), context, false);
11113 6 : appendStringInfoString(buf, ", ");
11114 6 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11115 6 : appendStringInfoString(buf, "))");
11116 6 : return true;
11117 :
11118 6 : case F_EXTRACT_TEXT_DATE:
11119 : case F_EXTRACT_TEXT_TIME:
11120 : case F_EXTRACT_TEXT_TIMETZ:
11121 : case F_EXTRACT_TEXT_TIMESTAMP:
11122 : case F_EXTRACT_TEXT_TIMESTAMPTZ:
11123 : case F_EXTRACT_TEXT_INTERVAL:
11124 : /* EXTRACT (x FROM y) */
11125 6 : appendStringInfoString(buf, "EXTRACT(");
11126 : {
11127 6 : Const *con = (Const *) linitial(expr->args);
11128 :
11129 : Assert(IsA(con, Const) &&
11130 : con->consttype == TEXTOID &&
11131 : !con->constisnull);
11132 6 : appendStringInfoString(buf, TextDatumGetCString(con->constvalue));
11133 : }
11134 6 : appendStringInfoString(buf, " FROM ");
11135 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11136 6 : appendStringInfoChar(buf, ')');
11137 6 : return true;
11138 :
11139 12 : case F_IS_NORMALIZED:
11140 : /* IS xxx NORMALIZED */
11141 12 : appendStringInfoChar(buf, '(');
11142 12 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11143 : (Node *) expr);
11144 12 : appendStringInfoString(buf, " IS");
11145 12 : if (list_length(expr->args) == 2)
11146 : {
11147 6 : Const *con = (Const *) lsecond(expr->args);
11148 :
11149 : Assert(IsA(con, Const) &&
11150 : con->consttype == TEXTOID &&
11151 : !con->constisnull);
11152 6 : appendStringInfo(buf, " %s",
11153 6 : TextDatumGetCString(con->constvalue));
11154 : }
11155 12 : appendStringInfoString(buf, " NORMALIZED)");
11156 12 : return true;
11157 :
11158 6 : case F_PG_COLLATION_FOR:
11159 : /* COLLATION FOR */
11160 6 : appendStringInfoString(buf, "COLLATION FOR (");
11161 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11162 6 : appendStringInfoChar(buf, ')');
11163 6 : return true;
11164 :
11165 12 : case F_NORMALIZE:
11166 : /* NORMALIZE() */
11167 12 : appendStringInfoString(buf, "NORMALIZE(");
11168 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11169 12 : if (list_length(expr->args) == 2)
11170 : {
11171 6 : Const *con = (Const *) lsecond(expr->args);
11172 :
11173 : Assert(IsA(con, Const) &&
11174 : con->consttype == TEXTOID &&
11175 : !con->constisnull);
11176 6 : appendStringInfo(buf, ", %s",
11177 6 : TextDatumGetCString(con->constvalue));
11178 : }
11179 12 : appendStringInfoChar(buf, ')');
11180 12 : return true;
11181 :
11182 12 : case F_OVERLAY_BIT_BIT_INT4:
11183 : case F_OVERLAY_BIT_BIT_INT4_INT4:
11184 : case F_OVERLAY_BYTEA_BYTEA_INT4:
11185 : case F_OVERLAY_BYTEA_BYTEA_INT4_INT4:
11186 : case F_OVERLAY_TEXT_TEXT_INT4:
11187 : case F_OVERLAY_TEXT_TEXT_INT4_INT4:
11188 : /* OVERLAY() */
11189 12 : appendStringInfoString(buf, "OVERLAY(");
11190 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11191 12 : appendStringInfoString(buf, " PLACING ");
11192 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11193 12 : appendStringInfoString(buf, " FROM ");
11194 12 : get_rule_expr((Node *) lthird(expr->args), context, false);
11195 12 : if (list_length(expr->args) == 4)
11196 : {
11197 6 : appendStringInfoString(buf, " FOR ");
11198 6 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11199 : }
11200 12 : appendStringInfoChar(buf, ')');
11201 12 : return true;
11202 :
11203 6 : case F_POSITION_BIT_BIT:
11204 : case F_POSITION_BYTEA_BYTEA:
11205 : case F_POSITION_TEXT_TEXT:
11206 : /* POSITION() ... extra parens since args are b_expr not a_expr */
11207 6 : appendStringInfoString(buf, "POSITION((");
11208 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11209 6 : appendStringInfoString(buf, ") IN (");
11210 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11211 6 : appendStringInfoString(buf, "))");
11212 6 : return true;
11213 :
11214 6 : case F_SUBSTRING_BIT_INT4:
11215 : case F_SUBSTRING_BIT_INT4_INT4:
11216 : case F_SUBSTRING_BYTEA_INT4:
11217 : case F_SUBSTRING_BYTEA_INT4_INT4:
11218 : case F_SUBSTRING_TEXT_INT4:
11219 : case F_SUBSTRING_TEXT_INT4_INT4:
11220 : /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
11221 6 : appendStringInfoString(buf, "SUBSTRING(");
11222 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11223 6 : appendStringInfoString(buf, " FROM ");
11224 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11225 6 : if (list_length(expr->args) == 3)
11226 : {
11227 6 : appendStringInfoString(buf, " FOR ");
11228 6 : get_rule_expr((Node *) lthird(expr->args), context, false);
11229 : }
11230 6 : appendStringInfoChar(buf, ')');
11231 6 : return true;
11232 :
11233 6 : case F_SUBSTRING_TEXT_TEXT_TEXT:
11234 : /* SUBSTRING SIMILAR/ESCAPE */
11235 6 : appendStringInfoString(buf, "SUBSTRING(");
11236 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11237 6 : appendStringInfoString(buf, " SIMILAR ");
11238 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11239 6 : appendStringInfoString(buf, " ESCAPE ");
11240 6 : get_rule_expr((Node *) lthird(expr->args), context, false);
11241 6 : appendStringInfoChar(buf, ')');
11242 6 : return true;
11243 :
11244 12 : case F_BTRIM_BYTEA_BYTEA:
11245 : case F_BTRIM_TEXT:
11246 : case F_BTRIM_TEXT_TEXT:
11247 : /* TRIM() */
11248 12 : appendStringInfoString(buf, "TRIM(BOTH");
11249 12 : if (list_length(expr->args) == 2)
11250 : {
11251 12 : appendStringInfoChar(buf, ' ');
11252 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11253 : }
11254 12 : appendStringInfoString(buf, " FROM ");
11255 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11256 12 : appendStringInfoChar(buf, ')');
11257 12 : return true;
11258 :
11259 12 : case F_LTRIM_BYTEA_BYTEA:
11260 : case F_LTRIM_TEXT:
11261 : case F_LTRIM_TEXT_TEXT:
11262 : /* TRIM() */
11263 12 : appendStringInfoString(buf, "TRIM(LEADING");
11264 12 : if (list_length(expr->args) == 2)
11265 : {
11266 12 : appendStringInfoChar(buf, ' ');
11267 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11268 : }
11269 12 : appendStringInfoString(buf, " FROM ");
11270 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11271 12 : appendStringInfoChar(buf, ')');
11272 12 : return true;
11273 :
11274 12 : case F_RTRIM_BYTEA_BYTEA:
11275 : case F_RTRIM_TEXT:
11276 : case F_RTRIM_TEXT_TEXT:
11277 : /* TRIM() */
11278 12 : appendStringInfoString(buf, "TRIM(TRAILING");
11279 12 : if (list_length(expr->args) == 2)
11280 : {
11281 6 : appendStringInfoChar(buf, ' ');
11282 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11283 : }
11284 12 : appendStringInfoString(buf, " FROM ");
11285 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11286 12 : appendStringInfoChar(buf, ')');
11287 12 : return true;
11288 :
11289 12 : case F_SYSTEM_USER:
11290 12 : appendStringInfoString(buf, "SYSTEM_USER");
11291 12 : return true;
11292 :
11293 0 : case F_XMLEXISTS:
11294 : /* XMLEXISTS ... extra parens because args are c_expr */
11295 0 : appendStringInfoString(buf, "XMLEXISTS((");
11296 0 : get_rule_expr((Node *) linitial(expr->args), context, false);
11297 0 : appendStringInfoString(buf, ") PASSING (");
11298 0 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11299 0 : appendStringInfoString(buf, "))");
11300 0 : return true;
11301 : }
11302 6 : return false;
11303 : }
11304 :
11305 : /* ----------
11306 : * get_coercion_expr
11307 : *
11308 : * Make a string representation of a value coerced to a specific type
11309 : * ----------
11310 : */
11311 : static void
11312 4784 : get_coercion_expr(Node *arg, deparse_context *context,
11313 : Oid resulttype, int32 resulttypmod,
11314 : Node *parentNode)
11315 : {
11316 4784 : StringInfo buf = context->buf;
11317 :
11318 : /*
11319 : * Since parse_coerce.c doesn't immediately collapse application of
11320 : * length-coercion functions to constants, what we'll typically see in
11321 : * such cases is a Const with typmod -1 and a length-coercion function
11322 : * right above it. Avoid generating redundant output. However, beware of
11323 : * suppressing casts when the user actually wrote something like
11324 : * 'foo'::text::char(3).
11325 : *
11326 : * Note: it might seem that we are missing the possibility of needing to
11327 : * print a COLLATE clause for such a Const. However, a Const could only
11328 : * have nondefault collation in a post-constant-folding tree, in which the
11329 : * length coercion would have been folded too. See also the special
11330 : * handling of CollateExpr in coerce_to_target_type(): any collation
11331 : * marking will be above the coercion node, not below it.
11332 : */
11333 4784 : if (arg && IsA(arg, Const) &&
11334 504 : ((Const *) arg)->consttype == resulttype &&
11335 24 : ((Const *) arg)->consttypmod == -1)
11336 : {
11337 : /* Show the constant without normal ::typename decoration */
11338 24 : get_const_expr((Const *) arg, context, -1);
11339 : }
11340 : else
11341 : {
11342 4760 : if (!PRETTY_PAREN(context))
11343 4402 : appendStringInfoChar(buf, '(');
11344 4760 : get_rule_expr_paren(arg, context, false, parentNode);
11345 4760 : if (!PRETTY_PAREN(context))
11346 4402 : appendStringInfoChar(buf, ')');
11347 : }
11348 :
11349 : /*
11350 : * Never emit resulttype(arg) functional notation. A pg_proc entry could
11351 : * take precedence, and a resulttype in pg_temp would require schema
11352 : * qualification that format_type_with_typemod() would usually omit. We've
11353 : * standardized on arg::resulttype, but CAST(arg AS resulttype) notation
11354 : * would work fine.
11355 : */
11356 4784 : appendStringInfo(buf, "::%s",
11357 : format_type_with_typemod(resulttype, resulttypmod));
11358 4784 : }
11359 :
11360 : /* ----------
11361 : * get_const_expr
11362 : *
11363 : * Make a string representation of a Const
11364 : *
11365 : * showtype can be -1 to never show "::typename" decoration, or +1 to always
11366 : * show it, or 0 to show it only if the constant wouldn't be assumed to be
11367 : * the right type by default.
11368 : *
11369 : * If the Const's collation isn't default for its type, show that too.
11370 : * We mustn't do this when showtype is -1 (since that means the caller will
11371 : * print "::typename", and we can't put a COLLATE clause in between). It's
11372 : * caller's responsibility that collation isn't missed in such cases.
11373 : * ----------
11374 : */
11375 : static void
11376 65768 : get_const_expr(Const *constval, deparse_context *context, int showtype)
11377 : {
11378 65768 : StringInfo buf = context->buf;
11379 : Oid typoutput;
11380 : bool typIsVarlena;
11381 : char *extval;
11382 65768 : bool needlabel = false;
11383 :
11384 65768 : if (constval->constisnull)
11385 : {
11386 : /*
11387 : * Always label the type of a NULL constant to prevent misdecisions
11388 : * about type when reparsing.
11389 : */
11390 1062 : appendStringInfoString(buf, "NULL");
11391 1062 : if (showtype >= 0)
11392 : {
11393 1018 : appendStringInfo(buf, "::%s",
11394 : format_type_with_typemod(constval->consttype,
11395 : constval->consttypmod));
11396 1018 : get_const_collation(constval, context);
11397 : }
11398 8572 : return;
11399 : }
11400 :
11401 64706 : getTypeOutputInfo(constval->consttype,
11402 : &typoutput, &typIsVarlena);
11403 :
11404 64706 : extval = OidOutputFunctionCall(typoutput, constval->constvalue);
11405 :
11406 64706 : switch (constval->consttype)
11407 : {
11408 36942 : case INT4OID:
11409 :
11410 : /*
11411 : * INT4 can be printed without any decoration, unless it is
11412 : * negative; in that case print it as '-nnn'::integer to ensure
11413 : * that the output will re-parse as a constant, not as a constant
11414 : * plus operator. In most cases we could get away with printing
11415 : * (-nnn) instead, because of the way that gram.y handles negative
11416 : * literals; but that doesn't work for INT_MIN, and it doesn't
11417 : * seem that much prettier anyway.
11418 : */
11419 36942 : if (extval[0] != '-')
11420 36440 : appendStringInfoString(buf, extval);
11421 : else
11422 : {
11423 502 : appendStringInfo(buf, "'%s'", extval);
11424 502 : needlabel = true; /* we must attach a cast */
11425 : }
11426 36942 : break;
11427 :
11428 1060 : case NUMERICOID:
11429 :
11430 : /*
11431 : * NUMERIC can be printed without quotes if it looks like a float
11432 : * constant (not an integer, and not Infinity or NaN) and doesn't
11433 : * have a leading sign (for the same reason as for INT4).
11434 : */
11435 1060 : if (isdigit((unsigned char) extval[0]) &&
11436 1060 : strcspn(extval, "eE.") != strlen(extval))
11437 : {
11438 380 : appendStringInfoString(buf, extval);
11439 : }
11440 : else
11441 : {
11442 680 : appendStringInfo(buf, "'%s'", extval);
11443 680 : needlabel = true; /* we must attach a cast */
11444 : }
11445 1060 : break;
11446 :
11447 1468 : case BOOLOID:
11448 1468 : if (strcmp(extval, "t") == 0)
11449 696 : appendStringInfoString(buf, "true");
11450 : else
11451 772 : appendStringInfoString(buf, "false");
11452 1468 : break;
11453 :
11454 25236 : default:
11455 25236 : simple_quote_literal(buf, extval);
11456 25236 : break;
11457 : }
11458 :
11459 64706 : pfree(extval);
11460 :
11461 64706 : if (showtype < 0)
11462 7510 : return;
11463 :
11464 : /*
11465 : * For showtype == 0, append ::typename unless the constant will be
11466 : * implicitly typed as the right type when it is read in.
11467 : *
11468 : * XXX this code has to be kept in sync with the behavior of the parser,
11469 : * especially make_const.
11470 : */
11471 57196 : switch (constval->consttype)
11472 : {
11473 1536 : case BOOLOID:
11474 : case UNKNOWNOID:
11475 : /* These types can be left unlabeled */
11476 1536 : needlabel = false;
11477 1536 : break;
11478 32620 : case INT4OID:
11479 : /* We determined above whether a label is needed */
11480 32620 : break;
11481 1060 : case NUMERICOID:
11482 :
11483 : /*
11484 : * Float-looking constants will be typed as numeric, which we
11485 : * checked above; but if there's a nondefault typmod we need to
11486 : * show it.
11487 : */
11488 1060 : needlabel |= (constval->consttypmod >= 0);
11489 1060 : break;
11490 21980 : default:
11491 21980 : needlabel = true;
11492 21980 : break;
11493 : }
11494 57196 : if (needlabel || showtype > 0)
11495 23148 : appendStringInfo(buf, "::%s",
11496 : format_type_with_typemod(constval->consttype,
11497 : constval->consttypmod));
11498 :
11499 57196 : get_const_collation(constval, context);
11500 : }
11501 :
11502 : /*
11503 : * helper for get_const_expr: append COLLATE if needed
11504 : */
11505 : static void
11506 58214 : get_const_collation(Const *constval, deparse_context *context)
11507 : {
11508 58214 : StringInfo buf = context->buf;
11509 :
11510 58214 : if (OidIsValid(constval->constcollid))
11511 : {
11512 8580 : Oid typcollation = get_typcollation(constval->consttype);
11513 :
11514 8580 : if (constval->constcollid != typcollation)
11515 : {
11516 70 : appendStringInfo(buf, " COLLATE %s",
11517 : generate_collation_name(constval->constcollid));
11518 : }
11519 : }
11520 58214 : }
11521 :
11522 : /*
11523 : * get_json_path_spec - Parse back a JSON path specification
11524 : */
11525 : static void
11526 456 : get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
11527 : {
11528 456 : if (IsA(path_spec, Const))
11529 456 : get_const_expr((Const *) path_spec, context, -1);
11530 : else
11531 0 : get_rule_expr(path_spec, context, showimplicit);
11532 456 : }
11533 :
11534 : /*
11535 : * get_json_format - Parse back a JsonFormat node
11536 : */
11537 : static void
11538 186 : get_json_format(JsonFormat *format, StringInfo buf)
11539 : {
11540 186 : if (format->format_type == JS_FORMAT_DEFAULT)
11541 108 : return;
11542 :
11543 78 : appendStringInfoString(buf,
11544 78 : format->format_type == JS_FORMAT_JSONB ?
11545 : " FORMAT JSONB" : " FORMAT JSON");
11546 :
11547 78 : if (format->encoding != JS_ENC_DEFAULT)
11548 : {
11549 : const char *encoding;
11550 :
11551 6 : encoding =
11552 12 : format->encoding == JS_ENC_UTF16 ? "UTF16" :
11553 6 : format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
11554 :
11555 6 : appendStringInfo(buf, " ENCODING %s", encoding);
11556 : }
11557 : }
11558 :
11559 : /*
11560 : * get_json_returning - Parse back a JsonReturning structure
11561 : */
11562 : static void
11563 168 : get_json_returning(JsonReturning *returning, StringInfo buf,
11564 : bool json_format_by_default)
11565 : {
11566 168 : if (!OidIsValid(returning->typid))
11567 0 : return;
11568 :
11569 168 : appendStringInfo(buf, " RETURNING %s",
11570 : format_type_with_typemod(returning->typid,
11571 : returning->typmod));
11572 :
11573 324 : if (!json_format_by_default ||
11574 156 : returning->format->format_type !=
11575 156 : (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
11576 36 : get_json_format(returning->format, buf);
11577 : }
11578 :
11579 : /*
11580 : * get_json_constructor - Parse back a JsonConstructorExpr node
11581 : */
11582 : static void
11583 174 : get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
11584 : bool showimplicit)
11585 : {
11586 174 : StringInfo buf = context->buf;
11587 : const char *funcname;
11588 : bool is_json_object;
11589 : int curridx;
11590 : ListCell *lc;
11591 :
11592 174 : if (ctor->type == JSCTOR_JSON_OBJECTAGG)
11593 : {
11594 36 : get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
11595 36 : return;
11596 : }
11597 138 : else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
11598 : {
11599 30 : get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
11600 30 : return;
11601 : }
11602 :
11603 108 : switch (ctor->type)
11604 : {
11605 30 : case JSCTOR_JSON_OBJECT:
11606 30 : funcname = "JSON_OBJECT";
11607 30 : break;
11608 12 : case JSCTOR_JSON_ARRAY:
11609 12 : funcname = "JSON_ARRAY";
11610 12 : break;
11611 42 : case JSCTOR_JSON_PARSE:
11612 42 : funcname = "JSON";
11613 42 : break;
11614 12 : case JSCTOR_JSON_SCALAR:
11615 12 : funcname = "JSON_SCALAR";
11616 12 : break;
11617 12 : case JSCTOR_JSON_SERIALIZE:
11618 12 : funcname = "JSON_SERIALIZE";
11619 12 : break;
11620 0 : default:
11621 0 : elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
11622 : }
11623 :
11624 108 : appendStringInfo(buf, "%s(", funcname);
11625 :
11626 108 : is_json_object = ctor->type == JSCTOR_JSON_OBJECT;
11627 282 : foreach(lc, ctor->args)
11628 : {
11629 174 : curridx = foreach_current_index(lc);
11630 174 : if (curridx > 0)
11631 : {
11632 : const char *sep;
11633 :
11634 66 : sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
11635 66 : appendStringInfoString(buf, sep);
11636 : }
11637 :
11638 174 : get_rule_expr((Node *) lfirst(lc), context, true);
11639 : }
11640 :
11641 108 : get_json_constructor_options(ctor, buf);
11642 108 : appendStringInfoChar(buf, ')');
11643 : }
11644 :
11645 : /*
11646 : * Append options, if any, to the JSON constructor being deparsed
11647 : */
11648 : static void
11649 174 : get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
11650 : {
11651 174 : if (ctor->absent_on_null)
11652 : {
11653 24 : if (ctor->type == JSCTOR_JSON_OBJECT ||
11654 24 : ctor->type == JSCTOR_JSON_OBJECTAGG)
11655 0 : appendStringInfoString(buf, " ABSENT ON NULL");
11656 : }
11657 : else
11658 : {
11659 150 : if (ctor->type == JSCTOR_JSON_ARRAY ||
11660 150 : ctor->type == JSCTOR_JSON_ARRAYAGG)
11661 18 : appendStringInfoString(buf, " NULL ON NULL");
11662 : }
11663 :
11664 174 : if (ctor->unique)
11665 24 : appendStringInfoString(buf, " WITH UNIQUE KEYS");
11666 :
11667 : /*
11668 : * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
11669 : * support one.
11670 : */
11671 174 : if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
11672 120 : get_json_returning(ctor->returning, buf, true);
11673 174 : }
11674 :
11675 : /*
11676 : * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
11677 : */
11678 : static void
11679 66 : get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
11680 : const char *funcname, bool is_json_objectagg)
11681 : {
11682 : StringInfoData options;
11683 :
11684 66 : initStringInfo(&options);
11685 66 : get_json_constructor_options(ctor, &options);
11686 :
11687 66 : if (IsA(ctor->func, Aggref))
11688 54 : get_agg_expr_helper((Aggref *) ctor->func, context,
11689 54 : (Aggref *) ctor->func,
11690 54 : funcname, options.data, is_json_objectagg);
11691 12 : else if (IsA(ctor->func, WindowFunc))
11692 12 : get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
11693 12 : funcname, options.data,
11694 : is_json_objectagg);
11695 : else
11696 0 : elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
11697 : nodeTag(ctor->func));
11698 66 : }
11699 :
11700 : /*
11701 : * simple_quote_literal - Format a string as a SQL literal, append to buf
11702 : */
11703 : static void
11704 26296 : simple_quote_literal(StringInfo buf, const char *val)
11705 : {
11706 : const char *valptr;
11707 :
11708 : /*
11709 : * We form the string literal according to the prevailing setting of
11710 : * standard_conforming_strings; we never use E''. User is responsible for
11711 : * making sure result is used correctly.
11712 : */
11713 26296 : appendStringInfoChar(buf, '\'');
11714 267064 : for (valptr = val; *valptr; valptr++)
11715 : {
11716 240768 : char ch = *valptr;
11717 :
11718 240768 : if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
11719 306 : appendStringInfoChar(buf, ch);
11720 240768 : appendStringInfoChar(buf, ch);
11721 : }
11722 26296 : appendStringInfoChar(buf, '\'');
11723 26296 : }
11724 :
11725 :
11726 : /* ----------
11727 : * get_sublink_expr - Parse back a sublink
11728 : * ----------
11729 : */
11730 : static void
11731 460 : get_sublink_expr(SubLink *sublink, deparse_context *context)
11732 : {
11733 460 : StringInfo buf = context->buf;
11734 460 : Query *query = (Query *) (sublink->subselect);
11735 460 : char *opname = NULL;
11736 : bool need_paren;
11737 :
11738 460 : if (sublink->subLinkType == ARRAY_SUBLINK)
11739 24 : appendStringInfoString(buf, "ARRAY(");
11740 : else
11741 436 : appendStringInfoChar(buf, '(');
11742 :
11743 : /*
11744 : * Note that we print the name of only the first operator, when there are
11745 : * multiple combining operators. This is an approximation that could go
11746 : * wrong in various scenarios (operators in different schemas, renamed
11747 : * operators, etc) but there is not a whole lot we can do about it, since
11748 : * the syntax allows only one operator to be shown.
11749 : */
11750 460 : if (sublink->testexpr)
11751 : {
11752 18 : if (IsA(sublink->testexpr, OpExpr))
11753 : {
11754 : /* single combining operator */
11755 6 : OpExpr *opexpr = (OpExpr *) sublink->testexpr;
11756 :
11757 6 : get_rule_expr(linitial(opexpr->args), context, true);
11758 6 : opname = generate_operator_name(opexpr->opno,
11759 6 : exprType(linitial(opexpr->args)),
11760 6 : exprType(lsecond(opexpr->args)));
11761 : }
11762 12 : else if (IsA(sublink->testexpr, BoolExpr))
11763 : {
11764 : /* multiple combining operators, = or <> cases */
11765 : char *sep;
11766 : ListCell *l;
11767 :
11768 6 : appendStringInfoChar(buf, '(');
11769 6 : sep = "";
11770 18 : foreach(l, ((BoolExpr *) sublink->testexpr)->args)
11771 : {
11772 12 : OpExpr *opexpr = lfirst_node(OpExpr, l);
11773 :
11774 12 : appendStringInfoString(buf, sep);
11775 12 : get_rule_expr(linitial(opexpr->args), context, true);
11776 12 : if (!opname)
11777 6 : opname = generate_operator_name(opexpr->opno,
11778 6 : exprType(linitial(opexpr->args)),
11779 6 : exprType(lsecond(opexpr->args)));
11780 12 : sep = ", ";
11781 : }
11782 6 : appendStringInfoChar(buf, ')');
11783 : }
11784 6 : else if (IsA(sublink->testexpr, RowCompareExpr))
11785 : {
11786 : /* multiple combining operators, < <= > >= cases */
11787 6 : RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
11788 :
11789 6 : appendStringInfoChar(buf, '(');
11790 6 : get_rule_expr((Node *) rcexpr->largs, context, true);
11791 6 : opname = generate_operator_name(linitial_oid(rcexpr->opnos),
11792 6 : exprType(linitial(rcexpr->largs)),
11793 6 : exprType(linitial(rcexpr->rargs)));
11794 6 : appendStringInfoChar(buf, ')');
11795 : }
11796 : else
11797 0 : elog(ERROR, "unrecognized testexpr type: %d",
11798 : (int) nodeTag(sublink->testexpr));
11799 : }
11800 :
11801 460 : need_paren = true;
11802 :
11803 460 : switch (sublink->subLinkType)
11804 : {
11805 176 : case EXISTS_SUBLINK:
11806 176 : appendStringInfoString(buf, "EXISTS ");
11807 176 : break;
11808 :
11809 12 : case ANY_SUBLINK:
11810 12 : if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
11811 6 : appendStringInfoString(buf, " IN ");
11812 : else
11813 6 : appendStringInfo(buf, " %s ANY ", opname);
11814 12 : break;
11815 :
11816 6 : case ALL_SUBLINK:
11817 6 : appendStringInfo(buf, " %s ALL ", opname);
11818 6 : break;
11819 :
11820 0 : case ROWCOMPARE_SUBLINK:
11821 0 : appendStringInfo(buf, " %s ", opname);
11822 0 : break;
11823 :
11824 266 : case EXPR_SUBLINK:
11825 : case MULTIEXPR_SUBLINK:
11826 : case ARRAY_SUBLINK:
11827 266 : need_paren = false;
11828 266 : break;
11829 :
11830 0 : case CTE_SUBLINK: /* shouldn't occur in a SubLink */
11831 : default:
11832 0 : elog(ERROR, "unrecognized sublink type: %d",
11833 : (int) sublink->subLinkType);
11834 : break;
11835 : }
11836 :
11837 460 : if (need_paren)
11838 194 : appendStringInfoChar(buf, '(');
11839 :
11840 460 : get_query_def(query, buf, context->namespaces, NULL, false,
11841 : context->prettyFlags, context->wrapColumn,
11842 : context->indentLevel);
11843 :
11844 460 : if (need_paren)
11845 194 : appendStringInfoString(buf, "))");
11846 : else
11847 266 : appendStringInfoChar(buf, ')');
11848 460 : }
11849 :
11850 :
11851 : /* ----------
11852 : * get_xmltable - Parse back a XMLTABLE function
11853 : * ----------
11854 : */
11855 : static void
11856 62 : get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
11857 : {
11858 62 : StringInfo buf = context->buf;
11859 :
11860 62 : appendStringInfoString(buf, "XMLTABLE(");
11861 :
11862 62 : if (tf->ns_uris != NIL)
11863 : {
11864 : ListCell *lc1,
11865 : *lc2;
11866 16 : bool first = true;
11867 :
11868 16 : appendStringInfoString(buf, "XMLNAMESPACES (");
11869 32 : forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
11870 : {
11871 16 : Node *expr = (Node *) lfirst(lc1);
11872 16 : String *ns_node = lfirst_node(String, lc2);
11873 :
11874 16 : if (!first)
11875 0 : appendStringInfoString(buf, ", ");
11876 : else
11877 16 : first = false;
11878 :
11879 16 : if (ns_node != NULL)
11880 : {
11881 16 : get_rule_expr(expr, context, showimplicit);
11882 16 : appendStringInfo(buf, " AS %s",
11883 16 : quote_identifier(strVal(ns_node)));
11884 : }
11885 : else
11886 : {
11887 0 : appendStringInfoString(buf, "DEFAULT ");
11888 0 : get_rule_expr(expr, context, showimplicit);
11889 : }
11890 : }
11891 16 : appendStringInfoString(buf, "), ");
11892 : }
11893 :
11894 62 : appendStringInfoChar(buf, '(');
11895 62 : get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
11896 62 : appendStringInfoString(buf, ") PASSING (");
11897 62 : get_rule_expr((Node *) tf->docexpr, context, showimplicit);
11898 62 : appendStringInfoChar(buf, ')');
11899 :
11900 62 : if (tf->colexprs != NIL)
11901 : {
11902 : ListCell *l1;
11903 : ListCell *l2;
11904 : ListCell *l3;
11905 : ListCell *l4;
11906 : ListCell *l5;
11907 62 : int colnum = 0;
11908 :
11909 62 : appendStringInfoString(buf, " COLUMNS ");
11910 374 : forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
11911 : l4, tf->colexprs, l5, tf->coldefexprs)
11912 : {
11913 312 : char *colname = strVal(lfirst(l1));
11914 312 : Oid typid = lfirst_oid(l2);
11915 312 : int32 typmod = lfirst_int(l3);
11916 312 : Node *colexpr = (Node *) lfirst(l4);
11917 312 : Node *coldefexpr = (Node *) lfirst(l5);
11918 312 : bool ordinality = (tf->ordinalitycol == colnum);
11919 312 : bool notnull = bms_is_member(colnum, tf->notnulls);
11920 :
11921 312 : if (colnum > 0)
11922 250 : appendStringInfoString(buf, ", ");
11923 312 : colnum++;
11924 :
11925 590 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
11926 : ordinality ? "FOR ORDINALITY" :
11927 278 : format_type_with_typemod(typid, typmod));
11928 312 : if (ordinality)
11929 34 : continue;
11930 :
11931 278 : if (coldefexpr != NULL)
11932 : {
11933 34 : appendStringInfoString(buf, " DEFAULT (");
11934 34 : get_rule_expr((Node *) coldefexpr, context, showimplicit);
11935 34 : appendStringInfoChar(buf, ')');
11936 : }
11937 278 : if (colexpr != NULL)
11938 : {
11939 254 : appendStringInfoString(buf, " PATH (");
11940 254 : get_rule_expr((Node *) colexpr, context, showimplicit);
11941 254 : appendStringInfoChar(buf, ')');
11942 : }
11943 278 : if (notnull)
11944 34 : appendStringInfoString(buf, " NOT NULL");
11945 : }
11946 : }
11947 :
11948 62 : appendStringInfoChar(buf, ')');
11949 62 : }
11950 :
11951 : /*
11952 : * get_json_table_nested_columns - Parse back nested JSON_TABLE columns
11953 : */
11954 : static void
11955 102 : get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
11956 : deparse_context *context, bool showimplicit,
11957 : bool needcomma)
11958 : {
11959 102 : if (IsA(plan, JsonTablePathScan))
11960 : {
11961 72 : JsonTablePathScan *scan = castNode(JsonTablePathScan, plan);
11962 :
11963 72 : if (needcomma)
11964 48 : appendStringInfoChar(context->buf, ',');
11965 :
11966 72 : appendStringInfoChar(context->buf, ' ');
11967 72 : appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
11968 72 : get_const_expr(scan->path->value, context, -1);
11969 72 : appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
11970 72 : get_json_table_columns(tf, scan, context, showimplicit);
11971 : }
11972 30 : else if (IsA(plan, JsonTableSiblingJoin))
11973 : {
11974 30 : JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
11975 :
11976 30 : get_json_table_nested_columns(tf, join->lplan, context, showimplicit,
11977 : needcomma);
11978 30 : get_json_table_nested_columns(tf, join->rplan, context, showimplicit,
11979 : true);
11980 : }
11981 102 : }
11982 :
11983 : /*
11984 : * get_json_table_columns - Parse back JSON_TABLE columns
11985 : */
11986 : static void
11987 180 : get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
11988 : deparse_context *context,
11989 : bool showimplicit)
11990 : {
11991 180 : StringInfo buf = context->buf;
11992 : ListCell *lc_colname;
11993 : ListCell *lc_coltype;
11994 : ListCell *lc_coltypmod;
11995 : ListCell *lc_colvalexpr;
11996 180 : int colnum = 0;
11997 :
11998 180 : appendStringInfoChar(buf, ' ');
11999 180 : appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12000 :
12001 180 : if (PRETTY_INDENT(context))
12002 138 : context->indentLevel += PRETTYINDENT_VAR;
12003 :
12004 858 : forfour(lc_colname, tf->colnames,
12005 : lc_coltype, tf->coltypes,
12006 : lc_coltypmod, tf->coltypmods,
12007 : lc_colvalexpr, tf->colvalexprs)
12008 : {
12009 726 : char *colname = strVal(lfirst(lc_colname));
12010 : JsonExpr *colexpr;
12011 : Oid typid;
12012 : int32 typmod;
12013 : bool ordinality;
12014 : JsonBehaviorType default_behavior;
12015 :
12016 726 : typid = lfirst_oid(lc_coltype);
12017 726 : typmod = lfirst_int(lc_coltypmod);
12018 726 : colexpr = castNode(JsonExpr, lfirst(lc_colvalexpr));
12019 :
12020 : /* Skip columns that don't belong to this scan. */
12021 726 : if (scan->colMin < 0 || colnum < scan->colMin)
12022 : {
12023 264 : colnum++;
12024 264 : continue;
12025 : }
12026 462 : if (colnum > scan->colMax)
12027 48 : break;
12028 :
12029 414 : if (colnum > scan->colMin)
12030 258 : appendStringInfoString(buf, ", ");
12031 :
12032 414 : colnum++;
12033 :
12034 414 : ordinality = !colexpr;
12035 :
12036 414 : appendContextKeyword(context, "", 0, 0, 0);
12037 :
12038 810 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
12039 : ordinality ? "FOR ORDINALITY" :
12040 396 : format_type_with_typemod(typid, typmod));
12041 414 : if (ordinality)
12042 18 : continue;
12043 :
12044 : /*
12045 : * Set default_behavior to guide get_json_expr_options() on whether to
12046 : * to emit the ON ERROR / EMPTY clauses.
12047 : */
12048 396 : if (colexpr->op == JSON_EXISTS_OP)
12049 : {
12050 36 : appendStringInfoString(buf, " EXISTS");
12051 36 : default_behavior = JSON_BEHAVIOR_FALSE;
12052 : }
12053 : else
12054 : {
12055 360 : if (colexpr->op == JSON_QUERY_OP)
12056 : {
12057 : char typcategory;
12058 : bool typispreferred;
12059 :
12060 174 : get_type_category_preferred(typid, &typcategory, &typispreferred);
12061 :
12062 174 : if (typcategory == TYPCATEGORY_STRING)
12063 36 : appendStringInfoString(buf,
12064 36 : colexpr->format->format_type == JS_FORMAT_JSONB ?
12065 : " FORMAT JSONB" : " FORMAT JSON");
12066 : }
12067 :
12068 360 : default_behavior = JSON_BEHAVIOR_NULL;
12069 : }
12070 :
12071 396 : appendStringInfoString(buf, " PATH ");
12072 :
12073 396 : get_json_path_spec(colexpr->path_spec, context, showimplicit);
12074 :
12075 396 : get_json_expr_options(colexpr, context, default_behavior);
12076 : }
12077 :
12078 180 : if (scan->child)
12079 42 : get_json_table_nested_columns(tf, scan->child, context, showimplicit,
12080 42 : scan->colMin >= 0);
12081 :
12082 180 : if (PRETTY_INDENT(context))
12083 138 : context->indentLevel -= PRETTYINDENT_VAR;
12084 :
12085 180 : appendContextKeyword(context, ")", 0, 0, 0);
12086 180 : }
12087 :
12088 : /* ----------
12089 : * get_json_table - Parse back a JSON_TABLE function
12090 : * ----------
12091 : */
12092 : static void
12093 108 : get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
12094 : {
12095 108 : StringInfo buf = context->buf;
12096 108 : JsonExpr *jexpr = castNode(JsonExpr, tf->docexpr);
12097 108 : JsonTablePathScan *root = castNode(JsonTablePathScan, tf->plan);
12098 :
12099 108 : appendStringInfoString(buf, "JSON_TABLE(");
12100 :
12101 108 : if (PRETTY_INDENT(context))
12102 66 : context->indentLevel += PRETTYINDENT_VAR;
12103 :
12104 108 : appendContextKeyword(context, "", 0, 0, 0);
12105 :
12106 108 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12107 :
12108 108 : appendStringInfoString(buf, ", ");
12109 :
12110 108 : get_const_expr(root->path->value, context, -1);
12111 :
12112 108 : appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12113 :
12114 108 : if (jexpr->passing_values)
12115 : {
12116 : ListCell *lc1,
12117 : *lc2;
12118 84 : bool needcomma = false;
12119 :
12120 84 : appendStringInfoChar(buf, ' ');
12121 84 : appendContextKeyword(context, "PASSING ", 0, 0, 0);
12122 :
12123 84 : if (PRETTY_INDENT(context))
12124 42 : context->indentLevel += PRETTYINDENT_VAR;
12125 :
12126 252 : forboth(lc1, jexpr->passing_names,
12127 : lc2, jexpr->passing_values)
12128 : {
12129 168 : if (needcomma)
12130 84 : appendStringInfoString(buf, ", ");
12131 168 : needcomma = true;
12132 :
12133 168 : appendContextKeyword(context, "", 0, 0, 0);
12134 :
12135 168 : get_rule_expr((Node *) lfirst(lc2), context, false);
12136 168 : appendStringInfo(buf, " AS %s",
12137 168 : quote_identifier((lfirst_node(String, lc1))->sval)
12138 : );
12139 : }
12140 :
12141 84 : if (PRETTY_INDENT(context))
12142 42 : context->indentLevel -= PRETTYINDENT_VAR;
12143 : }
12144 :
12145 108 : get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12146 : showimplicit);
12147 :
12148 108 : if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12149 6 : get_json_behavior(jexpr->on_error, context, "ERROR");
12150 :
12151 108 : if (PRETTY_INDENT(context))
12152 66 : context->indentLevel -= PRETTYINDENT_VAR;
12153 :
12154 108 : appendContextKeyword(context, ")", 0, 0, 0);
12155 108 : }
12156 :
12157 : /* ----------
12158 : * get_tablefunc - Parse back a table function
12159 : * ----------
12160 : */
12161 : static void
12162 170 : get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
12163 : {
12164 : /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12165 :
12166 170 : if (tf->functype == TFT_XMLTABLE)
12167 62 : get_xmltable(tf, context, showimplicit);
12168 108 : else if (tf->functype == TFT_JSON_TABLE)
12169 108 : get_json_table(tf, context, showimplicit);
12170 170 : }
12171 :
12172 : /* ----------
12173 : * get_from_clause - Parse back a FROM clause
12174 : *
12175 : * "prefix" is the keyword that denotes the start of the list of FROM
12176 : * elements. It is FROM when used to parse back SELECT and UPDATE, but
12177 : * is USING when parsing back DELETE.
12178 : * ----------
12179 : */
12180 : static void
12181 4818 : get_from_clause(Query *query, const char *prefix, deparse_context *context)
12182 : {
12183 4818 : StringInfo buf = context->buf;
12184 4818 : bool first = true;
12185 : ListCell *l;
12186 :
12187 : /*
12188 : * We use the query's jointree as a guide to what to print. However, we
12189 : * must ignore auto-added RTEs that are marked not inFromCl. (These can
12190 : * only appear at the top level of the jointree, so it's sufficient to
12191 : * check here.) This check also ensures we ignore the rule pseudo-RTEs
12192 : * for NEW and OLD.
12193 : */
12194 9610 : foreach(l, query->jointree->fromlist)
12195 : {
12196 4792 : Node *jtnode = (Node *) lfirst(l);
12197 :
12198 4792 : if (IsA(jtnode, RangeTblRef))
12199 : {
12200 3840 : int varno = ((RangeTblRef *) jtnode)->rtindex;
12201 3840 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12202 :
12203 3840 : if (!rte->inFromCl)
12204 400 : continue;
12205 : }
12206 :
12207 4392 : if (first)
12208 : {
12209 4046 : appendContextKeyword(context, prefix,
12210 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
12211 4046 : first = false;
12212 :
12213 4046 : get_from_clause_item(jtnode, query, context);
12214 : }
12215 : else
12216 : {
12217 : StringInfoData itembuf;
12218 :
12219 346 : appendStringInfoString(buf, ", ");
12220 :
12221 : /*
12222 : * Put the new FROM item's text into itembuf so we can decide
12223 : * after we've got it whether or not it needs to go on a new line.
12224 : */
12225 346 : initStringInfo(&itembuf);
12226 346 : context->buf = &itembuf;
12227 :
12228 346 : get_from_clause_item(jtnode, query, context);
12229 :
12230 : /* Restore context's output buffer */
12231 346 : context->buf = buf;
12232 :
12233 : /* Consider line-wrapping if enabled */
12234 346 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
12235 : {
12236 : /* Does the new item start with a new line? */
12237 346 : if (itembuf.len > 0 && itembuf.data[0] == '\n')
12238 : {
12239 : /* If so, we shouldn't add anything */
12240 : /* instead, remove any trailing spaces currently in buf */
12241 0 : removeStringInfoSpaces(buf);
12242 : }
12243 : else
12244 : {
12245 : char *trailing_nl;
12246 :
12247 : /* Locate the start of the current line in the buffer */
12248 346 : trailing_nl = strrchr(buf->data, '\n');
12249 346 : if (trailing_nl == NULL)
12250 0 : trailing_nl = buf->data;
12251 : else
12252 346 : trailing_nl++;
12253 :
12254 : /*
12255 : * Add a newline, plus some indentation, if the new item
12256 : * would cause an overflow.
12257 : */
12258 346 : if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
12259 346 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
12260 : PRETTYINDENT_STD,
12261 : PRETTYINDENT_VAR);
12262 : }
12263 : }
12264 :
12265 : /* Add the new item */
12266 346 : appendBinaryStringInfo(buf, itembuf.data, itembuf.len);
12267 :
12268 : /* clean up */
12269 346 : pfree(itembuf.data);
12270 : }
12271 : }
12272 4818 : }
12273 :
12274 : static void
12275 7388 : get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
12276 : {
12277 7388 : StringInfo buf = context->buf;
12278 7388 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
12279 :
12280 7388 : if (IsA(jtnode, RangeTblRef))
12281 : {
12282 5890 : int varno = ((RangeTblRef *) jtnode)->rtindex;
12283 5890 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12284 5890 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
12285 5890 : RangeTblFunction *rtfunc1 = NULL;
12286 :
12287 5890 : if (rte->lateral)
12288 112 : appendStringInfoString(buf, "LATERAL ");
12289 :
12290 : /* Print the FROM item proper */
12291 5890 : switch (rte->rtekind)
12292 : {
12293 4494 : case RTE_RELATION:
12294 : /* Normal relation RTE */
12295 8988 : appendStringInfo(buf, "%s%s",
12296 4494 : only_marker(rte),
12297 : generate_relation_name(rte->relid,
12298 : context->namespaces));
12299 4494 : break;
12300 292 : case RTE_SUBQUERY:
12301 : /* Subquery RTE */
12302 292 : appendStringInfoChar(buf, '(');
12303 292 : get_query_def(rte->subquery, buf, context->namespaces, NULL,
12304 : true,
12305 : context->prettyFlags, context->wrapColumn,
12306 : context->indentLevel);
12307 292 : appendStringInfoChar(buf, ')');
12308 292 : break;
12309 810 : case RTE_FUNCTION:
12310 : /* Function RTE */
12311 810 : rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
12312 :
12313 : /*
12314 : * Omit ROWS FROM() syntax for just one function, unless it
12315 : * has both a coldeflist and WITH ORDINALITY. If it has both,
12316 : * we must use ROWS FROM() syntax to avoid ambiguity about
12317 : * whether the coldeflist includes the ordinality column.
12318 : */
12319 810 : if (list_length(rte->functions) == 1 &&
12320 780 : (rtfunc1->funccolnames == NIL || !rte->funcordinality))
12321 : {
12322 780 : get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
12323 : /* we'll print the coldeflist below, if it has one */
12324 : }
12325 : else
12326 : {
12327 : bool all_unnest;
12328 : ListCell *lc;
12329 :
12330 : /*
12331 : * If all the function calls in the list are to unnest,
12332 : * and none need a coldeflist, then collapse the list back
12333 : * down to UNNEST(args). (If we had more than one
12334 : * built-in unnest function, this would get more
12335 : * difficult.)
12336 : *
12337 : * XXX This is pretty ugly, since it makes not-terribly-
12338 : * future-proof assumptions about what the parser would do
12339 : * with the output; but the alternative is to emit our
12340 : * nonstandard ROWS FROM() notation for what might have
12341 : * been a perfectly spec-compliant multi-argument
12342 : * UNNEST().
12343 : */
12344 30 : all_unnest = true;
12345 78 : foreach(lc, rte->functions)
12346 : {
12347 66 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12348 :
12349 66 : if (!IsA(rtfunc->funcexpr, FuncExpr) ||
12350 66 : ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
12351 48 : rtfunc->funccolnames != NIL)
12352 : {
12353 18 : all_unnest = false;
12354 18 : break;
12355 : }
12356 : }
12357 :
12358 30 : if (all_unnest)
12359 : {
12360 12 : List *allargs = NIL;
12361 :
12362 48 : foreach(lc, rte->functions)
12363 : {
12364 36 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12365 36 : List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
12366 :
12367 36 : allargs = list_concat(allargs, args);
12368 : }
12369 :
12370 12 : appendStringInfoString(buf, "UNNEST(");
12371 12 : get_rule_expr((Node *) allargs, context, true);
12372 12 : appendStringInfoChar(buf, ')');
12373 : }
12374 : else
12375 : {
12376 18 : int funcno = 0;
12377 :
12378 18 : appendStringInfoString(buf, "ROWS FROM(");
12379 66 : foreach(lc, rte->functions)
12380 : {
12381 48 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12382 :
12383 48 : if (funcno > 0)
12384 30 : appendStringInfoString(buf, ", ");
12385 48 : get_rule_expr_funccall(rtfunc->funcexpr, context, true);
12386 48 : if (rtfunc->funccolnames != NIL)
12387 : {
12388 : /* Reconstruct the column definition list */
12389 6 : appendStringInfoString(buf, " AS ");
12390 6 : get_from_clause_coldeflist(rtfunc,
12391 : NULL,
12392 : context);
12393 : }
12394 48 : funcno++;
12395 : }
12396 18 : appendStringInfoChar(buf, ')');
12397 : }
12398 : /* prevent printing duplicate coldeflist below */
12399 30 : rtfunc1 = NULL;
12400 : }
12401 810 : if (rte->funcordinality)
12402 18 : appendStringInfoString(buf, " WITH ORDINALITY");
12403 810 : break;
12404 98 : case RTE_TABLEFUNC:
12405 98 : get_tablefunc(rte->tablefunc, context, true);
12406 98 : break;
12407 12 : case RTE_VALUES:
12408 : /* Values list RTE */
12409 12 : appendStringInfoChar(buf, '(');
12410 12 : get_values_def(rte->values_lists, context);
12411 12 : appendStringInfoChar(buf, ')');
12412 12 : break;
12413 184 : case RTE_CTE:
12414 184 : appendStringInfoString(buf, quote_identifier(rte->ctename));
12415 184 : break;
12416 0 : default:
12417 0 : elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
12418 : break;
12419 : }
12420 :
12421 : /* Print the relation alias, if needed */
12422 5890 : get_rte_alias(rte, varno, false, context);
12423 :
12424 : /* Print the column definitions or aliases, if needed */
12425 5890 : if (rtfunc1 && rtfunc1->funccolnames != NIL)
12426 : {
12427 : /* Reconstruct the columndef list, which is also the aliases */
12428 0 : get_from_clause_coldeflist(rtfunc1, colinfo, context);
12429 : }
12430 : else
12431 : {
12432 : /* Else print column aliases as needed */
12433 5890 : get_column_alias_list(colinfo, context);
12434 : }
12435 :
12436 : /* Tablesample clause must go after any alias */
12437 5890 : if (rte->rtekind == RTE_RELATION && rte->tablesample)
12438 32 : get_tablesample_def(rte->tablesample, context);
12439 : }
12440 1498 : else if (IsA(jtnode, JoinExpr))
12441 : {
12442 1498 : JoinExpr *j = (JoinExpr *) jtnode;
12443 1498 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
12444 : bool need_paren_on_right;
12445 :
12446 3434 : need_paren_on_right = PRETTY_PAREN(context) &&
12447 1498 : !IsA(j->rarg, RangeTblRef) &&
12448 0 : !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
12449 :
12450 1498 : if (!PRETTY_PAREN(context) || j->alias != NULL)
12451 1168 : appendStringInfoChar(buf, '(');
12452 :
12453 1498 : get_from_clause_item(j->larg, query, context);
12454 :
12455 1498 : switch (j->jointype)
12456 : {
12457 820 : case JOIN_INNER:
12458 820 : if (j->quals)
12459 778 : appendContextKeyword(context, " JOIN ",
12460 : -PRETTYINDENT_STD,
12461 : PRETTYINDENT_STD,
12462 : PRETTYINDENT_JOIN);
12463 : else
12464 42 : appendContextKeyword(context, " CROSS JOIN ",
12465 : -PRETTYINDENT_STD,
12466 : PRETTYINDENT_STD,
12467 : PRETTYINDENT_JOIN);
12468 820 : break;
12469 576 : case JOIN_LEFT:
12470 576 : appendContextKeyword(context, " LEFT JOIN ",
12471 : -PRETTYINDENT_STD,
12472 : PRETTYINDENT_STD,
12473 : PRETTYINDENT_JOIN);
12474 576 : break;
12475 102 : case JOIN_FULL:
12476 102 : appendContextKeyword(context, " FULL JOIN ",
12477 : -PRETTYINDENT_STD,
12478 : PRETTYINDENT_STD,
12479 : PRETTYINDENT_JOIN);
12480 102 : break;
12481 0 : case JOIN_RIGHT:
12482 0 : appendContextKeyword(context, " RIGHT JOIN ",
12483 : -PRETTYINDENT_STD,
12484 : PRETTYINDENT_STD,
12485 : PRETTYINDENT_JOIN);
12486 0 : break;
12487 0 : default:
12488 0 : elog(ERROR, "unrecognized join type: %d",
12489 : (int) j->jointype);
12490 : }
12491 :
12492 1498 : if (need_paren_on_right)
12493 0 : appendStringInfoChar(buf, '(');
12494 1498 : get_from_clause_item(j->rarg, query, context);
12495 1498 : if (need_paren_on_right)
12496 0 : appendStringInfoChar(buf, ')');
12497 :
12498 1498 : if (j->usingClause)
12499 : {
12500 : ListCell *lc;
12501 424 : bool first = true;
12502 :
12503 424 : appendStringInfoString(buf, " USING (");
12504 : /* Use the assigned names, not what's in usingClause */
12505 1004 : foreach(lc, colinfo->usingNames)
12506 : {
12507 580 : char *colname = (char *) lfirst(lc);
12508 :
12509 580 : if (first)
12510 424 : first = false;
12511 : else
12512 156 : appendStringInfoString(buf, ", ");
12513 580 : appendStringInfoString(buf, quote_identifier(colname));
12514 : }
12515 424 : appendStringInfoChar(buf, ')');
12516 :
12517 424 : if (j->join_using_alias)
12518 12 : appendStringInfo(buf, " AS %s",
12519 12 : quote_identifier(j->join_using_alias->aliasname));
12520 : }
12521 1074 : else if (j->quals)
12522 : {
12523 1026 : appendStringInfoString(buf, " ON ");
12524 1026 : if (!PRETTY_PAREN(context))
12525 1020 : appendStringInfoChar(buf, '(');
12526 1026 : get_rule_expr(j->quals, context, false);
12527 1026 : if (!PRETTY_PAREN(context))
12528 1020 : appendStringInfoChar(buf, ')');
12529 : }
12530 48 : else if (j->jointype != JOIN_INNER)
12531 : {
12532 : /* If we didn't say CROSS JOIN above, we must provide an ON */
12533 6 : appendStringInfoString(buf, " ON TRUE");
12534 : }
12535 :
12536 1498 : if (!PRETTY_PAREN(context) || j->alias != NULL)
12537 1168 : appendStringInfoChar(buf, ')');
12538 :
12539 : /* Yes, it's correct to put alias after the right paren ... */
12540 1498 : if (j->alias != NULL)
12541 : {
12542 : /*
12543 : * Note that it's correct to emit an alias clause if and only if
12544 : * there was one originally. Otherwise we'd be converting a named
12545 : * join to unnamed or vice versa, which creates semantic
12546 : * subtleties we don't want. However, we might print a different
12547 : * alias name than was there originally.
12548 : */
12549 108 : appendStringInfo(buf, " %s",
12550 108 : quote_identifier(get_rtable_name(j->rtindex,
12551 : context)));
12552 108 : get_column_alias_list(colinfo, context);
12553 : }
12554 : }
12555 : else
12556 0 : elog(ERROR, "unrecognized node type: %d",
12557 : (int) nodeTag(jtnode));
12558 7388 : }
12559 :
12560 : /*
12561 : * get_rte_alias - print the relation's alias, if needed
12562 : *
12563 : * If printed, the alias is preceded by a space, or by " AS " if use_as is true.
12564 : */
12565 : static void
12566 6472 : get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
12567 : deparse_context *context)
12568 : {
12569 6472 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
12570 6472 : char *refname = get_rtable_name(varno, context);
12571 6472 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
12572 6472 : bool printalias = false;
12573 :
12574 6472 : if (rte->alias != NULL)
12575 : {
12576 : /* Always print alias if user provided one */
12577 3034 : printalias = true;
12578 : }
12579 3438 : else if (colinfo->printaliases)
12580 : {
12581 : /* Always print alias if we need to print column aliases */
12582 294 : printalias = true;
12583 : }
12584 3144 : else if (rte->rtekind == RTE_RELATION)
12585 : {
12586 : /*
12587 : * No need to print alias if it's same as relation name (this would
12588 : * normally be the case, but not if set_rtable_names had to resolve a
12589 : * conflict).
12590 : */
12591 2872 : if (strcmp(refname, get_relation_name(rte->relid)) != 0)
12592 80 : printalias = true;
12593 : }
12594 272 : else if (rte->rtekind == RTE_FUNCTION)
12595 : {
12596 : /*
12597 : * For a function RTE, always print alias. This covers possible
12598 : * renaming of the function and/or instability of the FigureColname
12599 : * rules for things that aren't simple functions. Note we'd need to
12600 : * force it anyway for the columndef list case.
12601 : */
12602 0 : printalias = true;
12603 : }
12604 272 : else if (rte->rtekind == RTE_SUBQUERY ||
12605 248 : rte->rtekind == RTE_VALUES)
12606 : {
12607 : /*
12608 : * For a subquery, always print alias. This makes the output
12609 : * SQL-spec-compliant, even though we allow such aliases to be omitted
12610 : * on input.
12611 : */
12612 36 : printalias = true;
12613 : }
12614 236 : else if (rte->rtekind == RTE_CTE)
12615 : {
12616 : /*
12617 : * No need to print alias if it's same as CTE name (this would
12618 : * normally be the case, but not if set_rtable_names had to resolve a
12619 : * conflict).
12620 : */
12621 144 : if (strcmp(refname, rte->ctename) != 0)
12622 22 : printalias = true;
12623 : }
12624 :
12625 6472 : if (printalias)
12626 3466 : appendStringInfo(context->buf, "%s%s",
12627 : use_as ? " AS " : " ",
12628 : quote_identifier(refname));
12629 6472 : }
12630 :
12631 : /*
12632 : * get_column_alias_list - print column alias list for an RTE
12633 : *
12634 : * Caller must already have printed the relation's alias name.
12635 : */
12636 : static void
12637 5998 : get_column_alias_list(deparse_columns *colinfo, deparse_context *context)
12638 : {
12639 5998 : StringInfo buf = context->buf;
12640 : int i;
12641 5998 : bool first = true;
12642 :
12643 : /* Don't print aliases if not needed */
12644 5998 : if (!colinfo->printaliases)
12645 4828 : return;
12646 :
12647 9420 : for (i = 0; i < colinfo->num_new_cols; i++)
12648 : {
12649 8250 : char *colname = colinfo->new_colnames[i];
12650 :
12651 8250 : if (first)
12652 : {
12653 1170 : appendStringInfoChar(buf, '(');
12654 1170 : first = false;
12655 : }
12656 : else
12657 7080 : appendStringInfoString(buf, ", ");
12658 8250 : appendStringInfoString(buf, quote_identifier(colname));
12659 : }
12660 1170 : if (!first)
12661 1170 : appendStringInfoChar(buf, ')');
12662 : }
12663 :
12664 : /*
12665 : * get_from_clause_coldeflist - reproduce FROM clause coldeflist
12666 : *
12667 : * When printing a top-level coldeflist (which is syntactically also the
12668 : * relation's column alias list), use column names from colinfo. But when
12669 : * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
12670 : * original coldeflist's names, which are available in rtfunc->funccolnames.
12671 : * Pass NULL for colinfo to select the latter behavior.
12672 : *
12673 : * The coldeflist is appended immediately (no space) to buf. Caller is
12674 : * responsible for ensuring that an alias or AS is present before it.
12675 : */
12676 : static void
12677 6 : get_from_clause_coldeflist(RangeTblFunction *rtfunc,
12678 : deparse_columns *colinfo,
12679 : deparse_context *context)
12680 : {
12681 6 : StringInfo buf = context->buf;
12682 : ListCell *l1;
12683 : ListCell *l2;
12684 : ListCell *l3;
12685 : ListCell *l4;
12686 : int i;
12687 :
12688 6 : appendStringInfoChar(buf, '(');
12689 :
12690 6 : i = 0;
12691 24 : forfour(l1, rtfunc->funccoltypes,
12692 : l2, rtfunc->funccoltypmods,
12693 : l3, rtfunc->funccolcollations,
12694 : l4, rtfunc->funccolnames)
12695 : {
12696 18 : Oid atttypid = lfirst_oid(l1);
12697 18 : int32 atttypmod = lfirst_int(l2);
12698 18 : Oid attcollation = lfirst_oid(l3);
12699 : char *attname;
12700 :
12701 18 : if (colinfo)
12702 0 : attname = colinfo->colnames[i];
12703 : else
12704 18 : attname = strVal(lfirst(l4));
12705 :
12706 : Assert(attname); /* shouldn't be any dropped columns here */
12707 :
12708 18 : if (i > 0)
12709 12 : appendStringInfoString(buf, ", ");
12710 18 : appendStringInfo(buf, "%s %s",
12711 : quote_identifier(attname),
12712 : format_type_with_typemod(atttypid, atttypmod));
12713 24 : if (OidIsValid(attcollation) &&
12714 6 : attcollation != get_typcollation(atttypid))
12715 0 : appendStringInfo(buf, " COLLATE %s",
12716 : generate_collation_name(attcollation));
12717 :
12718 18 : i++;
12719 : }
12720 :
12721 6 : appendStringInfoChar(buf, ')');
12722 6 : }
12723 :
12724 : /*
12725 : * get_tablesample_def - print a TableSampleClause
12726 : */
12727 : static void
12728 32 : get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
12729 : {
12730 32 : StringInfo buf = context->buf;
12731 : Oid argtypes[1];
12732 : int nargs;
12733 : ListCell *l;
12734 :
12735 : /*
12736 : * We should qualify the handler's function name if it wouldn't be
12737 : * resolved by lookup in the current search path.
12738 : */
12739 32 : argtypes[0] = INTERNALOID;
12740 32 : appendStringInfo(buf, " TABLESAMPLE %s (",
12741 : generate_function_name(tablesample->tsmhandler, 1,
12742 : NIL, argtypes,
12743 : false, NULL, false));
12744 :
12745 32 : nargs = 0;
12746 64 : foreach(l, tablesample->args)
12747 : {
12748 32 : if (nargs++ > 0)
12749 0 : appendStringInfoString(buf, ", ");
12750 32 : get_rule_expr((Node *) lfirst(l), context, false);
12751 : }
12752 32 : appendStringInfoChar(buf, ')');
12753 :
12754 32 : if (tablesample->repeatable != NULL)
12755 : {
12756 16 : appendStringInfoString(buf, " REPEATABLE (");
12757 16 : get_rule_expr((Node *) tablesample->repeatable, context, false);
12758 16 : appendStringInfoChar(buf, ')');
12759 : }
12760 32 : }
12761 :
12762 : /*
12763 : * get_opclass_name - fetch name of an index operator class
12764 : *
12765 : * The opclass name is appended (after a space) to buf.
12766 : *
12767 : * Output is suppressed if the opclass is the default for the given
12768 : * actual_datatype. (If you don't want this behavior, just pass
12769 : * InvalidOid for actual_datatype.)
12770 : */
12771 : static void
12772 11886 : get_opclass_name(Oid opclass, Oid actual_datatype,
12773 : StringInfo buf)
12774 : {
12775 : HeapTuple ht_opc;
12776 : Form_pg_opclass opcrec;
12777 : char *opcname;
12778 : char *nspname;
12779 :
12780 11886 : ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
12781 11886 : if (!HeapTupleIsValid(ht_opc))
12782 0 : elog(ERROR, "cache lookup failed for opclass %u", opclass);
12783 11886 : opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
12784 :
12785 23732 : if (!OidIsValid(actual_datatype) ||
12786 11846 : GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
12787 : {
12788 : /* Okay, we need the opclass name. Do we need to qualify it? */
12789 558 : opcname = NameStr(opcrec->opcname);
12790 558 : if (OpclassIsVisible(opclass))
12791 558 : appendStringInfo(buf, " %s", quote_identifier(opcname));
12792 : else
12793 : {
12794 0 : nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
12795 0 : appendStringInfo(buf, " %s.%s",
12796 : quote_identifier(nspname),
12797 : quote_identifier(opcname));
12798 : }
12799 : }
12800 11886 : ReleaseSysCache(ht_opc);
12801 11886 : }
12802 :
12803 : /*
12804 : * generate_opclass_name
12805 : * Compute the name to display for an opclass specified by OID
12806 : *
12807 : * The result includes all necessary quoting and schema-prefixing.
12808 : */
12809 : char *
12810 6 : generate_opclass_name(Oid opclass)
12811 : {
12812 : StringInfoData buf;
12813 :
12814 6 : initStringInfo(&buf);
12815 6 : get_opclass_name(opclass, InvalidOid, &buf);
12816 :
12817 6 : return &buf.data[1]; /* get_opclass_name() prepends space */
12818 : }
12819 :
12820 : /*
12821 : * processIndirection - take care of array and subfield assignment
12822 : *
12823 : * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
12824 : * appear in the input, printing them as decoration for the base column
12825 : * name (which we assume the caller just printed). We might also need to
12826 : * strip CoerceToDomain nodes, but only ones that appear above assignment
12827 : * nodes.
12828 : *
12829 : * Returns the subexpression that's to be assigned.
12830 : */
12831 : static Node *
12832 1272 : processIndirection(Node *node, deparse_context *context)
12833 : {
12834 1272 : StringInfo buf = context->buf;
12835 1272 : CoerceToDomain *cdomain = NULL;
12836 :
12837 : for (;;)
12838 : {
12839 1578 : if (node == NULL)
12840 0 : break;
12841 1578 : if (IsA(node, FieldStore))
12842 : {
12843 108 : FieldStore *fstore = (FieldStore *) node;
12844 : Oid typrelid;
12845 : char *fieldname;
12846 :
12847 : /* lookup tuple type */
12848 108 : typrelid = get_typ_typrelid(fstore->resulttype);
12849 108 : if (!OidIsValid(typrelid))
12850 0 : elog(ERROR, "argument type %s of FieldStore is not a tuple type",
12851 : format_type_be(fstore->resulttype));
12852 :
12853 : /*
12854 : * Print the field name. There should only be one target field in
12855 : * stored rules. There could be more than that in executable
12856 : * target lists, but this function cannot be used for that case.
12857 : */
12858 : Assert(list_length(fstore->fieldnums) == 1);
12859 108 : fieldname = get_attname(typrelid,
12860 108 : linitial_int(fstore->fieldnums), false);
12861 108 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
12862 :
12863 : /*
12864 : * We ignore arg since it should be an uninteresting reference to
12865 : * the target column or subcolumn.
12866 : */
12867 108 : node = (Node *) linitial(fstore->newvals);
12868 : }
12869 1470 : else if (IsA(node, SubscriptingRef))
12870 : {
12871 138 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
12872 :
12873 138 : if (sbsref->refassgnexpr == NULL)
12874 0 : break;
12875 :
12876 138 : printSubscripts(sbsref, context);
12877 :
12878 : /*
12879 : * We ignore refexpr since it should be an uninteresting reference
12880 : * to the target column or subcolumn.
12881 : */
12882 138 : node = (Node *) sbsref->refassgnexpr;
12883 : }
12884 1332 : else if (IsA(node, CoerceToDomain))
12885 : {
12886 60 : cdomain = (CoerceToDomain *) node;
12887 : /* If it's an explicit domain coercion, we're done */
12888 60 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
12889 0 : break;
12890 : /* Tentatively descend past the CoerceToDomain */
12891 60 : node = (Node *) cdomain->arg;
12892 : }
12893 : else
12894 1272 : break;
12895 : }
12896 :
12897 : /*
12898 : * If we descended past a CoerceToDomain whose argument turned out not to
12899 : * be a FieldStore or array assignment, back up to the CoerceToDomain.
12900 : * (This is not enough to be fully correct if there are nested implicit
12901 : * CoerceToDomains, but such cases shouldn't ever occur.)
12902 : */
12903 1272 : if (cdomain && node == (Node *) cdomain->arg)
12904 0 : node = (Node *) cdomain;
12905 :
12906 1272 : return node;
12907 : }
12908 :
12909 : static void
12910 454 : printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
12911 : {
12912 454 : StringInfo buf = context->buf;
12913 : ListCell *lowlist_item;
12914 : ListCell *uplist_item;
12915 :
12916 454 : lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
12917 908 : foreach(uplist_item, sbsref->refupperindexpr)
12918 : {
12919 454 : appendStringInfoChar(buf, '[');
12920 454 : if (lowlist_item)
12921 : {
12922 : /* If subexpression is NULL, get_rule_expr prints nothing */
12923 0 : get_rule_expr((Node *) lfirst(lowlist_item), context, false);
12924 0 : appendStringInfoChar(buf, ':');
12925 0 : lowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item);
12926 : }
12927 : /* If subexpression is NULL, get_rule_expr prints nothing */
12928 454 : get_rule_expr((Node *) lfirst(uplist_item), context, false);
12929 454 : appendStringInfoChar(buf, ']');
12930 : }
12931 454 : }
12932 :
12933 : /*
12934 : * quote_identifier - Quote an identifier only if needed
12935 : *
12936 : * When quotes are needed, we palloc the required space; slightly
12937 : * space-wasteful but well worth it for notational simplicity.
12938 : */
12939 : const char *
12940 2442158 : quote_identifier(const char *ident)
12941 : {
12942 : /*
12943 : * Can avoid quoting if ident starts with a lowercase letter or underscore
12944 : * and contains only lowercase letters, digits, and underscores, *and* is
12945 : * not any SQL keyword. Otherwise, supply quotes.
12946 : */
12947 2442158 : int nquotes = 0;
12948 : bool safe;
12949 : const char *ptr;
12950 : char *result;
12951 : char *optr;
12952 :
12953 : /*
12954 : * would like to use <ctype.h> macros here, but they might yield unwanted
12955 : * locale-specific results...
12956 : */
12957 2442158 : safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
12958 :
12959 20920478 : for (ptr = ident; *ptr; ptr++)
12960 : {
12961 18478320 : char ch = *ptr;
12962 :
12963 18478320 : if ((ch >= 'a' && ch <= 'z') ||
12964 2179192 : (ch >= '0' && ch <= '9') ||
12965 : (ch == '_'))
12966 : {
12967 : /* okay */
12968 : }
12969 : else
12970 : {
12971 61916 : safe = false;
12972 61916 : if (ch == '"')
12973 12 : nquotes++;
12974 : }
12975 : }
12976 :
12977 2442158 : if (quote_all_identifiers)
12978 11550 : safe = false;
12979 :
12980 2442158 : if (safe)
12981 : {
12982 : /*
12983 : * Check for keyword. We quote keywords except for unreserved ones.
12984 : * (In some cases we could avoid quoting a col_name or type_func_name
12985 : * keyword, but it seems much harder than it's worth to tell that.)
12986 : *
12987 : * Note: ScanKeywordLookup() does case-insensitive comparison, but
12988 : * that's fine, since we already know we have all-lower-case.
12989 : */
12990 2404774 : int kwnum = ScanKeywordLookup(ident, &ScanKeywords);
12991 :
12992 2404774 : if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
12993 3148 : safe = false;
12994 : }
12995 :
12996 2442158 : if (safe)
12997 2401626 : return ident; /* no change needed */
12998 :
12999 40532 : result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13000 :
13001 40532 : optr = result;
13002 40532 : *optr++ = '"';
13003 234216 : for (ptr = ident; *ptr; ptr++)
13004 : {
13005 193684 : char ch = *ptr;
13006 :
13007 193684 : if (ch == '"')
13008 12 : *optr++ = '"';
13009 193684 : *optr++ = ch;
13010 : }
13011 40532 : *optr++ = '"';
13012 40532 : *optr = '\0';
13013 :
13014 40532 : return result;
13015 : }
13016 :
13017 : /*
13018 : * quote_qualified_identifier - Quote a possibly-qualified identifier
13019 : *
13020 : * Return a name of the form qualifier.ident, or just ident if qualifier
13021 : * is NULL, quoting each component if necessary. The result is palloc'd.
13022 : */
13023 : char *
13024 1203798 : quote_qualified_identifier(const char *qualifier,
13025 : const char *ident)
13026 : {
13027 : StringInfoData buf;
13028 :
13029 1203798 : initStringInfo(&buf);
13030 1203798 : if (qualifier)
13031 439446 : appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
13032 1203798 : appendStringInfoString(&buf, quote_identifier(ident));
13033 1203798 : return buf.data;
13034 : }
13035 :
13036 : /*
13037 : * get_relation_name
13038 : * Get the unqualified name of a relation specified by OID
13039 : *
13040 : * This differs from the underlying get_rel_name() function in that it will
13041 : * throw error instead of silently returning NULL if the OID is bad.
13042 : */
13043 : static char *
13044 15472 : get_relation_name(Oid relid)
13045 : {
13046 15472 : char *relname = get_rel_name(relid);
13047 :
13048 15472 : if (!relname)
13049 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13050 15472 : return relname;
13051 : }
13052 :
13053 : /*
13054 : * generate_relation_name
13055 : * Compute the name to display for a relation specified by OID
13056 : *
13057 : * The result includes all necessary quoting and schema-prefixing.
13058 : *
13059 : * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
13060 : * We will forcibly qualify the relation name if it equals any CTE name
13061 : * visible in the namespace list.
13062 : */
13063 : static char *
13064 7806 : generate_relation_name(Oid relid, List *namespaces)
13065 : {
13066 : HeapTuple tp;
13067 : Form_pg_class reltup;
13068 : bool need_qual;
13069 : ListCell *nslist;
13070 : char *relname;
13071 : char *nspname;
13072 : char *result;
13073 :
13074 7806 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13075 7806 : if (!HeapTupleIsValid(tp))
13076 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13077 7806 : reltup = (Form_pg_class) GETSTRUCT(tp);
13078 7806 : relname = NameStr(reltup->relname);
13079 :
13080 : /* Check for conflicting CTE name */
13081 7806 : need_qual = false;
13082 13528 : foreach(nslist, namespaces)
13083 : {
13084 5722 : deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
13085 : ListCell *ctlist;
13086 :
13087 5854 : foreach(ctlist, dpns->ctes)
13088 : {
13089 132 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);
13090 :
13091 132 : if (strcmp(cte->ctename, relname) == 0)
13092 : {
13093 0 : need_qual = true;
13094 0 : break;
13095 : }
13096 : }
13097 5722 : if (need_qual)
13098 0 : break;
13099 : }
13100 :
13101 : /* Otherwise, qualify the name if not visible in search path */
13102 7806 : if (!need_qual)
13103 7806 : need_qual = !RelationIsVisible(relid);
13104 :
13105 7806 : if (need_qual)
13106 2264 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
13107 : else
13108 5542 : nspname = NULL;
13109 :
13110 7806 : result = quote_qualified_identifier(nspname, relname);
13111 :
13112 7806 : ReleaseSysCache(tp);
13113 :
13114 7806 : return result;
13115 : }
13116 :
13117 : /*
13118 : * generate_qualified_relation_name
13119 : * Compute the name to display for a relation specified by OID
13120 : *
13121 : * As above, but unconditionally schema-qualify the name.
13122 : */
13123 : static char *
13124 7652 : generate_qualified_relation_name(Oid relid)
13125 : {
13126 : HeapTuple tp;
13127 : Form_pg_class reltup;
13128 : char *relname;
13129 : char *nspname;
13130 : char *result;
13131 :
13132 7652 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13133 7652 : if (!HeapTupleIsValid(tp))
13134 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13135 7652 : reltup = (Form_pg_class) GETSTRUCT(tp);
13136 7652 : relname = NameStr(reltup->relname);
13137 :
13138 7652 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
13139 7652 : if (!nspname)
13140 0 : elog(ERROR, "cache lookup failed for namespace %u",
13141 : reltup->relnamespace);
13142 :
13143 7652 : result = quote_qualified_identifier(nspname, relname);
13144 :
13145 7652 : ReleaseSysCache(tp);
13146 :
13147 7652 : return result;
13148 : }
13149 :
13150 : /*
13151 : * generate_function_name
13152 : * Compute the name to display for a function specified by OID,
13153 : * given that it is being called with the specified actual arg names and
13154 : * types. (Those matter because of ambiguous-function resolution rules.)
13155 : *
13156 : * If we're dealing with a potentially variadic function (in practice, this
13157 : * means a FuncExpr or Aggref, not some other way of calling a function), then
13158 : * has_variadic must specify whether variadic arguments have been merged,
13159 : * and *use_variadic_p will be set to indicate whether to print VARIADIC in
13160 : * the output. For non-FuncExpr cases, has_variadic should be false and
13161 : * use_variadic_p can be NULL.
13162 : *
13163 : * inGroupBy must be true if we're deparsing a GROUP BY clause.
13164 : *
13165 : * The result includes all necessary quoting and schema-prefixing.
13166 : */
13167 : static char *
13168 12100 : generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
13169 : bool has_variadic, bool *use_variadic_p,
13170 : bool inGroupBy)
13171 : {
13172 : char *result;
13173 : HeapTuple proctup;
13174 : Form_pg_proc procform;
13175 : char *proname;
13176 : bool use_variadic;
13177 : char *nspname;
13178 : FuncDetailCode p_result;
13179 : Oid p_funcid;
13180 : Oid p_rettype;
13181 : bool p_retset;
13182 : int p_nvargs;
13183 : Oid p_vatype;
13184 : Oid *p_true_typeids;
13185 12100 : bool force_qualify = false;
13186 :
13187 12100 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
13188 12100 : if (!HeapTupleIsValid(proctup))
13189 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
13190 12100 : procform = (Form_pg_proc) GETSTRUCT(proctup);
13191 12100 : proname = NameStr(procform->proname);
13192 :
13193 : /*
13194 : * Due to parser hacks to avoid needing to reserve CUBE, we need to force
13195 : * qualification of some function names within GROUP BY.
13196 : */
13197 12100 : if (inGroupBy)
13198 : {
13199 0 : if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
13200 0 : force_qualify = true;
13201 : }
13202 :
13203 : /*
13204 : * Determine whether VARIADIC should be printed. We must do this first
13205 : * since it affects the lookup rules in func_get_detail().
13206 : *
13207 : * We always print VARIADIC if the function has a merged variadic-array
13208 : * argument. Note that this is always the case for functions taking a
13209 : * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
13210 : * and printed the array elements as separate arguments, the call could
13211 : * match a newer non-VARIADIC function.
13212 : */
13213 12100 : if (use_variadic_p)
13214 : {
13215 : /* Parser should not have set funcvariadic unless fn is variadic */
13216 : Assert(!has_variadic || OidIsValid(procform->provariadic));
13217 10500 : use_variadic = has_variadic;
13218 10500 : *use_variadic_p = use_variadic;
13219 : }
13220 : else
13221 : {
13222 : Assert(!has_variadic);
13223 1600 : use_variadic = false;
13224 : }
13225 :
13226 : /*
13227 : * The idea here is to schema-qualify only if the parser would fail to
13228 : * resolve the correct function given the unqualified func name with the
13229 : * specified argtypes and VARIADIC flag. But if we already decided to
13230 : * force qualification, then we can skip the lookup and pretend we didn't
13231 : * find it.
13232 : */
13233 12100 : if (!force_qualify)
13234 12100 : p_result = func_get_detail(list_make1(makeString(proname)),
13235 : NIL, argnames, nargs, argtypes,
13236 12100 : !use_variadic, true, false,
13237 : &p_funcid, &p_rettype,
13238 : &p_retset, &p_nvargs, &p_vatype,
13239 12100 : &p_true_typeids, NULL);
13240 : else
13241 : {
13242 0 : p_result = FUNCDETAIL_NOTFOUND;
13243 0 : p_funcid = InvalidOid;
13244 : }
13245 :
13246 12100 : if ((p_result == FUNCDETAIL_NORMAL ||
13247 1180 : p_result == FUNCDETAIL_AGGREGATE ||
13248 11028 : p_result == FUNCDETAIL_WINDOWFUNC) &&
13249 11028 : p_funcid == funcid)
13250 11028 : nspname = NULL;
13251 : else
13252 1072 : nspname = get_namespace_name_or_temp(procform->pronamespace);
13253 :
13254 12100 : result = quote_qualified_identifier(nspname, proname);
13255 :
13256 12100 : ReleaseSysCache(proctup);
13257 :
13258 12100 : return result;
13259 : }
13260 :
13261 : /*
13262 : * generate_operator_name
13263 : * Compute the name to display for an operator specified by OID,
13264 : * given that it is being called with the specified actual arg types.
13265 : * (Arg types matter because of ambiguous-operator resolution rules.
13266 : * Pass InvalidOid for unused arg of a unary operator.)
13267 : *
13268 : * The result includes all necessary quoting and schema-prefixing,
13269 : * plus the OPERATOR() decoration needed to use a qualified operator name
13270 : * in an expression.
13271 : */
13272 : static char *
13273 60106 : generate_operator_name(Oid operid, Oid arg1, Oid arg2)
13274 : {
13275 : StringInfoData buf;
13276 : HeapTuple opertup;
13277 : Form_pg_operator operform;
13278 : char *oprname;
13279 : char *nspname;
13280 : Operator p_result;
13281 :
13282 60106 : initStringInfo(&buf);
13283 :
13284 60106 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
13285 60106 : if (!HeapTupleIsValid(opertup))
13286 0 : elog(ERROR, "cache lookup failed for operator %u", operid);
13287 60106 : operform = (Form_pg_operator) GETSTRUCT(opertup);
13288 60106 : oprname = NameStr(operform->oprname);
13289 :
13290 : /*
13291 : * The idea here is to schema-qualify only if the parser would fail to
13292 : * resolve the correct operator given the unqualified op name with the
13293 : * specified argtypes.
13294 : */
13295 60106 : switch (operform->oprkind)
13296 : {
13297 60076 : case 'b':
13298 60076 : p_result = oper(NULL, list_make1(makeString(oprname)), arg1, arg2,
13299 : true, -1);
13300 60076 : break;
13301 30 : case 'l':
13302 30 : p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2,
13303 : true, -1);
13304 30 : break;
13305 0 : default:
13306 0 : elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
13307 : p_result = NULL; /* keep compiler quiet */
13308 : break;
13309 : }
13310 :
13311 60106 : if (p_result != NULL && oprid(p_result) == operid)
13312 60096 : nspname = NULL;
13313 : else
13314 : {
13315 10 : nspname = get_namespace_name_or_temp(operform->oprnamespace);
13316 10 : appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
13317 : }
13318 :
13319 60106 : appendStringInfoString(&buf, oprname);
13320 :
13321 60106 : if (nspname)
13322 10 : appendStringInfoChar(&buf, ')');
13323 :
13324 60106 : if (p_result != NULL)
13325 60096 : ReleaseSysCache(p_result);
13326 :
13327 60106 : ReleaseSysCache(opertup);
13328 :
13329 60106 : return buf.data;
13330 : }
13331 :
13332 : /*
13333 : * generate_operator_clause --- generate a binary-operator WHERE clause
13334 : *
13335 : * This is used for internally-generated-and-executed SQL queries, where
13336 : * precision is essential and readability is secondary. The basic
13337 : * requirement is to append "leftop op rightop" to buf, where leftop and
13338 : * rightop are given as strings and are assumed to yield types leftoptype
13339 : * and rightoptype; the operator is identified by OID. The complexity
13340 : * comes from needing to be sure that the parser will select the desired
13341 : * operator when the query is parsed. We always name the operator using
13342 : * OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
13343 : * We have to emit casts too, if either input isn't already the input type
13344 : * of the operator; else we are at the mercy of the parser's heuristics for
13345 : * ambiguous-operator resolution. The caller must ensure that leftop and
13346 : * rightop are suitable arguments for a cast operation; it's best to insert
13347 : * parentheses if they aren't just variables or parameters.
13348 : */
13349 : void
13350 6250 : generate_operator_clause(StringInfo buf,
13351 : const char *leftop, Oid leftoptype,
13352 : Oid opoid,
13353 : const char *rightop, Oid rightoptype)
13354 : {
13355 : HeapTuple opertup;
13356 : Form_pg_operator operform;
13357 : char *oprname;
13358 : char *nspname;
13359 :
13360 6250 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
13361 6250 : if (!HeapTupleIsValid(opertup))
13362 0 : elog(ERROR, "cache lookup failed for operator %u", opoid);
13363 6250 : operform = (Form_pg_operator) GETSTRUCT(opertup);
13364 : Assert(operform->oprkind == 'b');
13365 6250 : oprname = NameStr(operform->oprname);
13366 :
13367 6250 : nspname = get_namespace_name(operform->oprnamespace);
13368 :
13369 6250 : appendStringInfoString(buf, leftop);
13370 6250 : if (leftoptype != operform->oprleft)
13371 1148 : add_cast_to(buf, operform->oprleft);
13372 6250 : appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
13373 6250 : appendStringInfoString(buf, oprname);
13374 6250 : appendStringInfo(buf, ") %s", rightop);
13375 6250 : if (rightoptype != operform->oprright)
13376 914 : add_cast_to(buf, operform->oprright);
13377 :
13378 6250 : ReleaseSysCache(opertup);
13379 6250 : }
13380 :
13381 : /*
13382 : * Add a cast specification to buf. We spell out the type name the hard way,
13383 : * intentionally not using format_type_be(). This is to avoid corner cases
13384 : * for CHARACTER, BIT, and perhaps other types, where specifying the type
13385 : * using SQL-standard syntax results in undesirable data truncation. By
13386 : * doing it this way we can be certain that the cast will have default (-1)
13387 : * target typmod.
13388 : */
13389 : static void
13390 2062 : add_cast_to(StringInfo buf, Oid typid)
13391 : {
13392 : HeapTuple typetup;
13393 : Form_pg_type typform;
13394 : char *typname;
13395 : char *nspname;
13396 :
13397 2062 : typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
13398 2062 : if (!HeapTupleIsValid(typetup))
13399 0 : elog(ERROR, "cache lookup failed for type %u", typid);
13400 2062 : typform = (Form_pg_type) GETSTRUCT(typetup);
13401 :
13402 2062 : typname = NameStr(typform->typname);
13403 2062 : nspname = get_namespace_name_or_temp(typform->typnamespace);
13404 :
13405 2062 : appendStringInfo(buf, "::%s.%s",
13406 : quote_identifier(nspname), quote_identifier(typname));
13407 :
13408 2062 : ReleaseSysCache(typetup);
13409 2062 : }
13410 :
13411 : /*
13412 : * generate_qualified_type_name
13413 : * Compute the name to display for a type specified by OID
13414 : *
13415 : * This is different from format_type_be() in that we unconditionally
13416 : * schema-qualify the name. That also means no special syntax for
13417 : * SQL-standard type names ... although in current usage, this should
13418 : * only get used for domains, so such cases wouldn't occur anyway.
13419 : */
13420 : static char *
13421 14 : generate_qualified_type_name(Oid typid)
13422 : {
13423 : HeapTuple tp;
13424 : Form_pg_type typtup;
13425 : char *typname;
13426 : char *nspname;
13427 : char *result;
13428 :
13429 14 : tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
13430 14 : if (!HeapTupleIsValid(tp))
13431 0 : elog(ERROR, "cache lookup failed for type %u", typid);
13432 14 : typtup = (Form_pg_type) GETSTRUCT(tp);
13433 14 : typname = NameStr(typtup->typname);
13434 :
13435 14 : nspname = get_namespace_name_or_temp(typtup->typnamespace);
13436 14 : if (!nspname)
13437 0 : elog(ERROR, "cache lookup failed for namespace %u",
13438 : typtup->typnamespace);
13439 :
13440 14 : result = quote_qualified_identifier(nspname, typname);
13441 :
13442 14 : ReleaseSysCache(tp);
13443 :
13444 14 : return result;
13445 : }
13446 :
13447 : /*
13448 : * generate_collation_name
13449 : * Compute the name to display for a collation specified by OID
13450 : *
13451 : * The result includes all necessary quoting and schema-prefixing.
13452 : */
13453 : char *
13454 290 : generate_collation_name(Oid collid)
13455 : {
13456 : HeapTuple tp;
13457 : Form_pg_collation colltup;
13458 : char *collname;
13459 : char *nspname;
13460 : char *result;
13461 :
13462 290 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
13463 290 : if (!HeapTupleIsValid(tp))
13464 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
13465 290 : colltup = (Form_pg_collation) GETSTRUCT(tp);
13466 290 : collname = NameStr(colltup->collname);
13467 :
13468 290 : if (!CollationIsVisible(collid))
13469 0 : nspname = get_namespace_name_or_temp(colltup->collnamespace);
13470 : else
13471 290 : nspname = NULL;
13472 :
13473 290 : result = quote_qualified_identifier(nspname, collname);
13474 :
13475 290 : ReleaseSysCache(tp);
13476 :
13477 290 : return result;
13478 : }
13479 :
13480 : /*
13481 : * Given a C string, produce a TEXT datum.
13482 : *
13483 : * We assume that the input was palloc'd and may be freed.
13484 : */
13485 : static text *
13486 41122 : string_to_text(char *str)
13487 : {
13488 : text *result;
13489 :
13490 41122 : result = cstring_to_text(str);
13491 41122 : pfree(str);
13492 41122 : return result;
13493 : }
13494 :
13495 : /*
13496 : * Generate a C string representing a relation options from text[] datum.
13497 : */
13498 : static void
13499 244 : get_reloptions(StringInfo buf, Datum reloptions)
13500 : {
13501 : Datum *options;
13502 : int noptions;
13503 : int i;
13504 :
13505 244 : deconstruct_array_builtin(DatumGetArrayTypeP(reloptions), TEXTOID,
13506 : &options, NULL, &noptions);
13507 :
13508 508 : for (i = 0; i < noptions; i++)
13509 : {
13510 264 : char *option = TextDatumGetCString(options[i]);
13511 : char *name;
13512 : char *separator;
13513 : char *value;
13514 :
13515 : /*
13516 : * Each array element should have the form name=value. If the "=" is
13517 : * missing for some reason, treat it like an empty value.
13518 : */
13519 264 : name = option;
13520 264 : separator = strchr(option, '=');
13521 264 : if (separator)
13522 : {
13523 264 : *separator = '\0';
13524 264 : value = separator + 1;
13525 : }
13526 : else
13527 0 : value = "";
13528 :
13529 264 : if (i > 0)
13530 20 : appendStringInfoString(buf, ", ");
13531 264 : appendStringInfo(buf, "%s=", quote_identifier(name));
13532 :
13533 : /*
13534 : * In general we need to quote the value; but to avoid unnecessary
13535 : * clutter, do not quote if it is an identifier that would not need
13536 : * quoting. (We could also allow numbers, but that is a bit trickier
13537 : * than it looks --- for example, are leading zeroes significant? We
13538 : * don't want to assume very much here about what custom reloptions
13539 : * might mean.)
13540 : */
13541 264 : if (quote_identifier(value) == value)
13542 8 : appendStringInfoString(buf, value);
13543 : else
13544 256 : simple_quote_literal(buf, value);
13545 :
13546 264 : pfree(option);
13547 : }
13548 244 : }
13549 :
13550 : /*
13551 : * Generate a C string representing a relation's reloptions, or NULL if none.
13552 : */
13553 : static char *
13554 7228 : flatten_reloptions(Oid relid)
13555 : {
13556 7228 : char *result = NULL;
13557 : HeapTuple tuple;
13558 : Datum reloptions;
13559 : bool isnull;
13560 :
13561 7228 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13562 7228 : if (!HeapTupleIsValid(tuple))
13563 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13564 :
13565 7228 : reloptions = SysCacheGetAttr(RELOID, tuple,
13566 : Anum_pg_class_reloptions, &isnull);
13567 7228 : if (!isnull)
13568 : {
13569 : StringInfoData buf;
13570 :
13571 210 : initStringInfo(&buf);
13572 210 : get_reloptions(&buf, reloptions);
13573 :
13574 210 : result = buf.data;
13575 : }
13576 :
13577 7228 : ReleaseSysCache(tuple);
13578 :
13579 7228 : return result;
13580 : }
13581 :
13582 : /*
13583 : * get_range_partbound_string
13584 : * A C string representation of one range partition bound
13585 : */
13586 : char *
13587 4284 : get_range_partbound_string(List *bound_datums)
13588 : {
13589 : deparse_context context;
13590 4284 : StringInfo buf = makeStringInfo();
13591 : ListCell *cell;
13592 : char *sep;
13593 :
13594 4284 : memset(&context, 0, sizeof(deparse_context));
13595 4284 : context.buf = buf;
13596 :
13597 4284 : appendStringInfoChar(buf, '(');
13598 4284 : sep = "";
13599 9360 : foreach(cell, bound_datums)
13600 : {
13601 5076 : PartitionRangeDatum *datum =
13602 : lfirst_node(PartitionRangeDatum, cell);
13603 :
13604 5076 : appendStringInfoString(buf, sep);
13605 5076 : if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
13606 222 : appendStringInfoString(buf, "MINVALUE");
13607 4854 : else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
13608 120 : appendStringInfoString(buf, "MAXVALUE");
13609 : else
13610 : {
13611 4734 : Const *val = castNode(Const, datum->value);
13612 :
13613 4734 : get_const_expr(val, &context, -1);
13614 : }
13615 5076 : sep = ", ";
13616 : }
13617 4284 : appendStringInfoChar(buf, ')');
13618 :
13619 4284 : return buf->data;
13620 : }
|