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-2024, 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 : /* Workspace for column alias assignment: */
171 : bool unique_using; /* Are we making USING names globally unique */
172 : List *using_names; /* List of assigned names for USING columns */
173 : /* Remaining fields are used only when deparsing a Plan tree: */
174 : Plan *plan; /* immediate parent of current expression */
175 : List *ancestors; /* ancestors of plan */
176 : Plan *outer_plan; /* outer subnode, or NULL if none */
177 : Plan *inner_plan; /* inner subnode, or NULL if none */
178 : List *outer_tlist; /* referent for OUTER_VAR Vars */
179 : List *inner_tlist; /* referent for INNER_VAR Vars */
180 : List *index_tlist; /* referent for INDEX_VAR Vars */
181 : /* Special namespace representing a function signature: */
182 : char *funcname;
183 : int numargs;
184 : char **argnames;
185 : } deparse_namespace;
186 :
187 : /*
188 : * Per-relation data about column alias names.
189 : *
190 : * Selecting aliases is unreasonably complicated because of the need to dump
191 : * rules/views whose underlying tables may have had columns added, deleted, or
192 : * renamed since the query was parsed. We must nonetheless print the rule/view
193 : * in a form that can be reloaded and will produce the same results as before.
194 : *
195 : * For each RTE used in the query, we must assign column aliases that are
196 : * unique within that RTE. SQL does not require this of the original query,
197 : * but due to factors such as *-expansion we need to be able to uniquely
198 : * reference every column in a decompiled query. As long as we qualify all
199 : * column references, per-RTE uniqueness is sufficient for that.
200 : *
201 : * However, we can't ensure per-column name uniqueness for unnamed join RTEs,
202 : * since they just inherit column names from their input RTEs, and we can't
203 : * rename the columns at the join level. Most of the time this isn't an issue
204 : * because we don't need to reference the join's output columns as such; we
205 : * can reference the input columns instead. That approach can fail for merged
206 : * JOIN USING columns, however, so when we have one of those in an unnamed
207 : * join, we have to make that column's alias globally unique across the whole
208 : * query to ensure it can be referenced unambiguously.
209 : *
210 : * Another problem is that a JOIN USING clause requires the columns to be
211 : * merged to have the same aliases in both input RTEs, and that no other
212 : * columns in those RTEs or their children conflict with the USING names.
213 : * To handle that, we do USING-column alias assignment in a recursive
214 : * traversal of the query's jointree. When descending through a JOIN with
215 : * USING, we preassign the USING column names to the child columns, overriding
216 : * other rules for column alias assignment. We also mark each RTE with a list
217 : * of all USING column names selected for joins containing that RTE, so that
218 : * when we assign other columns' aliases later, we can avoid conflicts.
219 : *
220 : * Another problem is that if a JOIN's input tables have had columns added or
221 : * deleted since the query was parsed, we must generate a column alias list
222 : * for the join that matches the current set of input columns --- otherwise, a
223 : * change in the number of columns in the left input would throw off matching
224 : * of aliases to columns of the right input. Thus, positions in the printable
225 : * column alias list are not necessarily one-for-one with varattnos of the
226 : * JOIN, so we need a separate new_colnames[] array for printing purposes.
227 : *
228 : * Finally, when dealing with wide tables we risk O(N^2) costs in assigning
229 : * non-duplicate column names. We ameliorate that by using a hash table that
230 : * holds all the strings appearing in colnames, new_colnames, and parentUsing.
231 : */
232 : typedef struct
233 : {
234 : /*
235 : * colnames is an array containing column aliases to use for columns that
236 : * existed when the query was parsed. Dropped columns have NULL entries.
237 : * This array can be directly indexed by varattno to get a Var's name.
238 : *
239 : * Non-NULL entries are guaranteed unique within the RTE, *except* when
240 : * this is for an unnamed JOIN RTE. In that case we merely copy up names
241 : * from the two input RTEs.
242 : *
243 : * During the recursive descent in set_using_names(), forcible assignment
244 : * of a child RTE's column name is represented by pre-setting that element
245 : * of the child's colnames array. So at that stage, NULL entries in this
246 : * array just mean that no name has been preassigned, not necessarily that
247 : * the column is dropped.
248 : */
249 : int num_cols; /* length of colnames[] array */
250 : char **colnames; /* array of C strings and NULLs */
251 :
252 : /*
253 : * new_colnames is an array containing column aliases to use for columns
254 : * that would exist if the query was re-parsed against the current
255 : * definitions of its base tables. This is what to print as the column
256 : * alias list for the RTE. This array does not include dropped columns,
257 : * but it will include columns added since original parsing. Indexes in
258 : * it therefore have little to do with current varattno values. As above,
259 : * entries are unique unless this is for an unnamed JOIN RTE. (In such an
260 : * RTE, we never actually print this array, but we must compute it anyway
261 : * for possible use in computing column names of upper joins.) The
262 : * parallel array is_new_col marks which of these columns are new since
263 : * original parsing. Entries with is_new_col false must match the
264 : * non-NULL colnames entries one-for-one.
265 : */
266 : int num_new_cols; /* length of new_colnames[] array */
267 : char **new_colnames; /* array of C strings */
268 : bool *is_new_col; /* array of bool flags */
269 :
270 : /* This flag tells whether we should actually print a column alias list */
271 : bool printaliases;
272 :
273 : /* This list has all names used as USING names in joins above this RTE */
274 : List *parentUsing; /* names assigned to parent merged columns */
275 :
276 : /*
277 : * If this struct is for a JOIN RTE, we fill these fields during the
278 : * set_using_names() pass to describe its relationship to its child RTEs.
279 : *
280 : * leftattnos and rightattnos are arrays with one entry per existing
281 : * output column of the join (hence, indexable by join varattno). For a
282 : * simple reference to a column of the left child, leftattnos[i] is the
283 : * child RTE's attno and rightattnos[i] is zero; and conversely for a
284 : * column of the right child. But for merged columns produced by JOIN
285 : * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.
286 : * Note that a simple reference might be to a child RTE column that's been
287 : * dropped; but that's OK since the column could not be used in the query.
288 : *
289 : * If it's a JOIN USING, usingNames holds the alias names selected for the
290 : * merged columns (these might be different from the original USING list,
291 : * if we had to modify names to achieve uniqueness).
292 : */
293 : int leftrti; /* rangetable index of left child */
294 : int rightrti; /* rangetable index of right child */
295 : int *leftattnos; /* left-child varattnos of join cols, or 0 */
296 : int *rightattnos; /* right-child varattnos of join cols, or 0 */
297 : List *usingNames; /* names assigned to merged columns */
298 :
299 : /*
300 : * Hash table holding copies of all the strings appearing in this struct's
301 : * colnames, new_colnames, and parentUsing. We use a hash table only for
302 : * sufficiently wide relations, and only during the colname-assignment
303 : * functions set_relation_column_names and set_join_column_names;
304 : * otherwise, names_hash is NULL.
305 : */
306 : HTAB *names_hash; /* entries are just strings */
307 : } deparse_columns;
308 :
309 : /* This macro is analogous to rt_fetch(), but for deparse_columns structs */
310 : #define deparse_columns_fetch(rangetable_index, dpns) \
311 : ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
312 :
313 : /*
314 : * Entry in set_rtable_names' hash table
315 : */
316 : typedef struct
317 : {
318 : char name[NAMEDATALEN]; /* Hash key --- must be first */
319 : int counter; /* Largest addition used so far for name */
320 : } NameHashEntry;
321 :
322 : /* Callback signature for resolve_special_varno() */
323 : typedef void (*rsv_callback) (Node *node, deparse_context *context,
324 : void *callback_arg);
325 :
326 :
327 : /* ----------
328 : * Global data
329 : * ----------
330 : */
331 : static SPIPlanPtr plan_getrulebyoid = NULL;
332 : static const char *const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
333 : static SPIPlanPtr plan_getviewrule = NULL;
334 : static const char *const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
335 :
336 : /* GUC parameters */
337 : bool quote_all_identifiers = false;
338 :
339 :
340 : /* ----------
341 : * Local functions
342 : *
343 : * Most of these functions used to use fixed-size buffers to build their
344 : * results. Now, they take an (already initialized) StringInfo object
345 : * as a parameter, and append their text output to its contents.
346 : * ----------
347 : */
348 : static char *deparse_expression_pretty(Node *expr, List *dpcontext,
349 : bool forceprefix, bool showimplicit,
350 : int prettyFlags, int startIndent);
351 : static char *pg_get_viewdef_worker(Oid viewoid,
352 : int prettyFlags, int wrapColumn);
353 : static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
354 : static int decompile_column_index_array(Datum column_index_array, Oid relId,
355 : bool withPeriod, StringInfo buf);
356 : static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
357 : static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
358 : const Oid *excludeOps,
359 : bool attrsOnly, bool keysOnly,
360 : bool showTblSpc, bool inherits,
361 : int prettyFlags, bool missing_ok);
362 : static char *pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
363 : bool missing_ok);
364 : static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
365 : bool attrsOnly, bool missing_ok);
366 : static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
367 : int prettyFlags, bool missing_ok);
368 : static text *pg_get_expr_worker(text *expr, Oid relid, int prettyFlags);
369 : static int print_function_arguments(StringInfo buf, HeapTuple proctup,
370 : bool print_table_args, bool print_defaults);
371 : static void print_function_rettype(StringInfo buf, HeapTuple proctup);
372 : static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
373 : static void print_function_sqlbody(StringInfo buf, HeapTuple proctup);
374 : static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
375 : Bitmapset *rels_used);
376 : static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
377 : List *parent_namespaces);
378 : static void set_simple_column_names(deparse_namespace *dpns);
379 : static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
380 : static void set_using_names(deparse_namespace *dpns, Node *jtnode,
381 : List *parentUsing);
382 : static void set_relation_column_names(deparse_namespace *dpns,
383 : RangeTblEntry *rte,
384 : deparse_columns *colinfo);
385 : static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
386 : deparse_columns *colinfo);
387 : static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
388 : deparse_columns *colinfo);
389 : static char *make_colname_unique(char *colname, deparse_namespace *dpns,
390 : deparse_columns *colinfo);
391 : static void expand_colnames_array_to(deparse_columns *colinfo, int n);
392 : static void build_colinfo_names_hash(deparse_columns *colinfo);
393 : static void add_to_names_hash(deparse_columns *colinfo, const char *name);
394 : static void destroy_colinfo_names_hash(deparse_columns *colinfo);
395 : static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
396 : deparse_columns *colinfo);
397 : static char *get_rtable_name(int rtindex, deparse_context *context);
398 : static void set_deparse_plan(deparse_namespace *dpns, Plan *plan);
399 : static Plan *find_recursive_union(deparse_namespace *dpns,
400 : WorkTableScan *wtscan);
401 : static void push_child_plan(deparse_namespace *dpns, Plan *plan,
402 : deparse_namespace *save_dpns);
403 : static void pop_child_plan(deparse_namespace *dpns,
404 : deparse_namespace *save_dpns);
405 : static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
406 : deparse_namespace *save_dpns);
407 : static void pop_ancestor_plan(deparse_namespace *dpns,
408 : deparse_namespace *save_dpns);
409 : static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
410 : int prettyFlags);
411 : static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
412 : int prettyFlags, int wrapColumn);
413 : static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
414 : TupleDesc resultDesc, bool colNamesVisible,
415 : int prettyFlags, int wrapColumn, int startIndent);
416 : static void get_values_def(List *values_lists, deparse_context *context);
417 : static void get_with_clause(Query *query, deparse_context *context);
418 : static void get_select_query_def(Query *query, deparse_context *context);
419 : static void get_insert_query_def(Query *query, deparse_context *context);
420 : static void get_update_query_def(Query *query, deparse_context *context);
421 : static void get_update_query_targetlist_def(Query *query, List *targetList,
422 : deparse_context *context,
423 : RangeTblEntry *rte);
424 : static void get_delete_query_def(Query *query, deparse_context *context);
425 : static void get_merge_query_def(Query *query, deparse_context *context);
426 : static void get_utility_query_def(Query *query, deparse_context *context);
427 : static void get_basic_select_query(Query *query, deparse_context *context);
428 : static void get_target_list(List *targetList, deparse_context *context);
429 : static void get_setop_query(Node *setOp, Query *query,
430 : deparse_context *context);
431 : static Node *get_rule_sortgroupclause(Index ref, List *tlist,
432 : bool force_colno,
433 : deparse_context *context);
434 : static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
435 : bool omit_parens, deparse_context *context);
436 : static void get_rule_orderby(List *orderList, List *targetList,
437 : bool force_colno, deparse_context *context);
438 : static void get_rule_windowclause(Query *query, deparse_context *context);
439 : static void get_rule_windowspec(WindowClause *wc, List *targetList,
440 : deparse_context *context);
441 : static char *get_variable(Var *var, int levelsup, bool istoplevel,
442 : deparse_context *context);
443 : static void get_special_variable(Node *node, deparse_context *context,
444 : void *callback_arg);
445 : static void resolve_special_varno(Node *node, deparse_context *context,
446 : rsv_callback callback, void *callback_arg);
447 : static Node *find_param_referent(Param *param, deparse_context *context,
448 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
449 : static SubPlan *find_param_generator(Param *param, deparse_context *context,
450 : int *column_p);
451 : static SubPlan *find_param_generator_initplan(Param *param, Plan *plan,
452 : int *column_p);
453 : static void get_parameter(Param *param, deparse_context *context);
454 : static const char *get_simple_binary_op_name(OpExpr *expr);
455 : static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
456 : static void appendContextKeyword(deparse_context *context, const char *str,
457 : int indentBefore, int indentAfter, int indentPlus);
458 : static void removeStringInfoSpaces(StringInfo str);
459 : static void get_rule_expr(Node *node, deparse_context *context,
460 : bool showimplicit);
461 : static void get_rule_expr_toplevel(Node *node, deparse_context *context,
462 : bool showimplicit);
463 : static void get_rule_list_toplevel(List *lst, deparse_context *context,
464 : bool showimplicit);
465 : static void get_rule_expr_funccall(Node *node, deparse_context *context,
466 : bool showimplicit);
467 : static bool looks_like_function(Node *node);
468 : static void get_oper_expr(OpExpr *expr, deparse_context *context);
469 : static void get_func_expr(FuncExpr *expr, deparse_context *context,
470 : bool showimplicit);
471 : static void get_agg_expr(Aggref *aggref, deparse_context *context,
472 : Aggref *original_aggref);
473 : static void get_agg_expr_helper(Aggref *aggref, deparse_context *context,
474 : Aggref *original_aggref, const char *funcname,
475 : const char *options, bool is_json_objectagg);
476 : static void get_agg_combine_expr(Node *node, deparse_context *context,
477 : void *callback_arg);
478 : static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
479 : static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
480 : const char *funcname, const char *options,
481 : bool is_json_objectagg);
482 : static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
483 : static void get_coercion_expr(Node *arg, deparse_context *context,
484 : Oid resulttype, int32 resulttypmod,
485 : Node *parentNode);
486 : static void get_const_expr(Const *constval, deparse_context *context,
487 : int showtype);
488 : static void get_const_collation(Const *constval, deparse_context *context);
489 : static void get_json_format(JsonFormat *format, StringInfo buf);
490 : static void get_json_returning(JsonReturning *returning, StringInfo buf,
491 : bool json_format_by_default);
492 : static void get_json_constructor(JsonConstructorExpr *ctor,
493 : deparse_context *context, bool showimplicit);
494 : static void get_json_constructor_options(JsonConstructorExpr *ctor,
495 : StringInfo buf);
496 : static void get_json_agg_constructor(JsonConstructorExpr *ctor,
497 : deparse_context *context,
498 : const char *funcname,
499 : bool is_json_objectagg);
500 : static void simple_quote_literal(StringInfo buf, const char *val);
501 : static void get_sublink_expr(SubLink *sublink, deparse_context *context);
502 : static void get_tablefunc(TableFunc *tf, deparse_context *context,
503 : bool showimplicit);
504 : static void get_from_clause(Query *query, const char *prefix,
505 : deparse_context *context);
506 : static void get_from_clause_item(Node *jtnode, Query *query,
507 : deparse_context *context);
508 : static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
509 : deparse_context *context);
510 : static void get_column_alias_list(deparse_columns *colinfo,
511 : deparse_context *context);
512 : static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
513 : deparse_columns *colinfo,
514 : deparse_context *context);
515 : static void get_tablesample_def(TableSampleClause *tablesample,
516 : deparse_context *context);
517 : static void get_opclass_name(Oid opclass, Oid actual_datatype,
518 : StringInfo buf);
519 : static Node *processIndirection(Node *node, deparse_context *context);
520 : static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
521 : static char *get_relation_name(Oid relid);
522 : static char *generate_relation_name(Oid relid, List *namespaces);
523 : static char *generate_qualified_relation_name(Oid relid);
524 : static char *generate_function_name(Oid funcid, int nargs,
525 : List *argnames, Oid *argtypes,
526 : bool has_variadic, bool *use_variadic_p,
527 : bool inGroupBy);
528 : static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
529 : static void add_cast_to(StringInfo buf, Oid typid);
530 : static char *generate_qualified_type_name(Oid typid);
531 : static text *string_to_text(char *str);
532 : static char *flatten_reloptions(Oid relid);
533 : static void get_reloptions(StringInfo buf, Datum reloptions);
534 : static void get_json_path_spec(Node *path_spec, deparse_context *context,
535 : bool showimplicit);
536 : static void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
537 : deparse_context *context,
538 : bool showimplicit);
539 : static void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
540 : deparse_context *context,
541 : bool showimplicit,
542 : bool needcomma);
543 :
544 : #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
545 :
546 :
547 : /* ----------
548 : * pg_get_ruledef - Do it all and return a text
549 : * that could be used as a statement
550 : * to recreate the rule
551 : * ----------
552 : */
553 : Datum
554 450 : pg_get_ruledef(PG_FUNCTION_ARGS)
555 : {
556 450 : Oid ruleoid = PG_GETARG_OID(0);
557 : int prettyFlags;
558 : char *res;
559 :
560 450 : prettyFlags = PRETTYFLAG_INDENT;
561 :
562 450 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
563 :
564 450 : if (res == NULL)
565 6 : PG_RETURN_NULL();
566 :
567 444 : PG_RETURN_TEXT_P(string_to_text(res));
568 : }
569 :
570 :
571 : Datum
572 114 : pg_get_ruledef_ext(PG_FUNCTION_ARGS)
573 : {
574 114 : Oid ruleoid = PG_GETARG_OID(0);
575 114 : bool pretty = PG_GETARG_BOOL(1);
576 : int prettyFlags;
577 : char *res;
578 :
579 114 : prettyFlags = GET_PRETTY_FLAGS(pretty);
580 :
581 114 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
582 :
583 114 : if (res == NULL)
584 0 : PG_RETURN_NULL();
585 :
586 114 : PG_RETURN_TEXT_P(string_to_text(res));
587 : }
588 :
589 :
590 : static char *
591 564 : pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
592 : {
593 : Datum args[1];
594 : char nulls[1];
595 : int spirc;
596 : HeapTuple ruletup;
597 : TupleDesc rulettc;
598 : StringInfoData buf;
599 :
600 : /*
601 : * Do this first so that string is alloc'd in outer context not SPI's.
602 : */
603 564 : initStringInfo(&buf);
604 :
605 : /*
606 : * Connect to SPI manager
607 : */
608 564 : SPI_connect();
609 :
610 : /*
611 : * On the first call prepare the plan to lookup pg_rewrite. We read
612 : * pg_rewrite over the SPI manager instead of using the syscache to be
613 : * checked for read access on pg_rewrite.
614 : */
615 564 : if (plan_getrulebyoid == NULL)
616 : {
617 : Oid argtypes[1];
618 : SPIPlanPtr plan;
619 :
620 40 : argtypes[0] = OIDOID;
621 40 : plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
622 40 : if (plan == NULL)
623 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
624 40 : SPI_keepplan(plan);
625 40 : plan_getrulebyoid = plan;
626 : }
627 :
628 : /*
629 : * Get the pg_rewrite tuple for this rule
630 : */
631 564 : args[0] = ObjectIdGetDatum(ruleoid);
632 564 : nulls[0] = ' ';
633 564 : spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
634 564 : if (spirc != SPI_OK_SELECT)
635 0 : elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
636 564 : if (SPI_processed != 1)
637 : {
638 : /*
639 : * There is no tuple data available here, just keep the output buffer
640 : * empty.
641 : */
642 : }
643 : else
644 : {
645 : /*
646 : * Get the rule's definition and put it into executor's memory
647 : */
648 558 : ruletup = SPI_tuptable->vals[0];
649 558 : rulettc = SPI_tuptable->tupdesc;
650 558 : make_ruledef(&buf, ruletup, rulettc, prettyFlags);
651 : }
652 :
653 : /*
654 : * Disconnect from SPI manager
655 : */
656 564 : if (SPI_finish() != SPI_OK_FINISH)
657 0 : elog(ERROR, "SPI_finish failed");
658 :
659 564 : if (buf.len == 0)
660 6 : return NULL;
661 :
662 558 : return buf.data;
663 : }
664 :
665 :
666 : /* ----------
667 : * pg_get_viewdef - Mainly the same thing, but we
668 : * only return the SELECT part of a view
669 : * ----------
670 : */
671 : Datum
672 2378 : pg_get_viewdef(PG_FUNCTION_ARGS)
673 : {
674 : /* By OID */
675 2378 : Oid viewoid = PG_GETARG_OID(0);
676 : int prettyFlags;
677 : char *res;
678 :
679 2378 : prettyFlags = PRETTYFLAG_INDENT;
680 :
681 2378 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
682 :
683 2378 : if (res == NULL)
684 6 : PG_RETURN_NULL();
685 :
686 2372 : PG_RETURN_TEXT_P(string_to_text(res));
687 : }
688 :
689 :
690 : Datum
691 550 : pg_get_viewdef_ext(PG_FUNCTION_ARGS)
692 : {
693 : /* By OID */
694 550 : Oid viewoid = PG_GETARG_OID(0);
695 550 : bool pretty = PG_GETARG_BOOL(1);
696 : int prettyFlags;
697 : char *res;
698 :
699 550 : prettyFlags = GET_PRETTY_FLAGS(pretty);
700 :
701 550 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
702 :
703 550 : if (res == NULL)
704 0 : PG_RETURN_NULL();
705 :
706 550 : PG_RETURN_TEXT_P(string_to_text(res));
707 : }
708 :
709 : Datum
710 6 : pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
711 : {
712 : /* By OID */
713 6 : Oid viewoid = PG_GETARG_OID(0);
714 6 : int wrap = PG_GETARG_INT32(1);
715 : int prettyFlags;
716 : char *res;
717 :
718 : /* calling this implies we want pretty printing */
719 6 : prettyFlags = GET_PRETTY_FLAGS(true);
720 :
721 6 : res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
722 :
723 6 : if (res == NULL)
724 0 : PG_RETURN_NULL();
725 :
726 6 : PG_RETURN_TEXT_P(string_to_text(res));
727 : }
728 :
729 : Datum
730 72 : pg_get_viewdef_name(PG_FUNCTION_ARGS)
731 : {
732 : /* By qualified name */
733 72 : text *viewname = PG_GETARG_TEXT_PP(0);
734 : int prettyFlags;
735 : RangeVar *viewrel;
736 : Oid viewoid;
737 : char *res;
738 :
739 72 : prettyFlags = PRETTYFLAG_INDENT;
740 :
741 : /* Look up view name. Can't lock it - we might not have privileges. */
742 72 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
743 72 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
744 :
745 72 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
746 :
747 72 : if (res == NULL)
748 0 : PG_RETURN_NULL();
749 :
750 72 : PG_RETURN_TEXT_P(string_to_text(res));
751 : }
752 :
753 :
754 : Datum
755 402 : pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
756 : {
757 : /* By qualified name */
758 402 : text *viewname = PG_GETARG_TEXT_PP(0);
759 402 : bool pretty = PG_GETARG_BOOL(1);
760 : int prettyFlags;
761 : RangeVar *viewrel;
762 : Oid viewoid;
763 : char *res;
764 :
765 402 : prettyFlags = GET_PRETTY_FLAGS(pretty);
766 :
767 : /* Look up view name. Can't lock it - we might not have privileges. */
768 402 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
769 402 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
770 :
771 402 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
772 :
773 402 : if (res == NULL)
774 0 : PG_RETURN_NULL();
775 :
776 402 : PG_RETURN_TEXT_P(string_to_text(res));
777 : }
778 :
779 : /*
780 : * Common code for by-OID and by-name variants of pg_get_viewdef
781 : */
782 : static char *
783 3408 : pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
784 : {
785 : Datum args[2];
786 : char nulls[2];
787 : int spirc;
788 : HeapTuple ruletup;
789 : TupleDesc rulettc;
790 : StringInfoData buf;
791 :
792 : /*
793 : * Do this first so that string is alloc'd in outer context not SPI's.
794 : */
795 3408 : initStringInfo(&buf);
796 :
797 : /*
798 : * Connect to SPI manager
799 : */
800 3408 : SPI_connect();
801 :
802 : /*
803 : * On the first call prepare the plan to lookup pg_rewrite. We read
804 : * pg_rewrite over the SPI manager instead of using the syscache to be
805 : * checked for read access on pg_rewrite.
806 : */
807 3408 : if (plan_getviewrule == NULL)
808 : {
809 : Oid argtypes[2];
810 : SPIPlanPtr plan;
811 :
812 234 : argtypes[0] = OIDOID;
813 234 : argtypes[1] = NAMEOID;
814 234 : plan = SPI_prepare(query_getviewrule, 2, argtypes);
815 234 : if (plan == NULL)
816 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
817 234 : SPI_keepplan(plan);
818 234 : plan_getviewrule = plan;
819 : }
820 :
821 : /*
822 : * Get the pg_rewrite tuple for the view's SELECT rule
823 : */
824 3408 : args[0] = ObjectIdGetDatum(viewoid);
825 3408 : args[1] = DirectFunctionCall1(namein, CStringGetDatum(ViewSelectRuleName));
826 3408 : nulls[0] = ' ';
827 3408 : nulls[1] = ' ';
828 3408 : spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
829 3408 : if (spirc != SPI_OK_SELECT)
830 0 : elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
831 3408 : if (SPI_processed != 1)
832 : {
833 : /*
834 : * There is no tuple data available here, just keep the output buffer
835 : * empty.
836 : */
837 : }
838 : else
839 : {
840 : /*
841 : * Get the rule's definition and put it into executor's memory
842 : */
843 3402 : ruletup = SPI_tuptable->vals[0];
844 3402 : rulettc = SPI_tuptable->tupdesc;
845 3402 : make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
846 : }
847 :
848 : /*
849 : * Disconnect from SPI manager
850 : */
851 3408 : if (SPI_finish() != SPI_OK_FINISH)
852 0 : elog(ERROR, "SPI_finish failed");
853 :
854 3408 : if (buf.len == 0)
855 6 : return NULL;
856 :
857 3402 : return buf.data;
858 : }
859 :
860 : /* ----------
861 : * pg_get_triggerdef - Get the definition of a trigger
862 : * ----------
863 : */
864 : Datum
865 204 : pg_get_triggerdef(PG_FUNCTION_ARGS)
866 : {
867 204 : Oid trigid = PG_GETARG_OID(0);
868 : char *res;
869 :
870 204 : res = pg_get_triggerdef_worker(trigid, false);
871 :
872 204 : if (res == NULL)
873 6 : PG_RETURN_NULL();
874 :
875 198 : PG_RETURN_TEXT_P(string_to_text(res));
876 : }
877 :
878 : Datum
879 1130 : pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
880 : {
881 1130 : Oid trigid = PG_GETARG_OID(0);
882 1130 : bool pretty = PG_GETARG_BOOL(1);
883 : char *res;
884 :
885 1130 : res = pg_get_triggerdef_worker(trigid, pretty);
886 :
887 1130 : if (res == NULL)
888 0 : PG_RETURN_NULL();
889 :
890 1130 : PG_RETURN_TEXT_P(string_to_text(res));
891 : }
892 :
893 : static char *
894 1334 : pg_get_triggerdef_worker(Oid trigid, bool pretty)
895 : {
896 : HeapTuple ht_trig;
897 : Form_pg_trigger trigrec;
898 : StringInfoData buf;
899 : Relation tgrel;
900 : ScanKeyData skey[1];
901 : SysScanDesc tgscan;
902 1334 : int findx = 0;
903 : char *tgname;
904 : char *tgoldtable;
905 : char *tgnewtable;
906 : Datum value;
907 : bool isnull;
908 :
909 : /*
910 : * Fetch the pg_trigger tuple by the Oid of the trigger
911 : */
912 1334 : tgrel = table_open(TriggerRelationId, AccessShareLock);
913 :
914 1334 : ScanKeyInit(&skey[0],
915 : Anum_pg_trigger_oid,
916 : BTEqualStrategyNumber, F_OIDEQ,
917 : ObjectIdGetDatum(trigid));
918 :
919 1334 : tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
920 : NULL, 1, skey);
921 :
922 1334 : ht_trig = systable_getnext(tgscan);
923 :
924 1334 : if (!HeapTupleIsValid(ht_trig))
925 : {
926 6 : systable_endscan(tgscan);
927 6 : table_close(tgrel, AccessShareLock);
928 6 : return NULL;
929 : }
930 :
931 1328 : trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
932 :
933 : /*
934 : * Start the trigger definition. Note that the trigger's name should never
935 : * be schema-qualified, but the trigger rel's name may be.
936 : */
937 1328 : initStringInfo(&buf);
938 :
939 1328 : tgname = NameStr(trigrec->tgname);
940 2656 : appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
941 1328 : OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
942 : quote_identifier(tgname));
943 :
944 1328 : if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
945 520 : appendStringInfoString(&buf, "BEFORE");
946 808 : else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
947 784 : appendStringInfoString(&buf, "AFTER");
948 24 : else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
949 24 : appendStringInfoString(&buf, "INSTEAD OF");
950 : else
951 0 : elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
952 :
953 1328 : if (TRIGGER_FOR_INSERT(trigrec->tgtype))
954 : {
955 864 : appendStringInfoString(&buf, " INSERT");
956 864 : findx++;
957 : }
958 1328 : if (TRIGGER_FOR_DELETE(trigrec->tgtype))
959 : {
960 226 : if (findx > 0)
961 90 : appendStringInfoString(&buf, " OR DELETE");
962 : else
963 136 : appendStringInfoString(&buf, " DELETE");
964 226 : findx++;
965 : }
966 1328 : if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
967 : {
968 648 : if (findx > 0)
969 320 : appendStringInfoString(&buf, " OR UPDATE");
970 : else
971 328 : appendStringInfoString(&buf, " UPDATE");
972 648 : findx++;
973 : /* tgattr is first var-width field, so OK to access directly */
974 648 : if (trigrec->tgattr.dim1 > 0)
975 : {
976 : int i;
977 :
978 76 : appendStringInfoString(&buf, " OF ");
979 168 : for (i = 0; i < trigrec->tgattr.dim1; i++)
980 : {
981 : char *attname;
982 :
983 92 : if (i > 0)
984 16 : appendStringInfoString(&buf, ", ");
985 92 : attname = get_attname(trigrec->tgrelid,
986 92 : trigrec->tgattr.values[i], false);
987 92 : appendStringInfoString(&buf, quote_identifier(attname));
988 : }
989 : }
990 : }
991 1328 : if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
992 : {
993 0 : if (findx > 0)
994 0 : appendStringInfoString(&buf, " OR TRUNCATE");
995 : else
996 0 : appendStringInfoString(&buf, " TRUNCATE");
997 0 : findx++;
998 : }
999 :
1000 : /*
1001 : * In non-pretty mode, always schema-qualify the target table name for
1002 : * safety. In pretty mode, schema-qualify only if not visible.
1003 : */
1004 2656 : appendStringInfo(&buf, " ON %s ",
1005 : pretty ?
1006 138 : generate_relation_name(trigrec->tgrelid, NIL) :
1007 1190 : generate_qualified_relation_name(trigrec->tgrelid));
1008 :
1009 1328 : if (OidIsValid(trigrec->tgconstraint))
1010 : {
1011 0 : if (OidIsValid(trigrec->tgconstrrelid))
1012 0 : appendStringInfo(&buf, "FROM %s ",
1013 : generate_relation_name(trigrec->tgconstrrelid, NIL));
1014 0 : if (!trigrec->tgdeferrable)
1015 0 : appendStringInfoString(&buf, "NOT ");
1016 0 : appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
1017 0 : if (trigrec->tginitdeferred)
1018 0 : appendStringInfoString(&buf, "DEFERRED ");
1019 : else
1020 0 : appendStringInfoString(&buf, "IMMEDIATE ");
1021 : }
1022 :
1023 1328 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
1024 : tgrel->rd_att, &isnull);
1025 1328 : if (!isnull)
1026 98 : tgoldtable = NameStr(*DatumGetName(value));
1027 : else
1028 1230 : tgoldtable = NULL;
1029 1328 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
1030 : tgrel->rd_att, &isnull);
1031 1328 : if (!isnull)
1032 108 : tgnewtable = NameStr(*DatumGetName(value));
1033 : else
1034 1220 : tgnewtable = NULL;
1035 1328 : if (tgoldtable != NULL || tgnewtable != NULL)
1036 : {
1037 152 : appendStringInfoString(&buf, "REFERENCING ");
1038 152 : if (tgoldtable != NULL)
1039 98 : appendStringInfo(&buf, "OLD TABLE AS %s ",
1040 : quote_identifier(tgoldtable));
1041 152 : if (tgnewtable != NULL)
1042 108 : appendStringInfo(&buf, "NEW TABLE AS %s ",
1043 : quote_identifier(tgnewtable));
1044 : }
1045 :
1046 1328 : if (TRIGGER_FOR_ROW(trigrec->tgtype))
1047 1010 : appendStringInfoString(&buf, "FOR EACH ROW ");
1048 : else
1049 318 : appendStringInfoString(&buf, "FOR EACH STATEMENT ");
1050 :
1051 : /* If the trigger has a WHEN qualification, add that */
1052 1328 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
1053 : tgrel->rd_att, &isnull);
1054 1328 : if (!isnull)
1055 : {
1056 : Node *qual;
1057 : char relkind;
1058 : deparse_context context;
1059 : deparse_namespace dpns;
1060 : RangeTblEntry *oldrte;
1061 : RangeTblEntry *newrte;
1062 :
1063 138 : appendStringInfoString(&buf, "WHEN (");
1064 :
1065 138 : qual = stringToNode(TextDatumGetCString(value));
1066 :
1067 138 : relkind = get_rel_relkind(trigrec->tgrelid);
1068 :
1069 : /* Build minimal OLD and NEW RTEs for the rel */
1070 138 : oldrte = makeNode(RangeTblEntry);
1071 138 : oldrte->rtekind = RTE_RELATION;
1072 138 : oldrte->relid = trigrec->tgrelid;
1073 138 : oldrte->relkind = relkind;
1074 138 : oldrte->rellockmode = AccessShareLock;
1075 138 : oldrte->alias = makeAlias("old", NIL);
1076 138 : oldrte->eref = oldrte->alias;
1077 138 : oldrte->lateral = false;
1078 138 : oldrte->inh = false;
1079 138 : oldrte->inFromCl = true;
1080 :
1081 138 : newrte = makeNode(RangeTblEntry);
1082 138 : newrte->rtekind = RTE_RELATION;
1083 138 : newrte->relid = trigrec->tgrelid;
1084 138 : newrte->relkind = relkind;
1085 138 : newrte->rellockmode = AccessShareLock;
1086 138 : newrte->alias = makeAlias("new", NIL);
1087 138 : newrte->eref = newrte->alias;
1088 138 : newrte->lateral = false;
1089 138 : newrte->inh = false;
1090 138 : newrte->inFromCl = true;
1091 :
1092 : /* Build two-element rtable */
1093 138 : memset(&dpns, 0, sizeof(dpns));
1094 138 : dpns.rtable = list_make2(oldrte, newrte);
1095 138 : dpns.subplans = NIL;
1096 138 : dpns.ctes = NIL;
1097 138 : dpns.appendrels = NULL;
1098 138 : set_rtable_names(&dpns, NIL, NULL);
1099 138 : set_simple_column_names(&dpns);
1100 :
1101 : /* Set up context with one-deep namespace stack */
1102 138 : context.buf = &buf;
1103 138 : context.namespaces = list_make1(&dpns);
1104 138 : context.resultDesc = NULL;
1105 138 : context.targetList = NIL;
1106 138 : context.windowClause = NIL;
1107 138 : context.varprefix = true;
1108 138 : context.prettyFlags = GET_PRETTY_FLAGS(pretty);
1109 138 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
1110 138 : context.indentLevel = PRETTYINDENT_STD;
1111 138 : context.colNamesVisible = true;
1112 138 : context.inGroupBy = false;
1113 138 : context.varInOrderBy = false;
1114 138 : context.appendparents = NULL;
1115 :
1116 138 : get_rule_expr(qual, &context, false);
1117 :
1118 138 : appendStringInfoString(&buf, ") ");
1119 : }
1120 :
1121 1328 : appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
1122 : generate_function_name(trigrec->tgfoid, 0,
1123 : NIL, NULL,
1124 : false, NULL, false));
1125 :
1126 1328 : if (trigrec->tgnargs > 0)
1127 : {
1128 : char *p;
1129 : int i;
1130 :
1131 450 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
1132 : tgrel->rd_att, &isnull);
1133 450 : if (isnull)
1134 0 : elog(ERROR, "tgargs is null for trigger %u", trigid);
1135 450 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
1136 1196 : for (i = 0; i < trigrec->tgnargs; i++)
1137 : {
1138 746 : if (i > 0)
1139 296 : appendStringInfoString(&buf, ", ");
1140 746 : simple_quote_literal(&buf, p);
1141 : /* advance p to next string embedded in tgargs */
1142 6756 : while (*p)
1143 6010 : p++;
1144 746 : p++;
1145 : }
1146 : }
1147 :
1148 : /* We deliberately do not put semi-colon at end */
1149 1328 : appendStringInfoChar(&buf, ')');
1150 :
1151 : /* Clean up */
1152 1328 : systable_endscan(tgscan);
1153 :
1154 1328 : table_close(tgrel, AccessShareLock);
1155 :
1156 1328 : return buf.data;
1157 : }
1158 :
1159 : /* ----------
1160 : * pg_get_indexdef - Get the definition of an index
1161 : *
1162 : * In the extended version, there is a colno argument as well as pretty bool.
1163 : * if colno == 0, we want a complete index definition.
1164 : * if colno > 0, we only want the Nth index key's variable or expression.
1165 : *
1166 : * Note that the SQL-function versions of this omit any info about the
1167 : * index tablespace; this is intentional because pg_dump wants it that way.
1168 : * However pg_get_indexdef_string() includes the index tablespace.
1169 : * ----------
1170 : */
1171 : Datum
1172 5208 : pg_get_indexdef(PG_FUNCTION_ARGS)
1173 : {
1174 5208 : Oid indexrelid = PG_GETARG_OID(0);
1175 : int prettyFlags;
1176 : char *res;
1177 :
1178 5208 : prettyFlags = PRETTYFLAG_INDENT;
1179 :
1180 5208 : res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1181 : false, false,
1182 : false, false,
1183 : prettyFlags, true);
1184 :
1185 5208 : if (res == NULL)
1186 6 : PG_RETURN_NULL();
1187 :
1188 5202 : PG_RETURN_TEXT_P(string_to_text(res));
1189 : }
1190 :
1191 : Datum
1192 1934 : pg_get_indexdef_ext(PG_FUNCTION_ARGS)
1193 : {
1194 1934 : Oid indexrelid = PG_GETARG_OID(0);
1195 1934 : int32 colno = PG_GETARG_INT32(1);
1196 1934 : bool pretty = PG_GETARG_BOOL(2);
1197 : int prettyFlags;
1198 : char *res;
1199 :
1200 1934 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1201 :
1202 1934 : res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1203 : colno != 0, false,
1204 : false, false,
1205 : prettyFlags, true);
1206 :
1207 1934 : if (res == NULL)
1208 0 : PG_RETURN_NULL();
1209 :
1210 1934 : PG_RETURN_TEXT_P(string_to_text(res));
1211 : }
1212 :
1213 : /*
1214 : * Internal version for use by ALTER TABLE.
1215 : * Includes a tablespace clause in the result.
1216 : * Returns a palloc'd C string; no pretty-printing.
1217 : */
1218 : char *
1219 216 : pg_get_indexdef_string(Oid indexrelid)
1220 : {
1221 216 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1222 : false, false,
1223 : true, true,
1224 : 0, false);
1225 : }
1226 :
1227 : /* Internal version that just reports the key-column definitions */
1228 : char *
1229 934 : pg_get_indexdef_columns(Oid indexrelid, bool pretty)
1230 : {
1231 : int prettyFlags;
1232 :
1233 934 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1234 :
1235 934 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1236 : true, true,
1237 : false, false,
1238 : prettyFlags, false);
1239 : }
1240 :
1241 : /* Internal version, extensible with flags to control its behavior */
1242 : char *
1243 8 : pg_get_indexdef_columns_extended(Oid indexrelid, bits16 flags)
1244 : {
1245 8 : bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1246 8 : bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1247 : int prettyFlags;
1248 :
1249 8 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1250 :
1251 8 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1252 : true, keys_only,
1253 : false, false,
1254 : prettyFlags, false);
1255 : }
1256 :
1257 : /*
1258 : * Internal workhorse to decompile an index definition.
1259 : *
1260 : * This is now used for exclusion constraints as well: if excludeOps is not
1261 : * NULL then it points to an array of exclusion operator OIDs.
1262 : */
1263 : static char *
1264 8404 : pg_get_indexdef_worker(Oid indexrelid, int colno,
1265 : const Oid *excludeOps,
1266 : bool attrsOnly, bool keysOnly,
1267 : bool showTblSpc, bool inherits,
1268 : int prettyFlags, bool missing_ok)
1269 : {
1270 : /* might want a separate isConstraint parameter later */
1271 8404 : bool isConstraint = (excludeOps != NULL);
1272 : HeapTuple ht_idx;
1273 : HeapTuple ht_idxrel;
1274 : HeapTuple ht_am;
1275 : Form_pg_index idxrec;
1276 : Form_pg_class idxrelrec;
1277 : Form_pg_am amrec;
1278 : IndexAmRoutine *amroutine;
1279 : List *indexprs;
1280 : ListCell *indexpr_item;
1281 : List *context;
1282 : Oid indrelid;
1283 : int keyno;
1284 : Datum indcollDatum;
1285 : Datum indclassDatum;
1286 : Datum indoptionDatum;
1287 : oidvector *indcollation;
1288 : oidvector *indclass;
1289 : int2vector *indoption;
1290 : StringInfoData buf;
1291 : char *str;
1292 : char *sep;
1293 :
1294 : /*
1295 : * Fetch the pg_index tuple by the Oid of the index
1296 : */
1297 8404 : ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
1298 8404 : if (!HeapTupleIsValid(ht_idx))
1299 : {
1300 6 : if (missing_ok)
1301 6 : return NULL;
1302 0 : elog(ERROR, "cache lookup failed for index %u", indexrelid);
1303 : }
1304 8398 : idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
1305 :
1306 8398 : indrelid = idxrec->indrelid;
1307 : Assert(indexrelid == idxrec->indexrelid);
1308 :
1309 : /* Must get indcollation, indclass, and indoption the hard way */
1310 8398 : indcollDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1311 : Anum_pg_index_indcollation);
1312 8398 : indcollation = (oidvector *) DatumGetPointer(indcollDatum);
1313 :
1314 8398 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1315 : Anum_pg_index_indclass);
1316 8398 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
1317 :
1318 8398 : indoptionDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1319 : Anum_pg_index_indoption);
1320 8398 : indoption = (int2vector *) DatumGetPointer(indoptionDatum);
1321 :
1322 : /*
1323 : * Fetch the pg_class tuple of the index relation
1324 : */
1325 8398 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
1326 8398 : if (!HeapTupleIsValid(ht_idxrel))
1327 0 : elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1328 8398 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
1329 :
1330 : /*
1331 : * Fetch the pg_am tuple of the index' access method
1332 : */
1333 8398 : ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
1334 8398 : if (!HeapTupleIsValid(ht_am))
1335 0 : elog(ERROR, "cache lookup failed for access method %u",
1336 : idxrelrec->relam);
1337 8398 : amrec = (Form_pg_am) GETSTRUCT(ht_am);
1338 :
1339 : /* Fetch the index AM's API struct */
1340 8398 : amroutine = GetIndexAmRoutine(amrec->amhandler);
1341 :
1342 : /*
1343 : * Get the index expressions, if any. (NOTE: we do not use the relcache
1344 : * versions of the expressions and predicate, because we want to display
1345 : * non-const-folded expressions.)
1346 : */
1347 8398 : if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs, NULL))
1348 : {
1349 : Datum exprsDatum;
1350 : char *exprsString;
1351 :
1352 550 : exprsDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1353 : Anum_pg_index_indexprs);
1354 550 : exprsString = TextDatumGetCString(exprsDatum);
1355 550 : indexprs = (List *) stringToNode(exprsString);
1356 550 : pfree(exprsString);
1357 : }
1358 : else
1359 7848 : indexprs = NIL;
1360 :
1361 8398 : indexpr_item = list_head(indexprs);
1362 :
1363 8398 : context = deparse_context_for(get_relation_name(indrelid), indrelid);
1364 :
1365 : /*
1366 : * Start the index definition. Note that the index's name should never be
1367 : * schema-qualified, but the indexed rel's name may be.
1368 : */
1369 8398 : initStringInfo(&buf);
1370 :
1371 8398 : if (!attrsOnly)
1372 : {
1373 7006 : if (!isConstraint)
1374 13804 : appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
1375 6902 : idxrec->indisunique ? "UNIQUE " : "",
1376 6902 : quote_identifier(NameStr(idxrelrec->relname)),
1377 6902 : idxrelrec->relkind == RELKIND_PARTITIONED_INDEX
1378 658 : && !inherits ? "ONLY " : "",
1379 6902 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
1380 1484 : generate_relation_name(indrelid, NIL) :
1381 5418 : generate_qualified_relation_name(indrelid),
1382 6902 : quote_identifier(NameStr(amrec->amname)));
1383 : else /* currently, must be EXCLUDE constraint */
1384 104 : appendStringInfo(&buf, "EXCLUDE USING %s (",
1385 104 : quote_identifier(NameStr(amrec->amname)));
1386 : }
1387 :
1388 : /*
1389 : * Report the indexed attributes
1390 : */
1391 8398 : sep = "";
1392 21194 : for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1393 : {
1394 12894 : AttrNumber attnum = idxrec->indkey.values[keyno];
1395 : Oid keycoltype;
1396 : Oid keycolcollation;
1397 :
1398 : /*
1399 : * Ignore non-key attributes if told to.
1400 : */
1401 12894 : if (keysOnly && keyno >= idxrec->indnkeyatts)
1402 98 : break;
1403 :
1404 : /* Otherwise, print INCLUDE to divide key and non-key attrs. */
1405 12796 : if (!colno && keyno == idxrec->indnkeyatts)
1406 : {
1407 250 : appendStringInfoString(&buf, ") INCLUDE (");
1408 250 : sep = "";
1409 : }
1410 :
1411 12796 : if (!colno)
1412 12166 : appendStringInfoString(&buf, sep);
1413 12796 : sep = ", ";
1414 :
1415 12796 : if (attnum != 0)
1416 : {
1417 : /* Simple index column */
1418 : char *attname;
1419 : int32 keycoltypmod;
1420 :
1421 12100 : attname = get_attname(indrelid, attnum, false);
1422 12100 : if (!colno || colno == keyno + 1)
1423 11932 : appendStringInfoString(&buf, quote_identifier(attname));
1424 12100 : get_atttypetypmodcoll(indrelid, attnum,
1425 : &keycoltype, &keycoltypmod,
1426 : &keycolcollation);
1427 : }
1428 : else
1429 : {
1430 : /* expressional index */
1431 : Node *indexkey;
1432 :
1433 696 : if (indexpr_item == NULL)
1434 0 : elog(ERROR, "too few entries in indexprs list");
1435 696 : indexkey = (Node *) lfirst(indexpr_item);
1436 696 : indexpr_item = lnext(indexprs, indexpr_item);
1437 : /* Deparse */
1438 696 : str = deparse_expression_pretty(indexkey, context, false, false,
1439 : prettyFlags, 0);
1440 696 : if (!colno || colno == keyno + 1)
1441 : {
1442 : /* Need parens if it's not a bare function call */
1443 684 : if (looks_like_function(indexkey))
1444 52 : appendStringInfoString(&buf, str);
1445 : else
1446 632 : appendStringInfo(&buf, "(%s)", str);
1447 : }
1448 696 : keycoltype = exprType(indexkey);
1449 696 : keycolcollation = exprCollation(indexkey);
1450 : }
1451 :
1452 : /* Print additional decoration for (selected) key columns */
1453 12796 : if (!attrsOnly && keyno < idxrec->indnkeyatts &&
1454 0 : (!colno || colno == keyno + 1))
1455 : {
1456 10434 : int16 opt = indoption->values[keyno];
1457 10434 : Oid indcoll = indcollation->values[keyno];
1458 10434 : Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1459 10434 : bool has_options = attoptions != (Datum) 0;
1460 :
1461 : /* Add collation, if not default for column */
1462 10434 : if (OidIsValid(indcoll) && indcoll != keycolcollation)
1463 94 : appendStringInfo(&buf, " COLLATE %s",
1464 : generate_collation_name((indcoll)));
1465 :
1466 : /* Add the operator class name, if not default */
1467 10434 : get_opclass_name(indclass->values[keyno],
1468 : has_options ? InvalidOid : keycoltype, &buf);
1469 :
1470 10434 : if (has_options)
1471 : {
1472 34 : appendStringInfoString(&buf, " (");
1473 34 : get_reloptions(&buf, attoptions);
1474 34 : appendStringInfoChar(&buf, ')');
1475 : }
1476 :
1477 : /* Add options if relevant */
1478 10434 : if (amroutine->amcanorder)
1479 : {
1480 : /* if it supports sort ordering, report DESC and NULLS opts */
1481 8378 : if (opt & INDOPTION_DESC)
1482 : {
1483 0 : appendStringInfoString(&buf, " DESC");
1484 : /* NULLS FIRST is the default in this case */
1485 0 : if (!(opt & INDOPTION_NULLS_FIRST))
1486 0 : appendStringInfoString(&buf, " NULLS LAST");
1487 : }
1488 : else
1489 : {
1490 8378 : if (opt & INDOPTION_NULLS_FIRST)
1491 0 : appendStringInfoString(&buf, " NULLS FIRST");
1492 : }
1493 : }
1494 :
1495 : /* Add the exclusion operator if relevant */
1496 10434 : if (excludeOps != NULL)
1497 124 : appendStringInfo(&buf, " WITH %s",
1498 124 : generate_operator_name(excludeOps[keyno],
1499 : keycoltype,
1500 : keycoltype));
1501 : }
1502 : }
1503 :
1504 8398 : if (!attrsOnly)
1505 : {
1506 7006 : appendStringInfoChar(&buf, ')');
1507 :
1508 7006 : if (idxrec->indnullsnotdistinct)
1509 12 : appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1510 :
1511 : /*
1512 : * If it has options, append "WITH (options)"
1513 : */
1514 7006 : str = flatten_reloptions(indexrelid);
1515 7006 : if (str)
1516 : {
1517 210 : appendStringInfo(&buf, " WITH (%s)", str);
1518 210 : pfree(str);
1519 : }
1520 :
1521 : /*
1522 : * Print tablespace, but only if requested
1523 : */
1524 7006 : if (showTblSpc)
1525 : {
1526 : Oid tblspc;
1527 :
1528 216 : tblspc = get_rel_tablespace(indexrelid);
1529 216 : if (OidIsValid(tblspc))
1530 : {
1531 54 : if (isConstraint)
1532 0 : appendStringInfoString(&buf, " USING INDEX");
1533 54 : appendStringInfo(&buf, " TABLESPACE %s",
1534 54 : quote_identifier(get_tablespace_name(tblspc)));
1535 : }
1536 : }
1537 :
1538 : /*
1539 : * If it's a partial index, decompile and append the predicate
1540 : */
1541 7006 : if (!heap_attisnull(ht_idx, Anum_pg_index_indpred, NULL))
1542 : {
1543 : Node *node;
1544 : Datum predDatum;
1545 : char *predString;
1546 :
1547 : /* Convert text string to node tree */
1548 314 : predDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1549 : Anum_pg_index_indpred);
1550 314 : predString = TextDatumGetCString(predDatum);
1551 314 : node = (Node *) stringToNode(predString);
1552 314 : pfree(predString);
1553 :
1554 : /* Deparse */
1555 314 : str = deparse_expression_pretty(node, context, false, false,
1556 : prettyFlags, 0);
1557 314 : if (isConstraint)
1558 42 : appendStringInfo(&buf, " WHERE (%s)", str);
1559 : else
1560 272 : appendStringInfo(&buf, " WHERE %s", str);
1561 : }
1562 : }
1563 :
1564 : /* Clean up */
1565 8398 : ReleaseSysCache(ht_idx);
1566 8398 : ReleaseSysCache(ht_idxrel);
1567 8398 : ReleaseSysCache(ht_am);
1568 :
1569 8398 : return buf.data;
1570 : }
1571 :
1572 : /* ----------
1573 : * pg_get_querydef
1574 : *
1575 : * Public entry point to deparse one query parsetree.
1576 : * The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
1577 : *
1578 : * The result is a palloc'd C string.
1579 : * ----------
1580 : */
1581 : char *
1582 0 : pg_get_querydef(Query *query, bool pretty)
1583 : {
1584 : StringInfoData buf;
1585 : int prettyFlags;
1586 :
1587 0 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1588 :
1589 0 : initStringInfo(&buf);
1590 :
1591 0 : get_query_def(query, &buf, NIL, NULL, true,
1592 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1593 :
1594 0 : return buf.data;
1595 : }
1596 :
1597 : /*
1598 : * pg_get_statisticsobjdef
1599 : * Get the definition of an extended statistics object
1600 : */
1601 : Datum
1602 248 : pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
1603 : {
1604 248 : Oid statextid = PG_GETARG_OID(0);
1605 : char *res;
1606 :
1607 248 : res = pg_get_statisticsobj_worker(statextid, false, true);
1608 :
1609 248 : if (res == NULL)
1610 6 : PG_RETURN_NULL();
1611 :
1612 242 : PG_RETURN_TEXT_P(string_to_text(res));
1613 : }
1614 :
1615 : /*
1616 : * Internal version for use by ALTER TABLE.
1617 : * Includes a tablespace clause in the result.
1618 : * Returns a palloc'd C string; no pretty-printing.
1619 : */
1620 : char *
1621 14 : pg_get_statisticsobjdef_string(Oid statextid)
1622 : {
1623 14 : return pg_get_statisticsobj_worker(statextid, false, false);
1624 : }
1625 :
1626 : /*
1627 : * pg_get_statisticsobjdef_columns
1628 : * Get columns and expressions for an extended statistics object
1629 : */
1630 : Datum
1631 402 : pg_get_statisticsobjdef_columns(PG_FUNCTION_ARGS)
1632 : {
1633 402 : Oid statextid = PG_GETARG_OID(0);
1634 : char *res;
1635 :
1636 402 : res = pg_get_statisticsobj_worker(statextid, true, true);
1637 :
1638 402 : if (res == NULL)
1639 0 : PG_RETURN_NULL();
1640 :
1641 402 : PG_RETURN_TEXT_P(string_to_text(res));
1642 : }
1643 :
1644 : /*
1645 : * Internal workhorse to decompile an extended statistics object.
1646 : */
1647 : static char *
1648 664 : pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
1649 : {
1650 : Form_pg_statistic_ext statextrec;
1651 : HeapTuple statexttup;
1652 : StringInfoData buf;
1653 : int colno;
1654 : char *nsp;
1655 : ArrayType *arr;
1656 : char *enabled;
1657 : Datum datum;
1658 : bool ndistinct_enabled;
1659 : bool dependencies_enabled;
1660 : bool mcv_enabled;
1661 : int i;
1662 : List *context;
1663 : ListCell *lc;
1664 664 : List *exprs = NIL;
1665 : bool has_exprs;
1666 : int ncolumns;
1667 :
1668 664 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1669 :
1670 664 : if (!HeapTupleIsValid(statexttup))
1671 : {
1672 6 : if (missing_ok)
1673 6 : return NULL;
1674 0 : elog(ERROR, "cache lookup failed for statistics object %u", statextid);
1675 : }
1676 :
1677 : /* has the statistics expressions? */
1678 658 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
1679 :
1680 658 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1681 :
1682 : /*
1683 : * Get the statistics expressions, if any. (NOTE: we do not use the
1684 : * relcache versions of the expressions, because we want to display
1685 : * non-const-folded expressions.)
1686 : */
1687 658 : if (has_exprs)
1688 : {
1689 : Datum exprsDatum;
1690 : char *exprsString;
1691 :
1692 142 : exprsDatum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1693 : Anum_pg_statistic_ext_stxexprs);
1694 142 : exprsString = TextDatumGetCString(exprsDatum);
1695 142 : exprs = (List *) stringToNode(exprsString);
1696 142 : pfree(exprsString);
1697 : }
1698 : else
1699 516 : exprs = NIL;
1700 :
1701 : /* count the number of columns (attributes and expressions) */
1702 658 : ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
1703 :
1704 658 : initStringInfo(&buf);
1705 :
1706 658 : if (!columns_only)
1707 : {
1708 256 : nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
1709 256 : appendStringInfo(&buf, "CREATE STATISTICS %s",
1710 : quote_qualified_identifier(nsp,
1711 256 : NameStr(statextrec->stxname)));
1712 :
1713 : /*
1714 : * Decode the stxkind column so that we know which stats types to
1715 : * print.
1716 : */
1717 256 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1718 : Anum_pg_statistic_ext_stxkind);
1719 256 : arr = DatumGetArrayTypeP(datum);
1720 256 : if (ARR_NDIM(arr) != 1 ||
1721 256 : ARR_HASNULL(arr) ||
1722 256 : ARR_ELEMTYPE(arr) != CHAROID)
1723 0 : elog(ERROR, "stxkind is not a 1-D char array");
1724 256 : enabled = (char *) ARR_DATA_PTR(arr);
1725 :
1726 256 : ndistinct_enabled = false;
1727 256 : dependencies_enabled = false;
1728 256 : mcv_enabled = false;
1729 :
1730 662 : for (i = 0; i < ARR_DIMS(arr)[0]; i++)
1731 : {
1732 406 : if (enabled[i] == STATS_EXT_NDISTINCT)
1733 136 : ndistinct_enabled = true;
1734 270 : else if (enabled[i] == STATS_EXT_DEPENDENCIES)
1735 88 : dependencies_enabled = true;
1736 182 : else if (enabled[i] == STATS_EXT_MCV)
1737 106 : mcv_enabled = true;
1738 :
1739 : /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
1740 : }
1741 :
1742 : /*
1743 : * If any option is disabled, then we'll need to append the types
1744 : * clause to show which options are enabled. We omit the types clause
1745 : * on purpose when all options are enabled, so a pg_dump/pg_restore
1746 : * will create all statistics types on a newer postgres version, if
1747 : * the statistics had all options enabled on the original version.
1748 : *
1749 : * But if the statistics is defined on just a single column, it has to
1750 : * be an expression statistics. In that case we don't need to specify
1751 : * kinds.
1752 : */
1753 256 : if ((!ndistinct_enabled || !dependencies_enabled || !mcv_enabled) &&
1754 : (ncolumns > 1))
1755 : {
1756 120 : bool gotone = false;
1757 :
1758 120 : appendStringInfoString(&buf, " (");
1759 :
1760 120 : if (ndistinct_enabled)
1761 : {
1762 66 : appendStringInfoString(&buf, "ndistinct");
1763 66 : gotone = true;
1764 : }
1765 :
1766 120 : if (dependencies_enabled)
1767 : {
1768 18 : appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
1769 18 : gotone = true;
1770 : }
1771 :
1772 120 : if (mcv_enabled)
1773 36 : appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
1774 :
1775 120 : appendStringInfoChar(&buf, ')');
1776 : }
1777 :
1778 256 : appendStringInfoString(&buf, " ON ");
1779 : }
1780 :
1781 : /* decode simple column references */
1782 1870 : for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
1783 : {
1784 1212 : AttrNumber attnum = statextrec->stxkeys.values[colno];
1785 : char *attname;
1786 :
1787 1212 : if (colno > 0)
1788 684 : appendStringInfoString(&buf, ", ");
1789 :
1790 1212 : attname = get_attname(statextrec->stxrelid, attnum, false);
1791 :
1792 1212 : appendStringInfoString(&buf, quote_identifier(attname));
1793 : }
1794 :
1795 658 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
1796 : statextrec->stxrelid);
1797 :
1798 886 : foreach(lc, exprs)
1799 : {
1800 228 : Node *expr = (Node *) lfirst(lc);
1801 : char *str;
1802 228 : int prettyFlags = PRETTYFLAG_PAREN;
1803 :
1804 228 : str = deparse_expression_pretty(expr, context, false, false,
1805 : prettyFlags, 0);
1806 :
1807 228 : if (colno > 0)
1808 98 : appendStringInfoString(&buf, ", ");
1809 :
1810 : /* Need parens if it's not a bare function call */
1811 228 : if (looks_like_function(expr))
1812 34 : appendStringInfoString(&buf, str);
1813 : else
1814 194 : appendStringInfo(&buf, "(%s)", str);
1815 :
1816 228 : colno++;
1817 : }
1818 :
1819 658 : if (!columns_only)
1820 256 : appendStringInfo(&buf, " FROM %s",
1821 : generate_relation_name(statextrec->stxrelid, NIL));
1822 :
1823 658 : ReleaseSysCache(statexttup);
1824 :
1825 658 : return buf.data;
1826 : }
1827 :
1828 : /*
1829 : * Generate text array of expressions for statistics object.
1830 : */
1831 : Datum
1832 24 : pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
1833 : {
1834 24 : Oid statextid = PG_GETARG_OID(0);
1835 : Form_pg_statistic_ext statextrec;
1836 : HeapTuple statexttup;
1837 : Datum datum;
1838 : List *context;
1839 : ListCell *lc;
1840 24 : List *exprs = NIL;
1841 : bool has_exprs;
1842 : char *tmp;
1843 24 : ArrayBuildState *astate = NULL;
1844 :
1845 24 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1846 :
1847 24 : if (!HeapTupleIsValid(statexttup))
1848 0 : PG_RETURN_NULL();
1849 :
1850 : /* Does the stats object have expressions? */
1851 24 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
1852 :
1853 : /* no expressions? we're done */
1854 24 : if (!has_exprs)
1855 : {
1856 12 : ReleaseSysCache(statexttup);
1857 12 : PG_RETURN_NULL();
1858 : }
1859 :
1860 12 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1861 :
1862 : /*
1863 : * Get the statistics expressions, and deparse them into text values.
1864 : */
1865 12 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1866 : Anum_pg_statistic_ext_stxexprs);
1867 12 : tmp = TextDatumGetCString(datum);
1868 12 : exprs = (List *) stringToNode(tmp);
1869 12 : pfree(tmp);
1870 :
1871 12 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
1872 : statextrec->stxrelid);
1873 :
1874 36 : foreach(lc, exprs)
1875 : {
1876 24 : Node *expr = (Node *) lfirst(lc);
1877 : char *str;
1878 24 : int prettyFlags = PRETTYFLAG_INDENT;
1879 :
1880 24 : str = deparse_expression_pretty(expr, context, false, false,
1881 : prettyFlags, 0);
1882 :
1883 24 : astate = accumArrayResult(astate,
1884 24 : PointerGetDatum(cstring_to_text(str)),
1885 : false,
1886 : TEXTOID,
1887 : CurrentMemoryContext);
1888 : }
1889 :
1890 12 : ReleaseSysCache(statexttup);
1891 :
1892 12 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1893 : }
1894 :
1895 : /*
1896 : * pg_get_partkeydef
1897 : *
1898 : * Returns the partition key specification, ie, the following:
1899 : *
1900 : * { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
1901 : */
1902 : Datum
1903 1308 : pg_get_partkeydef(PG_FUNCTION_ARGS)
1904 : {
1905 1308 : Oid relid = PG_GETARG_OID(0);
1906 : char *res;
1907 :
1908 1308 : res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
1909 :
1910 1308 : if (res == NULL)
1911 6 : PG_RETURN_NULL();
1912 :
1913 1302 : PG_RETURN_TEXT_P(string_to_text(res));
1914 : }
1915 :
1916 : /* Internal version that just reports the column definitions */
1917 : char *
1918 142 : pg_get_partkeydef_columns(Oid relid, bool pretty)
1919 : {
1920 : int prettyFlags;
1921 :
1922 142 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1923 :
1924 142 : return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
1925 : }
1926 :
1927 : /*
1928 : * Internal workhorse to decompile a partition key definition.
1929 : */
1930 : static char *
1931 1450 : pg_get_partkeydef_worker(Oid relid, int prettyFlags,
1932 : bool attrsOnly, bool missing_ok)
1933 : {
1934 : Form_pg_partitioned_table form;
1935 : HeapTuple tuple;
1936 : oidvector *partclass;
1937 : oidvector *partcollation;
1938 : List *partexprs;
1939 : ListCell *partexpr_item;
1940 : List *context;
1941 : Datum datum;
1942 : StringInfoData buf;
1943 : int keyno;
1944 : char *str;
1945 : char *sep;
1946 :
1947 1450 : tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
1948 1450 : if (!HeapTupleIsValid(tuple))
1949 : {
1950 6 : if (missing_ok)
1951 6 : return NULL;
1952 0 : elog(ERROR, "cache lookup failed for partition key of %u", relid);
1953 : }
1954 :
1955 1444 : form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
1956 :
1957 : Assert(form->partrelid == relid);
1958 :
1959 : /* Must get partclass and partcollation the hard way */
1960 1444 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1961 : Anum_pg_partitioned_table_partclass);
1962 1444 : partclass = (oidvector *) DatumGetPointer(datum);
1963 :
1964 1444 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1965 : Anum_pg_partitioned_table_partcollation);
1966 1444 : partcollation = (oidvector *) DatumGetPointer(datum);
1967 :
1968 :
1969 : /*
1970 : * Get the expressions, if any. (NOTE: we do not use the relcache
1971 : * versions of the expressions, because we want to display
1972 : * non-const-folded expressions.)
1973 : */
1974 1444 : if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprs, NULL))
1975 : {
1976 : Datum exprsDatum;
1977 : char *exprsString;
1978 :
1979 146 : exprsDatum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1980 : Anum_pg_partitioned_table_partexprs);
1981 146 : exprsString = TextDatumGetCString(exprsDatum);
1982 146 : partexprs = (List *) stringToNode(exprsString);
1983 :
1984 146 : if (!IsA(partexprs, List))
1985 0 : elog(ERROR, "unexpected node type found in partexprs: %d",
1986 : (int) nodeTag(partexprs));
1987 :
1988 146 : pfree(exprsString);
1989 : }
1990 : else
1991 1298 : partexprs = NIL;
1992 :
1993 1444 : partexpr_item = list_head(partexprs);
1994 1444 : context = deparse_context_for(get_relation_name(relid), relid);
1995 :
1996 1444 : initStringInfo(&buf);
1997 :
1998 1444 : switch (form->partstrat)
1999 : {
2000 54 : case PARTITION_STRATEGY_HASH:
2001 54 : if (!attrsOnly)
2002 54 : appendStringInfoString(&buf, "HASH");
2003 54 : break;
2004 556 : case PARTITION_STRATEGY_LIST:
2005 556 : if (!attrsOnly)
2006 516 : appendStringInfoString(&buf, "LIST");
2007 556 : break;
2008 834 : case PARTITION_STRATEGY_RANGE:
2009 834 : if (!attrsOnly)
2010 732 : appendStringInfoString(&buf, "RANGE");
2011 834 : break;
2012 0 : default:
2013 0 : elog(ERROR, "unexpected partition strategy: %d",
2014 : (int) form->partstrat);
2015 : }
2016 :
2017 1444 : if (!attrsOnly)
2018 1302 : appendStringInfoString(&buf, " (");
2019 1444 : sep = "";
2020 3040 : for (keyno = 0; keyno < form->partnatts; keyno++)
2021 : {
2022 1596 : AttrNumber attnum = form->partattrs.values[keyno];
2023 : Oid keycoltype;
2024 : Oid keycolcollation;
2025 : Oid partcoll;
2026 :
2027 1596 : appendStringInfoString(&buf, sep);
2028 1596 : sep = ", ";
2029 1596 : if (attnum != 0)
2030 : {
2031 : /* Simple attribute reference */
2032 : char *attname;
2033 : int32 keycoltypmod;
2034 :
2035 1438 : attname = get_attname(relid, attnum, false);
2036 1438 : appendStringInfoString(&buf, quote_identifier(attname));
2037 1438 : get_atttypetypmodcoll(relid, attnum,
2038 : &keycoltype, &keycoltypmod,
2039 : &keycolcollation);
2040 : }
2041 : else
2042 : {
2043 : /* Expression */
2044 : Node *partkey;
2045 :
2046 158 : if (partexpr_item == NULL)
2047 0 : elog(ERROR, "too few entries in partexprs list");
2048 158 : partkey = (Node *) lfirst(partexpr_item);
2049 158 : partexpr_item = lnext(partexprs, partexpr_item);
2050 :
2051 : /* Deparse */
2052 158 : str = deparse_expression_pretty(partkey, context, false, false,
2053 : prettyFlags, 0);
2054 : /* Need parens if it's not a bare function call */
2055 158 : if (looks_like_function(partkey))
2056 56 : appendStringInfoString(&buf, str);
2057 : else
2058 102 : appendStringInfo(&buf, "(%s)", str);
2059 :
2060 158 : keycoltype = exprType(partkey);
2061 158 : keycolcollation = exprCollation(partkey);
2062 : }
2063 :
2064 : /* Add collation, if not default for column */
2065 1596 : partcoll = partcollation->values[keyno];
2066 1596 : if (!attrsOnly && OidIsValid(partcoll) && partcoll != keycolcollation)
2067 6 : appendStringInfo(&buf, " COLLATE %s",
2068 : generate_collation_name((partcoll)));
2069 :
2070 : /* Add the operator class name, if not default */
2071 1596 : if (!attrsOnly)
2072 1400 : get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2073 : }
2074 :
2075 1444 : if (!attrsOnly)
2076 1302 : appendStringInfoChar(&buf, ')');
2077 :
2078 : /* Clean up */
2079 1444 : ReleaseSysCache(tuple);
2080 :
2081 1444 : return buf.data;
2082 : }
2083 :
2084 : /*
2085 : * pg_get_partition_constraintdef
2086 : *
2087 : * Returns partition constraint expression as a string for the input relation
2088 : */
2089 : Datum
2090 170 : pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
2091 : {
2092 170 : Oid relationId = PG_GETARG_OID(0);
2093 : Expr *constr_expr;
2094 : int prettyFlags;
2095 : List *context;
2096 : char *consrc;
2097 :
2098 170 : constr_expr = get_partition_qual_relid(relationId);
2099 :
2100 : /* Quick exit if no partition constraint */
2101 170 : if (constr_expr == NULL)
2102 18 : PG_RETURN_NULL();
2103 :
2104 : /*
2105 : * Deparse and return the constraint expression.
2106 : */
2107 152 : prettyFlags = PRETTYFLAG_INDENT;
2108 152 : context = deparse_context_for(get_relation_name(relationId), relationId);
2109 152 : consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2110 : false, prettyFlags, 0);
2111 :
2112 152 : PG_RETURN_TEXT_P(string_to_text(consrc));
2113 : }
2114 :
2115 : /*
2116 : * pg_get_partconstrdef_string
2117 : *
2118 : * Returns the partition constraint as a C-string for the input relation, with
2119 : * the given alias. No pretty-printing.
2120 : */
2121 : char *
2122 86 : pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
2123 : {
2124 : Expr *constr_expr;
2125 : List *context;
2126 :
2127 86 : constr_expr = get_partition_qual_relid(partitionId);
2128 86 : context = deparse_context_for(aliasname, partitionId);
2129 :
2130 86 : return deparse_expression((Node *) constr_expr, context, true, false);
2131 : }
2132 :
2133 : /*
2134 : * pg_get_constraintdef
2135 : *
2136 : * Returns the definition for the constraint, ie, everything that needs to
2137 : * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
2138 : */
2139 : Datum
2140 1742 : pg_get_constraintdef(PG_FUNCTION_ARGS)
2141 : {
2142 1742 : Oid constraintId = PG_GETARG_OID(0);
2143 : int prettyFlags;
2144 : char *res;
2145 :
2146 1742 : prettyFlags = PRETTYFLAG_INDENT;
2147 :
2148 1742 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2149 :
2150 1742 : if (res == NULL)
2151 6 : PG_RETURN_NULL();
2152 :
2153 1736 : PG_RETURN_TEXT_P(string_to_text(res));
2154 : }
2155 :
2156 : Datum
2157 4318 : pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
2158 : {
2159 4318 : Oid constraintId = PG_GETARG_OID(0);
2160 4318 : bool pretty = PG_GETARG_BOOL(1);
2161 : int prettyFlags;
2162 : char *res;
2163 :
2164 4318 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2165 :
2166 4318 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2167 :
2168 4318 : if (res == NULL)
2169 0 : PG_RETURN_NULL();
2170 :
2171 4318 : PG_RETURN_TEXT_P(string_to_text(res));
2172 : }
2173 :
2174 : /*
2175 : * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
2176 : */
2177 : char *
2178 596 : pg_get_constraintdef_command(Oid constraintId)
2179 : {
2180 596 : return pg_get_constraintdef_worker(constraintId, true, 0, false);
2181 : }
2182 :
2183 : /*
2184 : * As of 9.4, we now use an MVCC snapshot for this.
2185 : */
2186 : static char *
2187 6656 : pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
2188 : int prettyFlags, bool missing_ok)
2189 : {
2190 : HeapTuple tup;
2191 : Form_pg_constraint conForm;
2192 : StringInfoData buf;
2193 : SysScanDesc scandesc;
2194 : ScanKeyData scankey[1];
2195 6656 : Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());
2196 6656 : Relation relation = table_open(ConstraintRelationId, AccessShareLock);
2197 :
2198 6656 : ScanKeyInit(&scankey[0],
2199 : Anum_pg_constraint_oid,
2200 : BTEqualStrategyNumber, F_OIDEQ,
2201 : ObjectIdGetDatum(constraintId));
2202 :
2203 6656 : scandesc = systable_beginscan(relation,
2204 : ConstraintOidIndexId,
2205 : true,
2206 : snapshot,
2207 : 1,
2208 : scankey);
2209 :
2210 : /*
2211 : * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2212 : * via SearchSysCache, which works fine.
2213 : */
2214 6656 : tup = systable_getnext(scandesc);
2215 :
2216 6656 : UnregisterSnapshot(snapshot);
2217 :
2218 6656 : if (!HeapTupleIsValid(tup))
2219 : {
2220 6 : if (missing_ok)
2221 : {
2222 6 : systable_endscan(scandesc);
2223 6 : table_close(relation, AccessShareLock);
2224 6 : return NULL;
2225 : }
2226 0 : elog(ERROR, "could not find tuple for constraint %u", constraintId);
2227 : }
2228 :
2229 6650 : conForm = (Form_pg_constraint) GETSTRUCT(tup);
2230 :
2231 6650 : initStringInfo(&buf);
2232 :
2233 6650 : if (fullCommand)
2234 : {
2235 596 : if (OidIsValid(conForm->conrelid))
2236 : {
2237 : /*
2238 : * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2239 : * constraints, and other types of constraints don't inherit
2240 : * anyway so it doesn't matter whether we say ONLY or not. Someday
2241 : * we might need to let callers specify whether to put ONLY in the
2242 : * command.
2243 : */
2244 582 : appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2245 : generate_qualified_relation_name(conForm->conrelid),
2246 582 : quote_identifier(NameStr(conForm->conname)));
2247 : }
2248 : else
2249 : {
2250 : /* Must be a domain constraint */
2251 : Assert(OidIsValid(conForm->contypid));
2252 14 : appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2253 : generate_qualified_type_name(conForm->contypid),
2254 14 : quote_identifier(NameStr(conForm->conname)));
2255 : }
2256 : }
2257 :
2258 6650 : switch (conForm->contype)
2259 : {
2260 726 : case CONSTRAINT_FOREIGN:
2261 : {
2262 : Datum val;
2263 : bool isnull;
2264 : const char *string;
2265 :
2266 : /* Start off the constraint definition */
2267 726 : appendStringInfoString(&buf, "FOREIGN KEY (");
2268 :
2269 : /* Fetch and build referencing-column list */
2270 726 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2271 : Anum_pg_constraint_conkey);
2272 :
2273 : /* If it is a temporal foreign key then it uses PERIOD. */
2274 726 : decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
2275 :
2276 : /* add foreign relation name */
2277 726 : appendStringInfo(&buf, ") REFERENCES %s(",
2278 : generate_relation_name(conForm->confrelid,
2279 : NIL));
2280 :
2281 : /* Fetch and build referenced-column list */
2282 726 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2283 : Anum_pg_constraint_confkey);
2284 :
2285 726 : decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
2286 :
2287 726 : appendStringInfoChar(&buf, ')');
2288 :
2289 : /* Add match type */
2290 726 : switch (conForm->confmatchtype)
2291 : {
2292 34 : case FKCONSTR_MATCH_FULL:
2293 34 : string = " MATCH FULL";
2294 34 : break;
2295 0 : case FKCONSTR_MATCH_PARTIAL:
2296 0 : string = " MATCH PARTIAL";
2297 0 : break;
2298 692 : case FKCONSTR_MATCH_SIMPLE:
2299 692 : string = "";
2300 692 : break;
2301 0 : default:
2302 0 : elog(ERROR, "unrecognized confmatchtype: %d",
2303 : conForm->confmatchtype);
2304 : string = ""; /* keep compiler quiet */
2305 : break;
2306 : }
2307 726 : appendStringInfoString(&buf, string);
2308 :
2309 : /* Add ON UPDATE and ON DELETE clauses, if needed */
2310 726 : switch (conForm->confupdtype)
2311 : {
2312 602 : case FKCONSTR_ACTION_NOACTION:
2313 602 : string = NULL; /* suppress default */
2314 602 : break;
2315 0 : case FKCONSTR_ACTION_RESTRICT:
2316 0 : string = "RESTRICT";
2317 0 : break;
2318 96 : case FKCONSTR_ACTION_CASCADE:
2319 96 : string = "CASCADE";
2320 96 : break;
2321 28 : case FKCONSTR_ACTION_SETNULL:
2322 28 : string = "SET NULL";
2323 28 : break;
2324 0 : case FKCONSTR_ACTION_SETDEFAULT:
2325 0 : string = "SET DEFAULT";
2326 0 : break;
2327 0 : default:
2328 0 : elog(ERROR, "unrecognized confupdtype: %d",
2329 : conForm->confupdtype);
2330 : string = NULL; /* keep compiler quiet */
2331 : break;
2332 : }
2333 726 : if (string)
2334 124 : appendStringInfo(&buf, " ON UPDATE %s", string);
2335 :
2336 726 : switch (conForm->confdeltype)
2337 : {
2338 586 : case FKCONSTR_ACTION_NOACTION:
2339 586 : string = NULL; /* suppress default */
2340 586 : break;
2341 20 : case FKCONSTR_ACTION_RESTRICT:
2342 20 : string = "RESTRICT";
2343 20 : break;
2344 96 : case FKCONSTR_ACTION_CASCADE:
2345 96 : string = "CASCADE";
2346 96 : break;
2347 18 : case FKCONSTR_ACTION_SETNULL:
2348 18 : string = "SET NULL";
2349 18 : break;
2350 6 : case FKCONSTR_ACTION_SETDEFAULT:
2351 6 : string = "SET DEFAULT";
2352 6 : break;
2353 0 : default:
2354 0 : elog(ERROR, "unrecognized confdeltype: %d",
2355 : conForm->confdeltype);
2356 : string = NULL; /* keep compiler quiet */
2357 : break;
2358 : }
2359 726 : if (string)
2360 140 : appendStringInfo(&buf, " ON DELETE %s", string);
2361 :
2362 : /*
2363 : * Add columns specified to SET NULL or SET DEFAULT if
2364 : * provided.
2365 : */
2366 726 : val = SysCacheGetAttr(CONSTROID, tup,
2367 : Anum_pg_constraint_confdelsetcols, &isnull);
2368 726 : if (!isnull)
2369 : {
2370 12 : appendStringInfoString(&buf, " (");
2371 12 : decompile_column_index_array(val, conForm->conrelid, false, &buf);
2372 12 : appendStringInfoChar(&buf, ')');
2373 : }
2374 :
2375 726 : break;
2376 : }
2377 3620 : case CONSTRAINT_PRIMARY:
2378 : case CONSTRAINT_UNIQUE:
2379 : {
2380 : Datum val;
2381 : Oid indexId;
2382 : int keyatts;
2383 : HeapTuple indtup;
2384 :
2385 : /* Start off the constraint definition */
2386 3620 : if (conForm->contype == CONSTRAINT_PRIMARY)
2387 2910 : appendStringInfoString(&buf, "PRIMARY KEY ");
2388 : else
2389 710 : appendStringInfoString(&buf, "UNIQUE ");
2390 :
2391 3620 : indexId = conForm->conindid;
2392 :
2393 3620 : indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
2394 3620 : if (!HeapTupleIsValid(indtup))
2395 0 : elog(ERROR, "cache lookup failed for index %u", indexId);
2396 3620 : if (conForm->contype == CONSTRAINT_UNIQUE &&
2397 710 : ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
2398 0 : appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2399 :
2400 3620 : appendStringInfoChar(&buf, '(');
2401 :
2402 : /* Fetch and build target column list */
2403 3620 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2404 : Anum_pg_constraint_conkey);
2405 :
2406 3620 : keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
2407 3620 : if (conForm->conperiod)
2408 362 : appendStringInfoString(&buf, " WITHOUT OVERLAPS");
2409 :
2410 3620 : appendStringInfoChar(&buf, ')');
2411 :
2412 : /* Build including column list (from pg_index.indkeys) */
2413 3620 : val = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2414 : Anum_pg_index_indnatts);
2415 3620 : if (DatumGetInt32(val) > keyatts)
2416 : {
2417 : Datum cols;
2418 : Datum *keys;
2419 : int nKeys;
2420 : int j;
2421 :
2422 82 : appendStringInfoString(&buf, " INCLUDE (");
2423 :
2424 82 : cols = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2425 : Anum_pg_index_indkey);
2426 :
2427 82 : deconstruct_array_builtin(DatumGetArrayTypeP(cols), INT2OID,
2428 : &keys, NULL, &nKeys);
2429 :
2430 246 : for (j = keyatts; j < nKeys; j++)
2431 : {
2432 : char *colName;
2433 :
2434 164 : colName = get_attname(conForm->conrelid,
2435 164 : DatumGetInt16(keys[j]), false);
2436 164 : if (j > keyatts)
2437 82 : appendStringInfoString(&buf, ", ");
2438 164 : appendStringInfoString(&buf, quote_identifier(colName));
2439 : }
2440 :
2441 82 : appendStringInfoChar(&buf, ')');
2442 : }
2443 3620 : ReleaseSysCache(indtup);
2444 :
2445 : /* XXX why do we only print these bits if fullCommand? */
2446 3620 : if (fullCommand && OidIsValid(indexId))
2447 : {
2448 204 : char *options = flatten_reloptions(indexId);
2449 : Oid tblspc;
2450 :
2451 204 : if (options)
2452 : {
2453 0 : appendStringInfo(&buf, " WITH (%s)", options);
2454 0 : pfree(options);
2455 : }
2456 :
2457 : /*
2458 : * Print the tablespace, unless it's the database default.
2459 : * This is to help ALTER TABLE usage of this facility,
2460 : * which needs this behavior to recreate exact catalog
2461 : * state.
2462 : */
2463 204 : tblspc = get_rel_tablespace(indexId);
2464 204 : if (OidIsValid(tblspc))
2465 24 : appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2466 24 : quote_identifier(get_tablespace_name(tblspc)));
2467 : }
2468 :
2469 3620 : break;
2470 : }
2471 2002 : case CONSTRAINT_CHECK:
2472 : {
2473 : Datum val;
2474 : char *conbin;
2475 : char *consrc;
2476 : Node *expr;
2477 : List *context;
2478 :
2479 : /* Fetch constraint expression in parsetree form */
2480 2002 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2481 : Anum_pg_constraint_conbin);
2482 :
2483 2002 : conbin = TextDatumGetCString(val);
2484 2002 : expr = stringToNode(conbin);
2485 :
2486 : /* Set up deparsing context for Var nodes in constraint */
2487 2002 : if (conForm->conrelid != InvalidOid)
2488 : {
2489 : /* relation constraint */
2490 1794 : context = deparse_context_for(get_relation_name(conForm->conrelid),
2491 : conForm->conrelid);
2492 : }
2493 : else
2494 : {
2495 : /* domain constraint --- can't have Vars */
2496 208 : context = NIL;
2497 : }
2498 :
2499 2002 : consrc = deparse_expression_pretty(expr, context, false, false,
2500 : prettyFlags, 0);
2501 :
2502 : /*
2503 : * Now emit the constraint definition, adding NO INHERIT if
2504 : * necessary.
2505 : *
2506 : * There are cases where the constraint expression will be
2507 : * fully parenthesized and we don't need the outer parens ...
2508 : * but there are other cases where we do need 'em. Be
2509 : * conservative for now.
2510 : *
2511 : * Note that simply checking for leading '(' and trailing ')'
2512 : * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2513 : */
2514 2002 : appendStringInfo(&buf, "CHECK (%s)%s",
2515 : consrc,
2516 2002 : conForm->connoinherit ? " NO INHERIT" : "");
2517 2002 : break;
2518 : }
2519 198 : case CONSTRAINT_NOTNULL:
2520 : {
2521 198 : if (conForm->conrelid)
2522 : {
2523 : AttrNumber attnum;
2524 :
2525 198 : attnum = extractNotNullColumn(tup);
2526 :
2527 198 : appendStringInfo(&buf, "NOT NULL %s",
2528 198 : quote_identifier(get_attname(conForm->conrelid,
2529 : attnum, false)));
2530 198 : if (((Form_pg_constraint) GETSTRUCT(tup))->connoinherit)
2531 0 : appendStringInfoString(&buf, " NO INHERIT");
2532 : }
2533 0 : else if (conForm->contypid)
2534 : {
2535 : /* conkey is null for domain not-null constraints */
2536 0 : appendStringInfoString(&buf, "NOT NULL");
2537 : }
2538 198 : break;
2539 : }
2540 :
2541 0 : case CONSTRAINT_TRIGGER:
2542 :
2543 : /*
2544 : * There isn't an ALTER TABLE syntax for creating a user-defined
2545 : * constraint trigger, but it seems better to print something than
2546 : * throw an error; if we throw error then this function couldn't
2547 : * safely be applied to all rows of pg_constraint.
2548 : */
2549 0 : appendStringInfoString(&buf, "TRIGGER");
2550 0 : break;
2551 104 : case CONSTRAINT_EXCLUSION:
2552 : {
2553 104 : Oid indexOid = conForm->conindid;
2554 : Datum val;
2555 : Datum *elems;
2556 : int nElems;
2557 : int i;
2558 : Oid *operators;
2559 :
2560 : /* Extract operator OIDs from the pg_constraint tuple */
2561 104 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2562 : Anum_pg_constraint_conexclop);
2563 :
2564 104 : deconstruct_array_builtin(DatumGetArrayTypeP(val), OIDOID,
2565 : &elems, NULL, &nElems);
2566 :
2567 104 : operators = (Oid *) palloc(nElems * sizeof(Oid));
2568 228 : for (i = 0; i < nElems; i++)
2569 124 : operators[i] = DatumGetObjectId(elems[i]);
2570 :
2571 : /* pg_get_indexdef_worker does the rest */
2572 : /* suppress tablespace because pg_dump wants it that way */
2573 104 : appendStringInfoString(&buf,
2574 104 : pg_get_indexdef_worker(indexOid,
2575 : 0,
2576 : operators,
2577 : false,
2578 : false,
2579 : false,
2580 : false,
2581 : prettyFlags,
2582 : false));
2583 104 : break;
2584 : }
2585 0 : default:
2586 0 : elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2587 : break;
2588 : }
2589 :
2590 6650 : if (conForm->condeferrable)
2591 120 : appendStringInfoString(&buf, " DEFERRABLE");
2592 6650 : if (conForm->condeferred)
2593 48 : appendStringInfoString(&buf, " INITIALLY DEFERRED");
2594 6650 : if (!conForm->convalidated)
2595 94 : appendStringInfoString(&buf, " NOT VALID");
2596 :
2597 : /* Cleanup */
2598 6650 : systable_endscan(scandesc);
2599 6650 : table_close(relation, AccessShareLock);
2600 :
2601 6650 : return buf.data;
2602 : }
2603 :
2604 :
2605 : /*
2606 : * Convert an int16[] Datum into a comma-separated list of column names
2607 : * for the indicated relation; append the list to buf. Returns the number
2608 : * of keys.
2609 : */
2610 : static int
2611 5084 : decompile_column_index_array(Datum column_index_array, Oid relId,
2612 : bool withPeriod, StringInfo buf)
2613 : {
2614 : Datum *keys;
2615 : int nKeys;
2616 : int j;
2617 :
2618 : /* Extract data from array of int16 */
2619 5084 : deconstruct_array_builtin(DatumGetArrayTypeP(column_index_array), INT2OID,
2620 : &keys, NULL, &nKeys);
2621 :
2622 12398 : for (j = 0; j < nKeys; j++)
2623 : {
2624 : char *colName;
2625 :
2626 7314 : colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2627 :
2628 7314 : if (j == 0)
2629 5084 : appendStringInfoString(buf, quote_identifier(colName));
2630 : else
2631 2478 : appendStringInfo(buf, ", %s%s",
2632 248 : (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
2633 : quote_identifier(colName));
2634 : }
2635 :
2636 5084 : return nKeys;
2637 : }
2638 :
2639 :
2640 : /* ----------
2641 : * pg_get_expr - Decompile an expression tree
2642 : *
2643 : * Input: an expression tree in nodeToString form, and a relation OID
2644 : *
2645 : * Output: reverse-listed expression
2646 : *
2647 : * Currently, the expression can only refer to a single relation, namely
2648 : * the one specified by the second parameter. This is sufficient for
2649 : * partial indexes, column default expressions, etc. We also support
2650 : * Var-free expressions, for which the OID can be InvalidOid.
2651 : *
2652 : * If the OID is nonzero but not actually valid, don't throw an error,
2653 : * just return NULL. This is a bit questionable, but it's what we've
2654 : * done historically, and it can help avoid unwanted failures when
2655 : * examining catalog entries for just-deleted relations.
2656 : *
2657 : * We expect this function to work, or throw a reasonably clean error,
2658 : * for any node tree that can appear in a catalog pg_node_tree column.
2659 : * Query trees, such as those appearing in pg_rewrite.ev_action, are
2660 : * not supported. Nor are expressions in more than one relation, which
2661 : * can appear in places like pg_rewrite.ev_qual.
2662 : * ----------
2663 : */
2664 : Datum
2665 7178 : pg_get_expr(PG_FUNCTION_ARGS)
2666 : {
2667 7178 : text *expr = PG_GETARG_TEXT_PP(0);
2668 7178 : Oid relid = PG_GETARG_OID(1);
2669 : text *result;
2670 : int prettyFlags;
2671 :
2672 7178 : prettyFlags = PRETTYFLAG_INDENT;
2673 :
2674 7178 : result = pg_get_expr_worker(expr, relid, prettyFlags);
2675 7178 : if (result)
2676 7178 : PG_RETURN_TEXT_P(result);
2677 : else
2678 0 : PG_RETURN_NULL();
2679 : }
2680 :
2681 : Datum
2682 484 : pg_get_expr_ext(PG_FUNCTION_ARGS)
2683 : {
2684 484 : text *expr = PG_GETARG_TEXT_PP(0);
2685 484 : Oid relid = PG_GETARG_OID(1);
2686 484 : bool pretty = PG_GETARG_BOOL(2);
2687 : text *result;
2688 : int prettyFlags;
2689 :
2690 484 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2691 :
2692 484 : result = pg_get_expr_worker(expr, relid, prettyFlags);
2693 484 : if (result)
2694 484 : PG_RETURN_TEXT_P(result);
2695 : else
2696 0 : PG_RETURN_NULL();
2697 : }
2698 :
2699 : static text *
2700 7662 : pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
2701 : {
2702 : Node *node;
2703 : Node *tst;
2704 : Relids relids;
2705 : List *context;
2706 : char *exprstr;
2707 7662 : Relation rel = NULL;
2708 : char *str;
2709 :
2710 : /* Convert input pg_node_tree (really TEXT) object to C string */
2711 7662 : exprstr = text_to_cstring(expr);
2712 :
2713 : /* Convert expression to node tree */
2714 7662 : node = (Node *) stringToNode(exprstr);
2715 :
2716 7662 : pfree(exprstr);
2717 :
2718 : /*
2719 : * Throw error if the input is a querytree rather than an expression tree.
2720 : * While we could support queries here, there seems no very good reason
2721 : * to. In most such catalog columns, we'll see a List of Query nodes, or
2722 : * even nested Lists, so drill down to a non-List node before checking.
2723 : */
2724 7662 : tst = node;
2725 7662 : while (tst && IsA(tst, List))
2726 0 : tst = linitial((List *) tst);
2727 7662 : if (tst && IsA(tst, Query))
2728 0 : ereport(ERROR,
2729 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2730 : errmsg("input is a query, not an expression")));
2731 :
2732 : /*
2733 : * Throw error if the expression contains Vars we won't be able to
2734 : * deparse.
2735 : */
2736 7662 : relids = pull_varnos(NULL, node);
2737 7662 : if (OidIsValid(relid))
2738 : {
2739 7602 : if (!bms_is_subset(relids, bms_make_singleton(1)))
2740 0 : ereport(ERROR,
2741 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2742 : errmsg("expression contains variables of more than one relation")));
2743 : }
2744 : else
2745 : {
2746 60 : if (!bms_is_empty(relids))
2747 0 : ereport(ERROR,
2748 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2749 : errmsg("expression contains variables")));
2750 : }
2751 :
2752 : /*
2753 : * Prepare deparse context if needed. If we are deparsing with a relid,
2754 : * we need to transiently open and lock the rel, to make sure it won't go
2755 : * away underneath us. (set_relation_column_names would lock it anyway,
2756 : * so this isn't really introducing any new behavior.)
2757 : */
2758 7662 : if (OidIsValid(relid))
2759 : {
2760 7602 : rel = try_relation_open(relid, AccessShareLock);
2761 7602 : if (rel == NULL)
2762 0 : return NULL;
2763 7602 : context = deparse_context_for(RelationGetRelationName(rel), relid);
2764 : }
2765 : else
2766 60 : context = NIL;
2767 :
2768 : /* Deparse */
2769 7662 : str = deparse_expression_pretty(node, context, false, false,
2770 : prettyFlags, 0);
2771 :
2772 7662 : if (rel != NULL)
2773 7602 : relation_close(rel, AccessShareLock);
2774 :
2775 7662 : return string_to_text(str);
2776 : }
2777 :
2778 :
2779 : /* ----------
2780 : * pg_get_userbyid - Get a user name by roleid and
2781 : * fallback to 'unknown (OID=n)'
2782 : * ----------
2783 : */
2784 : Datum
2785 1658 : pg_get_userbyid(PG_FUNCTION_ARGS)
2786 : {
2787 1658 : Oid roleid = PG_GETARG_OID(0);
2788 : Name result;
2789 : HeapTuple roletup;
2790 : Form_pg_authid role_rec;
2791 :
2792 : /*
2793 : * Allocate space for the result
2794 : */
2795 1658 : result = (Name) palloc(NAMEDATALEN);
2796 1658 : memset(NameStr(*result), 0, NAMEDATALEN);
2797 :
2798 : /*
2799 : * Get the pg_authid entry and print the result
2800 : */
2801 1658 : roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
2802 1658 : if (HeapTupleIsValid(roletup))
2803 : {
2804 1658 : role_rec = (Form_pg_authid) GETSTRUCT(roletup);
2805 1658 : *result = role_rec->rolname;
2806 1658 : ReleaseSysCache(roletup);
2807 : }
2808 : else
2809 0 : sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
2810 :
2811 1658 : PG_RETURN_NAME(result);
2812 : }
2813 :
2814 :
2815 : /*
2816 : * pg_get_serial_sequence
2817 : * Get the name of the sequence used by an identity or serial column,
2818 : * formatted suitably for passing to setval, nextval or currval.
2819 : * First parameter is not treated as double-quoted, second parameter
2820 : * is --- see documentation for reason.
2821 : */
2822 : Datum
2823 12 : pg_get_serial_sequence(PG_FUNCTION_ARGS)
2824 : {
2825 12 : text *tablename = PG_GETARG_TEXT_PP(0);
2826 12 : text *columnname = PG_GETARG_TEXT_PP(1);
2827 : RangeVar *tablerv;
2828 : Oid tableOid;
2829 : char *column;
2830 : AttrNumber attnum;
2831 12 : Oid sequenceId = InvalidOid;
2832 : Relation depRel;
2833 : ScanKeyData key[3];
2834 : SysScanDesc scan;
2835 : HeapTuple tup;
2836 :
2837 : /* Look up table name. Can't lock it - we might not have privileges. */
2838 12 : tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
2839 12 : tableOid = RangeVarGetRelid(tablerv, NoLock, false);
2840 :
2841 : /* Get the number of the column */
2842 12 : column = text_to_cstring(columnname);
2843 :
2844 12 : attnum = get_attnum(tableOid, column);
2845 12 : if (attnum == InvalidAttrNumber)
2846 0 : ereport(ERROR,
2847 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2848 : errmsg("column \"%s\" of relation \"%s\" does not exist",
2849 : column, tablerv->relname)));
2850 :
2851 : /* Search the dependency table for the dependent sequence */
2852 12 : depRel = table_open(DependRelationId, AccessShareLock);
2853 :
2854 12 : ScanKeyInit(&key[0],
2855 : Anum_pg_depend_refclassid,
2856 : BTEqualStrategyNumber, F_OIDEQ,
2857 : ObjectIdGetDatum(RelationRelationId));
2858 12 : ScanKeyInit(&key[1],
2859 : Anum_pg_depend_refobjid,
2860 : BTEqualStrategyNumber, F_OIDEQ,
2861 : ObjectIdGetDatum(tableOid));
2862 12 : ScanKeyInit(&key[2],
2863 : Anum_pg_depend_refobjsubid,
2864 : BTEqualStrategyNumber, F_INT4EQ,
2865 : Int32GetDatum(attnum));
2866 :
2867 12 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
2868 : NULL, 3, key);
2869 :
2870 30 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
2871 : {
2872 30 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
2873 :
2874 : /*
2875 : * Look for an auto dependency (serial column) or internal dependency
2876 : * (identity column) of a sequence on a column. (We need the relkind
2877 : * test because indexes can also have auto dependencies on columns.)
2878 : */
2879 30 : if (deprec->classid == RelationRelationId &&
2880 12 : deprec->objsubid == 0 &&
2881 12 : (deprec->deptype == DEPENDENCY_AUTO ||
2882 18 : deprec->deptype == DEPENDENCY_INTERNAL) &&
2883 12 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
2884 : {
2885 12 : sequenceId = deprec->objid;
2886 12 : break;
2887 : }
2888 : }
2889 :
2890 12 : systable_endscan(scan);
2891 12 : table_close(depRel, AccessShareLock);
2892 :
2893 12 : if (OidIsValid(sequenceId))
2894 : {
2895 : char *result;
2896 :
2897 12 : result = generate_qualified_relation_name(sequenceId);
2898 :
2899 12 : PG_RETURN_TEXT_P(string_to_text(result));
2900 : }
2901 :
2902 0 : PG_RETURN_NULL();
2903 : }
2904 :
2905 :
2906 : /*
2907 : * pg_get_functiondef
2908 : * Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
2909 : * the specified function.
2910 : *
2911 : * Note: if you change the output format of this function, be careful not
2912 : * to break psql's rules (in \ef and \sf) for identifying the start of the
2913 : * function body. To wit: the function body starts on a line that begins with
2914 : * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
2915 : */
2916 : Datum
2917 166 : pg_get_functiondef(PG_FUNCTION_ARGS)
2918 : {
2919 166 : Oid funcid = PG_GETARG_OID(0);
2920 : StringInfoData buf;
2921 : StringInfoData dq;
2922 : HeapTuple proctup;
2923 : Form_pg_proc proc;
2924 : bool isfunction;
2925 : Datum tmp;
2926 : bool isnull;
2927 : const char *prosrc;
2928 : const char *name;
2929 : const char *nsp;
2930 : float4 procost;
2931 : int oldlen;
2932 :
2933 166 : initStringInfo(&buf);
2934 :
2935 : /* Look up the function */
2936 166 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
2937 166 : if (!HeapTupleIsValid(proctup))
2938 6 : PG_RETURN_NULL();
2939 :
2940 160 : proc = (Form_pg_proc) GETSTRUCT(proctup);
2941 160 : name = NameStr(proc->proname);
2942 :
2943 160 : if (proc->prokind == PROKIND_AGGREGATE)
2944 0 : ereport(ERROR,
2945 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2946 : errmsg("\"%s\" is an aggregate function", name)));
2947 :
2948 160 : isfunction = (proc->prokind != PROKIND_PROCEDURE);
2949 :
2950 : /*
2951 : * We always qualify the function name, to ensure the right function gets
2952 : * replaced.
2953 : */
2954 160 : nsp = get_namespace_name_or_temp(proc->pronamespace);
2955 160 : appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
2956 : isfunction ? "FUNCTION" : "PROCEDURE",
2957 : quote_qualified_identifier(nsp, name));
2958 160 : (void) print_function_arguments(&buf, proctup, false, true);
2959 160 : appendStringInfoString(&buf, ")\n");
2960 160 : if (isfunction)
2961 : {
2962 140 : appendStringInfoString(&buf, " RETURNS ");
2963 140 : print_function_rettype(&buf, proctup);
2964 140 : appendStringInfoChar(&buf, '\n');
2965 : }
2966 :
2967 160 : print_function_trftypes(&buf, proctup);
2968 :
2969 160 : appendStringInfo(&buf, " LANGUAGE %s\n",
2970 160 : quote_identifier(get_language_name(proc->prolang, false)));
2971 :
2972 : /* Emit some miscellaneous options on one line */
2973 160 : oldlen = buf.len;
2974 :
2975 160 : if (proc->prokind == PROKIND_WINDOW)
2976 0 : appendStringInfoString(&buf, " WINDOW");
2977 160 : switch (proc->provolatile)
2978 : {
2979 12 : case PROVOLATILE_IMMUTABLE:
2980 12 : appendStringInfoString(&buf, " IMMUTABLE");
2981 12 : break;
2982 30 : case PROVOLATILE_STABLE:
2983 30 : appendStringInfoString(&buf, " STABLE");
2984 30 : break;
2985 118 : case PROVOLATILE_VOLATILE:
2986 118 : break;
2987 : }
2988 :
2989 160 : switch (proc->proparallel)
2990 : {
2991 28 : case PROPARALLEL_SAFE:
2992 28 : appendStringInfoString(&buf, " PARALLEL SAFE");
2993 28 : break;
2994 0 : case PROPARALLEL_RESTRICTED:
2995 0 : appendStringInfoString(&buf, " PARALLEL RESTRICTED");
2996 0 : break;
2997 132 : case PROPARALLEL_UNSAFE:
2998 132 : break;
2999 : }
3000 :
3001 160 : if (proc->proisstrict)
3002 50 : appendStringInfoString(&buf, " STRICT");
3003 160 : if (proc->prosecdef)
3004 6 : appendStringInfoString(&buf, " SECURITY DEFINER");
3005 160 : if (proc->proleakproof)
3006 0 : appendStringInfoString(&buf, " LEAKPROOF");
3007 :
3008 : /* This code for the default cost and rows should match functioncmds.c */
3009 160 : if (proc->prolang == INTERNALlanguageId ||
3010 160 : proc->prolang == ClanguageId)
3011 10 : procost = 1;
3012 : else
3013 150 : procost = 100;
3014 160 : if (proc->procost != procost)
3015 6 : appendStringInfo(&buf, " COST %g", proc->procost);
3016 :
3017 160 : if (proc->prorows > 0 && proc->prorows != 1000)
3018 0 : appendStringInfo(&buf, " ROWS %g", proc->prorows);
3019 :
3020 160 : if (proc->prosupport)
3021 : {
3022 : Oid argtypes[1];
3023 :
3024 : /*
3025 : * We should qualify the support function's name if it wouldn't be
3026 : * resolved by lookup in the current search path.
3027 : */
3028 0 : argtypes[0] = INTERNALOID;
3029 0 : appendStringInfo(&buf, " SUPPORT %s",
3030 : generate_function_name(proc->prosupport, 1,
3031 : NIL, argtypes,
3032 : false, NULL, false));
3033 : }
3034 :
3035 160 : if (oldlen != buf.len)
3036 64 : appendStringInfoChar(&buf, '\n');
3037 :
3038 : /* Emit any proconfig options, one per line */
3039 160 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
3040 160 : if (!isnull)
3041 : {
3042 6 : ArrayType *a = DatumGetArrayTypeP(tmp);
3043 : int i;
3044 :
3045 : Assert(ARR_ELEMTYPE(a) == TEXTOID);
3046 : Assert(ARR_NDIM(a) == 1);
3047 : Assert(ARR_LBOUND(a)[0] == 1);
3048 :
3049 36 : for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3050 : {
3051 : Datum d;
3052 :
3053 30 : d = array_ref(a, 1, &i,
3054 : -1 /* varlenarray */ ,
3055 : -1 /* TEXT's typlen */ ,
3056 : false /* TEXT's typbyval */ ,
3057 : TYPALIGN_INT /* TEXT's typalign */ ,
3058 : &isnull);
3059 30 : if (!isnull)
3060 : {
3061 30 : char *configitem = TextDatumGetCString(d);
3062 : char *pos;
3063 :
3064 30 : pos = strchr(configitem, '=');
3065 30 : if (pos == NULL)
3066 0 : continue;
3067 30 : *pos++ = '\0';
3068 :
3069 30 : appendStringInfo(&buf, " SET %s TO ",
3070 : quote_identifier(configitem));
3071 :
3072 : /*
3073 : * Variables that are marked GUC_LIST_QUOTE were already fully
3074 : * quoted by flatten_set_variable_args() before they were put
3075 : * into the proconfig array. However, because the quoting
3076 : * rules used there aren't exactly like SQL's, we have to
3077 : * break the list value apart and then quote the elements as
3078 : * string literals. (The elements may be double-quoted as-is,
3079 : * but we can't just feed them to the SQL parser; it would do
3080 : * the wrong thing with elements that are zero-length or
3081 : * longer than NAMEDATALEN.)
3082 : *
3083 : * Variables that are not so marked should just be emitted as
3084 : * simple string literals. If the variable is not known to
3085 : * guc.c, we'll do that; this makes it unsafe to use
3086 : * GUC_LIST_QUOTE for extension variables.
3087 : */
3088 30 : if (GetConfigOptionFlags(configitem, true) & GUC_LIST_QUOTE)
3089 : {
3090 : List *namelist;
3091 : ListCell *lc;
3092 :
3093 : /* Parse string into list of identifiers */
3094 12 : if (!SplitGUCList(pos, ',', &namelist))
3095 : {
3096 : /* this shouldn't fail really */
3097 0 : elog(ERROR, "invalid list syntax in proconfig item");
3098 : }
3099 42 : foreach(lc, namelist)
3100 : {
3101 30 : char *curname = (char *) lfirst(lc);
3102 :
3103 30 : simple_quote_literal(&buf, curname);
3104 30 : if (lnext(namelist, lc))
3105 18 : appendStringInfoString(&buf, ", ");
3106 : }
3107 : }
3108 : else
3109 18 : simple_quote_literal(&buf, pos);
3110 30 : appendStringInfoChar(&buf, '\n');
3111 : }
3112 : }
3113 : }
3114 :
3115 : /* And finally the function definition ... */
3116 160 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3117 160 : if (proc->prolang == SQLlanguageId && !isnull)
3118 : {
3119 108 : print_function_sqlbody(&buf, proctup);
3120 : }
3121 : else
3122 : {
3123 52 : appendStringInfoString(&buf, "AS ");
3124 :
3125 52 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
3126 52 : if (!isnull)
3127 : {
3128 10 : simple_quote_literal(&buf, TextDatumGetCString(tmp));
3129 10 : appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3130 : }
3131 :
3132 52 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosrc);
3133 52 : prosrc = TextDatumGetCString(tmp);
3134 :
3135 : /*
3136 : * We always use dollar quoting. Figure out a suitable delimiter.
3137 : *
3138 : * Since the user is likely to be editing the function body string, we
3139 : * shouldn't use a short delimiter that he might easily create a
3140 : * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3141 : * if needed.
3142 : */
3143 52 : initStringInfo(&dq);
3144 52 : appendStringInfoChar(&dq, '$');
3145 52 : appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3146 52 : while (strstr(prosrc, dq.data) != NULL)
3147 0 : appendStringInfoChar(&dq, 'x');
3148 52 : appendStringInfoChar(&dq, '$');
3149 :
3150 52 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3151 52 : appendStringInfoString(&buf, prosrc);
3152 52 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3153 : }
3154 :
3155 160 : appendStringInfoChar(&buf, '\n');
3156 :
3157 160 : ReleaseSysCache(proctup);
3158 :
3159 160 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3160 : }
3161 :
3162 : /*
3163 : * pg_get_function_arguments
3164 : * Get a nicely-formatted list of arguments for a function.
3165 : * This is everything that would go between the parentheses in
3166 : * CREATE FUNCTION.
3167 : */
3168 : Datum
3169 4590 : pg_get_function_arguments(PG_FUNCTION_ARGS)
3170 : {
3171 4590 : Oid funcid = PG_GETARG_OID(0);
3172 : StringInfoData buf;
3173 : HeapTuple proctup;
3174 :
3175 4590 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3176 4590 : if (!HeapTupleIsValid(proctup))
3177 6 : PG_RETURN_NULL();
3178 :
3179 4584 : initStringInfo(&buf);
3180 :
3181 4584 : (void) print_function_arguments(&buf, proctup, false, true);
3182 :
3183 4584 : ReleaseSysCache(proctup);
3184 :
3185 4584 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3186 : }
3187 :
3188 : /*
3189 : * pg_get_function_identity_arguments
3190 : * Get a formatted list of arguments for a function.
3191 : * This is everything that would go between the parentheses in
3192 : * ALTER FUNCTION, etc. In particular, don't print defaults.
3193 : */
3194 : Datum
3195 4070 : pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
3196 : {
3197 4070 : Oid funcid = PG_GETARG_OID(0);
3198 : StringInfoData buf;
3199 : HeapTuple proctup;
3200 :
3201 4070 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3202 4070 : if (!HeapTupleIsValid(proctup))
3203 6 : PG_RETURN_NULL();
3204 :
3205 4064 : initStringInfo(&buf);
3206 :
3207 4064 : (void) print_function_arguments(&buf, proctup, false, false);
3208 :
3209 4064 : ReleaseSysCache(proctup);
3210 :
3211 4064 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3212 : }
3213 :
3214 : /*
3215 : * pg_get_function_result
3216 : * Get a nicely-formatted version of the result type of a function.
3217 : * This is what would appear after RETURNS in CREATE FUNCTION.
3218 : */
3219 : Datum
3220 4004 : pg_get_function_result(PG_FUNCTION_ARGS)
3221 : {
3222 4004 : Oid funcid = PG_GETARG_OID(0);
3223 : StringInfoData buf;
3224 : HeapTuple proctup;
3225 :
3226 4004 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3227 4004 : if (!HeapTupleIsValid(proctup))
3228 6 : PG_RETURN_NULL();
3229 :
3230 3998 : if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
3231 : {
3232 240 : ReleaseSysCache(proctup);
3233 240 : PG_RETURN_NULL();
3234 : }
3235 :
3236 3758 : initStringInfo(&buf);
3237 :
3238 3758 : print_function_rettype(&buf, proctup);
3239 :
3240 3758 : ReleaseSysCache(proctup);
3241 :
3242 3758 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3243 : }
3244 :
3245 : /*
3246 : * Guts of pg_get_function_result: append the function's return type
3247 : * to the specified buffer.
3248 : */
3249 : static void
3250 3898 : print_function_rettype(StringInfo buf, HeapTuple proctup)
3251 : {
3252 3898 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3253 3898 : int ntabargs = 0;
3254 : StringInfoData rbuf;
3255 :
3256 3898 : initStringInfo(&rbuf);
3257 :
3258 3898 : if (proc->proretset)
3259 : {
3260 : /* It might be a table function; try to print the arguments */
3261 406 : appendStringInfoString(&rbuf, "TABLE(");
3262 406 : ntabargs = print_function_arguments(&rbuf, proctup, true, false);
3263 406 : if (ntabargs > 0)
3264 76 : appendStringInfoChar(&rbuf, ')');
3265 : else
3266 330 : resetStringInfo(&rbuf);
3267 : }
3268 :
3269 3898 : if (ntabargs == 0)
3270 : {
3271 : /* Not a table function, so do the normal thing */
3272 3822 : if (proc->proretset)
3273 330 : appendStringInfoString(&rbuf, "SETOF ");
3274 3822 : appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3275 : }
3276 :
3277 3898 : appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
3278 3898 : }
3279 :
3280 : /*
3281 : * Common code for pg_get_function_arguments and pg_get_function_result:
3282 : * append the desired subset of arguments to buf. We print only TABLE
3283 : * arguments when print_table_args is true, and all the others when it's false.
3284 : * We print argument defaults only if print_defaults is true.
3285 : * Function return value is the number of arguments printed.
3286 : */
3287 : static int
3288 9214 : print_function_arguments(StringInfo buf, HeapTuple proctup,
3289 : bool print_table_args, bool print_defaults)
3290 : {
3291 9214 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3292 : int numargs;
3293 : Oid *argtypes;
3294 : char **argnames;
3295 : char *argmodes;
3296 9214 : int insertorderbyat = -1;
3297 : int argsprinted;
3298 : int inputargno;
3299 : int nlackdefaults;
3300 9214 : List *argdefaults = NIL;
3301 9214 : ListCell *nextargdefault = NULL;
3302 : int i;
3303 :
3304 9214 : numargs = get_func_arg_info(proctup,
3305 : &argtypes, &argnames, &argmodes);
3306 :
3307 9214 : nlackdefaults = numargs;
3308 9214 : if (print_defaults && proc->pronargdefaults > 0)
3309 : {
3310 : Datum proargdefaults;
3311 : bool isnull;
3312 :
3313 38 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3314 : Anum_pg_proc_proargdefaults,
3315 : &isnull);
3316 38 : if (!isnull)
3317 : {
3318 : char *str;
3319 :
3320 38 : str = TextDatumGetCString(proargdefaults);
3321 38 : argdefaults = castNode(List, stringToNode(str));
3322 38 : pfree(str);
3323 38 : nextargdefault = list_head(argdefaults);
3324 : /* nlackdefaults counts only *input* arguments lacking defaults */
3325 38 : nlackdefaults = proc->pronargs - list_length(argdefaults);
3326 : }
3327 : }
3328 :
3329 : /* Check for special treatment of ordered-set aggregates */
3330 9214 : if (proc->prokind == PROKIND_AGGREGATE)
3331 : {
3332 : HeapTuple aggtup;
3333 : Form_pg_aggregate agg;
3334 :
3335 1174 : aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(proc->oid));
3336 1174 : if (!HeapTupleIsValid(aggtup))
3337 0 : elog(ERROR, "cache lookup failed for aggregate %u",
3338 : proc->oid);
3339 1174 : agg = (Form_pg_aggregate) GETSTRUCT(aggtup);
3340 1174 : if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3341 52 : insertorderbyat = agg->aggnumdirectargs;
3342 1174 : ReleaseSysCache(aggtup);
3343 : }
3344 :
3345 9214 : argsprinted = 0;
3346 9214 : inputargno = 0;
3347 18560 : for (i = 0; i < numargs; i++)
3348 : {
3349 9346 : Oid argtype = argtypes[i];
3350 9346 : char *argname = argnames ? argnames[i] : NULL;
3351 9346 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
3352 : const char *modename;
3353 : bool isinput;
3354 :
3355 9346 : switch (argmode)
3356 : {
3357 7740 : case PROARGMODE_IN:
3358 :
3359 : /*
3360 : * For procedures, explicitly mark all argument modes, so as
3361 : * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3362 : */
3363 7740 : if (proc->prokind == PROKIND_PROCEDURE)
3364 536 : modename = "IN ";
3365 : else
3366 7204 : modename = "";
3367 7740 : isinput = true;
3368 7740 : break;
3369 100 : case PROARGMODE_INOUT:
3370 100 : modename = "INOUT ";
3371 100 : isinput = true;
3372 100 : break;
3373 956 : case PROARGMODE_OUT:
3374 956 : modename = "OUT ";
3375 956 : isinput = false;
3376 956 : break;
3377 178 : case PROARGMODE_VARIADIC:
3378 178 : modename = "VARIADIC ";
3379 178 : isinput = true;
3380 178 : break;
3381 372 : case PROARGMODE_TABLE:
3382 372 : modename = "";
3383 372 : isinput = false;
3384 372 : break;
3385 0 : default:
3386 0 : elog(ERROR, "invalid parameter mode '%c'", argmode);
3387 : modename = NULL; /* keep compiler quiet */
3388 : isinput = false;
3389 : break;
3390 : }
3391 9346 : if (isinput)
3392 8018 : inputargno++; /* this is a 1-based counter */
3393 :
3394 9346 : if (print_table_args != (argmode == PROARGMODE_TABLE))
3395 728 : continue;
3396 :
3397 8618 : if (argsprinted == insertorderbyat)
3398 : {
3399 52 : if (argsprinted)
3400 52 : appendStringInfoChar(buf, ' ');
3401 52 : appendStringInfoString(buf, "ORDER BY ");
3402 : }
3403 8566 : else if (argsprinted)
3404 2740 : appendStringInfoString(buf, ", ");
3405 :
3406 8618 : appendStringInfoString(buf, modename);
3407 8618 : if (argname && argname[0])
3408 3074 : appendStringInfo(buf, "%s ", quote_identifier(argname));
3409 8618 : appendStringInfoString(buf, format_type_be(argtype));
3410 8618 : if (print_defaults && isinput && inputargno > nlackdefaults)
3411 : {
3412 : Node *expr;
3413 :
3414 : Assert(nextargdefault != NULL);
3415 58 : expr = (Node *) lfirst(nextargdefault);
3416 58 : nextargdefault = lnext(argdefaults, nextargdefault);
3417 :
3418 58 : appendStringInfo(buf, " DEFAULT %s",
3419 : deparse_expression(expr, NIL, false, false));
3420 : }
3421 8618 : argsprinted++;
3422 :
3423 : /* nasty hack: print the last arg twice for variadic ordered-set agg */
3424 8618 : if (argsprinted == insertorderbyat && i == numargs - 1)
3425 : {
3426 26 : i--;
3427 : /* aggs shouldn't have defaults anyway, but just to be sure ... */
3428 26 : print_defaults = false;
3429 : }
3430 : }
3431 :
3432 9214 : return argsprinted;
3433 : }
3434 :
3435 : static bool
3436 96 : is_input_argument(int nth, const char *argmodes)
3437 : {
3438 : return (!argmodes
3439 42 : || argmodes[nth] == PROARGMODE_IN
3440 18 : || argmodes[nth] == PROARGMODE_INOUT
3441 138 : || argmodes[nth] == PROARGMODE_VARIADIC);
3442 : }
3443 :
3444 : /*
3445 : * Append used transformed types to specified buffer
3446 : */
3447 : static void
3448 160 : print_function_trftypes(StringInfo buf, HeapTuple proctup)
3449 : {
3450 : Oid *trftypes;
3451 : int ntypes;
3452 :
3453 160 : ntypes = get_func_trftypes(proctup, &trftypes);
3454 160 : if (ntypes > 0)
3455 : {
3456 : int i;
3457 :
3458 6 : appendStringInfoString(buf, " TRANSFORM ");
3459 16 : for (i = 0; i < ntypes; i++)
3460 : {
3461 10 : if (i != 0)
3462 4 : appendStringInfoString(buf, ", ");
3463 10 : appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3464 : }
3465 6 : appendStringInfoChar(buf, '\n');
3466 : }
3467 160 : }
3468 :
3469 : /*
3470 : * Get textual representation of a function argument's default value. The
3471 : * second argument of this function is the argument number among all arguments
3472 : * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
3473 : * how information_schema.sql uses it.
3474 : */
3475 : Datum
3476 54 : pg_get_function_arg_default(PG_FUNCTION_ARGS)
3477 : {
3478 54 : Oid funcid = PG_GETARG_OID(0);
3479 54 : int32 nth_arg = PG_GETARG_INT32(1);
3480 : HeapTuple proctup;
3481 : Form_pg_proc proc;
3482 : int numargs;
3483 : Oid *argtypes;
3484 : char **argnames;
3485 : char *argmodes;
3486 : int i;
3487 : List *argdefaults;
3488 : Node *node;
3489 : char *str;
3490 : int nth_inputarg;
3491 : Datum proargdefaults;
3492 : bool isnull;
3493 : int nth_default;
3494 :
3495 54 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3496 54 : if (!HeapTupleIsValid(proctup))
3497 12 : PG_RETURN_NULL();
3498 :
3499 42 : numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3500 42 : if (nth_arg < 1 || nth_arg > numargs || !is_input_argument(nth_arg - 1, argmodes))
3501 : {
3502 12 : ReleaseSysCache(proctup);
3503 12 : PG_RETURN_NULL();
3504 : }
3505 :
3506 30 : nth_inputarg = 0;
3507 84 : for (i = 0; i < nth_arg; i++)
3508 54 : if (is_input_argument(i, argmodes))
3509 48 : nth_inputarg++;
3510 :
3511 30 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3512 : Anum_pg_proc_proargdefaults,
3513 : &isnull);
3514 30 : if (isnull)
3515 : {
3516 0 : ReleaseSysCache(proctup);
3517 0 : PG_RETURN_NULL();
3518 : }
3519 :
3520 30 : str = TextDatumGetCString(proargdefaults);
3521 30 : argdefaults = castNode(List, stringToNode(str));
3522 30 : pfree(str);
3523 :
3524 30 : proc = (Form_pg_proc) GETSTRUCT(proctup);
3525 :
3526 : /*
3527 : * Calculate index into proargdefaults: proargdefaults corresponds to the
3528 : * last N input arguments, where N = pronargdefaults.
3529 : */
3530 30 : nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3531 :
3532 30 : if (nth_default < 0 || nth_default >= list_length(argdefaults))
3533 : {
3534 6 : ReleaseSysCache(proctup);
3535 6 : PG_RETURN_NULL();
3536 : }
3537 24 : node = list_nth(argdefaults, nth_default);
3538 24 : str = deparse_expression(node, NIL, false, false);
3539 :
3540 24 : ReleaseSysCache(proctup);
3541 :
3542 24 : PG_RETURN_TEXT_P(string_to_text(str));
3543 : }
3544 :
3545 : static void
3546 206 : print_function_sqlbody(StringInfo buf, HeapTuple proctup)
3547 : {
3548 : int numargs;
3549 : Oid *argtypes;
3550 : char **argnames;
3551 : char *argmodes;
3552 206 : deparse_namespace dpns = {0};
3553 : Datum tmp;
3554 : Node *n;
3555 :
3556 206 : dpns.funcname = pstrdup(NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname));
3557 206 : numargs = get_func_arg_info(proctup,
3558 : &argtypes, &argnames, &argmodes);
3559 206 : dpns.numargs = numargs;
3560 206 : dpns.argnames = argnames;
3561 :
3562 206 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosqlbody);
3563 206 : n = stringToNode(TextDatumGetCString(tmp));
3564 :
3565 206 : if (IsA(n, List))
3566 : {
3567 : List *stmts;
3568 : ListCell *lc;
3569 :
3570 160 : stmts = linitial(castNode(List, n));
3571 :
3572 160 : appendStringInfoString(buf, "BEGIN ATOMIC\n");
3573 :
3574 310 : foreach(lc, stmts)
3575 : {
3576 150 : Query *query = lfirst_node(Query, lc);
3577 :
3578 : /* It seems advisable to get at least AccessShareLock on rels */
3579 150 : AcquireRewriteLocks(query, false, false);
3580 150 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3581 : PRETTYFLAG_INDENT, WRAP_COLUMN_DEFAULT, 1);
3582 150 : appendStringInfoChar(buf, ';');
3583 150 : appendStringInfoChar(buf, '\n');
3584 : }
3585 :
3586 160 : appendStringInfoString(buf, "END");
3587 : }
3588 : else
3589 : {
3590 46 : Query *query = castNode(Query, n);
3591 :
3592 : /* It seems advisable to get at least AccessShareLock on rels */
3593 46 : AcquireRewriteLocks(query, false, false);
3594 46 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3595 : 0, WRAP_COLUMN_DEFAULT, 0);
3596 : }
3597 206 : }
3598 :
3599 : Datum
3600 3492 : pg_get_function_sqlbody(PG_FUNCTION_ARGS)
3601 : {
3602 3492 : Oid funcid = PG_GETARG_OID(0);
3603 : StringInfoData buf;
3604 : HeapTuple proctup;
3605 : bool isnull;
3606 :
3607 3492 : initStringInfo(&buf);
3608 :
3609 : /* Look up the function */
3610 3492 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3611 3492 : if (!HeapTupleIsValid(proctup))
3612 0 : PG_RETURN_NULL();
3613 :
3614 3492 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3615 3492 : if (isnull)
3616 : {
3617 3394 : ReleaseSysCache(proctup);
3618 3394 : PG_RETURN_NULL();
3619 : }
3620 :
3621 98 : print_function_sqlbody(&buf, proctup);
3622 :
3623 98 : ReleaseSysCache(proctup);
3624 :
3625 98 : PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
3626 : }
3627 :
3628 :
3629 : /*
3630 : * deparse_expression - General utility for deparsing expressions
3631 : *
3632 : * calls deparse_expression_pretty with all prettyPrinting disabled
3633 : */
3634 : char *
3635 63546 : deparse_expression(Node *expr, List *dpcontext,
3636 : bool forceprefix, bool showimplicit)
3637 : {
3638 63546 : return deparse_expression_pretty(expr, dpcontext, forceprefix,
3639 : showimplicit, 0, 0);
3640 : }
3641 :
3642 : /* ----------
3643 : * deparse_expression_pretty - General utility for deparsing expressions
3644 : *
3645 : * expr is the node tree to be deparsed. It must be a transformed expression
3646 : * tree (ie, not the raw output of gram.y).
3647 : *
3648 : * dpcontext is a list of deparse_namespace nodes representing the context
3649 : * for interpreting Vars in the node tree. It can be NIL if no Vars are
3650 : * expected.
3651 : *
3652 : * forceprefix is true to force all Vars to be prefixed with their table names.
3653 : *
3654 : * showimplicit is true to force all implicit casts to be shown explicitly.
3655 : *
3656 : * Tries to pretty up the output according to prettyFlags and startIndent.
3657 : *
3658 : * The result is a palloc'd string.
3659 : * ----------
3660 : */
3661 : static char *
3662 74782 : deparse_expression_pretty(Node *expr, List *dpcontext,
3663 : bool forceprefix, bool showimplicit,
3664 : int prettyFlags, int startIndent)
3665 : {
3666 : StringInfoData buf;
3667 : deparse_context context;
3668 :
3669 74782 : initStringInfo(&buf);
3670 74782 : context.buf = &buf;
3671 74782 : context.namespaces = dpcontext;
3672 74782 : context.resultDesc = NULL;
3673 74782 : context.targetList = NIL;
3674 74782 : context.windowClause = NIL;
3675 74782 : context.varprefix = forceprefix;
3676 74782 : context.prettyFlags = prettyFlags;
3677 74782 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
3678 74782 : context.indentLevel = startIndent;
3679 74782 : context.colNamesVisible = true;
3680 74782 : context.inGroupBy = false;
3681 74782 : context.varInOrderBy = false;
3682 74782 : context.appendparents = NULL;
3683 :
3684 74782 : get_rule_expr(expr, &context, showimplicit);
3685 :
3686 74782 : return buf.data;
3687 : }
3688 :
3689 : /* ----------
3690 : * deparse_context_for - Build deparse context for a single relation
3691 : *
3692 : * Given the reference name (alias) and OID of a relation, build deparsing
3693 : * context for an expression referencing only that relation (as varno 1,
3694 : * varlevelsup 0). This is sufficient for many uses of deparse_expression.
3695 : * ----------
3696 : */
3697 : List *
3698 21096 : deparse_context_for(const char *aliasname, Oid relid)
3699 : {
3700 : deparse_namespace *dpns;
3701 : RangeTblEntry *rte;
3702 :
3703 21096 : dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
3704 :
3705 : /* Build a minimal RTE for the rel */
3706 21096 : rte = makeNode(RangeTblEntry);
3707 21096 : rte->rtekind = RTE_RELATION;
3708 21096 : rte->relid = relid;
3709 21096 : rte->relkind = RELKIND_RELATION; /* no need for exactness here */
3710 21096 : rte->rellockmode = AccessShareLock;
3711 21096 : rte->alias = makeAlias(aliasname, NIL);
3712 21096 : rte->eref = rte->alias;
3713 21096 : rte->lateral = false;
3714 21096 : rte->inh = false;
3715 21096 : rte->inFromCl = true;
3716 :
3717 : /* Build one-element rtable */
3718 21096 : dpns->rtable = list_make1(rte);
3719 21096 : dpns->subplans = NIL;
3720 21096 : dpns->ctes = NIL;
3721 21096 : dpns->appendrels = NULL;
3722 21096 : set_rtable_names(dpns, NIL, NULL);
3723 21096 : set_simple_column_names(dpns);
3724 :
3725 : /* Return a one-deep namespace stack */
3726 21096 : return list_make1(dpns);
3727 : }
3728 :
3729 : /*
3730 : * deparse_context_for_plan_tree - Build deparse context for a Plan tree
3731 : *
3732 : * When deparsing an expression in a Plan tree, we use the plan's rangetable
3733 : * to resolve names of simple Vars. The initialization of column names for
3734 : * this is rather expensive if the rangetable is large, and it'll be the same
3735 : * for every expression in the Plan tree; so we do it just once and re-use
3736 : * the result of this function for each expression. (Note that the result
3737 : * is not usable until set_deparse_context_plan() is applied to it.)
3738 : *
3739 : * In addition to the PlannedStmt, pass the per-RTE alias names
3740 : * assigned by a previous call to select_rtable_names_for_explain.
3741 : */
3742 : List *
3743 22164 : deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
3744 : {
3745 : deparse_namespace *dpns;
3746 :
3747 22164 : dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
3748 :
3749 : /* Initialize fields that stay the same across the whole plan tree */
3750 22164 : dpns->rtable = pstmt->rtable;
3751 22164 : dpns->rtable_names = rtable_names;
3752 22164 : dpns->subplans = pstmt->subplans;
3753 22164 : dpns->ctes = NIL;
3754 22164 : if (pstmt->appendRelations)
3755 : {
3756 : /* Set up the array, indexed by child relid */
3757 3648 : int ntables = list_length(dpns->rtable);
3758 : ListCell *lc;
3759 :
3760 3648 : dpns->appendrels = (AppendRelInfo **)
3761 3648 : palloc0((ntables + 1) * sizeof(AppendRelInfo *));
3762 20026 : foreach(lc, pstmt->appendRelations)
3763 : {
3764 16378 : AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
3765 16378 : Index crelid = appinfo->child_relid;
3766 :
3767 : Assert(crelid > 0 && crelid <= ntables);
3768 : Assert(dpns->appendrels[crelid] == NULL);
3769 16378 : dpns->appendrels[crelid] = appinfo;
3770 : }
3771 : }
3772 : else
3773 18516 : dpns->appendrels = NULL; /* don't need it */
3774 :
3775 : /*
3776 : * Set up column name aliases. We will get rather bogus results for join
3777 : * RTEs, but that doesn't matter because plan trees don't contain any join
3778 : * alias Vars.
3779 : */
3780 22164 : set_simple_column_names(dpns);
3781 :
3782 : /* Return a one-deep namespace stack */
3783 22164 : return list_make1(dpns);
3784 : }
3785 :
3786 : /*
3787 : * set_deparse_context_plan - Specify Plan node containing expression
3788 : *
3789 : * When deparsing an expression in a Plan tree, we might have to resolve
3790 : * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
3791 : * provide the parent Plan node. Then OUTER_VAR and INNER_VAR references
3792 : * can be resolved by drilling down into the left and right child plans.
3793 : * Similarly, INDEX_VAR references can be resolved by reference to the
3794 : * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
3795 : * ForeignScan and CustomScan nodes. (Note that we don't currently support
3796 : * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
3797 : * for those, we can only deparse the indexqualorig fields, which won't
3798 : * contain INDEX_VAR Vars.)
3799 : *
3800 : * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
3801 : * the most-closely-nested first. This is needed to resolve PARAM_EXEC
3802 : * Params. Note we assume that all the Plan nodes share the same rtable.
3803 : *
3804 : * Once this function has been called, deparse_expression() can be called on
3805 : * subsidiary expression(s) of the specified Plan node. To deparse
3806 : * expressions of a different Plan node in the same Plan tree, re-call this
3807 : * function to identify the new parent Plan node.
3808 : *
3809 : * The result is the same List passed in; this is a notational convenience.
3810 : */
3811 : List *
3812 47996 : set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
3813 : {
3814 : deparse_namespace *dpns;
3815 :
3816 : /* Should always have one-entry namespace list for Plan deparsing */
3817 : Assert(list_length(dpcontext) == 1);
3818 47996 : dpns = (deparse_namespace *) linitial(dpcontext);
3819 :
3820 : /* Set our attention on the specific plan node passed in */
3821 47996 : dpns->ancestors = ancestors;
3822 47996 : set_deparse_plan(dpns, plan);
3823 :
3824 47996 : return dpcontext;
3825 : }
3826 :
3827 : /*
3828 : * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
3829 : *
3830 : * Determine the relation aliases we'll use during an EXPLAIN operation.
3831 : * This is just a frontend to set_rtable_names. We have to expose the aliases
3832 : * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
3833 : */
3834 : List *
3835 22164 : select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
3836 : {
3837 : deparse_namespace dpns;
3838 :
3839 22164 : memset(&dpns, 0, sizeof(dpns));
3840 22164 : dpns.rtable = rtable;
3841 22164 : dpns.subplans = NIL;
3842 22164 : dpns.ctes = NIL;
3843 22164 : dpns.appendrels = NULL;
3844 22164 : set_rtable_names(&dpns, NIL, rels_used);
3845 : /* We needn't bother computing column aliases yet */
3846 :
3847 22164 : return dpns.rtable_names;
3848 : }
3849 :
3850 : /*
3851 : * set_rtable_names: select RTE aliases to be used in printing a query
3852 : *
3853 : * We fill in dpns->rtable_names with a list of names that is one-for-one with
3854 : * the already-filled dpns->rtable list. Each RTE name is unique among those
3855 : * in the new namespace plus any ancestor namespaces listed in
3856 : * parent_namespaces.
3857 : *
3858 : * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
3859 : *
3860 : * Note that this function is only concerned with relation names, not column
3861 : * names.
3862 : */
3863 : static void
3864 49056 : set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
3865 : Bitmapset *rels_used)
3866 : {
3867 : HASHCTL hash_ctl;
3868 : HTAB *names_hash;
3869 : NameHashEntry *hentry;
3870 : bool found;
3871 : int rtindex;
3872 : ListCell *lc;
3873 :
3874 49056 : dpns->rtable_names = NIL;
3875 : /* nothing more to do if empty rtable */
3876 49056 : if (dpns->rtable == NIL)
3877 518 : return;
3878 :
3879 : /*
3880 : * We use a hash table to hold known names, so that this process is O(N)
3881 : * not O(N^2) for N names.
3882 : */
3883 48538 : hash_ctl.keysize = NAMEDATALEN;
3884 48538 : hash_ctl.entrysize = sizeof(NameHashEntry);
3885 48538 : hash_ctl.hcxt = CurrentMemoryContext;
3886 48538 : names_hash = hash_create("set_rtable_names names",
3887 48538 : list_length(dpns->rtable),
3888 : &hash_ctl,
3889 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
3890 :
3891 : /* Preload the hash table with names appearing in parent_namespaces */
3892 50138 : foreach(lc, parent_namespaces)
3893 : {
3894 1600 : deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
3895 : ListCell *lc2;
3896 :
3897 6036 : foreach(lc2, olddpns->rtable_names)
3898 : {
3899 4436 : char *oldname = (char *) lfirst(lc2);
3900 :
3901 4436 : if (oldname == NULL)
3902 336 : continue;
3903 4100 : hentry = (NameHashEntry *) hash_search(names_hash,
3904 : oldname,
3905 : HASH_ENTER,
3906 : &found);
3907 : /* we do not complain about duplicate names in parent namespaces */
3908 4100 : hentry->counter = 0;
3909 : }
3910 : }
3911 :
3912 : /* Now we can scan the rtable */
3913 48538 : rtindex = 1;
3914 139542 : foreach(lc, dpns->rtable)
3915 : {
3916 91004 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
3917 : char *refname;
3918 :
3919 : /* Just in case this takes an unreasonable amount of time ... */
3920 91004 : CHECK_FOR_INTERRUPTS();
3921 :
3922 91004 : if (rels_used && !bms_is_member(rtindex, rels_used))
3923 : {
3924 : /* Ignore unreferenced RTE */
3925 15518 : refname = NULL;
3926 : }
3927 75486 : else if (rte->alias)
3928 : {
3929 : /* If RTE has a user-defined alias, prefer that */
3930 50134 : refname = rte->alias->aliasname;
3931 : }
3932 25352 : else if (rte->rtekind == RTE_RELATION)
3933 : {
3934 : /* Use the current actual name of the relation */
3935 20800 : refname = get_rel_name(rte->relid);
3936 : }
3937 4552 : else if (rte->rtekind == RTE_JOIN)
3938 : {
3939 : /* Unnamed join has no refname */
3940 1818 : refname = NULL;
3941 : }
3942 : else
3943 : {
3944 : /* Otherwise use whatever the parser assigned */
3945 2734 : refname = rte->eref->aliasname;
3946 : }
3947 :
3948 : /*
3949 : * If the selected name isn't unique, append digits to make it so, and
3950 : * make a new hash entry for it once we've got a unique name. For a
3951 : * very long input name, we might have to truncate to stay within
3952 : * NAMEDATALEN.
3953 : */
3954 91004 : if (refname)
3955 : {
3956 73668 : hentry = (NameHashEntry *) hash_search(names_hash,
3957 : refname,
3958 : HASH_ENTER,
3959 : &found);
3960 73668 : if (found)
3961 : {
3962 : /* Name already in use, must choose a new one */
3963 13334 : int refnamelen = strlen(refname);
3964 13334 : char *modname = (char *) palloc(refnamelen + 16);
3965 : NameHashEntry *hentry2;
3966 :
3967 : do
3968 : {
3969 13340 : hentry->counter++;
3970 : for (;;)
3971 : {
3972 13352 : memcpy(modname, refname, refnamelen);
3973 13352 : sprintf(modname + refnamelen, "_%d", hentry->counter);
3974 13352 : if (strlen(modname) < NAMEDATALEN)
3975 13340 : break;
3976 : /* drop chars from refname to keep all the digits */
3977 12 : refnamelen = pg_mbcliplen(refname, refnamelen,
3978 : refnamelen - 1);
3979 : }
3980 13340 : hentry2 = (NameHashEntry *) hash_search(names_hash,
3981 : modname,
3982 : HASH_ENTER,
3983 : &found);
3984 13340 : } while (found);
3985 13334 : hentry2->counter = 0; /* init new hash entry */
3986 13334 : refname = modname;
3987 : }
3988 : else
3989 : {
3990 : /* Name not previously used, need only initialize hentry */
3991 60334 : hentry->counter = 0;
3992 : }
3993 : }
3994 :
3995 91004 : dpns->rtable_names = lappend(dpns->rtable_names, refname);
3996 91004 : rtindex++;
3997 : }
3998 :
3999 48538 : hash_destroy(names_hash);
4000 : }
4001 :
4002 : /*
4003 : * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
4004 : *
4005 : * For convenience, this is defined to initialize the deparse_namespace struct
4006 : * from scratch.
4007 : */
4008 : static void
4009 5658 : set_deparse_for_query(deparse_namespace *dpns, Query *query,
4010 : List *parent_namespaces)
4011 : {
4012 : ListCell *lc;
4013 : ListCell *lc2;
4014 :
4015 : /* Initialize *dpns and fill rtable/ctes links */
4016 5658 : memset(dpns, 0, sizeof(deparse_namespace));
4017 5658 : dpns->rtable = query->rtable;
4018 5658 : dpns->subplans = NIL;
4019 5658 : dpns->ctes = query->cteList;
4020 5658 : dpns->appendrels = NULL;
4021 :
4022 : /* Assign a unique relation alias to each RTE */
4023 5658 : set_rtable_names(dpns, parent_namespaces, NULL);
4024 :
4025 : /* Initialize dpns->rtable_columns to contain zeroed structs */
4026 5658 : dpns->rtable_columns = NIL;
4027 16002 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4028 10344 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4029 : palloc0(sizeof(deparse_columns)));
4030 :
4031 : /* If it's a utility query, it won't have a jointree */
4032 5658 : if (query->jointree)
4033 : {
4034 : /* Detect whether global uniqueness of USING names is needed */
4035 5642 : dpns->unique_using =
4036 5642 : has_dangerous_join_using(dpns, (Node *) query->jointree);
4037 :
4038 : /*
4039 : * Select names for columns merged by USING, via a recursive pass over
4040 : * the query jointree.
4041 : */
4042 5642 : set_using_names(dpns, (Node *) query->jointree, NIL);
4043 : }
4044 :
4045 : /*
4046 : * Now assign remaining column aliases for each RTE. We do this in a
4047 : * linear scan of the rtable, so as to process RTEs whether or not they
4048 : * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4049 : * etc). JOIN RTEs must be processed after their children, but this is
4050 : * okay because they appear later in the rtable list than their children
4051 : * (cf Asserts in identify_join_columns()).
4052 : */
4053 16002 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4054 : {
4055 10344 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4056 10344 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4057 :
4058 10344 : if (rte->rtekind == RTE_JOIN)
4059 1498 : set_join_column_names(dpns, rte, colinfo);
4060 : else
4061 8846 : set_relation_column_names(dpns, rte, colinfo);
4062 : }
4063 5658 : }
4064 :
4065 : /*
4066 : * set_simple_column_names: fill in column aliases for non-query situations
4067 : *
4068 : * This handles EXPLAIN and cases where we only have relation RTEs. Without
4069 : * a join tree, we can't do anything smart about join RTEs, but we don't
4070 : * need to (note that EXPLAIN should never see join alias Vars anyway).
4071 : * If we do hit a join RTE we'll just process it like a non-table base RTE.
4072 : */
4073 : static void
4074 43398 : set_simple_column_names(deparse_namespace *dpns)
4075 : {
4076 : ListCell *lc;
4077 : ListCell *lc2;
4078 :
4079 : /* Initialize dpns->rtable_columns to contain zeroed structs */
4080 43398 : dpns->rtable_columns = NIL;
4081 124058 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4082 80660 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4083 : palloc0(sizeof(deparse_columns)));
4084 :
4085 : /* Assign unique column aliases within each RTE */
4086 124058 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4087 : {
4088 80660 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4089 80660 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4090 :
4091 80660 : set_relation_column_names(dpns, rte, colinfo);
4092 : }
4093 43398 : }
4094 :
4095 : /*
4096 : * has_dangerous_join_using: search jointree for unnamed JOIN USING
4097 : *
4098 : * Merged columns of a JOIN USING may act differently from either of the input
4099 : * columns, either because they are merged with COALESCE (in a FULL JOIN) or
4100 : * because an implicit coercion of the underlying input column is required.
4101 : * In such a case the column must be referenced as a column of the JOIN not as
4102 : * a column of either input. And this is problematic if the join is unnamed
4103 : * (alias-less): we cannot qualify the column's name with an RTE name, since
4104 : * there is none. (Forcibly assigning an alias to the join is not a solution,
4105 : * since that will prevent legal references to tables below the join.)
4106 : * To ensure that every column in the query is unambiguously referenceable,
4107 : * we must assign such merged columns names that are globally unique across
4108 : * the whole query, aliasing other columns out of the way as necessary.
4109 : *
4110 : * Because the ensuing re-aliasing is fairly damaging to the readability of
4111 : * the query, we don't do this unless we have to. So, we must pre-scan
4112 : * the join tree to see if we have to, before starting set_using_names().
4113 : */
4114 : static bool
4115 13484 : has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
4116 : {
4117 13484 : if (IsA(jtnode, RangeTblRef))
4118 : {
4119 : /* nothing to do here */
4120 : }
4121 7074 : else if (IsA(jtnode, FromExpr))
4122 : {
4123 5642 : FromExpr *f = (FromExpr *) jtnode;
4124 : ListCell *lc;
4125 :
4126 10692 : foreach(lc, f->fromlist)
4127 : {
4128 5122 : if (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))
4129 72 : return true;
4130 : }
4131 : }
4132 1432 : else if (IsA(jtnode, JoinExpr))
4133 : {
4134 1432 : JoinExpr *j = (JoinExpr *) jtnode;
4135 :
4136 : /* Is it an unnamed JOIN with USING? */
4137 1432 : if (j->alias == NULL && j->usingClause)
4138 : {
4139 : /*
4140 : * Yes, so check each join alias var to see if any of them are not
4141 : * simple references to underlying columns. If so, we have a
4142 : * dangerous situation and must pick unique aliases.
4143 : */
4144 286 : RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4145 :
4146 : /* We need only examine the merged columns */
4147 596 : for (int i = 0; i < jrte->joinmergedcols; i++)
4148 : {
4149 382 : Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4150 :
4151 382 : if (!IsA(aliasvar, Var))
4152 72 : return true;
4153 : }
4154 : }
4155 :
4156 : /* Nope, but inspect children */
4157 1360 : if (has_dangerous_join_using(dpns, j->larg))
4158 0 : return true;
4159 1360 : if (has_dangerous_join_using(dpns, j->rarg))
4160 0 : return true;
4161 : }
4162 : else
4163 0 : elog(ERROR, "unrecognized node type: %d",
4164 : (int) nodeTag(jtnode));
4165 13340 : return false;
4166 : }
4167 :
4168 : /*
4169 : * set_using_names: select column aliases to be used for merged USING columns
4170 : *
4171 : * We do this during a recursive descent of the query jointree.
4172 : * dpns->unique_using must already be set to determine the global strategy.
4173 : *
4174 : * Column alias info is saved in the dpns->rtable_columns list, which is
4175 : * assumed to be filled with pre-zeroed deparse_columns structs.
4176 : *
4177 : * parentUsing is a list of all USING aliases assigned in parent joins of
4178 : * the current jointree node. (The passed-in list must not be modified.)
4179 : *
4180 : * Note that we do not use per-deparse_columns hash tables in this function.
4181 : * The number of names that need to be assigned should be small enough that
4182 : * we don't need to trouble with that.
4183 : */
4184 : static void
4185 13802 : set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
4186 : {
4187 13802 : if (IsA(jtnode, RangeTblRef))
4188 : {
4189 : /* nothing to do now */
4190 : }
4191 7140 : else if (IsA(jtnode, FromExpr))
4192 : {
4193 5642 : FromExpr *f = (FromExpr *) jtnode;
4194 : ListCell *lc;
4195 :
4196 10806 : foreach(lc, f->fromlist)
4197 5164 : set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4198 : }
4199 1498 : else if (IsA(jtnode, JoinExpr))
4200 : {
4201 1498 : JoinExpr *j = (JoinExpr *) jtnode;
4202 1498 : RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4203 1498 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
4204 : int *leftattnos;
4205 : int *rightattnos;
4206 : deparse_columns *leftcolinfo;
4207 : deparse_columns *rightcolinfo;
4208 : int i;
4209 : ListCell *lc;
4210 :
4211 : /* Get info about the shape of the join */
4212 1498 : identify_join_columns(j, rte, colinfo);
4213 1498 : leftattnos = colinfo->leftattnos;
4214 1498 : rightattnos = colinfo->rightattnos;
4215 :
4216 : /* Look up the not-yet-filled-in child deparse_columns structs */
4217 1498 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4218 1498 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4219 :
4220 : /*
4221 : * If this join is unnamed, then we cannot substitute new aliases at
4222 : * this level, so any name requirements pushed down to here must be
4223 : * pushed down again to the children.
4224 : */
4225 1498 : if (rte->alias == NULL)
4226 : {
4227 1528 : for (i = 0; i < colinfo->num_cols; i++)
4228 : {
4229 138 : char *colname = colinfo->colnames[i];
4230 :
4231 138 : if (colname == NULL)
4232 24 : continue;
4233 :
4234 : /* Push down to left column, unless it's a system column */
4235 114 : if (leftattnos[i] > 0)
4236 : {
4237 102 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4238 102 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4239 : }
4240 :
4241 : /* Same on the righthand side */
4242 114 : if (rightattnos[i] > 0)
4243 : {
4244 114 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4245 114 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4246 : }
4247 : }
4248 : }
4249 :
4250 : /*
4251 : * If there's a USING clause, select the USING column names and push
4252 : * those names down to the children. We have two strategies:
4253 : *
4254 : * If dpns->unique_using is true, we force all USING names to be
4255 : * unique across the whole query level. In principle we'd only need
4256 : * the names of dangerous USING columns to be globally unique, but to
4257 : * safely assign all USING names in a single pass, we have to enforce
4258 : * the same uniqueness rule for all of them. However, if a USING
4259 : * column's name has been pushed down from the parent, we should use
4260 : * it as-is rather than making a uniqueness adjustment. This is
4261 : * necessary when we're at an unnamed join, and it creates no risk of
4262 : * ambiguity. Also, if there's a user-written output alias for a
4263 : * merged column, we prefer to use that rather than the input name;
4264 : * this simplifies the logic and seems likely to lead to less aliasing
4265 : * overall.
4266 : *
4267 : * If dpns->unique_using is false, we only need USING names to be
4268 : * unique within their own join RTE. We still need to honor
4269 : * pushed-down names, though.
4270 : *
4271 : * Though significantly different in results, these two strategies are
4272 : * implemented by the same code, with only the difference of whether
4273 : * to put assigned names into dpns->using_names.
4274 : */
4275 1498 : if (j->usingClause)
4276 : {
4277 : /* Copy the input parentUsing list so we don't modify it */
4278 424 : parentUsing = list_copy(parentUsing);
4279 :
4280 : /* USING names must correspond to the first join output columns */
4281 424 : expand_colnames_array_to(colinfo, list_length(j->usingClause));
4282 424 : i = 0;
4283 1004 : foreach(lc, j->usingClause)
4284 : {
4285 580 : char *colname = strVal(lfirst(lc));
4286 :
4287 : /* Assert it's a merged column */
4288 : Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4289 :
4290 : /* Adopt passed-down name if any, else select unique name */
4291 580 : if (colinfo->colnames[i] != NULL)
4292 102 : colname = colinfo->colnames[i];
4293 : else
4294 : {
4295 : /* Prefer user-written output alias if any */
4296 478 : if (rte->alias && i < list_length(rte->alias->colnames))
4297 0 : colname = strVal(list_nth(rte->alias->colnames, i));
4298 : /* Make it appropriately unique */
4299 478 : colname = make_colname_unique(colname, dpns, colinfo);
4300 478 : if (dpns->unique_using)
4301 126 : dpns->using_names = lappend(dpns->using_names,
4302 : colname);
4303 : /* Save it as output column name, too */
4304 478 : colinfo->colnames[i] = colname;
4305 : }
4306 :
4307 : /* Remember selected names for use later */
4308 580 : colinfo->usingNames = lappend(colinfo->usingNames, colname);
4309 580 : parentUsing = lappend(parentUsing, colname);
4310 :
4311 : /* Push down to left column, unless it's a system column */
4312 580 : if (leftattnos[i] > 0)
4313 : {
4314 580 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4315 580 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4316 : }
4317 :
4318 : /* Same on the righthand side */
4319 580 : if (rightattnos[i] > 0)
4320 : {
4321 580 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4322 580 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4323 : }
4324 :
4325 580 : i++;
4326 : }
4327 : }
4328 :
4329 : /* Mark child deparse_columns structs with correct parentUsing info */
4330 1498 : leftcolinfo->parentUsing = parentUsing;
4331 1498 : rightcolinfo->parentUsing = parentUsing;
4332 :
4333 : /* Now recursively assign USING column names in children */
4334 1498 : set_using_names(dpns, j->larg, parentUsing);
4335 1498 : set_using_names(dpns, j->rarg, parentUsing);
4336 : }
4337 : else
4338 0 : elog(ERROR, "unrecognized node type: %d",
4339 : (int) nodeTag(jtnode));
4340 13802 : }
4341 :
4342 : /*
4343 : * set_relation_column_names: select column aliases for a non-join RTE
4344 : *
4345 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4346 : * If any colnames entries are already filled in, those override local
4347 : * choices.
4348 : */
4349 : static void
4350 89506 : set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4351 : deparse_columns *colinfo)
4352 : {
4353 : int ncolumns;
4354 : char **real_colnames;
4355 : bool changed_any;
4356 : int noldcolumns;
4357 : int i;
4358 : int j;
4359 :
4360 : /*
4361 : * Construct an array of the current "real" column names of the RTE.
4362 : * real_colnames[] will be indexed by physical column number, with NULL
4363 : * entries for dropped columns.
4364 : */
4365 89506 : if (rte->rtekind == RTE_RELATION)
4366 : {
4367 : /* Relation --- look to the system catalogs for up-to-date info */
4368 : Relation rel;
4369 : TupleDesc tupdesc;
4370 :
4371 72084 : rel = relation_open(rte->relid, AccessShareLock);
4372 72084 : tupdesc = RelationGetDescr(rel);
4373 :
4374 72084 : ncolumns = tupdesc->natts;
4375 72084 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4376 :
4377 468674 : for (i = 0; i < ncolumns; i++)
4378 : {
4379 396590 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4380 :
4381 396590 : if (attr->attisdropped)
4382 3040 : real_colnames[i] = NULL;
4383 : else
4384 393550 : real_colnames[i] = pstrdup(NameStr(attr->attname));
4385 : }
4386 72084 : relation_close(rel, AccessShareLock);
4387 : }
4388 : else
4389 : {
4390 : /* Otherwise get the column names from eref or expandRTE() */
4391 : List *colnames;
4392 : ListCell *lc;
4393 :
4394 : /*
4395 : * Functions returning composites have the annoying property that some
4396 : * of the composite type's columns might have been dropped since the
4397 : * query was parsed. If possible, use expandRTE() to handle that
4398 : * case, since it has the tedious logic needed to find out about
4399 : * dropped columns. However, if we're explaining a plan, then we
4400 : * don't have rte->functions because the planner thinks that won't be
4401 : * needed later, and that breaks expandRTE(). So in that case we have
4402 : * to rely on rte->eref, which may lead us to report a dropped
4403 : * column's old name; that seems close enough for EXPLAIN's purposes.
4404 : *
4405 : * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4406 : * which should be sufficiently up-to-date: no other RTE types can
4407 : * have columns get dropped from under them after parsing.
4408 : */
4409 17422 : if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4410 : {
4411 : /* Since we're not creating Vars, rtindex etc. don't matter */
4412 786 : expandRTE(rte, 1, 0, -1, true /* include dropped */ ,
4413 : &colnames, NULL);
4414 : }
4415 : else
4416 16636 : colnames = rte->eref->colnames;
4417 :
4418 17422 : ncolumns = list_length(colnames);
4419 17422 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4420 :
4421 17422 : i = 0;
4422 96682 : foreach(lc, colnames)
4423 : {
4424 : /*
4425 : * If the column name we find here is an empty string, then it's a
4426 : * dropped column, so change to NULL.
4427 : */
4428 79260 : char *cname = strVal(lfirst(lc));
4429 :
4430 79260 : if (cname[0] == '\0')
4431 54 : cname = NULL;
4432 79260 : real_colnames[i] = cname;
4433 79260 : i++;
4434 : }
4435 : }
4436 :
4437 : /*
4438 : * Ensure colinfo->colnames has a slot for each column. (It could be long
4439 : * enough already, if we pushed down a name for the last column.) Note:
4440 : * it's possible that there are now more columns than there were when the
4441 : * query was parsed, ie colnames could be longer than rte->eref->colnames.
4442 : * We must assign unique aliases to the new columns too, else there could
4443 : * be unresolved conflicts when the view/rule is reloaded.
4444 : */
4445 89506 : expand_colnames_array_to(colinfo, ncolumns);
4446 : Assert(colinfo->num_cols == ncolumns);
4447 :
4448 : /*
4449 : * Make sufficiently large new_colnames and is_new_col arrays, too.
4450 : *
4451 : * Note: because we leave colinfo->num_new_cols zero until after the loop,
4452 : * colname_is_unique will not consult that array, which is fine because it
4453 : * would only be duplicate effort.
4454 : */
4455 89506 : colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4456 89506 : colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4457 :
4458 : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4459 89506 : build_colinfo_names_hash(colinfo);
4460 :
4461 : /*
4462 : * Scan the columns, select a unique alias for each one, and store it in
4463 : * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4464 : * entries for dropped columns, the latter omits them. Also mark
4465 : * new_colnames entries as to whether they are new since parse time; this
4466 : * is the case for entries beyond the length of rte->eref->colnames.
4467 : */
4468 89506 : noldcolumns = list_length(rte->eref->colnames);
4469 89506 : changed_any = false;
4470 89506 : j = 0;
4471 565356 : for (i = 0; i < ncolumns; i++)
4472 : {
4473 475850 : char *real_colname = real_colnames[i];
4474 475850 : char *colname = colinfo->colnames[i];
4475 :
4476 : /* Skip dropped columns */
4477 475850 : if (real_colname == NULL)
4478 : {
4479 : Assert(colname == NULL); /* colnames[i] is already NULL */
4480 3094 : continue;
4481 : }
4482 :
4483 : /* If alias already assigned, that's what to use */
4484 472756 : if (colname == NULL)
4485 : {
4486 : /* If user wrote an alias, prefer that over real column name */
4487 471698 : if (rte->alias && i < list_length(rte->alias->colnames))
4488 41224 : colname = strVal(list_nth(rte->alias->colnames, i));
4489 : else
4490 430474 : colname = real_colname;
4491 :
4492 : /* Unique-ify and insert into colinfo */
4493 471698 : colname = make_colname_unique(colname, dpns, colinfo);
4494 :
4495 471698 : colinfo->colnames[i] = colname;
4496 471698 : add_to_names_hash(colinfo, colname);
4497 : }
4498 :
4499 : /* Put names of non-dropped columns in new_colnames[] too */
4500 472756 : colinfo->new_colnames[j] = colname;
4501 : /* And mark them as new or not */
4502 472756 : colinfo->is_new_col[j] = (i >= noldcolumns);
4503 472756 : j++;
4504 :
4505 : /* Remember if any assigned aliases differ from "real" name */
4506 472756 : if (!changed_any && strcmp(colname, real_colname) != 0)
4507 5092 : changed_any = true;
4508 : }
4509 :
4510 : /* We're now done needing the colinfo's names_hash */
4511 89506 : destroy_colinfo_names_hash(colinfo);
4512 :
4513 : /*
4514 : * Set correct length for new_colnames[] array. (Note: if columns have
4515 : * been added, colinfo->num_cols includes them, which is not really quite
4516 : * right but is harmless, since any new columns must be at the end where
4517 : * they won't affect varattnos of pre-existing columns.)
4518 : */
4519 89506 : colinfo->num_new_cols = j;
4520 :
4521 : /*
4522 : * For a relation RTE, we need only print the alias column names if any
4523 : * are different from the underlying "real" names. For a function RTE,
4524 : * always emit a complete column alias list; this is to protect against
4525 : * possible instability of the default column names (eg, from altering
4526 : * parameter names). For tablefunc RTEs, we never print aliases, because
4527 : * the column names are part of the clause itself. For other RTE types,
4528 : * print if we changed anything OR if there were user-written column
4529 : * aliases (since the latter would be part of the underlying "reality").
4530 : */
4531 89506 : if (rte->rtekind == RTE_RELATION)
4532 72084 : colinfo->printaliases = changed_any;
4533 17422 : else if (rte->rtekind == RTE_FUNCTION)
4534 1254 : colinfo->printaliases = true;
4535 16168 : else if (rte->rtekind == RTE_TABLEFUNC)
4536 170 : colinfo->printaliases = false;
4537 15998 : else if (rte->alias && rte->alias->colnames != NIL)
4538 768 : colinfo->printaliases = true;
4539 : else
4540 15230 : colinfo->printaliases = changed_any;
4541 89506 : }
4542 :
4543 : /*
4544 : * set_join_column_names: select column aliases for a join RTE
4545 : *
4546 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4547 : * If any colnames entries are already filled in, those override local
4548 : * choices. Also, names for USING columns were already chosen by
4549 : * set_using_names(). We further expect that column alias selection has been
4550 : * completed for both input RTEs.
4551 : */
4552 : static void
4553 1498 : set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4554 : deparse_columns *colinfo)
4555 : {
4556 : deparse_columns *leftcolinfo;
4557 : deparse_columns *rightcolinfo;
4558 : bool changed_any;
4559 : int noldcolumns;
4560 : int nnewcolumns;
4561 1498 : Bitmapset *leftmerged = NULL;
4562 1498 : Bitmapset *rightmerged = NULL;
4563 : int i;
4564 : int j;
4565 : int ic;
4566 : int jc;
4567 :
4568 : /* Look up the previously-filled-in child deparse_columns structs */
4569 1498 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4570 1498 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4571 :
4572 : /*
4573 : * Ensure colinfo->colnames has a slot for each column. (It could be long
4574 : * enough already, if we pushed down a name for the last column.) Note:
4575 : * it's possible that one or both inputs now have more columns than there
4576 : * were when the query was parsed, but we'll deal with that below. We
4577 : * only need entries in colnames for pre-existing columns.
4578 : */
4579 1498 : noldcolumns = list_length(rte->eref->colnames);
4580 1498 : expand_colnames_array_to(colinfo, noldcolumns);
4581 : Assert(colinfo->num_cols == noldcolumns);
4582 :
4583 : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4584 1498 : build_colinfo_names_hash(colinfo);
4585 :
4586 : /*
4587 : * Scan the join output columns, select an alias for each one, and store
4588 : * it in colinfo->colnames. If there are USING columns, set_using_names()
4589 : * already selected their names, so we can start the loop at the first
4590 : * non-merged column.
4591 : */
4592 1498 : changed_any = false;
4593 49096 : for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4594 : {
4595 47598 : char *colname = colinfo->colnames[i];
4596 : char *real_colname;
4597 :
4598 : /* Join column must refer to at least one input column */
4599 : Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4600 :
4601 : /* Get the child column name */
4602 47598 : if (colinfo->leftattnos[i] > 0)
4603 33454 : real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4604 14144 : else if (colinfo->rightattnos[i] > 0)
4605 14144 : real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4606 : else
4607 : {
4608 : /* We're joining system columns --- use eref name */
4609 0 : real_colname = strVal(list_nth(rte->eref->colnames, i));
4610 : }
4611 :
4612 : /* If child col has been dropped, no need to assign a join colname */
4613 47598 : if (real_colname == NULL)
4614 : {
4615 6 : colinfo->colnames[i] = NULL;
4616 6 : continue;
4617 : }
4618 :
4619 : /* In an unnamed join, just report child column names as-is */
4620 47592 : if (rte->alias == NULL)
4621 : {
4622 47214 : colinfo->colnames[i] = real_colname;
4623 47214 : add_to_names_hash(colinfo, real_colname);
4624 47214 : continue;
4625 : }
4626 :
4627 : /* If alias already assigned, that's what to use */
4628 378 : if (colname == NULL)
4629 : {
4630 : /* If user wrote an alias, prefer that over real column name */
4631 378 : if (rte->alias && i < list_length(rte->alias->colnames))
4632 96 : colname = strVal(list_nth(rte->alias->colnames, i));
4633 : else
4634 282 : colname = real_colname;
4635 :
4636 : /* Unique-ify and insert into colinfo */
4637 378 : colname = make_colname_unique(colname, dpns, colinfo);
4638 :
4639 378 : colinfo->colnames[i] = colname;
4640 378 : add_to_names_hash(colinfo, colname);
4641 : }
4642 :
4643 : /* Remember if any assigned aliases differ from "real" name */
4644 378 : if (!changed_any && strcmp(colname, real_colname) != 0)
4645 24 : changed_any = true;
4646 : }
4647 :
4648 : /*
4649 : * Calculate number of columns the join would have if it were re-parsed
4650 : * now, and create storage for the new_colnames and is_new_col arrays.
4651 : *
4652 : * Note: colname_is_unique will be consulting new_colnames[] during the
4653 : * loops below, so its not-yet-filled entries must be zeroes.
4654 : */
4655 2996 : nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
4656 1498 : list_length(colinfo->usingNames);
4657 1498 : colinfo->num_new_cols = nnewcolumns;
4658 1498 : colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
4659 1498 : colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
4660 :
4661 : /*
4662 : * Generating the new_colnames array is a bit tricky since any new columns
4663 : * added since parse time must be inserted in the right places. This code
4664 : * must match the parser, which will order a join's columns as merged
4665 : * columns first (in USING-clause order), then non-merged columns from the
4666 : * left input (in attnum order), then non-merged columns from the right
4667 : * input (ditto). If one of the inputs is itself a join, its columns will
4668 : * be ordered according to the same rule, which means newly-added columns
4669 : * might not be at the end. We can figure out what's what by consulting
4670 : * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
4671 : *
4672 : * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
4673 : * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
4674 : * meanings for the current child RTE.
4675 : */
4676 :
4677 : /* Handle merged columns; they are first and can't be new */
4678 1498 : i = j = 0;
4679 2078 : while (i < noldcolumns &&
4680 2078 : colinfo->leftattnos[i] != 0 &&
4681 2078 : colinfo->rightattnos[i] != 0)
4682 : {
4683 : /* column name is already determined and known unique */
4684 580 : colinfo->new_colnames[j] = colinfo->colnames[i];
4685 580 : colinfo->is_new_col[j] = false;
4686 :
4687 : /* build bitmapsets of child attnums of merged columns */
4688 580 : if (colinfo->leftattnos[i] > 0)
4689 580 : leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
4690 580 : if (colinfo->rightattnos[i] > 0)
4691 580 : rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);
4692 :
4693 580 : i++, j++;
4694 : }
4695 :
4696 : /* Handle non-merged left-child columns */
4697 1498 : ic = 0;
4698 36018 : for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
4699 : {
4700 34520 : char *child_colname = leftcolinfo->new_colnames[jc];
4701 :
4702 34520 : if (!leftcolinfo->is_new_col[jc])
4703 : {
4704 : /* Advance ic to next non-dropped old column of left child */
4705 34112 : while (ic < leftcolinfo->num_cols &&
4706 34112 : leftcolinfo->colnames[ic] == NULL)
4707 84 : ic++;
4708 : Assert(ic < leftcolinfo->num_cols);
4709 34028 : ic++;
4710 : /* If it is a merged column, we already processed it */
4711 34028 : if (bms_is_member(ic, leftmerged))
4712 580 : continue;
4713 : /* Else, advance i to the corresponding existing join column */
4714 33454 : while (i < colinfo->num_cols &&
4715 33454 : colinfo->colnames[i] == NULL)
4716 6 : i++;
4717 : Assert(i < colinfo->num_cols);
4718 : Assert(ic == colinfo->leftattnos[i]);
4719 : /* Use the already-assigned name of this column */
4720 33448 : colinfo->new_colnames[j] = colinfo->colnames[i];
4721 33448 : i++;
4722 : }
4723 : else
4724 : {
4725 : /*
4726 : * Unique-ify the new child column name and assign, unless we're
4727 : * in an unnamed join, in which case just copy
4728 : */
4729 492 : if (rte->alias != NULL)
4730 : {
4731 264 : colinfo->new_colnames[j] =
4732 132 : make_colname_unique(child_colname, dpns, colinfo);
4733 132 : if (!changed_any &&
4734 108 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4735 12 : changed_any = true;
4736 : }
4737 : else
4738 360 : colinfo->new_colnames[j] = child_colname;
4739 492 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
4740 : }
4741 :
4742 33940 : colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
4743 33940 : j++;
4744 : }
4745 :
4746 : /* Handle non-merged right-child columns in exactly the same way */
4747 1498 : ic = 0;
4748 16390 : for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
4749 : {
4750 14892 : char *child_colname = rightcolinfo->new_colnames[jc];
4751 :
4752 14892 : if (!rightcolinfo->is_new_col[jc])
4753 : {
4754 : /* Advance ic to next non-dropped old column of right child */
4755 14724 : while (ic < rightcolinfo->num_cols &&
4756 14724 : rightcolinfo->colnames[ic] == NULL)
4757 0 : ic++;
4758 : Assert(ic < rightcolinfo->num_cols);
4759 14724 : ic++;
4760 : /* If it is a merged column, we already processed it */
4761 14724 : if (bms_is_member(ic, rightmerged))
4762 580 : continue;
4763 : /* Else, advance i to the corresponding existing join column */
4764 14144 : while (i < colinfo->num_cols &&
4765 14144 : colinfo->colnames[i] == NULL)
4766 0 : i++;
4767 : Assert(i < colinfo->num_cols);
4768 : Assert(ic == colinfo->rightattnos[i]);
4769 : /* Use the already-assigned name of this column */
4770 14144 : colinfo->new_colnames[j] = colinfo->colnames[i];
4771 14144 : i++;
4772 : }
4773 : else
4774 : {
4775 : /*
4776 : * Unique-ify the new child column name and assign, unless we're
4777 : * in an unnamed join, in which case just copy
4778 : */
4779 168 : if (rte->alias != NULL)
4780 : {
4781 48 : colinfo->new_colnames[j] =
4782 24 : make_colname_unique(child_colname, dpns, colinfo);
4783 24 : if (!changed_any &&
4784 24 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4785 12 : changed_any = true;
4786 : }
4787 : else
4788 144 : colinfo->new_colnames[j] = child_colname;
4789 168 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
4790 : }
4791 :
4792 14312 : colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
4793 14312 : j++;
4794 : }
4795 :
4796 : /* Assert we processed the right number of columns */
4797 : #ifdef USE_ASSERT_CHECKING
4798 : while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
4799 : i++;
4800 : Assert(i == colinfo->num_cols);
4801 : Assert(j == nnewcolumns);
4802 : #endif
4803 :
4804 : /* We're now done needing the colinfo's names_hash */
4805 1498 : destroy_colinfo_names_hash(colinfo);
4806 :
4807 : /*
4808 : * For a named join, print column aliases if we changed any from the child
4809 : * names. Unnamed joins cannot print aliases.
4810 : */
4811 1498 : if (rte->alias != NULL)
4812 108 : colinfo->printaliases = changed_any;
4813 : else
4814 1390 : colinfo->printaliases = false;
4815 1498 : }
4816 :
4817 : /*
4818 : * colname_is_unique: is colname distinct from already-chosen column names?
4819 : *
4820 : * dpns is query-wide info, colinfo is for the column's RTE
4821 : */
4822 : static bool
4823 499668 : colname_is_unique(const char *colname, deparse_namespace *dpns,
4824 : deparse_columns *colinfo)
4825 : {
4826 : int i;
4827 : ListCell *lc;
4828 :
4829 : /*
4830 : * If we have a hash table, consult that instead of linearly scanning the
4831 : * colinfo's strings.
4832 : */
4833 499668 : if (colinfo->names_hash)
4834 : {
4835 41618 : if (hash_search(colinfo->names_hash,
4836 : colname,
4837 : HASH_FIND,
4838 : NULL) != NULL)
4839 9492 : return false;
4840 : }
4841 : else
4842 : {
4843 : /* Check against already-assigned column aliases within RTE */
4844 6270838 : for (i = 0; i < colinfo->num_cols; i++)
4845 : {
4846 5830182 : char *oldname = colinfo->colnames[i];
4847 :
4848 5830182 : if (oldname && strcmp(oldname, colname) == 0)
4849 17394 : return false;
4850 : }
4851 :
4852 : /*
4853 : * If we're building a new_colnames array, check that too (this will
4854 : * be partially but not completely redundant with the previous checks)
4855 : */
4856 441928 : for (i = 0; i < colinfo->num_new_cols; i++)
4857 : {
4858 1296 : char *oldname = colinfo->new_colnames[i];
4859 :
4860 1296 : if (oldname && strcmp(oldname, colname) == 0)
4861 24 : return false;
4862 : }
4863 :
4864 : /*
4865 : * Also check against names already assigned for parent-join USING
4866 : * cols
4867 : */
4868 443224 : foreach(lc, colinfo->parentUsing)
4869 : {
4870 2598 : char *oldname = (char *) lfirst(lc);
4871 :
4872 2598 : if (strcmp(oldname, colname) == 0)
4873 6 : return false;
4874 : }
4875 : }
4876 :
4877 : /*
4878 : * Also check against USING-column names that must be globally unique.
4879 : * These are not hashed, but there should be few of them.
4880 : */
4881 473592 : foreach(lc, dpns->using_names)
4882 : {
4883 882 : char *oldname = (char *) lfirst(lc);
4884 :
4885 882 : if (strcmp(oldname, colname) == 0)
4886 42 : return false;
4887 : }
4888 :
4889 472710 : return true;
4890 : }
4891 :
4892 : /*
4893 : * make_colname_unique: modify colname if necessary to make it unique
4894 : *
4895 : * dpns is query-wide info, colinfo is for the column's RTE
4896 : */
4897 : static char *
4898 472710 : make_colname_unique(char *colname, deparse_namespace *dpns,
4899 : deparse_columns *colinfo)
4900 : {
4901 : /*
4902 : * If the selected name isn't unique, append digits to make it so. For a
4903 : * very long input name, we might have to truncate to stay within
4904 : * NAMEDATALEN.
4905 : */
4906 472710 : if (!colname_is_unique(colname, dpns, colinfo))
4907 : {
4908 22326 : int colnamelen = strlen(colname);
4909 22326 : char *modname = (char *) palloc(colnamelen + 16);
4910 22326 : int i = 0;
4911 :
4912 : do
4913 : {
4914 26958 : i++;
4915 : for (;;)
4916 : {
4917 26958 : memcpy(modname, colname, colnamelen);
4918 26958 : sprintf(modname + colnamelen, "_%d", i);
4919 26958 : if (strlen(modname) < NAMEDATALEN)
4920 26958 : break;
4921 : /* drop chars from colname to keep all the digits */
4922 0 : colnamelen = pg_mbcliplen(colname, colnamelen,
4923 : colnamelen - 1);
4924 : }
4925 26958 : } while (!colname_is_unique(modname, dpns, colinfo));
4926 22326 : colname = modname;
4927 : }
4928 472710 : return colname;
4929 : }
4930 :
4931 : /*
4932 : * expand_colnames_array_to: make colinfo->colnames at least n items long
4933 : *
4934 : * Any added array entries are initialized to zero.
4935 : */
4936 : static void
4937 92804 : expand_colnames_array_to(deparse_columns *colinfo, int n)
4938 : {
4939 92804 : if (n > colinfo->num_cols)
4940 : {
4941 90242 : if (colinfo->colnames == NULL)
4942 88826 : colinfo->colnames = palloc0_array(char *, n);
4943 : else
4944 1416 : colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
4945 90242 : colinfo->num_cols = n;
4946 : }
4947 92804 : }
4948 :
4949 : /*
4950 : * build_colinfo_names_hash: optionally construct a hash table for colinfo
4951 : */
4952 : static void
4953 91004 : build_colinfo_names_hash(deparse_columns *colinfo)
4954 : {
4955 : HASHCTL hash_ctl;
4956 : int i;
4957 : ListCell *lc;
4958 :
4959 : /*
4960 : * Use a hash table only for RTEs with at least 32 columns. (The cutoff
4961 : * is somewhat arbitrary, but let's choose it so that this code does get
4962 : * exercised in the regression tests.)
4963 : */
4964 91004 : if (colinfo->num_cols < 32)
4965 89288 : return;
4966 :
4967 : /*
4968 : * Set up the hash table. The entries are just strings with no other
4969 : * payload.
4970 : */
4971 1716 : hash_ctl.keysize = NAMEDATALEN;
4972 1716 : hash_ctl.entrysize = NAMEDATALEN;
4973 1716 : hash_ctl.hcxt = CurrentMemoryContext;
4974 3432 : colinfo->names_hash = hash_create("deparse_columns names",
4975 1716 : colinfo->num_cols + colinfo->num_new_cols,
4976 : &hash_ctl,
4977 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
4978 :
4979 : /*
4980 : * Preload the hash table with any names already present (these would have
4981 : * come from set_using_names).
4982 : */
4983 76346 : for (i = 0; i < colinfo->num_cols; i++)
4984 : {
4985 74630 : char *oldname = colinfo->colnames[i];
4986 :
4987 74630 : if (oldname)
4988 0 : add_to_names_hash(colinfo, oldname);
4989 : }
4990 :
4991 1716 : for (i = 0; i < colinfo->num_new_cols; i++)
4992 : {
4993 0 : char *oldname = colinfo->new_colnames[i];
4994 :
4995 0 : if (oldname)
4996 0 : add_to_names_hash(colinfo, oldname);
4997 : }
4998 :
4999 1716 : foreach(lc, colinfo->parentUsing)
5000 : {
5001 0 : char *oldname = (char *) lfirst(lc);
5002 :
5003 0 : add_to_names_hash(colinfo, oldname);
5004 : }
5005 : }
5006 :
5007 : /*
5008 : * add_to_names_hash: add a string to the names_hash, if we're using one
5009 : */
5010 : static void
5011 519950 : add_to_names_hash(deparse_columns *colinfo, const char *name)
5012 : {
5013 519950 : if (colinfo->names_hash)
5014 74630 : (void) hash_search(colinfo->names_hash,
5015 : name,
5016 : HASH_ENTER,
5017 : NULL);
5018 519950 : }
5019 :
5020 : /*
5021 : * destroy_colinfo_names_hash: destroy hash table when done with it
5022 : */
5023 : static void
5024 91004 : destroy_colinfo_names_hash(deparse_columns *colinfo)
5025 : {
5026 91004 : if (colinfo->names_hash)
5027 : {
5028 1716 : hash_destroy(colinfo->names_hash);
5029 1716 : colinfo->names_hash = NULL;
5030 : }
5031 91004 : }
5032 :
5033 : /*
5034 : * identify_join_columns: figure out where columns of a join come from
5035 : *
5036 : * Fills the join-specific fields of the colinfo struct, except for
5037 : * usingNames which is filled later.
5038 : */
5039 : static void
5040 1498 : identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
5041 : deparse_columns *colinfo)
5042 : {
5043 : int numjoincols;
5044 : int jcolno;
5045 : int rcolno;
5046 : ListCell *lc;
5047 :
5048 : /* Extract left/right child RT indexes */
5049 1498 : if (IsA(j->larg, RangeTblRef))
5050 952 : colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
5051 546 : else if (IsA(j->larg, JoinExpr))
5052 546 : colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
5053 : else
5054 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5055 : (int) nodeTag(j->larg));
5056 1498 : if (IsA(j->rarg, RangeTblRef))
5057 1498 : colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
5058 0 : else if (IsA(j->rarg, JoinExpr))
5059 0 : colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
5060 : else
5061 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5062 : (int) nodeTag(j->rarg));
5063 :
5064 : /* Assert children will be processed earlier than join in second pass */
5065 : Assert(colinfo->leftrti < j->rtindex);
5066 : Assert(colinfo->rightrti < j->rtindex);
5067 :
5068 : /* Initialize result arrays with zeroes */
5069 1498 : numjoincols = list_length(jrte->joinaliasvars);
5070 : Assert(numjoincols == list_length(jrte->eref->colnames));
5071 1498 : colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
5072 1498 : colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
5073 :
5074 : /*
5075 : * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
5076 : * Recall that the column(s) merged due to USING are the first column(s)
5077 : * of the join output. We need not do anything special while scanning
5078 : * joinleftcols, but while scanning joinrightcols we must distinguish
5079 : * merged from unmerged columns.
5080 : */
5081 1498 : jcolno = 0;
5082 35532 : foreach(lc, jrte->joinleftcols)
5083 : {
5084 34034 : int leftattno = lfirst_int(lc);
5085 :
5086 34034 : colinfo->leftattnos[jcolno++] = leftattno;
5087 : }
5088 1498 : rcolno = 0;
5089 16222 : foreach(lc, jrte->joinrightcols)
5090 : {
5091 14724 : int rightattno = lfirst_int(lc);
5092 :
5093 14724 : if (rcolno < jrte->joinmergedcols) /* merged column? */
5094 580 : colinfo->rightattnos[rcolno] = rightattno;
5095 : else
5096 14144 : colinfo->rightattnos[jcolno++] = rightattno;
5097 14724 : rcolno++;
5098 : }
5099 : Assert(jcolno == numjoincols);
5100 1498 : }
5101 :
5102 : /*
5103 : * get_rtable_name: convenience function to get a previously assigned RTE alias
5104 : *
5105 : * The RTE must belong to the topmost namespace level in "context".
5106 : */
5107 : static char *
5108 6508 : get_rtable_name(int rtindex, deparse_context *context)
5109 : {
5110 6508 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
5111 :
5112 : Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
5113 6508 : return (char *) list_nth(dpns->rtable_names, rtindex - 1);
5114 : }
5115 :
5116 : /*
5117 : * set_deparse_plan: set up deparse_namespace to parse subexpressions
5118 : * of a given Plan node
5119 : *
5120 : * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
5121 : * and index_tlist fields. Caller must already have adjusted the ancestors
5122 : * list if necessary. Note that the rtable, subplans, and ctes fields do
5123 : * not need to change when shifting attention to different plan nodes in a
5124 : * single plan tree.
5125 : */
5126 : static void
5127 112524 : set_deparse_plan(deparse_namespace *dpns, Plan *plan)
5128 : {
5129 112524 : dpns->plan = plan;
5130 :
5131 : /*
5132 : * We special-case Append and MergeAppend to pretend that the first child
5133 : * plan is the OUTER referent; we have to interpret OUTER Vars in their
5134 : * tlists according to one of the children, and the first one is the most
5135 : * natural choice.
5136 : */
5137 112524 : if (IsA(plan, Append))
5138 4030 : dpns->outer_plan = linitial(((Append *) plan)->appendplans);
5139 108494 : else if (IsA(plan, MergeAppend))
5140 480 : dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
5141 : else
5142 108014 : dpns->outer_plan = outerPlan(plan);
5143 :
5144 112524 : if (dpns->outer_plan)
5145 50796 : dpns->outer_tlist = dpns->outer_plan->targetlist;
5146 : else
5147 61728 : dpns->outer_tlist = NIL;
5148 :
5149 : /*
5150 : * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
5151 : * use OUTER because that could someday conflict with the normal meaning.)
5152 : * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
5153 : * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
5154 : * that as INNER referent.
5155 : *
5156 : * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
5157 : * INNER referent. This is the join from the target relation to the data
5158 : * source, and all INNER_VAR Vars in other parts of the query refer to its
5159 : * targetlist.
5160 : *
5161 : * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the
5162 : * excluded expression's tlist. (Similar to the SubqueryScan we don't want
5163 : * to reuse OUTER, it's used for RETURNING in some modify table cases,
5164 : * although not INSERT .. CONFLICT).
5165 : */
5166 112524 : if (IsA(plan, SubqueryScan))
5167 632 : dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5168 111892 : else if (IsA(plan, CteScan))
5169 552 : dpns->inner_plan = list_nth(dpns->subplans,
5170 552 : ((CteScan *) plan)->ctePlanId - 1);
5171 111340 : else if (IsA(plan, WorkTableScan))
5172 174 : dpns->inner_plan = find_recursive_union(dpns,
5173 : (WorkTableScan *) plan);
5174 111166 : else if (IsA(plan, ModifyTable))
5175 : {
5176 270 : if (((ModifyTable *) plan)->operation == CMD_MERGE)
5177 60 : dpns->inner_plan = outerPlan(plan);
5178 : else
5179 210 : dpns->inner_plan = plan;
5180 : }
5181 : else
5182 110896 : dpns->inner_plan = innerPlan(plan);
5183 :
5184 112524 : if (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)
5185 140 : dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
5186 112384 : else if (dpns->inner_plan)
5187 18680 : dpns->inner_tlist = dpns->inner_plan->targetlist;
5188 : else
5189 93704 : dpns->inner_tlist = NIL;
5190 :
5191 : /* Set up referent for INDEX_VAR Vars, if needed */
5192 112524 : if (IsA(plan, IndexOnlyScan))
5193 2858 : dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5194 109666 : else if (IsA(plan, ForeignScan))
5195 2744 : dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5196 106922 : else if (IsA(plan, CustomScan))
5197 0 : dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5198 : else
5199 106922 : dpns->index_tlist = NIL;
5200 112524 : }
5201 :
5202 : /*
5203 : * Locate the ancestor plan node that is the RecursiveUnion generating
5204 : * the WorkTableScan's work table. We can match on wtParam, since that
5205 : * should be unique within the plan tree.
5206 : */
5207 : static Plan *
5208 174 : find_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)
5209 : {
5210 : ListCell *lc;
5211 :
5212 438 : foreach(lc, dpns->ancestors)
5213 : {
5214 438 : Plan *ancestor = (Plan *) lfirst(lc);
5215 :
5216 438 : if (IsA(ancestor, RecursiveUnion) &&
5217 174 : ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5218 174 : return ancestor;
5219 : }
5220 0 : elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5221 : wtscan->wtParam);
5222 : return NULL;
5223 : }
5224 :
5225 : /*
5226 : * push_child_plan: temporarily transfer deparsing attention to a child plan
5227 : *
5228 : * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
5229 : * deparse context in case the referenced expression itself uses
5230 : * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
5231 : * affecting levelsup issues (although in a Plan tree there really shouldn't
5232 : * be any).
5233 : *
5234 : * Caller must provide a local deparse_namespace variable to save the
5235 : * previous state for pop_child_plan.
5236 : */
5237 : static void
5238 60768 : push_child_plan(deparse_namespace *dpns, Plan *plan,
5239 : deparse_namespace *save_dpns)
5240 : {
5241 : /* Save state for restoration later */
5242 60768 : *save_dpns = *dpns;
5243 :
5244 : /* Link current plan node into ancestors list */
5245 60768 : dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5246 :
5247 : /* Set attention on selected child */
5248 60768 : set_deparse_plan(dpns, plan);
5249 60768 : }
5250 :
5251 : /*
5252 : * pop_child_plan: undo the effects of push_child_plan
5253 : */
5254 : static void
5255 60768 : pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5256 : {
5257 : List *ancestors;
5258 :
5259 : /* Get rid of ancestors list cell added by push_child_plan */
5260 60768 : ancestors = list_delete_first(dpns->ancestors);
5261 :
5262 : /* Restore fields changed by push_child_plan */
5263 60768 : *dpns = *save_dpns;
5264 :
5265 : /* Make sure dpns->ancestors is right (may be unnecessary) */
5266 60768 : dpns->ancestors = ancestors;
5267 60768 : }
5268 :
5269 : /*
5270 : * push_ancestor_plan: temporarily transfer deparsing attention to an
5271 : * ancestor plan
5272 : *
5273 : * When expanding a Param reference, we must adjust the deparse context
5274 : * to match the plan node that contains the expression being printed;
5275 : * otherwise we'd fail if that expression itself contains a Param or
5276 : * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
5277 : *
5278 : * The target ancestor is conveniently identified by the ListCell holding it
5279 : * in dpns->ancestors.
5280 : *
5281 : * Caller must provide a local deparse_namespace variable to save the
5282 : * previous state for pop_ancestor_plan.
5283 : */
5284 : static void
5285 3760 : push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
5286 : deparse_namespace *save_dpns)
5287 : {
5288 3760 : Plan *plan = (Plan *) lfirst(ancestor_cell);
5289 :
5290 : /* Save state for restoration later */
5291 3760 : *save_dpns = *dpns;
5292 :
5293 : /* Build a new ancestor list with just this node's ancestors */
5294 3760 : dpns->ancestors =
5295 3760 : list_copy_tail(dpns->ancestors,
5296 3760 : list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5297 :
5298 : /* Set attention on selected ancestor */
5299 3760 : set_deparse_plan(dpns, plan);
5300 3760 : }
5301 :
5302 : /*
5303 : * pop_ancestor_plan: undo the effects of push_ancestor_plan
5304 : */
5305 : static void
5306 3760 : pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5307 : {
5308 : /* Free the ancestor list made in push_ancestor_plan */
5309 3760 : list_free(dpns->ancestors);
5310 :
5311 : /* Restore fields changed by push_ancestor_plan */
5312 3760 : *dpns = *save_dpns;
5313 3760 : }
5314 :
5315 :
5316 : /* ----------
5317 : * make_ruledef - reconstruct the CREATE RULE command
5318 : * for a given pg_rewrite tuple
5319 : * ----------
5320 : */
5321 : static void
5322 558 : make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5323 : int prettyFlags)
5324 : {
5325 : char *rulename;
5326 : char ev_type;
5327 : Oid ev_class;
5328 : bool is_instead;
5329 : char *ev_qual;
5330 : char *ev_action;
5331 : List *actions;
5332 : Relation ev_relation;
5333 558 : TupleDesc viewResultDesc = NULL;
5334 : int fno;
5335 : Datum dat;
5336 : bool isnull;
5337 :
5338 : /*
5339 : * Get the attribute values from the rules tuple
5340 : */
5341 558 : fno = SPI_fnumber(rulettc, "rulename");
5342 558 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5343 : Assert(!isnull);
5344 558 : rulename = NameStr(*(DatumGetName(dat)));
5345 :
5346 558 : fno = SPI_fnumber(rulettc, "ev_type");
5347 558 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5348 : Assert(!isnull);
5349 558 : ev_type = DatumGetChar(dat);
5350 :
5351 558 : fno = SPI_fnumber(rulettc, "ev_class");
5352 558 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5353 : Assert(!isnull);
5354 558 : ev_class = DatumGetObjectId(dat);
5355 :
5356 558 : fno = SPI_fnumber(rulettc, "is_instead");
5357 558 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5358 : Assert(!isnull);
5359 558 : is_instead = DatumGetBool(dat);
5360 :
5361 558 : fno = SPI_fnumber(rulettc, "ev_qual");
5362 558 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5363 : Assert(ev_qual != NULL);
5364 :
5365 558 : fno = SPI_fnumber(rulettc, "ev_action");
5366 558 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5367 : Assert(ev_action != NULL);
5368 558 : actions = (List *) stringToNode(ev_action);
5369 558 : if (actions == NIL)
5370 0 : elog(ERROR, "invalid empty ev_action list");
5371 :
5372 558 : ev_relation = table_open(ev_class, AccessShareLock);
5373 :
5374 : /*
5375 : * Build the rules definition text
5376 : */
5377 558 : appendStringInfo(buf, "CREATE RULE %s AS",
5378 : quote_identifier(rulename));
5379 :
5380 558 : if (prettyFlags & PRETTYFLAG_INDENT)
5381 558 : appendStringInfoString(buf, "\n ON ");
5382 : else
5383 0 : appendStringInfoString(buf, " ON ");
5384 :
5385 : /* The event the rule is fired for */
5386 558 : switch (ev_type)
5387 : {
5388 6 : case '1':
5389 6 : appendStringInfoString(buf, "SELECT");
5390 6 : viewResultDesc = RelationGetDescr(ev_relation);
5391 6 : break;
5392 :
5393 154 : case '2':
5394 154 : appendStringInfoString(buf, "UPDATE");
5395 154 : break;
5396 :
5397 294 : case '3':
5398 294 : appendStringInfoString(buf, "INSERT");
5399 294 : break;
5400 :
5401 104 : case '4':
5402 104 : appendStringInfoString(buf, "DELETE");
5403 104 : break;
5404 :
5405 0 : default:
5406 0 : ereport(ERROR,
5407 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5408 : errmsg("rule \"%s\" has unsupported event type %d",
5409 : rulename, ev_type)));
5410 : break;
5411 : }
5412 :
5413 : /* The relation the rule is fired on */
5414 558 : appendStringInfo(buf, " TO %s",
5415 558 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
5416 114 : generate_relation_name(ev_class, NIL) :
5417 444 : generate_qualified_relation_name(ev_class));
5418 :
5419 : /* If the rule has an event qualification, add it */
5420 558 : if (strcmp(ev_qual, "<>") != 0)
5421 : {
5422 : Node *qual;
5423 : Query *query;
5424 : deparse_context context;
5425 : deparse_namespace dpns;
5426 :
5427 122 : if (prettyFlags & PRETTYFLAG_INDENT)
5428 122 : appendStringInfoString(buf, "\n ");
5429 122 : appendStringInfoString(buf, " WHERE ");
5430 :
5431 122 : qual = stringToNode(ev_qual);
5432 :
5433 : /*
5434 : * We need to make a context for recognizing any Vars in the qual
5435 : * (which can only be references to OLD and NEW). Use the rtable of
5436 : * the first query in the action list for this purpose.
5437 : */
5438 122 : query = (Query *) linitial(actions);
5439 :
5440 : /*
5441 : * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5442 : * into the SELECT, and that's what we need to look at. (Ugly kluge
5443 : * ... try to fix this when we redesign querytrees.)
5444 : */
5445 122 : query = getInsertSelectQuery(query, NULL);
5446 :
5447 : /* Must acquire locks right away; see notes in get_query_def() */
5448 122 : AcquireRewriteLocks(query, false, false);
5449 :
5450 122 : context.buf = buf;
5451 122 : context.namespaces = list_make1(&dpns);
5452 122 : context.resultDesc = NULL;
5453 122 : context.targetList = NIL;
5454 122 : context.windowClause = NIL;
5455 122 : context.varprefix = (list_length(query->rtable) != 1);
5456 122 : context.prettyFlags = prettyFlags;
5457 122 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
5458 122 : context.indentLevel = PRETTYINDENT_STD;
5459 122 : context.colNamesVisible = true;
5460 122 : context.inGroupBy = false;
5461 122 : context.varInOrderBy = false;
5462 122 : context.appendparents = NULL;
5463 :
5464 122 : set_deparse_for_query(&dpns, query, NIL);
5465 :
5466 122 : get_rule_expr(qual, &context, false);
5467 : }
5468 :
5469 558 : appendStringInfoString(buf, " DO ");
5470 :
5471 : /* The INSTEAD keyword (if so) */
5472 558 : if (is_instead)
5473 330 : appendStringInfoString(buf, "INSTEAD ");
5474 :
5475 : /* Finally the rules actions */
5476 558 : if (list_length(actions) > 1)
5477 : {
5478 : ListCell *action;
5479 : Query *query;
5480 :
5481 20 : appendStringInfoChar(buf, '(');
5482 60 : foreach(action, actions)
5483 : {
5484 40 : query = (Query *) lfirst(action);
5485 40 : get_query_def(query, buf, NIL, viewResultDesc, true,
5486 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5487 40 : if (prettyFlags)
5488 40 : appendStringInfoString(buf, ";\n");
5489 : else
5490 0 : appendStringInfoString(buf, "; ");
5491 : }
5492 20 : appendStringInfoString(buf, ");");
5493 : }
5494 : else
5495 : {
5496 : Query *query;
5497 :
5498 538 : query = (Query *) linitial(actions);
5499 538 : get_query_def(query, buf, NIL, viewResultDesc, true,
5500 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5501 538 : appendStringInfoChar(buf, ';');
5502 : }
5503 :
5504 558 : table_close(ev_relation, AccessShareLock);
5505 558 : }
5506 :
5507 :
5508 : /* ----------
5509 : * make_viewdef - reconstruct the SELECT part of a
5510 : * view rewrite rule
5511 : * ----------
5512 : */
5513 : static void
5514 3402 : make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5515 : int prettyFlags, int wrapColumn)
5516 : {
5517 : Query *query;
5518 : char ev_type;
5519 : Oid ev_class;
5520 : bool is_instead;
5521 : char *ev_qual;
5522 : char *ev_action;
5523 : List *actions;
5524 : Relation ev_relation;
5525 : int fno;
5526 : Datum dat;
5527 : bool isnull;
5528 :
5529 : /*
5530 : * Get the attribute values from the rules tuple
5531 : */
5532 3402 : fno = SPI_fnumber(rulettc, "ev_type");
5533 3402 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5534 : Assert(!isnull);
5535 3402 : ev_type = DatumGetChar(dat);
5536 :
5537 3402 : fno = SPI_fnumber(rulettc, "ev_class");
5538 3402 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5539 : Assert(!isnull);
5540 3402 : ev_class = DatumGetObjectId(dat);
5541 :
5542 3402 : fno = SPI_fnumber(rulettc, "is_instead");
5543 3402 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5544 : Assert(!isnull);
5545 3402 : is_instead = DatumGetBool(dat);
5546 :
5547 3402 : fno = SPI_fnumber(rulettc, "ev_qual");
5548 3402 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5549 : Assert(ev_qual != NULL);
5550 :
5551 3402 : fno = SPI_fnumber(rulettc, "ev_action");
5552 3402 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5553 : Assert(ev_action != NULL);
5554 3402 : actions = (List *) stringToNode(ev_action);
5555 :
5556 3402 : if (list_length(actions) != 1)
5557 : {
5558 : /* keep output buffer empty and leave */
5559 0 : return;
5560 : }
5561 :
5562 3402 : query = (Query *) linitial(actions);
5563 :
5564 3402 : if (ev_type != '1' || !is_instead ||
5565 3402 : strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5566 : {
5567 : /* keep output buffer empty and leave */
5568 0 : return;
5569 : }
5570 :
5571 3402 : ev_relation = table_open(ev_class, AccessShareLock);
5572 :
5573 3402 : get_query_def(query, buf, NIL, RelationGetDescr(ev_relation), true,
5574 : prettyFlags, wrapColumn, 0);
5575 3402 : appendStringInfoChar(buf, ';');
5576 :
5577 3402 : table_close(ev_relation, AccessShareLock);
5578 : }
5579 :
5580 :
5581 : /* ----------
5582 : * get_query_def - Parse back one query parsetree
5583 : *
5584 : * query: parsetree to be displayed
5585 : * buf: output text is appended to buf
5586 : * parentnamespace: list (initially empty) of outer-level deparse_namespace's
5587 : * resultDesc: if not NULL, the output tuple descriptor for the view
5588 : * represented by a SELECT query. We use the column names from it
5589 : * to label SELECT output columns, in preference to names in the query
5590 : * colNamesVisible: true if the surrounding context cares about the output
5591 : * column names at all (as, for example, an EXISTS() context does not);
5592 : * when false, we can suppress dummy column labels such as "?column?"
5593 : * prettyFlags: bitmask of PRETTYFLAG_XXX options
5594 : * wrapColumn: maximum line length, or -1 to disable wrapping
5595 : * startIndent: initial indentation amount
5596 : * ----------
5597 : */
5598 : static void
5599 5500 : get_query_def(Query *query, StringInfo buf, List *parentnamespace,
5600 : TupleDesc resultDesc, bool colNamesVisible,
5601 : int prettyFlags, int wrapColumn, int startIndent)
5602 : {
5603 : deparse_context context;
5604 : deparse_namespace dpns;
5605 : int rtable_size;
5606 :
5607 : /* Guard against excessively long or deeply-nested queries */
5608 5500 : CHECK_FOR_INTERRUPTS();
5609 5500 : check_stack_depth();
5610 :
5611 11000 : rtable_size = query->hasGroupRTE ?
5612 5500 : list_length(query->rtable) - 1 :
5613 5282 : list_length(query->rtable);
5614 :
5615 : /*
5616 : * Replace any Vars in the query's targetlist and havingQual that
5617 : * reference GROUP outputs with the underlying grouping expressions.
5618 : */
5619 5500 : if (query->hasGroupRTE)
5620 : {
5621 218 : query->targetList = (List *)
5622 218 : flatten_group_exprs(NULL, query, (Node *) query->targetList);
5623 218 : query->havingQual =
5624 218 : flatten_group_exprs(NULL, query, query->havingQual);
5625 : }
5626 :
5627 : /*
5628 : * Before we begin to examine the query, acquire locks on referenced
5629 : * relations, and fix up deleted columns in JOIN RTEs. This ensures
5630 : * consistent results. Note we assume it's OK to scribble on the passed
5631 : * querytree!
5632 : *
5633 : * We are only deparsing the query (we are not about to execute it), so we
5634 : * only need AccessShareLock on the relations it mentions.
5635 : */
5636 5500 : AcquireRewriteLocks(query, false, false);
5637 :
5638 5500 : context.buf = buf;
5639 5500 : context.namespaces = lcons(&dpns, list_copy(parentnamespace));
5640 5500 : context.resultDesc = NULL;
5641 5500 : context.targetList = NIL;
5642 5500 : context.windowClause = NIL;
5643 5500 : context.varprefix = (parentnamespace != NIL ||
5644 : rtable_size != 1);
5645 5500 : context.prettyFlags = prettyFlags;
5646 5500 : context.wrapColumn = wrapColumn;
5647 5500 : context.indentLevel = startIndent;
5648 5500 : context.colNamesVisible = colNamesVisible;
5649 5500 : context.inGroupBy = false;
5650 5500 : context.varInOrderBy = false;
5651 5500 : context.appendparents = NULL;
5652 :
5653 5500 : set_deparse_for_query(&dpns, query, parentnamespace);
5654 :
5655 5500 : switch (query->commandType)
5656 : {
5657 4884 : case CMD_SELECT:
5658 : /* We set context.resultDesc only if it's a SELECT */
5659 4884 : context.resultDesc = resultDesc;
5660 4884 : get_select_query_def(query, &context);
5661 4884 : break;
5662 :
5663 130 : case CMD_UPDATE:
5664 130 : get_update_query_def(query, &context);
5665 130 : break;
5666 :
5667 340 : case CMD_INSERT:
5668 340 : get_insert_query_def(query, &context);
5669 340 : break;
5670 :
5671 76 : case CMD_DELETE:
5672 76 : get_delete_query_def(query, &context);
5673 76 : break;
5674 :
5675 12 : case CMD_MERGE:
5676 12 : get_merge_query_def(query, &context);
5677 12 : break;
5678 :
5679 42 : case CMD_NOTHING:
5680 42 : appendStringInfoString(buf, "NOTHING");
5681 42 : break;
5682 :
5683 16 : case CMD_UTILITY:
5684 16 : get_utility_query_def(query, &context);
5685 16 : break;
5686 :
5687 0 : default:
5688 0 : elog(ERROR, "unrecognized query command type: %d",
5689 : query->commandType);
5690 : break;
5691 : }
5692 5500 : }
5693 :
5694 : /* ----------
5695 : * get_values_def - Parse back a VALUES list
5696 : * ----------
5697 : */
5698 : static void
5699 272 : get_values_def(List *values_lists, deparse_context *context)
5700 : {
5701 272 : StringInfo buf = context->buf;
5702 272 : bool first_list = true;
5703 : ListCell *vtl;
5704 :
5705 272 : appendStringInfoString(buf, "VALUES ");
5706 :
5707 778 : foreach(vtl, values_lists)
5708 : {
5709 506 : List *sublist = (List *) lfirst(vtl);
5710 506 : bool first_col = true;
5711 : ListCell *lc;
5712 :
5713 506 : if (first_list)
5714 272 : first_list = false;
5715 : else
5716 234 : appendStringInfoString(buf, ", ");
5717 :
5718 506 : appendStringInfoChar(buf, '(');
5719 1958 : foreach(lc, sublist)
5720 : {
5721 1452 : Node *col = (Node *) lfirst(lc);
5722 :
5723 1452 : if (first_col)
5724 506 : first_col = false;
5725 : else
5726 946 : appendStringInfoChar(buf, ',');
5727 :
5728 : /*
5729 : * Print the value. Whole-row Vars need special treatment.
5730 : */
5731 1452 : get_rule_expr_toplevel(col, context, false);
5732 : }
5733 506 : appendStringInfoChar(buf, ')');
5734 : }
5735 272 : }
5736 :
5737 : /* ----------
5738 : * get_with_clause - Parse back a WITH clause
5739 : * ----------
5740 : */
5741 : static void
5742 5442 : get_with_clause(Query *query, deparse_context *context)
5743 : {
5744 5442 : StringInfo buf = context->buf;
5745 : const char *sep;
5746 : ListCell *l;
5747 :
5748 5442 : if (query->cteList == NIL)
5749 5352 : return;
5750 :
5751 90 : if (PRETTY_INDENT(context))
5752 : {
5753 90 : context->indentLevel += PRETTYINDENT_STD;
5754 90 : appendStringInfoChar(buf, ' ');
5755 : }
5756 :
5757 90 : if (query->hasRecursive)
5758 56 : sep = "WITH RECURSIVE ";
5759 : else
5760 34 : sep = "WITH ";
5761 218 : foreach(l, query->cteList)
5762 : {
5763 128 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
5764 :
5765 128 : appendStringInfoString(buf, sep);
5766 128 : appendStringInfoString(buf, quote_identifier(cte->ctename));
5767 128 : if (cte->aliascolnames)
5768 : {
5769 56 : bool first = true;
5770 : ListCell *col;
5771 :
5772 56 : appendStringInfoChar(buf, '(');
5773 148 : foreach(col, cte->aliascolnames)
5774 : {
5775 92 : if (first)
5776 56 : first = false;
5777 : else
5778 36 : appendStringInfoString(buf, ", ");
5779 92 : appendStringInfoString(buf,
5780 92 : quote_identifier(strVal(lfirst(col))));
5781 : }
5782 56 : appendStringInfoChar(buf, ')');
5783 : }
5784 128 : appendStringInfoString(buf, " AS ");
5785 128 : switch (cte->ctematerialized)
5786 : {
5787 110 : case CTEMaterializeDefault:
5788 110 : break;
5789 18 : case CTEMaterializeAlways:
5790 18 : appendStringInfoString(buf, "MATERIALIZED ");
5791 18 : break;
5792 0 : case CTEMaterializeNever:
5793 0 : appendStringInfoString(buf, "NOT MATERIALIZED ");
5794 0 : break;
5795 : }
5796 128 : appendStringInfoChar(buf, '(');
5797 128 : if (PRETTY_INDENT(context))
5798 128 : appendContextKeyword(context, "", 0, 0, 0);
5799 128 : get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
5800 : true,
5801 : context->prettyFlags, context->wrapColumn,
5802 : context->indentLevel);
5803 128 : if (PRETTY_INDENT(context))
5804 128 : appendContextKeyword(context, "", 0, 0, 0);
5805 128 : appendStringInfoChar(buf, ')');
5806 :
5807 128 : if (cte->search_clause)
5808 : {
5809 6 : bool first = true;
5810 : ListCell *lc;
5811 :
5812 6 : appendStringInfo(buf, " SEARCH %s FIRST BY ",
5813 6 : cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
5814 :
5815 18 : foreach(lc, cte->search_clause->search_col_list)
5816 : {
5817 12 : if (first)
5818 6 : first = false;
5819 : else
5820 6 : appendStringInfoString(buf, ", ");
5821 12 : appendStringInfoString(buf,
5822 12 : quote_identifier(strVal(lfirst(lc))));
5823 : }
5824 :
5825 6 : appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
5826 : }
5827 :
5828 128 : if (cte->cycle_clause)
5829 : {
5830 12 : bool first = true;
5831 : ListCell *lc;
5832 :
5833 12 : appendStringInfoString(buf, " CYCLE ");
5834 :
5835 36 : foreach(lc, cte->cycle_clause->cycle_col_list)
5836 : {
5837 24 : if (first)
5838 12 : first = false;
5839 : else
5840 12 : appendStringInfoString(buf, ", ");
5841 24 : appendStringInfoString(buf,
5842 24 : quote_identifier(strVal(lfirst(lc))));
5843 : }
5844 :
5845 12 : appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
5846 :
5847 : {
5848 12 : Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
5849 12 : Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
5850 :
5851 18 : if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
5852 6 : cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
5853 : {
5854 6 : appendStringInfoString(buf, " TO ");
5855 6 : get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
5856 6 : appendStringInfoString(buf, " DEFAULT ");
5857 6 : get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
5858 : }
5859 : }
5860 :
5861 12 : appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
5862 : }
5863 :
5864 128 : sep = ", ";
5865 : }
5866 :
5867 90 : if (PRETTY_INDENT(context))
5868 : {
5869 90 : context->indentLevel -= PRETTYINDENT_STD;
5870 90 : appendContextKeyword(context, "", 0, 0, 0);
5871 : }
5872 : else
5873 0 : appendStringInfoChar(buf, ' ');
5874 : }
5875 :
5876 : /* ----------
5877 : * get_select_query_def - Parse back a SELECT parsetree
5878 : * ----------
5879 : */
5880 : static void
5881 4884 : get_select_query_def(Query *query, deparse_context *context)
5882 : {
5883 4884 : StringInfo buf = context->buf;
5884 : bool force_colno;
5885 : ListCell *l;
5886 :
5887 : /* Insert the WITH clause if given */
5888 4884 : get_with_clause(query, context);
5889 :
5890 : /* Subroutines may need to consult the SELECT targetlist and windowClause */
5891 4884 : context->targetList = query->targetList;
5892 4884 : context->windowClause = query->windowClause;
5893 :
5894 : /*
5895 : * If the Query node has a setOperations tree, then it's the top level of
5896 : * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
5897 : * fields are interesting in the top query itself.
5898 : */
5899 4884 : if (query->setOperations)
5900 : {
5901 152 : get_setop_query(query->setOperations, query, context);
5902 : /* ORDER BY clauses must be simple in this case */
5903 152 : force_colno = true;
5904 : }
5905 : else
5906 : {
5907 4732 : get_basic_select_query(query, context);
5908 4732 : force_colno = false;
5909 : }
5910 :
5911 : /* Add the ORDER BY clause if given */
5912 4884 : if (query->sortClause != NIL)
5913 : {
5914 152 : appendContextKeyword(context, " ORDER BY ",
5915 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5916 152 : get_rule_orderby(query->sortClause, query->targetList,
5917 : force_colno, context);
5918 : }
5919 :
5920 : /*
5921 : * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
5922 : * standard spelling of LIMIT.
5923 : */
5924 4884 : if (query->limitOffset != NULL)
5925 : {
5926 32 : appendContextKeyword(context, " OFFSET ",
5927 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5928 32 : get_rule_expr(query->limitOffset, context, false);
5929 : }
5930 4884 : if (query->limitCount != NULL)
5931 : {
5932 70 : if (query->limitOption == LIMIT_OPTION_WITH_TIES)
5933 : {
5934 32 : appendContextKeyword(context, " FETCH FIRST ",
5935 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5936 32 : get_rule_expr(query->limitCount, context, false);
5937 32 : appendStringInfoString(buf, " ROWS WITH TIES");
5938 : }
5939 : else
5940 : {
5941 38 : appendContextKeyword(context, " LIMIT ",
5942 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5943 38 : if (IsA(query->limitCount, Const) &&
5944 16 : ((Const *) query->limitCount)->constisnull)
5945 16 : appendStringInfoString(buf, "ALL");
5946 : else
5947 22 : get_rule_expr(query->limitCount, context, false);
5948 : }
5949 : }
5950 :
5951 : /* Add FOR [KEY] UPDATE/SHARE clauses if present */
5952 4884 : if (query->hasForUpdate)
5953 : {
5954 12 : foreach(l, query->rowMarks)
5955 : {
5956 6 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
5957 :
5958 : /* don't print implicit clauses */
5959 6 : if (rc->pushedDown)
5960 0 : continue;
5961 :
5962 6 : switch (rc->strength)
5963 : {
5964 0 : case LCS_NONE:
5965 : /* we intentionally throw an error for LCS_NONE */
5966 0 : elog(ERROR, "unrecognized LockClauseStrength %d",
5967 : (int) rc->strength);
5968 : break;
5969 0 : case LCS_FORKEYSHARE:
5970 0 : appendContextKeyword(context, " FOR KEY SHARE",
5971 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5972 0 : break;
5973 0 : case LCS_FORSHARE:
5974 0 : appendContextKeyword(context, " FOR SHARE",
5975 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5976 0 : break;
5977 0 : case LCS_FORNOKEYUPDATE:
5978 0 : appendContextKeyword(context, " FOR NO KEY UPDATE",
5979 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5980 0 : break;
5981 6 : case LCS_FORUPDATE:
5982 6 : appendContextKeyword(context, " FOR UPDATE",
5983 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5984 6 : break;
5985 : }
5986 :
5987 6 : appendStringInfo(buf, " OF %s",
5988 6 : quote_identifier(get_rtable_name(rc->rti,
5989 : context)));
5990 6 : if (rc->waitPolicy == LockWaitError)
5991 0 : appendStringInfoString(buf, " NOWAIT");
5992 6 : else if (rc->waitPolicy == LockWaitSkip)
5993 0 : appendStringInfoString(buf, " SKIP LOCKED");
5994 : }
5995 : }
5996 4884 : }
5997 :
5998 : /*
5999 : * Detect whether query looks like SELECT ... FROM VALUES(),
6000 : * with no need to rename the output columns of the VALUES RTE.
6001 : * If so, return the VALUES RTE. Otherwise return NULL.
6002 : */
6003 : static RangeTblEntry *
6004 4732 : get_simple_values_rte(Query *query, TupleDesc resultDesc)
6005 : {
6006 4732 : RangeTblEntry *result = NULL;
6007 : ListCell *lc;
6008 :
6009 : /*
6010 : * We want to detect a match even if the Query also contains OLD or NEW
6011 : * rule RTEs. So the idea is to scan the rtable and see if there is only
6012 : * one inFromCl RTE that is a VALUES RTE.
6013 : */
6014 5104 : foreach(lc, query->rtable)
6015 : {
6016 4340 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
6017 :
6018 4340 : if (rte->rtekind == RTE_VALUES && rte->inFromCl)
6019 : {
6020 228 : if (result)
6021 3968 : return NULL; /* multiple VALUES (probably not possible) */
6022 228 : result = rte;
6023 : }
6024 4112 : else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
6025 144 : continue; /* ignore rule entries */
6026 : else
6027 3968 : return NULL; /* something else -> not simple VALUES */
6028 : }
6029 :
6030 : /*
6031 : * We don't need to check the targetlist in any great detail, because
6032 : * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
6033 : * appear inside auto-generated sub-queries with very restricted
6034 : * structure. However, DefineView might have modified the tlist by
6035 : * injecting new column aliases, or we might have some other column
6036 : * aliases forced by a resultDesc. We can only simplify if the RTE's
6037 : * column names match the names that get_target_list() would select.
6038 : */
6039 764 : if (result)
6040 : {
6041 : ListCell *lcn;
6042 : int colno;
6043 :
6044 228 : if (list_length(query->targetList) != list_length(result->eref->colnames))
6045 0 : return NULL; /* this probably cannot happen */
6046 228 : colno = 0;
6047 842 : forboth(lc, query->targetList, lcn, result->eref->colnames)
6048 : {
6049 626 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
6050 626 : char *cname = strVal(lfirst(lcn));
6051 : char *colname;
6052 :
6053 626 : if (tle->resjunk)
6054 12 : return NULL; /* this probably cannot happen */
6055 :
6056 : /* compute name that get_target_list would use for column */
6057 626 : colno++;
6058 626 : if (resultDesc && colno <= resultDesc->natts)
6059 30 : colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6060 : else
6061 596 : colname = tle->resname;
6062 :
6063 : /* does it match the VALUES RTE? */
6064 626 : if (colname == NULL || strcmp(colname, cname) != 0)
6065 12 : return NULL; /* column name has been changed */
6066 : }
6067 : }
6068 :
6069 752 : return result;
6070 : }
6071 :
6072 : static void
6073 4732 : get_basic_select_query(Query *query, deparse_context *context)
6074 : {
6075 4732 : StringInfo buf = context->buf;
6076 : RangeTblEntry *values_rte;
6077 : char *sep;
6078 : ListCell *l;
6079 :
6080 4732 : if (PRETTY_INDENT(context))
6081 : {
6082 4686 : context->indentLevel += PRETTYINDENT_STD;
6083 4686 : appendStringInfoChar(buf, ' ');
6084 : }
6085 :
6086 : /*
6087 : * If the query looks like SELECT * FROM (VALUES ...), then print just the
6088 : * VALUES part. This reverses what transformValuesClause() did at parse
6089 : * time.
6090 : */
6091 4732 : values_rte = get_simple_values_rte(query, context->resultDesc);
6092 4732 : if (values_rte)
6093 : {
6094 216 : get_values_def(values_rte->values_lists, context);
6095 216 : return;
6096 : }
6097 :
6098 : /*
6099 : * Build up the query string - first we say SELECT
6100 : */
6101 4516 : if (query->isReturn)
6102 52 : appendStringInfoString(buf, "RETURN");
6103 : else
6104 4464 : appendStringInfoString(buf, "SELECT");
6105 :
6106 : /* Add the DISTINCT clause if given */
6107 4516 : if (query->distinctClause != NIL)
6108 : {
6109 0 : if (query->hasDistinctOn)
6110 : {
6111 0 : appendStringInfoString(buf, " DISTINCT ON (");
6112 0 : sep = "";
6113 0 : foreach(l, query->distinctClause)
6114 : {
6115 0 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6116 :
6117 0 : appendStringInfoString(buf, sep);
6118 0 : get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6119 : false, context);
6120 0 : sep = ", ";
6121 : }
6122 0 : appendStringInfoChar(buf, ')');
6123 : }
6124 : else
6125 0 : appendStringInfoString(buf, " DISTINCT");
6126 : }
6127 :
6128 : /* Then we tell what to select (the targetlist) */
6129 4516 : get_target_list(query->targetList, context);
6130 :
6131 : /* Add the FROM clause if needed */
6132 4516 : get_from_clause(query, " FROM ", context);
6133 :
6134 : /* Add the WHERE clause if given */
6135 4516 : if (query->jointree->quals != NULL)
6136 : {
6137 1406 : appendContextKeyword(context, " WHERE ",
6138 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6139 1406 : get_rule_expr(query->jointree->quals, context, false);
6140 : }
6141 :
6142 : /* Add the GROUP BY clause if given */
6143 4516 : if (query->groupClause != NULL || query->groupingSets != NULL)
6144 : {
6145 : bool save_ingroupby;
6146 :
6147 218 : appendContextKeyword(context, " GROUP BY ",
6148 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6149 218 : if (query->groupDistinct)
6150 0 : appendStringInfoString(buf, "DISTINCT ");
6151 :
6152 218 : save_ingroupby = context->inGroupBy;
6153 218 : context->inGroupBy = true;
6154 :
6155 218 : if (query->groupingSets == NIL)
6156 : {
6157 212 : sep = "";
6158 482 : foreach(l, query->groupClause)
6159 : {
6160 270 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6161 :
6162 270 : appendStringInfoString(buf, sep);
6163 270 : get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
6164 : false, context);
6165 270 : sep = ", ";
6166 : }
6167 : }
6168 : else
6169 : {
6170 6 : sep = "";
6171 12 : foreach(l, query->groupingSets)
6172 : {
6173 6 : GroupingSet *grp = lfirst(l);
6174 :
6175 6 : appendStringInfoString(buf, sep);
6176 6 : get_rule_groupingset(grp, query->targetList, true, context);
6177 6 : sep = ", ";
6178 : }
6179 : }
6180 :
6181 218 : context->inGroupBy = save_ingroupby;
6182 : }
6183 :
6184 : /* Add the HAVING clause if given */
6185 4516 : if (query->havingQual != NULL)
6186 : {
6187 10 : appendContextKeyword(context, " HAVING ",
6188 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6189 10 : get_rule_expr(query->havingQual, context, false);
6190 : }
6191 :
6192 : /* Add the WINDOW clause if needed */
6193 4516 : if (query->windowClause != NIL)
6194 42 : get_rule_windowclause(query, context);
6195 : }
6196 :
6197 : /* ----------
6198 : * get_target_list - Parse back a SELECT target list
6199 : *
6200 : * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.
6201 : * ----------
6202 : */
6203 : static void
6204 4638 : get_target_list(List *targetList, deparse_context *context)
6205 : {
6206 4638 : StringInfo buf = context->buf;
6207 : StringInfoData targetbuf;
6208 4638 : bool last_was_multiline = false;
6209 : char *sep;
6210 : int colno;
6211 : ListCell *l;
6212 :
6213 : /* we use targetbuf to hold each TLE's text temporarily */
6214 4638 : initStringInfo(&targetbuf);
6215 :
6216 4638 : sep = " ";
6217 4638 : colno = 0;
6218 23796 : foreach(l, targetList)
6219 : {
6220 19158 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6221 : char *colname;
6222 : char *attname;
6223 :
6224 19158 : if (tle->resjunk)
6225 34 : continue; /* ignore junk entries */
6226 :
6227 19124 : appendStringInfoString(buf, sep);
6228 19124 : sep = ", ";
6229 19124 : colno++;
6230 :
6231 : /*
6232 : * Put the new field text into targetbuf so we can decide after we've
6233 : * got it whether or not it needs to go on a new line.
6234 : */
6235 19124 : resetStringInfo(&targetbuf);
6236 19124 : context->buf = &targetbuf;
6237 :
6238 : /*
6239 : * We special-case Var nodes rather than using get_rule_expr. This is
6240 : * needed because get_rule_expr will display a whole-row Var as
6241 : * "foo.*", which is the preferred notation in most contexts, but at
6242 : * the top level of a SELECT list it's not right (the parser will
6243 : * expand that notation into multiple columns, yielding behavior
6244 : * different from a whole-row Var). We need to call get_variable
6245 : * directly so that we can tell it to do the right thing, and so that
6246 : * we can get the attribute name which is the default AS label.
6247 : */
6248 19124 : if (tle->expr && (IsA(tle->expr, Var)))
6249 : {
6250 14806 : attname = get_variable((Var *) tle->expr, 0, true, context);
6251 : }
6252 : else
6253 : {
6254 4318 : get_rule_expr((Node *) tle->expr, context, true);
6255 :
6256 : /*
6257 : * When colNamesVisible is true, we should always show the
6258 : * assigned column name explicitly. Otherwise, show it only if
6259 : * it's not FigureColname's fallback.
6260 : */
6261 4318 : attname = context->colNamesVisible ? NULL : "?column?";
6262 : }
6263 :
6264 : /*
6265 : * Figure out what the result column should be called. In the context
6266 : * of a view, use the view's tuple descriptor (so as to pick up the
6267 : * effects of any column RENAME that's been done on the view).
6268 : * Otherwise, just use what we can find in the TLE.
6269 : */
6270 19124 : if (context->resultDesc && colno <= context->resultDesc->natts)
6271 17584 : colname = NameStr(TupleDescAttr(context->resultDesc,
6272 : colno - 1)->attname);
6273 : else
6274 1540 : colname = tle->resname;
6275 :
6276 : /* Show AS unless the column's name is correct as-is */
6277 19124 : if (colname) /* resname could be NULL */
6278 : {
6279 19072 : if (attname == NULL || strcmp(attname, colname) != 0)
6280 6264 : appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
6281 : }
6282 :
6283 : /* Restore context's output buffer */
6284 19124 : context->buf = buf;
6285 :
6286 : /* Consider line-wrapping if enabled */
6287 19124 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
6288 : {
6289 : int leading_nl_pos;
6290 :
6291 : /* Does the new field start with a new line? */
6292 19078 : if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
6293 462 : leading_nl_pos = 0;
6294 : else
6295 18616 : leading_nl_pos = -1;
6296 :
6297 : /* If so, we shouldn't add anything */
6298 19078 : if (leading_nl_pos >= 0)
6299 : {
6300 : /* instead, remove any trailing spaces currently in buf */
6301 462 : removeStringInfoSpaces(buf);
6302 : }
6303 : else
6304 : {
6305 : char *trailing_nl;
6306 :
6307 : /* Locate the start of the current line in the output buffer */
6308 18616 : trailing_nl = strrchr(buf->data, '\n');
6309 18616 : if (trailing_nl == NULL)
6310 5660 : trailing_nl = buf->data;
6311 : else
6312 12956 : trailing_nl++;
6313 :
6314 : /*
6315 : * Add a newline, plus some indentation, if the new field is
6316 : * not the first and either the new field would cause an
6317 : * overflow or the last field used more than one line.
6318 : */
6319 18616 : if (colno > 1 &&
6320 14040 : ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
6321 : last_was_multiline))
6322 14040 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
6323 : PRETTYINDENT_STD, PRETTYINDENT_VAR);
6324 : }
6325 :
6326 : /* Remember this field's multiline status for next iteration */
6327 19078 : last_was_multiline =
6328 19078 : (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
6329 : }
6330 :
6331 : /* Add the new field */
6332 19124 : appendBinaryStringInfo(buf, targetbuf.data, targetbuf.len);
6333 : }
6334 :
6335 : /* clean up */
6336 4638 : pfree(targetbuf.data);
6337 4638 : }
6338 :
6339 : static void
6340 720 : get_setop_query(Node *setOp, Query *query, deparse_context *context)
6341 : {
6342 720 : StringInfo buf = context->buf;
6343 : bool need_paren;
6344 :
6345 : /* Guard against excessively long or deeply-nested queries */
6346 720 : CHECK_FOR_INTERRUPTS();
6347 720 : check_stack_depth();
6348 :
6349 720 : if (IsA(setOp, RangeTblRef))
6350 : {
6351 436 : RangeTblRef *rtr = (RangeTblRef *) setOp;
6352 436 : RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
6353 436 : Query *subquery = rte->subquery;
6354 :
6355 : Assert(subquery != NULL);
6356 :
6357 : /*
6358 : * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.
6359 : * Also add parens if the leaf query contains its own set operations.
6360 : * (That shouldn't happen unless one of the other clauses is also
6361 : * present, see transformSetOperationTree; but let's be safe.)
6362 : */
6363 1308 : need_paren = (subquery->cteList ||
6364 436 : subquery->sortClause ||
6365 436 : subquery->rowMarks ||
6366 436 : subquery->limitOffset ||
6367 1308 : subquery->limitCount ||
6368 436 : subquery->setOperations);
6369 436 : if (need_paren)
6370 0 : appendStringInfoChar(buf, '(');
6371 436 : get_query_def(subquery, buf, context->namespaces,
6372 436 : context->resultDesc, context->colNamesVisible,
6373 : context->prettyFlags, context->wrapColumn,
6374 : context->indentLevel);
6375 436 : if (need_paren)
6376 0 : appendStringInfoChar(buf, ')');
6377 : }
6378 284 : else if (IsA(setOp, SetOperationStmt))
6379 : {
6380 284 : SetOperationStmt *op = (SetOperationStmt *) setOp;
6381 : int subindent;
6382 : bool save_colnamesvisible;
6383 :
6384 : /*
6385 : * We force parens when nesting two SetOperationStmts, except when the
6386 : * lefthand input is another setop of the same kind. Syntactically,
6387 : * we could omit parens in rather more cases, but it seems best to use
6388 : * parens to flag cases where the setop operator changes. If we use
6389 : * parens, we also increase the indentation level for the child query.
6390 : *
6391 : * There are some cases in which parens are needed around a leaf query
6392 : * too, but those are more easily handled at the next level down (see
6393 : * code above).
6394 : */
6395 284 : if (IsA(op->larg, SetOperationStmt))
6396 : {
6397 132 : SetOperationStmt *lop = (SetOperationStmt *) op->larg;
6398 :
6399 132 : if (op->op == lop->op && op->all == lop->all)
6400 132 : need_paren = false;
6401 : else
6402 0 : need_paren = true;
6403 : }
6404 : else
6405 152 : need_paren = false;
6406 :
6407 284 : if (need_paren)
6408 : {
6409 0 : appendStringInfoChar(buf, '(');
6410 0 : subindent = PRETTYINDENT_STD;
6411 0 : appendContextKeyword(context, "", subindent, 0, 0);
6412 : }
6413 : else
6414 284 : subindent = 0;
6415 :
6416 284 : get_setop_query(op->larg, query, context);
6417 :
6418 284 : if (need_paren)
6419 0 : appendContextKeyword(context, ") ", -subindent, 0, 0);
6420 284 : else if (PRETTY_INDENT(context))
6421 284 : appendContextKeyword(context, "", -subindent, 0, 0);
6422 : else
6423 0 : appendStringInfoChar(buf, ' ');
6424 :
6425 284 : switch (op->op)
6426 : {
6427 284 : case SETOP_UNION:
6428 284 : appendStringInfoString(buf, "UNION ");
6429 284 : break;
6430 0 : case SETOP_INTERSECT:
6431 0 : appendStringInfoString(buf, "INTERSECT ");
6432 0 : break;
6433 0 : case SETOP_EXCEPT:
6434 0 : appendStringInfoString(buf, "EXCEPT ");
6435 0 : break;
6436 0 : default:
6437 0 : elog(ERROR, "unrecognized set op: %d",
6438 : (int) op->op);
6439 : }
6440 284 : if (op->all)
6441 272 : appendStringInfoString(buf, "ALL ");
6442 :
6443 : /* Always parenthesize if RHS is another setop */
6444 284 : need_paren = IsA(op->rarg, SetOperationStmt);
6445 :
6446 : /*
6447 : * The indentation code here is deliberately a bit different from that
6448 : * for the lefthand input, because we want the line breaks in
6449 : * different places.
6450 : */
6451 284 : if (need_paren)
6452 : {
6453 0 : appendStringInfoChar(buf, '(');
6454 0 : subindent = PRETTYINDENT_STD;
6455 : }
6456 : else
6457 284 : subindent = 0;
6458 284 : appendContextKeyword(context, "", subindent, 0, 0);
6459 :
6460 : /*
6461 : * The output column names of the RHS sub-select don't matter.
6462 : */
6463 284 : save_colnamesvisible = context->colNamesVisible;
6464 284 : context->colNamesVisible = false;
6465 :
6466 284 : get_setop_query(op->rarg, query, context);
6467 :
6468 284 : context->colNamesVisible = save_colnamesvisible;
6469 :
6470 284 : if (PRETTY_INDENT(context))
6471 284 : context->indentLevel -= subindent;
6472 284 : if (need_paren)
6473 0 : appendContextKeyword(context, ")", 0, 0, 0);
6474 : }
6475 : else
6476 : {
6477 0 : elog(ERROR, "unrecognized node type: %d",
6478 : (int) nodeTag(setOp));
6479 : }
6480 720 : }
6481 :
6482 : /*
6483 : * Display a sort/group clause.
6484 : *
6485 : * Also returns the expression tree, so caller need not find it again.
6486 : */
6487 : static Node *
6488 636 : get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
6489 : deparse_context *context)
6490 : {
6491 636 : StringInfo buf = context->buf;
6492 : TargetEntry *tle;
6493 : Node *expr;
6494 :
6495 636 : tle = get_sortgroupref_tle(ref, tlist);
6496 636 : expr = (Node *) tle->expr;
6497 :
6498 : /*
6499 : * Use column-number form if requested by caller. Otherwise, if
6500 : * expression is a constant, force it to be dumped with an explicit cast
6501 : * as decoration --- this is because a simple integer constant is
6502 : * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
6503 : * we dump it without any decoration. Similarly, if it's just a Var,
6504 : * there is risk of misinterpretation if the column name is reassigned in
6505 : * the SELECT list, so we may need to force table qualification. And, if
6506 : * it's anything more complex than a simple Var, then force extra parens
6507 : * around it, to ensure it can't be misinterpreted as a cube() or rollup()
6508 : * construct.
6509 : */
6510 636 : if (force_colno)
6511 : {
6512 : Assert(!tle->resjunk);
6513 0 : appendStringInfo(buf, "%d", tle->resno);
6514 : }
6515 636 : else if (!expr)
6516 : /* do nothing, probably can't happen */ ;
6517 636 : else if (IsA(expr, Const))
6518 0 : get_const_expr((Const *) expr, context, 1);
6519 636 : else if (IsA(expr, Var))
6520 : {
6521 : /* Tell get_variable to check for name conflict */
6522 608 : bool save_varinorderby = context->varInOrderBy;
6523 :
6524 608 : context->varInOrderBy = true;
6525 608 : (void) get_variable((Var *) expr, 0, false, context);
6526 608 : context->varInOrderBy = save_varinorderby;
6527 : }
6528 : else
6529 : {
6530 : /*
6531 : * We must force parens for function-like expressions even if
6532 : * PRETTY_PAREN is off, since those are the ones in danger of
6533 : * misparsing. For other expressions we need to force them only if
6534 : * PRETTY_PAREN is on, since otherwise the expression will output them
6535 : * itself. (We can't skip the parens.)
6536 : */
6537 56 : bool need_paren = (PRETTY_PAREN(context)
6538 28 : || IsA(expr, FuncExpr)
6539 24 : || IsA(expr, Aggref)
6540 24 : || IsA(expr, WindowFunc)
6541 56 : || IsA(expr, JsonConstructorExpr));
6542 :
6543 28 : if (need_paren)
6544 4 : appendStringInfoChar(context->buf, '(');
6545 28 : get_rule_expr(expr, context, true);
6546 28 : if (need_paren)
6547 4 : appendStringInfoChar(context->buf, ')');
6548 : }
6549 :
6550 636 : return expr;
6551 : }
6552 :
6553 : /*
6554 : * Display a GroupingSet
6555 : */
6556 : static void
6557 18 : get_rule_groupingset(GroupingSet *gset, List *targetlist,
6558 : bool omit_parens, deparse_context *context)
6559 : {
6560 : ListCell *l;
6561 18 : StringInfo buf = context->buf;
6562 18 : bool omit_child_parens = true;
6563 18 : char *sep = "";
6564 :
6565 18 : switch (gset->kind)
6566 : {
6567 0 : case GROUPING_SET_EMPTY:
6568 0 : appendStringInfoString(buf, "()");
6569 0 : return;
6570 :
6571 12 : case GROUPING_SET_SIMPLE:
6572 : {
6573 12 : if (!omit_parens || list_length(gset->content) != 1)
6574 12 : appendStringInfoChar(buf, '(');
6575 :
6576 42 : foreach(l, gset->content)
6577 : {
6578 30 : Index ref = lfirst_int(l);
6579 :
6580 30 : appendStringInfoString(buf, sep);
6581 30 : get_rule_sortgroupclause(ref, targetlist,
6582 : false, context);
6583 30 : sep = ", ";
6584 : }
6585 :
6586 12 : if (!omit_parens || list_length(gset->content) != 1)
6587 12 : appendStringInfoChar(buf, ')');
6588 : }
6589 12 : return;
6590 :
6591 6 : case GROUPING_SET_ROLLUP:
6592 6 : appendStringInfoString(buf, "ROLLUP(");
6593 6 : break;
6594 0 : case GROUPING_SET_CUBE:
6595 0 : appendStringInfoString(buf, "CUBE(");
6596 0 : break;
6597 0 : case GROUPING_SET_SETS:
6598 0 : appendStringInfoString(buf, "GROUPING SETS (");
6599 0 : omit_child_parens = false;
6600 0 : break;
6601 : }
6602 :
6603 18 : foreach(l, gset->content)
6604 : {
6605 12 : appendStringInfoString(buf, sep);
6606 12 : get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
6607 12 : sep = ", ";
6608 : }
6609 :
6610 6 : appendStringInfoChar(buf, ')');
6611 : }
6612 :
6613 : /*
6614 : * Display an ORDER BY list.
6615 : */
6616 : static void
6617 304 : get_rule_orderby(List *orderList, List *targetList,
6618 : bool force_colno, deparse_context *context)
6619 : {
6620 304 : StringInfo buf = context->buf;
6621 : const char *sep;
6622 : ListCell *l;
6623 :
6624 304 : sep = "";
6625 640 : foreach(l, orderList)
6626 : {
6627 336 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6628 : Node *sortexpr;
6629 : Oid sortcoltype;
6630 : TypeCacheEntry *typentry;
6631 :
6632 336 : appendStringInfoString(buf, sep);
6633 336 : sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
6634 : force_colno, context);
6635 336 : sortcoltype = exprType(sortexpr);
6636 : /* See whether operator is default < or > for datatype */
6637 336 : typentry = lookup_type_cache(sortcoltype,
6638 : TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
6639 336 : if (srt->sortop == typentry->lt_opr)
6640 : {
6641 : /* ASC is default, so emit nothing for it */
6642 308 : if (srt->nulls_first)
6643 0 : appendStringInfoString(buf, " NULLS FIRST");
6644 : }
6645 28 : else if (srt->sortop == typentry->gt_opr)
6646 : {
6647 10 : appendStringInfoString(buf, " DESC");
6648 : /* DESC defaults to NULLS FIRST */
6649 10 : if (!srt->nulls_first)
6650 2 : appendStringInfoString(buf, " NULLS LAST");
6651 : }
6652 : else
6653 : {
6654 18 : appendStringInfo(buf, " USING %s",
6655 : generate_operator_name(srt->sortop,
6656 : sortcoltype,
6657 : sortcoltype));
6658 : /* be specific to eliminate ambiguity */
6659 18 : if (srt->nulls_first)
6660 0 : appendStringInfoString(buf, " NULLS FIRST");
6661 : else
6662 18 : appendStringInfoString(buf, " NULLS LAST");
6663 : }
6664 336 : sep = ", ";
6665 : }
6666 304 : }
6667 :
6668 : /*
6669 : * Display a WINDOW clause.
6670 : *
6671 : * Note that the windowClause list might contain only anonymous window
6672 : * specifications, in which case we should print nothing here.
6673 : */
6674 : static void
6675 42 : get_rule_windowclause(Query *query, deparse_context *context)
6676 : {
6677 42 : StringInfo buf = context->buf;
6678 : const char *sep;
6679 : ListCell *l;
6680 :
6681 42 : sep = NULL;
6682 84 : foreach(l, query->windowClause)
6683 : {
6684 42 : WindowClause *wc = (WindowClause *) lfirst(l);
6685 :
6686 42 : if (wc->name == NULL)
6687 42 : continue; /* ignore anonymous windows */
6688 :
6689 0 : if (sep == NULL)
6690 0 : appendContextKeyword(context, " WINDOW ",
6691 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6692 : else
6693 0 : appendStringInfoString(buf, sep);
6694 :
6695 0 : appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
6696 :
6697 0 : get_rule_windowspec(wc, query->targetList, context);
6698 :
6699 0 : sep = ", ";
6700 : }
6701 42 : }
6702 :
6703 : /*
6704 : * Display a window definition
6705 : */
6706 : static void
6707 42 : get_rule_windowspec(WindowClause *wc, List *targetList,
6708 : deparse_context *context)
6709 : {
6710 42 : StringInfo buf = context->buf;
6711 42 : bool needspace = false;
6712 : const char *sep;
6713 : ListCell *l;
6714 :
6715 42 : appendStringInfoChar(buf, '(');
6716 42 : if (wc->refname)
6717 : {
6718 0 : appendStringInfoString(buf, quote_identifier(wc->refname));
6719 0 : needspace = true;
6720 : }
6721 : /* partition clauses are always inherited, so only print if no refname */
6722 42 : if (wc->partitionClause && !wc->refname)
6723 : {
6724 0 : if (needspace)
6725 0 : appendStringInfoChar(buf, ' ');
6726 0 : appendStringInfoString(buf, "PARTITION BY ");
6727 0 : sep = "";
6728 0 : foreach(l, wc->partitionClause)
6729 : {
6730 0 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6731 :
6732 0 : appendStringInfoString(buf, sep);
6733 0 : get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
6734 : false, context);
6735 0 : sep = ", ";
6736 : }
6737 0 : needspace = true;
6738 : }
6739 : /* print ordering clause only if not inherited */
6740 42 : if (wc->orderClause && !wc->copiedOrder)
6741 : {
6742 42 : if (needspace)
6743 0 : appendStringInfoChar(buf, ' ');
6744 42 : appendStringInfoString(buf, "ORDER BY ");
6745 42 : get_rule_orderby(wc->orderClause, targetList, false, context);
6746 42 : needspace = true;
6747 : }
6748 : /* framing clause is never inherited, so print unless it's default */
6749 42 : if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
6750 : {
6751 42 : if (needspace)
6752 42 : appendStringInfoChar(buf, ' ');
6753 42 : if (wc->frameOptions & FRAMEOPTION_RANGE)
6754 6 : appendStringInfoString(buf, "RANGE ");
6755 36 : else if (wc->frameOptions & FRAMEOPTION_ROWS)
6756 30 : appendStringInfoString(buf, "ROWS ");
6757 6 : else if (wc->frameOptions & FRAMEOPTION_GROUPS)
6758 6 : appendStringInfoString(buf, "GROUPS ");
6759 : else
6760 : Assert(false);
6761 42 : if (wc->frameOptions & FRAMEOPTION_BETWEEN)
6762 42 : appendStringInfoString(buf, "BETWEEN ");
6763 42 : if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
6764 0 : appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
6765 42 : else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW)
6766 0 : appendStringInfoString(buf, "CURRENT ROW ");
6767 42 : else if (wc->frameOptions & FRAMEOPTION_START_OFFSET)
6768 : {
6769 42 : get_rule_expr(wc->startOffset, context, false);
6770 42 : if (wc->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
6771 42 : appendStringInfoString(buf, " PRECEDING ");
6772 0 : else if (wc->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
6773 0 : appendStringInfoString(buf, " FOLLOWING ");
6774 : else
6775 : Assert(false);
6776 : }
6777 : else
6778 : Assert(false);
6779 42 : if (wc->frameOptions & FRAMEOPTION_BETWEEN)
6780 : {
6781 42 : appendStringInfoString(buf, "AND ");
6782 42 : if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
6783 0 : appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
6784 42 : else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW)
6785 0 : appendStringInfoString(buf, "CURRENT ROW ");
6786 42 : else if (wc->frameOptions & FRAMEOPTION_END_OFFSET)
6787 : {
6788 42 : get_rule_expr(wc->endOffset, context, false);
6789 42 : if (wc->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
6790 0 : appendStringInfoString(buf, " PRECEDING ");
6791 42 : else if (wc->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
6792 42 : appendStringInfoString(buf, " FOLLOWING ");
6793 : else
6794 : Assert(false);
6795 : }
6796 : else
6797 : Assert(false);
6798 : }
6799 42 : if (wc->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
6800 6 : appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
6801 36 : else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
6802 6 : appendStringInfoString(buf, "EXCLUDE GROUP ");
6803 30 : else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES)
6804 6 : appendStringInfoString(buf, "EXCLUDE TIES ");
6805 : /* we will now have a trailing space; remove it */
6806 42 : buf->len--;
6807 : }
6808 42 : appendStringInfoChar(buf, ')');
6809 42 : }
6810 :
6811 : /* ----------
6812 : * get_insert_query_def - Parse back an INSERT parsetree
6813 : * ----------
6814 : */
6815 : static void
6816 340 : get_insert_query_def(Query *query, deparse_context *context)
6817 : {
6818 340 : StringInfo buf = context->buf;
6819 340 : RangeTblEntry *select_rte = NULL;
6820 340 : RangeTblEntry *values_rte = NULL;
6821 : RangeTblEntry *rte;
6822 : char *sep;
6823 : ListCell *l;
6824 : List *strippedexprs;
6825 :
6826 : /* Insert the WITH clause if given */
6827 340 : get_with_clause(query, context);
6828 :
6829 : /*
6830 : * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
6831 : * single RTE for the SELECT or VALUES. Plain VALUES has neither.
6832 : */
6833 1322 : foreach(l, query->rtable)
6834 : {
6835 982 : rte = (RangeTblEntry *) lfirst(l);
6836 :
6837 982 : if (rte->rtekind == RTE_SUBQUERY)
6838 : {
6839 50 : if (select_rte)
6840 0 : elog(ERROR, "too many subquery RTEs in INSERT");
6841 50 : select_rte = rte;
6842 : }
6843 :
6844 982 : if (rte->rtekind == RTE_VALUES)
6845 : {
6846 44 : if (values_rte)
6847 0 : elog(ERROR, "too many values RTEs in INSERT");
6848 44 : values_rte = rte;
6849 : }
6850 : }
6851 340 : if (select_rte && values_rte)
6852 0 : elog(ERROR, "both subquery and values RTEs in INSERT");
6853 :
6854 : /*
6855 : * Start the query with INSERT INTO relname
6856 : */
6857 340 : rte = rt_fetch(query->resultRelation, query->rtable);
6858 : Assert(rte->rtekind == RTE_RELATION);
6859 :
6860 340 : if (PRETTY_INDENT(context))
6861 : {
6862 340 : context->indentLevel += PRETTYINDENT_STD;
6863 340 : appendStringInfoChar(buf, ' ');
6864 : }
6865 340 : appendStringInfo(buf, "INSERT INTO %s",
6866 : generate_relation_name(rte->relid, NIL));
6867 :
6868 : /* Print the relation alias, if needed; INSERT requires explicit AS */
6869 340 : get_rte_alias(rte, query->resultRelation, true, context);
6870 :
6871 : /* always want a space here */
6872 340 : appendStringInfoChar(buf, ' ');
6873 :
6874 : /*
6875 : * Add the insert-column-names list. Any indirection decoration needed on
6876 : * the column names can be inferred from the top targetlist.
6877 : */
6878 340 : strippedexprs = NIL;
6879 340 : sep = "";
6880 340 : if (query->targetList)
6881 340 : appendStringInfoChar(buf, '(');
6882 1242 : foreach(l, query->targetList)
6883 : {
6884 902 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6885 :
6886 902 : if (tle->resjunk)
6887 0 : continue; /* ignore junk entries */
6888 :
6889 902 : appendStringInfoString(buf, sep);
6890 902 : sep = ", ";
6891 :
6892 : /*
6893 : * Put out name of target column; look in the catalogs, not at
6894 : * tle->resname, since resname will fail to track RENAME.
6895 : */
6896 902 : appendStringInfoString(buf,
6897 902 : quote_identifier(get_attname(rte->relid,
6898 902 : tle->resno,
6899 : false)));
6900 :
6901 : /*
6902 : * Print any indirection needed (subfields or subscripts), and strip
6903 : * off the top-level nodes representing the indirection assignments.
6904 : * Add the stripped expressions to strippedexprs. (If it's a
6905 : * single-VALUES statement, the stripped expressions are the VALUES to
6906 : * print below. Otherwise they're just Vars and not really
6907 : * interesting.)
6908 : */
6909 902 : strippedexprs = lappend(strippedexprs,
6910 902 : processIndirection((Node *) tle->expr,
6911 : context));
6912 : }
6913 340 : if (query->targetList)
6914 340 : appendStringInfoString(buf, ") ");
6915 :
6916 340 : if (query->override)
6917 : {
6918 0 : if (query->override == OVERRIDING_SYSTEM_VALUE)
6919 0 : appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
6920 0 : else if (query->override == OVERRIDING_USER_VALUE)
6921 0 : appendStringInfoString(buf, "OVERRIDING USER VALUE ");
6922 : }
6923 :
6924 340 : if (select_rte)
6925 : {
6926 : /* Add the SELECT */
6927 50 : get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
6928 : false,
6929 : context->prettyFlags, context->wrapColumn,
6930 : context->indentLevel);
6931 : }
6932 290 : else if (values_rte)
6933 : {
6934 : /* Add the multi-VALUES expression lists */
6935 44 : get_values_def(values_rte->values_lists, context);
6936 : }
6937 246 : else if (strippedexprs)
6938 : {
6939 : /* Add the single-VALUES expression list */
6940 246 : appendContextKeyword(context, "VALUES (",
6941 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
6942 246 : get_rule_list_toplevel(strippedexprs, context, false);
6943 246 : appendStringInfoChar(buf, ')');
6944 : }
6945 : else
6946 : {
6947 : /* No expressions, so it must be DEFAULT VALUES */
6948 0 : appendStringInfoString(buf, "DEFAULT VALUES");
6949 : }
6950 :
6951 : /* Add ON CONFLICT if present */
6952 340 : if (query->onConflict)
6953 : {
6954 30 : OnConflictExpr *confl = query->onConflict;
6955 :
6956 30 : appendStringInfoString(buf, " ON CONFLICT");
6957 :
6958 30 : if (confl->arbiterElems)
6959 : {
6960 : /* Add the single-VALUES expression list */
6961 24 : appendStringInfoChar(buf, '(');
6962 24 : get_rule_expr((Node *) confl->arbiterElems, context, false);
6963 24 : appendStringInfoChar(buf, ')');
6964 :
6965 : /* Add a WHERE clause (for partial indexes) if given */
6966 24 : if (confl->arbiterWhere != NULL)
6967 : {
6968 : bool save_varprefix;
6969 :
6970 : /*
6971 : * Force non-prefixing of Vars, since parser assumes that they
6972 : * belong to target relation. WHERE clause does not use
6973 : * InferenceElem, so this is separately required.
6974 : */
6975 12 : save_varprefix = context->varprefix;
6976 12 : context->varprefix = false;
6977 :
6978 12 : appendContextKeyword(context, " WHERE ",
6979 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6980 12 : get_rule_expr(confl->arbiterWhere, context, false);
6981 :
6982 12 : context->varprefix = save_varprefix;
6983 : }
6984 : }
6985 6 : else if (OidIsValid(confl->constraint))
6986 : {
6987 0 : char *constraint = get_constraint_name(confl->constraint);
6988 :
6989 0 : if (!constraint)
6990 0 : elog(ERROR, "cache lookup failed for constraint %u",
6991 : confl->constraint);
6992 0 : appendStringInfo(buf, " ON CONSTRAINT %s",
6993 : quote_identifier(constraint));
6994 : }
6995 :
6996 30 : if (confl->action == ONCONFLICT_NOTHING)
6997 : {
6998 18 : appendStringInfoString(buf, " DO NOTHING");
6999 : }
7000 : else
7001 : {
7002 12 : appendStringInfoString(buf, " DO UPDATE SET ");
7003 : /* Deparse targetlist */
7004 12 : get_update_query_targetlist_def(query, confl->onConflictSet,
7005 : context, rte);
7006 :
7007 : /* Add a WHERE clause if given */
7008 12 : if (confl->onConflictWhere != NULL)
7009 : {
7010 12 : appendContextKeyword(context, " WHERE ",
7011 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7012 12 : get_rule_expr(confl->onConflictWhere, context, false);
7013 : }
7014 : }
7015 : }
7016 :
7017 : /* Add RETURNING if present */
7018 340 : if (query->returningList)
7019 : {
7020 78 : appendContextKeyword(context, " RETURNING",
7021 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7022 78 : get_target_list(query->returningList, context);
7023 : }
7024 340 : }
7025 :
7026 :
7027 : /* ----------
7028 : * get_update_query_def - Parse back an UPDATE parsetree
7029 : * ----------
7030 : */
7031 : static void
7032 130 : get_update_query_def(Query *query, deparse_context *context)
7033 : {
7034 130 : StringInfo buf = context->buf;
7035 : RangeTblEntry *rte;
7036 :
7037 : /* Insert the WITH clause if given */
7038 130 : get_with_clause(query, context);
7039 :
7040 : /*
7041 : * Start the query with UPDATE relname SET
7042 : */
7043 130 : rte = rt_fetch(query->resultRelation, query->rtable);
7044 : Assert(rte->rtekind == RTE_RELATION);
7045 130 : if (PRETTY_INDENT(context))
7046 : {
7047 130 : appendStringInfoChar(buf, ' ');
7048 130 : context->indentLevel += PRETTYINDENT_STD;
7049 : }
7050 260 : appendStringInfo(buf, "UPDATE %s%s",
7051 130 : only_marker(rte),
7052 : generate_relation_name(rte->relid, NIL));
7053 :
7054 : /* Print the relation alias, if needed */
7055 130 : get_rte_alias(rte, query->resultRelation, false, context);
7056 :
7057 130 : appendStringInfoString(buf, " SET ");
7058 :
7059 : /* Deparse targetlist */
7060 130 : get_update_query_targetlist_def(query, query->targetList, context, rte);
7061 :
7062 : /* Add the FROM clause if needed */
7063 130 : get_from_clause(query, " FROM ", context);
7064 :
7065 : /* Add a WHERE clause if given */
7066 130 : if (query->jointree->quals != NULL)
7067 : {
7068 114 : appendContextKeyword(context, " WHERE ",
7069 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7070 114 : get_rule_expr(query->jointree->quals, context, false);
7071 : }
7072 :
7073 : /* Add RETURNING if present */
7074 130 : if (query->returningList)
7075 : {
7076 22 : appendContextKeyword(context, " RETURNING",
7077 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7078 22 : get_target_list(query->returningList, context);
7079 : }
7080 130 : }
7081 :
7082 :
7083 : /* ----------
7084 : * get_update_query_targetlist_def - Parse back an UPDATE targetlist
7085 : * ----------
7086 : */
7087 : static void
7088 166 : get_update_query_targetlist_def(Query *query, List *targetList,
7089 : deparse_context *context, RangeTblEntry *rte)
7090 : {
7091 166 : StringInfo buf = context->buf;
7092 : ListCell *l;
7093 : ListCell *next_ma_cell;
7094 : int remaining_ma_columns;
7095 : const char *sep;
7096 : SubLink *cur_ma_sublink;
7097 : List *ma_sublinks;
7098 :
7099 : /*
7100 : * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
7101 : * into a list. We expect them to appear, in ID order, in resjunk tlist
7102 : * entries.
7103 : */
7104 166 : ma_sublinks = NIL;
7105 166 : if (query->hasSubLinks) /* else there can't be any */
7106 : {
7107 30 : foreach(l, targetList)
7108 : {
7109 24 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7110 :
7111 24 : if (tle->resjunk && IsA(tle->expr, SubLink))
7112 : {
7113 6 : SubLink *sl = (SubLink *) tle->expr;
7114 :
7115 6 : if (sl->subLinkType == MULTIEXPR_SUBLINK)
7116 : {
7117 6 : ma_sublinks = lappend(ma_sublinks, sl);
7118 : Assert(sl->subLinkId == list_length(ma_sublinks));
7119 : }
7120 : }
7121 : }
7122 : }
7123 166 : next_ma_cell = list_head(ma_sublinks);
7124 166 : cur_ma_sublink = NULL;
7125 166 : remaining_ma_columns = 0;
7126 :
7127 : /* Add the comma separated list of 'attname = value' */
7128 166 : sep = "";
7129 440 : foreach(l, targetList)
7130 : {
7131 274 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7132 : Node *expr;
7133 :
7134 274 : if (tle->resjunk)
7135 6 : continue; /* ignore junk entries */
7136 :
7137 : /* Emit separator (OK whether we're in multiassignment or not) */
7138 268 : appendStringInfoString(buf, sep);
7139 268 : sep = ", ";
7140 :
7141 : /*
7142 : * Check to see if we're starting a multiassignment group: if so,
7143 : * output a left paren.
7144 : */
7145 268 : if (next_ma_cell != NULL && cur_ma_sublink == NULL)
7146 : {
7147 : /*
7148 : * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
7149 : * Param. That could be buried under FieldStores and
7150 : * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
7151 : * and underneath those there could be an implicit type coercion.
7152 : * Because we would ignore implicit type coercions anyway, we
7153 : * don't need to be as careful as processIndirection() is about
7154 : * descending past implicit CoerceToDomains.
7155 : */
7156 6 : expr = (Node *) tle->expr;
7157 12 : while (expr)
7158 : {
7159 12 : if (IsA(expr, FieldStore))
7160 : {
7161 0 : FieldStore *fstore = (FieldStore *) expr;
7162 :
7163 0 : expr = (Node *) linitial(fstore->newvals);
7164 : }
7165 12 : else if (IsA(expr, SubscriptingRef))
7166 : {
7167 6 : SubscriptingRef *sbsref = (SubscriptingRef *) expr;
7168 :
7169 6 : if (sbsref->refassgnexpr == NULL)
7170 0 : break;
7171 :
7172 6 : expr = (Node *) sbsref->refassgnexpr;
7173 : }
7174 6 : else if (IsA(expr, CoerceToDomain))
7175 : {
7176 0 : CoerceToDomain *cdomain = (CoerceToDomain *) expr;
7177 :
7178 0 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
7179 0 : break;
7180 0 : expr = (Node *) cdomain->arg;
7181 : }
7182 : else
7183 6 : break;
7184 : }
7185 6 : expr = strip_implicit_coercions(expr);
7186 :
7187 6 : if (expr && IsA(expr, Param) &&
7188 6 : ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
7189 : {
7190 6 : cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
7191 6 : next_ma_cell = lnext(ma_sublinks, next_ma_cell);
7192 6 : remaining_ma_columns = count_nonjunk_tlist_entries(((Query *) cur_ma_sublink->subselect)->targetList);
7193 : Assert(((Param *) expr)->paramid ==
7194 : ((cur_ma_sublink->subLinkId << 16) | 1));
7195 6 : appendStringInfoChar(buf, '(');
7196 : }
7197 : }
7198 :
7199 : /*
7200 : * Put out name of target column; look in the catalogs, not at
7201 : * tle->resname, since resname will fail to track RENAME.
7202 : */
7203 268 : appendStringInfoString(buf,
7204 268 : quote_identifier(get_attname(rte->relid,
7205 268 : tle->resno,
7206 : false)));
7207 :
7208 : /*
7209 : * Print any indirection needed (subfields or subscripts), and strip
7210 : * off the top-level nodes representing the indirection assignments.
7211 : */
7212 268 : expr = processIndirection((Node *) tle->expr, context);
7213 :
7214 : /*
7215 : * If we're in a multiassignment, skip printing anything more, unless
7216 : * this is the last column; in which case, what we print should be the
7217 : * sublink, not the Param.
7218 : */
7219 268 : if (cur_ma_sublink != NULL)
7220 : {
7221 18 : if (--remaining_ma_columns > 0)
7222 12 : continue; /* not the last column of multiassignment */
7223 6 : appendStringInfoChar(buf, ')');
7224 6 : expr = (Node *) cur_ma_sublink;
7225 6 : cur_ma_sublink = NULL;
7226 : }
7227 :
7228 256 : appendStringInfoString(buf, " = ");
7229 :
7230 256 : get_rule_expr(expr, context, false);
7231 : }
7232 166 : }
7233 :
7234 :
7235 : /* ----------
7236 : * get_delete_query_def - Parse back a DELETE parsetree
7237 : * ----------
7238 : */
7239 : static void
7240 76 : get_delete_query_def(Query *query, deparse_context *context)
7241 : {
7242 76 : StringInfo buf = context->buf;
7243 : RangeTblEntry *rte;
7244 :
7245 : /* Insert the WITH clause if given */
7246 76 : get_with_clause(query, context);
7247 :
7248 : /*
7249 : * Start the query with DELETE FROM relname
7250 : */
7251 76 : rte = rt_fetch(query->resultRelation, query->rtable);
7252 : Assert(rte->rtekind == RTE_RELATION);
7253 76 : if (PRETTY_INDENT(context))
7254 : {
7255 76 : appendStringInfoChar(buf, ' ');
7256 76 : context->indentLevel += PRETTYINDENT_STD;
7257 : }
7258 152 : appendStringInfo(buf, "DELETE FROM %s%s",
7259 76 : only_marker(rte),
7260 : generate_relation_name(rte->relid, NIL));
7261 :
7262 : /* Print the relation alias, if needed */
7263 76 : get_rte_alias(rte, query->resultRelation, false, context);
7264 :
7265 : /* Add the USING clause if given */
7266 76 : get_from_clause(query, " USING ", context);
7267 :
7268 : /* Add a WHERE clause if given */
7269 76 : if (query->jointree->quals != NULL)
7270 : {
7271 76 : appendContextKeyword(context, " WHERE ",
7272 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7273 76 : get_rule_expr(query->jointree->quals, context, false);
7274 : }
7275 :
7276 : /* Add RETURNING if present */
7277 76 : if (query->returningList)
7278 : {
7279 16 : appendContextKeyword(context, " RETURNING",
7280 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7281 16 : get_target_list(query->returningList, context);
7282 : }
7283 76 : }
7284 :
7285 :
7286 : /* ----------
7287 : * get_merge_query_def - Parse back a MERGE parsetree
7288 : * ----------
7289 : */
7290 : static void
7291 12 : get_merge_query_def(Query *query, deparse_context *context)
7292 : {
7293 12 : StringInfo buf = context->buf;
7294 : RangeTblEntry *rte;
7295 : ListCell *lc;
7296 : bool haveNotMatchedBySource;
7297 :
7298 : /* Insert the WITH clause if given */
7299 12 : get_with_clause(query, context);
7300 :
7301 : /*
7302 : * Start the query with MERGE INTO relname
7303 : */
7304 12 : rte = rt_fetch(query->resultRelation, query->rtable);
7305 : Assert(rte->rtekind == RTE_RELATION);
7306 12 : if (PRETTY_INDENT(context))
7307 : {
7308 12 : appendStringInfoChar(buf, ' ');
7309 12 : context->indentLevel += PRETTYINDENT_STD;
7310 : }
7311 24 : appendStringInfo(buf, "MERGE INTO %s%s",
7312 12 : only_marker(rte),
7313 : generate_relation_name(rte->relid, NIL));
7314 :
7315 : /* Print the relation alias, if needed */
7316 12 : get_rte_alias(rte, query->resultRelation, false, context);
7317 :
7318 : /* Print the source relation and join clause */
7319 12 : get_from_clause(query, " USING ", context);
7320 12 : appendContextKeyword(context, " ON ",
7321 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7322 12 : get_rule_expr(query->mergeJoinCondition, context, false);
7323 :
7324 : /*
7325 : * Test for any NOT MATCHED BY SOURCE actions. If there are none, then
7326 : * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
7327 : * SQL standard. Otherwise, we have a non-SQL-standard query, so output
7328 : * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
7329 : * more explicit.
7330 : */
7331 12 : haveNotMatchedBySource = false;
7332 84 : foreach(lc, query->mergeActionList)
7333 : {
7334 78 : MergeAction *action = lfirst_node(MergeAction, lc);
7335 :
7336 78 : if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
7337 : {
7338 6 : haveNotMatchedBySource = true;
7339 6 : break;
7340 : }
7341 : }
7342 :
7343 : /* Print each merge action */
7344 90 : foreach(lc, query->mergeActionList)
7345 : {
7346 78 : MergeAction *action = lfirst_node(MergeAction, lc);
7347 :
7348 78 : appendContextKeyword(context, " WHEN ",
7349 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7350 78 : switch (action->matchKind)
7351 : {
7352 36 : case MERGE_WHEN_MATCHED:
7353 36 : appendStringInfoString(buf, "MATCHED");
7354 36 : break;
7355 6 : case MERGE_WHEN_NOT_MATCHED_BY_SOURCE:
7356 6 : appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
7357 6 : break;
7358 36 : case MERGE_WHEN_NOT_MATCHED_BY_TARGET:
7359 36 : if (haveNotMatchedBySource)
7360 6 : appendStringInfoString(buf, "NOT MATCHED BY TARGET");
7361 : else
7362 30 : appendStringInfoString(buf, "NOT MATCHED");
7363 36 : break;
7364 0 : default:
7365 0 : elog(ERROR, "unrecognized matchKind: %d",
7366 : (int) action->matchKind);
7367 : }
7368 :
7369 78 : if (action->qual)
7370 : {
7371 48 : appendContextKeyword(context, " AND ",
7372 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7373 48 : get_rule_expr(action->qual, context, false);
7374 : }
7375 78 : appendContextKeyword(context, " THEN ",
7376 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7377 :
7378 78 : if (action->commandType == CMD_INSERT)
7379 : {
7380 : /* This generally matches get_insert_query_def() */
7381 36 : List *strippedexprs = NIL;
7382 36 : const char *sep = "";
7383 : ListCell *lc2;
7384 :
7385 36 : appendStringInfoString(buf, "INSERT");
7386 :
7387 36 : if (action->targetList)
7388 30 : appendStringInfoString(buf, " (");
7389 102 : foreach(lc2, action->targetList)
7390 : {
7391 66 : TargetEntry *tle = (TargetEntry *) lfirst(lc2);
7392 :
7393 : Assert(!tle->resjunk);
7394 :
7395 66 : appendStringInfoString(buf, sep);
7396 66 : sep = ", ";
7397 :
7398 66 : appendStringInfoString(buf,
7399 66 : quote_identifier(get_attname(rte->relid,
7400 66 : tle->resno,
7401 : false)));
7402 66 : strippedexprs = lappend(strippedexprs,
7403 66 : processIndirection((Node *) tle->expr,
7404 : context));
7405 : }
7406 36 : if (action->targetList)
7407 30 : appendStringInfoChar(buf, ')');
7408 :
7409 36 : if (action->override)
7410 : {
7411 6 : if (action->override == OVERRIDING_SYSTEM_VALUE)
7412 0 : appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
7413 6 : else if (action->override == OVERRIDING_USER_VALUE)
7414 6 : appendStringInfoString(buf, " OVERRIDING USER VALUE");
7415 : }
7416 :
7417 36 : if (strippedexprs)
7418 : {
7419 30 : appendContextKeyword(context, " VALUES (",
7420 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);
7421 30 : get_rule_list_toplevel(strippedexprs, context, false);
7422 30 : appendStringInfoChar(buf, ')');
7423 : }
7424 : else
7425 6 : appendStringInfoString(buf, " DEFAULT VALUES");
7426 : }
7427 42 : else if (action->commandType == CMD_UPDATE)
7428 : {
7429 24 : appendStringInfoString(buf, "UPDATE SET ");
7430 24 : get_update_query_targetlist_def(query, action->targetList,
7431 : context, rte);
7432 : }
7433 18 : else if (action->commandType == CMD_DELETE)
7434 12 : appendStringInfoString(buf, "DELETE");
7435 6 : else if (action->commandType == CMD_NOTHING)
7436 6 : appendStringInfoString(buf, "DO NOTHING");
7437 : }
7438 :
7439 : /* Add RETURNING if present */
7440 12 : if (query->returningList)
7441 : {
7442 6 : appendContextKeyword(context, " RETURNING",
7443 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7444 6 : get_target_list(query->returningList, context);
7445 : }
7446 12 : }
7447 :
7448 :
7449 : /* ----------
7450 : * get_utility_query_def - Parse back a UTILITY parsetree
7451 : * ----------
7452 : */
7453 : static void
7454 16 : get_utility_query_def(Query *query, deparse_context *context)
7455 : {
7456 16 : StringInfo buf = context->buf;
7457 :
7458 16 : if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7459 16 : {
7460 16 : NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7461 :
7462 16 : appendContextKeyword(context, "",
7463 : 0, PRETTYINDENT_STD, 1);
7464 16 : appendStringInfo(buf, "NOTIFY %s",
7465 16 : quote_identifier(stmt->conditionname));
7466 16 : if (stmt->payload)
7467 : {
7468 0 : appendStringInfoString(buf, ", ");
7469 0 : simple_quote_literal(buf, stmt->payload);
7470 : }
7471 : }
7472 : else
7473 : {
7474 : /* Currently only NOTIFY utility commands can appear in rules */
7475 0 : elog(ERROR, "unexpected utility statement type");
7476 : }
7477 16 : }
7478 :
7479 : /*
7480 : * Display a Var appropriately.
7481 : *
7482 : * In some cases (currently only when recursing into an unnamed join)
7483 : * the Var's varlevelsup has to be interpreted with respect to a context
7484 : * above the current one; levelsup indicates the offset.
7485 : *
7486 : * If istoplevel is true, the Var is at the top level of a SELECT's
7487 : * targetlist, which means we need special treatment of whole-row Vars.
7488 : * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
7489 : * dirty hack to prevent "tab.*" from being expanded into multiple columns.
7490 : * (The parser will strip the useless coercion, so no inefficiency is added in
7491 : * dump and reload.) We used to print just "tab" in such cases, but that is
7492 : * ambiguous and will yield the wrong result if "tab" is also a plain column
7493 : * name in the query.
7494 : *
7495 : * Returns the attname of the Var, or NULL if the Var has no attname (because
7496 : * it is a whole-row Var or a subplan output reference).
7497 : */
7498 : static char *
7499 153026 : get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
7500 : {
7501 153026 : StringInfo buf = context->buf;
7502 : RangeTblEntry *rte;
7503 : AttrNumber attnum;
7504 : int netlevelsup;
7505 : deparse_namespace *dpns;
7506 : int varno;
7507 : AttrNumber varattno;
7508 : deparse_columns *colinfo;
7509 : char *refname;
7510 : char *attname;
7511 : bool need_prefix;
7512 :
7513 : /* Find appropriate nesting depth */
7514 153026 : netlevelsup = var->varlevelsup + levelsup;
7515 153026 : if (netlevelsup >= list_length(context->namespaces))
7516 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
7517 : var->varlevelsup, levelsup);
7518 153026 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7519 : netlevelsup);
7520 :
7521 : /*
7522 : * If we have a syntactic referent for the Var, and we're working from a
7523 : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
7524 : * on the semantic referent. (Forcing use of the semantic referent when
7525 : * printing plan trees is a design choice that's perhaps more motivated by
7526 : * backwards compatibility than anything else. But it does have the
7527 : * advantage of making plans more explicit.)
7528 : */
7529 153026 : if (var->varnosyn > 0 && dpns->plan == NULL)
7530 : {
7531 35398 : varno = var->varnosyn;
7532 35398 : varattno = var->varattnosyn;
7533 : }
7534 : else
7535 : {
7536 117628 : varno = var->varno;
7537 117628 : varattno = var->varattno;
7538 : }
7539 :
7540 : /*
7541 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
7542 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
7543 : * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
7544 : * find the aliases previously assigned for this RTE.
7545 : */
7546 153026 : if (varno >= 1 && varno <= list_length(dpns->rtable))
7547 : {
7548 : /*
7549 : * We might have been asked to map child Vars to some parent relation.
7550 : */
7551 115618 : if (context->appendparents && dpns->appendrels)
7552 : {
7553 3668 : int pvarno = varno;
7554 3668 : AttrNumber pvarattno = varattno;
7555 3668 : AppendRelInfo *appinfo = dpns->appendrels[pvarno];
7556 3668 : bool found = false;
7557 :
7558 : /* Only map up to inheritance parents, not UNION ALL appendrels */
7559 7458 : while (appinfo &&
7560 4080 : rt_fetch(appinfo->parent_relid,
7561 4080 : dpns->rtable)->rtekind == RTE_RELATION)
7562 : {
7563 3790 : found = false;
7564 3790 : if (pvarattno > 0) /* system columns stay as-is */
7565 : {
7566 3516 : if (pvarattno > appinfo->num_child_cols)
7567 0 : break; /* safety check */
7568 3516 : pvarattno = appinfo->parent_colnos[pvarattno - 1];
7569 3516 : if (pvarattno == 0)
7570 0 : break; /* Var is local to child */
7571 : }
7572 :
7573 3790 : pvarno = appinfo->parent_relid;
7574 3790 : found = true;
7575 :
7576 : /* If the parent is itself a child, continue up. */
7577 : Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
7578 3790 : appinfo = dpns->appendrels[pvarno];
7579 : }
7580 :
7581 : /*
7582 : * If we found an ancestral rel, and that rel is included in
7583 : * appendparents, print that column not the original one.
7584 : */
7585 3668 : if (found && bms_is_member(pvarno, context->appendparents))
7586 : {
7587 3024 : varno = pvarno;
7588 3024 : varattno = pvarattno;
7589 : }
7590 : }
7591 :
7592 115618 : rte = rt_fetch(varno, dpns->rtable);
7593 115618 : refname = (char *) list_nth(dpns->rtable_names, varno - 1);
7594 115618 : colinfo = deparse_columns_fetch(varno, dpns);
7595 115618 : attnum = varattno;
7596 : }
7597 : else
7598 : {
7599 37408 : resolve_special_varno((Node *) var, context,
7600 : get_special_variable, NULL);
7601 37408 : return NULL;
7602 : }
7603 :
7604 : /*
7605 : * The planner will sometimes emit Vars referencing resjunk elements of a
7606 : * subquery's target list (this is currently only possible if it chooses
7607 : * to generate a "physical tlist" for a SubqueryScan or CteScan node).
7608 : * Although we prefer to print subquery-referencing Vars using the
7609 : * subquery's alias, that's not possible for resjunk items since they have
7610 : * no alias. So in that case, drill down to the subplan and print the
7611 : * contents of the referenced tlist item. This works because in a plan
7612 : * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
7613 : * we'll have set dpns->inner_plan to reference the child plan node.
7614 : */
7615 119824 : if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
7616 4206 : attnum > list_length(rte->eref->colnames) &&
7617 2 : dpns->inner_plan)
7618 : {
7619 : TargetEntry *tle;
7620 : deparse_namespace save_dpns;
7621 :
7622 2 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
7623 2 : if (!tle)
7624 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
7625 : attnum, rte->eref->aliasname);
7626 :
7627 : Assert(netlevelsup == 0);
7628 2 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7629 :
7630 : /*
7631 : * Force parentheses because our caller probably assumed a Var is a
7632 : * simple expression.
7633 : */
7634 2 : if (!IsA(tle->expr, Var))
7635 0 : appendStringInfoChar(buf, '(');
7636 2 : get_rule_expr((Node *) tle->expr, context, true);
7637 2 : if (!IsA(tle->expr, Var))
7638 0 : appendStringInfoChar(buf, ')');
7639 :
7640 2 : pop_child_plan(dpns, &save_dpns);
7641 2 : return NULL;
7642 : }
7643 :
7644 : /*
7645 : * If it's an unnamed join, look at the expansion of the alias variable.
7646 : * If it's a simple reference to one of the input vars, then recursively
7647 : * print the name of that var instead. When it's not a simple reference,
7648 : * we have to just print the unqualified join column name. (This can only
7649 : * happen with "dangerous" merged columns in a JOIN USING; we took pains
7650 : * previously to make the unqualified column name unique in such cases.)
7651 : *
7652 : * This wouldn't work in decompiling plan trees, because we don't store
7653 : * joinaliasvars lists after planning; but a plan tree should never
7654 : * contain a join alias variable.
7655 : */
7656 115616 : if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
7657 : {
7658 96 : if (rte->joinaliasvars == NIL)
7659 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
7660 96 : if (attnum > 0)
7661 : {
7662 : Var *aliasvar;
7663 :
7664 96 : aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
7665 : /* we intentionally don't strip implicit coercions here */
7666 96 : if (aliasvar && IsA(aliasvar, Var))
7667 : {
7668 0 : return get_variable(aliasvar, var->varlevelsup + levelsup,
7669 : istoplevel, context);
7670 : }
7671 : }
7672 :
7673 : /*
7674 : * Unnamed join has no refname. (Note: since it's unnamed, there is
7675 : * no way the user could have referenced it to create a whole-row Var
7676 : * for it. So we don't have to cover that case below.)
7677 : */
7678 : Assert(refname == NULL);
7679 : }
7680 :
7681 115616 : if (attnum == InvalidAttrNumber)
7682 820 : attname = NULL;
7683 114796 : else if (attnum > 0)
7684 : {
7685 : /* Get column name to use from the colinfo struct */
7686 113428 : if (attnum > colinfo->num_cols)
7687 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
7688 : attnum, rte->eref->aliasname);
7689 113428 : attname = colinfo->colnames[attnum - 1];
7690 :
7691 : /*
7692 : * If we find a Var referencing a dropped column, it seems better to
7693 : * print something (anything) than to fail. In general this should
7694 : * not happen, but it used to be possible for some cases involving
7695 : * functions returning named composite types, and perhaps there are
7696 : * still bugs out there.
7697 : */
7698 113428 : if (attname == NULL)
7699 6 : attname = "?dropped?column?";
7700 : }
7701 : else
7702 : {
7703 : /* System column - name is fixed, get it from the catalog */
7704 1368 : attname = get_rte_attribute_name(rte, attnum);
7705 : }
7706 :
7707 115616 : need_prefix = (context->varprefix || attname == NULL);
7708 :
7709 : /*
7710 : * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
7711 : * clause, we may need to add a table-name prefix to prevent
7712 : * findTargetlistEntrySQL92 from misinterpreting the name as an
7713 : * output-column name. To avoid cluttering the output with unnecessary
7714 : * prefixes, do so only if there is a name match to a SELECT tlist item
7715 : * that is different from the Var.
7716 : */
7717 115616 : if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
7718 : {
7719 224 : int colno = 0;
7720 :
7721 874 : foreach_node(TargetEntry, tle, context->targetList)
7722 : {
7723 : char *colname;
7724 :
7725 438 : if (tle->resjunk)
7726 0 : continue; /* ignore junk entries */
7727 438 : colno++;
7728 :
7729 : /* This must match colname-choosing logic in get_target_list() */
7730 438 : if (context->resultDesc && colno <= context->resultDesc->natts)
7731 438 : colname = NameStr(TupleDescAttr(context->resultDesc,
7732 : colno - 1)->attname);
7733 : else
7734 0 : colname = tle->resname;
7735 :
7736 438 : if (colname && strcmp(colname, attname) == 0 &&
7737 152 : !equal(var, tle->expr))
7738 : {
7739 12 : need_prefix = true;
7740 12 : break;
7741 : }
7742 : }
7743 : }
7744 :
7745 115616 : if (refname && need_prefix)
7746 : {
7747 56322 : appendStringInfoString(buf, quote_identifier(refname));
7748 56322 : appendStringInfoChar(buf, '.');
7749 : }
7750 115616 : if (attname)
7751 114796 : appendStringInfoString(buf, quote_identifier(attname));
7752 : else
7753 : {
7754 820 : appendStringInfoChar(buf, '*');
7755 820 : if (istoplevel)
7756 72 : appendStringInfo(buf, "::%s",
7757 : format_type_with_typemod(var->vartype,
7758 : var->vartypmod));
7759 : }
7760 :
7761 115616 : return attname;
7762 : }
7763 :
7764 : /*
7765 : * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
7766 : * routine is actually a callback for resolve_special_varno, which handles
7767 : * finding the correct TargetEntry. We get the expression contained in that
7768 : * TargetEntry and just need to deparse it, a job we can throw back on
7769 : * get_rule_expr.
7770 : */
7771 : static void
7772 37408 : get_special_variable(Node *node, deparse_context *context, void *callback_arg)
7773 : {
7774 37408 : StringInfo buf = context->buf;
7775 :
7776 : /*
7777 : * For a non-Var referent, force parentheses because our caller probably
7778 : * assumed a Var is a simple expression.
7779 : */
7780 37408 : if (!IsA(node, Var))
7781 3266 : appendStringInfoChar(buf, '(');
7782 37408 : get_rule_expr(node, context, true);
7783 37408 : if (!IsA(node, Var))
7784 3266 : appendStringInfoChar(buf, ')');
7785 37408 : }
7786 :
7787 : /*
7788 : * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
7789 : * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
7790 : * invoke the callback provided.
7791 : */
7792 : static void
7793 102882 : resolve_special_varno(Node *node, deparse_context *context,
7794 : rsv_callback callback, void *callback_arg)
7795 : {
7796 : Var *var;
7797 : deparse_namespace *dpns;
7798 :
7799 : /* This function is recursive, so let's be paranoid. */
7800 102882 : check_stack_depth();
7801 :
7802 : /* If it's not a Var, invoke the callback. */
7803 102882 : if (!IsA(node, Var))
7804 : {
7805 3526 : (*callback) (node, context, callback_arg);
7806 3526 : return;
7807 : }
7808 :
7809 : /* Find appropriate nesting depth */
7810 99356 : var = (Var *) node;
7811 99356 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7812 99356 : var->varlevelsup);
7813 :
7814 : /*
7815 : * If varno is special, recurse. (Don't worry about varnosyn; if we're
7816 : * here, we already decided not to use that.)
7817 : */
7818 99356 : if (var->varno == OUTER_VAR && dpns->outer_tlist)
7819 : {
7820 : TargetEntry *tle;
7821 : deparse_namespace save_dpns;
7822 : Bitmapset *save_appendparents;
7823 :
7824 48752 : tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
7825 48752 : if (!tle)
7826 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
7827 :
7828 : /*
7829 : * If we're descending to the first child of an Append or MergeAppend,
7830 : * update appendparents. This will affect deparsing of all Vars
7831 : * appearing within the eventually-resolved subexpression.
7832 : */
7833 48752 : save_appendparents = context->appendparents;
7834 :
7835 48752 : if (IsA(dpns->plan, Append))
7836 4078 : context->appendparents = bms_union(context->appendparents,
7837 4078 : ((Append *) dpns->plan)->apprelids);
7838 44674 : else if (IsA(dpns->plan, MergeAppend))
7839 554 : context->appendparents = bms_union(context->appendparents,
7840 554 : ((MergeAppend *) dpns->plan)->apprelids);
7841 :
7842 48752 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
7843 48752 : resolve_special_varno((Node *) tle->expr, context,
7844 : callback, callback_arg);
7845 48752 : pop_child_plan(dpns, &save_dpns);
7846 48752 : context->appendparents = save_appendparents;
7847 48752 : return;
7848 : }
7849 50604 : else if (var->varno == INNER_VAR && dpns->inner_tlist)
7850 : {
7851 : TargetEntry *tle;
7852 : deparse_namespace save_dpns;
7853 :
7854 11882 : tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
7855 11882 : if (!tle)
7856 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
7857 :
7858 11882 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7859 11882 : resolve_special_varno((Node *) tle->expr, context,
7860 : callback, callback_arg);
7861 11882 : pop_child_plan(dpns, &save_dpns);
7862 11882 : return;
7863 : }
7864 38722 : else if (var->varno == INDEX_VAR && dpns->index_tlist)
7865 : {
7866 : TargetEntry *tle;
7867 :
7868 4580 : tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
7869 4580 : if (!tle)
7870 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
7871 :
7872 4580 : resolve_special_varno((Node *) tle->expr, context,
7873 : callback, callback_arg);
7874 4580 : return;
7875 : }
7876 34142 : else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
7877 0 : elog(ERROR, "bogus varno: %d", var->varno);
7878 :
7879 : /* Not special. Just invoke the callback. */
7880 34142 : (*callback) (node, context, callback_arg);
7881 : }
7882 :
7883 : /*
7884 : * Get the name of a field of an expression of composite type. The
7885 : * expression is usually a Var, but we handle other cases too.
7886 : *
7887 : * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
7888 : *
7889 : * This is fairly straightforward when the expression has a named composite
7890 : * type; we need only look up the type in the catalogs. However, the type
7891 : * could also be RECORD. Since no actual table or view column is allowed to
7892 : * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
7893 : * or to a subquery output. We drill down to find the ultimate defining
7894 : * expression and attempt to infer the field name from it. We ereport if we
7895 : * can't determine the name.
7896 : *
7897 : * Similarly, a PARAM of type RECORD has to refer to some expression of
7898 : * a determinable composite type.
7899 : */
7900 : static const char *
7901 1304 : get_name_for_var_field(Var *var, int fieldno,
7902 : int levelsup, deparse_context *context)
7903 : {
7904 : RangeTblEntry *rte;
7905 : AttrNumber attnum;
7906 : int netlevelsup;
7907 : deparse_namespace *dpns;
7908 : int varno;
7909 : AttrNumber varattno;
7910 : TupleDesc tupleDesc;
7911 : Node *expr;
7912 :
7913 : /*
7914 : * If it's a RowExpr that was expanded from a whole-row Var, use the
7915 : * column names attached to it. (We could let get_expr_result_tupdesc()
7916 : * handle this, but it's much cheaper to just pull out the name we need.)
7917 : */
7918 1304 : if (IsA(var, RowExpr))
7919 : {
7920 36 : RowExpr *r = (RowExpr *) var;
7921 :
7922 36 : if (fieldno > 0 && fieldno <= list_length(r->colnames))
7923 36 : return strVal(list_nth(r->colnames, fieldno - 1));
7924 : }
7925 :
7926 : /*
7927 : * If it's a Param of type RECORD, try to find what the Param refers to.
7928 : */
7929 1268 : if (IsA(var, Param))
7930 : {
7931 18 : Param *param = (Param *) var;
7932 : ListCell *ancestor_cell;
7933 :
7934 18 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
7935 18 : if (expr)
7936 : {
7937 : /* Found a match, so recurse to decipher the field name */
7938 : deparse_namespace save_dpns;
7939 : const char *result;
7940 :
7941 18 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
7942 18 : result = get_name_for_var_field((Var *) expr, fieldno,
7943 : 0, context);
7944 18 : pop_ancestor_plan(dpns, &save_dpns);
7945 18 : return result;
7946 : }
7947 : }
7948 :
7949 : /*
7950 : * If it's a Var of type RECORD, we have to find what the Var refers to;
7951 : * if not, we can use get_expr_result_tupdesc().
7952 : */
7953 1250 : if (!IsA(var, Var) ||
7954 1170 : var->vartype != RECORDOID)
7955 : {
7956 1004 : tupleDesc = get_expr_result_tupdesc((Node *) var, false);
7957 : /* Got the tupdesc, so we can extract the field name */
7958 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
7959 1004 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
7960 : }
7961 :
7962 : /* Find appropriate nesting depth */
7963 246 : netlevelsup = var->varlevelsup + levelsup;
7964 246 : if (netlevelsup >= list_length(context->namespaces))
7965 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
7966 : var->varlevelsup, levelsup);
7967 246 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7968 : netlevelsup);
7969 :
7970 : /*
7971 : * If we have a syntactic referent for the Var, and we're working from a
7972 : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
7973 : * on the semantic referent. (See comments in get_variable().)
7974 : */
7975 246 : if (var->varnosyn > 0 && dpns->plan == NULL)
7976 : {
7977 96 : varno = var->varnosyn;
7978 96 : varattno = var->varattnosyn;
7979 : }
7980 : else
7981 : {
7982 150 : varno = var->varno;
7983 150 : varattno = var->varattno;
7984 : }
7985 :
7986 : /*
7987 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
7988 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
7989 : * down into the subplans, or INDEX_VAR, which is resolved similarly.
7990 : *
7991 : * Note: unlike get_variable and resolve_special_varno, we need not worry
7992 : * about inheritance mapping: a child Var should have the same datatype as
7993 : * its parent, and here we're really only interested in the Var's type.
7994 : */
7995 246 : if (varno >= 1 && varno <= list_length(dpns->rtable))
7996 : {
7997 168 : rte = rt_fetch(varno, dpns->rtable);
7998 168 : attnum = varattno;
7999 : }
8000 78 : else if (varno == OUTER_VAR && dpns->outer_tlist)
8001 : {
8002 : TargetEntry *tle;
8003 : deparse_namespace save_dpns;
8004 : const char *result;
8005 :
8006 60 : tle = get_tle_by_resno(dpns->outer_tlist, varattno);
8007 60 : if (!tle)
8008 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
8009 :
8010 : Assert(netlevelsup == 0);
8011 60 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8012 :
8013 60 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8014 : levelsup, context);
8015 :
8016 60 : pop_child_plan(dpns, &save_dpns);
8017 60 : return result;
8018 : }
8019 18 : else if (varno == INNER_VAR && dpns->inner_tlist)
8020 : {
8021 : TargetEntry *tle;
8022 : deparse_namespace save_dpns;
8023 : const char *result;
8024 :
8025 18 : tle = get_tle_by_resno(dpns->inner_tlist, varattno);
8026 18 : if (!tle)
8027 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
8028 :
8029 : Assert(netlevelsup == 0);
8030 18 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8031 :
8032 18 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8033 : levelsup, context);
8034 :
8035 18 : pop_child_plan(dpns, &save_dpns);
8036 18 : return result;
8037 : }
8038 0 : else if (varno == INDEX_VAR && dpns->index_tlist)
8039 : {
8040 : TargetEntry *tle;
8041 : const char *result;
8042 :
8043 0 : tle = get_tle_by_resno(dpns->index_tlist, varattno);
8044 0 : if (!tle)
8045 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
8046 :
8047 : Assert(netlevelsup == 0);
8048 :
8049 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8050 : levelsup, context);
8051 :
8052 0 : return result;
8053 : }
8054 : else
8055 : {
8056 0 : elog(ERROR, "bogus varno: %d", varno);
8057 : return NULL; /* keep compiler quiet */
8058 : }
8059 :
8060 168 : if (attnum == InvalidAttrNumber)
8061 : {
8062 : /* Var is whole-row reference to RTE, so select the right field */
8063 24 : return get_rte_attribute_name(rte, fieldno);
8064 : }
8065 :
8066 : /*
8067 : * This part has essentially the same logic as the parser's
8068 : * expandRecordVariable() function, but we are dealing with a different
8069 : * representation of the input context, and we only need one field name
8070 : * not a TupleDesc. Also, we need special cases for finding subquery and
8071 : * CTE subplans when deparsing Plan trees.
8072 : */
8073 144 : expr = (Node *) var; /* default if we can't drill down */
8074 :
8075 144 : switch (rte->rtekind)
8076 : {
8077 0 : case RTE_RELATION:
8078 : case RTE_VALUES:
8079 : case RTE_NAMEDTUPLESTORE:
8080 : case RTE_RESULT:
8081 :
8082 : /*
8083 : * This case should not occur: a column of a table, values list,
8084 : * or ENR shouldn't have type RECORD. Fall through and fail (most
8085 : * likely) at the bottom.
8086 : */
8087 0 : break;
8088 72 : case RTE_SUBQUERY:
8089 : /* Subselect-in-FROM: examine sub-select's output expr */
8090 : {
8091 72 : if (rte->subquery)
8092 : {
8093 42 : TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
8094 : attnum);
8095 :
8096 42 : if (ste == NULL || ste->resjunk)
8097 0 : elog(ERROR, "subquery %s does not have attribute %d",
8098 : rte->eref->aliasname, attnum);
8099 42 : expr = (Node *) ste->expr;
8100 42 : if (IsA(expr, Var))
8101 : {
8102 : /*
8103 : * Recurse into the sub-select to see what its Var
8104 : * refers to. We have to build an additional level of
8105 : * namespace to keep in step with varlevelsup in the
8106 : * subselect; furthermore, the subquery RTE might be
8107 : * from an outer query level, in which case the
8108 : * namespace for the subselect must have that outer
8109 : * level as parent namespace.
8110 : */
8111 18 : List *save_nslist = context->namespaces;
8112 : List *parent_namespaces;
8113 : deparse_namespace mydpns;
8114 : const char *result;
8115 :
8116 18 : parent_namespaces = list_copy_tail(context->namespaces,
8117 : netlevelsup);
8118 :
8119 18 : set_deparse_for_query(&mydpns, rte->subquery,
8120 : parent_namespaces);
8121 :
8122 18 : context->namespaces = lcons(&mydpns, parent_namespaces);
8123 :
8124 18 : result = get_name_for_var_field((Var *) expr, fieldno,
8125 : 0, context);
8126 :
8127 18 : context->namespaces = save_nslist;
8128 :
8129 18 : return result;
8130 : }
8131 : /* else fall through to inspect the expression */
8132 : }
8133 : else
8134 : {
8135 : /*
8136 : * We're deparsing a Plan tree so we don't have complete
8137 : * RTE entries (in particular, rte->subquery is NULL). But
8138 : * the only place we'd normally see a Var directly
8139 : * referencing a SUBQUERY RTE is in a SubqueryScan plan
8140 : * node, and we can look into the child plan's tlist
8141 : * instead. An exception occurs if the subquery was
8142 : * proven empty and optimized away: then we'd find such a
8143 : * Var in a childless Result node, and there's nothing in
8144 : * the plan tree that would let us figure out what it had
8145 : * originally referenced. In that case, fall back on
8146 : * printing "fN", analogously to the default column names
8147 : * for RowExprs.
8148 : */
8149 : TargetEntry *tle;
8150 : deparse_namespace save_dpns;
8151 : const char *result;
8152 :
8153 30 : if (!dpns->inner_plan)
8154 : {
8155 12 : char *dummy_name = palloc(32);
8156 :
8157 : Assert(dpns->plan && IsA(dpns->plan, Result));
8158 12 : snprintf(dummy_name, 32, "f%d", fieldno);
8159 12 : return dummy_name;
8160 : }
8161 : Assert(dpns->plan && IsA(dpns->plan, SubqueryScan));
8162 :
8163 18 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8164 18 : if (!tle)
8165 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8166 : attnum);
8167 : Assert(netlevelsup == 0);
8168 18 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8169 :
8170 18 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8171 : levelsup, context);
8172 :
8173 18 : pop_child_plan(dpns, &save_dpns);
8174 18 : return result;
8175 : }
8176 : }
8177 24 : break;
8178 0 : case RTE_JOIN:
8179 : /* Join RTE --- recursively inspect the alias variable */
8180 0 : if (rte->joinaliasvars == NIL)
8181 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
8182 : Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
8183 0 : expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
8184 : Assert(expr != NULL);
8185 : /* we intentionally don't strip implicit coercions here */
8186 0 : if (IsA(expr, Var))
8187 0 : return get_name_for_var_field((Var *) expr, fieldno,
8188 0 : var->varlevelsup + levelsup,
8189 : context);
8190 : /* else fall through to inspect the expression */
8191 0 : break;
8192 0 : case RTE_FUNCTION:
8193 : case RTE_TABLEFUNC:
8194 :
8195 : /*
8196 : * We couldn't get here unless a function is declared with one of
8197 : * its result columns as RECORD, which is not allowed.
8198 : */
8199 0 : break;
8200 72 : case RTE_CTE:
8201 : /* CTE reference: examine subquery's output expr */
8202 : {
8203 72 : CommonTableExpr *cte = NULL;
8204 : Index ctelevelsup;
8205 : ListCell *lc;
8206 :
8207 : /*
8208 : * Try to find the referenced CTE using the namespace stack.
8209 : */
8210 72 : ctelevelsup = rte->ctelevelsup + netlevelsup;
8211 72 : if (ctelevelsup >= list_length(context->namespaces))
8212 12 : lc = NULL;
8213 : else
8214 : {
8215 : deparse_namespace *ctedpns;
8216 :
8217 : ctedpns = (deparse_namespace *)
8218 60 : list_nth(context->namespaces, ctelevelsup);
8219 66 : foreach(lc, ctedpns->ctes)
8220 : {
8221 36 : cte = (CommonTableExpr *) lfirst(lc);
8222 36 : if (strcmp(cte->ctename, rte->ctename) == 0)
8223 30 : break;
8224 : }
8225 : }
8226 72 : if (lc != NULL)
8227 : {
8228 30 : Query *ctequery = (Query *) cte->ctequery;
8229 30 : TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
8230 : attnum);
8231 :
8232 30 : if (ste == NULL || ste->resjunk)
8233 0 : elog(ERROR, "CTE %s does not have attribute %d",
8234 : rte->eref->aliasname, attnum);
8235 30 : expr = (Node *) ste->expr;
8236 30 : if (IsA(expr, Var))
8237 : {
8238 : /*
8239 : * Recurse into the CTE to see what its Var refers to.
8240 : * We have to build an additional level of namespace
8241 : * to keep in step with varlevelsup in the CTE;
8242 : * furthermore it could be an outer CTE (compare
8243 : * SUBQUERY case above).
8244 : */
8245 18 : List *save_nslist = context->namespaces;
8246 : List *parent_namespaces;
8247 : deparse_namespace mydpns;
8248 : const char *result;
8249 :
8250 18 : parent_namespaces = list_copy_tail(context->namespaces,
8251 : ctelevelsup);
8252 :
8253 18 : set_deparse_for_query(&mydpns, ctequery,
8254 : parent_namespaces);
8255 :
8256 18 : context->namespaces = lcons(&mydpns, parent_namespaces);
8257 :
8258 18 : result = get_name_for_var_field((Var *) expr, fieldno,
8259 : 0, context);
8260 :
8261 18 : context->namespaces = save_nslist;
8262 :
8263 18 : return result;
8264 : }
8265 : /* else fall through to inspect the expression */
8266 : }
8267 : else
8268 : {
8269 : /*
8270 : * We're deparsing a Plan tree so we don't have a CTE
8271 : * list. But the only places we'd normally see a Var
8272 : * directly referencing a CTE RTE are in CteScan or
8273 : * WorkTableScan plan nodes. For those cases,
8274 : * set_deparse_plan arranged for dpns->inner_plan to be
8275 : * the plan node that emits the CTE or RecursiveUnion
8276 : * result, and we can look at its tlist instead. As
8277 : * above, this can fail if the CTE has been proven empty,
8278 : * in which case fall back to "fN".
8279 : */
8280 : TargetEntry *tle;
8281 : deparse_namespace save_dpns;
8282 : const char *result;
8283 :
8284 42 : if (!dpns->inner_plan)
8285 : {
8286 6 : char *dummy_name = palloc(32);
8287 :
8288 : Assert(dpns->plan && IsA(dpns->plan, Result));
8289 6 : snprintf(dummy_name, 32, "f%d", fieldno);
8290 6 : return dummy_name;
8291 : }
8292 : Assert(dpns->plan && (IsA(dpns->plan, CteScan) ||
8293 : IsA(dpns->plan, WorkTableScan)));
8294 :
8295 36 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8296 36 : if (!tle)
8297 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8298 : attnum);
8299 : Assert(netlevelsup == 0);
8300 36 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8301 :
8302 36 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8303 : levelsup, context);
8304 :
8305 36 : pop_child_plan(dpns, &save_dpns);
8306 36 : return result;
8307 : }
8308 : }
8309 12 : break;
8310 0 : case RTE_GROUP:
8311 :
8312 : /*
8313 : * We couldn't get here: any Vars that reference the RTE_GROUP RTE
8314 : * should have been replaced with the underlying grouping
8315 : * expressions.
8316 : */
8317 0 : break;
8318 : }
8319 :
8320 : /*
8321 : * We now have an expression we can't expand any more, so see if
8322 : * get_expr_result_tupdesc() can do anything with it.
8323 : */
8324 36 : tupleDesc = get_expr_result_tupdesc(expr, false);
8325 : /* Got the tupdesc, so we can extract the field name */
8326 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
8327 36 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
8328 : }
8329 :
8330 : /*
8331 : * Try to find the referenced expression for a PARAM_EXEC Param that might
8332 : * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
8333 : *
8334 : * If successful, return the expression and set *dpns_p and *ancestor_cell_p
8335 : * appropriately for calling push_ancestor_plan(). If no referent can be
8336 : * found, return NULL.
8337 : */
8338 : static Node *
8339 6198 : find_param_referent(Param *param, deparse_context *context,
8340 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
8341 : {
8342 : /* Initialize output parameters to prevent compiler warnings */
8343 6198 : *dpns_p = NULL;
8344 6198 : *ancestor_cell_p = NULL;
8345 :
8346 : /*
8347 : * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
8348 : * SubPlan argument. This will necessarily be in some ancestor of the
8349 : * current expression's Plan node.
8350 : */
8351 6198 : if (param->paramkind == PARAM_EXEC)
8352 : {
8353 : deparse_namespace *dpns;
8354 : Plan *child_plan;
8355 : ListCell *lc;
8356 :
8357 5336 : dpns = (deparse_namespace *) linitial(context->namespaces);
8358 5336 : child_plan = dpns->plan;
8359 :
8360 9786 : foreach(lc, dpns->ancestors)
8361 : {
8362 8210 : Node *ancestor = (Node *) lfirst(lc);
8363 : ListCell *lc2;
8364 :
8365 : /*
8366 : * NestLoops transmit params to their inner child only.
8367 : */
8368 8210 : if (IsA(ancestor, NestLoop) &&
8369 3410 : child_plan == innerPlan(ancestor))
8370 : {
8371 3332 : NestLoop *nl = (NestLoop *) ancestor;
8372 :
8373 4240 : foreach(lc2, nl->nestParams)
8374 : {
8375 4108 : NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
8376 :
8377 4108 : if (nlp->paramno == param->paramid)
8378 : {
8379 : /* Found a match, so return it */
8380 3200 : *dpns_p = dpns;
8381 3200 : *ancestor_cell_p = lc;
8382 3200 : return (Node *) nlp->paramval;
8383 : }
8384 : }
8385 : }
8386 :
8387 : /*
8388 : * If ancestor is a SubPlan, check the arguments it provides.
8389 : */
8390 5010 : if (IsA(ancestor, SubPlan))
8391 : {
8392 908 : SubPlan *subplan = (SubPlan *) ancestor;
8393 : ListCell *lc3;
8394 : ListCell *lc4;
8395 :
8396 1220 : forboth(lc3, subplan->parParam, lc4, subplan->args)
8397 : {
8398 872 : int paramid = lfirst_int(lc3);
8399 872 : Node *arg = (Node *) lfirst(lc4);
8400 :
8401 872 : if (paramid == param->paramid)
8402 : {
8403 : /*
8404 : * Found a match, so return it. But, since Vars in
8405 : * the arg are to be evaluated in the surrounding
8406 : * context, we have to point to the next ancestor item
8407 : * that is *not* a SubPlan.
8408 : */
8409 : ListCell *rest;
8410 :
8411 560 : for_each_cell(rest, dpns->ancestors,
8412 : lnext(dpns->ancestors, lc))
8413 : {
8414 560 : Node *ancestor2 = (Node *) lfirst(rest);
8415 :
8416 560 : if (!IsA(ancestor2, SubPlan))
8417 : {
8418 560 : *dpns_p = dpns;
8419 560 : *ancestor_cell_p = rest;
8420 560 : return arg;
8421 : }
8422 : }
8423 0 : elog(ERROR, "SubPlan cannot be outermost ancestor");
8424 : }
8425 : }
8426 :
8427 : /* SubPlan isn't a kind of Plan, so skip the rest */
8428 348 : continue;
8429 : }
8430 :
8431 : /*
8432 : * We need not consider the ancestor's initPlan list, since
8433 : * initplans never have any parParams.
8434 : */
8435 :
8436 : /* No luck, crawl up to next ancestor */
8437 4102 : child_plan = (Plan *) ancestor;
8438 : }
8439 : }
8440 :
8441 : /* No referent found */
8442 2438 : return NULL;
8443 : }
8444 :
8445 : /*
8446 : * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.
8447 : *
8448 : * If successful, return the generating subplan/initplan and set *column_p
8449 : * to the subplan's 0-based output column number.
8450 : * Otherwise, return NULL.
8451 : */
8452 : static SubPlan *
8453 2438 : find_param_generator(Param *param, deparse_context *context, int *column_p)
8454 : {
8455 : /* Initialize output parameter to prevent compiler warnings */
8456 2438 : *column_p = 0;
8457 :
8458 : /*
8459 : * If it's a PARAM_EXEC parameter, search the current plan node as well as
8460 : * ancestor nodes looking for a subplan or initplan that emits the value
8461 : * for the Param. It could appear in the setParams of an initplan or
8462 : * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
8463 : */
8464 2438 : if (param->paramkind == PARAM_EXEC)
8465 : {
8466 : SubPlan *result;
8467 : deparse_namespace *dpns;
8468 : ListCell *lc;
8469 :
8470 1576 : dpns = (deparse_namespace *) linitial(context->namespaces);
8471 :
8472 : /* First check the innermost plan node's initplans */
8473 1576 : result = find_param_generator_initplan(param, dpns->plan, column_p);
8474 1576 : if (result)
8475 452 : return result;
8476 :
8477 : /*
8478 : * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
8479 : * which can be referenced by Params elsewhere in the targetlist.
8480 : * (Such Params should always be in the same targetlist, so there's no
8481 : * need to do this work at upper plan nodes.)
8482 : */
8483 5716 : foreach_node(TargetEntry, tle, dpns->plan->targetlist)
8484 : {
8485 3572 : if (tle->expr && IsA(tle->expr, SubPlan))
8486 : {
8487 100 : SubPlan *subplan = (SubPlan *) tle->expr;
8488 :
8489 100 : if (subplan->subLinkType == MULTIEXPR_SUBLINK)
8490 : {
8491 78 : foreach_int(paramid, subplan->setParam)
8492 : {
8493 78 : if (paramid == param->paramid)
8494 : {
8495 : /* Found a match, so return it. */
8496 52 : *column_p = foreach_current_index(paramid);
8497 52 : return subplan;
8498 : }
8499 : }
8500 : }
8501 : }
8502 : }
8503 :
8504 : /* No luck, so check the ancestor nodes */
8505 1414 : foreach(lc, dpns->ancestors)
8506 : {
8507 1414 : Node *ancestor = (Node *) lfirst(lc);
8508 :
8509 : /*
8510 : * If ancestor is a SubPlan, check the paramIds it provides.
8511 : */
8512 1414 : if (IsA(ancestor, SubPlan))
8513 : {
8514 204 : SubPlan *subplan = (SubPlan *) ancestor;
8515 :
8516 230 : foreach_int(paramid, subplan->paramIds)
8517 : {
8518 230 : if (paramid == param->paramid)
8519 : {
8520 : /* Found a match, so return it. */
8521 204 : *column_p = foreach_current_index(paramid);
8522 204 : return subplan;
8523 : }
8524 : }
8525 :
8526 : /* SubPlan isn't a kind of Plan, so skip the rest */
8527 0 : continue;
8528 : }
8529 :
8530 : /*
8531 : * Otherwise, it's some kind of Plan node, so check its initplans.
8532 : */
8533 1210 : result = find_param_generator_initplan(param, (Plan *) ancestor,
8534 : column_p);
8535 1210 : if (result)
8536 868 : return result;
8537 :
8538 : /* No luck, crawl up to next ancestor */
8539 : }
8540 : }
8541 :
8542 : /* No generator found */
8543 862 : return NULL;
8544 : }
8545 :
8546 : /*
8547 : * Subroutine for find_param_generator: search one Plan node's initplans
8548 : */
8549 : static SubPlan *
8550 2786 : find_param_generator_initplan(Param *param, Plan *plan, int *column_p)
8551 : {
8552 4392 : foreach_node(SubPlan, subplan, plan->initPlan)
8553 : {
8554 1746 : foreach_int(paramid, subplan->setParam)
8555 : {
8556 1466 : if (paramid == param->paramid)
8557 : {
8558 : /* Found a match, so return it. */
8559 1320 : *column_p = foreach_current_index(paramid);
8560 1320 : return subplan;
8561 : }
8562 : }
8563 : }
8564 1466 : return NULL;
8565 : }
8566 :
8567 : /*
8568 : * Display a Param appropriately.
8569 : */
8570 : static void
8571 6180 : get_parameter(Param *param, deparse_context *context)
8572 : {
8573 : Node *expr;
8574 : deparse_namespace *dpns;
8575 : ListCell *ancestor_cell;
8576 : SubPlan *subplan;
8577 : int column;
8578 :
8579 : /*
8580 : * If it's a PARAM_EXEC parameter, try to locate the expression from which
8581 : * the parameter was computed. This stanza handles only cases in which
8582 : * the Param represents an input to the subplan we are currently in.
8583 : */
8584 6180 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8585 6180 : if (expr)
8586 : {
8587 : /* Found a match, so print it */
8588 : deparse_namespace save_dpns;
8589 : bool save_varprefix;
8590 : bool need_paren;
8591 :
8592 : /* Switch attention to the ancestor plan node */
8593 3742 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
8594 :
8595 : /*
8596 : * Force prefixing of Vars, since they won't belong to the relation
8597 : * being scanned in the original plan node.
8598 : */
8599 3742 : save_varprefix = context->varprefix;
8600 3742 : context->varprefix = true;
8601 :
8602 : /*
8603 : * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
8604 : * upper-level Param, which wouldn't need extra parentheses.
8605 : * Otherwise, insert parens to ensure the expression looks atomic.
8606 : */
8607 3748 : need_paren = !(IsA(expr, Var) ||
8608 6 : IsA(expr, Aggref) ||
8609 6 : IsA(expr, GroupingFunc) ||
8610 0 : IsA(expr, Param));
8611 3742 : if (need_paren)
8612 0 : appendStringInfoChar(context->buf, '(');
8613 :
8614 3742 : get_rule_expr(expr, context, false);
8615 :
8616 3742 : if (need_paren)
8617 0 : appendStringInfoChar(context->buf, ')');
8618 :
8619 3742 : context->varprefix = save_varprefix;
8620 :
8621 3742 : pop_ancestor_plan(dpns, &save_dpns);
8622 :
8623 3742 : return;
8624 : }
8625 :
8626 : /*
8627 : * Alternatively, maybe it's a subplan output, which we print as a
8628 : * reference to the subplan. (We could drill down into the subplan and
8629 : * print the relevant targetlist expression, but that has been deemed too
8630 : * confusing since it would violate normal SQL scope rules. Also, we're
8631 : * relying on this reference to show that the testexpr containing the
8632 : * Param has anything to do with that subplan at all.)
8633 : */
8634 2438 : subplan = find_param_generator(param, context, &column);
8635 2438 : if (subplan)
8636 : {
8637 1576 : appendStringInfo(context->buf, "(%s%s).col%d",
8638 1576 : subplan->useHashTable ? "hashed " : "",
8639 : subplan->plan_name, column + 1);
8640 :
8641 1576 : return;
8642 : }
8643 :
8644 : /*
8645 : * If it's an external parameter, see if the outermost namespace provides
8646 : * function argument names.
8647 : */
8648 862 : if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
8649 : {
8650 862 : dpns = llast(context->namespaces);
8651 862 : if (dpns->argnames &&
8652 68 : param->paramid > 0 &&
8653 68 : param->paramid <= dpns->numargs)
8654 : {
8655 68 : char *argname = dpns->argnames[param->paramid - 1];
8656 :
8657 68 : if (argname)
8658 : {
8659 68 : bool should_qualify = false;
8660 : ListCell *lc;
8661 :
8662 : /*
8663 : * Qualify the parameter name if there are any other deparse
8664 : * namespaces with range tables. This avoids qualifying in
8665 : * trivial cases like "RETURN a + b", but makes it safe in all
8666 : * other cases.
8667 : */
8668 156 : foreach(lc, context->namespaces)
8669 : {
8670 118 : deparse_namespace *depns = lfirst(lc);
8671 :
8672 118 : if (depns->rtable_names != NIL)
8673 : {
8674 30 : should_qualify = true;
8675 30 : break;
8676 : }
8677 : }
8678 68 : if (should_qualify)
8679 : {
8680 30 : appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
8681 30 : appendStringInfoChar(context->buf, '.');
8682 : }
8683 :
8684 68 : appendStringInfoString(context->buf, quote_identifier(argname));
8685 68 : return;
8686 : }
8687 : }
8688 : }
8689 :
8690 : /*
8691 : * Not PARAM_EXEC, or couldn't find referent: just print $N.
8692 : *
8693 : * It's a bug if we get here for anything except PARAM_EXTERN Params, but
8694 : * in production builds printing $N seems more useful than failing.
8695 : */
8696 : Assert(param->paramkind == PARAM_EXTERN);
8697 :
8698 794 : appendStringInfo(context->buf, "$%d", param->paramid);
8699 : }
8700 :
8701 : /*
8702 : * get_simple_binary_op_name
8703 : *
8704 : * helper function for isSimpleNode
8705 : * will return single char binary operator name, or NULL if it's not
8706 : */
8707 : static const char *
8708 114 : get_simple_binary_op_name(OpExpr *expr)
8709 : {
8710 114 : List *args = expr->args;
8711 :
8712 114 : if (list_length(args) == 2)
8713 : {
8714 : /* binary operator */
8715 114 : Node *arg1 = (Node *) linitial(args);
8716 114 : Node *arg2 = (Node *) lsecond(args);
8717 : const char *op;
8718 :
8719 114 : op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
8720 114 : if (strlen(op) == 1)
8721 114 : return op;
8722 : }
8723 0 : return NULL;
8724 : }
8725 :
8726 :
8727 : /*
8728 : * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
8729 : *
8730 : * true : simple in the context of parent node's type
8731 : * false : not simple
8732 : */
8733 : static bool
8734 4882 : isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
8735 : {
8736 4882 : if (!node)
8737 0 : return false;
8738 :
8739 4882 : switch (nodeTag(node))
8740 : {
8741 4104 : case T_Var:
8742 : case T_Const:
8743 : case T_Param:
8744 : case T_CoerceToDomainValue:
8745 : case T_SetToDefault:
8746 : case T_CurrentOfExpr:
8747 : /* single words: always simple */
8748 4104 : return true;
8749 :
8750 412 : case T_SubscriptingRef:
8751 : case T_ArrayExpr:
8752 : case T_RowExpr:
8753 : case T_CoalesceExpr:
8754 : case T_MinMaxExpr:
8755 : case T_SQLValueFunction:
8756 : case T_XmlExpr:
8757 : case T_NextValueExpr:
8758 : case T_NullIfExpr:
8759 : case T_Aggref:
8760 : case T_GroupingFunc:
8761 : case T_WindowFunc:
8762 : case T_MergeSupportFunc:
8763 : case T_FuncExpr:
8764 : case T_JsonConstructorExpr:
8765 : case T_JsonExpr:
8766 : /* function-like: name(..) or name[..] */
8767 412 : return true;
8768 :
8769 : /* CASE keywords act as parentheses */
8770 0 : case T_CaseExpr:
8771 0 : return true;
8772 :
8773 54 : case T_FieldSelect:
8774 :
8775 : /*
8776 : * appears simple since . has top precedence, unless parent is
8777 : * T_FieldSelect itself!
8778 : */
8779 54 : return !IsA(parentNode, FieldSelect);
8780 :
8781 0 : case T_FieldStore:
8782 :
8783 : /*
8784 : * treat like FieldSelect (probably doesn't matter)
8785 : */
8786 0 : return !IsA(parentNode, FieldStore);
8787 :
8788 0 : case T_CoerceToDomain:
8789 : /* maybe simple, check args */
8790 0 : return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
8791 : node, prettyFlags);
8792 6 : case T_RelabelType:
8793 6 : return isSimpleNode((Node *) ((RelabelType *) node)->arg,
8794 : node, prettyFlags);
8795 0 : case T_CoerceViaIO:
8796 0 : return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
8797 : node, prettyFlags);
8798 0 : case T_ArrayCoerceExpr:
8799 0 : return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
8800 : node, prettyFlags);
8801 0 : case T_ConvertRowtypeExpr:
8802 0 : return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
8803 : node, prettyFlags);
8804 :
8805 252 : case T_OpExpr:
8806 : {
8807 : /* depends on parent node type; needs further checking */
8808 252 : if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
8809 : {
8810 : const char *op;
8811 : const char *parentOp;
8812 : bool is_lopriop;
8813 : bool is_hipriop;
8814 : bool is_lopriparent;
8815 : bool is_hipriparent;
8816 :
8817 60 : op = get_simple_binary_op_name((OpExpr *) node);
8818 60 : if (!op)
8819 0 : return false;
8820 :
8821 : /* We know only the basic operators + - and * / % */
8822 60 : is_lopriop = (strchr("+-", *op) != NULL);
8823 60 : is_hipriop = (strchr("*/%", *op) != NULL);
8824 60 : if (!(is_lopriop || is_hipriop))
8825 6 : return false;
8826 :
8827 54 : parentOp = get_simple_binary_op_name((OpExpr *) parentNode);
8828 54 : if (!parentOp)
8829 0 : return false;
8830 :
8831 54 : is_lopriparent = (strchr("+-", *parentOp) != NULL);
8832 54 : is_hipriparent = (strchr("*/%", *parentOp) != NULL);
8833 54 : if (!(is_lopriparent || is_hipriparent))
8834 0 : return false;
8835 :
8836 54 : if (is_hipriop && is_lopriparent)
8837 12 : return true; /* op binds tighter than parent */
8838 :
8839 42 : if (is_lopriop && is_hipriparent)
8840 30 : return false;
8841 :
8842 : /*
8843 : * Operators are same priority --- can skip parens only if
8844 : * we have (a - b) - c, not a - (b - c).
8845 : */
8846 12 : if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
8847 6 : return true;
8848 :
8849 6 : return false;
8850 : }
8851 : /* else do the same stuff as for T_SubLink et al. */
8852 : }
8853 : /* FALLTHROUGH */
8854 :
8855 : case T_SubLink:
8856 : case T_NullTest:
8857 : case T_BooleanTest:
8858 : case T_DistinctExpr:
8859 : case T_JsonIsPredicate:
8860 222 : switch (nodeTag(parentNode))
8861 : {
8862 30 : case T_FuncExpr:
8863 : {
8864 : /* special handling for casts and COERCE_SQL_SYNTAX */
8865 30 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
8866 :
8867 30 : if (type == COERCE_EXPLICIT_CAST ||
8868 6 : type == COERCE_IMPLICIT_CAST ||
8869 : type == COERCE_SQL_SYNTAX)
8870 30 : return false;
8871 0 : return true; /* own parentheses */
8872 : }
8873 162 : case T_BoolExpr: /* lower precedence */
8874 : case T_SubscriptingRef: /* other separators */
8875 : case T_ArrayExpr: /* other separators */
8876 : case T_RowExpr: /* other separators */
8877 : case T_CoalesceExpr: /* own parentheses */
8878 : case T_MinMaxExpr: /* own parentheses */
8879 : case T_XmlExpr: /* own parentheses */
8880 : case T_NullIfExpr: /* other separators */
8881 : case T_Aggref: /* own parentheses */
8882 : case T_GroupingFunc: /* own parentheses */
8883 : case T_WindowFunc: /* own parentheses */
8884 : case T_CaseExpr: /* other separators */
8885 162 : return true;
8886 30 : default:
8887 30 : return false;
8888 : }
8889 :
8890 18 : case T_BoolExpr:
8891 18 : switch (nodeTag(parentNode))
8892 : {
8893 18 : case T_BoolExpr:
8894 18 : if (prettyFlags & PRETTYFLAG_PAREN)
8895 : {
8896 : BoolExprType type;
8897 : BoolExprType parentType;
8898 :
8899 18 : type = ((BoolExpr *) node)->boolop;
8900 18 : parentType = ((BoolExpr *) parentNode)->boolop;
8901 : switch (type)
8902 : {
8903 12 : case NOT_EXPR:
8904 : case AND_EXPR:
8905 12 : if (parentType == AND_EXPR || parentType == OR_EXPR)
8906 12 : return true;
8907 0 : break;
8908 6 : case OR_EXPR:
8909 6 : if (parentType == OR_EXPR)
8910 0 : return true;
8911 6 : break;
8912 : }
8913 0 : }
8914 6 : return false;
8915 0 : case T_FuncExpr:
8916 : {
8917 : /* special handling for casts and COERCE_SQL_SYNTAX */
8918 0 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
8919 :
8920 0 : if (type == COERCE_EXPLICIT_CAST ||
8921 0 : type == COERCE_IMPLICIT_CAST ||
8922 : type == COERCE_SQL_SYNTAX)
8923 0 : return false;
8924 0 : return true; /* own parentheses */
8925 : }
8926 0 : case T_SubscriptingRef: /* other separators */
8927 : case T_ArrayExpr: /* other separators */
8928 : case T_RowExpr: /* other separators */
8929 : case T_CoalesceExpr: /* own parentheses */
8930 : case T_MinMaxExpr: /* own parentheses */
8931 : case T_XmlExpr: /* own parentheses */
8932 : case T_NullIfExpr: /* other separators */
8933 : case T_Aggref: /* own parentheses */
8934 : case T_GroupingFunc: /* own parentheses */
8935 : case T_WindowFunc: /* own parentheses */
8936 : case T_CaseExpr: /* other separators */
8937 : case T_JsonExpr: /* own parentheses */
8938 0 : return true;
8939 0 : default:
8940 0 : return false;
8941 : }
8942 :
8943 0 : case T_JsonValueExpr:
8944 : /* maybe simple, check args */
8945 0 : return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
8946 : node, prettyFlags);
8947 :
8948 6 : default:
8949 6 : break;
8950 : }
8951 : /* those we don't know: in dubio complexo */
8952 6 : return false;
8953 : }
8954 :
8955 :
8956 : /*
8957 : * appendContextKeyword - append a keyword to buffer
8958 : *
8959 : * If prettyPrint is enabled, perform a line break, and adjust indentation.
8960 : * Otherwise, just append the keyword.
8961 : */
8962 : static void
8963 28656 : appendContextKeyword(deparse_context *context, const char *str,
8964 : int indentBefore, int indentAfter, int indentPlus)
8965 : {
8966 28656 : StringInfo buf = context->buf;
8967 :
8968 28656 : if (PRETTY_INDENT(context))
8969 : {
8970 : int indentAmount;
8971 :
8972 27740 : context->indentLevel += indentBefore;
8973 :
8974 : /* remove any trailing spaces currently in the buffer ... */
8975 27740 : removeStringInfoSpaces(buf);
8976 : /* ... then add a newline and some spaces */
8977 27740 : appendStringInfoChar(buf, '\n');
8978 :
8979 27740 : if (context->indentLevel < PRETTYINDENT_LIMIT)
8980 27740 : indentAmount = Max(context->indentLevel, 0) + indentPlus;
8981 : else
8982 : {
8983 : /*
8984 : * If we're indented more than PRETTYINDENT_LIMIT characters, try
8985 : * to conserve horizontal space by reducing the per-level
8986 : * indentation. For best results the scale factor here should
8987 : * divide all the indent amounts that get added to indentLevel
8988 : * (PRETTYINDENT_STD, etc). It's important that the indentation
8989 : * not grow unboundedly, else deeply-nested trees use O(N^2)
8990 : * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
8991 : */
8992 0 : indentAmount = PRETTYINDENT_LIMIT +
8993 0 : (context->indentLevel - PRETTYINDENT_LIMIT) /
8994 : (PRETTYINDENT_STD / 2);
8995 0 : indentAmount %= PRETTYINDENT_LIMIT;
8996 : /* scale/wrap logic affects indentLevel, but not indentPlus */
8997 0 : indentAmount += indentPlus;
8998 : }
8999 27740 : appendStringInfoSpaces(buf, indentAmount);
9000 :
9001 27740 : appendStringInfoString(buf, str);
9002 :
9003 27740 : context->indentLevel += indentAfter;
9004 27740 : if (context->indentLevel < 0)
9005 0 : context->indentLevel = 0;
9006 : }
9007 : else
9008 916 : appendStringInfoString(buf, str);
9009 28656 : }
9010 :
9011 : /*
9012 : * removeStringInfoSpaces - delete trailing spaces from a buffer.
9013 : *
9014 : * Possibly this should move to stringinfo.c at some point.
9015 : */
9016 : static void
9017 28202 : removeStringInfoSpaces(StringInfo str)
9018 : {
9019 44052 : while (str->len > 0 && str->data[str->len - 1] == ' ')
9020 15850 : str->data[--(str->len)] = '\0';
9021 28202 : }
9022 :
9023 :
9024 : /*
9025 : * get_rule_expr_paren - deparse expr using get_rule_expr,
9026 : * embracing the string with parentheses if necessary for prettyPrint.
9027 : *
9028 : * Never embrace if prettyFlags=0, because it's done in the calling node.
9029 : *
9030 : * Any node that does *not* embrace its argument node by sql syntax (with
9031 : * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
9032 : * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
9033 : * added.
9034 : */
9035 : static void
9036 148156 : get_rule_expr_paren(Node *node, deparse_context *context,
9037 : bool showimplicit, Node *parentNode)
9038 : {
9039 : bool need_paren;
9040 :
9041 153032 : need_paren = PRETTY_PAREN(context) &&
9042 4876 : !isSimpleNode(node, parentNode, context->prettyFlags);
9043 :
9044 148156 : if (need_paren)
9045 114 : appendStringInfoChar(context->buf, '(');
9046 :
9047 148156 : get_rule_expr(node, context, showimplicit);
9048 :
9049 148156 : if (need_paren)
9050 114 : appendStringInfoChar(context->buf, ')');
9051 148156 : }
9052 :
9053 : static void
9054 84 : get_json_behavior(JsonBehavior *behavior, deparse_context *context,
9055 : const char *on)
9056 : {
9057 : /*
9058 : * The order of array elements must correspond to the order of
9059 : * JsonBehaviorType members.
9060 : */
9061 84 : const char *behavior_names[] =
9062 : {
9063 : " NULL",
9064 : " ERROR",
9065 : " EMPTY",
9066 : " TRUE",
9067 : " FALSE",
9068 : " UNKNOWN",
9069 : " EMPTY ARRAY",
9070 : " EMPTY OBJECT",
9071 : " DEFAULT "
9072 : };
9073 :
9074 84 : if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
9075 0 : elog(ERROR, "invalid json behavior type: %d", behavior->btype);
9076 :
9077 84 : appendStringInfoString(context->buf, behavior_names[behavior->btype]);
9078 :
9079 84 : if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
9080 18 : get_rule_expr(behavior->expr, context, false);
9081 :
9082 84 : appendStringInfo(context->buf, " ON %s", on);
9083 84 : }
9084 :
9085 : /*
9086 : * get_json_expr_options
9087 : *
9088 : * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and
9089 : * JSON_TABLE columns.
9090 : */
9091 : static void
9092 456 : get_json_expr_options(JsonExpr *jsexpr, deparse_context *context,
9093 : JsonBehaviorType default_behavior)
9094 : {
9095 456 : if (jsexpr->op == JSON_QUERY_OP)
9096 : {
9097 210 : if (jsexpr->wrapper == JSW_CONDITIONAL)
9098 12 : appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
9099 198 : else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
9100 30 : appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
9101 : /* The default */
9102 168 : else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
9103 168 : appendStringInfoString(context->buf, " WITHOUT WRAPPER");
9104 :
9105 210 : if (jsexpr->omit_quotes)
9106 42 : appendStringInfoString(context->buf, " OMIT QUOTES");
9107 : /* The default */
9108 : else
9109 168 : appendStringInfoString(context->buf, " KEEP QUOTES");
9110 : }
9111 :
9112 456 : if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
9113 30 : get_json_behavior(jsexpr->on_empty, context, "EMPTY");
9114 :
9115 456 : if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
9116 48 : get_json_behavior(jsexpr->on_error, context, "ERROR");
9117 456 : }
9118 :
9119 : /* ----------
9120 : * get_rule_expr - Parse back an expression
9121 : *
9122 : * Note: showimplicit determines whether we display any implicit cast that
9123 : * is present at the top of the expression tree. It is a passed argument,
9124 : * not a field of the context struct, because we change the value as we
9125 : * recurse down into the expression. In general we suppress implicit casts
9126 : * when the result type is known with certainty (eg, the arguments of an
9127 : * OR must be boolean). We display implicit casts for arguments of functions
9128 : * and operators, since this is needed to be certain that the same function
9129 : * or operator will be chosen when the expression is re-parsed.
9130 : * ----------
9131 : */
9132 : static void
9133 299938 : get_rule_expr(Node *node, deparse_context *context,
9134 : bool showimplicit)
9135 : {
9136 299938 : StringInfo buf = context->buf;
9137 :
9138 299938 : if (node == NULL)
9139 90 : return;
9140 :
9141 : /* Guard against excessively long or deeply-nested queries */
9142 299848 : CHECK_FOR_INTERRUPTS();
9143 299848 : check_stack_depth();
9144 :
9145 : /*
9146 : * Each level of get_rule_expr must emit an indivisible term
9147 : * (parenthesized if necessary) to ensure result is reparsed into the same
9148 : * expression tree. The only exception is that when the input is a List,
9149 : * we emit the component items comma-separated with no surrounding
9150 : * decoration; this is convenient for most callers.
9151 : */
9152 299848 : switch (nodeTag(node))
9153 : {
9154 136524 : case T_Var:
9155 136524 : (void) get_variable((Var *) node, 0, false, context);
9156 136524 : break;
9157 :
9158 56584 : case T_Const:
9159 56584 : get_const_expr((Const *) node, context, 0);
9160 56584 : break;
9161 :
9162 6180 : case T_Param:
9163 6180 : get_parameter((Param *) node, context);
9164 6180 : break;
9165 :
9166 1632 : case T_Aggref:
9167 1632 : get_agg_expr((Aggref *) node, context, (Aggref *) node);
9168 1632 : break;
9169 :
9170 112 : case T_GroupingFunc:
9171 : {
9172 112 : GroupingFunc *gexpr = (GroupingFunc *) node;
9173 :
9174 112 : appendStringInfoString(buf, "GROUPING(");
9175 112 : get_rule_expr((Node *) gexpr->args, context, true);
9176 112 : appendStringInfoChar(buf, ')');
9177 : }
9178 112 : break;
9179 :
9180 240 : case T_WindowFunc:
9181 240 : get_windowfunc_expr((WindowFunc *) node, context);
9182 240 : break;
9183 :
9184 6 : case T_MergeSupportFunc:
9185 6 : appendStringInfoString(buf, "MERGE_ACTION()");
9186 6 : break;
9187 :
9188 328 : case T_SubscriptingRef:
9189 : {
9190 328 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
9191 : bool need_parens;
9192 :
9193 : /*
9194 : * If the argument is a CaseTestExpr, we must be inside a
9195 : * FieldStore, ie, we are assigning to an element of an array
9196 : * within a composite column. Since we already punted on
9197 : * displaying the FieldStore's target information, just punt
9198 : * here too, and display only the assignment source
9199 : * expression.
9200 : */
9201 328 : if (IsA(sbsref->refexpr, CaseTestExpr))
9202 : {
9203 : Assert(sbsref->refassgnexpr);
9204 0 : get_rule_expr((Node *) sbsref->refassgnexpr,
9205 : context, showimplicit);
9206 0 : break;
9207 : }
9208 :
9209 : /*
9210 : * Parenthesize the argument unless it's a simple Var or a
9211 : * FieldSelect. (In particular, if it's another
9212 : * SubscriptingRef, we *must* parenthesize to avoid
9213 : * confusion.)
9214 : */
9215 482 : need_parens = !IsA(sbsref->refexpr, Var) &&
9216 154 : !IsA(sbsref->refexpr, FieldSelect);
9217 328 : if (need_parens)
9218 94 : appendStringInfoChar(buf, '(');
9219 328 : get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
9220 328 : if (need_parens)
9221 94 : appendStringInfoChar(buf, ')');
9222 :
9223 : /*
9224 : * If there's a refassgnexpr, we want to print the node in the
9225 : * format "container[subscripts] := refassgnexpr". This is
9226 : * not legal SQL, so decompilation of INSERT or UPDATE
9227 : * statements should always use processIndirection as part of
9228 : * the statement-level syntax. We should only see this when
9229 : * EXPLAIN tries to print the targetlist of a plan resulting
9230 : * from such a statement.
9231 : */
9232 328 : if (sbsref->refassgnexpr)
9233 : {
9234 : Node *refassgnexpr;
9235 :
9236 : /*
9237 : * Use processIndirection to print this node's subscripts
9238 : * as well as any additional field selections or
9239 : * subscripting in immediate descendants. It returns the
9240 : * RHS expr that is actually being "assigned".
9241 : */
9242 12 : refassgnexpr = processIndirection(node, context);
9243 12 : appendStringInfoString(buf, " := ");
9244 12 : get_rule_expr(refassgnexpr, context, showimplicit);
9245 : }
9246 : else
9247 : {
9248 : /* Just an ordinary container fetch, so print subscripts */
9249 316 : printSubscripts(sbsref, context);
9250 : }
9251 : }
9252 328 : break;
9253 :
9254 11528 : case T_FuncExpr:
9255 11528 : get_func_expr((FuncExpr *) node, context, showimplicit);
9256 11528 : break;
9257 :
9258 18 : case T_NamedArgExpr:
9259 : {
9260 18 : NamedArgExpr *na = (NamedArgExpr *) node;
9261 :
9262 18 : appendStringInfo(buf, "%s => ", quote_identifier(na->name));
9263 18 : get_rule_expr((Node *) na->arg, context, showimplicit);
9264 : }
9265 18 : break;
9266 :
9267 55346 : case T_OpExpr:
9268 55346 : get_oper_expr((OpExpr *) node, context);
9269 55346 : break;
9270 :
9271 18 : case T_DistinctExpr:
9272 : {
9273 18 : DistinctExpr *expr = (DistinctExpr *) node;
9274 18 : List *args = expr->args;
9275 18 : Node *arg1 = (Node *) linitial(args);
9276 18 : Node *arg2 = (Node *) lsecond(args);
9277 :
9278 18 : if (!PRETTY_PAREN(context))
9279 12 : appendStringInfoChar(buf, '(');
9280 18 : get_rule_expr_paren(arg1, context, true, node);
9281 18 : appendStringInfoString(buf, " IS DISTINCT FROM ");
9282 18 : get_rule_expr_paren(arg2, context, true, node);
9283 18 : if (!PRETTY_PAREN(context))
9284 12 : appendStringInfoChar(buf, ')');
9285 : }
9286 18 : break;
9287 :
9288 20 : case T_NullIfExpr:
9289 : {
9290 20 : NullIfExpr *nullifexpr = (NullIfExpr *) node;
9291 :
9292 20 : appendStringInfoString(buf, "NULLIF(");
9293 20 : get_rule_expr((Node *) nullifexpr->args, context, true);
9294 20 : appendStringInfoChar(buf, ')');
9295 : }
9296 20 : break;
9297 :
9298 2696 : case T_ScalarArrayOpExpr:
9299 : {
9300 2696 : ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
9301 2696 : List *args = expr->args;
9302 2696 : Node *arg1 = (Node *) linitial(args);
9303 2696 : Node *arg2 = (Node *) lsecond(args);
9304 :
9305 2696 : if (!PRETTY_PAREN(context))
9306 2684 : appendStringInfoChar(buf, '(');
9307 2696 : get_rule_expr_paren(arg1, context, true, node);
9308 2696 : appendStringInfo(buf, " %s %s (",
9309 : generate_operator_name(expr->opno,
9310 : exprType(arg1),
9311 : get_base_element_type(exprType(arg2))),
9312 2696 : expr->useOr ? "ANY" : "ALL");
9313 2696 : get_rule_expr_paren(arg2, context, true, node);
9314 :
9315 : /*
9316 : * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
9317 : * a bare sub-SELECT. Since we're here, the sub-SELECT must
9318 : * be meant as a scalar sub-SELECT yielding an array value to
9319 : * be used in ScalarArrayOpExpr; but the grammar will
9320 : * preferentially interpret such a construct as an ANY/ALL
9321 : * SubLink. To prevent misparsing the output that way, insert
9322 : * a dummy coercion (which will be stripped by parse analysis,
9323 : * so no inefficiency is added in dump and reload). This is
9324 : * indeed most likely what the user wrote to get the construct
9325 : * accepted in the first place.
9326 : */
9327 2696 : if (IsA(arg2, SubLink) &&
9328 6 : ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
9329 6 : appendStringInfo(buf, "::%s",
9330 : format_type_with_typemod(exprType(arg2),
9331 : exprTypmod(arg2)));
9332 2696 : appendStringInfoChar(buf, ')');
9333 2696 : if (!PRETTY_PAREN(context))
9334 2684 : appendStringInfoChar(buf, ')');
9335 : }
9336 2696 : break;
9337 :
9338 10416 : case T_BoolExpr:
9339 : {
9340 10416 : BoolExpr *expr = (BoolExpr *) node;
9341 10416 : Node *first_arg = linitial(expr->args);
9342 : ListCell *arg;
9343 :
9344 10416 : switch (expr->boolop)
9345 : {
9346 8474 : case AND_EXPR:
9347 8474 : if (!PRETTY_PAREN(context))
9348 8408 : appendStringInfoChar(buf, '(');
9349 8474 : get_rule_expr_paren(first_arg, context,
9350 : false, node);
9351 19308 : for_each_from(arg, expr->args, 1)
9352 : {
9353 10834 : appendStringInfoString(buf, " AND ");
9354 10834 : get_rule_expr_paren((Node *) lfirst(arg), context,
9355 : false, node);
9356 : }
9357 8474 : if (!PRETTY_PAREN(context))
9358 8408 : appendStringInfoChar(buf, ')');
9359 8474 : break;
9360 :
9361 1588 : case OR_EXPR:
9362 1588 : if (!PRETTY_PAREN(context))
9363 1576 : appendStringInfoChar(buf, '(');
9364 1588 : get_rule_expr_paren(first_arg, context,
9365 : false, node);
9366 3752 : for_each_from(arg, expr->args, 1)
9367 : {
9368 2164 : appendStringInfoString(buf, " OR ");
9369 2164 : get_rule_expr_paren((Node *) lfirst(arg), context,
9370 : false, node);
9371 : }
9372 1588 : if (!PRETTY_PAREN(context))
9373 1576 : appendStringInfoChar(buf, ')');
9374 1588 : break;
9375 :
9376 354 : case NOT_EXPR:
9377 354 : if (!PRETTY_PAREN(context))
9378 342 : appendStringInfoChar(buf, '(');
9379 354 : appendStringInfoString(buf, "NOT ");
9380 354 : get_rule_expr_paren(first_arg, context,
9381 : false, node);
9382 354 : if (!PRETTY_PAREN(context))
9383 342 : appendStringInfoChar(buf, ')');
9384 354 : break;
9385 :
9386 0 : default:
9387 0 : elog(ERROR, "unrecognized boolop: %d",
9388 : (int) expr->boolop);
9389 : }
9390 : }
9391 10416 : break;
9392 :
9393 418 : case T_SubLink:
9394 418 : get_sublink_expr((SubLink *) node, context);
9395 418 : break;
9396 :
9397 646 : case T_SubPlan:
9398 : {
9399 646 : SubPlan *subplan = (SubPlan *) node;
9400 :
9401 : /*
9402 : * We cannot see an already-planned subplan in rule deparsing,
9403 : * only while EXPLAINing a query plan. We don't try to
9404 : * reconstruct the original SQL, just reference the subplan
9405 : * that appears elsewhere in EXPLAIN's result. It does seem
9406 : * useful to show the subLinkType and testexpr (if any), and
9407 : * we also note whether the subplan will be hashed.
9408 : */
9409 646 : switch (subplan->subLinkType)
9410 : {
9411 78 : case EXISTS_SUBLINK:
9412 78 : appendStringInfoString(buf, "EXISTS(");
9413 : Assert(subplan->testexpr == NULL);
9414 78 : break;
9415 6 : case ALL_SUBLINK:
9416 6 : appendStringInfoString(buf, "(ALL ");
9417 : Assert(subplan->testexpr != NULL);
9418 6 : break;
9419 160 : case ANY_SUBLINK:
9420 160 : appendStringInfoString(buf, "(ANY ");
9421 : Assert(subplan->testexpr != NULL);
9422 160 : break;
9423 6 : case ROWCOMPARE_SUBLINK:
9424 : /* Parenthesizing the testexpr seems sufficient */
9425 6 : appendStringInfoChar(buf, '(');
9426 : Assert(subplan->testexpr != NULL);
9427 6 : break;
9428 358 : case EXPR_SUBLINK:
9429 : /* No need to decorate these subplan references */
9430 358 : appendStringInfoChar(buf, '(');
9431 : Assert(subplan->testexpr == NULL);
9432 358 : break;
9433 26 : case MULTIEXPR_SUBLINK:
9434 : /* MULTIEXPR isn't executed in the normal way */
9435 26 : appendStringInfoString(buf, "(rescan ");
9436 : Assert(subplan->testexpr == NULL);
9437 26 : break;
9438 12 : case ARRAY_SUBLINK:
9439 12 : appendStringInfoString(buf, "ARRAY(");
9440 : Assert(subplan->testexpr == NULL);
9441 12 : break;
9442 0 : case CTE_SUBLINK:
9443 : /* This case is unreachable within expressions */
9444 0 : appendStringInfoString(buf, "CTE(");
9445 : Assert(subplan->testexpr == NULL);
9446 0 : break;
9447 : }
9448 :
9449 646 : if (subplan->testexpr != NULL)
9450 : {
9451 : deparse_namespace *dpns;
9452 :
9453 : /*
9454 : * Push SubPlan into ancestors list while deparsing
9455 : * testexpr, so that we can handle PARAM_EXEC references
9456 : * to the SubPlan's paramIds. (This makes it look like
9457 : * the SubPlan is an "ancestor" of the current plan node,
9458 : * which is a little weird, but it does no harm.) In this
9459 : * path, we don't need to mention the SubPlan explicitly,
9460 : * because the referencing Params will show its existence.
9461 : */
9462 172 : dpns = (deparse_namespace *) linitial(context->namespaces);
9463 172 : dpns->ancestors = lcons(subplan, dpns->ancestors);
9464 :
9465 172 : get_rule_expr(subplan->testexpr, context, showimplicit);
9466 172 : appendStringInfoChar(buf, ')');
9467 :
9468 172 : dpns->ancestors = list_delete_first(dpns->ancestors);
9469 : }
9470 : else
9471 : {
9472 : /* No referencing Params, so show the SubPlan's name */
9473 474 : if (subplan->useHashTable)
9474 0 : appendStringInfo(buf, "hashed %s)", subplan->plan_name);
9475 : else
9476 474 : appendStringInfo(buf, "%s)", subplan->plan_name);
9477 : }
9478 : }
9479 646 : break;
9480 :
9481 0 : case T_AlternativeSubPlan:
9482 : {
9483 0 : AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
9484 : ListCell *lc;
9485 :
9486 : /*
9487 : * This case cannot be reached in normal usage, since no
9488 : * AlternativeSubPlan can appear either in parsetrees or
9489 : * finished plan trees. We keep it just in case somebody
9490 : * wants to use this code to print planner data structures.
9491 : */
9492 0 : appendStringInfoString(buf, "(alternatives: ");
9493 0 : foreach(lc, asplan->subplans)
9494 : {
9495 0 : SubPlan *splan = lfirst_node(SubPlan, lc);
9496 :
9497 0 : if (splan->useHashTable)
9498 0 : appendStringInfo(buf, "hashed %s", splan->plan_name);
9499 : else
9500 0 : appendStringInfoString(buf, splan->plan_name);
9501 0 : if (lnext(asplan->subplans, lc))
9502 0 : appendStringInfoString(buf, " or ");
9503 : }
9504 0 : appendStringInfoChar(buf, ')');
9505 : }
9506 0 : break;
9507 :
9508 1118 : case T_FieldSelect:
9509 : {
9510 1118 : FieldSelect *fselect = (FieldSelect *) node;
9511 1118 : Node *arg = (Node *) fselect->arg;
9512 1118 : int fno = fselect->fieldnum;
9513 : const char *fieldname;
9514 : bool need_parens;
9515 :
9516 : /*
9517 : * Parenthesize the argument unless it's an SubscriptingRef or
9518 : * another FieldSelect. Note in particular that it would be
9519 : * WRONG to not parenthesize a Var argument; simplicity is not
9520 : * the issue here, having the right number of names is.
9521 : */
9522 2200 : need_parens = !IsA(arg, SubscriptingRef) &&
9523 1082 : !IsA(arg, FieldSelect);
9524 1118 : if (need_parens)
9525 1082 : appendStringInfoChar(buf, '(');
9526 1118 : get_rule_expr(arg, context, true);
9527 1118 : if (need_parens)
9528 1082 : appendStringInfoChar(buf, ')');
9529 :
9530 : /*
9531 : * Get and print the field name.
9532 : */
9533 1118 : fieldname = get_name_for_var_field((Var *) arg, fno,
9534 : 0, context);
9535 1118 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
9536 : }
9537 1118 : break;
9538 :
9539 6 : case T_FieldStore:
9540 : {
9541 6 : FieldStore *fstore = (FieldStore *) node;
9542 : bool need_parens;
9543 :
9544 : /*
9545 : * There is no good way to represent a FieldStore as real SQL,
9546 : * so decompilation of INSERT or UPDATE statements should
9547 : * always use processIndirection as part of the
9548 : * statement-level syntax. We should only get here when
9549 : * EXPLAIN tries to print the targetlist of a plan resulting
9550 : * from such a statement. The plan case is even harder than
9551 : * ordinary rules would be, because the planner tries to
9552 : * collapse multiple assignments to the same field or subfield
9553 : * into one FieldStore; so we can see a list of target fields
9554 : * not just one, and the arguments could be FieldStores
9555 : * themselves. We don't bother to try to print the target
9556 : * field names; we just print the source arguments, with a
9557 : * ROW() around them if there's more than one. This isn't
9558 : * terribly complete, but it's probably good enough for
9559 : * EXPLAIN's purposes; especially since anything more would be
9560 : * either hopelessly confusing or an even poorer
9561 : * representation of what the plan is actually doing.
9562 : */
9563 6 : need_parens = (list_length(fstore->newvals) != 1);
9564 6 : if (need_parens)
9565 6 : appendStringInfoString(buf, "ROW(");
9566 6 : get_rule_expr((Node *) fstore->newvals, context, showimplicit);
9567 6 : if (need_parens)
9568 6 : appendStringInfoChar(buf, ')');
9569 : }
9570 6 : break;
9571 :
9572 2456 : case T_RelabelType:
9573 : {
9574 2456 : RelabelType *relabel = (RelabelType *) node;
9575 2456 : Node *arg = (Node *) relabel->arg;
9576 :
9577 2456 : if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
9578 2318 : !showimplicit)
9579 : {
9580 : /* don't show the implicit cast */
9581 64 : get_rule_expr_paren(arg, context, false, node);
9582 : }
9583 : else
9584 : {
9585 2392 : get_coercion_expr(arg, context,
9586 : relabel->resulttype,
9587 : relabel->resulttypmod,
9588 : node);
9589 : }
9590 : }
9591 2456 : break;
9592 :
9593 644 : case T_CoerceViaIO:
9594 : {
9595 644 : CoerceViaIO *iocoerce = (CoerceViaIO *) node;
9596 644 : Node *arg = (Node *) iocoerce->arg;
9597 :
9598 644 : if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
9599 24 : !showimplicit)
9600 : {
9601 : /* don't show the implicit cast */
9602 24 : get_rule_expr_paren(arg, context, false, node);
9603 : }
9604 : else
9605 : {
9606 620 : get_coercion_expr(arg, context,
9607 : iocoerce->resulttype,
9608 : -1,
9609 : node);
9610 : }
9611 : }
9612 644 : break;
9613 :
9614 48 : case T_ArrayCoerceExpr:
9615 : {
9616 48 : ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
9617 48 : Node *arg = (Node *) acoerce->arg;
9618 :
9619 48 : if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
9620 48 : !showimplicit)
9621 : {
9622 : /* don't show the implicit cast */
9623 0 : get_rule_expr_paren(arg, context, false, node);
9624 : }
9625 : else
9626 : {
9627 48 : get_coercion_expr(arg, context,
9628 : acoerce->resulttype,
9629 : acoerce->resulttypmod,
9630 : node);
9631 : }
9632 : }
9633 48 : break;
9634 :
9635 88 : case T_ConvertRowtypeExpr:
9636 : {
9637 88 : ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
9638 88 : Node *arg = (Node *) convert->arg;
9639 :
9640 88 : if (convert->convertformat == COERCE_IMPLICIT_CAST &&
9641 82 : !showimplicit)
9642 : {
9643 : /* don't show the implicit cast */
9644 24 : get_rule_expr_paren(arg, context, false, node);
9645 : }
9646 : else
9647 : {
9648 64 : get_coercion_expr(arg, context,
9649 : convert->resulttype, -1,
9650 : node);
9651 : }
9652 : }
9653 88 : break;
9654 :
9655 90 : case T_CollateExpr:
9656 : {
9657 90 : CollateExpr *collate = (CollateExpr *) node;
9658 90 : Node *arg = (Node *) collate->arg;
9659 :
9660 90 : if (!PRETTY_PAREN(context))
9661 84 : appendStringInfoChar(buf, '(');
9662 90 : get_rule_expr_paren(arg, context, showimplicit, node);
9663 90 : appendStringInfo(buf, " COLLATE %s",
9664 : generate_collation_name(collate->collOid));
9665 90 : if (!PRETTY_PAREN(context))
9666 84 : appendStringInfoChar(buf, ')');
9667 : }
9668 90 : break;
9669 :
9670 594 : case T_CaseExpr:
9671 : {
9672 594 : CaseExpr *caseexpr = (CaseExpr *) node;
9673 : ListCell *temp;
9674 :
9675 594 : appendContextKeyword(context, "CASE",
9676 : 0, PRETTYINDENT_VAR, 0);
9677 594 : if (caseexpr->arg)
9678 : {
9679 174 : appendStringInfoChar(buf, ' ');
9680 174 : get_rule_expr((Node *) caseexpr->arg, context, true);
9681 : }
9682 2626 : foreach(temp, caseexpr->args)
9683 : {
9684 2032 : CaseWhen *when = (CaseWhen *) lfirst(temp);
9685 2032 : Node *w = (Node *) when->expr;
9686 :
9687 2032 : if (caseexpr->arg)
9688 : {
9689 : /*
9690 : * The parser should have produced WHEN clauses of the
9691 : * form "CaseTestExpr = RHS", possibly with an
9692 : * implicit coercion inserted above the CaseTestExpr.
9693 : * For accurate decompilation of rules it's essential
9694 : * that we show just the RHS. However in an
9695 : * expression that's been through the optimizer, the
9696 : * WHEN clause could be almost anything (since the
9697 : * equality operator could have been expanded into an
9698 : * inline function). If we don't recognize the form
9699 : * of the WHEN clause, just punt and display it as-is.
9700 : */
9701 748 : if (IsA(w, OpExpr))
9702 : {
9703 748 : List *args = ((OpExpr *) w)->args;
9704 :
9705 748 : if (list_length(args) == 2 &&
9706 748 : IsA(strip_implicit_coercions(linitial(args)),
9707 : CaseTestExpr))
9708 748 : w = (Node *) lsecond(args);
9709 : }
9710 : }
9711 :
9712 2032 : if (!PRETTY_INDENT(context))
9713 118 : appendStringInfoChar(buf, ' ');
9714 2032 : appendContextKeyword(context, "WHEN ",
9715 : 0, 0, 0);
9716 2032 : get_rule_expr(w, context, false);
9717 2032 : appendStringInfoString(buf, " THEN ");
9718 2032 : get_rule_expr((Node *) when->result, context, true);
9719 : }
9720 594 : if (!PRETTY_INDENT(context))
9721 108 : appendStringInfoChar(buf, ' ');
9722 594 : appendContextKeyword(context, "ELSE ",
9723 : 0, 0, 0);
9724 594 : get_rule_expr((Node *) caseexpr->defresult, context, true);
9725 594 : if (!PRETTY_INDENT(context))
9726 108 : appendStringInfoChar(buf, ' ');
9727 594 : appendContextKeyword(context, "END",
9728 : -PRETTYINDENT_VAR, 0, 0);
9729 : }
9730 594 : break;
9731 :
9732 0 : case T_CaseTestExpr:
9733 : {
9734 : /*
9735 : * Normally we should never get here, since for expressions
9736 : * that can contain this node type we attempt to avoid
9737 : * recursing to it. But in an optimized expression we might
9738 : * be unable to avoid that (see comments for CaseExpr). If we
9739 : * do see one, print it as CASE_TEST_EXPR.
9740 : */
9741 0 : appendStringInfoString(buf, "CASE_TEST_EXPR");
9742 : }
9743 0 : break;
9744 :
9745 530 : case T_ArrayExpr:
9746 : {
9747 530 : ArrayExpr *arrayexpr = (ArrayExpr *) node;
9748 :
9749 530 : appendStringInfoString(buf, "ARRAY[");
9750 530 : get_rule_expr((Node *) arrayexpr->elements, context, true);
9751 530 : appendStringInfoChar(buf, ']');
9752 :
9753 : /*
9754 : * If the array isn't empty, we assume its elements are
9755 : * coerced to the desired type. If it's empty, though, we
9756 : * need an explicit coercion to the array type.
9757 : */
9758 530 : if (arrayexpr->elements == NIL)
9759 6 : appendStringInfo(buf, "::%s",
9760 : format_type_with_typemod(arrayexpr->array_typeid, -1));
9761 : }
9762 530 : break;
9763 :
9764 186 : case T_RowExpr:
9765 : {
9766 186 : RowExpr *rowexpr = (RowExpr *) node;
9767 186 : TupleDesc tupdesc = NULL;
9768 : ListCell *arg;
9769 : int i;
9770 : char *sep;
9771 :
9772 : /*
9773 : * If it's a named type and not RECORD, we may have to skip
9774 : * dropped columns and/or claim there are NULLs for added
9775 : * columns.
9776 : */
9777 186 : if (rowexpr->row_typeid != RECORDOID)
9778 : {
9779 48 : tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
9780 : Assert(list_length(rowexpr->args) <= tupdesc->natts);
9781 : }
9782 :
9783 : /*
9784 : * SQL99 allows "ROW" to be omitted when there is more than
9785 : * one column, but for simplicity we always print it.
9786 : */
9787 186 : appendStringInfoString(buf, "ROW(");
9788 186 : sep = "";
9789 186 : i = 0;
9790 534 : foreach(arg, rowexpr->args)
9791 : {
9792 348 : Node *e = (Node *) lfirst(arg);
9793 :
9794 348 : if (tupdesc == NULL ||
9795 90 : !TupleDescAttr(tupdesc, i)->attisdropped)
9796 : {
9797 348 : appendStringInfoString(buf, sep);
9798 : /* Whole-row Vars need special treatment here */
9799 348 : get_rule_expr_toplevel(e, context, true);
9800 348 : sep = ", ";
9801 : }
9802 348 : i++;
9803 : }
9804 186 : if (tupdesc != NULL)
9805 : {
9806 48 : while (i < tupdesc->natts)
9807 : {
9808 0 : if (!TupleDescAttr(tupdesc, i)->attisdropped)
9809 : {
9810 0 : appendStringInfoString(buf, sep);
9811 0 : appendStringInfoString(buf, "NULL");
9812 0 : sep = ", ";
9813 : }
9814 0 : i++;
9815 : }
9816 :
9817 48 : ReleaseTupleDesc(tupdesc);
9818 : }
9819 186 : appendStringInfoChar(buf, ')');
9820 186 : if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
9821 36 : appendStringInfo(buf, "::%s",
9822 : format_type_with_typemod(rowexpr->row_typeid, -1));
9823 : }
9824 186 : break;
9825 :
9826 66 : case T_RowCompareExpr:
9827 : {
9828 66 : RowCompareExpr *rcexpr = (RowCompareExpr *) node;
9829 :
9830 : /*
9831 : * SQL99 allows "ROW" to be omitted when there is more than
9832 : * one column, but for simplicity we always print it. Within
9833 : * a ROW expression, whole-row Vars need special treatment, so
9834 : * use get_rule_list_toplevel.
9835 : */
9836 66 : appendStringInfoString(buf, "(ROW(");
9837 66 : get_rule_list_toplevel(rcexpr->largs, context, true);
9838 :
9839 : /*
9840 : * We assume that the name of the first-column operator will
9841 : * do for all the rest too. This is definitely open to
9842 : * failure, eg if some but not all operators were renamed
9843 : * since the construct was parsed, but there seems no way to
9844 : * be perfect.
9845 : */
9846 66 : appendStringInfo(buf, ") %s ROW(",
9847 66 : generate_operator_name(linitial_oid(rcexpr->opnos),
9848 66 : exprType(linitial(rcexpr->largs)),
9849 66 : exprType(linitial(rcexpr->rargs))));
9850 66 : get_rule_list_toplevel(rcexpr->rargs, context, true);
9851 66 : appendStringInfoString(buf, "))");
9852 : }
9853 66 : break;
9854 :
9855 1002 : case T_CoalesceExpr:
9856 : {
9857 1002 : CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
9858 :
9859 1002 : appendStringInfoString(buf, "COALESCE(");
9860 1002 : get_rule_expr((Node *) coalesceexpr->args, context, true);
9861 1002 : appendStringInfoChar(buf, ')');
9862 : }
9863 1002 : break;
9864 :
9865 36 : case T_MinMaxExpr:
9866 : {
9867 36 : MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
9868 :
9869 36 : switch (minmaxexpr->op)
9870 : {
9871 6 : case IS_GREATEST:
9872 6 : appendStringInfoString(buf, "GREATEST(");
9873 6 : break;
9874 30 : case IS_LEAST:
9875 30 : appendStringInfoString(buf, "LEAST(");
9876 30 : break;
9877 : }
9878 36 : get_rule_expr((Node *) minmaxexpr->args, context, true);
9879 36 : appendStringInfoChar(buf, ')');
9880 : }
9881 36 : break;
9882 :
9883 694 : case T_SQLValueFunction:
9884 : {
9885 694 : SQLValueFunction *svf = (SQLValueFunction *) node;
9886 :
9887 : /*
9888 : * Note: this code knows that typmod for time, timestamp, and
9889 : * timestamptz just prints as integer.
9890 : */
9891 694 : switch (svf->op)
9892 : {
9893 104 : case SVFOP_CURRENT_DATE:
9894 104 : appendStringInfoString(buf, "CURRENT_DATE");
9895 104 : break;
9896 12 : case SVFOP_CURRENT_TIME:
9897 12 : appendStringInfoString(buf, "CURRENT_TIME");
9898 12 : break;
9899 12 : case SVFOP_CURRENT_TIME_N:
9900 12 : appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
9901 12 : break;
9902 12 : case SVFOP_CURRENT_TIMESTAMP:
9903 12 : appendStringInfoString(buf, "CURRENT_TIMESTAMP");
9904 12 : break;
9905 104 : case SVFOP_CURRENT_TIMESTAMP_N:
9906 104 : appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
9907 : svf->typmod);
9908 104 : break;
9909 12 : case SVFOP_LOCALTIME:
9910 12 : appendStringInfoString(buf, "LOCALTIME");
9911 12 : break;
9912 12 : case SVFOP_LOCALTIME_N:
9913 12 : appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
9914 12 : break;
9915 30 : case SVFOP_LOCALTIMESTAMP:
9916 30 : appendStringInfoString(buf, "LOCALTIMESTAMP");
9917 30 : break;
9918 18 : case SVFOP_LOCALTIMESTAMP_N:
9919 18 : appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
9920 : svf->typmod);
9921 18 : break;
9922 12 : case SVFOP_CURRENT_ROLE:
9923 12 : appendStringInfoString(buf, "CURRENT_ROLE");
9924 12 : break;
9925 296 : case SVFOP_CURRENT_USER:
9926 296 : appendStringInfoString(buf, "CURRENT_USER");
9927 296 : break;
9928 12 : case SVFOP_USER:
9929 12 : appendStringInfoString(buf, "USER");
9930 12 : break;
9931 34 : case SVFOP_SESSION_USER:
9932 34 : appendStringInfoString(buf, "SESSION_USER");
9933 34 : break;
9934 12 : case SVFOP_CURRENT_CATALOG:
9935 12 : appendStringInfoString(buf, "CURRENT_CATALOG");
9936 12 : break;
9937 12 : case SVFOP_CURRENT_SCHEMA:
9938 12 : appendStringInfoString(buf, "CURRENT_SCHEMA");
9939 12 : break;
9940 : }
9941 694 : }
9942 694 : break;
9943 :
9944 144 : case T_XmlExpr:
9945 : {
9946 144 : XmlExpr *xexpr = (XmlExpr *) node;
9947 144 : bool needcomma = false;
9948 : ListCell *arg;
9949 : ListCell *narg;
9950 : Const *con;
9951 :
9952 144 : switch (xexpr->op)
9953 : {
9954 16 : case IS_XMLCONCAT:
9955 16 : appendStringInfoString(buf, "XMLCONCAT(");
9956 16 : break;
9957 32 : case IS_XMLELEMENT:
9958 32 : appendStringInfoString(buf, "XMLELEMENT(");
9959 32 : break;
9960 16 : case IS_XMLFOREST:
9961 16 : appendStringInfoString(buf, "XMLFOREST(");
9962 16 : break;
9963 16 : case IS_XMLPARSE:
9964 16 : appendStringInfoString(buf, "XMLPARSE(");
9965 16 : break;
9966 16 : case IS_XMLPI:
9967 16 : appendStringInfoString(buf, "XMLPI(");
9968 16 : break;
9969 16 : case IS_XMLROOT:
9970 16 : appendStringInfoString(buf, "XMLROOT(");
9971 16 : break;
9972 32 : case IS_XMLSERIALIZE:
9973 32 : appendStringInfoString(buf, "XMLSERIALIZE(");
9974 32 : break;
9975 0 : case IS_DOCUMENT:
9976 0 : break;
9977 : }
9978 144 : if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
9979 : {
9980 48 : if (xexpr->xmloption == XMLOPTION_DOCUMENT)
9981 0 : appendStringInfoString(buf, "DOCUMENT ");
9982 : else
9983 48 : appendStringInfoString(buf, "CONTENT ");
9984 : }
9985 144 : if (xexpr->name)
9986 : {
9987 48 : appendStringInfo(buf, "NAME %s",
9988 48 : quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));
9989 48 : needcomma = true;
9990 : }
9991 144 : if (xexpr->named_args)
9992 : {
9993 32 : if (xexpr->op != IS_XMLFOREST)
9994 : {
9995 16 : if (needcomma)
9996 16 : appendStringInfoString(buf, ", ");
9997 16 : appendStringInfoString(buf, "XMLATTRIBUTES(");
9998 16 : needcomma = false;
9999 : }
10000 112 : forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
10001 : {
10002 80 : Node *e = (Node *) lfirst(arg);
10003 80 : char *argname = strVal(lfirst(narg));
10004 :
10005 80 : if (needcomma)
10006 48 : appendStringInfoString(buf, ", ");
10007 80 : get_rule_expr((Node *) e, context, true);
10008 80 : appendStringInfo(buf, " AS %s",
10009 80 : quote_identifier(map_xml_name_to_sql_identifier(argname)));
10010 80 : needcomma = true;
10011 : }
10012 32 : if (xexpr->op != IS_XMLFOREST)
10013 16 : appendStringInfoChar(buf, ')');
10014 : }
10015 144 : if (xexpr->args)
10016 : {
10017 128 : if (needcomma)
10018 48 : appendStringInfoString(buf, ", ");
10019 128 : switch (xexpr->op)
10020 : {
10021 96 : case IS_XMLCONCAT:
10022 : case IS_XMLELEMENT:
10023 : case IS_XMLFOREST:
10024 : case IS_XMLPI:
10025 : case IS_XMLSERIALIZE:
10026 : /* no extra decoration needed */
10027 96 : get_rule_expr((Node *) xexpr->args, context, true);
10028 96 : break;
10029 16 : case IS_XMLPARSE:
10030 : Assert(list_length(xexpr->args) == 2);
10031 :
10032 16 : get_rule_expr((Node *) linitial(xexpr->args),
10033 : context, true);
10034 :
10035 16 : con = lsecond_node(Const, xexpr->args);
10036 : Assert(!con->constisnull);
10037 16 : if (DatumGetBool(con->constvalue))
10038 0 : appendStringInfoString(buf,
10039 : " PRESERVE WHITESPACE");
10040 : else
10041 16 : appendStringInfoString(buf,
10042 : " STRIP WHITESPACE");
10043 16 : break;
10044 16 : case IS_XMLROOT:
10045 : Assert(list_length(xexpr->args) == 3);
10046 :
10047 16 : get_rule_expr((Node *) linitial(xexpr->args),
10048 : context, true);
10049 :
10050 16 : appendStringInfoString(buf, ", VERSION ");
10051 16 : con = (Const *) lsecond(xexpr->args);
10052 16 : if (IsA(con, Const) &&
10053 16 : con->constisnull)
10054 16 : appendStringInfoString(buf, "NO VALUE");
10055 : else
10056 0 : get_rule_expr((Node *) con, context, false);
10057 :
10058 16 : con = lthird_node(Const, xexpr->args);
10059 16 : if (con->constisnull)
10060 : /* suppress STANDALONE NO VALUE */ ;
10061 : else
10062 : {
10063 16 : switch (DatumGetInt32(con->constvalue))
10064 : {
10065 16 : case XML_STANDALONE_YES:
10066 16 : appendStringInfoString(buf,
10067 : ", STANDALONE YES");
10068 16 : break;
10069 0 : case XML_STANDALONE_NO:
10070 0 : appendStringInfoString(buf,
10071 : ", STANDALONE NO");
10072 0 : break;
10073 0 : case XML_STANDALONE_NO_VALUE:
10074 0 : appendStringInfoString(buf,
10075 : ", STANDALONE NO VALUE");
10076 0 : break;
10077 0 : default:
10078 0 : break;
10079 : }
10080 : }
10081 16 : break;
10082 0 : case IS_DOCUMENT:
10083 0 : get_rule_expr_paren((Node *) xexpr->args, context, false, node);
10084 0 : break;
10085 : }
10086 16 : }
10087 144 : if (xexpr->op == IS_XMLSERIALIZE)
10088 32 : appendStringInfo(buf, " AS %s",
10089 : format_type_with_typemod(xexpr->type,
10090 : xexpr->typmod));
10091 144 : if (xexpr->op == IS_DOCUMENT)
10092 0 : appendStringInfoString(buf, " IS DOCUMENT");
10093 : else
10094 144 : appendStringInfoChar(buf, ')');
10095 : }
10096 144 : break;
10097 :
10098 2192 : case T_NullTest:
10099 : {
10100 2192 : NullTest *ntest = (NullTest *) node;
10101 :
10102 2192 : if (!PRETTY_PAREN(context))
10103 2138 : appendStringInfoChar(buf, '(');
10104 2192 : get_rule_expr_paren((Node *) ntest->arg, context, true, node);
10105 :
10106 : /*
10107 : * For scalar inputs, we prefer to print as IS [NOT] NULL,
10108 : * which is shorter and traditional. If it's a rowtype input
10109 : * but we're applying a scalar test, must print IS [NOT]
10110 : * DISTINCT FROM NULL to be semantically correct.
10111 : */
10112 2192 : if (ntest->argisrow ||
10113 2162 : !type_is_rowtype(exprType((Node *) ntest->arg)))
10114 : {
10115 4348 : switch (ntest->nulltesttype)
10116 : {
10117 780 : case IS_NULL:
10118 780 : appendStringInfoString(buf, " IS NULL");
10119 780 : break;
10120 1394 : case IS_NOT_NULL:
10121 1394 : appendStringInfoString(buf, " IS NOT NULL");
10122 1394 : break;
10123 0 : default:
10124 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10125 : (int) ntest->nulltesttype);
10126 : }
10127 : }
10128 : else
10129 : {
10130 18 : switch (ntest->nulltesttype)
10131 : {
10132 6 : case IS_NULL:
10133 6 : appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
10134 6 : break;
10135 12 : case IS_NOT_NULL:
10136 12 : appendStringInfoString(buf, " IS DISTINCT FROM NULL");
10137 12 : break;
10138 0 : default:
10139 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10140 : (int) ntest->nulltesttype);
10141 : }
10142 : }
10143 2192 : if (!PRETTY_PAREN(context))
10144 2138 : appendStringInfoChar(buf, ')');
10145 : }
10146 2192 : break;
10147 :
10148 306 : case T_BooleanTest:
10149 : {
10150 306 : BooleanTest *btest = (BooleanTest *) node;
10151 :
10152 306 : if (!PRETTY_PAREN(context))
10153 306 : appendStringInfoChar(buf, '(');
10154 306 : get_rule_expr_paren((Node *) btest->arg, context, false, node);
10155 306 : switch (btest->booltesttype)
10156 : {
10157 36 : case IS_TRUE:
10158 36 : appendStringInfoString(buf, " IS TRUE");
10159 36 : break;
10160 138 : case IS_NOT_TRUE:
10161 138 : appendStringInfoString(buf, " IS NOT TRUE");
10162 138 : break;
10163 0 : case IS_FALSE:
10164 0 : appendStringInfoString(buf, " IS FALSE");
10165 0 : break;
10166 54 : case IS_NOT_FALSE:
10167 54 : appendStringInfoString(buf, " IS NOT FALSE");
10168 54 : break;
10169 24 : case IS_UNKNOWN:
10170 24 : appendStringInfoString(buf, " IS UNKNOWN");
10171 24 : break;
10172 54 : case IS_NOT_UNKNOWN:
10173 54 : appendStringInfoString(buf, " IS NOT UNKNOWN");
10174 54 : break;
10175 0 : default:
10176 0 : elog(ERROR, "unrecognized booltesttype: %d",
10177 : (int) btest->booltesttype);
10178 : }
10179 306 : if (!PRETTY_PAREN(context))
10180 306 : appendStringInfoChar(buf, ')');
10181 : }
10182 306 : break;
10183 :
10184 32 : case T_CoerceToDomain:
10185 : {
10186 32 : CoerceToDomain *ctest = (CoerceToDomain *) node;
10187 32 : Node *arg = (Node *) ctest->arg;
10188 :
10189 32 : if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
10190 22 : !showimplicit)
10191 : {
10192 : /* don't show the implicit cast */
10193 22 : get_rule_expr(arg, context, false);
10194 : }
10195 : else
10196 : {
10197 10 : get_coercion_expr(arg, context,
10198 : ctest->resulttype,
10199 : ctest->resulttypmod,
10200 : node);
10201 : }
10202 : }
10203 32 : break;
10204 :
10205 366 : case T_CoerceToDomainValue:
10206 366 : appendStringInfoString(buf, "VALUE");
10207 366 : break;
10208 :
10209 76 : case T_SetToDefault:
10210 76 : appendStringInfoString(buf, "DEFAULT");
10211 76 : break;
10212 :
10213 24 : case T_CurrentOfExpr:
10214 : {
10215 24 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
10216 :
10217 24 : if (cexpr->cursor_name)
10218 24 : appendStringInfo(buf, "CURRENT OF %s",
10219 24 : quote_identifier(cexpr->cursor_name));
10220 : else
10221 0 : appendStringInfo(buf, "CURRENT OF $%d",
10222 : cexpr->cursor_param);
10223 : }
10224 24 : break;
10225 :
10226 0 : case T_NextValueExpr:
10227 : {
10228 0 : NextValueExpr *nvexpr = (NextValueExpr *) node;
10229 :
10230 : /*
10231 : * This isn't exactly nextval(), but that seems close enough
10232 : * for EXPLAIN's purposes.
10233 : */
10234 0 : appendStringInfoString(buf, "nextval(");
10235 0 : simple_quote_literal(buf,
10236 0 : generate_relation_name(nvexpr->seqid,
10237 : NIL));
10238 0 : appendStringInfoChar(buf, ')');
10239 : }
10240 0 : break;
10241 :
10242 24 : case T_InferenceElem:
10243 : {
10244 24 : InferenceElem *iexpr = (InferenceElem *) node;
10245 : bool save_varprefix;
10246 : bool need_parens;
10247 :
10248 : /*
10249 : * InferenceElem can only refer to target relation, so a
10250 : * prefix is not useful, and indeed would cause parse errors.
10251 : */
10252 24 : save_varprefix = context->varprefix;
10253 24 : context->varprefix = false;
10254 :
10255 : /*
10256 : * Parenthesize the element unless it's a simple Var or a bare
10257 : * function call. Follows pg_get_indexdef_worker().
10258 : */
10259 24 : need_parens = !IsA(iexpr->expr, Var);
10260 24 : if (IsA(iexpr->expr, FuncExpr) &&
10261 0 : ((FuncExpr *) iexpr->expr)->funcformat ==
10262 : COERCE_EXPLICIT_CALL)
10263 0 : need_parens = false;
10264 :
10265 24 : if (need_parens)
10266 0 : appendStringInfoChar(buf, '(');
10267 24 : get_rule_expr((Node *) iexpr->expr,
10268 : context, false);
10269 24 : if (need_parens)
10270 0 : appendStringInfoChar(buf, ')');
10271 :
10272 24 : context->varprefix = save_varprefix;
10273 :
10274 24 : if (iexpr->infercollid)
10275 12 : appendStringInfo(buf, " COLLATE %s",
10276 : generate_collation_name(iexpr->infercollid));
10277 :
10278 : /* Add the operator class name, if not default */
10279 24 : if (iexpr->inferopclass)
10280 : {
10281 12 : Oid inferopclass = iexpr->inferopclass;
10282 12 : Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
10283 :
10284 12 : get_opclass_name(inferopclass, inferopcinputtype, buf);
10285 : }
10286 : }
10287 24 : break;
10288 :
10289 3754 : case T_PartitionBoundSpec:
10290 : {
10291 3754 : PartitionBoundSpec *spec = (PartitionBoundSpec *) node;
10292 : ListCell *cell;
10293 : char *sep;
10294 :
10295 3754 : if (spec->is_default)
10296 : {
10297 146 : appendStringInfoString(buf, "DEFAULT");
10298 146 : break;
10299 : }
10300 :
10301 3608 : switch (spec->strategy)
10302 : {
10303 182 : case PARTITION_STRATEGY_HASH:
10304 : Assert(spec->modulus > 0 && spec->remainder >= 0);
10305 : Assert(spec->modulus > spec->remainder);
10306 :
10307 182 : appendStringInfoString(buf, "FOR VALUES");
10308 182 : appendStringInfo(buf, " WITH (modulus %d, remainder %d)",
10309 : spec->modulus, spec->remainder);
10310 182 : break;
10311 :
10312 1296 : case PARTITION_STRATEGY_LIST:
10313 : Assert(spec->listdatums != NIL);
10314 :
10315 1296 : appendStringInfoString(buf, "FOR VALUES IN (");
10316 1296 : sep = "";
10317 3456 : foreach(cell, spec->listdatums)
10318 : {
10319 2160 : Const *val = lfirst_node(Const, cell);
10320 :
10321 2160 : appendStringInfoString(buf, sep);
10322 2160 : get_const_expr(val, context, -1);
10323 2160 : sep = ", ";
10324 : }
10325 :
10326 1296 : appendStringInfoChar(buf, ')');
10327 1296 : break;
10328 :
10329 2130 : case PARTITION_STRATEGY_RANGE:
10330 : Assert(spec->lowerdatums != NIL &&
10331 : spec->upperdatums != NIL &&
10332 : list_length(spec->lowerdatums) ==
10333 : list_length(spec->upperdatums));
10334 :
10335 2130 : appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
10336 : get_range_partbound_string(spec->lowerdatums),
10337 : get_range_partbound_string(spec->upperdatums));
10338 2130 : break;
10339 :
10340 0 : default:
10341 0 : elog(ERROR, "unrecognized partition strategy: %d",
10342 : (int) spec->strategy);
10343 : break;
10344 : }
10345 : }
10346 3608 : break;
10347 :
10348 150 : case T_JsonValueExpr:
10349 : {
10350 150 : JsonValueExpr *jve = (JsonValueExpr *) node;
10351 :
10352 150 : get_rule_expr((Node *) jve->raw_expr, context, false);
10353 150 : get_json_format(jve->format, context->buf);
10354 : }
10355 150 : break;
10356 :
10357 174 : case T_JsonConstructorExpr:
10358 174 : get_json_constructor((JsonConstructorExpr *) node, context, false);
10359 174 : break;
10360 :
10361 60 : case T_JsonIsPredicate:
10362 : {
10363 60 : JsonIsPredicate *pred = (JsonIsPredicate *) node;
10364 :
10365 60 : if (!PRETTY_PAREN(context))
10366 30 : appendStringInfoChar(context->buf, '(');
10367 :
10368 60 : get_rule_expr_paren(pred->expr, context, true, node);
10369 :
10370 60 : appendStringInfoString(context->buf, " IS JSON");
10371 :
10372 : /* TODO: handle FORMAT clause */
10373 :
10374 60 : switch (pred->item_type)
10375 : {
10376 12 : case JS_TYPE_SCALAR:
10377 12 : appendStringInfoString(context->buf, " SCALAR");
10378 12 : break;
10379 12 : case JS_TYPE_ARRAY:
10380 12 : appendStringInfoString(context->buf, " ARRAY");
10381 12 : break;
10382 12 : case JS_TYPE_OBJECT:
10383 12 : appendStringInfoString(context->buf, " OBJECT");
10384 12 : break;
10385 24 : default:
10386 24 : break;
10387 : }
10388 :
10389 60 : if (pred->unique_keys)
10390 12 : appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
10391 :
10392 60 : if (!PRETTY_PAREN(context))
10393 30 : appendStringInfoChar(context->buf, ')');
10394 : }
10395 60 : break;
10396 :
10397 60 : case T_JsonExpr:
10398 : {
10399 60 : JsonExpr *jexpr = (JsonExpr *) node;
10400 :
10401 60 : switch (jexpr->op)
10402 : {
10403 12 : case JSON_EXISTS_OP:
10404 12 : appendStringInfoString(buf, "JSON_EXISTS(");
10405 12 : break;
10406 36 : case JSON_QUERY_OP:
10407 36 : appendStringInfoString(buf, "JSON_QUERY(");
10408 36 : break;
10409 12 : case JSON_VALUE_OP:
10410 12 : appendStringInfoString(buf, "JSON_VALUE(");
10411 12 : break;
10412 0 : default:
10413 0 : elog(ERROR, "unrecognized JsonExpr op: %d",
10414 : (int) jexpr->op);
10415 : }
10416 :
10417 60 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
10418 :
10419 60 : appendStringInfoString(buf, ", ");
10420 :
10421 60 : get_json_path_spec(jexpr->path_spec, context, showimplicit);
10422 :
10423 60 : if (jexpr->passing_values)
10424 : {
10425 : ListCell *lc1,
10426 : *lc2;
10427 12 : bool needcomma = false;
10428 :
10429 12 : appendStringInfoString(buf, " PASSING ");
10430 :
10431 48 : forboth(lc1, jexpr->passing_names,
10432 : lc2, jexpr->passing_values)
10433 : {
10434 36 : if (needcomma)
10435 24 : appendStringInfoString(buf, ", ");
10436 36 : needcomma = true;
10437 :
10438 36 : get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
10439 36 : appendStringInfo(buf, " AS %s",
10440 36 : ((String *) lfirst_node(String, lc1))->sval);
10441 : }
10442 : }
10443 :
10444 60 : if (jexpr->op != JSON_EXISTS_OP ||
10445 12 : jexpr->returning->typid != BOOLOID)
10446 48 : get_json_returning(jexpr->returning, context->buf,
10447 48 : jexpr->op == JSON_QUERY_OP);
10448 :
10449 60 : get_json_expr_options(jexpr, context,
10450 60 : jexpr->op != JSON_EXISTS_OP ?
10451 : JSON_BEHAVIOR_NULL :
10452 : JSON_BEHAVIOR_FALSE);
10453 :
10454 60 : appendStringInfoChar(buf, ')');
10455 : }
10456 60 : break;
10457 :
10458 2144 : case T_List:
10459 : {
10460 : char *sep;
10461 : ListCell *l;
10462 :
10463 2144 : sep = "";
10464 6106 : foreach(l, (List *) node)
10465 : {
10466 3962 : appendStringInfoString(buf, sep);
10467 3962 : get_rule_expr((Node *) lfirst(l), context, showimplicit);
10468 3962 : sep = ", ";
10469 : }
10470 : }
10471 2144 : break;
10472 :
10473 72 : case T_TableFunc:
10474 72 : get_tablefunc((TableFunc *) node, context, showimplicit);
10475 72 : break;
10476 :
10477 0 : default:
10478 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
10479 : break;
10480 : }
10481 : }
10482 :
10483 : /*
10484 : * get_rule_expr_toplevel - Parse back a toplevel expression
10485 : *
10486 : * Same as get_rule_expr(), except that if the expr is just a Var, we pass
10487 : * istoplevel = true not false to get_variable(). This causes whole-row Vars
10488 : * to get printed with decoration that will prevent expansion of "*".
10489 : * We need to use this in contexts such as ROW() and VALUES(), where the
10490 : * parser would expand "foo.*" appearing at top level. (In principle we'd
10491 : * use this in get_target_list() too, but that has additional worries about
10492 : * whether to print AS, so it needs to invoke get_variable() directly anyway.)
10493 : */
10494 : static void
10495 2822 : get_rule_expr_toplevel(Node *node, deparse_context *context,
10496 : bool showimplicit)
10497 : {
10498 2822 : if (node && IsA(node, Var))
10499 1088 : (void) get_variable((Var *) node, 0, true, context);
10500 : else
10501 1734 : get_rule_expr(node, context, showimplicit);
10502 2822 : }
10503 :
10504 : /*
10505 : * get_rule_list_toplevel - Parse back a list of toplevel expressions
10506 : *
10507 : * Apply get_rule_expr_toplevel() to each element of a List.
10508 : *
10509 : * This adds commas between the expressions, but caller is responsible
10510 : * for printing surrounding decoration.
10511 : */
10512 : static void
10513 408 : get_rule_list_toplevel(List *lst, deparse_context *context,
10514 : bool showimplicit)
10515 : {
10516 : const char *sep;
10517 : ListCell *lc;
10518 :
10519 408 : sep = "";
10520 1430 : foreach(lc, lst)
10521 : {
10522 1022 : Node *e = (Node *) lfirst(lc);
10523 :
10524 1022 : appendStringInfoString(context->buf, sep);
10525 1022 : get_rule_expr_toplevel(e, context, showimplicit);
10526 1022 : sep = ", ";
10527 : }
10528 408 : }
10529 :
10530 : /*
10531 : * get_rule_expr_funccall - Parse back a function-call expression
10532 : *
10533 : * Same as get_rule_expr(), except that we guarantee that the output will
10534 : * look like a function call, or like one of the things the grammar treats as
10535 : * equivalent to a function call (see the func_expr_windowless production).
10536 : * This is needed in places where the grammar uses func_expr_windowless and
10537 : * you can't substitute a parenthesized a_expr. If what we have isn't going
10538 : * to look like a function call, wrap it in a dummy CAST() expression, which
10539 : * will satisfy the grammar --- and, indeed, is likely what the user wrote to
10540 : * produce such a thing.
10541 : */
10542 : static void
10543 804 : get_rule_expr_funccall(Node *node, deparse_context *context,
10544 : bool showimplicit)
10545 : {
10546 804 : if (looks_like_function(node))
10547 792 : get_rule_expr(node, context, showimplicit);
10548 : else
10549 : {
10550 12 : StringInfo buf = context->buf;
10551 :
10552 12 : appendStringInfoString(buf, "CAST(");
10553 : /* no point in showing any top-level implicit cast */
10554 12 : get_rule_expr(node, context, false);
10555 12 : appendStringInfo(buf, " AS %s)",
10556 : format_type_with_typemod(exprType(node),
10557 : exprTypmod(node)));
10558 : }
10559 804 : }
10560 :
10561 : /*
10562 : * Helper function to identify node types that satisfy func_expr_windowless.
10563 : * If in doubt, "false" is always a safe answer.
10564 : */
10565 : static bool
10566 1874 : looks_like_function(Node *node)
10567 : {
10568 1874 : if (node == NULL)
10569 0 : return false; /* probably shouldn't happen */
10570 1874 : switch (nodeTag(node))
10571 : {
10572 832 : case T_FuncExpr:
10573 : /* OK, unless it's going to deparse as a cast */
10574 850 : return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
10575 18 : ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
10576 108 : case T_NullIfExpr:
10577 : case T_CoalesceExpr:
10578 : case T_MinMaxExpr:
10579 : case T_SQLValueFunction:
10580 : case T_XmlExpr:
10581 : case T_JsonExpr:
10582 : /* these are all accepted by func_expr_common_subexpr */
10583 108 : return true;
10584 934 : default:
10585 934 : break;
10586 : }
10587 934 : return false;
10588 : }
10589 :
10590 :
10591 : /*
10592 : * get_oper_expr - Parse back an OpExpr node
10593 : */
10594 : static void
10595 55346 : get_oper_expr(OpExpr *expr, deparse_context *context)
10596 : {
10597 55346 : StringInfo buf = context->buf;
10598 55346 : Oid opno = expr->opno;
10599 55346 : List *args = expr->args;
10600 :
10601 55346 : if (!PRETTY_PAREN(context))
10602 53370 : appendStringInfoChar(buf, '(');
10603 55346 : if (list_length(args) == 2)
10604 : {
10605 : /* binary operator */
10606 55316 : Node *arg1 = (Node *) linitial(args);
10607 55316 : Node *arg2 = (Node *) lsecond(args);
10608 :
10609 55316 : get_rule_expr_paren(arg1, context, true, (Node *) expr);
10610 55316 : appendStringInfo(buf, " %s ",
10611 : generate_operator_name(opno,
10612 : exprType(arg1),
10613 : exprType(arg2)));
10614 55316 : get_rule_expr_paren(arg2, context, true, (Node *) expr);
10615 : }
10616 : else
10617 : {
10618 : /* prefix operator */
10619 30 : Node *arg = (Node *) linitial(args);
10620 :
10621 30 : appendStringInfo(buf, "%s ",
10622 : generate_operator_name(opno,
10623 : InvalidOid,
10624 : exprType(arg)));
10625 30 : get_rule_expr_paren(arg, context, true, (Node *) expr);
10626 : }
10627 55346 : if (!PRETTY_PAREN(context))
10628 53370 : appendStringInfoChar(buf, ')');
10629 55346 : }
10630 :
10631 : /*
10632 : * get_func_expr - Parse back a FuncExpr node
10633 : */
10634 : static void
10635 11528 : get_func_expr(FuncExpr *expr, deparse_context *context,
10636 : bool showimplicit)
10637 : {
10638 11528 : StringInfo buf = context->buf;
10639 11528 : Oid funcoid = expr->funcid;
10640 : Oid argtypes[FUNC_MAX_ARGS];
10641 : int nargs;
10642 : List *argnames;
10643 : bool use_variadic;
10644 : ListCell *l;
10645 :
10646 : /*
10647 : * If the function call came from an implicit coercion, then just show the
10648 : * first argument --- unless caller wants to see implicit coercions.
10649 : */
10650 11528 : if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
10651 : {
10652 1168 : get_rule_expr_paren((Node *) linitial(expr->args), context,
10653 : false, (Node *) expr);
10654 2866 : return;
10655 : }
10656 :
10657 : /*
10658 : * If the function call came from a cast, then show the first argument
10659 : * plus an explicit cast operation.
10660 : */
10661 10360 : if (expr->funcformat == COERCE_EXPLICIT_CAST ||
10662 9770 : expr->funcformat == COERCE_IMPLICIT_CAST)
10663 : {
10664 1536 : Node *arg = linitial(expr->args);
10665 1536 : Oid rettype = expr->funcresulttype;
10666 : int32 coercedTypmod;
10667 :
10668 : /* Get the typmod if this is a length-coercion function */
10669 1536 : (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
10670 :
10671 1536 : get_coercion_expr(arg, context,
10672 : rettype, coercedTypmod,
10673 : (Node *) expr);
10674 :
10675 1536 : return;
10676 : }
10677 :
10678 : /*
10679 : * If the function was called using one of the SQL spec's random special
10680 : * syntaxes, try to reproduce that. If we don't recognize the function,
10681 : * fall through.
10682 : */
10683 8824 : if (expr->funcformat == COERCE_SQL_SYNTAX)
10684 : {
10685 168 : if (get_func_sql_syntax(expr, context))
10686 162 : return;
10687 : }
10688 :
10689 : /*
10690 : * Normal function: display as proname(args). First we need to extract
10691 : * the argument datatypes.
10692 : */
10693 8662 : if (list_length(expr->args) > FUNC_MAX_ARGS)
10694 0 : ereport(ERROR,
10695 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
10696 : errmsg("too many arguments")));
10697 8662 : nargs = 0;
10698 8662 : argnames = NIL;
10699 18060 : foreach(l, expr->args)
10700 : {
10701 9398 : Node *arg = (Node *) lfirst(l);
10702 :
10703 9398 : if (IsA(arg, NamedArgExpr))
10704 18 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
10705 9398 : argtypes[nargs] = exprType(arg);
10706 9398 : nargs++;
10707 : }
10708 :
10709 8662 : appendStringInfo(buf, "%s(",
10710 : generate_function_name(funcoid, nargs,
10711 : argnames, argtypes,
10712 8662 : expr->funcvariadic,
10713 : &use_variadic,
10714 8662 : context->inGroupBy));
10715 8662 : nargs = 0;
10716 18060 : foreach(l, expr->args)
10717 : {
10718 9398 : if (nargs++ > 0)
10719 1722 : appendStringInfoString(buf, ", ");
10720 9398 : if (use_variadic && lnext(expr->args, l) == NULL)
10721 12 : appendStringInfoString(buf, "VARIADIC ");
10722 9398 : get_rule_expr((Node *) lfirst(l), context, true);
10723 : }
10724 8662 : appendStringInfoChar(buf, ')');
10725 : }
10726 :
10727 : /*
10728 : * get_agg_expr - Parse back an Aggref node
10729 : */
10730 : static void
10731 1892 : get_agg_expr(Aggref *aggref, deparse_context *context,
10732 : Aggref *original_aggref)
10733 : {
10734 1892 : get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
10735 : false);
10736 1892 : }
10737 :
10738 : /*
10739 : * get_agg_expr_helper - subroutine for get_agg_expr and
10740 : * get_json_agg_constructor
10741 : */
10742 : static void
10743 1946 : get_agg_expr_helper(Aggref *aggref, deparse_context *context,
10744 : Aggref *original_aggref, const char *funcname,
10745 : const char *options, bool is_json_objectagg)
10746 : {
10747 1946 : StringInfo buf = context->buf;
10748 : Oid argtypes[FUNC_MAX_ARGS];
10749 : int nargs;
10750 1946 : bool use_variadic = false;
10751 :
10752 : /*
10753 : * For a combining aggregate, we look up and deparse the corresponding
10754 : * partial aggregate instead. This is necessary because our input
10755 : * argument list has been replaced; the new argument list always has just
10756 : * one element, which will point to a partial Aggref that supplies us with
10757 : * transition states to combine.
10758 : */
10759 1946 : if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
10760 : {
10761 : TargetEntry *tle;
10762 :
10763 : Assert(list_length(aggref->args) == 1);
10764 260 : tle = linitial_node(TargetEntry, aggref->args);
10765 260 : resolve_special_varno((Node *) tle->expr, context,
10766 : get_agg_combine_expr, original_aggref);
10767 260 : return;
10768 : }
10769 :
10770 : /*
10771 : * Mark as PARTIAL, if appropriate. We look to the original aggref so as
10772 : * to avoid printing this when recursing from the code just above.
10773 : */
10774 1686 : if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))
10775 60 : appendStringInfoString(buf, "PARTIAL ");
10776 :
10777 : /* Extract the argument types as seen by the parser */
10778 1686 : nargs = get_aggregate_argtypes(aggref, argtypes);
10779 :
10780 1686 : if (!funcname)
10781 1632 : funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
10782 1632 : argtypes, aggref->aggvariadic,
10783 : &use_variadic,
10784 1632 : context->inGroupBy);
10785 :
10786 : /* Print the aggregate name, schema-qualified if needed */
10787 1686 : appendStringInfo(buf, "%s(%s", funcname,
10788 1686 : (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
10789 :
10790 1686 : if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
10791 : {
10792 : /*
10793 : * Ordered-set aggregates do not use "*" syntax. Also, we needn't
10794 : * worry about inserting VARIADIC. So we can just dump the direct
10795 : * args as-is.
10796 : */
10797 : Assert(!aggref->aggvariadic);
10798 28 : get_rule_expr((Node *) aggref->aggdirectargs, context, true);
10799 : Assert(aggref->aggorder != NIL);
10800 28 : appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
10801 28 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
10802 : }
10803 : else
10804 : {
10805 : /* aggstar can be set only in zero-argument aggregates */
10806 1658 : if (aggref->aggstar)
10807 238 : appendStringInfoChar(buf, '*');
10808 : else
10809 : {
10810 : ListCell *l;
10811 : int i;
10812 :
10813 1420 : i = 0;
10814 3028 : foreach(l, aggref->args)
10815 : {
10816 1608 : TargetEntry *tle = (TargetEntry *) lfirst(l);
10817 1608 : Node *arg = (Node *) tle->expr;
10818 :
10819 : Assert(!IsA(arg, NamedArgExpr));
10820 1608 : if (tle->resjunk)
10821 50 : continue;
10822 1558 : if (i++ > 0)
10823 : {
10824 138 : if (is_json_objectagg)
10825 : {
10826 : /*
10827 : * the ABSENT ON NULL and WITH UNIQUE args are printed
10828 : * separately, so ignore them here
10829 : */
10830 30 : if (i > 2)
10831 0 : break;
10832 :
10833 30 : appendStringInfoString(buf, " : ");
10834 : }
10835 : else
10836 108 : appendStringInfoString(buf, ", ");
10837 : }
10838 1558 : if (use_variadic && i == nargs)
10839 8 : appendStringInfoString(buf, "VARIADIC ");
10840 1558 : get_rule_expr(arg, context, true);
10841 : }
10842 : }
10843 :
10844 1658 : if (aggref->aggorder != NIL)
10845 : {
10846 82 : appendStringInfoString(buf, " ORDER BY ");
10847 82 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
10848 : }
10849 : }
10850 :
10851 1686 : if (options)
10852 54 : appendStringInfoString(buf, options);
10853 :
10854 1686 : if (aggref->aggfilter != NULL)
10855 : {
10856 46 : appendStringInfoString(buf, ") FILTER (WHERE ");
10857 46 : get_rule_expr((Node *) aggref->aggfilter, context, false);
10858 : }
10859 :
10860 1686 : appendStringInfoChar(buf, ')');
10861 : }
10862 :
10863 : /*
10864 : * This is a helper function for get_agg_expr(). It's used when we deparse
10865 : * a combining Aggref; resolve_special_varno locates the corresponding partial
10866 : * Aggref and then calls this.
10867 : */
10868 : static void
10869 260 : get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
10870 : {
10871 : Aggref *aggref;
10872 260 : Aggref *original_aggref = callback_arg;
10873 :
10874 260 : if (!IsA(node, Aggref))
10875 0 : elog(ERROR, "combining Aggref does not point to an Aggref");
10876 :
10877 260 : aggref = (Aggref *) node;
10878 260 : get_agg_expr(aggref, context, original_aggref);
10879 260 : }
10880 :
10881 : /*
10882 : * get_windowfunc_expr - Parse back a WindowFunc node
10883 : */
10884 : static void
10885 240 : get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
10886 : {
10887 240 : get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
10888 240 : }
10889 :
10890 :
10891 : /*
10892 : * get_windowfunc_expr_helper - subroutine for get_windowfunc_expr and
10893 : * get_json_agg_constructor
10894 : */
10895 : static void
10896 252 : get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
10897 : const char *funcname, const char *options,
10898 : bool is_json_objectagg)
10899 : {
10900 252 : StringInfo buf = context->buf;
10901 : Oid argtypes[FUNC_MAX_ARGS];
10902 : int nargs;
10903 : List *argnames;
10904 : ListCell *l;
10905 :
10906 252 : if (list_length(wfunc->args) > FUNC_MAX_ARGS)
10907 0 : ereport(ERROR,
10908 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
10909 : errmsg("too many arguments")));
10910 252 : nargs = 0;
10911 252 : argnames = NIL;
10912 402 : foreach(l, wfunc->args)
10913 : {
10914 150 : Node *arg = (Node *) lfirst(l);
10915 :
10916 150 : if (IsA(arg, NamedArgExpr))
10917 0 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
10918 150 : argtypes[nargs] = exprType(arg);
10919 150 : nargs++;
10920 : }
10921 :
10922 252 : if (!funcname)
10923 240 : funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
10924 : argtypes, false, NULL,
10925 240 : context->inGroupBy);
10926 :
10927 252 : appendStringInfo(buf, "%s(", funcname);
10928 :
10929 : /* winstar can be set only in zero-argument aggregates */
10930 252 : if (wfunc->winstar)
10931 24 : appendStringInfoChar(buf, '*');
10932 : else
10933 : {
10934 228 : if (is_json_objectagg)
10935 : {
10936 6 : get_rule_expr((Node *) linitial(wfunc->args), context, false);
10937 6 : appendStringInfoString(buf, " : ");
10938 6 : get_rule_expr((Node *) lsecond(wfunc->args), context, false);
10939 : }
10940 : else
10941 222 : get_rule_expr((Node *) wfunc->args, context, true);
10942 : }
10943 :
10944 252 : if (options)
10945 12 : appendStringInfoString(buf, options);
10946 :
10947 252 : if (wfunc->aggfilter != NULL)
10948 : {
10949 0 : appendStringInfoString(buf, ") FILTER (WHERE ");
10950 0 : get_rule_expr((Node *) wfunc->aggfilter, context, false);
10951 : }
10952 :
10953 252 : appendStringInfoString(buf, ") OVER ");
10954 :
10955 252 : foreach(l, context->windowClause)
10956 : {
10957 42 : WindowClause *wc = (WindowClause *) lfirst(l);
10958 :
10959 42 : if (wc->winref == wfunc->winref)
10960 : {
10961 42 : if (wc->name)
10962 0 : appendStringInfoString(buf, quote_identifier(wc->name));
10963 : else
10964 42 : get_rule_windowspec(wc, context->targetList, context);
10965 42 : break;
10966 : }
10967 : }
10968 252 : if (l == NULL)
10969 : {
10970 210 : if (context->windowClause)
10971 0 : elog(ERROR, "could not find window clause for winref %u",
10972 : wfunc->winref);
10973 :
10974 : /*
10975 : * In EXPLAIN, we don't have window context information available, so
10976 : * we have to settle for this:
10977 : */
10978 210 : appendStringInfoString(buf, "(?)");
10979 : }
10980 252 : }
10981 :
10982 : /*
10983 : * get_func_sql_syntax - Parse back a SQL-syntax function call
10984 : *
10985 : * Returns true if we successfully deparsed, false if we did not
10986 : * recognize the function.
10987 : */
10988 : static bool
10989 168 : get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
10990 : {
10991 168 : StringInfo buf = context->buf;
10992 168 : Oid funcoid = expr->funcid;
10993 :
10994 168 : switch (funcoid)
10995 : {
10996 24 : case F_TIMEZONE_INTERVAL_TIMESTAMP:
10997 : case F_TIMEZONE_INTERVAL_TIMESTAMPTZ:
10998 : case F_TIMEZONE_INTERVAL_TIMETZ:
10999 : case F_TIMEZONE_TEXT_TIMESTAMP:
11000 : case F_TIMEZONE_TEXT_TIMESTAMPTZ:
11001 : case F_TIMEZONE_TEXT_TIMETZ:
11002 : /* AT TIME ZONE ... note reversed argument order */
11003 24 : appendStringInfoChar(buf, '(');
11004 24 : get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
11005 : (Node *) expr);
11006 24 : appendStringInfoString(buf, " AT TIME ZONE ");
11007 24 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11008 : (Node *) expr);
11009 24 : appendStringInfoChar(buf, ')');
11010 24 : return true;
11011 :
11012 18 : case F_TIMEZONE_TIMESTAMP:
11013 : case F_TIMEZONE_TIMESTAMPTZ:
11014 : case F_TIMEZONE_TIMETZ:
11015 : /* AT LOCAL */
11016 18 : appendStringInfoChar(buf, '(');
11017 18 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11018 : (Node *) expr);
11019 18 : appendStringInfoString(buf, " AT LOCAL)");
11020 18 : return true;
11021 :
11022 6 : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL:
11023 : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ:
11024 : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL:
11025 : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ:
11026 : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_INTERVAL:
11027 : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_TIMESTAMP:
11028 : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_INTERVAL:
11029 : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_TIMESTAMP:
11030 : case F_OVERLAPS_TIMETZ_TIMETZ_TIMETZ_TIMETZ:
11031 : case F_OVERLAPS_TIME_INTERVAL_TIME_INTERVAL:
11032 : case F_OVERLAPS_TIME_INTERVAL_TIME_TIME:
11033 : case F_OVERLAPS_TIME_TIME_TIME_INTERVAL:
11034 : case F_OVERLAPS_TIME_TIME_TIME_TIME:
11035 : /* (x1, x2) OVERLAPS (y1, y2) */
11036 6 : appendStringInfoString(buf, "((");
11037 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11038 6 : appendStringInfoString(buf, ", ");
11039 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11040 6 : appendStringInfoString(buf, ") OVERLAPS (");
11041 6 : get_rule_expr((Node *) lthird(expr->args), context, false);
11042 6 : appendStringInfoString(buf, ", ");
11043 6 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11044 6 : appendStringInfoString(buf, "))");
11045 6 : return true;
11046 :
11047 6 : case F_EXTRACT_TEXT_DATE:
11048 : case F_EXTRACT_TEXT_TIME:
11049 : case F_EXTRACT_TEXT_TIMETZ:
11050 : case F_EXTRACT_TEXT_TIMESTAMP:
11051 : case F_EXTRACT_TEXT_TIMESTAMPTZ:
11052 : case F_EXTRACT_TEXT_INTERVAL:
11053 : /* EXTRACT (x FROM y) */
11054 6 : appendStringInfoString(buf, "EXTRACT(");
11055 : {
11056 6 : Const *con = (Const *) linitial(expr->args);
11057 :
11058 : Assert(IsA(con, Const) &&
11059 : con->consttype == TEXTOID &&
11060 : !con->constisnull);
11061 6 : appendStringInfoString(buf, TextDatumGetCString(con->constvalue));
11062 : }
11063 6 : appendStringInfoString(buf, " FROM ");
11064 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11065 6 : appendStringInfoChar(buf, ')');
11066 6 : return true;
11067 :
11068 12 : case F_IS_NORMALIZED:
11069 : /* IS xxx NORMALIZED */
11070 12 : appendStringInfoChar(buf, '(');
11071 12 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11072 : (Node *) expr);
11073 12 : appendStringInfoString(buf, " IS");
11074 12 : if (list_length(expr->args) == 2)
11075 : {
11076 6 : Const *con = (Const *) lsecond(expr->args);
11077 :
11078 : Assert(IsA(con, Const) &&
11079 : con->consttype == TEXTOID &&
11080 : !con->constisnull);
11081 6 : appendStringInfo(buf, " %s",
11082 6 : TextDatumGetCString(con->constvalue));
11083 : }
11084 12 : appendStringInfoString(buf, " NORMALIZED)");
11085 12 : return true;
11086 :
11087 6 : case F_PG_COLLATION_FOR:
11088 : /* COLLATION FOR */
11089 6 : appendStringInfoString(buf, "COLLATION FOR (");
11090 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11091 6 : appendStringInfoChar(buf, ')');
11092 6 : return true;
11093 :
11094 12 : case F_NORMALIZE:
11095 : /* NORMALIZE() */
11096 12 : appendStringInfoString(buf, "NORMALIZE(");
11097 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11098 12 : if (list_length(expr->args) == 2)
11099 : {
11100 6 : Const *con = (Const *) lsecond(expr->args);
11101 :
11102 : Assert(IsA(con, Const) &&
11103 : con->consttype == TEXTOID &&
11104 : !con->constisnull);
11105 6 : appendStringInfo(buf, ", %s",
11106 6 : TextDatumGetCString(con->constvalue));
11107 : }
11108 12 : appendStringInfoChar(buf, ')');
11109 12 : return true;
11110 :
11111 12 : case F_OVERLAY_BIT_BIT_INT4:
11112 : case F_OVERLAY_BIT_BIT_INT4_INT4:
11113 : case F_OVERLAY_BYTEA_BYTEA_INT4:
11114 : case F_OVERLAY_BYTEA_BYTEA_INT4_INT4:
11115 : case F_OVERLAY_TEXT_TEXT_INT4:
11116 : case F_OVERLAY_TEXT_TEXT_INT4_INT4:
11117 : /* OVERLAY() */
11118 12 : appendStringInfoString(buf, "OVERLAY(");
11119 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11120 12 : appendStringInfoString(buf, " PLACING ");
11121 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11122 12 : appendStringInfoString(buf, " FROM ");
11123 12 : get_rule_expr((Node *) lthird(expr->args), context, false);
11124 12 : if (list_length(expr->args) == 4)
11125 : {
11126 6 : appendStringInfoString(buf, " FOR ");
11127 6 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11128 : }
11129 12 : appendStringInfoChar(buf, ')');
11130 12 : return true;
11131 :
11132 6 : case F_POSITION_BIT_BIT:
11133 : case F_POSITION_BYTEA_BYTEA:
11134 : case F_POSITION_TEXT_TEXT:
11135 : /* POSITION() ... extra parens since args are b_expr not a_expr */
11136 6 : appendStringInfoString(buf, "POSITION((");
11137 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11138 6 : appendStringInfoString(buf, ") IN (");
11139 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11140 6 : appendStringInfoString(buf, "))");
11141 6 : return true;
11142 :
11143 6 : case F_SUBSTRING_BIT_INT4:
11144 : case F_SUBSTRING_BIT_INT4_INT4:
11145 : case F_SUBSTRING_BYTEA_INT4:
11146 : case F_SUBSTRING_BYTEA_INT4_INT4:
11147 : case F_SUBSTRING_TEXT_INT4:
11148 : case F_SUBSTRING_TEXT_INT4_INT4:
11149 : /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
11150 6 : appendStringInfoString(buf, "SUBSTRING(");
11151 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11152 6 : appendStringInfoString(buf, " FROM ");
11153 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11154 6 : if (list_length(expr->args) == 3)
11155 : {
11156 6 : appendStringInfoString(buf, " FOR ");
11157 6 : get_rule_expr((Node *) lthird(expr->args), context, false);
11158 : }
11159 6 : appendStringInfoChar(buf, ')');
11160 6 : return true;
11161 :
11162 6 : case F_SUBSTRING_TEXT_TEXT_TEXT:
11163 : /* SUBSTRING SIMILAR/ESCAPE */
11164 6 : appendStringInfoString(buf, "SUBSTRING(");
11165 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11166 6 : appendStringInfoString(buf, " SIMILAR ");
11167 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11168 6 : appendStringInfoString(buf, " ESCAPE ");
11169 6 : get_rule_expr((Node *) lthird(expr->args), context, false);
11170 6 : appendStringInfoChar(buf, ')');
11171 6 : return true;
11172 :
11173 12 : case F_BTRIM_BYTEA_BYTEA:
11174 : case F_BTRIM_TEXT:
11175 : case F_BTRIM_TEXT_TEXT:
11176 : /* TRIM() */
11177 12 : appendStringInfoString(buf, "TRIM(BOTH");
11178 12 : if (list_length(expr->args) == 2)
11179 : {
11180 12 : appendStringInfoChar(buf, ' ');
11181 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11182 : }
11183 12 : appendStringInfoString(buf, " FROM ");
11184 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11185 12 : appendStringInfoChar(buf, ')');
11186 12 : return true;
11187 :
11188 12 : case F_LTRIM_BYTEA_BYTEA:
11189 : case F_LTRIM_TEXT:
11190 : case F_LTRIM_TEXT_TEXT:
11191 : /* TRIM() */
11192 12 : appendStringInfoString(buf, "TRIM(LEADING");
11193 12 : if (list_length(expr->args) == 2)
11194 : {
11195 12 : appendStringInfoChar(buf, ' ');
11196 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11197 : }
11198 12 : appendStringInfoString(buf, " FROM ");
11199 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11200 12 : appendStringInfoChar(buf, ')');
11201 12 : return true;
11202 :
11203 12 : case F_RTRIM_BYTEA_BYTEA:
11204 : case F_RTRIM_TEXT:
11205 : case F_RTRIM_TEXT_TEXT:
11206 : /* TRIM() */
11207 12 : appendStringInfoString(buf, "TRIM(TRAILING");
11208 12 : if (list_length(expr->args) == 2)
11209 : {
11210 6 : appendStringInfoChar(buf, ' ');
11211 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11212 : }
11213 12 : appendStringInfoString(buf, " FROM ");
11214 12 : get_rule_expr((Node *) linitial(expr->args), context, false);
11215 12 : appendStringInfoChar(buf, ')');
11216 12 : return true;
11217 :
11218 12 : case F_SYSTEM_USER:
11219 12 : appendStringInfoString(buf, "SYSTEM_USER");
11220 12 : return true;
11221 :
11222 0 : case F_XMLEXISTS:
11223 : /* XMLEXISTS ... extra parens because args are c_expr */
11224 0 : appendStringInfoString(buf, "XMLEXISTS((");
11225 0 : get_rule_expr((Node *) linitial(expr->args), context, false);
11226 0 : appendStringInfoString(buf, ") PASSING (");
11227 0 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11228 0 : appendStringInfoString(buf, "))");
11229 0 : return true;
11230 : }
11231 6 : return false;
11232 : }
11233 :
11234 : /* ----------
11235 : * get_coercion_expr
11236 : *
11237 : * Make a string representation of a value coerced to a specific type
11238 : * ----------
11239 : */
11240 : static void
11241 4670 : get_coercion_expr(Node *arg, deparse_context *context,
11242 : Oid resulttype, int32 resulttypmod,
11243 : Node *parentNode)
11244 : {
11245 4670 : StringInfo buf = context->buf;
11246 :
11247 : /*
11248 : * Since parse_coerce.c doesn't immediately collapse application of
11249 : * length-coercion functions to constants, what we'll typically see in
11250 : * such cases is a Const with typmod -1 and a length-coercion function
11251 : * right above it. Avoid generating redundant output. However, beware of
11252 : * suppressing casts when the user actually wrote something like
11253 : * 'foo'::text::char(3).
11254 : *
11255 : * Note: it might seem that we are missing the possibility of needing to
11256 : * print a COLLATE clause for such a Const. However, a Const could only
11257 : * have nondefault collation in a post-constant-folding tree, in which the
11258 : * length coercion would have been folded too. See also the special
11259 : * handling of CollateExpr in coerce_to_target_type(): any collation
11260 : * marking will be above the coercion node, not below it.
11261 : */
11262 4670 : if (arg && IsA(arg, Const) &&
11263 504 : ((Const *) arg)->consttype == resulttype &&
11264 24 : ((Const *) arg)->consttypmod == -1)
11265 : {
11266 : /* Show the constant without normal ::typename decoration */
11267 24 : get_const_expr((Const *) arg, context, -1);
11268 : }
11269 : else
11270 : {
11271 4646 : if (!PRETTY_PAREN(context))
11272 4288 : appendStringInfoChar(buf, '(');
11273 4646 : get_rule_expr_paren(arg, context, false, parentNode);
11274 4646 : if (!PRETTY_PAREN(context))
11275 4288 : appendStringInfoChar(buf, ')');
11276 : }
11277 :
11278 : /*
11279 : * Never emit resulttype(arg) functional notation. A pg_proc entry could
11280 : * take precedence, and a resulttype in pg_temp would require schema
11281 : * qualification that format_type_with_typemod() would usually omit. We've
11282 : * standardized on arg::resulttype, but CAST(arg AS resulttype) notation
11283 : * would work fine.
11284 : */
11285 4670 : appendStringInfo(buf, "::%s",
11286 : format_type_with_typemod(resulttype, resulttypmod));
11287 4670 : }
11288 :
11289 : /* ----------
11290 : * get_const_expr
11291 : *
11292 : * Make a string representation of a Const
11293 : *
11294 : * showtype can be -1 to never show "::typename" decoration, or +1 to always
11295 : * show it, or 0 to show it only if the constant wouldn't be assumed to be
11296 : * the right type by default.
11297 : *
11298 : * If the Const's collation isn't default for its type, show that too.
11299 : * We mustn't do this when showtype is -1 (since that means the caller will
11300 : * print "::typename", and we can't put a COLLATE clause in between). It's
11301 : * caller's responsibility that collation isn't missed in such cases.
11302 : * ----------
11303 : */
11304 : static void
11305 64138 : get_const_expr(Const *constval, deparse_context *context, int showtype)
11306 : {
11307 64138 : StringInfo buf = context->buf;
11308 : Oid typoutput;
11309 : bool typIsVarlena;
11310 : char *extval;
11311 64138 : bool needlabel = false;
11312 :
11313 64138 : if (constval->constisnull)
11314 : {
11315 : /*
11316 : * Always label the type of a NULL constant to prevent misdecisions
11317 : * about type when reparsing.
11318 : */
11319 1038 : appendStringInfoString(buf, "NULL");
11320 1038 : if (showtype >= 0)
11321 : {
11322 994 : appendStringInfo(buf, "::%s",
11323 : format_type_with_typemod(constval->consttype,
11324 : constval->consttypmod));
11325 994 : get_const_collation(constval, context);
11326 : }
11327 8548 : return;
11328 : }
11329 :
11330 63100 : getTypeOutputInfo(constval->consttype,
11331 : &typoutput, &typIsVarlena);
11332 :
11333 63100 : extval = OidOutputFunctionCall(typoutput, constval->constvalue);
11334 :
11335 63100 : switch (constval->consttype)
11336 : {
11337 35752 : case INT4OID:
11338 :
11339 : /*
11340 : * INT4 can be printed without any decoration, unless it is
11341 : * negative; in that case print it as '-nnn'::integer to ensure
11342 : * that the output will re-parse as a constant, not as a constant
11343 : * plus operator. In most cases we could get away with printing
11344 : * (-nnn) instead, because of the way that gram.y handles negative
11345 : * literals; but that doesn't work for INT_MIN, and it doesn't
11346 : * seem that much prettier anyway.
11347 : */
11348 35752 : if (extval[0] != '-')
11349 35258 : appendStringInfoString(buf, extval);
11350 : else
11351 : {
11352 494 : appendStringInfo(buf, "'%s'", extval);
11353 494 : needlabel = true; /* we must attach a cast */
11354 : }
11355 35752 : break;
11356 :
11357 1036 : case NUMERICOID:
11358 :
11359 : /*
11360 : * NUMERIC can be printed without quotes if it looks like a float
11361 : * constant (not an integer, and not Infinity or NaN) and doesn't
11362 : * have a leading sign (for the same reason as for INT4).
11363 : */
11364 1036 : if (isdigit((unsigned char) extval[0]) &&
11365 1036 : strcspn(extval, "eE.") != strlen(extval))
11366 : {
11367 380 : appendStringInfoString(buf, extval);
11368 : }
11369 : else
11370 : {
11371 656 : appendStringInfo(buf, "'%s'", extval);
11372 656 : needlabel = true; /* we must attach a cast */
11373 : }
11374 1036 : break;
11375 :
11376 1466 : case BOOLOID:
11377 1466 : if (strcmp(extval, "t") == 0)
11378 696 : appendStringInfoString(buf, "true");
11379 : else
11380 770 : appendStringInfoString(buf, "false");
11381 1466 : break;
11382 :
11383 24846 : default:
11384 24846 : simple_quote_literal(buf, extval);
11385 24846 : break;
11386 : }
11387 :
11388 63100 : pfree(extval);
11389 :
11390 63100 : if (showtype < 0)
11391 7510 : return;
11392 :
11393 : /*
11394 : * For showtype == 0, append ::typename unless the constant will be
11395 : * implicitly typed as the right type when it is read in.
11396 : *
11397 : * XXX this code has to be kept in sync with the behavior of the parser,
11398 : * especially make_const.
11399 : */
11400 55590 : switch (constval->consttype)
11401 : {
11402 1534 : case BOOLOID:
11403 : case UNKNOWNOID:
11404 : /* These types can be left unlabeled */
11405 1534 : needlabel = false;
11406 1534 : break;
11407 31430 : case INT4OID:
11408 : /* We determined above whether a label is needed */
11409 31430 : break;
11410 1036 : case NUMERICOID:
11411 :
11412 : /*
11413 : * Float-looking constants will be typed as numeric, which we
11414 : * checked above; but if there's a nondefault typmod we need to
11415 : * show it.
11416 : */
11417 1036 : needlabel |= (constval->consttypmod >= 0);
11418 1036 : break;
11419 21590 : default:
11420 21590 : needlabel = true;
11421 21590 : break;
11422 : }
11423 55590 : if (needlabel || showtype > 0)
11424 22726 : appendStringInfo(buf, "::%s",
11425 : format_type_with_typemod(constval->consttype,
11426 : constval->consttypmod));
11427 :
11428 55590 : get_const_collation(constval, context);
11429 : }
11430 :
11431 : /*
11432 : * helper for get_const_expr: append COLLATE if needed
11433 : */
11434 : static void
11435 56584 : get_const_collation(Const *constval, deparse_context *context)
11436 : {
11437 56584 : StringInfo buf = context->buf;
11438 :
11439 56584 : if (OidIsValid(constval->constcollid))
11440 : {
11441 8472 : Oid typcollation = get_typcollation(constval->consttype);
11442 :
11443 8472 : if (constval->constcollid != typcollation)
11444 : {
11445 70 : appendStringInfo(buf, " COLLATE %s",
11446 : generate_collation_name(constval->constcollid));
11447 : }
11448 : }
11449 56584 : }
11450 :
11451 : /*
11452 : * get_json_path_spec - Parse back a JSON path specification
11453 : */
11454 : static void
11455 456 : get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
11456 : {
11457 456 : if (IsA(path_spec, Const))
11458 456 : get_const_expr((Const *) path_spec, context, -1);
11459 : else
11460 0 : get_rule_expr(path_spec, context, showimplicit);
11461 456 : }
11462 :
11463 : /*
11464 : * get_json_format - Parse back a JsonFormat node
11465 : */
11466 : static void
11467 186 : get_json_format(JsonFormat *format, StringInfo buf)
11468 : {
11469 186 : if (format->format_type == JS_FORMAT_DEFAULT)
11470 108 : return;
11471 :
11472 78 : appendStringInfoString(buf,
11473 78 : format->format_type == JS_FORMAT_JSONB ?
11474 : " FORMAT JSONB" : " FORMAT JSON");
11475 :
11476 78 : if (format->encoding != JS_ENC_DEFAULT)
11477 : {
11478 : const char *encoding;
11479 :
11480 6 : encoding =
11481 12 : format->encoding == JS_ENC_UTF16 ? "UTF16" :
11482 6 : format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
11483 :
11484 6 : appendStringInfo(buf, " ENCODING %s", encoding);
11485 : }
11486 : }
11487 :
11488 : /*
11489 : * get_json_returning - Parse back a JsonReturning structure
11490 : */
11491 : static void
11492 168 : get_json_returning(JsonReturning *returning, StringInfo buf,
11493 : bool json_format_by_default)
11494 : {
11495 168 : if (!OidIsValid(returning->typid))
11496 0 : return;
11497 :
11498 168 : appendStringInfo(buf, " RETURNING %s",
11499 : format_type_with_typemod(returning->typid,
11500 : returning->typmod));
11501 :
11502 324 : if (!json_format_by_default ||
11503 156 : returning->format->format_type !=
11504 156 : (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
11505 36 : get_json_format(returning->format, buf);
11506 : }
11507 :
11508 : /*
11509 : * get_json_constructor - Parse back a JsonConstructorExpr node
11510 : */
11511 : static void
11512 174 : get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
11513 : bool showimplicit)
11514 : {
11515 174 : StringInfo buf = context->buf;
11516 : const char *funcname;
11517 : bool is_json_object;
11518 : int curridx;
11519 : ListCell *lc;
11520 :
11521 174 : if (ctor->type == JSCTOR_JSON_OBJECTAGG)
11522 : {
11523 36 : get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
11524 36 : return;
11525 : }
11526 138 : else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
11527 : {
11528 30 : get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
11529 30 : return;
11530 : }
11531 :
11532 108 : switch (ctor->type)
11533 : {
11534 30 : case JSCTOR_JSON_OBJECT:
11535 30 : funcname = "JSON_OBJECT";
11536 30 : break;
11537 12 : case JSCTOR_JSON_ARRAY:
11538 12 : funcname = "JSON_ARRAY";
11539 12 : break;
11540 42 : case JSCTOR_JSON_PARSE:
11541 42 : funcname = "JSON";
11542 42 : break;
11543 12 : case JSCTOR_JSON_SCALAR:
11544 12 : funcname = "JSON_SCALAR";
11545 12 : break;
11546 12 : case JSCTOR_JSON_SERIALIZE:
11547 12 : funcname = "JSON_SERIALIZE";
11548 12 : break;
11549 0 : default:
11550 0 : elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
11551 : }
11552 :
11553 108 : appendStringInfo(buf, "%s(", funcname);
11554 :
11555 108 : is_json_object = ctor->type == JSCTOR_JSON_OBJECT;
11556 282 : foreach(lc, ctor->args)
11557 : {
11558 174 : curridx = foreach_current_index(lc);
11559 174 : if (curridx > 0)
11560 : {
11561 : const char *sep;
11562 :
11563 66 : sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
11564 66 : appendStringInfoString(buf, sep);
11565 : }
11566 :
11567 174 : get_rule_expr((Node *) lfirst(lc), context, true);
11568 : }
11569 :
11570 108 : get_json_constructor_options(ctor, buf);
11571 108 : appendStringInfoChar(buf, ')');
11572 : }
11573 :
11574 : /*
11575 : * Append options, if any, to the JSON constructor being deparsed
11576 : */
11577 : static void
11578 174 : get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
11579 : {
11580 174 : if (ctor->absent_on_null)
11581 : {
11582 24 : if (ctor->type == JSCTOR_JSON_OBJECT ||
11583 24 : ctor->type == JSCTOR_JSON_OBJECTAGG)
11584 0 : appendStringInfoString(buf, " ABSENT ON NULL");
11585 : }
11586 : else
11587 : {
11588 150 : if (ctor->type == JSCTOR_JSON_ARRAY ||
11589 150 : ctor->type == JSCTOR_JSON_ARRAYAGG)
11590 18 : appendStringInfoString(buf, " NULL ON NULL");
11591 : }
11592 :
11593 174 : if (ctor->unique)
11594 24 : appendStringInfoString(buf, " WITH UNIQUE KEYS");
11595 :
11596 : /*
11597 : * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
11598 : * support one.
11599 : */
11600 174 : if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
11601 120 : get_json_returning(ctor->returning, buf, true);
11602 174 : }
11603 :
11604 : /*
11605 : * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
11606 : */
11607 : static void
11608 66 : get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
11609 : const char *funcname, bool is_json_objectagg)
11610 : {
11611 : StringInfoData options;
11612 :
11613 66 : initStringInfo(&options);
11614 66 : get_json_constructor_options(ctor, &options);
11615 :
11616 66 : if (IsA(ctor->func, Aggref))
11617 54 : get_agg_expr_helper((Aggref *) ctor->func, context,
11618 54 : (Aggref *) ctor->func,
11619 54 : funcname, options.data, is_json_objectagg);
11620 12 : else if (IsA(ctor->func, WindowFunc))
11621 12 : get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
11622 12 : funcname, options.data,
11623 : is_json_objectagg);
11624 : else
11625 0 : elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
11626 : nodeTag(ctor->func));
11627 66 : }
11628 :
11629 : /*
11630 : * simple_quote_literal - Format a string as a SQL literal, append to buf
11631 : */
11632 : static void
11633 25906 : simple_quote_literal(StringInfo buf, const char *val)
11634 : {
11635 : const char *valptr;
11636 :
11637 : /*
11638 : * We form the string literal according to the prevailing setting of
11639 : * standard_conforming_strings; we never use E''. User is responsible for
11640 : * making sure result is used correctly.
11641 : */
11642 25906 : appendStringInfoChar(buf, '\'');
11643 262174 : for (valptr = val; *valptr; valptr++)
11644 : {
11645 236268 : char ch = *valptr;
11646 :
11647 236268 : if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
11648 306 : appendStringInfoChar(buf, ch);
11649 236268 : appendStringInfoChar(buf, ch);
11650 : }
11651 25906 : appendStringInfoChar(buf, '\'');
11652 25906 : }
11653 :
11654 :
11655 : /* ----------
11656 : * get_sublink_expr - Parse back a sublink
11657 : * ----------
11658 : */
11659 : static void
11660 418 : get_sublink_expr(SubLink *sublink, deparse_context *context)
11661 : {
11662 418 : StringInfo buf = context->buf;
11663 418 : Query *query = (Query *) (sublink->subselect);
11664 418 : char *opname = NULL;
11665 : bool need_paren;
11666 :
11667 418 : if (sublink->subLinkType == ARRAY_SUBLINK)
11668 24 : appendStringInfoString(buf, "ARRAY(");
11669 : else
11670 394 : appendStringInfoChar(buf, '(');
11671 :
11672 : /*
11673 : * Note that we print the name of only the first operator, when there are
11674 : * multiple combining operators. This is an approximation that could go
11675 : * wrong in various scenarios (operators in different schemas, renamed
11676 : * operators, etc) but there is not a whole lot we can do about it, since
11677 : * the syntax allows only one operator to be shown.
11678 : */
11679 418 : if (sublink->testexpr)
11680 : {
11681 18 : if (IsA(sublink->testexpr, OpExpr))
11682 : {
11683 : /* single combining operator */
11684 6 : OpExpr *opexpr = (OpExpr *) sublink->testexpr;
11685 :
11686 6 : get_rule_expr(linitial(opexpr->args), context, true);
11687 6 : opname = generate_operator_name(opexpr->opno,
11688 6 : exprType(linitial(opexpr->args)),
11689 6 : exprType(lsecond(opexpr->args)));
11690 : }
11691 12 : else if (IsA(sublink->testexpr, BoolExpr))
11692 : {
11693 : /* multiple combining operators, = or <> cases */
11694 : char *sep;
11695 : ListCell *l;
11696 :
11697 6 : appendStringInfoChar(buf, '(');
11698 6 : sep = "";
11699 18 : foreach(l, ((BoolExpr *) sublink->testexpr)->args)
11700 : {
11701 12 : OpExpr *opexpr = lfirst_node(OpExpr, l);
11702 :
11703 12 : appendStringInfoString(buf, sep);
11704 12 : get_rule_expr(linitial(opexpr->args), context, true);
11705 12 : if (!opname)
11706 6 : opname = generate_operator_name(opexpr->opno,
11707 6 : exprType(linitial(opexpr->args)),
11708 6 : exprType(lsecond(opexpr->args)));
11709 12 : sep = ", ";
11710 : }
11711 6 : appendStringInfoChar(buf, ')');
11712 : }
11713 6 : else if (IsA(sublink->testexpr, RowCompareExpr))
11714 : {
11715 : /* multiple combining operators, < <= > >= cases */
11716 6 : RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
11717 :
11718 6 : appendStringInfoChar(buf, '(');
11719 6 : get_rule_expr((Node *) rcexpr->largs, context, true);
11720 6 : opname = generate_operator_name(linitial_oid(rcexpr->opnos),
11721 6 : exprType(linitial(rcexpr->largs)),
11722 6 : exprType(linitial(rcexpr->rargs)));
11723 6 : appendStringInfoChar(buf, ')');
11724 : }
11725 : else
11726 0 : elog(ERROR, "unrecognized testexpr type: %d",
11727 : (int) nodeTag(sublink->testexpr));
11728 : }
11729 :
11730 418 : need_paren = true;
11731 :
11732 418 : switch (sublink->subLinkType)
11733 : {
11734 164 : case EXISTS_SUBLINK:
11735 164 : appendStringInfoString(buf, "EXISTS ");
11736 164 : break;
11737 :
11738 12 : case ANY_SUBLINK:
11739 12 : if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
11740 6 : appendStringInfoString(buf, " IN ");
11741 : else
11742 6 : appendStringInfo(buf, " %s ANY ", opname);
11743 12 : break;
11744 :
11745 6 : case ALL_SUBLINK:
11746 6 : appendStringInfo(buf, " %s ALL ", opname);
11747 6 : break;
11748 :
11749 0 : case ROWCOMPARE_SUBLINK:
11750 0 : appendStringInfo(buf, " %s ", opname);
11751 0 : break;
11752 :
11753 236 : case EXPR_SUBLINK:
11754 : case MULTIEXPR_SUBLINK:
11755 : case ARRAY_SUBLINK:
11756 236 : need_paren = false;
11757 236 : break;
11758 :
11759 0 : case CTE_SUBLINK: /* shouldn't occur in a SubLink */
11760 : default:
11761 0 : elog(ERROR, "unrecognized sublink type: %d",
11762 : (int) sublink->subLinkType);
11763 : break;
11764 : }
11765 :
11766 418 : if (need_paren)
11767 182 : appendStringInfoChar(buf, '(');
11768 :
11769 418 : get_query_def(query, buf, context->namespaces, NULL, false,
11770 : context->prettyFlags, context->wrapColumn,
11771 : context->indentLevel);
11772 :
11773 418 : if (need_paren)
11774 182 : appendStringInfoString(buf, "))");
11775 : else
11776 236 : appendStringInfoChar(buf, ')');
11777 418 : }
11778 :
11779 :
11780 : /* ----------
11781 : * get_xmltable - Parse back a XMLTABLE function
11782 : * ----------
11783 : */
11784 : static void
11785 56 : get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
11786 : {
11787 56 : StringInfo buf = context->buf;
11788 :
11789 56 : appendStringInfoString(buf, "XMLTABLE(");
11790 :
11791 56 : if (tf->ns_uris != NIL)
11792 : {
11793 : ListCell *lc1,
11794 : *lc2;
11795 10 : bool first = true;
11796 :
11797 10 : appendStringInfoString(buf, "XMLNAMESPACES (");
11798 20 : forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
11799 : {
11800 10 : Node *expr = (Node *) lfirst(lc1);
11801 10 : String *ns_node = lfirst_node(String, lc2);
11802 :
11803 10 : if (!first)
11804 0 : appendStringInfoString(buf, ", ");
11805 : else
11806 10 : first = false;
11807 :
11808 10 : if (ns_node != NULL)
11809 : {
11810 10 : get_rule_expr(expr, context, showimplicit);
11811 10 : appendStringInfo(buf, " AS %s", strVal(ns_node));
11812 : }
11813 : else
11814 : {
11815 0 : appendStringInfoString(buf, "DEFAULT ");
11816 0 : get_rule_expr(expr, context, showimplicit);
11817 : }
11818 : }
11819 10 : appendStringInfoString(buf, "), ");
11820 : }
11821 :
11822 56 : appendStringInfoChar(buf, '(');
11823 56 : get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
11824 56 : appendStringInfoString(buf, ") PASSING (");
11825 56 : get_rule_expr((Node *) tf->docexpr, context, showimplicit);
11826 56 : appendStringInfoChar(buf, ')');
11827 :
11828 56 : if (tf->colexprs != NIL)
11829 : {
11830 : ListCell *l1;
11831 : ListCell *l2;
11832 : ListCell *l3;
11833 : ListCell *l4;
11834 : ListCell *l5;
11835 56 : int colnum = 0;
11836 :
11837 56 : appendStringInfoString(buf, " COLUMNS ");
11838 362 : forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
11839 : l4, tf->colexprs, l5, tf->coldefexprs)
11840 : {
11841 306 : char *colname = strVal(lfirst(l1));
11842 306 : Oid typid = lfirst_oid(l2);
11843 306 : int32 typmod = lfirst_int(l3);
11844 306 : Node *colexpr = (Node *) lfirst(l4);
11845 306 : Node *coldefexpr = (Node *) lfirst(l5);
11846 306 : bool ordinality = (tf->ordinalitycol == colnum);
11847 306 : bool notnull = bms_is_member(colnum, tf->notnulls);
11848 :
11849 306 : if (colnum > 0)
11850 250 : appendStringInfoString(buf, ", ");
11851 306 : colnum++;
11852 :
11853 578 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
11854 : ordinality ? "FOR ORDINALITY" :
11855 272 : format_type_with_typemod(typid, typmod));
11856 306 : if (ordinality)
11857 34 : continue;
11858 :
11859 272 : if (coldefexpr != NULL)
11860 : {
11861 34 : appendStringInfoString(buf, " DEFAULT (");
11862 34 : get_rule_expr((Node *) coldefexpr, context, showimplicit);
11863 34 : appendStringInfoChar(buf, ')');
11864 : }
11865 272 : if (colexpr != NULL)
11866 : {
11867 248 : appendStringInfoString(buf, " PATH (");
11868 248 : get_rule_expr((Node *) colexpr, context, showimplicit);
11869 248 : appendStringInfoChar(buf, ')');
11870 : }
11871 272 : if (notnull)
11872 34 : appendStringInfoString(buf, " NOT NULL");
11873 : }
11874 : }
11875 :
11876 56 : appendStringInfoChar(buf, ')');
11877 56 : }
11878 :
11879 : /*
11880 : * get_json_table_nested_columns - Parse back nested JSON_TABLE columns
11881 : */
11882 : static void
11883 102 : get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
11884 : deparse_context *context, bool showimplicit,
11885 : bool needcomma)
11886 : {
11887 102 : if (IsA(plan, JsonTablePathScan))
11888 : {
11889 72 : JsonTablePathScan *scan = castNode(JsonTablePathScan, plan);
11890 :
11891 72 : if (needcomma)
11892 48 : appendStringInfoChar(context->buf, ',');
11893 :
11894 72 : appendStringInfoChar(context->buf, ' ');
11895 72 : appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
11896 72 : get_const_expr(scan->path->value, context, -1);
11897 72 : appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
11898 72 : get_json_table_columns(tf, scan, context, showimplicit);
11899 : }
11900 30 : else if (IsA(plan, JsonTableSiblingJoin))
11901 : {
11902 30 : JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
11903 :
11904 30 : get_json_table_nested_columns(tf, join->lplan, context, showimplicit,
11905 : needcomma);
11906 30 : get_json_table_nested_columns(tf, join->rplan, context, showimplicit,
11907 : true);
11908 : }
11909 102 : }
11910 :
11911 : /*
11912 : * get_json_table_columns - Parse back JSON_TABLE columns
11913 : */
11914 : static void
11915 180 : get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
11916 : deparse_context *context,
11917 : bool showimplicit)
11918 : {
11919 180 : StringInfo buf = context->buf;
11920 : ListCell *lc_colname;
11921 : ListCell *lc_coltype;
11922 : ListCell *lc_coltypmod;
11923 : ListCell *lc_colvalexpr;
11924 180 : int colnum = 0;
11925 :
11926 180 : appendStringInfoChar(buf, ' ');
11927 180 : appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
11928 :
11929 180 : if (PRETTY_INDENT(context))
11930 138 : context->indentLevel += PRETTYINDENT_VAR;
11931 :
11932 858 : forfour(lc_colname, tf->colnames,
11933 : lc_coltype, tf->coltypes,
11934 : lc_coltypmod, tf->coltypmods,
11935 : lc_colvalexpr, tf->colvalexprs)
11936 : {
11937 726 : char *colname = strVal(lfirst(lc_colname));
11938 : JsonExpr *colexpr;
11939 : Oid typid;
11940 : int32 typmod;
11941 : bool ordinality;
11942 : JsonBehaviorType default_behavior;
11943 :
11944 726 : typid = lfirst_oid(lc_coltype);
11945 726 : typmod = lfirst_int(lc_coltypmod);
11946 726 : colexpr = castNode(JsonExpr, lfirst(lc_colvalexpr));
11947 :
11948 : /* Skip columns that don't belong to this scan. */
11949 726 : if (scan->colMin < 0 || colnum < scan->colMin)
11950 : {
11951 264 : colnum++;
11952 264 : continue;
11953 : }
11954 462 : if (colnum > scan->colMax)
11955 48 : break;
11956 :
11957 414 : if (colnum > scan->colMin)
11958 258 : appendStringInfoString(buf, ", ");
11959 :
11960 414 : colnum++;
11961 :
11962 414 : ordinality = !colexpr;
11963 :
11964 414 : appendContextKeyword(context, "", 0, 0, 0);
11965 :
11966 810 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
11967 : ordinality ? "FOR ORDINALITY" :
11968 396 : format_type_with_typemod(typid, typmod));
11969 414 : if (ordinality)
11970 18 : continue;
11971 :
11972 : /*
11973 : * Set default_behavior to guide get_json_expr_options() on whether to
11974 : * to emit the ON ERROR / EMPTY clauses.
11975 : */
11976 396 : if (colexpr->op == JSON_EXISTS_OP)
11977 : {
11978 36 : appendStringInfoString(buf, " EXISTS");
11979 36 : default_behavior = JSON_BEHAVIOR_FALSE;
11980 : }
11981 : else
11982 : {
11983 360 : if (colexpr->op == JSON_QUERY_OP)
11984 : {
11985 : char typcategory;
11986 : bool typispreferred;
11987 :
11988 174 : get_type_category_preferred(typid, &typcategory, &typispreferred);
11989 :
11990 174 : if (typcategory == TYPCATEGORY_STRING)
11991 36 : appendStringInfoString(buf,
11992 36 : colexpr->format->format_type == JS_FORMAT_JSONB ?
11993 : " FORMAT JSONB" : " FORMAT JSON");
11994 : }
11995 :
11996 360 : default_behavior = JSON_BEHAVIOR_NULL;
11997 : }
11998 :
11999 396 : appendStringInfoString(buf, " PATH ");
12000 :
12001 396 : get_json_path_spec(colexpr->path_spec, context, showimplicit);
12002 :
12003 396 : get_json_expr_options(colexpr, context, default_behavior);
12004 : }
12005 :
12006 180 : if (scan->child)
12007 42 : get_json_table_nested_columns(tf, scan->child, context, showimplicit,
12008 42 : scan->colMin >= 0);
12009 :
12010 180 : if (PRETTY_INDENT(context))
12011 138 : context->indentLevel -= PRETTYINDENT_VAR;
12012 :
12013 180 : appendContextKeyword(context, ")", 0, 0, 0);
12014 180 : }
12015 :
12016 : /* ----------
12017 : * get_json_table - Parse back a JSON_TABLE function
12018 : * ----------
12019 : */
12020 : static void
12021 108 : get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
12022 : {
12023 108 : StringInfo buf = context->buf;
12024 108 : JsonExpr *jexpr = castNode(JsonExpr, tf->docexpr);
12025 108 : JsonTablePathScan *root = castNode(JsonTablePathScan, tf->plan);
12026 :
12027 108 : appendStringInfoString(buf, "JSON_TABLE(");
12028 :
12029 108 : if (PRETTY_INDENT(context))
12030 66 : context->indentLevel += PRETTYINDENT_VAR;
12031 :
12032 108 : appendContextKeyword(context, "", 0, 0, 0);
12033 :
12034 108 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12035 :
12036 108 : appendStringInfoString(buf, ", ");
12037 :
12038 108 : get_const_expr(root->path->value, context, -1);
12039 :
12040 108 : appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12041 :
12042 108 : if (jexpr->passing_values)
12043 : {
12044 : ListCell *lc1,
12045 : *lc2;
12046 84 : bool needcomma = false;
12047 :
12048 84 : appendStringInfoChar(buf, ' ');
12049 84 : appendContextKeyword(context, "PASSING ", 0, 0, 0);
12050 :
12051 84 : if (PRETTY_INDENT(context))
12052 42 : context->indentLevel += PRETTYINDENT_VAR;
12053 :
12054 252 : forboth(lc1, jexpr->passing_names,
12055 : lc2, jexpr->passing_values)
12056 : {
12057 168 : if (needcomma)
12058 84 : appendStringInfoString(buf, ", ");
12059 168 : needcomma = true;
12060 :
12061 168 : appendContextKeyword(context, "", 0, 0, 0);
12062 :
12063 168 : get_rule_expr((Node *) lfirst(lc2), context, false);
12064 168 : appendStringInfo(buf, " AS %s",
12065 168 : quote_identifier((lfirst_node(String, lc1))->sval)
12066 : );
12067 : }
12068 :
12069 84 : if (PRETTY_INDENT(context))
12070 42 : context->indentLevel -= PRETTYINDENT_VAR;
12071 : }
12072 :
12073 108 : get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12074 : showimplicit);
12075 :
12076 108 : if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12077 6 : get_json_behavior(jexpr->on_error, context, "ERROR");
12078 :
12079 108 : if (PRETTY_INDENT(context))
12080 66 : context->indentLevel -= PRETTYINDENT_VAR;
12081 :
12082 108 : appendContextKeyword(context, ")", 0, 0, 0);
12083 108 : }
12084 :
12085 : /* ----------
12086 : * get_tablefunc - Parse back a table function
12087 : * ----------
12088 : */
12089 : static void
12090 164 : get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
12091 : {
12092 : /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12093 :
12094 164 : if (tf->functype == TFT_XMLTABLE)
12095 56 : get_xmltable(tf, context, showimplicit);
12096 108 : else if (tf->functype == TFT_JSON_TABLE)
12097 108 : get_json_table(tf, context, showimplicit);
12098 164 : }
12099 :
12100 : /* ----------
12101 : * get_from_clause - Parse back a FROM clause
12102 : *
12103 : * "prefix" is the keyword that denotes the start of the list of FROM
12104 : * elements. It is FROM when used to parse back SELECT and UPDATE, but
12105 : * is USING when parsing back DELETE.
12106 : * ----------
12107 : */
12108 : static void
12109 4734 : get_from_clause(Query *query, const char *prefix, deparse_context *context)
12110 : {
12111 4734 : StringInfo buf = context->buf;
12112 4734 : bool first = true;
12113 : ListCell *l;
12114 :
12115 : /*
12116 : * We use the query's jointree as a guide to what to print. However, we
12117 : * must ignore auto-added RTEs that are marked not inFromCl. (These can
12118 : * only appear at the top level of the jointree, so it's sufficient to
12119 : * check here.) This check also ensures we ignore the rule pseudo-RTEs
12120 : * for NEW and OLD.
12121 : */
12122 9448 : foreach(l, query->jointree->fromlist)
12123 : {
12124 4714 : Node *jtnode = (Node *) lfirst(l);
12125 :
12126 4714 : if (IsA(jtnode, RangeTblRef))
12127 : {
12128 3762 : int varno = ((RangeTblRef *) jtnode)->rtindex;
12129 3762 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12130 :
12131 3762 : if (!rte->inFromCl)
12132 376 : continue;
12133 : }
12134 :
12135 4338 : if (first)
12136 : {
12137 3992 : appendContextKeyword(context, prefix,
12138 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
12139 3992 : first = false;
12140 :
12141 3992 : get_from_clause_item(jtnode, query, context);
12142 : }
12143 : else
12144 : {
12145 : StringInfoData itembuf;
12146 :
12147 346 : appendStringInfoString(buf, ", ");
12148 :
12149 : /*
12150 : * Put the new FROM item's text into itembuf so we can decide
12151 : * after we've got it whether or not it needs to go on a new line.
12152 : */
12153 346 : initStringInfo(&itembuf);
12154 346 : context->buf = &itembuf;
12155 :
12156 346 : get_from_clause_item(jtnode, query, context);
12157 :
12158 : /* Restore context's output buffer */
12159 346 : context->buf = buf;
12160 :
12161 : /* Consider line-wrapping if enabled */
12162 346 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
12163 : {
12164 : /* Does the new item start with a new line? */
12165 346 : if (itembuf.len > 0 && itembuf.data[0] == '\n')
12166 : {
12167 : /* If so, we shouldn't add anything */
12168 : /* instead, remove any trailing spaces currently in buf */
12169 0 : removeStringInfoSpaces(buf);
12170 : }
12171 : else
12172 : {
12173 : char *trailing_nl;
12174 :
12175 : /* Locate the start of the current line in the buffer */
12176 346 : trailing_nl = strrchr(buf->data, '\n');
12177 346 : if (trailing_nl == NULL)
12178 0 : trailing_nl = buf->data;
12179 : else
12180 346 : trailing_nl++;
12181 :
12182 : /*
12183 : * Add a newline, plus some indentation, if the new item
12184 : * would cause an overflow.
12185 : */
12186 346 : if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
12187 346 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
12188 : PRETTYINDENT_STD,
12189 : PRETTYINDENT_VAR);
12190 : }
12191 : }
12192 :
12193 : /* Add the new item */
12194 346 : appendBinaryStringInfo(buf, itembuf.data, itembuf.len);
12195 :
12196 : /* clean up */
12197 346 : pfree(itembuf.data);
12198 : }
12199 : }
12200 4734 : }
12201 :
12202 : static void
12203 7334 : get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
12204 : {
12205 7334 : StringInfo buf = context->buf;
12206 7334 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
12207 :
12208 7334 : if (IsA(jtnode, RangeTblRef))
12209 : {
12210 5836 : int varno = ((RangeTblRef *) jtnode)->rtindex;
12211 5836 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12212 5836 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
12213 5836 : RangeTblFunction *rtfunc1 = NULL;
12214 :
12215 5836 : if (rte->lateral)
12216 112 : appendStringInfoString(buf, "LATERAL ");
12217 :
12218 : /* Print the FROM item proper */
12219 5836 : switch (rte->rtekind)
12220 : {
12221 4470 : case RTE_RELATION:
12222 : /* Normal relation RTE */
12223 8940 : appendStringInfo(buf, "%s%s",
12224 4470 : only_marker(rte),
12225 : generate_relation_name(rte->relid,
12226 : context->namespaces));
12227 4470 : break;
12228 292 : case RTE_SUBQUERY:
12229 : /* Subquery RTE */
12230 292 : appendStringInfoChar(buf, '(');
12231 292 : get_query_def(rte->subquery, buf, context->namespaces, NULL,
12232 : true,
12233 : context->prettyFlags, context->wrapColumn,
12234 : context->indentLevel);
12235 292 : appendStringInfoChar(buf, ')');
12236 292 : break;
12237 786 : case RTE_FUNCTION:
12238 : /* Function RTE */
12239 786 : rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
12240 :
12241 : /*
12242 : * Omit ROWS FROM() syntax for just one function, unless it
12243 : * has both a coldeflist and WITH ORDINALITY. If it has both,
12244 : * we must use ROWS FROM() syntax to avoid ambiguity about
12245 : * whether the coldeflist includes the ordinality column.
12246 : */
12247 786 : if (list_length(rte->functions) == 1 &&
12248 756 : (rtfunc1->funccolnames == NIL || !rte->funcordinality))
12249 : {
12250 756 : get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
12251 : /* we'll print the coldeflist below, if it has one */
12252 : }
12253 : else
12254 : {
12255 : bool all_unnest;
12256 : ListCell *lc;
12257 :
12258 : /*
12259 : * If all the function calls in the list are to unnest,
12260 : * and none need a coldeflist, then collapse the list back
12261 : * down to UNNEST(args). (If we had more than one
12262 : * built-in unnest function, this would get more
12263 : * difficult.)
12264 : *
12265 : * XXX This is pretty ugly, since it makes not-terribly-
12266 : * future-proof assumptions about what the parser would do
12267 : * with the output; but the alternative is to emit our
12268 : * nonstandard ROWS FROM() notation for what might have
12269 : * been a perfectly spec-compliant multi-argument
12270 : * UNNEST().
12271 : */
12272 30 : all_unnest = true;
12273 78 : foreach(lc, rte->functions)
12274 : {
12275 66 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12276 :
12277 66 : if (!IsA(rtfunc->funcexpr, FuncExpr) ||
12278 66 : ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
12279 48 : rtfunc->funccolnames != NIL)
12280 : {
12281 18 : all_unnest = false;
12282 18 : break;
12283 : }
12284 : }
12285 :
12286 30 : if (all_unnest)
12287 : {
12288 12 : List *allargs = NIL;
12289 :
12290 48 : foreach(lc, rte->functions)
12291 : {
12292 36 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12293 36 : List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
12294 :
12295 36 : allargs = list_concat(allargs, args);
12296 : }
12297 :
12298 12 : appendStringInfoString(buf, "UNNEST(");
12299 12 : get_rule_expr((Node *) allargs, context, true);
12300 12 : appendStringInfoChar(buf, ')');
12301 : }
12302 : else
12303 : {
12304 18 : int funcno = 0;
12305 :
12306 18 : appendStringInfoString(buf, "ROWS FROM(");
12307 66 : foreach(lc, rte->functions)
12308 : {
12309 48 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12310 :
12311 48 : if (funcno > 0)
12312 30 : appendStringInfoString(buf, ", ");
12313 48 : get_rule_expr_funccall(rtfunc->funcexpr, context, true);
12314 48 : if (rtfunc->funccolnames != NIL)
12315 : {
12316 : /* Reconstruct the column definition list */
12317 6 : appendStringInfoString(buf, " AS ");
12318 6 : get_from_clause_coldeflist(rtfunc,
12319 : NULL,
12320 : context);
12321 : }
12322 48 : funcno++;
12323 : }
12324 18 : appendStringInfoChar(buf, ')');
12325 : }
12326 : /* prevent printing duplicate coldeflist below */
12327 30 : rtfunc1 = NULL;
12328 : }
12329 786 : if (rte->funcordinality)
12330 18 : appendStringInfoString(buf, " WITH ORDINALITY");
12331 786 : break;
12332 92 : case RTE_TABLEFUNC:
12333 92 : get_tablefunc(rte->tablefunc, context, true);
12334 92 : break;
12335 12 : case RTE_VALUES:
12336 : /* Values list RTE */
12337 12 : appendStringInfoChar(buf, '(');
12338 12 : get_values_def(rte->values_lists, context);
12339 12 : appendStringInfoChar(buf, ')');
12340 12 : break;
12341 184 : case RTE_CTE:
12342 184 : appendStringInfoString(buf, quote_identifier(rte->ctename));
12343 184 : break;
12344 0 : default:
12345 0 : elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
12346 : break;
12347 : }
12348 :
12349 : /* Print the relation alias, if needed */
12350 5836 : get_rte_alias(rte, varno, false, context);
12351 :
12352 : /* Print the column definitions or aliases, if needed */
12353 5836 : if (rtfunc1 && rtfunc1->funccolnames != NIL)
12354 : {
12355 : /* Reconstruct the columndef list, which is also the aliases */
12356 0 : get_from_clause_coldeflist(rtfunc1, colinfo, context);
12357 : }
12358 : else
12359 : {
12360 : /* Else print column aliases as needed */
12361 5836 : get_column_alias_list(colinfo, context);
12362 : }
12363 :
12364 : /* Tablesample clause must go after any alias */
12365 5836 : if (rte->rtekind == RTE_RELATION && rte->tablesample)
12366 32 : get_tablesample_def(rte->tablesample, context);
12367 : }
12368 1498 : else if (IsA(jtnode, JoinExpr))
12369 : {
12370 1498 : JoinExpr *j = (JoinExpr *) jtnode;
12371 1498 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
12372 : bool need_paren_on_right;
12373 :
12374 3434 : need_paren_on_right = PRETTY_PAREN(context) &&
12375 1498 : !IsA(j->rarg, RangeTblRef) &&
12376 0 : !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
12377 :
12378 1498 : if (!PRETTY_PAREN(context) || j->alias != NULL)
12379 1168 : appendStringInfoChar(buf, '(');
12380 :
12381 1498 : get_from_clause_item(j->larg, query, context);
12382 :
12383 1498 : switch (j->jointype)
12384 : {
12385 820 : case JOIN_INNER:
12386 820 : if (j->quals)
12387 778 : appendContextKeyword(context, " JOIN ",
12388 : -PRETTYINDENT_STD,
12389 : PRETTYINDENT_STD,
12390 : PRETTYINDENT_JOIN);
12391 : else
12392 42 : appendContextKeyword(context, " CROSS JOIN ",
12393 : -PRETTYINDENT_STD,
12394 : PRETTYINDENT_STD,
12395 : PRETTYINDENT_JOIN);
12396 820 : break;
12397 576 : case JOIN_LEFT:
12398 576 : appendContextKeyword(context, " LEFT JOIN ",
12399 : -PRETTYINDENT_STD,
12400 : PRETTYINDENT_STD,
12401 : PRETTYINDENT_JOIN);
12402 576 : break;
12403 102 : case JOIN_FULL:
12404 102 : appendContextKeyword(context, " FULL JOIN ",
12405 : -PRETTYINDENT_STD,
12406 : PRETTYINDENT_STD,
12407 : PRETTYINDENT_JOIN);
12408 102 : break;
12409 0 : case JOIN_RIGHT:
12410 0 : appendContextKeyword(context, " RIGHT JOIN ",
12411 : -PRETTYINDENT_STD,
12412 : PRETTYINDENT_STD,
12413 : PRETTYINDENT_JOIN);
12414 0 : break;
12415 0 : default:
12416 0 : elog(ERROR, "unrecognized join type: %d",
12417 : (int) j->jointype);
12418 : }
12419 :
12420 1498 : if (need_paren_on_right)
12421 0 : appendStringInfoChar(buf, '(');
12422 1498 : get_from_clause_item(j->rarg, query, context);
12423 1498 : if (need_paren_on_right)
12424 0 : appendStringInfoChar(buf, ')');
12425 :
12426 1498 : if (j->usingClause)
12427 : {
12428 : ListCell *lc;
12429 424 : bool first = true;
12430 :
12431 424 : appendStringInfoString(buf, " USING (");
12432 : /* Use the assigned names, not what's in usingClause */
12433 1004 : foreach(lc, colinfo->usingNames)
12434 : {
12435 580 : char *colname = (char *) lfirst(lc);
12436 :
12437 580 : if (first)
12438 424 : first = false;
12439 : else
12440 156 : appendStringInfoString(buf, ", ");
12441 580 : appendStringInfoString(buf, quote_identifier(colname));
12442 : }
12443 424 : appendStringInfoChar(buf, ')');
12444 :
12445 424 : if (j->join_using_alias)
12446 12 : appendStringInfo(buf, " AS %s",
12447 12 : quote_identifier(j->join_using_alias->aliasname));
12448 : }
12449 1074 : else if (j->quals)
12450 : {
12451 1026 : appendStringInfoString(buf, " ON ");
12452 1026 : if (!PRETTY_PAREN(context))
12453 1020 : appendStringInfoChar(buf, '(');
12454 1026 : get_rule_expr(j->quals, context, false);
12455 1026 : if (!PRETTY_PAREN(context))
12456 1020 : appendStringInfoChar(buf, ')');
12457 : }
12458 48 : else if (j->jointype != JOIN_INNER)
12459 : {
12460 : /* If we didn't say CROSS JOIN above, we must provide an ON */
12461 6 : appendStringInfoString(buf, " ON TRUE");
12462 : }
12463 :
12464 1498 : if (!PRETTY_PAREN(context) || j->alias != NULL)
12465 1168 : appendStringInfoChar(buf, ')');
12466 :
12467 : /* Yes, it's correct to put alias after the right paren ... */
12468 1498 : if (j->alias != NULL)
12469 : {
12470 : /*
12471 : * Note that it's correct to emit an alias clause if and only if
12472 : * there was one originally. Otherwise we'd be converting a named
12473 : * join to unnamed or vice versa, which creates semantic
12474 : * subtleties we don't want. However, we might print a different
12475 : * alias name than was there originally.
12476 : */
12477 108 : appendStringInfo(buf, " %s",
12478 108 : quote_identifier(get_rtable_name(j->rtindex,
12479 : context)));
12480 108 : get_column_alias_list(colinfo, context);
12481 : }
12482 : }
12483 : else
12484 0 : elog(ERROR, "unrecognized node type: %d",
12485 : (int) nodeTag(jtnode));
12486 7334 : }
12487 :
12488 : /*
12489 : * get_rte_alias - print the relation's alias, if needed
12490 : *
12491 : * If printed, the alias is preceded by a space, or by " AS " if use_as is true.
12492 : */
12493 : static void
12494 6394 : get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
12495 : deparse_context *context)
12496 : {
12497 6394 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
12498 6394 : char *refname = get_rtable_name(varno, context);
12499 6394 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
12500 6394 : bool printalias = false;
12501 :
12502 6394 : if (rte->alias != NULL)
12503 : {
12504 : /* Always print alias if user provided one */
12505 2998 : printalias = true;
12506 : }
12507 3396 : else if (colinfo->printaliases)
12508 : {
12509 : /* Always print alias if we need to print column aliases */
12510 306 : printalias = true;
12511 : }
12512 3090 : else if (rte->rtekind == RTE_RELATION)
12513 : {
12514 : /*
12515 : * No need to print alias if it's same as relation name (this would
12516 : * normally be the case, but not if set_rtable_names had to resolve a
12517 : * conflict).
12518 : */
12519 2824 : if (strcmp(refname, get_relation_name(rte->relid)) != 0)
12520 38 : printalias = true;
12521 : }
12522 266 : else if (rte->rtekind == RTE_FUNCTION)
12523 : {
12524 : /*
12525 : * For a function RTE, always print alias. This covers possible
12526 : * renaming of the function and/or instability of the FigureColname
12527 : * rules for things that aren't simple functions. Note we'd need to
12528 : * force it anyway for the columndef list case.
12529 : */
12530 0 : printalias = true;
12531 : }
12532 266 : else if (rte->rtekind == RTE_SUBQUERY ||
12533 242 : rte->rtekind == RTE_VALUES)
12534 : {
12535 : /*
12536 : * For a subquery, always print alias. This makes the output
12537 : * SQL-spec-compliant, even though we allow such aliases to be omitted
12538 : * on input.
12539 : */
12540 36 : printalias = true;
12541 : }
12542 230 : else if (rte->rtekind == RTE_CTE)
12543 : {
12544 : /*
12545 : * No need to print alias if it's same as CTE name (this would
12546 : * normally be the case, but not if set_rtable_names had to resolve a
12547 : * conflict).
12548 : */
12549 144 : if (strcmp(refname, rte->ctename) != 0)
12550 22 : printalias = true;
12551 : }
12552 :
12553 6394 : if (printalias)
12554 3400 : appendStringInfo(context->buf, "%s%s",
12555 : use_as ? " AS " : " ",
12556 : quote_identifier(refname));
12557 6394 : }
12558 :
12559 : /*
12560 : * get_column_alias_list - print column alias list for an RTE
12561 : *
12562 : * Caller must already have printed the relation's alias name.
12563 : */
12564 : static void
12565 5944 : get_column_alias_list(deparse_columns *colinfo, deparse_context *context)
12566 : {
12567 5944 : StringInfo buf = context->buf;
12568 : int i;
12569 5944 : bool first = true;
12570 :
12571 : /* Don't print aliases if not needed */
12572 5944 : if (!colinfo->printaliases)
12573 4798 : return;
12574 :
12575 9300 : for (i = 0; i < colinfo->num_new_cols; i++)
12576 : {
12577 8154 : char *colname = colinfo->new_colnames[i];
12578 :
12579 8154 : if (first)
12580 : {
12581 1146 : appendStringInfoChar(buf, '(');
12582 1146 : first = false;
12583 : }
12584 : else
12585 7008 : appendStringInfoString(buf, ", ");
12586 8154 : appendStringInfoString(buf, quote_identifier(colname));
12587 : }
12588 1146 : if (!first)
12589 1146 : appendStringInfoChar(buf, ')');
12590 : }
12591 :
12592 : /*
12593 : * get_from_clause_coldeflist - reproduce FROM clause coldeflist
12594 : *
12595 : * When printing a top-level coldeflist (which is syntactically also the
12596 : * relation's column alias list), use column names from colinfo. But when
12597 : * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
12598 : * original coldeflist's names, which are available in rtfunc->funccolnames.
12599 : * Pass NULL for colinfo to select the latter behavior.
12600 : *
12601 : * The coldeflist is appended immediately (no space) to buf. Caller is
12602 : * responsible for ensuring that an alias or AS is present before it.
12603 : */
12604 : static void
12605 6 : get_from_clause_coldeflist(RangeTblFunction *rtfunc,
12606 : deparse_columns *colinfo,
12607 : deparse_context *context)
12608 : {
12609 6 : StringInfo buf = context->buf;
12610 : ListCell *l1;
12611 : ListCell *l2;
12612 : ListCell *l3;
12613 : ListCell *l4;
12614 : int i;
12615 :
12616 6 : appendStringInfoChar(buf, '(');
12617 :
12618 6 : i = 0;
12619 24 : forfour(l1, rtfunc->funccoltypes,
12620 : l2, rtfunc->funccoltypmods,
12621 : l3, rtfunc->funccolcollations,
12622 : l4, rtfunc->funccolnames)
12623 : {
12624 18 : Oid atttypid = lfirst_oid(l1);
12625 18 : int32 atttypmod = lfirst_int(l2);
12626 18 : Oid attcollation = lfirst_oid(l3);
12627 : char *attname;
12628 :
12629 18 : if (colinfo)
12630 0 : attname = colinfo->colnames[i];
12631 : else
12632 18 : attname = strVal(lfirst(l4));
12633 :
12634 : Assert(attname); /* shouldn't be any dropped columns here */
12635 :
12636 18 : if (i > 0)
12637 12 : appendStringInfoString(buf, ", ");
12638 18 : appendStringInfo(buf, "%s %s",
12639 : quote_identifier(attname),
12640 : format_type_with_typemod(atttypid, atttypmod));
12641 24 : if (OidIsValid(attcollation) &&
12642 6 : attcollation != get_typcollation(atttypid))
12643 0 : appendStringInfo(buf, " COLLATE %s",
12644 : generate_collation_name(attcollation));
12645 :
12646 18 : i++;
12647 : }
12648 :
12649 6 : appendStringInfoChar(buf, ')');
12650 6 : }
12651 :
12652 : /*
12653 : * get_tablesample_def - print a TableSampleClause
12654 : */
12655 : static void
12656 32 : get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
12657 : {
12658 32 : StringInfo buf = context->buf;
12659 : Oid argtypes[1];
12660 : int nargs;
12661 : ListCell *l;
12662 :
12663 : /*
12664 : * We should qualify the handler's function name if it wouldn't be
12665 : * resolved by lookup in the current search path.
12666 : */
12667 32 : argtypes[0] = INTERNALOID;
12668 32 : appendStringInfo(buf, " TABLESAMPLE %s (",
12669 : generate_function_name(tablesample->tsmhandler, 1,
12670 : NIL, argtypes,
12671 : false, NULL, false));
12672 :
12673 32 : nargs = 0;
12674 64 : foreach(l, tablesample->args)
12675 : {
12676 32 : if (nargs++ > 0)
12677 0 : appendStringInfoString(buf, ", ");
12678 32 : get_rule_expr((Node *) lfirst(l), context, false);
12679 : }
12680 32 : appendStringInfoChar(buf, ')');
12681 :
12682 32 : if (tablesample->repeatable != NULL)
12683 : {
12684 16 : appendStringInfoString(buf, " REPEATABLE (");
12685 16 : get_rule_expr((Node *) tablesample->repeatable, context, false);
12686 16 : appendStringInfoChar(buf, ')');
12687 : }
12688 32 : }
12689 :
12690 : /*
12691 : * get_opclass_name - fetch name of an index operator class
12692 : *
12693 : * The opclass name is appended (after a space) to buf.
12694 : *
12695 : * Output is suppressed if the opclass is the default for the given
12696 : * actual_datatype. (If you don't want this behavior, just pass
12697 : * InvalidOid for actual_datatype.)
12698 : */
12699 : static void
12700 11852 : get_opclass_name(Oid opclass, Oid actual_datatype,
12701 : StringInfo buf)
12702 : {
12703 : HeapTuple ht_opc;
12704 : Form_pg_opclass opcrec;
12705 : char *opcname;
12706 : char *nspname;
12707 :
12708 11852 : ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
12709 11852 : if (!HeapTupleIsValid(ht_opc))
12710 0 : elog(ERROR, "cache lookup failed for opclass %u", opclass);
12711 11852 : opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
12712 :
12713 23664 : if (!OidIsValid(actual_datatype) ||
12714 11812 : GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
12715 : {
12716 : /* Okay, we need the opclass name. Do we need to qualify it? */
12717 558 : opcname = NameStr(opcrec->opcname);
12718 558 : if (OpclassIsVisible(opclass))
12719 558 : appendStringInfo(buf, " %s", quote_identifier(opcname));
12720 : else
12721 : {
12722 0 : nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
12723 0 : appendStringInfo(buf, " %s.%s",
12724 : quote_identifier(nspname),
12725 : quote_identifier(opcname));
12726 : }
12727 : }
12728 11852 : ReleaseSysCache(ht_opc);
12729 11852 : }
12730 :
12731 : /*
12732 : * generate_opclass_name
12733 : * Compute the name to display for an opclass specified by OID
12734 : *
12735 : * The result includes all necessary quoting and schema-prefixing.
12736 : */
12737 : char *
12738 6 : generate_opclass_name(Oid opclass)
12739 : {
12740 : StringInfoData buf;
12741 :
12742 6 : initStringInfo(&buf);
12743 6 : get_opclass_name(opclass, InvalidOid, &buf);
12744 :
12745 6 : return &buf.data[1]; /* get_opclass_name() prepends space */
12746 : }
12747 :
12748 : /*
12749 : * processIndirection - take care of array and subfield assignment
12750 : *
12751 : * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
12752 : * appear in the input, printing them as decoration for the base column
12753 : * name (which we assume the caller just printed). We might also need to
12754 : * strip CoerceToDomain nodes, but only ones that appear above assignment
12755 : * nodes.
12756 : *
12757 : * Returns the subexpression that's to be assigned.
12758 : */
12759 : static Node *
12760 1248 : processIndirection(Node *node, deparse_context *context)
12761 : {
12762 1248 : StringInfo buf = context->buf;
12763 1248 : CoerceToDomain *cdomain = NULL;
12764 :
12765 : for (;;)
12766 : {
12767 1554 : if (node == NULL)
12768 0 : break;
12769 1554 : if (IsA(node, FieldStore))
12770 : {
12771 108 : FieldStore *fstore = (FieldStore *) node;
12772 : Oid typrelid;
12773 : char *fieldname;
12774 :
12775 : /* lookup tuple type */
12776 108 : typrelid = get_typ_typrelid(fstore->resulttype);
12777 108 : if (!OidIsValid(typrelid))
12778 0 : elog(ERROR, "argument type %s of FieldStore is not a tuple type",
12779 : format_type_be(fstore->resulttype));
12780 :
12781 : /*
12782 : * Print the field name. There should only be one target field in
12783 : * stored rules. There could be more than that in executable
12784 : * target lists, but this function cannot be used for that case.
12785 : */
12786 : Assert(list_length(fstore->fieldnums) == 1);
12787 108 : fieldname = get_attname(typrelid,
12788 108 : linitial_int(fstore->fieldnums), false);
12789 108 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
12790 :
12791 : /*
12792 : * We ignore arg since it should be an uninteresting reference to
12793 : * the target column or subcolumn.
12794 : */
12795 108 : node = (Node *) linitial(fstore->newvals);
12796 : }
12797 1446 : else if (IsA(node, SubscriptingRef))
12798 : {
12799 138 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
12800 :
12801 138 : if (sbsref->refassgnexpr == NULL)
12802 0 : break;
12803 :
12804 138 : printSubscripts(sbsref, context);
12805 :
12806 : /*
12807 : * We ignore refexpr since it should be an uninteresting reference
12808 : * to the target column or subcolumn.
12809 : */
12810 138 : node = (Node *) sbsref->refassgnexpr;
12811 : }
12812 1308 : else if (IsA(node, CoerceToDomain))
12813 : {
12814 60 : cdomain = (CoerceToDomain *) node;
12815 : /* If it's an explicit domain coercion, we're done */
12816 60 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
12817 0 : break;
12818 : /* Tentatively descend past the CoerceToDomain */
12819 60 : node = (Node *) cdomain->arg;
12820 : }
12821 : else
12822 1248 : break;
12823 : }
12824 :
12825 : /*
12826 : * If we descended past a CoerceToDomain whose argument turned out not to
12827 : * be a FieldStore or array assignment, back up to the CoerceToDomain.
12828 : * (This is not enough to be fully correct if there are nested implicit
12829 : * CoerceToDomains, but such cases shouldn't ever occur.)
12830 : */
12831 1248 : if (cdomain && node == (Node *) cdomain->arg)
12832 0 : node = (Node *) cdomain;
12833 :
12834 1248 : return node;
12835 : }
12836 :
12837 : static void
12838 454 : printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
12839 : {
12840 454 : StringInfo buf = context->buf;
12841 : ListCell *lowlist_item;
12842 : ListCell *uplist_item;
12843 :
12844 454 : lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
12845 908 : foreach(uplist_item, sbsref->refupperindexpr)
12846 : {
12847 454 : appendStringInfoChar(buf, '[');
12848 454 : if (lowlist_item)
12849 : {
12850 : /* If subexpression is NULL, get_rule_expr prints nothing */
12851 0 : get_rule_expr((Node *) lfirst(lowlist_item), context, false);
12852 0 : appendStringInfoChar(buf, ':');
12853 0 : lowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item);
12854 : }
12855 : /* If subexpression is NULL, get_rule_expr prints nothing */
12856 454 : get_rule_expr((Node *) lfirst(uplist_item), context, false);
12857 454 : appendStringInfoChar(buf, ']');
12858 : }
12859 454 : }
12860 :
12861 : /*
12862 : * quote_identifier - Quote an identifier only if needed
12863 : *
12864 : * When quotes are needed, we palloc the required space; slightly
12865 : * space-wasteful but well worth it for notational simplicity.
12866 : */
12867 : const char *
12868 2435136 : quote_identifier(const char *ident)
12869 : {
12870 : /*
12871 : * Can avoid quoting if ident starts with a lowercase letter or underscore
12872 : * and contains only lowercase letters, digits, and underscores, *and* is
12873 : * not any SQL keyword. Otherwise, supply quotes.
12874 : */
12875 2435136 : int nquotes = 0;
12876 : bool safe;
12877 : const char *ptr;
12878 : char *result;
12879 : char *optr;
12880 :
12881 : /*
12882 : * would like to use <ctype.h> macros here, but they might yield unwanted
12883 : * locale-specific results...
12884 : */
12885 2435136 : safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
12886 :
12887 20947000 : for (ptr = ident; *ptr; ptr++)
12888 : {
12889 18511864 : char ch = *ptr;
12890 :
12891 18511864 : if ((ch >= 'a' && ch <= 'z') ||
12892 2175092 : (ch >= '0' && ch <= '9') ||
12893 : (ch == '_'))
12894 : {
12895 : /* okay */
12896 : }
12897 : else
12898 : {
12899 62172 : safe = false;
12900 62172 : if (ch == '"')
12901 12 : nquotes++;
12902 : }
12903 : }
12904 :
12905 2435136 : if (quote_all_identifiers)
12906 11534 : safe = false;
12907 :
12908 2435136 : if (safe)
12909 : {
12910 : /*
12911 : * Check for keyword. We quote keywords except for unreserved ones.
12912 : * (In some cases we could avoid quoting a col_name or type_func_name
12913 : * keyword, but it seems much harder than it's worth to tell that.)
12914 : *
12915 : * Note: ScanKeywordLookup() does case-insensitive comparison, but
12916 : * that's fine, since we already know we have all-lower-case.
12917 : */
12918 2398192 : int kwnum = ScanKeywordLookup(ident, &ScanKeywords);
12919 :
12920 2398192 : if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
12921 3150 : safe = false;
12922 : }
12923 :
12924 2435136 : if (safe)
12925 2395042 : return ident; /* no change needed */
12926 :
12927 40094 : result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
12928 :
12929 40094 : optr = result;
12930 40094 : *optr++ = '"';
12931 233360 : for (ptr = ident; *ptr; ptr++)
12932 : {
12933 193266 : char ch = *ptr;
12934 :
12935 193266 : if (ch == '"')
12936 12 : *optr++ = '"';
12937 193266 : *optr++ = ch;
12938 : }
12939 40094 : *optr++ = '"';
12940 40094 : *optr = '\0';
12941 :
12942 40094 : return result;
12943 : }
12944 :
12945 : /*
12946 : * quote_qualified_identifier - Quote a possibly-qualified identifier
12947 : *
12948 : * Return a name of the form qualifier.ident, or just ident if qualifier
12949 : * is NULL, quoting each component if necessary. The result is palloc'd.
12950 : */
12951 : char *
12952 1208192 : quote_qualified_identifier(const char *qualifier,
12953 : const char *ident)
12954 : {
12955 : StringInfoData buf;
12956 :
12957 1208192 : initStringInfo(&buf);
12958 1208192 : if (qualifier)
12959 440312 : appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
12960 1208192 : appendStringInfoString(&buf, quote_identifier(ident));
12961 1208192 : return buf.data;
12962 : }
12963 :
12964 : /*
12965 : * get_relation_name
12966 : * Get the unqualified name of a relation specified by OID
12967 : *
12968 : * This differs from the underlying get_rel_name() function in that it will
12969 : * throw error instead of silently returning NULL if the OID is bad.
12970 : */
12971 : static char *
12972 15282 : get_relation_name(Oid relid)
12973 : {
12974 15282 : char *relname = get_rel_name(relid);
12975 :
12976 15282 : if (!relname)
12977 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
12978 15282 : return relname;
12979 : }
12980 :
12981 : /*
12982 : * generate_relation_name
12983 : * Compute the name to display for a relation specified by OID
12984 : *
12985 : * The result includes all necessary quoting and schema-prefixing.
12986 : *
12987 : * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
12988 : * We will forcibly qualify the relation name if it equals any CTE name
12989 : * visible in the namespace list.
12990 : */
12991 : static char *
12992 7746 : generate_relation_name(Oid relid, List *namespaces)
12993 : {
12994 : HeapTuple tp;
12995 : Form_pg_class reltup;
12996 : bool need_qual;
12997 : ListCell *nslist;
12998 : char *relname;
12999 : char *nspname;
13000 : char *result;
13001 :
13002 7746 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13003 7746 : if (!HeapTupleIsValid(tp))
13004 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13005 7746 : reltup = (Form_pg_class) GETSTRUCT(tp);
13006 7746 : relname = NameStr(reltup->relname);
13007 :
13008 : /* Check for conflicting CTE name */
13009 7746 : need_qual = false;
13010 13396 : foreach(nslist, namespaces)
13011 : {
13012 5650 : deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
13013 : ListCell *ctlist;
13014 :
13015 5710 : foreach(ctlist, dpns->ctes)
13016 : {
13017 60 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);
13018 :
13019 60 : if (strcmp(cte->ctename, relname) == 0)
13020 : {
13021 0 : need_qual = true;
13022 0 : break;
13023 : }
13024 : }
13025 5650 : if (need_qual)
13026 0 : break;
13027 : }
13028 :
13029 : /* Otherwise, qualify the name if not visible in search path */
13030 7746 : if (!need_qual)
13031 7746 : need_qual = !RelationIsVisible(relid);
13032 :
13033 7746 : if (need_qual)
13034 2264 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
13035 : else
13036 5482 : nspname = NULL;
13037 :
13038 7746 : result = quote_qualified_identifier(nspname, relname);
13039 :
13040 7746 : ReleaseSysCache(tp);
13041 :
13042 7746 : return result;
13043 : }
13044 :
13045 : /*
13046 : * generate_qualified_relation_name
13047 : * Compute the name to display for a relation specified by OID
13048 : *
13049 : * As above, but unconditionally schema-qualify the name.
13050 : */
13051 : static char *
13052 7646 : generate_qualified_relation_name(Oid relid)
13053 : {
13054 : HeapTuple tp;
13055 : Form_pg_class reltup;
13056 : char *relname;
13057 : char *nspname;
13058 : char *result;
13059 :
13060 7646 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13061 7646 : if (!HeapTupleIsValid(tp))
13062 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13063 7646 : reltup = (Form_pg_class) GETSTRUCT(tp);
13064 7646 : relname = NameStr(reltup->relname);
13065 :
13066 7646 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
13067 7646 : if (!nspname)
13068 0 : elog(ERROR, "cache lookup failed for namespace %u",
13069 : reltup->relnamespace);
13070 :
13071 7646 : result = quote_qualified_identifier(nspname, relname);
13072 :
13073 7646 : ReleaseSysCache(tp);
13074 :
13075 7646 : return result;
13076 : }
13077 :
13078 : /*
13079 : * generate_function_name
13080 : * Compute the name to display for a function specified by OID,
13081 : * given that it is being called with the specified actual arg names and
13082 : * types. (Those matter because of ambiguous-function resolution rules.)
13083 : *
13084 : * If we're dealing with a potentially variadic function (in practice, this
13085 : * means a FuncExpr or Aggref, not some other way of calling a function), then
13086 : * has_variadic must specify whether variadic arguments have been merged,
13087 : * and *use_variadic_p will be set to indicate whether to print VARIADIC in
13088 : * the output. For non-FuncExpr cases, has_variadic should be false and
13089 : * use_variadic_p can be NULL.
13090 : *
13091 : * inGroupBy must be true if we're deparsing a GROUP BY clause.
13092 : *
13093 : * The result includes all necessary quoting and schema-prefixing.
13094 : */
13095 : static char *
13096 11894 : generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
13097 : bool has_variadic, bool *use_variadic_p,
13098 : bool inGroupBy)
13099 : {
13100 : char *result;
13101 : HeapTuple proctup;
13102 : Form_pg_proc procform;
13103 : char *proname;
13104 : bool use_variadic;
13105 : char *nspname;
13106 : FuncDetailCode p_result;
13107 : Oid p_funcid;
13108 : Oid p_rettype;
13109 : bool p_retset;
13110 : int p_nvargs;
13111 : Oid p_vatype;
13112 : Oid *p_true_typeids;
13113 11894 : bool force_qualify = false;
13114 :
13115 11894 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
13116 11894 : if (!HeapTupleIsValid(proctup))
13117 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
13118 11894 : procform = (Form_pg_proc) GETSTRUCT(proctup);
13119 11894 : proname = NameStr(procform->proname);
13120 :
13121 : /*
13122 : * Due to parser hacks to avoid needing to reserve CUBE, we need to force
13123 : * qualification of some function names within GROUP BY.
13124 : */
13125 11894 : if (inGroupBy)
13126 : {
13127 0 : if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
13128 0 : force_qualify = true;
13129 : }
13130 :
13131 : /*
13132 : * Determine whether VARIADIC should be printed. We must do this first
13133 : * since it affects the lookup rules in func_get_detail().
13134 : *
13135 : * We always print VARIADIC if the function has a merged variadic-array
13136 : * argument. Note that this is always the case for functions taking a
13137 : * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
13138 : * and printed the array elements as separate arguments, the call could
13139 : * match a newer non-VARIADIC function.
13140 : */
13141 11894 : if (use_variadic_p)
13142 : {
13143 : /* Parser should not have set funcvariadic unless fn is variadic */
13144 : Assert(!has_variadic || OidIsValid(procform->provariadic));
13145 10294 : use_variadic = has_variadic;
13146 10294 : *use_variadic_p = use_variadic;
13147 : }
13148 : else
13149 : {
13150 : Assert(!has_variadic);
13151 1600 : use_variadic = false;
13152 : }
13153 :
13154 : /*
13155 : * The idea here is to schema-qualify only if the parser would fail to
13156 : * resolve the correct function given the unqualified func name with the
13157 : * specified argtypes and VARIADIC flag. But if we already decided to
13158 : * force qualification, then we can skip the lookup and pretend we didn't
13159 : * find it.
13160 : */
13161 11894 : if (!force_qualify)
13162 11894 : p_result = func_get_detail(list_make1(makeString(proname)),
13163 : NIL, argnames, nargs, argtypes,
13164 11894 : !use_variadic, true, false,
13165 : &p_funcid, &p_rettype,
13166 : &p_retset, &p_nvargs, &p_vatype,
13167 11894 : &p_true_typeids, NULL);
13168 : else
13169 : {
13170 0 : p_result = FUNCDETAIL_NOTFOUND;
13171 0 : p_funcid = InvalidOid;
13172 : }
13173 :
13174 11894 : if ((p_result == FUNCDETAIL_NORMAL ||
13175 1180 : p_result == FUNCDETAIL_AGGREGATE ||
13176 10822 : p_result == FUNCDETAIL_WINDOWFUNC) &&
13177 10822 : p_funcid == funcid)
13178 10822 : nspname = NULL;
13179 : else
13180 1072 : nspname = get_namespace_name_or_temp(procform->pronamespace);
13181 :
13182 11894 : result = quote_qualified_identifier(nspname, proname);
13183 :
13184 11894 : ReleaseSysCache(proctup);
13185 :
13186 11894 : return result;
13187 : }
13188 :
13189 : /*
13190 : * generate_operator_name
13191 : * Compute the name to display for an operator specified by OID,
13192 : * given that it is being called with the specified actual arg types.
13193 : * (Arg types matter because of ambiguous-operator resolution rules.
13194 : * Pass InvalidOid for unused arg of a unary operator.)
13195 : *
13196 : * The result includes all necessary quoting and schema-prefixing,
13197 : * plus the OPERATOR() decoration needed to use a qualified operator name
13198 : * in an expression.
13199 : */
13200 : static char *
13201 58382 : generate_operator_name(Oid operid, Oid arg1, Oid arg2)
13202 : {
13203 : StringInfoData buf;
13204 : HeapTuple opertup;
13205 : Form_pg_operator operform;
13206 : char *oprname;
13207 : char *nspname;
13208 : Operator p_result;
13209 :
13210 58382 : initStringInfo(&buf);
13211 :
13212 58382 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
13213 58382 : if (!HeapTupleIsValid(opertup))
13214 0 : elog(ERROR, "cache lookup failed for operator %u", operid);
13215 58382 : operform = (Form_pg_operator) GETSTRUCT(opertup);
13216 58382 : oprname = NameStr(operform->oprname);
13217 :
13218 : /*
13219 : * The idea here is to schema-qualify only if the parser would fail to
13220 : * resolve the correct operator given the unqualified op name with the
13221 : * specified argtypes.
13222 : */
13223 58382 : switch (operform->oprkind)
13224 : {
13225 58352 : case 'b':
13226 58352 : p_result = oper(NULL, list_make1(makeString(oprname)), arg1, arg2,
13227 : true, -1);
13228 58352 : break;
13229 30 : case 'l':
13230 30 : p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2,
13231 : true, -1);
13232 30 : break;
13233 0 : default:
13234 0 : elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
13235 : p_result = NULL; /* keep compiler quiet */
13236 : break;
13237 : }
13238 :
13239 58382 : if (p_result != NULL && oprid(p_result) == operid)
13240 58372 : nspname = NULL;
13241 : else
13242 : {
13243 10 : nspname = get_namespace_name_or_temp(operform->oprnamespace);
13244 10 : appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
13245 : }
13246 :
13247 58382 : appendStringInfoString(&buf, oprname);
13248 :
13249 58382 : if (nspname)
13250 10 : appendStringInfoChar(&buf, ')');
13251 :
13252 58382 : if (p_result != NULL)
13253 58372 : ReleaseSysCache(p_result);
13254 :
13255 58382 : ReleaseSysCache(opertup);
13256 :
13257 58382 : return buf.data;
13258 : }
13259 :
13260 : /*
13261 : * generate_operator_clause --- generate a binary-operator WHERE clause
13262 : *
13263 : * This is used for internally-generated-and-executed SQL queries, where
13264 : * precision is essential and readability is secondary. The basic
13265 : * requirement is to append "leftop op rightop" to buf, where leftop and
13266 : * rightop are given as strings and are assumed to yield types leftoptype
13267 : * and rightoptype; the operator is identified by OID. The complexity
13268 : * comes from needing to be sure that the parser will select the desired
13269 : * operator when the query is parsed. We always name the operator using
13270 : * OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
13271 : * We have to emit casts too, if either input isn't already the input type
13272 : * of the operator; else we are at the mercy of the parser's heuristics for
13273 : * ambiguous-operator resolution. The caller must ensure that leftop and
13274 : * rightop are suitable arguments for a cast operation; it's best to insert
13275 : * parentheses if they aren't just variables or parameters.
13276 : */
13277 : void
13278 6258 : generate_operator_clause(StringInfo buf,
13279 : const char *leftop, Oid leftoptype,
13280 : Oid opoid,
13281 : const char *rightop, Oid rightoptype)
13282 : {
13283 : HeapTuple opertup;
13284 : Form_pg_operator operform;
13285 : char *oprname;
13286 : char *nspname;
13287 :
13288 6258 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
13289 6258 : if (!HeapTupleIsValid(opertup))
13290 0 : elog(ERROR, "cache lookup failed for operator %u", opoid);
13291 6258 : operform = (Form_pg_operator) GETSTRUCT(opertup);
13292 : Assert(operform->oprkind == 'b');
13293 6258 : oprname = NameStr(operform->oprname);
13294 :
13295 6258 : nspname = get_namespace_name(operform->oprnamespace);
13296 :
13297 6258 : appendStringInfoString(buf, leftop);
13298 6258 : if (leftoptype != operform->oprleft)
13299 1138 : add_cast_to(buf, operform->oprleft);
13300 6258 : appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
13301 6258 : appendStringInfoString(buf, oprname);
13302 6258 : appendStringInfo(buf, ") %s", rightop);
13303 6258 : if (rightoptype != operform->oprright)
13304 904 : add_cast_to(buf, operform->oprright);
13305 :
13306 6258 : ReleaseSysCache(opertup);
13307 6258 : }
13308 :
13309 : /*
13310 : * Add a cast specification to buf. We spell out the type name the hard way,
13311 : * intentionally not using format_type_be(). This is to avoid corner cases
13312 : * for CHARACTER, BIT, and perhaps other types, where specifying the type
13313 : * using SQL-standard syntax results in undesirable data truncation. By
13314 : * doing it this way we can be certain that the cast will have default (-1)
13315 : * target typmod.
13316 : */
13317 : static void
13318 2042 : add_cast_to(StringInfo buf, Oid typid)
13319 : {
13320 : HeapTuple typetup;
13321 : Form_pg_type typform;
13322 : char *typname;
13323 : char *nspname;
13324 :
13325 2042 : typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
13326 2042 : if (!HeapTupleIsValid(typetup))
13327 0 : elog(ERROR, "cache lookup failed for type %u", typid);
13328 2042 : typform = (Form_pg_type) GETSTRUCT(typetup);
13329 :
13330 2042 : typname = NameStr(typform->typname);
13331 2042 : nspname = get_namespace_name_or_temp(typform->typnamespace);
13332 :
13333 2042 : appendStringInfo(buf, "::%s.%s",
13334 : quote_identifier(nspname), quote_identifier(typname));
13335 :
13336 2042 : ReleaseSysCache(typetup);
13337 2042 : }
13338 :
13339 : /*
13340 : * generate_qualified_type_name
13341 : * Compute the name to display for a type specified by OID
13342 : *
13343 : * This is different from format_type_be() in that we unconditionally
13344 : * schema-qualify the name. That also means no special syntax for
13345 : * SQL-standard type names ... although in current usage, this should
13346 : * only get used for domains, so such cases wouldn't occur anyway.
13347 : */
13348 : static char *
13349 14 : generate_qualified_type_name(Oid typid)
13350 : {
13351 : HeapTuple tp;
13352 : Form_pg_type typtup;
13353 : char *typname;
13354 : char *nspname;
13355 : char *result;
13356 :
13357 14 : tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
13358 14 : if (!HeapTupleIsValid(tp))
13359 0 : elog(ERROR, "cache lookup failed for type %u", typid);
13360 14 : typtup = (Form_pg_type) GETSTRUCT(tp);
13361 14 : typname = NameStr(typtup->typname);
13362 :
13363 14 : nspname = get_namespace_name_or_temp(typtup->typnamespace);
13364 14 : if (!nspname)
13365 0 : elog(ERROR, "cache lookup failed for namespace %u",
13366 : typtup->typnamespace);
13367 :
13368 14 : result = quote_qualified_identifier(nspname, typname);
13369 :
13370 14 : ReleaseSysCache(tp);
13371 :
13372 14 : return result;
13373 : }
13374 :
13375 : /*
13376 : * generate_collation_name
13377 : * Compute the name to display for a collation specified by OID
13378 : *
13379 : * The result includes all necessary quoting and schema-prefixing.
13380 : */
13381 : char *
13382 290 : generate_collation_name(Oid collid)
13383 : {
13384 : HeapTuple tp;
13385 : Form_pg_collation colltup;
13386 : char *collname;
13387 : char *nspname;
13388 : char *result;
13389 :
13390 290 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
13391 290 : if (!HeapTupleIsValid(tp))
13392 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
13393 290 : colltup = (Form_pg_collation) GETSTRUCT(tp);
13394 290 : collname = NameStr(colltup->collname);
13395 :
13396 290 : if (!CollationIsVisible(collid))
13397 0 : nspname = get_namespace_name_or_temp(colltup->collnamespace);
13398 : else
13399 290 : nspname = NULL;
13400 :
13401 290 : result = quote_qualified_identifier(nspname, collname);
13402 :
13403 290 : ReleaseSysCache(tp);
13404 :
13405 290 : return result;
13406 : }
13407 :
13408 : /*
13409 : * Given a C string, produce a TEXT datum.
13410 : *
13411 : * We assume that the input was palloc'd and may be freed.
13412 : */
13413 : static text *
13414 40840 : string_to_text(char *str)
13415 : {
13416 : text *result;
13417 :
13418 40840 : result = cstring_to_text(str);
13419 40840 : pfree(str);
13420 40840 : return result;
13421 : }
13422 :
13423 : /*
13424 : * Generate a C string representing a relation options from text[] datum.
13425 : */
13426 : static void
13427 244 : get_reloptions(StringInfo buf, Datum reloptions)
13428 : {
13429 : Datum *options;
13430 : int noptions;
13431 : int i;
13432 :
13433 244 : deconstruct_array_builtin(DatumGetArrayTypeP(reloptions), TEXTOID,
13434 : &options, NULL, &noptions);
13435 :
13436 508 : for (i = 0; i < noptions; i++)
13437 : {
13438 264 : char *option = TextDatumGetCString(options[i]);
13439 : char *name;
13440 : char *separator;
13441 : char *value;
13442 :
13443 : /*
13444 : * Each array element should have the form name=value. If the "=" is
13445 : * missing for some reason, treat it like an empty value.
13446 : */
13447 264 : name = option;
13448 264 : separator = strchr(option, '=');
13449 264 : if (separator)
13450 : {
13451 264 : *separator = '\0';
13452 264 : value = separator + 1;
13453 : }
13454 : else
13455 0 : value = "";
13456 :
13457 264 : if (i > 0)
13458 20 : appendStringInfoString(buf, ", ");
13459 264 : appendStringInfo(buf, "%s=", quote_identifier(name));
13460 :
13461 : /*
13462 : * In general we need to quote the value; but to avoid unnecessary
13463 : * clutter, do not quote if it is an identifier that would not need
13464 : * quoting. (We could also allow numbers, but that is a bit trickier
13465 : * than it looks --- for example, are leading zeroes significant? We
13466 : * don't want to assume very much here about what custom reloptions
13467 : * might mean.)
13468 : */
13469 264 : if (quote_identifier(value) == value)
13470 8 : appendStringInfoString(buf, value);
13471 : else
13472 256 : simple_quote_literal(buf, value);
13473 :
13474 264 : pfree(option);
13475 : }
13476 244 : }
13477 :
13478 : /*
13479 : * Generate a C string representing a relation's reloptions, or NULL if none.
13480 : */
13481 : static char *
13482 7210 : flatten_reloptions(Oid relid)
13483 : {
13484 7210 : char *result = NULL;
13485 : HeapTuple tuple;
13486 : Datum reloptions;
13487 : bool isnull;
13488 :
13489 7210 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13490 7210 : if (!HeapTupleIsValid(tuple))
13491 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13492 :
13493 7210 : reloptions = SysCacheGetAttr(RELOID, tuple,
13494 : Anum_pg_class_reloptions, &isnull);
13495 7210 : if (!isnull)
13496 : {
13497 : StringInfoData buf;
13498 :
13499 210 : initStringInfo(&buf);
13500 210 : get_reloptions(&buf, reloptions);
13501 :
13502 210 : result = buf.data;
13503 : }
13504 :
13505 7210 : ReleaseSysCache(tuple);
13506 :
13507 7210 : return result;
13508 : }
13509 :
13510 : /*
13511 : * get_range_partbound_string
13512 : * A C string representation of one range partition bound
13513 : */
13514 : char *
13515 4284 : get_range_partbound_string(List *bound_datums)
13516 : {
13517 : deparse_context context;
13518 4284 : StringInfo buf = makeStringInfo();
13519 : ListCell *cell;
13520 : char *sep;
13521 :
13522 4284 : memset(&context, 0, sizeof(deparse_context));
13523 4284 : context.buf = buf;
13524 :
13525 4284 : appendStringInfoChar(buf, '(');
13526 4284 : sep = "";
13527 9360 : foreach(cell, bound_datums)
13528 : {
13529 5076 : PartitionRangeDatum *datum =
13530 : lfirst_node(PartitionRangeDatum, cell);
13531 :
13532 5076 : appendStringInfoString(buf, sep);
13533 5076 : if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
13534 222 : appendStringInfoString(buf, "MINVALUE");
13535 4854 : else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
13536 120 : appendStringInfoString(buf, "MAXVALUE");
13537 : else
13538 : {
13539 4734 : Const *val = castNode(Const, datum->value);
13540 :
13541 4734 : get_const_expr(val, &context, -1);
13542 : }
13543 5076 : sep = ", ";
13544 : }
13545 4284 : appendStringInfoChar(buf, ')');
13546 :
13547 4284 : return buf->data;
13548 : }
|