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-2023, 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/sysattr.h"
26 : #include "access/table.h"
27 : #include "catalog/pg_aggregate.h"
28 : #include "catalog/pg_am.h"
29 : #include "catalog/pg_authid.h"
30 : #include "catalog/pg_collation.h"
31 : #include "catalog/pg_constraint.h"
32 : #include "catalog/pg_depend.h"
33 : #include "catalog/pg_language.h"
34 : #include "catalog/pg_opclass.h"
35 : #include "catalog/pg_operator.h"
36 : #include "catalog/pg_partitioned_table.h"
37 : #include "catalog/pg_proc.h"
38 : #include "catalog/pg_statistic_ext.h"
39 : #include "catalog/pg_trigger.h"
40 : #include "catalog/pg_type.h"
41 : #include "commands/defrem.h"
42 : #include "commands/tablespace.h"
43 : #include "common/keywords.h"
44 : #include "executor/spi.h"
45 : #include "funcapi.h"
46 : #include "mb/pg_wchar.h"
47 : #include "miscadmin.h"
48 : #include "nodes/makefuncs.h"
49 : #include "nodes/nodeFuncs.h"
50 : #include "nodes/pathnodes.h"
51 : #include "optimizer/optimizer.h"
52 : #include "parser/parse_agg.h"
53 : #include "parser/parse_func.h"
54 : #include "parser/parse_node.h"
55 : #include "parser/parse_oper.h"
56 : #include "parser/parse_relation.h"
57 : #include "parser/parser.h"
58 : #include "parser/parsetree.h"
59 : #include "rewrite/rewriteHandler.h"
60 : #include "rewrite/rewriteManip.h"
61 : #include "rewrite/rewriteSupport.h"
62 : #include "utils/array.h"
63 : #include "utils/builtins.h"
64 : #include "utils/fmgroids.h"
65 : #include "utils/guc.h"
66 : #include "utils/hsearch.h"
67 : #include "utils/lsyscache.h"
68 : #include "utils/partcache.h"
69 : #include "utils/rel.h"
70 : #include "utils/ruleutils.h"
71 : #include "utils/snapmgr.h"
72 : #include "utils/syscache.h"
73 : #include "utils/typcache.h"
74 : #include "utils/varlena.h"
75 : #include "utils/xml.h"
76 :
77 : /* ----------
78 : * Pretty formatting constants
79 : * ----------
80 : */
81 :
82 : /* Indent counts */
83 : #define PRETTYINDENT_STD 8
84 : #define PRETTYINDENT_JOIN 4
85 : #define PRETTYINDENT_VAR 4
86 :
87 : #define PRETTYINDENT_LIMIT 40 /* wrap limit */
88 :
89 : /* Pretty flags */
90 : #define PRETTYFLAG_PAREN 0x0001
91 : #define PRETTYFLAG_INDENT 0x0002
92 : #define PRETTYFLAG_SCHEMA 0x0004
93 :
94 : /* Standard conversion of a "bool pretty" option to detailed flags */
95 : #define GET_PRETTY_FLAGS(pretty) \
96 : ((pretty) ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) \
97 : : PRETTYFLAG_INDENT)
98 :
99 : /* Default line length for pretty-print wrapping: 0 means wrap always */
100 : #define WRAP_COLUMN_DEFAULT 0
101 :
102 : /* macros to test if pretty action needed */
103 : #define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
104 : #define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
105 : #define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
106 :
107 :
108 : /* ----------
109 : * Local data types
110 : * ----------
111 : */
112 :
113 : /* Context info needed for invoking a recursive querytree display routine */
114 : typedef struct
115 : {
116 : StringInfo buf; /* output buffer to append to */
117 : List *namespaces; /* List of deparse_namespace nodes */
118 : List *windowClause; /* Current query level's WINDOW clause */
119 : List *windowTList; /* targetlist for resolving WINDOW clause */
120 : int prettyFlags; /* enabling of pretty-print functions */
121 : int wrapColumn; /* max line length, or -1 for no limit */
122 : int indentLevel; /* current indent level for pretty-print */
123 : bool varprefix; /* true to print prefixes on Vars */
124 : ParseExprKind special_exprkind; /* set only for exprkinds needing special
125 : * handling */
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 : typedef struct
229 : {
230 : /*
231 : * colnames is an array containing column aliases to use for columns that
232 : * existed when the query was parsed. Dropped columns have NULL entries.
233 : * This array can be directly indexed by varattno to get a Var's name.
234 : *
235 : * Non-NULL entries are guaranteed unique within the RTE, *except* when
236 : * this is for an unnamed JOIN RTE. In that case we merely copy up names
237 : * from the two input RTEs.
238 : *
239 : * During the recursive descent in set_using_names(), forcible assignment
240 : * of a child RTE's column name is represented by pre-setting that element
241 : * of the child's colnames array. So at that stage, NULL entries in this
242 : * array just mean that no name has been preassigned, not necessarily that
243 : * the column is dropped.
244 : */
245 : int num_cols; /* length of colnames[] array */
246 : char **colnames; /* array of C strings and NULLs */
247 :
248 : /*
249 : * new_colnames is an array containing column aliases to use for columns
250 : * that would exist if the query was re-parsed against the current
251 : * definitions of its base tables. This is what to print as the column
252 : * alias list for the RTE. This array does not include dropped columns,
253 : * but it will include columns added since original parsing. Indexes in
254 : * it therefore have little to do with current varattno values. As above,
255 : * entries are unique unless this is for an unnamed JOIN RTE. (In such an
256 : * RTE, we never actually print this array, but we must compute it anyway
257 : * for possible use in computing column names of upper joins.) The
258 : * parallel array is_new_col marks which of these columns are new since
259 : * original parsing. Entries with is_new_col false must match the
260 : * non-NULL colnames entries one-for-one.
261 : */
262 : int num_new_cols; /* length of new_colnames[] array */
263 : char **new_colnames; /* array of C strings */
264 : bool *is_new_col; /* array of bool flags */
265 :
266 : /* This flag tells whether we should actually print a column alias list */
267 : bool printaliases;
268 :
269 : /* This list has all names used as USING names in joins above this RTE */
270 : List *parentUsing; /* names assigned to parent merged columns */
271 :
272 : /*
273 : * If this struct is for a JOIN RTE, we fill these fields during the
274 : * set_using_names() pass to describe its relationship to its child RTEs.
275 : *
276 : * leftattnos and rightattnos are arrays with one entry per existing
277 : * output column of the join (hence, indexable by join varattno). For a
278 : * simple reference to a column of the left child, leftattnos[i] is the
279 : * child RTE's attno and rightattnos[i] is zero; and conversely for a
280 : * column of the right child. But for merged columns produced by JOIN
281 : * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.
282 : * Note that a simple reference might be to a child RTE column that's been
283 : * dropped; but that's OK since the column could not be used in the query.
284 : *
285 : * If it's a JOIN USING, usingNames holds the alias names selected for the
286 : * merged columns (these might be different from the original USING list,
287 : * if we had to modify names to achieve uniqueness).
288 : */
289 : int leftrti; /* rangetable index of left child */
290 : int rightrti; /* rangetable index of right child */
291 : int *leftattnos; /* left-child varattnos of join cols, or 0 */
292 : int *rightattnos; /* right-child varattnos of join cols, or 0 */
293 : List *usingNames; /* names assigned to merged columns */
294 : } deparse_columns;
295 :
296 : /* This macro is analogous to rt_fetch(), but for deparse_columns structs */
297 : #define deparse_columns_fetch(rangetable_index, dpns) \
298 : ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
299 :
300 : /*
301 : * Entry in set_rtable_names' hash table
302 : */
303 : typedef struct
304 : {
305 : char name[NAMEDATALEN]; /* Hash key --- must be first */
306 : int counter; /* Largest addition used so far for name */
307 : } NameHashEntry;
308 :
309 : /* Callback signature for resolve_special_varno() */
310 : typedef void (*rsv_callback) (Node *node, deparse_context *context,
311 : void *callback_arg);
312 :
313 :
314 : /* ----------
315 : * Global data
316 : * ----------
317 : */
318 : static SPIPlanPtr plan_getrulebyoid = NULL;
319 : static const char *const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
320 : static SPIPlanPtr plan_getviewrule = NULL;
321 : static const char *const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
322 :
323 : /* GUC parameters */
324 : bool quote_all_identifiers = false;
325 :
326 :
327 : /* ----------
328 : * Local functions
329 : *
330 : * Most of these functions used to use fixed-size buffers to build their
331 : * results. Now, they take an (already initialized) StringInfo object
332 : * as a parameter, and append their text output to its contents.
333 : * ----------
334 : */
335 : static char *deparse_expression_pretty(Node *expr, List *dpcontext,
336 : bool forceprefix, bool showimplicit,
337 : int prettyFlags, int startIndent);
338 : static char *pg_get_viewdef_worker(Oid viewoid,
339 : int prettyFlags, int wrapColumn);
340 : static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
341 : static int decompile_column_index_array(Datum column_index_array, Oid relId,
342 : StringInfo buf);
343 : static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
344 : static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
345 : const Oid *excludeOps,
346 : bool attrsOnly, bool keysOnly,
347 : bool showTblSpc, bool inherits,
348 : int prettyFlags, bool missing_ok);
349 : static char *pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
350 : bool missing_ok);
351 : static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
352 : bool attrsOnly, bool missing_ok);
353 : static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
354 : int prettyFlags, bool missing_ok);
355 : static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname,
356 : int prettyFlags);
357 : static int print_function_arguments(StringInfo buf, HeapTuple proctup,
358 : bool print_table_args, bool print_defaults);
359 : static void print_function_rettype(StringInfo buf, HeapTuple proctup);
360 : static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
361 : static void print_function_sqlbody(StringInfo buf, HeapTuple proctup);
362 : static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
363 : Bitmapset *rels_used);
364 : static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
365 : List *parent_namespaces);
366 : static void set_simple_column_names(deparse_namespace *dpns);
367 : static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
368 : static void set_using_names(deparse_namespace *dpns, Node *jtnode,
369 : List *parentUsing);
370 : static void set_relation_column_names(deparse_namespace *dpns,
371 : RangeTblEntry *rte,
372 : deparse_columns *colinfo);
373 : static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
374 : deparse_columns *colinfo);
375 : static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
376 : deparse_columns *colinfo);
377 : static char *make_colname_unique(char *colname, deparse_namespace *dpns,
378 : deparse_columns *colinfo);
379 : static void expand_colnames_array_to(deparse_columns *colinfo, int n);
380 : static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
381 : deparse_columns *colinfo);
382 : static char *get_rtable_name(int rtindex, deparse_context *context);
383 : static void set_deparse_plan(deparse_namespace *dpns, Plan *plan);
384 : static Plan *find_recursive_union(deparse_namespace *dpns,
385 : WorkTableScan *wtscan);
386 : static void push_child_plan(deparse_namespace *dpns, Plan *plan,
387 : deparse_namespace *save_dpns);
388 : static void pop_child_plan(deparse_namespace *dpns,
389 : deparse_namespace *save_dpns);
390 : static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
391 : deparse_namespace *save_dpns);
392 : static void pop_ancestor_plan(deparse_namespace *dpns,
393 : deparse_namespace *save_dpns);
394 : static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
395 : int prettyFlags);
396 : static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
397 : int prettyFlags, int wrapColumn);
398 : static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
399 : TupleDesc resultDesc, bool colNamesVisible,
400 : int prettyFlags, int wrapColumn, int startIndent);
401 : static void get_values_def(List *values_lists, deparse_context *context);
402 : static void get_with_clause(Query *query, deparse_context *context);
403 : static void get_select_query_def(Query *query, deparse_context *context,
404 : TupleDesc resultDesc, bool colNamesVisible);
405 : static void get_insert_query_def(Query *query, deparse_context *context,
406 : bool colNamesVisible);
407 : static void get_update_query_def(Query *query, deparse_context *context,
408 : bool colNamesVisible);
409 : static void get_update_query_targetlist_def(Query *query, List *targetList,
410 : deparse_context *context,
411 : RangeTblEntry *rte);
412 : static void get_delete_query_def(Query *query, deparse_context *context,
413 : bool colNamesVisible);
414 : static void get_merge_query_def(Query *query, deparse_context *context,
415 : bool colNamesVisible);
416 : static void get_utility_query_def(Query *query, deparse_context *context);
417 : static void get_basic_select_query(Query *query, deparse_context *context,
418 : TupleDesc resultDesc, bool colNamesVisible);
419 : static void get_target_list(List *targetList, deparse_context *context,
420 : TupleDesc resultDesc, bool colNamesVisible);
421 : static void get_setop_query(Node *setOp, Query *query,
422 : deparse_context *context,
423 : TupleDesc resultDesc, bool colNamesVisible);
424 : static Node *get_rule_sortgroupclause(Index ref, List *tlist,
425 : bool force_colno,
426 : deparse_context *context);
427 : static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
428 : bool omit_parens, deparse_context *context);
429 : static void get_rule_orderby(List *orderList, List *targetList,
430 : bool force_colno, deparse_context *context);
431 : static void get_rule_windowclause(Query *query, deparse_context *context);
432 : static void get_rule_windowspec(WindowClause *wc, List *targetList,
433 : deparse_context *context);
434 : static char *get_variable(Var *var, int levelsup, bool istoplevel,
435 : deparse_context *context);
436 : static void get_special_variable(Node *node, deparse_context *context,
437 : void *callback_arg);
438 : static void resolve_special_varno(Node *node, deparse_context *context,
439 : rsv_callback callback, void *callback_arg);
440 : static Node *find_param_referent(Param *param, deparse_context *context,
441 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
442 : static void get_parameter(Param *param, deparse_context *context);
443 : static const char *get_simple_binary_op_name(OpExpr *expr);
444 : static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
445 : static void appendContextKeyword(deparse_context *context, const char *str,
446 : int indentBefore, int indentAfter, int indentPlus);
447 : static void removeStringInfoSpaces(StringInfo str);
448 : static void get_rule_expr(Node *node, deparse_context *context,
449 : bool showimplicit);
450 : static void get_rule_expr_toplevel(Node *node, deparse_context *context,
451 : bool showimplicit);
452 : static void get_rule_list_toplevel(List *lst, deparse_context *context,
453 : bool showimplicit);
454 : static void get_rule_expr_funccall(Node *node, deparse_context *context,
455 : bool showimplicit);
456 : static bool looks_like_function(Node *node);
457 : static void get_oper_expr(OpExpr *expr, deparse_context *context);
458 : static void get_func_expr(FuncExpr *expr, deparse_context *context,
459 : bool showimplicit);
460 : static void get_agg_expr(Aggref *aggref, deparse_context *context,
461 : Aggref *original_aggref);
462 : static void get_agg_expr_helper(Aggref *aggref, deparse_context *context,
463 : Aggref *original_aggref, const char *funcname,
464 : const char *options, bool is_json_objectagg);
465 : static void get_agg_combine_expr(Node *node, deparse_context *context,
466 : void *callback_arg);
467 : static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
468 : static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
469 : const char *funcname, const char *options,
470 : bool is_json_objectagg);
471 : static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
472 : static void get_coercion_expr(Node *arg, deparse_context *context,
473 : Oid resulttype, int32 resulttypmod,
474 : Node *parentNode);
475 : static void get_const_expr(Const *constval, deparse_context *context,
476 : int showtype);
477 : static void get_const_collation(Const *constval, deparse_context *context);
478 : static void get_json_format(JsonFormat *format, StringInfo buf);
479 : static void get_json_constructor(JsonConstructorExpr *ctor,
480 : deparse_context *context, bool showimplicit);
481 : static void get_json_constructor_options(JsonConstructorExpr *ctor,
482 : StringInfo buf);
483 : static void get_json_agg_constructor(JsonConstructorExpr *ctor,
484 : deparse_context *context,
485 : const char *funcname,
486 : bool is_json_objectagg);
487 : static void simple_quote_literal(StringInfo buf, const char *val);
488 : static void get_sublink_expr(SubLink *sublink, deparse_context *context);
489 : static void get_tablefunc(TableFunc *tf, deparse_context *context,
490 : bool showimplicit);
491 : static void get_from_clause(Query *query, const char *prefix,
492 : deparse_context *context);
493 : static void get_from_clause_item(Node *jtnode, Query *query,
494 : deparse_context *context);
495 : static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
496 : deparse_context *context);
497 : static void get_column_alias_list(deparse_columns *colinfo,
498 : deparse_context *context);
499 : static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
500 : deparse_columns *colinfo,
501 : deparse_context *context);
502 : static void get_tablesample_def(TableSampleClause *tablesample,
503 : deparse_context *context);
504 : static void get_opclass_name(Oid opclass, Oid actual_datatype,
505 : StringInfo buf);
506 : static Node *processIndirection(Node *node, deparse_context *context);
507 : static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
508 : static char *get_relation_name(Oid relid);
509 : static char *generate_relation_name(Oid relid, List *namespaces);
510 : static char *generate_qualified_relation_name(Oid relid);
511 : static char *generate_function_name(Oid funcid, int nargs,
512 : List *argnames, Oid *argtypes,
513 : bool has_variadic, bool *use_variadic_p,
514 : ParseExprKind special_exprkind);
515 : static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
516 : static void add_cast_to(StringInfo buf, Oid typid);
517 : static char *generate_qualified_type_name(Oid typid);
518 : static text *string_to_text(char *str);
519 : static char *flatten_reloptions(Oid relid);
520 : static void get_reloptions(StringInfo buf, Datum reloptions);
521 :
522 : #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
523 :
524 :
525 : /* ----------
526 : * pg_get_ruledef - Do it all and return a text
527 : * that could be used as a statement
528 : * to recreate the rule
529 : * ----------
530 : */
531 : Datum
532 442 : pg_get_ruledef(PG_FUNCTION_ARGS)
533 : {
534 442 : Oid ruleoid = PG_GETARG_OID(0);
535 : int prettyFlags;
536 : char *res;
537 :
538 442 : prettyFlags = PRETTYFLAG_INDENT;
539 :
540 442 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
541 :
542 442 : if (res == NULL)
543 6 : PG_RETURN_NULL();
544 :
545 436 : PG_RETURN_TEXT_P(string_to_text(res));
546 : }
547 :
548 :
549 : Datum
550 96 : pg_get_ruledef_ext(PG_FUNCTION_ARGS)
551 : {
552 96 : Oid ruleoid = PG_GETARG_OID(0);
553 96 : bool pretty = PG_GETARG_BOOL(1);
554 : int prettyFlags;
555 : char *res;
556 :
557 96 : prettyFlags = GET_PRETTY_FLAGS(pretty);
558 :
559 96 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
560 :
561 96 : if (res == NULL)
562 0 : PG_RETURN_NULL();
563 :
564 96 : PG_RETURN_TEXT_P(string_to_text(res));
565 : }
566 :
567 :
568 : static char *
569 538 : pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
570 : {
571 : Datum args[1];
572 : char nulls[1];
573 : int spirc;
574 : HeapTuple ruletup;
575 : TupleDesc rulettc;
576 : StringInfoData buf;
577 :
578 : /*
579 : * Do this first so that string is alloc'd in outer context not SPI's.
580 : */
581 538 : initStringInfo(&buf);
582 :
583 : /*
584 : * Connect to SPI manager
585 : */
586 538 : if (SPI_connect() != SPI_OK_CONNECT)
587 0 : elog(ERROR, "SPI_connect failed");
588 :
589 : /*
590 : * On the first call prepare the plan to lookup pg_rewrite. We read
591 : * pg_rewrite over the SPI manager instead of using the syscache to be
592 : * checked for read access on pg_rewrite.
593 : */
594 538 : if (plan_getrulebyoid == NULL)
595 : {
596 : Oid argtypes[1];
597 : SPIPlanPtr plan;
598 :
599 36 : argtypes[0] = OIDOID;
600 36 : plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
601 36 : if (plan == NULL)
602 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
603 36 : SPI_keepplan(plan);
604 36 : plan_getrulebyoid = plan;
605 : }
606 :
607 : /*
608 : * Get the pg_rewrite tuple for this rule
609 : */
610 538 : args[0] = ObjectIdGetDatum(ruleoid);
611 538 : nulls[0] = ' ';
612 538 : spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
613 538 : if (spirc != SPI_OK_SELECT)
614 0 : elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
615 538 : if (SPI_processed != 1)
616 : {
617 : /*
618 : * There is no tuple data available here, just keep the output buffer
619 : * empty.
620 : */
621 : }
622 : else
623 : {
624 : /*
625 : * Get the rule's definition and put it into executor's memory
626 : */
627 532 : ruletup = SPI_tuptable->vals[0];
628 532 : rulettc = SPI_tuptable->tupdesc;
629 532 : make_ruledef(&buf, ruletup, rulettc, prettyFlags);
630 : }
631 :
632 : /*
633 : * Disconnect from SPI manager
634 : */
635 538 : if (SPI_finish() != SPI_OK_FINISH)
636 0 : elog(ERROR, "SPI_finish failed");
637 :
638 538 : if (buf.len == 0)
639 6 : return NULL;
640 :
641 532 : return buf.data;
642 : }
643 :
644 :
645 : /* ----------
646 : * pg_get_viewdef - Mainly the same thing, but we
647 : * only return the SELECT part of a view
648 : * ----------
649 : */
650 : Datum
651 2044 : pg_get_viewdef(PG_FUNCTION_ARGS)
652 : {
653 : /* By OID */
654 2044 : Oid viewoid = PG_GETARG_OID(0);
655 : int prettyFlags;
656 : char *res;
657 :
658 2044 : prettyFlags = PRETTYFLAG_INDENT;
659 :
660 2044 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
661 :
662 2044 : if (res == NULL)
663 6 : PG_RETURN_NULL();
664 :
665 2038 : PG_RETURN_TEXT_P(string_to_text(res));
666 : }
667 :
668 :
669 : Datum
670 466 : pg_get_viewdef_ext(PG_FUNCTION_ARGS)
671 : {
672 : /* By OID */
673 466 : Oid viewoid = PG_GETARG_OID(0);
674 466 : bool pretty = PG_GETARG_BOOL(1);
675 : int prettyFlags;
676 : char *res;
677 :
678 466 : prettyFlags = GET_PRETTY_FLAGS(pretty);
679 :
680 466 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
681 :
682 466 : if (res == NULL)
683 0 : PG_RETURN_NULL();
684 :
685 466 : PG_RETURN_TEXT_P(string_to_text(res));
686 : }
687 :
688 : Datum
689 6 : pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
690 : {
691 : /* By OID */
692 6 : Oid viewoid = PG_GETARG_OID(0);
693 6 : int wrap = PG_GETARG_INT32(1);
694 : int prettyFlags;
695 : char *res;
696 :
697 : /* calling this implies we want pretty printing */
698 6 : prettyFlags = GET_PRETTY_FLAGS(true);
699 :
700 6 : res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
701 :
702 6 : if (res == NULL)
703 0 : PG_RETURN_NULL();
704 :
705 6 : PG_RETURN_TEXT_P(string_to_text(res));
706 : }
707 :
708 : Datum
709 72 : pg_get_viewdef_name(PG_FUNCTION_ARGS)
710 : {
711 : /* By qualified name */
712 72 : text *viewname = PG_GETARG_TEXT_PP(0);
713 : int prettyFlags;
714 : RangeVar *viewrel;
715 : Oid viewoid;
716 : char *res;
717 :
718 72 : prettyFlags = PRETTYFLAG_INDENT;
719 :
720 : /* Look up view name. Can't lock it - we might not have privileges. */
721 72 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
722 72 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
723 :
724 72 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
725 :
726 72 : if (res == NULL)
727 0 : PG_RETURN_NULL();
728 :
729 72 : PG_RETURN_TEXT_P(string_to_text(res));
730 : }
731 :
732 :
733 : Datum
734 402 : pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
735 : {
736 : /* By qualified name */
737 402 : text *viewname = PG_GETARG_TEXT_PP(0);
738 402 : bool pretty = PG_GETARG_BOOL(1);
739 : int prettyFlags;
740 : RangeVar *viewrel;
741 : Oid viewoid;
742 : char *res;
743 :
744 402 : prettyFlags = GET_PRETTY_FLAGS(pretty);
745 :
746 : /* Look up view name. Can't lock it - we might not have privileges. */
747 402 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
748 402 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
749 :
750 402 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
751 :
752 402 : if (res == NULL)
753 0 : PG_RETURN_NULL();
754 :
755 402 : PG_RETURN_TEXT_P(string_to_text(res));
756 : }
757 :
758 : /*
759 : * Common code for by-OID and by-name variants of pg_get_viewdef
760 : */
761 : static char *
762 2990 : pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
763 : {
764 : Datum args[2];
765 : char nulls[2];
766 : int spirc;
767 : HeapTuple ruletup;
768 : TupleDesc rulettc;
769 : StringInfoData buf;
770 :
771 : /*
772 : * Do this first so that string is alloc'd in outer context not SPI's.
773 : */
774 2990 : initStringInfo(&buf);
775 :
776 : /*
777 : * Connect to SPI manager
778 : */
779 2990 : if (SPI_connect() != SPI_OK_CONNECT)
780 0 : elog(ERROR, "SPI_connect failed");
781 :
782 : /*
783 : * On the first call prepare the plan to lookup pg_rewrite. We read
784 : * pg_rewrite over the SPI manager instead of using the syscache to be
785 : * checked for read access on pg_rewrite.
786 : */
787 2990 : if (plan_getviewrule == NULL)
788 : {
789 : Oid argtypes[2];
790 : SPIPlanPtr plan;
791 :
792 220 : argtypes[0] = OIDOID;
793 220 : argtypes[1] = NAMEOID;
794 220 : plan = SPI_prepare(query_getviewrule, 2, argtypes);
795 220 : if (plan == NULL)
796 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
797 220 : SPI_keepplan(plan);
798 220 : plan_getviewrule = plan;
799 : }
800 :
801 : /*
802 : * Get the pg_rewrite tuple for the view's SELECT rule
803 : */
804 2990 : args[0] = ObjectIdGetDatum(viewoid);
805 2990 : args[1] = DirectFunctionCall1(namein, CStringGetDatum(ViewSelectRuleName));
806 2990 : nulls[0] = ' ';
807 2990 : nulls[1] = ' ';
808 2990 : spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
809 2990 : if (spirc != SPI_OK_SELECT)
810 0 : elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
811 2990 : if (SPI_processed != 1)
812 : {
813 : /*
814 : * There is no tuple data available here, just keep the output buffer
815 : * empty.
816 : */
817 : }
818 : else
819 : {
820 : /*
821 : * Get the rule's definition and put it into executor's memory
822 : */
823 2984 : ruletup = SPI_tuptable->vals[0];
824 2984 : rulettc = SPI_tuptable->tupdesc;
825 2984 : make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
826 : }
827 :
828 : /*
829 : * Disconnect from SPI manager
830 : */
831 2990 : if (SPI_finish() != SPI_OK_FINISH)
832 0 : elog(ERROR, "SPI_finish failed");
833 :
834 2990 : if (buf.len == 0)
835 6 : return NULL;
836 :
837 2984 : return buf.data;
838 : }
839 :
840 : /* ----------
841 : * pg_get_triggerdef - Get the definition of a trigger
842 : * ----------
843 : */
844 : Datum
845 204 : pg_get_triggerdef(PG_FUNCTION_ARGS)
846 : {
847 204 : Oid trigid = PG_GETARG_OID(0);
848 : char *res;
849 :
850 204 : res = pg_get_triggerdef_worker(trigid, false);
851 :
852 204 : if (res == NULL)
853 6 : PG_RETURN_NULL();
854 :
855 198 : PG_RETURN_TEXT_P(string_to_text(res));
856 : }
857 :
858 : Datum
859 1130 : pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
860 : {
861 1130 : Oid trigid = PG_GETARG_OID(0);
862 1130 : bool pretty = PG_GETARG_BOOL(1);
863 : char *res;
864 :
865 1130 : res = pg_get_triggerdef_worker(trigid, pretty);
866 :
867 1130 : if (res == NULL)
868 0 : PG_RETURN_NULL();
869 :
870 1130 : PG_RETURN_TEXT_P(string_to_text(res));
871 : }
872 :
873 : static char *
874 1334 : pg_get_triggerdef_worker(Oid trigid, bool pretty)
875 : {
876 : HeapTuple ht_trig;
877 : Form_pg_trigger trigrec;
878 : StringInfoData buf;
879 : Relation tgrel;
880 : ScanKeyData skey[1];
881 : SysScanDesc tgscan;
882 1334 : int findx = 0;
883 : char *tgname;
884 : char *tgoldtable;
885 : char *tgnewtable;
886 : Datum value;
887 : bool isnull;
888 :
889 : /*
890 : * Fetch the pg_trigger tuple by the Oid of the trigger
891 : */
892 1334 : tgrel = table_open(TriggerRelationId, AccessShareLock);
893 :
894 1334 : ScanKeyInit(&skey[0],
895 : Anum_pg_trigger_oid,
896 : BTEqualStrategyNumber, F_OIDEQ,
897 : ObjectIdGetDatum(trigid));
898 :
899 1334 : tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
900 : NULL, 1, skey);
901 :
902 1334 : ht_trig = systable_getnext(tgscan);
903 :
904 1334 : if (!HeapTupleIsValid(ht_trig))
905 : {
906 6 : systable_endscan(tgscan);
907 6 : table_close(tgrel, AccessShareLock);
908 6 : return NULL;
909 : }
910 :
911 1328 : trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
912 :
913 : /*
914 : * Start the trigger definition. Note that the trigger's name should never
915 : * be schema-qualified, but the trigger rel's name may be.
916 : */
917 1328 : initStringInfo(&buf);
918 :
919 1328 : tgname = NameStr(trigrec->tgname);
920 2656 : appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
921 1328 : OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
922 : quote_identifier(tgname));
923 :
924 1328 : if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
925 520 : appendStringInfoString(&buf, "BEFORE");
926 808 : else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
927 784 : appendStringInfoString(&buf, "AFTER");
928 24 : else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
929 24 : appendStringInfoString(&buf, "INSTEAD OF");
930 : else
931 0 : elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
932 :
933 1328 : if (TRIGGER_FOR_INSERT(trigrec->tgtype))
934 : {
935 864 : appendStringInfoString(&buf, " INSERT");
936 864 : findx++;
937 : }
938 1328 : if (TRIGGER_FOR_DELETE(trigrec->tgtype))
939 : {
940 226 : if (findx > 0)
941 90 : appendStringInfoString(&buf, " OR DELETE");
942 : else
943 136 : appendStringInfoString(&buf, " DELETE");
944 226 : findx++;
945 : }
946 1328 : if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
947 : {
948 648 : if (findx > 0)
949 320 : appendStringInfoString(&buf, " OR UPDATE");
950 : else
951 328 : appendStringInfoString(&buf, " UPDATE");
952 648 : findx++;
953 : /* tgattr is first var-width field, so OK to access directly */
954 648 : if (trigrec->tgattr.dim1 > 0)
955 : {
956 : int i;
957 :
958 76 : appendStringInfoString(&buf, " OF ");
959 168 : for (i = 0; i < trigrec->tgattr.dim1; i++)
960 : {
961 : char *attname;
962 :
963 92 : if (i > 0)
964 16 : appendStringInfoString(&buf, ", ");
965 92 : attname = get_attname(trigrec->tgrelid,
966 92 : trigrec->tgattr.values[i], false);
967 92 : appendStringInfoString(&buf, quote_identifier(attname));
968 : }
969 : }
970 : }
971 1328 : if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
972 : {
973 0 : if (findx > 0)
974 0 : appendStringInfoString(&buf, " OR TRUNCATE");
975 : else
976 0 : appendStringInfoString(&buf, " TRUNCATE");
977 0 : findx++;
978 : }
979 :
980 : /*
981 : * In non-pretty mode, always schema-qualify the target table name for
982 : * safety. In pretty mode, schema-qualify only if not visible.
983 : */
984 2656 : appendStringInfo(&buf, " ON %s ",
985 : pretty ?
986 138 : generate_relation_name(trigrec->tgrelid, NIL) :
987 1190 : generate_qualified_relation_name(trigrec->tgrelid));
988 :
989 1328 : if (OidIsValid(trigrec->tgconstraint))
990 : {
991 0 : if (OidIsValid(trigrec->tgconstrrelid))
992 0 : appendStringInfo(&buf, "FROM %s ",
993 : generate_relation_name(trigrec->tgconstrrelid, NIL));
994 0 : if (!trigrec->tgdeferrable)
995 0 : appendStringInfoString(&buf, "NOT ");
996 0 : appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
997 0 : if (trigrec->tginitdeferred)
998 0 : appendStringInfoString(&buf, "DEFERRED ");
999 : else
1000 0 : appendStringInfoString(&buf, "IMMEDIATE ");
1001 : }
1002 :
1003 1328 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
1004 : tgrel->rd_att, &isnull);
1005 1328 : if (!isnull)
1006 98 : tgoldtable = NameStr(*DatumGetName(value));
1007 : else
1008 1230 : tgoldtable = NULL;
1009 1328 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
1010 : tgrel->rd_att, &isnull);
1011 1328 : if (!isnull)
1012 108 : tgnewtable = NameStr(*DatumGetName(value));
1013 : else
1014 1220 : tgnewtable = NULL;
1015 1328 : if (tgoldtable != NULL || tgnewtable != NULL)
1016 : {
1017 152 : appendStringInfoString(&buf, "REFERENCING ");
1018 152 : if (tgoldtable != NULL)
1019 98 : appendStringInfo(&buf, "OLD TABLE AS %s ",
1020 : quote_identifier(tgoldtable));
1021 152 : if (tgnewtable != NULL)
1022 108 : appendStringInfo(&buf, "NEW TABLE AS %s ",
1023 : quote_identifier(tgnewtable));
1024 : }
1025 :
1026 1328 : if (TRIGGER_FOR_ROW(trigrec->tgtype))
1027 1010 : appendStringInfoString(&buf, "FOR EACH ROW ");
1028 : else
1029 318 : appendStringInfoString(&buf, "FOR EACH STATEMENT ");
1030 :
1031 : /* If the trigger has a WHEN qualification, add that */
1032 1328 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
1033 : tgrel->rd_att, &isnull);
1034 1328 : if (!isnull)
1035 : {
1036 : Node *qual;
1037 : char relkind;
1038 : deparse_context context;
1039 : deparse_namespace dpns;
1040 : RangeTblEntry *oldrte;
1041 : RangeTblEntry *newrte;
1042 :
1043 138 : appendStringInfoString(&buf, "WHEN (");
1044 :
1045 138 : qual = stringToNode(TextDatumGetCString(value));
1046 :
1047 138 : relkind = get_rel_relkind(trigrec->tgrelid);
1048 :
1049 : /* Build minimal OLD and NEW RTEs for the rel */
1050 138 : oldrte = makeNode(RangeTblEntry);
1051 138 : oldrte->rtekind = RTE_RELATION;
1052 138 : oldrte->relid = trigrec->tgrelid;
1053 138 : oldrte->relkind = relkind;
1054 138 : oldrte->rellockmode = AccessShareLock;
1055 138 : oldrte->alias = makeAlias("old", NIL);
1056 138 : oldrte->eref = oldrte->alias;
1057 138 : oldrte->lateral = false;
1058 138 : oldrte->inh = false;
1059 138 : oldrte->inFromCl = true;
1060 :
1061 138 : newrte = makeNode(RangeTblEntry);
1062 138 : newrte->rtekind = RTE_RELATION;
1063 138 : newrte->relid = trigrec->tgrelid;
1064 138 : newrte->relkind = relkind;
1065 138 : newrte->rellockmode = AccessShareLock;
1066 138 : newrte->alias = makeAlias("new", NIL);
1067 138 : newrte->eref = newrte->alias;
1068 138 : newrte->lateral = false;
1069 138 : newrte->inh = false;
1070 138 : newrte->inFromCl = true;
1071 :
1072 : /* Build two-element rtable */
1073 138 : memset(&dpns, 0, sizeof(dpns));
1074 138 : dpns.rtable = list_make2(oldrte, newrte);
1075 138 : dpns.subplans = NIL;
1076 138 : dpns.ctes = NIL;
1077 138 : dpns.appendrels = NULL;
1078 138 : set_rtable_names(&dpns, NIL, NULL);
1079 138 : set_simple_column_names(&dpns);
1080 :
1081 : /* Set up context with one-deep namespace stack */
1082 138 : context.buf = &buf;
1083 138 : context.namespaces = list_make1(&dpns);
1084 138 : context.windowClause = NIL;
1085 138 : context.windowTList = NIL;
1086 138 : context.varprefix = true;
1087 138 : context.prettyFlags = GET_PRETTY_FLAGS(pretty);
1088 138 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
1089 138 : context.indentLevel = PRETTYINDENT_STD;
1090 138 : context.special_exprkind = EXPR_KIND_NONE;
1091 138 : context.appendparents = NULL;
1092 :
1093 138 : get_rule_expr(qual, &context, false);
1094 :
1095 138 : appendStringInfoString(&buf, ") ");
1096 : }
1097 :
1098 1328 : appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
1099 : generate_function_name(trigrec->tgfoid, 0,
1100 : NIL, NULL,
1101 : false, NULL, EXPR_KIND_NONE));
1102 :
1103 1328 : if (trigrec->tgnargs > 0)
1104 : {
1105 : char *p;
1106 : int i;
1107 :
1108 450 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
1109 : tgrel->rd_att, &isnull);
1110 450 : if (isnull)
1111 0 : elog(ERROR, "tgargs is null for trigger %u", trigid);
1112 450 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
1113 1196 : for (i = 0; i < trigrec->tgnargs; i++)
1114 : {
1115 746 : if (i > 0)
1116 296 : appendStringInfoString(&buf, ", ");
1117 746 : simple_quote_literal(&buf, p);
1118 : /* advance p to next string embedded in tgargs */
1119 6756 : while (*p)
1120 6010 : p++;
1121 746 : p++;
1122 : }
1123 : }
1124 :
1125 : /* We deliberately do not put semi-colon at end */
1126 1328 : appendStringInfoChar(&buf, ')');
1127 :
1128 : /* Clean up */
1129 1328 : systable_endscan(tgscan);
1130 :
1131 1328 : table_close(tgrel, AccessShareLock);
1132 :
1133 1328 : return buf.data;
1134 : }
1135 :
1136 : /* ----------
1137 : * pg_get_indexdef - Get the definition of an index
1138 : *
1139 : * In the extended version, there is a colno argument as well as pretty bool.
1140 : * if colno == 0, we want a complete index definition.
1141 : * if colno > 0, we only want the Nth index key's variable or expression.
1142 : *
1143 : * Note that the SQL-function versions of this omit any info about the
1144 : * index tablespace; this is intentional because pg_dump wants it that way.
1145 : * However pg_get_indexdef_string() includes the index tablespace.
1146 : * ----------
1147 : */
1148 : Datum
1149 4484 : pg_get_indexdef(PG_FUNCTION_ARGS)
1150 : {
1151 4484 : Oid indexrelid = PG_GETARG_OID(0);
1152 : int prettyFlags;
1153 : char *res;
1154 :
1155 4484 : prettyFlags = PRETTYFLAG_INDENT;
1156 :
1157 4484 : res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1158 : false, false,
1159 : false, false,
1160 : prettyFlags, true);
1161 :
1162 4484 : if (res == NULL)
1163 6 : PG_RETURN_NULL();
1164 :
1165 4478 : PG_RETURN_TEXT_P(string_to_text(res));
1166 : }
1167 :
1168 : Datum
1169 1718 : pg_get_indexdef_ext(PG_FUNCTION_ARGS)
1170 : {
1171 1718 : Oid indexrelid = PG_GETARG_OID(0);
1172 1718 : int32 colno = PG_GETARG_INT32(1);
1173 1718 : bool pretty = PG_GETARG_BOOL(2);
1174 : int prettyFlags;
1175 : char *res;
1176 :
1177 1718 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1178 :
1179 1718 : res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1180 : colno != 0, false,
1181 : false, false,
1182 : prettyFlags, true);
1183 :
1184 1718 : if (res == NULL)
1185 0 : PG_RETURN_NULL();
1186 :
1187 1718 : PG_RETURN_TEXT_P(string_to_text(res));
1188 : }
1189 :
1190 : /*
1191 : * Internal version for use by ALTER TABLE.
1192 : * Includes a tablespace clause in the result.
1193 : * Returns a palloc'd C string; no pretty-printing.
1194 : */
1195 : char *
1196 194 : pg_get_indexdef_string(Oid indexrelid)
1197 : {
1198 194 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1199 : false, false,
1200 : true, true,
1201 : 0, false);
1202 : }
1203 :
1204 : /* Internal version that just reports the key-column definitions */
1205 : char *
1206 708 : pg_get_indexdef_columns(Oid indexrelid, bool pretty)
1207 : {
1208 : int prettyFlags;
1209 :
1210 708 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1211 :
1212 708 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1213 : true, true,
1214 : false, false,
1215 : prettyFlags, false);
1216 : }
1217 :
1218 : /* Internal version, extensible with flags to control its behavior */
1219 : char *
1220 8 : pg_get_indexdef_columns_extended(Oid indexrelid, bits16 flags)
1221 : {
1222 8 : bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1223 8 : bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1224 : int prettyFlags;
1225 :
1226 8 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1227 :
1228 8 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1229 : true, keys_only,
1230 : false, false,
1231 : prettyFlags, false);
1232 : }
1233 :
1234 : /*
1235 : * Internal workhorse to decompile an index definition.
1236 : *
1237 : * This is now used for exclusion constraints as well: if excludeOps is not
1238 : * NULL then it points to an array of exclusion operator OIDs.
1239 : */
1240 : static char *
1241 7216 : pg_get_indexdef_worker(Oid indexrelid, int colno,
1242 : const Oid *excludeOps,
1243 : bool attrsOnly, bool keysOnly,
1244 : bool showTblSpc, bool inherits,
1245 : int prettyFlags, bool missing_ok)
1246 : {
1247 : /* might want a separate isConstraint parameter later */
1248 7216 : bool isConstraint = (excludeOps != NULL);
1249 : HeapTuple ht_idx;
1250 : HeapTuple ht_idxrel;
1251 : HeapTuple ht_am;
1252 : Form_pg_index idxrec;
1253 : Form_pg_class idxrelrec;
1254 : Form_pg_am amrec;
1255 : IndexAmRoutine *amroutine;
1256 : List *indexprs;
1257 : ListCell *indexpr_item;
1258 : List *context;
1259 : Oid indrelid;
1260 : int keyno;
1261 : Datum indcollDatum;
1262 : Datum indclassDatum;
1263 : Datum indoptionDatum;
1264 : oidvector *indcollation;
1265 : oidvector *indclass;
1266 : int2vector *indoption;
1267 : StringInfoData buf;
1268 : char *str;
1269 : char *sep;
1270 :
1271 : /*
1272 : * Fetch the pg_index tuple by the Oid of the index
1273 : */
1274 7216 : ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
1275 7216 : if (!HeapTupleIsValid(ht_idx))
1276 : {
1277 6 : if (missing_ok)
1278 6 : return NULL;
1279 0 : elog(ERROR, "cache lookup failed for index %u", indexrelid);
1280 : }
1281 7210 : idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
1282 :
1283 7210 : indrelid = idxrec->indrelid;
1284 : Assert(indexrelid == idxrec->indexrelid);
1285 :
1286 : /* Must get indcollation, indclass, and indoption the hard way */
1287 7210 : indcollDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1288 : Anum_pg_index_indcollation);
1289 7210 : indcollation = (oidvector *) DatumGetPointer(indcollDatum);
1290 :
1291 7210 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1292 : Anum_pg_index_indclass);
1293 7210 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
1294 :
1295 7210 : indoptionDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1296 : Anum_pg_index_indoption);
1297 7210 : indoption = (int2vector *) DatumGetPointer(indoptionDatum);
1298 :
1299 : /*
1300 : * Fetch the pg_class tuple of the index relation
1301 : */
1302 7210 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
1303 7210 : if (!HeapTupleIsValid(ht_idxrel))
1304 0 : elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1305 7210 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
1306 :
1307 : /*
1308 : * Fetch the pg_am tuple of the index' access method
1309 : */
1310 7210 : ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
1311 7210 : if (!HeapTupleIsValid(ht_am))
1312 0 : elog(ERROR, "cache lookup failed for access method %u",
1313 : idxrelrec->relam);
1314 7210 : amrec = (Form_pg_am) GETSTRUCT(ht_am);
1315 :
1316 : /* Fetch the index AM's API struct */
1317 7210 : amroutine = GetIndexAmRoutine(amrec->amhandler);
1318 :
1319 : /*
1320 : * Get the index expressions, if any. (NOTE: we do not use the relcache
1321 : * versions of the expressions and predicate, because we want to display
1322 : * non-const-folded expressions.)
1323 : */
1324 7210 : if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs, NULL))
1325 : {
1326 : Datum exprsDatum;
1327 : char *exprsString;
1328 :
1329 546 : exprsDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1330 : Anum_pg_index_indexprs);
1331 546 : exprsString = TextDatumGetCString(exprsDatum);
1332 546 : indexprs = (List *) stringToNode(exprsString);
1333 546 : pfree(exprsString);
1334 : }
1335 : else
1336 6664 : indexprs = NIL;
1337 :
1338 7210 : indexpr_item = list_head(indexprs);
1339 :
1340 7210 : context = deparse_context_for(get_relation_name(indrelid), indrelid);
1341 :
1342 : /*
1343 : * Start the index definition. Note that the index's name should never be
1344 : * schema-qualified, but the indexed rel's name may be.
1345 : */
1346 7210 : initStringInfo(&buf);
1347 :
1348 7210 : if (!attrsOnly)
1349 : {
1350 6044 : if (!isConstraint)
1351 11880 : appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
1352 5940 : idxrec->indisunique ? "UNIQUE " : "",
1353 5940 : quote_identifier(NameStr(idxrelrec->relname)),
1354 5940 : idxrelrec->relkind == RELKIND_PARTITIONED_INDEX
1355 624 : && !inherits ? "ONLY " : "",
1356 5940 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
1357 1268 : generate_relation_name(indrelid, NIL) :
1358 4672 : generate_qualified_relation_name(indrelid),
1359 5940 : quote_identifier(NameStr(amrec->amname)));
1360 : else /* currently, must be EXCLUDE constraint */
1361 104 : appendStringInfo(&buf, "EXCLUDE USING %s (",
1362 104 : quote_identifier(NameStr(amrec->amname)));
1363 : }
1364 :
1365 : /*
1366 : * Report the indexed attributes
1367 : */
1368 7210 : sep = "";
1369 17942 : for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1370 : {
1371 10830 : AttrNumber attnum = idxrec->indkey.values[keyno];
1372 : Oid keycoltype;
1373 : Oid keycolcollation;
1374 :
1375 : /*
1376 : * Ignore non-key attributes if told to.
1377 : */
1378 10830 : if (keysOnly && keyno >= idxrec->indnkeyatts)
1379 98 : break;
1380 :
1381 : /* Otherwise, print INCLUDE to divide key and non-key attrs. */
1382 10732 : if (!colno && keyno == idxrec->indnkeyatts)
1383 : {
1384 250 : appendStringInfoString(&buf, ") INCLUDE (");
1385 250 : sep = "";
1386 : }
1387 :
1388 10732 : if (!colno)
1389 10102 : appendStringInfoString(&buf, sep);
1390 10732 : sep = ", ";
1391 :
1392 10732 : if (attnum != 0)
1393 : {
1394 : /* Simple index column */
1395 : char *attname;
1396 : int32 keycoltypmod;
1397 :
1398 10040 : attname = get_attname(indrelid, attnum, false);
1399 10040 : if (!colno || colno == keyno + 1)
1400 9872 : appendStringInfoString(&buf, quote_identifier(attname));
1401 10040 : get_atttypetypmodcoll(indrelid, attnum,
1402 : &keycoltype, &keycoltypmod,
1403 : &keycolcollation);
1404 : }
1405 : else
1406 : {
1407 : /* expressional index */
1408 : Node *indexkey;
1409 :
1410 692 : if (indexpr_item == NULL)
1411 0 : elog(ERROR, "too few entries in indexprs list");
1412 692 : indexkey = (Node *) lfirst(indexpr_item);
1413 692 : indexpr_item = lnext(indexprs, indexpr_item);
1414 : /* Deparse */
1415 692 : str = deparse_expression_pretty(indexkey, context, false, false,
1416 : prettyFlags, 0);
1417 692 : if (!colno || colno == keyno + 1)
1418 : {
1419 : /* Need parens if it's not a bare function call */
1420 680 : if (looks_like_function(indexkey))
1421 52 : appendStringInfoString(&buf, str);
1422 : else
1423 628 : appendStringInfo(&buf, "(%s)", str);
1424 : }
1425 692 : keycoltype = exprType(indexkey);
1426 692 : keycolcollation = exprCollation(indexkey);
1427 : }
1428 :
1429 : /* Print additional decoration for (selected) key columns */
1430 10732 : if (!attrsOnly && keyno < idxrec->indnkeyatts &&
1431 0 : (!colno || colno == keyno + 1))
1432 : {
1433 8768 : int16 opt = indoption->values[keyno];
1434 8768 : Oid indcoll = indcollation->values[keyno];
1435 8768 : Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1436 8768 : bool has_options = attoptions != (Datum) 0;
1437 :
1438 : /* Add collation, if not default for column */
1439 8768 : if (OidIsValid(indcoll) && indcoll != keycolcollation)
1440 78 : appendStringInfo(&buf, " COLLATE %s",
1441 : generate_collation_name((indcoll)));
1442 :
1443 : /* Add the operator class name, if not default */
1444 8768 : get_opclass_name(indclass->values[keyno],
1445 : has_options ? InvalidOid : keycoltype, &buf);
1446 :
1447 8768 : if (has_options)
1448 : {
1449 30 : appendStringInfoString(&buf, " (");
1450 30 : get_reloptions(&buf, attoptions);
1451 30 : appendStringInfoChar(&buf, ')');
1452 : }
1453 :
1454 : /* Add options if relevant */
1455 8768 : if (amroutine->amcanorder)
1456 : {
1457 : /* if it supports sort ordering, report DESC and NULLS opts */
1458 7460 : if (opt & INDOPTION_DESC)
1459 : {
1460 0 : appendStringInfoString(&buf, " DESC");
1461 : /* NULLS FIRST is the default in this case */
1462 0 : if (!(opt & INDOPTION_NULLS_FIRST))
1463 0 : appendStringInfoString(&buf, " NULLS LAST");
1464 : }
1465 : else
1466 : {
1467 7460 : if (opt & INDOPTION_NULLS_FIRST)
1468 0 : appendStringInfoString(&buf, " NULLS FIRST");
1469 : }
1470 : }
1471 :
1472 : /* Add the exclusion operator if relevant */
1473 8768 : if (excludeOps != NULL)
1474 124 : appendStringInfo(&buf, " WITH %s",
1475 124 : generate_operator_name(excludeOps[keyno],
1476 : keycoltype,
1477 : keycoltype));
1478 : }
1479 : }
1480 :
1481 7210 : if (!attrsOnly)
1482 : {
1483 6044 : appendStringInfoChar(&buf, ')');
1484 :
1485 6044 : if (idxrec->indnullsnotdistinct)
1486 12 : appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1487 :
1488 : /*
1489 : * If it has options, append "WITH (options)"
1490 : */
1491 6044 : str = flatten_reloptions(indexrelid);
1492 6044 : if (str)
1493 : {
1494 210 : appendStringInfo(&buf, " WITH (%s)", str);
1495 210 : pfree(str);
1496 : }
1497 :
1498 : /*
1499 : * Print tablespace, but only if requested
1500 : */
1501 6044 : if (showTblSpc)
1502 : {
1503 : Oid tblspc;
1504 :
1505 194 : tblspc = get_rel_tablespace(indexrelid);
1506 194 : if (OidIsValid(tblspc))
1507 : {
1508 54 : if (isConstraint)
1509 0 : appendStringInfoString(&buf, " USING INDEX");
1510 54 : appendStringInfo(&buf, " TABLESPACE %s",
1511 54 : quote_identifier(get_tablespace_name(tblspc)));
1512 : }
1513 : }
1514 :
1515 : /*
1516 : * If it's a partial index, decompile and append the predicate
1517 : */
1518 6044 : if (!heap_attisnull(ht_idx, Anum_pg_index_indpred, NULL))
1519 : {
1520 : Node *node;
1521 : Datum predDatum;
1522 : char *predString;
1523 :
1524 : /* Convert text string to node tree */
1525 308 : predDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1526 : Anum_pg_index_indpred);
1527 308 : predString = TextDatumGetCString(predDatum);
1528 308 : node = (Node *) stringToNode(predString);
1529 308 : pfree(predString);
1530 :
1531 : /* Deparse */
1532 308 : str = deparse_expression_pretty(node, context, false, false,
1533 : prettyFlags, 0);
1534 308 : if (isConstraint)
1535 42 : appendStringInfo(&buf, " WHERE (%s)", str);
1536 : else
1537 266 : appendStringInfo(&buf, " WHERE %s", str);
1538 : }
1539 : }
1540 :
1541 : /* Clean up */
1542 7210 : ReleaseSysCache(ht_idx);
1543 7210 : ReleaseSysCache(ht_idxrel);
1544 7210 : ReleaseSysCache(ht_am);
1545 :
1546 7210 : return buf.data;
1547 : }
1548 :
1549 : /* ----------
1550 : * pg_get_querydef
1551 : *
1552 : * Public entry point to deparse one query parsetree.
1553 : * The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
1554 : *
1555 : * The result is a palloc'd C string.
1556 : * ----------
1557 : */
1558 : char *
1559 0 : pg_get_querydef(Query *query, bool pretty)
1560 : {
1561 : StringInfoData buf;
1562 : int prettyFlags;
1563 :
1564 0 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1565 :
1566 0 : initStringInfo(&buf);
1567 :
1568 0 : get_query_def(query, &buf, NIL, NULL, true,
1569 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1570 :
1571 0 : return buf.data;
1572 : }
1573 :
1574 : /*
1575 : * pg_get_statisticsobjdef
1576 : * Get the definition of an extended statistics object
1577 : */
1578 : Datum
1579 250 : pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
1580 : {
1581 250 : Oid statextid = PG_GETARG_OID(0);
1582 : char *res;
1583 :
1584 250 : res = pg_get_statisticsobj_worker(statextid, false, true);
1585 :
1586 250 : if (res == NULL)
1587 6 : PG_RETURN_NULL();
1588 :
1589 244 : PG_RETURN_TEXT_P(string_to_text(res));
1590 : }
1591 :
1592 : /*
1593 : * Internal version for use by ALTER TABLE.
1594 : * Includes a tablespace clause in the result.
1595 : * Returns a palloc'd C string; no pretty-printing.
1596 : */
1597 : char *
1598 14 : pg_get_statisticsobjdef_string(Oid statextid)
1599 : {
1600 14 : return pg_get_statisticsobj_worker(statextid, false, false);
1601 : }
1602 :
1603 : /*
1604 : * pg_get_statisticsobjdef_columns
1605 : * Get columns and expressions for an extended statistics object
1606 : */
1607 : Datum
1608 396 : pg_get_statisticsobjdef_columns(PG_FUNCTION_ARGS)
1609 : {
1610 396 : Oid statextid = PG_GETARG_OID(0);
1611 : char *res;
1612 :
1613 396 : res = pg_get_statisticsobj_worker(statextid, true, true);
1614 :
1615 396 : if (res == NULL)
1616 0 : PG_RETURN_NULL();
1617 :
1618 396 : PG_RETURN_TEXT_P(string_to_text(res));
1619 : }
1620 :
1621 : /*
1622 : * Internal workhorse to decompile an extended statistics object.
1623 : */
1624 : static char *
1625 660 : pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
1626 : {
1627 : Form_pg_statistic_ext statextrec;
1628 : HeapTuple statexttup;
1629 : StringInfoData buf;
1630 : int colno;
1631 : char *nsp;
1632 : ArrayType *arr;
1633 : char *enabled;
1634 : Datum datum;
1635 : bool ndistinct_enabled;
1636 : bool dependencies_enabled;
1637 : bool mcv_enabled;
1638 : int i;
1639 : List *context;
1640 : ListCell *lc;
1641 660 : List *exprs = NIL;
1642 : bool has_exprs;
1643 : int ncolumns;
1644 :
1645 660 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1646 :
1647 660 : if (!HeapTupleIsValid(statexttup))
1648 : {
1649 6 : if (missing_ok)
1650 6 : return NULL;
1651 0 : elog(ERROR, "cache lookup failed for statistics object %u", statextid);
1652 : }
1653 :
1654 : /* has the statistics expressions? */
1655 654 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
1656 :
1657 654 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1658 :
1659 : /*
1660 : * Get the statistics expressions, if any. (NOTE: we do not use the
1661 : * relcache versions of the expressions, because we want to display
1662 : * non-const-folded expressions.)
1663 : */
1664 654 : if (has_exprs)
1665 : {
1666 : Datum exprsDatum;
1667 : char *exprsString;
1668 :
1669 136 : exprsDatum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1670 : Anum_pg_statistic_ext_stxexprs);
1671 136 : exprsString = TextDatumGetCString(exprsDatum);
1672 136 : exprs = (List *) stringToNode(exprsString);
1673 136 : pfree(exprsString);
1674 : }
1675 : else
1676 518 : exprs = NIL;
1677 :
1678 : /* count the number of columns (attributes and expressions) */
1679 654 : ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
1680 :
1681 654 : initStringInfo(&buf);
1682 :
1683 654 : if (!columns_only)
1684 : {
1685 258 : nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
1686 258 : appendStringInfo(&buf, "CREATE STATISTICS %s",
1687 : quote_qualified_identifier(nsp,
1688 258 : NameStr(statextrec->stxname)));
1689 :
1690 : /*
1691 : * Decode the stxkind column so that we know which stats types to
1692 : * print.
1693 : */
1694 258 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1695 : Anum_pg_statistic_ext_stxkind);
1696 258 : arr = DatumGetArrayTypeP(datum);
1697 258 : if (ARR_NDIM(arr) != 1 ||
1698 258 : ARR_HASNULL(arr) ||
1699 258 : ARR_ELEMTYPE(arr) != CHAROID)
1700 0 : elog(ERROR, "stxkind is not a 1-D char array");
1701 258 : enabled = (char *) ARR_DATA_PTR(arr);
1702 :
1703 258 : ndistinct_enabled = false;
1704 258 : dependencies_enabled = false;
1705 258 : mcv_enabled = false;
1706 :
1707 670 : for (i = 0; i < ARR_DIMS(arr)[0]; i++)
1708 : {
1709 412 : if (enabled[i] == STATS_EXT_NDISTINCT)
1710 138 : ndistinct_enabled = true;
1711 274 : else if (enabled[i] == STATS_EXT_DEPENDENCIES)
1712 90 : dependencies_enabled = true;
1713 184 : else if (enabled[i] == STATS_EXT_MCV)
1714 108 : mcv_enabled = true;
1715 :
1716 : /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
1717 : }
1718 :
1719 : /*
1720 : * If any option is disabled, then we'll need to append the types
1721 : * clause to show which options are enabled. We omit the types clause
1722 : * on purpose when all options are enabled, so a pg_dump/pg_restore
1723 : * will create all statistics types on a newer postgres version, if
1724 : * the statistics had all options enabled on the original version.
1725 : *
1726 : * But if the statistics is defined on just a single column, it has to
1727 : * be an expression statistics. In that case we don't need to specify
1728 : * kinds.
1729 : */
1730 258 : if ((!ndistinct_enabled || !dependencies_enabled || !mcv_enabled) &&
1731 : (ncolumns > 1))
1732 : {
1733 120 : bool gotone = false;
1734 :
1735 120 : appendStringInfoString(&buf, " (");
1736 :
1737 120 : if (ndistinct_enabled)
1738 : {
1739 66 : appendStringInfoString(&buf, "ndistinct");
1740 66 : gotone = true;
1741 : }
1742 :
1743 120 : if (dependencies_enabled)
1744 : {
1745 18 : appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
1746 18 : gotone = true;
1747 : }
1748 :
1749 120 : if (mcv_enabled)
1750 36 : appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
1751 :
1752 120 : appendStringInfoChar(&buf, ')');
1753 : }
1754 :
1755 258 : appendStringInfoString(&buf, " ON ");
1756 : }
1757 :
1758 : /* decode simple column references */
1759 1870 : for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
1760 : {
1761 1216 : AttrNumber attnum = statextrec->stxkeys.values[colno];
1762 : char *attname;
1763 :
1764 1216 : if (colno > 0)
1765 686 : appendStringInfoString(&buf, ", ");
1766 :
1767 1216 : attname = get_attname(statextrec->stxrelid, attnum, false);
1768 :
1769 1216 : appendStringInfoString(&buf, quote_identifier(attname));
1770 : }
1771 :
1772 654 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
1773 : statextrec->stxrelid);
1774 :
1775 876 : foreach(lc, exprs)
1776 : {
1777 222 : Node *expr = (Node *) lfirst(lc);
1778 : char *str;
1779 222 : int prettyFlags = PRETTYFLAG_PAREN;
1780 :
1781 222 : str = deparse_expression_pretty(expr, context, false, false,
1782 : prettyFlags, 0);
1783 :
1784 222 : if (colno > 0)
1785 98 : appendStringInfoString(&buf, ", ");
1786 :
1787 : /* Need parens if it's not a bare function call */
1788 222 : if (looks_like_function(expr))
1789 34 : appendStringInfoString(&buf, str);
1790 : else
1791 188 : appendStringInfo(&buf, "(%s)", str);
1792 :
1793 222 : colno++;
1794 : }
1795 :
1796 654 : if (!columns_only)
1797 258 : appendStringInfo(&buf, " FROM %s",
1798 : generate_relation_name(statextrec->stxrelid, NIL));
1799 :
1800 654 : ReleaseSysCache(statexttup);
1801 :
1802 654 : return buf.data;
1803 : }
1804 :
1805 : /*
1806 : * Generate text array of expressions for statistics object.
1807 : */
1808 : Datum
1809 0 : pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
1810 : {
1811 0 : Oid statextid = PG_GETARG_OID(0);
1812 : Form_pg_statistic_ext statextrec;
1813 : HeapTuple statexttup;
1814 : Datum datum;
1815 : List *context;
1816 : ListCell *lc;
1817 0 : List *exprs = NIL;
1818 : bool has_exprs;
1819 : char *tmp;
1820 0 : ArrayBuildState *astate = NULL;
1821 :
1822 0 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1823 :
1824 0 : if (!HeapTupleIsValid(statexttup))
1825 0 : PG_RETURN_NULL();
1826 :
1827 : /* Does the stats object have expressions? */
1828 0 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
1829 :
1830 : /* no expressions? we're done */
1831 0 : if (!has_exprs)
1832 : {
1833 0 : ReleaseSysCache(statexttup);
1834 0 : PG_RETURN_NULL();
1835 : }
1836 :
1837 0 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1838 :
1839 : /*
1840 : * Get the statistics expressions, and deparse them into text values.
1841 : */
1842 0 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1843 : Anum_pg_statistic_ext_stxexprs);
1844 0 : tmp = TextDatumGetCString(datum);
1845 0 : exprs = (List *) stringToNode(tmp);
1846 0 : pfree(tmp);
1847 :
1848 0 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
1849 : statextrec->stxrelid);
1850 :
1851 0 : foreach(lc, exprs)
1852 : {
1853 0 : Node *expr = (Node *) lfirst(lc);
1854 : char *str;
1855 0 : int prettyFlags = PRETTYFLAG_INDENT;
1856 :
1857 0 : str = deparse_expression_pretty(expr, context, false, false,
1858 : prettyFlags, 0);
1859 :
1860 0 : astate = accumArrayResult(astate,
1861 0 : PointerGetDatum(cstring_to_text(str)),
1862 : false,
1863 : TEXTOID,
1864 : CurrentMemoryContext);
1865 : }
1866 :
1867 0 : ReleaseSysCache(statexttup);
1868 :
1869 0 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1870 : }
1871 :
1872 : /*
1873 : * pg_get_partkeydef
1874 : *
1875 : * Returns the partition key specification, ie, the following:
1876 : *
1877 : * { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
1878 : */
1879 : Datum
1880 1160 : pg_get_partkeydef(PG_FUNCTION_ARGS)
1881 : {
1882 1160 : Oid relid = PG_GETARG_OID(0);
1883 : char *res;
1884 :
1885 1160 : res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
1886 :
1887 1160 : if (res == NULL)
1888 6 : PG_RETURN_NULL();
1889 :
1890 1154 : PG_RETURN_TEXT_P(string_to_text(res));
1891 : }
1892 :
1893 : /* Internal version that just reports the column definitions */
1894 : char *
1895 136 : pg_get_partkeydef_columns(Oid relid, bool pretty)
1896 : {
1897 : int prettyFlags;
1898 :
1899 136 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1900 :
1901 136 : return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
1902 : }
1903 :
1904 : /*
1905 : * Internal workhorse to decompile a partition key definition.
1906 : */
1907 : static char *
1908 1296 : pg_get_partkeydef_worker(Oid relid, int prettyFlags,
1909 : bool attrsOnly, bool missing_ok)
1910 : {
1911 : Form_pg_partitioned_table form;
1912 : HeapTuple tuple;
1913 : oidvector *partclass;
1914 : oidvector *partcollation;
1915 : List *partexprs;
1916 : ListCell *partexpr_item;
1917 : List *context;
1918 : Datum datum;
1919 : StringInfoData buf;
1920 : int keyno;
1921 : char *str;
1922 : char *sep;
1923 :
1924 1296 : tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
1925 1296 : if (!HeapTupleIsValid(tuple))
1926 : {
1927 6 : if (missing_ok)
1928 6 : return NULL;
1929 0 : elog(ERROR, "cache lookup failed for partition key of %u", relid);
1930 : }
1931 :
1932 1290 : form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
1933 :
1934 : Assert(form->partrelid == relid);
1935 :
1936 : /* Must get partclass and partcollation the hard way */
1937 1290 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1938 : Anum_pg_partitioned_table_partclass);
1939 1290 : partclass = (oidvector *) DatumGetPointer(datum);
1940 :
1941 1290 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1942 : Anum_pg_partitioned_table_partcollation);
1943 1290 : partcollation = (oidvector *) DatumGetPointer(datum);
1944 :
1945 :
1946 : /*
1947 : * Get the expressions, if any. (NOTE: we do not use the relcache
1948 : * versions of the expressions, because we want to display
1949 : * non-const-folded expressions.)
1950 : */
1951 1290 : if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprs, NULL))
1952 : {
1953 : Datum exprsDatum;
1954 : char *exprsString;
1955 :
1956 146 : exprsDatum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1957 : Anum_pg_partitioned_table_partexprs);
1958 146 : exprsString = TextDatumGetCString(exprsDatum);
1959 146 : partexprs = (List *) stringToNode(exprsString);
1960 :
1961 146 : if (!IsA(partexprs, List))
1962 0 : elog(ERROR, "unexpected node type found in partexprs: %d",
1963 : (int) nodeTag(partexprs));
1964 :
1965 146 : pfree(exprsString);
1966 : }
1967 : else
1968 1144 : partexprs = NIL;
1969 :
1970 1290 : partexpr_item = list_head(partexprs);
1971 1290 : context = deparse_context_for(get_relation_name(relid), relid);
1972 :
1973 1290 : initStringInfo(&buf);
1974 :
1975 1290 : switch (form->partstrat)
1976 : {
1977 54 : case PARTITION_STRATEGY_HASH:
1978 54 : if (!attrsOnly)
1979 54 : appendStringInfoString(&buf, "HASH");
1980 54 : break;
1981 440 : case PARTITION_STRATEGY_LIST:
1982 440 : if (!attrsOnly)
1983 400 : appendStringInfoString(&buf, "LIST");
1984 440 : break;
1985 796 : case PARTITION_STRATEGY_RANGE:
1986 796 : if (!attrsOnly)
1987 700 : appendStringInfoString(&buf, "RANGE");
1988 796 : break;
1989 0 : default:
1990 0 : elog(ERROR, "unexpected partition strategy: %d",
1991 : (int) form->partstrat);
1992 : }
1993 :
1994 1290 : if (!attrsOnly)
1995 1154 : appendStringInfoString(&buf, " (");
1996 1290 : sep = "";
1997 2732 : for (keyno = 0; keyno < form->partnatts; keyno++)
1998 : {
1999 1442 : AttrNumber attnum = form->partattrs.values[keyno];
2000 : Oid keycoltype;
2001 : Oid keycolcollation;
2002 : Oid partcoll;
2003 :
2004 1442 : appendStringInfoString(&buf, sep);
2005 1442 : sep = ", ";
2006 1442 : if (attnum != 0)
2007 : {
2008 : /* Simple attribute reference */
2009 : char *attname;
2010 : int32 keycoltypmod;
2011 :
2012 1284 : attname = get_attname(relid, attnum, false);
2013 1284 : appendStringInfoString(&buf, quote_identifier(attname));
2014 1284 : get_atttypetypmodcoll(relid, attnum,
2015 : &keycoltype, &keycoltypmod,
2016 : &keycolcollation);
2017 : }
2018 : else
2019 : {
2020 : /* Expression */
2021 : Node *partkey;
2022 :
2023 158 : if (partexpr_item == NULL)
2024 0 : elog(ERROR, "too few entries in partexprs list");
2025 158 : partkey = (Node *) lfirst(partexpr_item);
2026 158 : partexpr_item = lnext(partexprs, partexpr_item);
2027 :
2028 : /* Deparse */
2029 158 : str = deparse_expression_pretty(partkey, context, false, false,
2030 : prettyFlags, 0);
2031 : /* Need parens if it's not a bare function call */
2032 158 : if (looks_like_function(partkey))
2033 56 : appendStringInfoString(&buf, str);
2034 : else
2035 102 : appendStringInfo(&buf, "(%s)", str);
2036 :
2037 158 : keycoltype = exprType(partkey);
2038 158 : keycolcollation = exprCollation(partkey);
2039 : }
2040 :
2041 : /* Add collation, if not default for column */
2042 1442 : partcoll = partcollation->values[keyno];
2043 1442 : if (!attrsOnly && OidIsValid(partcoll) && partcoll != keycolcollation)
2044 6 : appendStringInfo(&buf, " COLLATE %s",
2045 : generate_collation_name((partcoll)));
2046 :
2047 : /* Add the operator class name, if not default */
2048 1442 : if (!attrsOnly)
2049 1252 : get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2050 : }
2051 :
2052 1290 : if (!attrsOnly)
2053 1154 : appendStringInfoChar(&buf, ')');
2054 :
2055 : /* Clean up */
2056 1290 : ReleaseSysCache(tuple);
2057 :
2058 1290 : return buf.data;
2059 : }
2060 :
2061 : /*
2062 : * pg_get_partition_constraintdef
2063 : *
2064 : * Returns partition constraint expression as a string for the input relation
2065 : */
2066 : Datum
2067 156 : pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
2068 : {
2069 156 : Oid relationId = PG_GETARG_OID(0);
2070 : Expr *constr_expr;
2071 : int prettyFlags;
2072 : List *context;
2073 : char *consrc;
2074 :
2075 156 : constr_expr = get_partition_qual_relid(relationId);
2076 :
2077 : /* Quick exit if no partition constraint */
2078 156 : if (constr_expr == NULL)
2079 18 : PG_RETURN_NULL();
2080 :
2081 : /*
2082 : * Deparse and return the constraint expression.
2083 : */
2084 138 : prettyFlags = PRETTYFLAG_INDENT;
2085 138 : context = deparse_context_for(get_relation_name(relationId), relationId);
2086 138 : consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2087 : false, prettyFlags, 0);
2088 :
2089 138 : PG_RETURN_TEXT_P(string_to_text(consrc));
2090 : }
2091 :
2092 : /*
2093 : * pg_get_partconstrdef_string
2094 : *
2095 : * Returns the partition constraint as a C-string for the input relation, with
2096 : * the given alias. No pretty-printing.
2097 : */
2098 : char *
2099 86 : pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
2100 : {
2101 : Expr *constr_expr;
2102 : List *context;
2103 :
2104 86 : constr_expr = get_partition_qual_relid(partitionId);
2105 86 : context = deparse_context_for(aliasname, partitionId);
2106 :
2107 86 : return deparse_expression((Node *) constr_expr, context, true, false);
2108 : }
2109 :
2110 : /*
2111 : * pg_get_constraintdef
2112 : *
2113 : * Returns the definition for the constraint, ie, everything that needs to
2114 : * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
2115 : */
2116 : Datum
2117 1610 : pg_get_constraintdef(PG_FUNCTION_ARGS)
2118 : {
2119 1610 : Oid constraintId = PG_GETARG_OID(0);
2120 : int prettyFlags;
2121 : char *res;
2122 :
2123 1610 : prettyFlags = PRETTYFLAG_INDENT;
2124 :
2125 1610 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2126 :
2127 1610 : if (res == NULL)
2128 6 : PG_RETURN_NULL();
2129 :
2130 1604 : PG_RETURN_TEXT_P(string_to_text(res));
2131 : }
2132 :
2133 : Datum
2134 3366 : pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
2135 : {
2136 3366 : Oid constraintId = PG_GETARG_OID(0);
2137 3366 : bool pretty = PG_GETARG_BOOL(1);
2138 : int prettyFlags;
2139 : char *res;
2140 :
2141 3366 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2142 :
2143 3366 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2144 :
2145 3366 : if (res == NULL)
2146 0 : PG_RETURN_NULL();
2147 :
2148 3366 : PG_RETURN_TEXT_P(string_to_text(res));
2149 : }
2150 :
2151 : /*
2152 : * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
2153 : */
2154 : char *
2155 404 : pg_get_constraintdef_command(Oid constraintId)
2156 : {
2157 404 : return pg_get_constraintdef_worker(constraintId, true, 0, false);
2158 : }
2159 :
2160 : /*
2161 : * As of 9.4, we now use an MVCC snapshot for this.
2162 : */
2163 : static char *
2164 5380 : pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
2165 : int prettyFlags, bool missing_ok)
2166 : {
2167 : HeapTuple tup;
2168 : Form_pg_constraint conForm;
2169 : StringInfoData buf;
2170 : SysScanDesc scandesc;
2171 : ScanKeyData scankey[1];
2172 5380 : Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());
2173 5380 : Relation relation = table_open(ConstraintRelationId, AccessShareLock);
2174 :
2175 5380 : ScanKeyInit(&scankey[0],
2176 : Anum_pg_constraint_oid,
2177 : BTEqualStrategyNumber, F_OIDEQ,
2178 : ObjectIdGetDatum(constraintId));
2179 :
2180 5380 : scandesc = systable_beginscan(relation,
2181 : ConstraintOidIndexId,
2182 : true,
2183 : snapshot,
2184 : 1,
2185 : scankey);
2186 :
2187 : /*
2188 : * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2189 : * via SearchSysCache, which works fine.
2190 : */
2191 5380 : tup = systable_getnext(scandesc);
2192 :
2193 5380 : UnregisterSnapshot(snapshot);
2194 :
2195 5380 : if (!HeapTupleIsValid(tup))
2196 : {
2197 6 : if (missing_ok)
2198 : {
2199 6 : systable_endscan(scandesc);
2200 6 : table_close(relation, AccessShareLock);
2201 6 : return NULL;
2202 : }
2203 0 : elog(ERROR, "could not find tuple for constraint %u", constraintId);
2204 : }
2205 :
2206 5374 : conForm = (Form_pg_constraint) GETSTRUCT(tup);
2207 :
2208 5374 : initStringInfo(&buf);
2209 :
2210 5374 : if (fullCommand)
2211 : {
2212 404 : if (OidIsValid(conForm->conrelid))
2213 : {
2214 : /*
2215 : * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2216 : * constraints, and other types of constraints don't inherit
2217 : * anyway so it doesn't matter whether we say ONLY or not. Someday
2218 : * we might need to let callers specify whether to put ONLY in the
2219 : * command.
2220 : */
2221 390 : appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2222 : generate_qualified_relation_name(conForm->conrelid),
2223 390 : quote_identifier(NameStr(conForm->conname)));
2224 : }
2225 : else
2226 : {
2227 : /* Must be a domain constraint */
2228 : Assert(OidIsValid(conForm->contypid));
2229 14 : appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2230 : generate_qualified_type_name(conForm->contypid),
2231 14 : quote_identifier(NameStr(conForm->conname)));
2232 : }
2233 : }
2234 :
2235 5374 : switch (conForm->contype)
2236 : {
2237 616 : case CONSTRAINT_FOREIGN:
2238 : {
2239 : Datum val;
2240 : bool isnull;
2241 : const char *string;
2242 :
2243 : /* Start off the constraint definition */
2244 616 : appendStringInfoString(&buf, "FOREIGN KEY (");
2245 :
2246 : /* Fetch and build referencing-column list */
2247 616 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2248 : Anum_pg_constraint_conkey);
2249 :
2250 616 : decompile_column_index_array(val, conForm->conrelid, &buf);
2251 :
2252 : /* add foreign relation name */
2253 616 : appendStringInfo(&buf, ") REFERENCES %s(",
2254 : generate_relation_name(conForm->confrelid,
2255 : NIL));
2256 :
2257 : /* Fetch and build referenced-column list */
2258 616 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2259 : Anum_pg_constraint_confkey);
2260 :
2261 616 : decompile_column_index_array(val, conForm->confrelid, &buf);
2262 :
2263 616 : appendStringInfoChar(&buf, ')');
2264 :
2265 : /* Add match type */
2266 616 : switch (conForm->confmatchtype)
2267 : {
2268 34 : case FKCONSTR_MATCH_FULL:
2269 34 : string = " MATCH FULL";
2270 34 : break;
2271 0 : case FKCONSTR_MATCH_PARTIAL:
2272 0 : string = " MATCH PARTIAL";
2273 0 : break;
2274 582 : case FKCONSTR_MATCH_SIMPLE:
2275 582 : string = "";
2276 582 : break;
2277 0 : default:
2278 0 : elog(ERROR, "unrecognized confmatchtype: %d",
2279 : conForm->confmatchtype);
2280 : string = ""; /* keep compiler quiet */
2281 : break;
2282 : }
2283 616 : appendStringInfoString(&buf, string);
2284 :
2285 : /* Add ON UPDATE and ON DELETE clauses, if needed */
2286 616 : switch (conForm->confupdtype)
2287 : {
2288 492 : case FKCONSTR_ACTION_NOACTION:
2289 492 : string = NULL; /* suppress default */
2290 492 : break;
2291 0 : case FKCONSTR_ACTION_RESTRICT:
2292 0 : string = "RESTRICT";
2293 0 : break;
2294 96 : case FKCONSTR_ACTION_CASCADE:
2295 96 : string = "CASCADE";
2296 96 : break;
2297 28 : case FKCONSTR_ACTION_SETNULL:
2298 28 : string = "SET NULL";
2299 28 : break;
2300 0 : case FKCONSTR_ACTION_SETDEFAULT:
2301 0 : string = "SET DEFAULT";
2302 0 : break;
2303 0 : default:
2304 0 : elog(ERROR, "unrecognized confupdtype: %d",
2305 : conForm->confupdtype);
2306 : string = NULL; /* keep compiler quiet */
2307 : break;
2308 : }
2309 616 : if (string)
2310 124 : appendStringInfo(&buf, " ON UPDATE %s", string);
2311 :
2312 616 : switch (conForm->confdeltype)
2313 : {
2314 496 : case FKCONSTR_ACTION_NOACTION:
2315 496 : string = NULL; /* suppress default */
2316 496 : break;
2317 0 : case FKCONSTR_ACTION_RESTRICT:
2318 0 : string = "RESTRICT";
2319 0 : break;
2320 96 : case FKCONSTR_ACTION_CASCADE:
2321 96 : string = "CASCADE";
2322 96 : break;
2323 18 : case FKCONSTR_ACTION_SETNULL:
2324 18 : string = "SET NULL";
2325 18 : break;
2326 6 : case FKCONSTR_ACTION_SETDEFAULT:
2327 6 : string = "SET DEFAULT";
2328 6 : break;
2329 0 : default:
2330 0 : elog(ERROR, "unrecognized confdeltype: %d",
2331 : conForm->confdeltype);
2332 : string = NULL; /* keep compiler quiet */
2333 : break;
2334 : }
2335 616 : if (string)
2336 120 : appendStringInfo(&buf, " ON DELETE %s", string);
2337 :
2338 : /*
2339 : * Add columns specified to SET NULL or SET DEFAULT if
2340 : * provided.
2341 : */
2342 616 : val = SysCacheGetAttr(CONSTROID, tup,
2343 : Anum_pg_constraint_confdelsetcols, &isnull);
2344 616 : if (!isnull)
2345 : {
2346 12 : appendStringInfoString(&buf, " (");
2347 12 : decompile_column_index_array(val, conForm->conrelid, &buf);
2348 12 : appendStringInfoChar(&buf, ')');
2349 : }
2350 :
2351 616 : break;
2352 : }
2353 2704 : case CONSTRAINT_PRIMARY:
2354 : case CONSTRAINT_UNIQUE:
2355 : {
2356 : Datum val;
2357 : Oid indexId;
2358 : int keyatts;
2359 : HeapTuple indtup;
2360 :
2361 : /* Start off the constraint definition */
2362 2704 : if (conForm->contype == CONSTRAINT_PRIMARY)
2363 2284 : appendStringInfoString(&buf, "PRIMARY KEY ");
2364 : else
2365 420 : appendStringInfoString(&buf, "UNIQUE ");
2366 :
2367 2704 : indexId = conForm->conindid;
2368 :
2369 2704 : indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
2370 2704 : if (!HeapTupleIsValid(indtup))
2371 0 : elog(ERROR, "cache lookup failed for index %u", indexId);
2372 2704 : if (conForm->contype == CONSTRAINT_UNIQUE &&
2373 420 : ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
2374 0 : appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2375 :
2376 2704 : appendStringInfoChar(&buf, '(');
2377 :
2378 : /* Fetch and build target column list */
2379 2704 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2380 : Anum_pg_constraint_conkey);
2381 :
2382 2704 : keyatts = decompile_column_index_array(val, conForm->conrelid, &buf);
2383 :
2384 2704 : appendStringInfoChar(&buf, ')');
2385 :
2386 : /* Build including column list (from pg_index.indkeys) */
2387 2704 : val = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2388 : Anum_pg_index_indnatts);
2389 2704 : if (DatumGetInt32(val) > keyatts)
2390 : {
2391 : Datum cols;
2392 : Datum *keys;
2393 : int nKeys;
2394 : int j;
2395 :
2396 82 : appendStringInfoString(&buf, " INCLUDE (");
2397 :
2398 82 : cols = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2399 : Anum_pg_index_indkey);
2400 :
2401 82 : deconstruct_array_builtin(DatumGetArrayTypeP(cols), INT2OID,
2402 : &keys, NULL, &nKeys);
2403 :
2404 246 : for (j = keyatts; j < nKeys; j++)
2405 : {
2406 : char *colName;
2407 :
2408 164 : colName = get_attname(conForm->conrelid,
2409 164 : DatumGetInt16(keys[j]), false);
2410 164 : if (j > keyatts)
2411 82 : appendStringInfoString(&buf, ", ");
2412 164 : appendStringInfoString(&buf, quote_identifier(colName));
2413 : }
2414 :
2415 82 : appendStringInfoChar(&buf, ')');
2416 : }
2417 2704 : ReleaseSysCache(indtup);
2418 :
2419 : /* XXX why do we only print these bits if fullCommand? */
2420 2704 : if (fullCommand && OidIsValid(indexId))
2421 : {
2422 180 : char *options = flatten_reloptions(indexId);
2423 : Oid tblspc;
2424 :
2425 180 : if (options)
2426 : {
2427 0 : appendStringInfo(&buf, " WITH (%s)", options);
2428 0 : pfree(options);
2429 : }
2430 :
2431 : /*
2432 : * Print the tablespace, unless it's the database default.
2433 : * This is to help ALTER TABLE usage of this facility,
2434 : * which needs this behavior to recreate exact catalog
2435 : * state.
2436 : */
2437 180 : tblspc = get_rel_tablespace(indexId);
2438 180 : if (OidIsValid(tblspc))
2439 24 : appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2440 24 : quote_identifier(get_tablespace_name(tblspc)));
2441 : }
2442 :
2443 2704 : break;
2444 : }
2445 1896 : case CONSTRAINT_CHECK:
2446 : {
2447 : Datum val;
2448 : char *conbin;
2449 : char *consrc;
2450 : Node *expr;
2451 : List *context;
2452 :
2453 : /* Fetch constraint expression in parsetree form */
2454 1896 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2455 : Anum_pg_constraint_conbin);
2456 :
2457 1896 : conbin = TextDatumGetCString(val);
2458 1896 : expr = stringToNode(conbin);
2459 :
2460 : /* Set up deparsing context for Var nodes in constraint */
2461 1896 : if (conForm->conrelid != InvalidOid)
2462 : {
2463 : /* relation constraint */
2464 1720 : context = deparse_context_for(get_relation_name(conForm->conrelid),
2465 : conForm->conrelid);
2466 : }
2467 : else
2468 : {
2469 : /* domain constraint --- can't have Vars */
2470 176 : context = NIL;
2471 : }
2472 :
2473 1896 : consrc = deparse_expression_pretty(expr, context, false, false,
2474 : prettyFlags, 0);
2475 :
2476 : /*
2477 : * Now emit the constraint definition, adding NO INHERIT if
2478 : * necessary.
2479 : *
2480 : * There are cases where the constraint expression will be
2481 : * fully parenthesized and we don't need the outer parens ...
2482 : * but there are other cases where we do need 'em. Be
2483 : * conservative for now.
2484 : *
2485 : * Note that simply checking for leading '(' and trailing ')'
2486 : * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2487 : */
2488 1896 : appendStringInfo(&buf, "CHECK (%s)%s",
2489 : consrc,
2490 1896 : conForm->connoinherit ? " NO INHERIT" : "");
2491 1896 : break;
2492 : }
2493 54 : case CONSTRAINT_NOTNULL:
2494 : {
2495 : AttrNumber attnum;
2496 :
2497 54 : attnum = extractNotNullColumn(tup);
2498 :
2499 54 : appendStringInfo(&buf, "NOT NULL %s",
2500 54 : quote_identifier(get_attname(conForm->conrelid,
2501 : attnum, false)));
2502 54 : if (((Form_pg_constraint) GETSTRUCT(tup))->connoinherit)
2503 0 : appendStringInfoString(&buf, " NO INHERIT");
2504 54 : break;
2505 : }
2506 :
2507 0 : case CONSTRAINT_TRIGGER:
2508 :
2509 : /*
2510 : * There isn't an ALTER TABLE syntax for creating a user-defined
2511 : * constraint trigger, but it seems better to print something than
2512 : * throw an error; if we throw error then this function couldn't
2513 : * safely be applied to all rows of pg_constraint.
2514 : */
2515 0 : appendStringInfoString(&buf, "TRIGGER");
2516 0 : break;
2517 104 : case CONSTRAINT_EXCLUSION:
2518 : {
2519 104 : Oid indexOid = conForm->conindid;
2520 : Datum val;
2521 : Datum *elems;
2522 : int nElems;
2523 : int i;
2524 : Oid *operators;
2525 :
2526 : /* Extract operator OIDs from the pg_constraint tuple */
2527 104 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2528 : Anum_pg_constraint_conexclop);
2529 :
2530 104 : deconstruct_array_builtin(DatumGetArrayTypeP(val), OIDOID,
2531 : &elems, NULL, &nElems);
2532 :
2533 104 : operators = (Oid *) palloc(nElems * sizeof(Oid));
2534 228 : for (i = 0; i < nElems; i++)
2535 124 : operators[i] = DatumGetObjectId(elems[i]);
2536 :
2537 : /* pg_get_indexdef_worker does the rest */
2538 : /* suppress tablespace because pg_dump wants it that way */
2539 104 : appendStringInfoString(&buf,
2540 104 : pg_get_indexdef_worker(indexOid,
2541 : 0,
2542 : operators,
2543 : false,
2544 : false,
2545 : false,
2546 : false,
2547 : prettyFlags,
2548 : false));
2549 104 : break;
2550 : }
2551 0 : default:
2552 0 : elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2553 : break;
2554 : }
2555 :
2556 5374 : if (conForm->condeferrable)
2557 40 : appendStringInfoString(&buf, " DEFERRABLE");
2558 5374 : if (conForm->condeferred)
2559 0 : appendStringInfoString(&buf, " INITIALLY DEFERRED");
2560 5374 : if (!conForm->convalidated)
2561 94 : appendStringInfoString(&buf, " NOT VALID");
2562 :
2563 : /* Cleanup */
2564 5374 : systable_endscan(scandesc);
2565 5374 : table_close(relation, AccessShareLock);
2566 :
2567 5374 : return buf.data;
2568 : }
2569 :
2570 :
2571 : /*
2572 : * Convert an int16[] Datum into a comma-separated list of column names
2573 : * for the indicated relation; append the list to buf. Returns the number
2574 : * of keys.
2575 : */
2576 : static int
2577 3948 : decompile_column_index_array(Datum column_index_array, Oid relId,
2578 : StringInfo buf)
2579 : {
2580 : Datum *keys;
2581 : int nKeys;
2582 : int j;
2583 :
2584 : /* Extract data from array of int16 */
2585 3948 : deconstruct_array_builtin(DatumGetArrayTypeP(column_index_array), INT2OID,
2586 : &keys, NULL, &nKeys);
2587 :
2588 9134 : for (j = 0; j < nKeys; j++)
2589 : {
2590 : char *colName;
2591 :
2592 5186 : colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2593 :
2594 5186 : if (j == 0)
2595 3948 : appendStringInfoString(buf, quote_identifier(colName));
2596 : else
2597 1238 : appendStringInfo(buf, ", %s", quote_identifier(colName));
2598 : }
2599 :
2600 3948 : return nKeys;
2601 : }
2602 :
2603 :
2604 : /* ----------
2605 : * pg_get_expr - Decompile an expression tree
2606 : *
2607 : * Input: an expression tree in nodeToString form, and a relation OID
2608 : *
2609 : * Output: reverse-listed expression
2610 : *
2611 : * Currently, the expression can only refer to a single relation, namely
2612 : * the one specified by the second parameter. This is sufficient for
2613 : * partial indexes, column default expressions, etc. We also support
2614 : * Var-free expressions, for which the OID can be InvalidOid.
2615 : *
2616 : * We expect this function to work, or throw a reasonably clean error,
2617 : * for any node tree that can appear in a catalog pg_node_tree column.
2618 : * Query trees, such as those appearing in pg_rewrite.ev_action, are
2619 : * not supported. Nor are expressions in more than one relation, which
2620 : * can appear in places like pg_rewrite.ev_qual.
2621 : * ----------
2622 : */
2623 : Datum
2624 6582 : pg_get_expr(PG_FUNCTION_ARGS)
2625 : {
2626 6582 : text *expr = PG_GETARG_TEXT_PP(0);
2627 6582 : Oid relid = PG_GETARG_OID(1);
2628 : int prettyFlags;
2629 : char *relname;
2630 :
2631 6582 : prettyFlags = PRETTYFLAG_INDENT;
2632 :
2633 6582 : if (OidIsValid(relid))
2634 : {
2635 : /* Get the name for the relation */
2636 6582 : relname = get_rel_name(relid);
2637 :
2638 : /*
2639 : * If the OID isn't actually valid, don't throw an error, just return
2640 : * NULL. This is a bit questionable, but it's what we've done
2641 : * historically, and it can help avoid unwanted failures when
2642 : * examining catalog entries for just-deleted relations.
2643 : */
2644 6582 : if (relname == NULL)
2645 0 : PG_RETURN_NULL();
2646 : }
2647 : else
2648 0 : relname = NULL;
2649 :
2650 6582 : PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, prettyFlags));
2651 : }
2652 :
2653 : Datum
2654 406 : pg_get_expr_ext(PG_FUNCTION_ARGS)
2655 : {
2656 406 : text *expr = PG_GETARG_TEXT_PP(0);
2657 406 : Oid relid = PG_GETARG_OID(1);
2658 406 : bool pretty = PG_GETARG_BOOL(2);
2659 : int prettyFlags;
2660 : char *relname;
2661 :
2662 406 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2663 :
2664 406 : if (OidIsValid(relid))
2665 : {
2666 : /* Get the name for the relation */
2667 406 : relname = get_rel_name(relid);
2668 : /* See notes above */
2669 406 : if (relname == NULL)
2670 0 : PG_RETURN_NULL();
2671 : }
2672 : else
2673 0 : relname = NULL;
2674 :
2675 406 : PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, prettyFlags));
2676 : }
2677 :
2678 : static text *
2679 6988 : pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags)
2680 : {
2681 : Node *node;
2682 : Node *tst;
2683 : Relids relids;
2684 : List *context;
2685 : char *exprstr;
2686 : char *str;
2687 :
2688 : /* Convert input pg_node_tree (really TEXT) object to C string */
2689 6988 : exprstr = text_to_cstring(expr);
2690 :
2691 : /* Convert expression to node tree */
2692 6988 : node = (Node *) stringToNode(exprstr);
2693 :
2694 6988 : pfree(exprstr);
2695 :
2696 : /*
2697 : * Throw error if the input is a querytree rather than an expression tree.
2698 : * While we could support queries here, there seems no very good reason
2699 : * to. In most such catalog columns, we'll see a List of Query nodes, or
2700 : * even nested Lists, so drill down to a non-List node before checking.
2701 : */
2702 6988 : tst = node;
2703 6988 : while (tst && IsA(tst, List))
2704 0 : tst = linitial((List *) tst);
2705 6988 : if (tst && IsA(tst, Query))
2706 0 : ereport(ERROR,
2707 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2708 : errmsg("input is a query, not an expression")));
2709 :
2710 : /*
2711 : * Throw error if the expression contains Vars we won't be able to
2712 : * deparse.
2713 : */
2714 6988 : relids = pull_varnos(NULL, node);
2715 6988 : if (OidIsValid(relid))
2716 : {
2717 6988 : if (!bms_is_subset(relids, bms_make_singleton(1)))
2718 0 : ereport(ERROR,
2719 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2720 : errmsg("expression contains variables of more than one relation")));
2721 : }
2722 : else
2723 : {
2724 0 : if (!bms_is_empty(relids))
2725 0 : ereport(ERROR,
2726 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2727 : errmsg("expression contains variables")));
2728 : }
2729 :
2730 : /* Prepare deparse context if needed */
2731 6988 : if (OidIsValid(relid))
2732 6988 : context = deparse_context_for(relname, relid);
2733 : else
2734 0 : context = NIL;
2735 :
2736 : /* Deparse */
2737 6988 : str = deparse_expression_pretty(node, context, false, false,
2738 : prettyFlags, 0);
2739 :
2740 6988 : return string_to_text(str);
2741 : }
2742 :
2743 :
2744 : /* ----------
2745 : * pg_get_userbyid - Get a user name by roleid and
2746 : * fallback to 'unknown (OID=n)'
2747 : * ----------
2748 : */
2749 : Datum
2750 1576 : pg_get_userbyid(PG_FUNCTION_ARGS)
2751 : {
2752 1576 : Oid roleid = PG_GETARG_OID(0);
2753 : Name result;
2754 : HeapTuple roletup;
2755 : Form_pg_authid role_rec;
2756 :
2757 : /*
2758 : * Allocate space for the result
2759 : */
2760 1576 : result = (Name) palloc(NAMEDATALEN);
2761 1576 : memset(NameStr(*result), 0, NAMEDATALEN);
2762 :
2763 : /*
2764 : * Get the pg_authid entry and print the result
2765 : */
2766 1576 : roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
2767 1576 : if (HeapTupleIsValid(roletup))
2768 : {
2769 1576 : role_rec = (Form_pg_authid) GETSTRUCT(roletup);
2770 1576 : *result = role_rec->rolname;
2771 1576 : ReleaseSysCache(roletup);
2772 : }
2773 : else
2774 0 : sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
2775 :
2776 1576 : PG_RETURN_NAME(result);
2777 : }
2778 :
2779 :
2780 : /*
2781 : * pg_get_serial_sequence
2782 : * Get the name of the sequence used by an identity or serial column,
2783 : * formatted suitably for passing to setval, nextval or currval.
2784 : * First parameter is not treated as double-quoted, second parameter
2785 : * is --- see documentation for reason.
2786 : */
2787 : Datum
2788 12 : pg_get_serial_sequence(PG_FUNCTION_ARGS)
2789 : {
2790 12 : text *tablename = PG_GETARG_TEXT_PP(0);
2791 12 : text *columnname = PG_GETARG_TEXT_PP(1);
2792 : RangeVar *tablerv;
2793 : Oid tableOid;
2794 : char *column;
2795 : AttrNumber attnum;
2796 12 : Oid sequenceId = InvalidOid;
2797 : Relation depRel;
2798 : ScanKeyData key[3];
2799 : SysScanDesc scan;
2800 : HeapTuple tup;
2801 :
2802 : /* Look up table name. Can't lock it - we might not have privileges. */
2803 12 : tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
2804 12 : tableOid = RangeVarGetRelid(tablerv, NoLock, false);
2805 :
2806 : /* Get the number of the column */
2807 12 : column = text_to_cstring(columnname);
2808 :
2809 12 : attnum = get_attnum(tableOid, column);
2810 12 : if (attnum == InvalidAttrNumber)
2811 0 : ereport(ERROR,
2812 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2813 : errmsg("column \"%s\" of relation \"%s\" does not exist",
2814 : column, tablerv->relname)));
2815 :
2816 : /* Search the dependency table for the dependent sequence */
2817 12 : depRel = table_open(DependRelationId, AccessShareLock);
2818 :
2819 12 : ScanKeyInit(&key[0],
2820 : Anum_pg_depend_refclassid,
2821 : BTEqualStrategyNumber, F_OIDEQ,
2822 : ObjectIdGetDatum(RelationRelationId));
2823 12 : ScanKeyInit(&key[1],
2824 : Anum_pg_depend_refobjid,
2825 : BTEqualStrategyNumber, F_OIDEQ,
2826 : ObjectIdGetDatum(tableOid));
2827 12 : ScanKeyInit(&key[2],
2828 : Anum_pg_depend_refobjsubid,
2829 : BTEqualStrategyNumber, F_INT4EQ,
2830 : Int32GetDatum(attnum));
2831 :
2832 12 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
2833 : NULL, 3, key);
2834 :
2835 30 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
2836 : {
2837 30 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
2838 :
2839 : /*
2840 : * Look for an auto dependency (serial column) or internal dependency
2841 : * (identity column) of a sequence on a column. (We need the relkind
2842 : * test because indexes can also have auto dependencies on columns.)
2843 : */
2844 30 : if (deprec->classid == RelationRelationId &&
2845 12 : deprec->objsubid == 0 &&
2846 12 : (deprec->deptype == DEPENDENCY_AUTO ||
2847 18 : deprec->deptype == DEPENDENCY_INTERNAL) &&
2848 12 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
2849 : {
2850 12 : sequenceId = deprec->objid;
2851 12 : break;
2852 : }
2853 : }
2854 :
2855 12 : systable_endscan(scan);
2856 12 : table_close(depRel, AccessShareLock);
2857 :
2858 12 : if (OidIsValid(sequenceId))
2859 : {
2860 : char *result;
2861 :
2862 12 : result = generate_qualified_relation_name(sequenceId);
2863 :
2864 12 : PG_RETURN_TEXT_P(string_to_text(result));
2865 : }
2866 :
2867 0 : PG_RETURN_NULL();
2868 : }
2869 :
2870 :
2871 : /*
2872 : * pg_get_functiondef
2873 : * Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
2874 : * the specified function.
2875 : *
2876 : * Note: if you change the output format of this function, be careful not
2877 : * to break psql's rules (in \ef and \sf) for identifying the start of the
2878 : * function body. To wit: the function body starts on a line that begins with
2879 : * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
2880 : */
2881 : Datum
2882 154 : pg_get_functiondef(PG_FUNCTION_ARGS)
2883 : {
2884 154 : Oid funcid = PG_GETARG_OID(0);
2885 : StringInfoData buf;
2886 : StringInfoData dq;
2887 : HeapTuple proctup;
2888 : Form_pg_proc proc;
2889 : bool isfunction;
2890 : Datum tmp;
2891 : bool isnull;
2892 : const char *prosrc;
2893 : const char *name;
2894 : const char *nsp;
2895 : float4 procost;
2896 : int oldlen;
2897 :
2898 154 : initStringInfo(&buf);
2899 :
2900 : /* Look up the function */
2901 154 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
2902 154 : if (!HeapTupleIsValid(proctup))
2903 6 : PG_RETURN_NULL();
2904 :
2905 148 : proc = (Form_pg_proc) GETSTRUCT(proctup);
2906 148 : name = NameStr(proc->proname);
2907 :
2908 148 : if (proc->prokind == PROKIND_AGGREGATE)
2909 0 : ereport(ERROR,
2910 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2911 : errmsg("\"%s\" is an aggregate function", name)));
2912 :
2913 148 : isfunction = (proc->prokind != PROKIND_PROCEDURE);
2914 :
2915 : /*
2916 : * We always qualify the function name, to ensure the right function gets
2917 : * replaced.
2918 : */
2919 148 : nsp = get_namespace_name_or_temp(proc->pronamespace);
2920 148 : appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
2921 : isfunction ? "FUNCTION" : "PROCEDURE",
2922 : quote_qualified_identifier(nsp, name));
2923 148 : (void) print_function_arguments(&buf, proctup, false, true);
2924 148 : appendStringInfoString(&buf, ")\n");
2925 148 : if (isfunction)
2926 : {
2927 130 : appendStringInfoString(&buf, " RETURNS ");
2928 130 : print_function_rettype(&buf, proctup);
2929 130 : appendStringInfoChar(&buf, '\n');
2930 : }
2931 :
2932 148 : print_function_trftypes(&buf, proctup);
2933 :
2934 148 : appendStringInfo(&buf, " LANGUAGE %s\n",
2935 148 : quote_identifier(get_language_name(proc->prolang, false)));
2936 :
2937 : /* Emit some miscellaneous options on one line */
2938 148 : oldlen = buf.len;
2939 :
2940 148 : if (proc->prokind == PROKIND_WINDOW)
2941 0 : appendStringInfoString(&buf, " WINDOW");
2942 148 : switch (proc->provolatile)
2943 : {
2944 24 : case PROVOLATILE_IMMUTABLE:
2945 24 : appendStringInfoString(&buf, " IMMUTABLE");
2946 24 : break;
2947 18 : case PROVOLATILE_STABLE:
2948 18 : appendStringInfoString(&buf, " STABLE");
2949 18 : break;
2950 106 : case PROVOLATILE_VOLATILE:
2951 106 : break;
2952 : }
2953 :
2954 148 : switch (proc->proparallel)
2955 : {
2956 40 : case PROPARALLEL_SAFE:
2957 40 : appendStringInfoString(&buf, " PARALLEL SAFE");
2958 40 : break;
2959 0 : case PROPARALLEL_RESTRICTED:
2960 0 : appendStringInfoString(&buf, " PARALLEL RESTRICTED");
2961 0 : break;
2962 108 : case PROPARALLEL_UNSAFE:
2963 108 : break;
2964 : }
2965 :
2966 148 : if (proc->proisstrict)
2967 50 : appendStringInfoString(&buf, " STRICT");
2968 148 : if (proc->prosecdef)
2969 6 : appendStringInfoString(&buf, " SECURITY DEFINER");
2970 148 : if (proc->proleakproof)
2971 0 : appendStringInfoString(&buf, " LEAKPROOF");
2972 :
2973 : /* This code for the default cost and rows should match functioncmds.c */
2974 148 : if (proc->prolang == INTERNALlanguageId ||
2975 148 : proc->prolang == ClanguageId)
2976 10 : procost = 1;
2977 : else
2978 138 : procost = 100;
2979 148 : if (proc->procost != procost)
2980 6 : appendStringInfo(&buf, " COST %g", proc->procost);
2981 :
2982 148 : if (proc->prorows > 0 && proc->prorows != 1000)
2983 0 : appendStringInfo(&buf, " ROWS %g", proc->prorows);
2984 :
2985 148 : if (proc->prosupport)
2986 : {
2987 : Oid argtypes[1];
2988 :
2989 : /*
2990 : * We should qualify the support function's name if it wouldn't be
2991 : * resolved by lookup in the current search path.
2992 : */
2993 0 : argtypes[0] = INTERNALOID;
2994 0 : appendStringInfo(&buf, " SUPPORT %s",
2995 : generate_function_name(proc->prosupport, 1,
2996 : NIL, argtypes,
2997 : false, NULL, EXPR_KIND_NONE));
2998 : }
2999 :
3000 148 : if (oldlen != buf.len)
3001 64 : appendStringInfoChar(&buf, '\n');
3002 :
3003 : /* Emit any proconfig options, one per line */
3004 148 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
3005 148 : if (!isnull)
3006 : {
3007 6 : ArrayType *a = DatumGetArrayTypeP(tmp);
3008 : int i;
3009 :
3010 : Assert(ARR_ELEMTYPE(a) == TEXTOID);
3011 : Assert(ARR_NDIM(a) == 1);
3012 : Assert(ARR_LBOUND(a)[0] == 1);
3013 :
3014 36 : for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3015 : {
3016 : Datum d;
3017 :
3018 30 : d = array_ref(a, 1, &i,
3019 : -1 /* varlenarray */ ,
3020 : -1 /* TEXT's typlen */ ,
3021 : false /* TEXT's typbyval */ ,
3022 : TYPALIGN_INT /* TEXT's typalign */ ,
3023 : &isnull);
3024 30 : if (!isnull)
3025 : {
3026 30 : char *configitem = TextDatumGetCString(d);
3027 : char *pos;
3028 :
3029 30 : pos = strchr(configitem, '=');
3030 30 : if (pos == NULL)
3031 0 : continue;
3032 30 : *pos++ = '\0';
3033 :
3034 30 : appendStringInfo(&buf, " SET %s TO ",
3035 : quote_identifier(configitem));
3036 :
3037 : /*
3038 : * Variables that are marked GUC_LIST_QUOTE were already fully
3039 : * quoted by flatten_set_variable_args() before they were put
3040 : * into the proconfig array. However, because the quoting
3041 : * rules used there aren't exactly like SQL's, we have to
3042 : * break the list value apart and then quote the elements as
3043 : * string literals. (The elements may be double-quoted as-is,
3044 : * but we can't just feed them to the SQL parser; it would do
3045 : * the wrong thing with elements that are zero-length or
3046 : * longer than NAMEDATALEN.)
3047 : *
3048 : * Variables that are not so marked should just be emitted as
3049 : * simple string literals. If the variable is not known to
3050 : * guc.c, we'll do that; this makes it unsafe to use
3051 : * GUC_LIST_QUOTE for extension variables.
3052 : */
3053 30 : if (GetConfigOptionFlags(configitem, true) & GUC_LIST_QUOTE)
3054 : {
3055 : List *namelist;
3056 : ListCell *lc;
3057 :
3058 : /* Parse string into list of identifiers */
3059 12 : if (!SplitGUCList(pos, ',', &namelist))
3060 : {
3061 : /* this shouldn't fail really */
3062 0 : elog(ERROR, "invalid list syntax in proconfig item");
3063 : }
3064 42 : foreach(lc, namelist)
3065 : {
3066 30 : char *curname = (char *) lfirst(lc);
3067 :
3068 30 : simple_quote_literal(&buf, curname);
3069 30 : if (lnext(namelist, lc))
3070 18 : appendStringInfoString(&buf, ", ");
3071 : }
3072 : }
3073 : else
3074 18 : simple_quote_literal(&buf, pos);
3075 30 : appendStringInfoChar(&buf, '\n');
3076 : }
3077 : }
3078 : }
3079 :
3080 : /* And finally the function definition ... */
3081 148 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3082 148 : if (proc->prolang == SQLlanguageId && !isnull)
3083 : {
3084 84 : print_function_sqlbody(&buf, proctup);
3085 : }
3086 : else
3087 : {
3088 64 : appendStringInfoString(&buf, "AS ");
3089 :
3090 64 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
3091 64 : if (!isnull)
3092 : {
3093 10 : simple_quote_literal(&buf, TextDatumGetCString(tmp));
3094 10 : appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3095 : }
3096 :
3097 64 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosrc);
3098 64 : prosrc = TextDatumGetCString(tmp);
3099 :
3100 : /*
3101 : * We always use dollar quoting. Figure out a suitable delimiter.
3102 : *
3103 : * Since the user is likely to be editing the function body string, we
3104 : * shouldn't use a short delimiter that he might easily create a
3105 : * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3106 : * if needed.
3107 : */
3108 64 : initStringInfo(&dq);
3109 64 : appendStringInfoChar(&dq, '$');
3110 64 : appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3111 64 : while (strstr(prosrc, dq.data) != NULL)
3112 0 : appendStringInfoChar(&dq, 'x');
3113 64 : appendStringInfoChar(&dq, '$');
3114 :
3115 64 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3116 64 : appendStringInfoString(&buf, prosrc);
3117 64 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3118 : }
3119 :
3120 148 : appendStringInfoChar(&buf, '\n');
3121 :
3122 148 : ReleaseSysCache(proctup);
3123 :
3124 148 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3125 : }
3126 :
3127 : /*
3128 : * pg_get_function_arguments
3129 : * Get a nicely-formatted list of arguments for a function.
3130 : * This is everything that would go between the parentheses in
3131 : * CREATE FUNCTION.
3132 : */
3133 : Datum
3134 4522 : pg_get_function_arguments(PG_FUNCTION_ARGS)
3135 : {
3136 4522 : Oid funcid = PG_GETARG_OID(0);
3137 : StringInfoData buf;
3138 : HeapTuple proctup;
3139 :
3140 4522 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3141 4522 : if (!HeapTupleIsValid(proctup))
3142 6 : PG_RETURN_NULL();
3143 :
3144 4516 : initStringInfo(&buf);
3145 :
3146 4516 : (void) print_function_arguments(&buf, proctup, false, true);
3147 :
3148 4516 : ReleaseSysCache(proctup);
3149 :
3150 4516 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3151 : }
3152 :
3153 : /*
3154 : * pg_get_function_identity_arguments
3155 : * Get a formatted list of arguments for a function.
3156 : * This is everything that would go between the parentheses in
3157 : * ALTER FUNCTION, etc. In particular, don't print defaults.
3158 : */
3159 : Datum
3160 4002 : pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
3161 : {
3162 4002 : Oid funcid = PG_GETARG_OID(0);
3163 : StringInfoData buf;
3164 : HeapTuple proctup;
3165 :
3166 4002 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3167 4002 : if (!HeapTupleIsValid(proctup))
3168 6 : PG_RETURN_NULL();
3169 :
3170 3996 : initStringInfo(&buf);
3171 :
3172 3996 : (void) print_function_arguments(&buf, proctup, false, false);
3173 :
3174 3996 : ReleaseSysCache(proctup);
3175 :
3176 3996 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3177 : }
3178 :
3179 : /*
3180 : * pg_get_function_result
3181 : * Get a nicely-formatted version of the result type of a function.
3182 : * This is what would appear after RETURNS in CREATE FUNCTION.
3183 : */
3184 : Datum
3185 3936 : pg_get_function_result(PG_FUNCTION_ARGS)
3186 : {
3187 3936 : Oid funcid = PG_GETARG_OID(0);
3188 : StringInfoData buf;
3189 : HeapTuple proctup;
3190 :
3191 3936 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3192 3936 : if (!HeapTupleIsValid(proctup))
3193 6 : PG_RETURN_NULL();
3194 :
3195 3930 : if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
3196 : {
3197 210 : ReleaseSysCache(proctup);
3198 210 : PG_RETURN_NULL();
3199 : }
3200 :
3201 3720 : initStringInfo(&buf);
3202 :
3203 3720 : print_function_rettype(&buf, proctup);
3204 :
3205 3720 : ReleaseSysCache(proctup);
3206 :
3207 3720 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3208 : }
3209 :
3210 : /*
3211 : * Guts of pg_get_function_result: append the function's return type
3212 : * to the specified buffer.
3213 : */
3214 : static void
3215 3850 : print_function_rettype(StringInfo buf, HeapTuple proctup)
3216 : {
3217 3850 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3218 3850 : int ntabargs = 0;
3219 : StringInfoData rbuf;
3220 :
3221 3850 : initStringInfo(&rbuf);
3222 :
3223 3850 : if (proc->proretset)
3224 : {
3225 : /* It might be a table function; try to print the arguments */
3226 412 : appendStringInfoString(&rbuf, "TABLE(");
3227 412 : ntabargs = print_function_arguments(&rbuf, proctup, true, false);
3228 412 : if (ntabargs > 0)
3229 70 : appendStringInfoChar(&rbuf, ')');
3230 : else
3231 342 : resetStringInfo(&rbuf);
3232 : }
3233 :
3234 3850 : if (ntabargs == 0)
3235 : {
3236 : /* Not a table function, so do the normal thing */
3237 3780 : if (proc->proretset)
3238 342 : appendStringInfoString(&rbuf, "SETOF ");
3239 3780 : appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3240 : }
3241 :
3242 3850 : appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
3243 3850 : }
3244 :
3245 : /*
3246 : * Common code for pg_get_function_arguments and pg_get_function_result:
3247 : * append the desired subset of arguments to buf. We print only TABLE
3248 : * arguments when print_table_args is true, and all the others when it's false.
3249 : * We print argument defaults only if print_defaults is true.
3250 : * Function return value is the number of arguments printed.
3251 : */
3252 : static int
3253 9072 : print_function_arguments(StringInfo buf, HeapTuple proctup,
3254 : bool print_table_args, bool print_defaults)
3255 : {
3256 9072 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3257 : int numargs;
3258 : Oid *argtypes;
3259 : char **argnames;
3260 : char *argmodes;
3261 9072 : int insertorderbyat = -1;
3262 : int argsprinted;
3263 : int inputargno;
3264 : int nlackdefaults;
3265 9072 : List *argdefaults = NIL;
3266 9072 : ListCell *nextargdefault = NULL;
3267 : int i;
3268 :
3269 9072 : numargs = get_func_arg_info(proctup,
3270 : &argtypes, &argnames, &argmodes);
3271 :
3272 9072 : nlackdefaults = numargs;
3273 9072 : if (print_defaults && proc->pronargdefaults > 0)
3274 : {
3275 : Datum proargdefaults;
3276 : bool isnull;
3277 :
3278 38 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3279 : Anum_pg_proc_proargdefaults,
3280 : &isnull);
3281 38 : if (!isnull)
3282 : {
3283 : char *str;
3284 :
3285 38 : str = TextDatumGetCString(proargdefaults);
3286 38 : argdefaults = castNode(List, stringToNode(str));
3287 38 : pfree(str);
3288 38 : nextargdefault = list_head(argdefaults);
3289 : /* nlackdefaults counts only *input* arguments lacking defaults */
3290 38 : nlackdefaults = proc->pronargs - list_length(argdefaults);
3291 : }
3292 : }
3293 :
3294 : /* Check for special treatment of ordered-set aggregates */
3295 9072 : if (proc->prokind == PROKIND_AGGREGATE)
3296 : {
3297 : HeapTuple aggtup;
3298 : Form_pg_aggregate agg;
3299 :
3300 1174 : aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(proc->oid));
3301 1174 : if (!HeapTupleIsValid(aggtup))
3302 0 : elog(ERROR, "cache lookup failed for aggregate %u",
3303 : proc->oid);
3304 1174 : agg = (Form_pg_aggregate) GETSTRUCT(aggtup);
3305 1174 : if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3306 52 : insertorderbyat = agg->aggnumdirectargs;
3307 1174 : ReleaseSysCache(aggtup);
3308 : }
3309 :
3310 9072 : argsprinted = 0;
3311 9072 : inputargno = 0;
3312 18254 : for (i = 0; i < numargs; i++)
3313 : {
3314 9182 : Oid argtype = argtypes[i];
3315 9182 : char *argname = argnames ? argnames[i] : NULL;
3316 9182 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
3317 : const char *modename;
3318 : bool isinput;
3319 :
3320 9182 : switch (argmode)
3321 : {
3322 7720 : case PROARGMODE_IN:
3323 :
3324 : /*
3325 : * For procedures, explicitly mark all argument modes, so as
3326 : * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3327 : */
3328 7720 : if (proc->prokind == PROKIND_PROCEDURE)
3329 514 : modename = "IN ";
3330 : else
3331 7206 : modename = "";
3332 7720 : isinput = true;
3333 7720 : break;
3334 40 : case PROARGMODE_INOUT:
3335 40 : modename = "INOUT ";
3336 40 : isinput = true;
3337 40 : break;
3338 944 : case PROARGMODE_OUT:
3339 944 : modename = "OUT ";
3340 944 : isinput = false;
3341 944 : break;
3342 178 : case PROARGMODE_VARIADIC:
3343 178 : modename = "VARIADIC ";
3344 178 : isinput = true;
3345 178 : break;
3346 300 : case PROARGMODE_TABLE:
3347 300 : modename = "";
3348 300 : isinput = false;
3349 300 : break;
3350 0 : default:
3351 0 : elog(ERROR, "invalid parameter mode '%c'", argmode);
3352 : modename = NULL; /* keep compiler quiet */
3353 : isinput = false;
3354 : break;
3355 : }
3356 9182 : if (isinput)
3357 7938 : inputargno++; /* this is a 1-based counter */
3358 :
3359 9182 : if (print_table_args != (argmode == PROARGMODE_TABLE))
3360 728 : continue;
3361 :
3362 8454 : if (argsprinted == insertorderbyat)
3363 : {
3364 52 : if (argsprinted)
3365 52 : appendStringInfoChar(buf, ' ');
3366 52 : appendStringInfoString(buf, "ORDER BY ");
3367 : }
3368 8402 : else if (argsprinted)
3369 2642 : appendStringInfoString(buf, ", ");
3370 :
3371 8454 : appendStringInfoString(buf, modename);
3372 8454 : if (argname && argname[0])
3373 2920 : appendStringInfo(buf, "%s ", quote_identifier(argname));
3374 8454 : appendStringInfoString(buf, format_type_be(argtype));
3375 8454 : if (print_defaults && isinput && inputargno > nlackdefaults)
3376 : {
3377 : Node *expr;
3378 :
3379 : Assert(nextargdefault != NULL);
3380 58 : expr = (Node *) lfirst(nextargdefault);
3381 58 : nextargdefault = lnext(argdefaults, nextargdefault);
3382 :
3383 58 : appendStringInfo(buf, " DEFAULT %s",
3384 : deparse_expression(expr, NIL, false, false));
3385 : }
3386 8454 : argsprinted++;
3387 :
3388 : /* nasty hack: print the last arg twice for variadic ordered-set agg */
3389 8454 : if (argsprinted == insertorderbyat && i == numargs - 1)
3390 : {
3391 26 : i--;
3392 : /* aggs shouldn't have defaults anyway, but just to be sure ... */
3393 26 : print_defaults = false;
3394 : }
3395 : }
3396 :
3397 9072 : return argsprinted;
3398 : }
3399 :
3400 : static bool
3401 96 : is_input_argument(int nth, const char *argmodes)
3402 : {
3403 : return (!argmodes
3404 42 : || argmodes[nth] == PROARGMODE_IN
3405 18 : || argmodes[nth] == PROARGMODE_INOUT
3406 138 : || argmodes[nth] == PROARGMODE_VARIADIC);
3407 : }
3408 :
3409 : /*
3410 : * Append used transformed types to specified buffer
3411 : */
3412 : static void
3413 148 : print_function_trftypes(StringInfo buf, HeapTuple proctup)
3414 : {
3415 : Oid *trftypes;
3416 : int ntypes;
3417 :
3418 148 : ntypes = get_func_trftypes(proctup, &trftypes);
3419 148 : if (ntypes > 0)
3420 : {
3421 : int i;
3422 :
3423 6 : appendStringInfoString(buf, " TRANSFORM ");
3424 16 : for (i = 0; i < ntypes; i++)
3425 : {
3426 10 : if (i != 0)
3427 4 : appendStringInfoString(buf, ", ");
3428 10 : appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3429 : }
3430 6 : appendStringInfoChar(buf, '\n');
3431 : }
3432 148 : }
3433 :
3434 : /*
3435 : * Get textual representation of a function argument's default value. The
3436 : * second argument of this function is the argument number among all arguments
3437 : * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
3438 : * how information_schema.sql uses it.
3439 : */
3440 : Datum
3441 54 : pg_get_function_arg_default(PG_FUNCTION_ARGS)
3442 : {
3443 54 : Oid funcid = PG_GETARG_OID(0);
3444 54 : int32 nth_arg = PG_GETARG_INT32(1);
3445 : HeapTuple proctup;
3446 : Form_pg_proc proc;
3447 : int numargs;
3448 : Oid *argtypes;
3449 : char **argnames;
3450 : char *argmodes;
3451 : int i;
3452 : List *argdefaults;
3453 : Node *node;
3454 : char *str;
3455 : int nth_inputarg;
3456 : Datum proargdefaults;
3457 : bool isnull;
3458 : int nth_default;
3459 :
3460 54 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3461 54 : if (!HeapTupleIsValid(proctup))
3462 12 : PG_RETURN_NULL();
3463 :
3464 42 : numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3465 42 : if (nth_arg < 1 || nth_arg > numargs || !is_input_argument(nth_arg - 1, argmodes))
3466 : {
3467 12 : ReleaseSysCache(proctup);
3468 12 : PG_RETURN_NULL();
3469 : }
3470 :
3471 30 : nth_inputarg = 0;
3472 84 : for (i = 0; i < nth_arg; i++)
3473 54 : if (is_input_argument(i, argmodes))
3474 48 : nth_inputarg++;
3475 :
3476 30 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3477 : Anum_pg_proc_proargdefaults,
3478 : &isnull);
3479 30 : if (isnull)
3480 : {
3481 0 : ReleaseSysCache(proctup);
3482 0 : PG_RETURN_NULL();
3483 : }
3484 :
3485 30 : str = TextDatumGetCString(proargdefaults);
3486 30 : argdefaults = castNode(List, stringToNode(str));
3487 30 : pfree(str);
3488 :
3489 30 : proc = (Form_pg_proc) GETSTRUCT(proctup);
3490 :
3491 : /*
3492 : * Calculate index into proargdefaults: proargdefaults corresponds to the
3493 : * last N input arguments, where N = pronargdefaults.
3494 : */
3495 30 : nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3496 :
3497 30 : if (nth_default < 0 || nth_default >= list_length(argdefaults))
3498 : {
3499 6 : ReleaseSysCache(proctup);
3500 6 : PG_RETURN_NULL();
3501 : }
3502 24 : node = list_nth(argdefaults, nth_default);
3503 24 : str = deparse_expression(node, NIL, false, false);
3504 :
3505 24 : ReleaseSysCache(proctup);
3506 :
3507 24 : PG_RETURN_TEXT_P(string_to_text(str));
3508 : }
3509 :
3510 : static void
3511 182 : print_function_sqlbody(StringInfo buf, HeapTuple proctup)
3512 : {
3513 : int numargs;
3514 : Oid *argtypes;
3515 : char **argnames;
3516 : char *argmodes;
3517 182 : deparse_namespace dpns = {0};
3518 : Datum tmp;
3519 : Node *n;
3520 :
3521 182 : dpns.funcname = pstrdup(NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname));
3522 182 : numargs = get_func_arg_info(proctup,
3523 : &argtypes, &argnames, &argmodes);
3524 182 : dpns.numargs = numargs;
3525 182 : dpns.argnames = argnames;
3526 :
3527 182 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosqlbody);
3528 182 : n = stringToNode(TextDatumGetCString(tmp));
3529 :
3530 182 : if (IsA(n, List))
3531 : {
3532 : List *stmts;
3533 : ListCell *lc;
3534 :
3535 136 : stmts = linitial(castNode(List, n));
3536 :
3537 136 : appendStringInfoString(buf, "BEGIN ATOMIC\n");
3538 :
3539 262 : foreach(lc, stmts)
3540 : {
3541 126 : Query *query = lfirst_node(Query, lc);
3542 :
3543 : /* It seems advisable to get at least AccessShareLock on rels */
3544 126 : AcquireRewriteLocks(query, false, false);
3545 126 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3546 : PRETTYFLAG_INDENT, WRAP_COLUMN_DEFAULT, 1);
3547 126 : appendStringInfoChar(buf, ';');
3548 126 : appendStringInfoChar(buf, '\n');
3549 : }
3550 :
3551 136 : appendStringInfoString(buf, "END");
3552 : }
3553 : else
3554 : {
3555 46 : Query *query = castNode(Query, n);
3556 :
3557 : /* It seems advisable to get at least AccessShareLock on rels */
3558 46 : AcquireRewriteLocks(query, false, false);
3559 46 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3560 : 0, WRAP_COLUMN_DEFAULT, 0);
3561 : }
3562 182 : }
3563 :
3564 : Datum
3565 3424 : pg_get_function_sqlbody(PG_FUNCTION_ARGS)
3566 : {
3567 3424 : Oid funcid = PG_GETARG_OID(0);
3568 : StringInfoData buf;
3569 : HeapTuple proctup;
3570 : bool isnull;
3571 :
3572 3424 : initStringInfo(&buf);
3573 :
3574 : /* Look up the function */
3575 3424 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3576 3424 : if (!HeapTupleIsValid(proctup))
3577 0 : PG_RETURN_NULL();
3578 :
3579 3424 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3580 3424 : if (isnull)
3581 : {
3582 3326 : ReleaseSysCache(proctup);
3583 3326 : PG_RETURN_NULL();
3584 : }
3585 :
3586 98 : print_function_sqlbody(&buf, proctup);
3587 :
3588 98 : ReleaseSysCache(proctup);
3589 :
3590 98 : PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
3591 : }
3592 :
3593 :
3594 : /*
3595 : * deparse_expression - General utility for deparsing expressions
3596 : *
3597 : * calls deparse_expression_pretty with all prettyPrinting disabled
3598 : */
3599 : char *
3600 58828 : deparse_expression(Node *expr, List *dpcontext,
3601 : bool forceprefix, bool showimplicit)
3602 : {
3603 58828 : return deparse_expression_pretty(expr, dpcontext, forceprefix,
3604 : showimplicit, 0, 0);
3605 : }
3606 :
3607 : /* ----------
3608 : * deparse_expression_pretty - General utility for deparsing expressions
3609 : *
3610 : * expr is the node tree to be deparsed. It must be a transformed expression
3611 : * tree (ie, not the raw output of gram.y).
3612 : *
3613 : * dpcontext is a list of deparse_namespace nodes representing the context
3614 : * for interpreting Vars in the node tree. It can be NIL if no Vars are
3615 : * expected.
3616 : *
3617 : * forceprefix is true to force all Vars to be prefixed with their table names.
3618 : *
3619 : * showimplicit is true to force all implicit casts to be shown explicitly.
3620 : *
3621 : * Tries to pretty up the output according to prettyFlags and startIndent.
3622 : *
3623 : * The result is a palloc'd string.
3624 : * ----------
3625 : */
3626 : static char *
3627 69230 : deparse_expression_pretty(Node *expr, List *dpcontext,
3628 : bool forceprefix, bool showimplicit,
3629 : int prettyFlags, int startIndent)
3630 : {
3631 : StringInfoData buf;
3632 : deparse_context context;
3633 :
3634 69230 : initStringInfo(&buf);
3635 69230 : context.buf = &buf;
3636 69230 : context.namespaces = dpcontext;
3637 69230 : context.windowClause = NIL;
3638 69230 : context.windowTList = NIL;
3639 69230 : context.varprefix = forceprefix;
3640 69230 : context.prettyFlags = prettyFlags;
3641 69230 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
3642 69230 : context.indentLevel = startIndent;
3643 69230 : context.special_exprkind = EXPR_KIND_NONE;
3644 69230 : context.appendparents = NULL;
3645 :
3646 69230 : get_rule_expr(expr, &context, showimplicit);
3647 :
3648 69230 : return buf.data;
3649 : }
3650 :
3651 : /* ----------
3652 : * deparse_context_for - Build deparse context for a single relation
3653 : *
3654 : * Given the reference name (alias) and OID of a relation, build deparsing
3655 : * context for an expression referencing only that relation (as varno 1,
3656 : * varlevelsup 0). This is sufficient for many uses of deparse_expression.
3657 : * ----------
3658 : */
3659 : List *
3660 19030 : deparse_context_for(const char *aliasname, Oid relid)
3661 : {
3662 : deparse_namespace *dpns;
3663 : RangeTblEntry *rte;
3664 :
3665 19030 : dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
3666 :
3667 : /* Build a minimal RTE for the rel */
3668 19030 : rte = makeNode(RangeTblEntry);
3669 19030 : rte->rtekind = RTE_RELATION;
3670 19030 : rte->relid = relid;
3671 19030 : rte->relkind = RELKIND_RELATION; /* no need for exactness here */
3672 19030 : rte->rellockmode = AccessShareLock;
3673 19030 : rte->alias = makeAlias(aliasname, NIL);
3674 19030 : rte->eref = rte->alias;
3675 19030 : rte->lateral = false;
3676 19030 : rte->inh = false;
3677 19030 : rte->inFromCl = true;
3678 :
3679 : /* Build one-element rtable */
3680 19030 : dpns->rtable = list_make1(rte);
3681 19030 : dpns->subplans = NIL;
3682 19030 : dpns->ctes = NIL;
3683 19030 : dpns->appendrels = NULL;
3684 19030 : set_rtable_names(dpns, NIL, NULL);
3685 19030 : set_simple_column_names(dpns);
3686 :
3687 : /* Return a one-deep namespace stack */
3688 19030 : return list_make1(dpns);
3689 : }
3690 :
3691 : /*
3692 : * deparse_context_for_plan_tree - Build deparse context for a Plan tree
3693 : *
3694 : * When deparsing an expression in a Plan tree, we use the plan's rangetable
3695 : * to resolve names of simple Vars. The initialization of column names for
3696 : * this is rather expensive if the rangetable is large, and it'll be the same
3697 : * for every expression in the Plan tree; so we do it just once and re-use
3698 : * the result of this function for each expression. (Note that the result
3699 : * is not usable until set_deparse_context_plan() is applied to it.)
3700 : *
3701 : * In addition to the PlannedStmt, pass the per-RTE alias names
3702 : * assigned by a previous call to select_rtable_names_for_explain.
3703 : */
3704 : List *
3705 20576 : deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
3706 : {
3707 : deparse_namespace *dpns;
3708 :
3709 20576 : dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
3710 :
3711 : /* Initialize fields that stay the same across the whole plan tree */
3712 20576 : dpns->rtable = pstmt->rtable;
3713 20576 : dpns->rtable_names = rtable_names;
3714 20576 : dpns->subplans = pstmt->subplans;
3715 20576 : dpns->ctes = NIL;
3716 20576 : if (pstmt->appendRelations)
3717 : {
3718 : /* Set up the array, indexed by child relid */
3719 3450 : int ntables = list_length(dpns->rtable);
3720 : ListCell *lc;
3721 :
3722 3450 : dpns->appendrels = (AppendRelInfo **)
3723 3450 : palloc0((ntables + 1) * sizeof(AppendRelInfo *));
3724 19054 : foreach(lc, pstmt->appendRelations)
3725 : {
3726 15604 : AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
3727 15604 : Index crelid = appinfo->child_relid;
3728 :
3729 : Assert(crelid > 0 && crelid <= ntables);
3730 : Assert(dpns->appendrels[crelid] == NULL);
3731 15604 : dpns->appendrels[crelid] = appinfo;
3732 : }
3733 : }
3734 : else
3735 17126 : dpns->appendrels = NULL; /* don't need it */
3736 :
3737 : /*
3738 : * Set up column name aliases. We will get rather bogus results for join
3739 : * RTEs, but that doesn't matter because plan trees don't contain any join
3740 : * alias Vars.
3741 : */
3742 20576 : set_simple_column_names(dpns);
3743 :
3744 : /* Return a one-deep namespace stack */
3745 20576 : return list_make1(dpns);
3746 : }
3747 :
3748 : /*
3749 : * set_deparse_context_plan - Specify Plan node containing expression
3750 : *
3751 : * When deparsing an expression in a Plan tree, we might have to resolve
3752 : * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
3753 : * provide the parent Plan node. Then OUTER_VAR and INNER_VAR references
3754 : * can be resolved by drilling down into the left and right child plans.
3755 : * Similarly, INDEX_VAR references can be resolved by reference to the
3756 : * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
3757 : * ForeignScan and CustomScan nodes. (Note that we don't currently support
3758 : * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
3759 : * for those, we can only deparse the indexqualorig fields, which won't
3760 : * contain INDEX_VAR Vars.)
3761 : *
3762 : * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
3763 : * the most-closely-nested first. This is needed to resolve PARAM_EXEC
3764 : * Params. Note we assume that all the Plan nodes share the same rtable.
3765 : *
3766 : * Once this function has been called, deparse_expression() can be called on
3767 : * subsidiary expression(s) of the specified Plan node. To deparse
3768 : * expressions of a different Plan node in the same Plan tree, re-call this
3769 : * function to identify the new parent Plan node.
3770 : *
3771 : * The result is the same List passed in; this is a notational convenience.
3772 : */
3773 : List *
3774 44376 : set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
3775 : {
3776 : deparse_namespace *dpns;
3777 :
3778 : /* Should always have one-entry namespace list for Plan deparsing */
3779 : Assert(list_length(dpcontext) == 1);
3780 44376 : dpns = (deparse_namespace *) linitial(dpcontext);
3781 :
3782 : /* Set our attention on the specific plan node passed in */
3783 44376 : dpns->ancestors = ancestors;
3784 44376 : set_deparse_plan(dpns, plan);
3785 :
3786 44376 : return dpcontext;
3787 : }
3788 :
3789 : /*
3790 : * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
3791 : *
3792 : * Determine the relation aliases we'll use during an EXPLAIN operation.
3793 : * This is just a frontend to set_rtable_names. We have to expose the aliases
3794 : * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
3795 : */
3796 : List *
3797 20576 : select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
3798 : {
3799 : deparse_namespace dpns;
3800 :
3801 20576 : memset(&dpns, 0, sizeof(dpns));
3802 20576 : dpns.rtable = rtable;
3803 20576 : dpns.subplans = NIL;
3804 20576 : dpns.ctes = NIL;
3805 20576 : dpns.appendrels = NULL;
3806 20576 : set_rtable_names(&dpns, NIL, rels_used);
3807 : /* We needn't bother computing column aliases yet */
3808 :
3809 20576 : return dpns.rtable_names;
3810 : }
3811 :
3812 : /*
3813 : * set_rtable_names: select RTE aliases to be used in printing a query
3814 : *
3815 : * We fill in dpns->rtable_names with a list of names that is one-for-one with
3816 : * the already-filled dpns->rtable list. Each RTE name is unique among those
3817 : * in the new namespace plus any ancestor namespaces listed in
3818 : * parent_namespaces.
3819 : *
3820 : * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
3821 : *
3822 : * Note that this function is only concerned with relation names, not column
3823 : * names.
3824 : */
3825 : static void
3826 44814 : set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
3827 : Bitmapset *rels_used)
3828 : {
3829 : HASHCTL hash_ctl;
3830 : HTAB *names_hash;
3831 : NameHashEntry *hentry;
3832 : bool found;
3833 : int rtindex;
3834 : ListCell *lc;
3835 :
3836 44814 : dpns->rtable_names = NIL;
3837 : /* nothing more to do if empty rtable */
3838 44814 : if (dpns->rtable == NIL)
3839 498 : return;
3840 :
3841 : /*
3842 : * We use a hash table to hold known names, so that this process is O(N)
3843 : * not O(N^2) for N names.
3844 : */
3845 44316 : hash_ctl.keysize = NAMEDATALEN;
3846 44316 : hash_ctl.entrysize = sizeof(NameHashEntry);
3847 44316 : hash_ctl.hcxt = CurrentMemoryContext;
3848 44316 : names_hash = hash_create("set_rtable_names names",
3849 44316 : list_length(dpns->rtable),
3850 : &hash_ctl,
3851 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
3852 :
3853 : /* Preload the hash table with names appearing in parent_namespaces */
3854 45772 : foreach(lc, parent_namespaces)
3855 : {
3856 1456 : deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
3857 : ListCell *lc2;
3858 :
3859 5016 : foreach(lc2, olddpns->rtable_names)
3860 : {
3861 3560 : char *oldname = (char *) lfirst(lc2);
3862 :
3863 3560 : if (oldname == NULL)
3864 284 : continue;
3865 3276 : hentry = (NameHashEntry *) hash_search(names_hash,
3866 : oldname,
3867 : HASH_ENTER,
3868 : &found);
3869 : /* we do not complain about duplicate names in parent namespaces */
3870 3276 : hentry->counter = 0;
3871 : }
3872 : }
3873 :
3874 : /* Now we can scan the rtable */
3875 44316 : rtindex = 1;
3876 125802 : foreach(lc, dpns->rtable)
3877 : {
3878 81486 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
3879 : char *refname;
3880 :
3881 : /* Just in case this takes an unreasonable amount of time ... */
3882 81486 : CHECK_FOR_INTERRUPTS();
3883 :
3884 81486 : if (rels_used && !bms_is_member(rtindex, rels_used))
3885 : {
3886 : /* Ignore unreferenced RTE */
3887 13134 : refname = NULL;
3888 : }
3889 68352 : else if (rte->alias)
3890 : {
3891 : /* If RTE has a user-defined alias, prefer that */
3892 44874 : refname = rte->alias->aliasname;
3893 : }
3894 23478 : else if (rte->rtekind == RTE_RELATION)
3895 : {
3896 : /* Use the current actual name of the relation */
3897 19950 : refname = get_rel_name(rte->relid);
3898 : }
3899 3528 : else if (rte->rtekind == RTE_JOIN)
3900 : {
3901 : /* Unnamed join has no refname */
3902 1478 : refname = NULL;
3903 : }
3904 : else
3905 : {
3906 : /* Otherwise use whatever the parser assigned */
3907 2050 : refname = rte->eref->aliasname;
3908 : }
3909 :
3910 : /*
3911 : * If the selected name isn't unique, append digits to make it so, and
3912 : * make a new hash entry for it once we've got a unique name. For a
3913 : * very long input name, we might have to truncate to stay within
3914 : * NAMEDATALEN.
3915 : */
3916 81486 : if (refname)
3917 : {
3918 66874 : hentry = (NameHashEntry *) hash_search(names_hash,
3919 : refname,
3920 : HASH_ENTER,
3921 : &found);
3922 66874 : if (found)
3923 : {
3924 : /* Name already in use, must choose a new one */
3925 12700 : int refnamelen = strlen(refname);
3926 12700 : char *modname = (char *) palloc(refnamelen + 16);
3927 : NameHashEntry *hentry2;
3928 :
3929 : do
3930 : {
3931 12706 : hentry->counter++;
3932 : for (;;)
3933 : {
3934 12718 : memcpy(modname, refname, refnamelen);
3935 12718 : sprintf(modname + refnamelen, "_%d", hentry->counter);
3936 12718 : if (strlen(modname) < NAMEDATALEN)
3937 12706 : break;
3938 : /* drop chars from refname to keep all the digits */
3939 12 : refnamelen = pg_mbcliplen(refname, refnamelen,
3940 : refnamelen - 1);
3941 : }
3942 12706 : hentry2 = (NameHashEntry *) hash_search(names_hash,
3943 : modname,
3944 : HASH_ENTER,
3945 : &found);
3946 12706 : } while (found);
3947 12700 : hentry2->counter = 0; /* init new hash entry */
3948 12700 : refname = modname;
3949 : }
3950 : else
3951 : {
3952 : /* Name not previously used, need only initialize hentry */
3953 54174 : hentry->counter = 0;
3954 : }
3955 : }
3956 :
3957 81486 : dpns->rtable_names = lappend(dpns->rtable_names, refname);
3958 81486 : rtindex++;
3959 : }
3960 :
3961 44316 : hash_destroy(names_hash);
3962 : }
3963 :
3964 : /*
3965 : * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
3966 : *
3967 : * For convenience, this is defined to initialize the deparse_namespace struct
3968 : * from scratch.
3969 : */
3970 : static void
3971 5070 : set_deparse_for_query(deparse_namespace *dpns, Query *query,
3972 : List *parent_namespaces)
3973 : {
3974 : ListCell *lc;
3975 : ListCell *lc2;
3976 :
3977 : /* Initialize *dpns and fill rtable/ctes links */
3978 5070 : memset(dpns, 0, sizeof(deparse_namespace));
3979 5070 : dpns->rtable = query->rtable;
3980 5070 : dpns->subplans = NIL;
3981 5070 : dpns->ctes = query->cteList;
3982 5070 : dpns->appendrels = NULL;
3983 :
3984 : /* Assign a unique relation alias to each RTE */
3985 5070 : set_rtable_names(dpns, parent_namespaces, NULL);
3986 :
3987 : /* Initialize dpns->rtable_columns to contain zeroed structs */
3988 5070 : dpns->rtable_columns = NIL;
3989 13826 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
3990 8756 : dpns->rtable_columns = lappend(dpns->rtable_columns,
3991 : palloc0(sizeof(deparse_columns)));
3992 :
3993 : /* If it's a utility query, it won't have a jointree */
3994 5070 : if (query->jointree)
3995 : {
3996 : /* Detect whether global uniqueness of USING names is needed */
3997 5054 : dpns->unique_using =
3998 5054 : has_dangerous_join_using(dpns, (Node *) query->jointree);
3999 :
4000 : /*
4001 : * Select names for columns merged by USING, via a recursive pass over
4002 : * the query jointree.
4003 : */
4004 5054 : set_using_names(dpns, (Node *) query->jointree, NIL);
4005 : }
4006 :
4007 : /*
4008 : * Now assign remaining column aliases for each RTE. We do this in a
4009 : * linear scan of the rtable, so as to process RTEs whether or not they
4010 : * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4011 : * etc). JOIN RTEs must be processed after their children, but this is
4012 : * okay because they appear later in the rtable list than their children
4013 : * (cf Asserts in identify_join_columns()).
4014 : */
4015 13826 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4016 : {
4017 8756 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4018 8756 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4019 :
4020 8756 : if (rte->rtekind == RTE_JOIN)
4021 1170 : set_join_column_names(dpns, rte, colinfo);
4022 : else
4023 7586 : set_relation_column_names(dpns, rte, colinfo);
4024 : }
4025 5070 : }
4026 :
4027 : /*
4028 : * set_simple_column_names: fill in column aliases for non-query situations
4029 : *
4030 : * This handles EXPLAIN and cases where we only have relation RTEs. Without
4031 : * a join tree, we can't do anything smart about join RTEs, but we don't
4032 : * need to (note that EXPLAIN should never see join alias Vars anyway).
4033 : * If we do hit a join RTE we'll just process it like a non-table base RTE.
4034 : */
4035 : static void
4036 39744 : set_simple_column_names(deparse_namespace *dpns)
4037 : {
4038 : ListCell *lc;
4039 : ListCell *lc2;
4040 :
4041 : /* Initialize dpns->rtable_columns to contain zeroed structs */
4042 39744 : dpns->rtable_columns = NIL;
4043 112474 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4044 72730 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4045 : palloc0(sizeof(deparse_columns)));
4046 :
4047 : /* Assign unique column aliases within each RTE */
4048 112474 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4049 : {
4050 72730 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4051 72730 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4052 :
4053 72730 : set_relation_column_names(dpns, rte, colinfo);
4054 : }
4055 39744 : }
4056 :
4057 : /*
4058 : * has_dangerous_join_using: search jointree for unnamed JOIN USING
4059 : *
4060 : * Merged columns of a JOIN USING may act differently from either of the input
4061 : * columns, either because they are merged with COALESCE (in a FULL JOIN) or
4062 : * because an implicit coercion of the underlying input column is required.
4063 : * In such a case the column must be referenced as a column of the JOIN not as
4064 : * a column of either input. And this is problematic if the join is unnamed
4065 : * (alias-less): we cannot qualify the column's name with an RTE name, since
4066 : * there is none. (Forcibly assigning an alias to the join is not a solution,
4067 : * since that will prevent legal references to tables below the join.)
4068 : * To ensure that every column in the query is unambiguously referenceable,
4069 : * we must assign such merged columns names that are globally unique across
4070 : * the whole query, aliasing other columns out of the way as necessary.
4071 : *
4072 : * Because the ensuing re-aliasing is fairly damaging to the readability of
4073 : * the query, we don't do this unless we have to. So, we must pre-scan
4074 : * the join tree to see if we have to, before starting set_using_names().
4075 : */
4076 : static bool
4077 11662 : has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
4078 : {
4079 11662 : if (IsA(jtnode, RangeTblRef))
4080 : {
4081 : /* nothing to do here */
4082 : }
4083 6158 : else if (IsA(jtnode, FromExpr))
4084 : {
4085 5054 : FromExpr *f = (FromExpr *) jtnode;
4086 : ListCell *lc;
4087 :
4088 9526 : foreach(lc, f->fromlist)
4089 : {
4090 4544 : if (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))
4091 72 : return true;
4092 : }
4093 : }
4094 1104 : else if (IsA(jtnode, JoinExpr))
4095 : {
4096 1104 : JoinExpr *j = (JoinExpr *) jtnode;
4097 :
4098 : /* Is it an unnamed JOIN with USING? */
4099 1104 : if (j->alias == NULL && j->usingClause)
4100 : {
4101 : /*
4102 : * Yes, so check each join alias var to see if any of them are not
4103 : * simple references to underlying columns. If so, we have a
4104 : * dangerous situation and must pick unique aliases.
4105 : */
4106 286 : RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4107 :
4108 : /* We need only examine the merged columns */
4109 596 : for (int i = 0; i < jrte->joinmergedcols; i++)
4110 : {
4111 382 : Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4112 :
4113 382 : if (!IsA(aliasvar, Var))
4114 72 : return true;
4115 : }
4116 : }
4117 :
4118 : /* Nope, but inspect children */
4119 1032 : if (has_dangerous_join_using(dpns, j->larg))
4120 0 : return true;
4121 1032 : if (has_dangerous_join_using(dpns, j->rarg))
4122 0 : return true;
4123 : }
4124 : else
4125 0 : elog(ERROR, "unrecognized node type: %d",
4126 : (int) nodeTag(jtnode));
4127 11518 : return false;
4128 : }
4129 :
4130 : /*
4131 : * set_using_names: select column aliases to be used for merged USING columns
4132 : *
4133 : * We do this during a recursive descent of the query jointree.
4134 : * dpns->unique_using must already be set to determine the global strategy.
4135 : *
4136 : * Column alias info is saved in the dpns->rtable_columns list, which is
4137 : * assumed to be filled with pre-zeroed deparse_columns structs.
4138 : *
4139 : * parentUsing is a list of all USING aliases assigned in parent joins of
4140 : * the current jointree node. (The passed-in list must not be modified.)
4141 : */
4142 : static void
4143 11980 : set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
4144 : {
4145 11980 : if (IsA(jtnode, RangeTblRef))
4146 : {
4147 : /* nothing to do now */
4148 : }
4149 6224 : else if (IsA(jtnode, FromExpr))
4150 : {
4151 5054 : FromExpr *f = (FromExpr *) jtnode;
4152 : ListCell *lc;
4153 :
4154 9640 : foreach(lc, f->fromlist)
4155 4586 : set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4156 : }
4157 1170 : else if (IsA(jtnode, JoinExpr))
4158 : {
4159 1170 : JoinExpr *j = (JoinExpr *) jtnode;
4160 1170 : RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4161 1170 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
4162 : int *leftattnos;
4163 : int *rightattnos;
4164 : deparse_columns *leftcolinfo;
4165 : deparse_columns *rightcolinfo;
4166 : int i;
4167 : ListCell *lc;
4168 :
4169 : /* Get info about the shape of the join */
4170 1170 : identify_join_columns(j, rte, colinfo);
4171 1170 : leftattnos = colinfo->leftattnos;
4172 1170 : rightattnos = colinfo->rightattnos;
4173 :
4174 : /* Look up the not-yet-filled-in child deparse_columns structs */
4175 1170 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4176 1170 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4177 :
4178 : /*
4179 : * If this join is unnamed, then we cannot substitute new aliases at
4180 : * this level, so any name requirements pushed down to here must be
4181 : * pushed down again to the children.
4182 : */
4183 1170 : if (rte->alias == NULL)
4184 : {
4185 1200 : for (i = 0; i < colinfo->num_cols; i++)
4186 : {
4187 138 : char *colname = colinfo->colnames[i];
4188 :
4189 138 : if (colname == NULL)
4190 24 : continue;
4191 :
4192 : /* Push down to left column, unless it's a system column */
4193 114 : if (leftattnos[i] > 0)
4194 : {
4195 102 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4196 102 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4197 : }
4198 :
4199 : /* Same on the righthand side */
4200 114 : if (rightattnos[i] > 0)
4201 : {
4202 114 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4203 114 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4204 : }
4205 : }
4206 : }
4207 :
4208 : /*
4209 : * If there's a USING clause, select the USING column names and push
4210 : * those names down to the children. We have two strategies:
4211 : *
4212 : * If dpns->unique_using is true, we force all USING names to be
4213 : * unique across the whole query level. In principle we'd only need
4214 : * the names of dangerous USING columns to be globally unique, but to
4215 : * safely assign all USING names in a single pass, we have to enforce
4216 : * the same uniqueness rule for all of them. However, if a USING
4217 : * column's name has been pushed down from the parent, we should use
4218 : * it as-is rather than making a uniqueness adjustment. This is
4219 : * necessary when we're at an unnamed join, and it creates no risk of
4220 : * ambiguity. Also, if there's a user-written output alias for a
4221 : * merged column, we prefer to use that rather than the input name;
4222 : * this simplifies the logic and seems likely to lead to less aliasing
4223 : * overall.
4224 : *
4225 : * If dpns->unique_using is false, we only need USING names to be
4226 : * unique within their own join RTE. We still need to honor
4227 : * pushed-down names, though.
4228 : *
4229 : * Though significantly different in results, these two strategies are
4230 : * implemented by the same code, with only the difference of whether
4231 : * to put assigned names into dpns->using_names.
4232 : */
4233 1170 : if (j->usingClause)
4234 : {
4235 : /* Copy the input parentUsing list so we don't modify it */
4236 424 : parentUsing = list_copy(parentUsing);
4237 :
4238 : /* USING names must correspond to the first join output columns */
4239 424 : expand_colnames_array_to(colinfo, list_length(j->usingClause));
4240 424 : i = 0;
4241 1004 : foreach(lc, j->usingClause)
4242 : {
4243 580 : char *colname = strVal(lfirst(lc));
4244 :
4245 : /* Assert it's a merged column */
4246 : Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4247 :
4248 : /* Adopt passed-down name if any, else select unique name */
4249 580 : if (colinfo->colnames[i] != NULL)
4250 102 : colname = colinfo->colnames[i];
4251 : else
4252 : {
4253 : /* Prefer user-written output alias if any */
4254 478 : if (rte->alias && i < list_length(rte->alias->colnames))
4255 0 : colname = strVal(list_nth(rte->alias->colnames, i));
4256 : /* Make it appropriately unique */
4257 478 : colname = make_colname_unique(colname, dpns, colinfo);
4258 478 : if (dpns->unique_using)
4259 126 : dpns->using_names = lappend(dpns->using_names,
4260 : colname);
4261 : /* Save it as output column name, too */
4262 478 : colinfo->colnames[i] = colname;
4263 : }
4264 :
4265 : /* Remember selected names for use later */
4266 580 : colinfo->usingNames = lappend(colinfo->usingNames, colname);
4267 580 : parentUsing = lappend(parentUsing, colname);
4268 :
4269 : /* Push down to left column, unless it's a system column */
4270 580 : if (leftattnos[i] > 0)
4271 : {
4272 580 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4273 580 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4274 : }
4275 :
4276 : /* Same on the righthand side */
4277 580 : if (rightattnos[i] > 0)
4278 : {
4279 580 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4280 580 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4281 : }
4282 :
4283 580 : i++;
4284 : }
4285 : }
4286 :
4287 : /* Mark child deparse_columns structs with correct parentUsing info */
4288 1170 : leftcolinfo->parentUsing = parentUsing;
4289 1170 : rightcolinfo->parentUsing = parentUsing;
4290 :
4291 : /* Now recursively assign USING column names in children */
4292 1170 : set_using_names(dpns, j->larg, parentUsing);
4293 1170 : set_using_names(dpns, j->rarg, parentUsing);
4294 : }
4295 : else
4296 0 : elog(ERROR, "unrecognized node type: %d",
4297 : (int) nodeTag(jtnode));
4298 11980 : }
4299 :
4300 : /*
4301 : * set_relation_column_names: select column aliases for a non-join RTE
4302 : *
4303 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4304 : * If any colnames entries are already filled in, those override local
4305 : * choices.
4306 : */
4307 : static void
4308 80316 : set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4309 : deparse_columns *colinfo)
4310 : {
4311 : int ncolumns;
4312 : char **real_colnames;
4313 : bool changed_any;
4314 : int noldcolumns;
4315 : int i;
4316 : int j;
4317 :
4318 : /*
4319 : * Construct an array of the current "real" column names of the RTE.
4320 : * real_colnames[] will be indexed by physical column number, with NULL
4321 : * entries for dropped columns.
4322 : */
4323 80316 : if (rte->rtekind == RTE_RELATION)
4324 : {
4325 : /* Relation --- look to the system catalogs for up-to-date info */
4326 : Relation rel;
4327 : TupleDesc tupdesc;
4328 :
4329 66564 : rel = relation_open(rte->relid, AccessShareLock);
4330 66564 : tupdesc = RelationGetDescr(rel);
4331 :
4332 66564 : ncolumns = tupdesc->natts;
4333 66564 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4334 :
4335 429154 : for (i = 0; i < ncolumns; i++)
4336 : {
4337 362590 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4338 :
4339 362590 : if (attr->attisdropped)
4340 2988 : real_colnames[i] = NULL;
4341 : else
4342 359602 : real_colnames[i] = pstrdup(NameStr(attr->attname));
4343 : }
4344 66564 : relation_close(rel, AccessShareLock);
4345 : }
4346 : else
4347 : {
4348 : /* Otherwise get the column names from eref or expandRTE() */
4349 : List *colnames;
4350 : ListCell *lc;
4351 :
4352 : /*
4353 : * Functions returning composites have the annoying property that some
4354 : * of the composite type's columns might have been dropped since the
4355 : * query was parsed. If possible, use expandRTE() to handle that
4356 : * case, since it has the tedious logic needed to find out about
4357 : * dropped columns. However, if we're explaining a plan, then we
4358 : * don't have rte->functions because the planner thinks that won't be
4359 : * needed later, and that breaks expandRTE(). So in that case we have
4360 : * to rely on rte->eref, which may lead us to report a dropped
4361 : * column's old name; that seems close enough for EXPLAIN's purposes.
4362 : *
4363 : * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4364 : * which should be sufficiently up-to-date: no other RTE types can
4365 : * have columns get dropped from under them after parsing.
4366 : */
4367 13752 : if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4368 : {
4369 : /* Since we're not creating Vars, rtindex etc. don't matter */
4370 624 : expandRTE(rte, 1, 0, -1, true /* include dropped */ ,
4371 : &colnames, NULL);
4372 : }
4373 : else
4374 13128 : colnames = rte->eref->colnames;
4375 :
4376 13752 : ncolumns = list_length(colnames);
4377 13752 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4378 :
4379 13752 : i = 0;
4380 77152 : foreach(lc, colnames)
4381 : {
4382 : /*
4383 : * If the column name we find here is an empty string, then it's a
4384 : * dropped column, so change to NULL.
4385 : */
4386 63400 : char *cname = strVal(lfirst(lc));
4387 :
4388 63400 : if (cname[0] == '\0')
4389 54 : cname = NULL;
4390 63400 : real_colnames[i] = cname;
4391 63400 : i++;
4392 : }
4393 : }
4394 :
4395 : /*
4396 : * Ensure colinfo->colnames has a slot for each column. (It could be long
4397 : * enough already, if we pushed down a name for the last column.) Note:
4398 : * it's possible that there are now more columns than there were when the
4399 : * query was parsed, ie colnames could be longer than rte->eref->colnames.
4400 : * We must assign unique aliases to the new columns too, else there could
4401 : * be unresolved conflicts when the view/rule is reloaded.
4402 : */
4403 80316 : expand_colnames_array_to(colinfo, ncolumns);
4404 : Assert(colinfo->num_cols == ncolumns);
4405 :
4406 : /*
4407 : * Make sufficiently large new_colnames and is_new_col arrays, too.
4408 : *
4409 : * Note: because we leave colinfo->num_new_cols zero until after the loop,
4410 : * colname_is_unique will not consult that array, which is fine because it
4411 : * would only be duplicate effort.
4412 : */
4413 80316 : colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4414 80316 : colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4415 :
4416 : /*
4417 : * Scan the columns, select a unique alias for each one, and store it in
4418 : * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4419 : * entries for dropped columns, the latter omits them. Also mark
4420 : * new_colnames entries as to whether they are new since parse time; this
4421 : * is the case for entries beyond the length of rte->eref->colnames.
4422 : */
4423 80316 : noldcolumns = list_length(rte->eref->colnames);
4424 80316 : changed_any = false;
4425 80316 : j = 0;
4426 506306 : for (i = 0; i < ncolumns; i++)
4427 : {
4428 425990 : char *real_colname = real_colnames[i];
4429 425990 : char *colname = colinfo->colnames[i];
4430 :
4431 : /* Skip dropped columns */
4432 425990 : if (real_colname == NULL)
4433 : {
4434 : Assert(colname == NULL); /* colnames[i] is already NULL */
4435 3042 : continue;
4436 : }
4437 :
4438 : /* If alias already assigned, that's what to use */
4439 422948 : if (colname == NULL)
4440 : {
4441 : /* If user wrote an alias, prefer that over real column name */
4442 421890 : if (rte->alias && i < list_length(rte->alias->colnames))
4443 39160 : colname = strVal(list_nth(rte->alias->colnames, i));
4444 : else
4445 382730 : colname = real_colname;
4446 :
4447 : /* Unique-ify and insert into colinfo */
4448 421890 : colname = make_colname_unique(colname, dpns, colinfo);
4449 :
4450 421890 : colinfo->colnames[i] = colname;
4451 : }
4452 :
4453 : /* Put names of non-dropped columns in new_colnames[] too */
4454 422948 : colinfo->new_colnames[j] = colname;
4455 : /* And mark them as new or not */
4456 422948 : colinfo->is_new_col[j] = (i >= noldcolumns);
4457 422948 : j++;
4458 :
4459 : /* Remember if any assigned aliases differ from "real" name */
4460 422948 : if (!changed_any && strcmp(colname, real_colname) != 0)
4461 3880 : changed_any = true;
4462 : }
4463 :
4464 : /*
4465 : * Set correct length for new_colnames[] array. (Note: if columns have
4466 : * been added, colinfo->num_cols includes them, which is not really quite
4467 : * right but is harmless, since any new columns must be at the end where
4468 : * they won't affect varattnos of pre-existing columns.)
4469 : */
4470 80316 : colinfo->num_new_cols = j;
4471 :
4472 : /*
4473 : * For a relation RTE, we need only print the alias column names if any
4474 : * are different from the underlying "real" names. For a function RTE,
4475 : * always emit a complete column alias list; this is to protect against
4476 : * possible instability of the default column names (eg, from altering
4477 : * parameter names). For tablefunc RTEs, we never print aliases, because
4478 : * the column names are part of the clause itself. For other RTE types,
4479 : * print if we changed anything OR if there were user-written column
4480 : * aliases (since the latter would be part of the underlying "reality").
4481 : */
4482 80316 : if (rte->rtekind == RTE_RELATION)
4483 66564 : colinfo->printaliases = changed_any;
4484 13752 : else if (rte->rtekind == RTE_FUNCTION)
4485 1020 : colinfo->printaliases = true;
4486 12732 : else if (rte->rtekind == RTE_TABLEFUNC)
4487 56 : colinfo->printaliases = false;
4488 12676 : else if (rte->alias && rte->alias->colnames != NIL)
4489 672 : colinfo->printaliases = true;
4490 : else
4491 12004 : colinfo->printaliases = changed_any;
4492 80316 : }
4493 :
4494 : /*
4495 : * set_join_column_names: select column aliases for a join RTE
4496 : *
4497 : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4498 : * If any colnames entries are already filled in, those override local
4499 : * choices. Also, names for USING columns were already chosen by
4500 : * set_using_names(). We further expect that column alias selection has been
4501 : * completed for both input RTEs.
4502 : */
4503 : static void
4504 1170 : set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4505 : deparse_columns *colinfo)
4506 : {
4507 : deparse_columns *leftcolinfo;
4508 : deparse_columns *rightcolinfo;
4509 : bool changed_any;
4510 : int noldcolumns;
4511 : int nnewcolumns;
4512 1170 : Bitmapset *leftmerged = NULL;
4513 1170 : Bitmapset *rightmerged = NULL;
4514 : int i;
4515 : int j;
4516 : int ic;
4517 : int jc;
4518 :
4519 : /* Look up the previously-filled-in child deparse_columns structs */
4520 1170 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4521 1170 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4522 :
4523 : /*
4524 : * Ensure colinfo->colnames has a slot for each column. (It could be long
4525 : * enough already, if we pushed down a name for the last column.) Note:
4526 : * it's possible that one or both inputs now have more columns than there
4527 : * were when the query was parsed, but we'll deal with that below. We
4528 : * only need entries in colnames for pre-existing columns.
4529 : */
4530 1170 : noldcolumns = list_length(rte->eref->colnames);
4531 1170 : expand_colnames_array_to(colinfo, noldcolumns);
4532 : Assert(colinfo->num_cols == noldcolumns);
4533 :
4534 : /*
4535 : * Scan the join output columns, select an alias for each one, and store
4536 : * it in colinfo->colnames. If there are USING columns, set_using_names()
4537 : * already selected their names, so we can start the loop at the first
4538 : * non-merged column.
4539 : */
4540 1170 : changed_any = false;
4541 33592 : for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4542 : {
4543 32422 : char *colname = colinfo->colnames[i];
4544 : char *real_colname;
4545 :
4546 : /* Join column must refer to at least one input column */
4547 : Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4548 :
4549 : /* Get the child column name */
4550 32422 : if (colinfo->leftattnos[i] > 0)
4551 22566 : real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4552 9856 : else if (colinfo->rightattnos[i] > 0)
4553 9856 : real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4554 : else
4555 : {
4556 : /* We're joining system columns --- use eref name */
4557 0 : real_colname = strVal(list_nth(rte->eref->colnames, i));
4558 : }
4559 :
4560 : /* If child col has been dropped, no need to assign a join colname */
4561 32422 : if (real_colname == NULL)
4562 : {
4563 6 : colinfo->colnames[i] = NULL;
4564 6 : continue;
4565 : }
4566 :
4567 : /* In an unnamed join, just report child column names as-is */
4568 32416 : if (rte->alias == NULL)
4569 : {
4570 32038 : colinfo->colnames[i] = real_colname;
4571 32038 : continue;
4572 : }
4573 :
4574 : /* If alias already assigned, that's what to use */
4575 378 : if (colname == NULL)
4576 : {
4577 : /* If user wrote an alias, prefer that over real column name */
4578 378 : if (rte->alias && i < list_length(rte->alias->colnames))
4579 96 : colname = strVal(list_nth(rte->alias->colnames, i));
4580 : else
4581 282 : colname = real_colname;
4582 :
4583 : /* Unique-ify and insert into colinfo */
4584 378 : colname = make_colname_unique(colname, dpns, colinfo);
4585 :
4586 378 : colinfo->colnames[i] = colname;
4587 : }
4588 :
4589 : /* Remember if any assigned aliases differ from "real" name */
4590 378 : if (!changed_any && strcmp(colname, real_colname) != 0)
4591 24 : changed_any = true;
4592 : }
4593 :
4594 : /*
4595 : * Calculate number of columns the join would have if it were re-parsed
4596 : * now, and create storage for the new_colnames and is_new_col arrays.
4597 : *
4598 : * Note: colname_is_unique will be consulting new_colnames[] during the
4599 : * loops below, so its not-yet-filled entries must be zeroes.
4600 : */
4601 2340 : nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
4602 1170 : list_length(colinfo->usingNames);
4603 1170 : colinfo->num_new_cols = nnewcolumns;
4604 1170 : colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
4605 1170 : colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
4606 :
4607 : /*
4608 : * Generating the new_colnames array is a bit tricky since any new columns
4609 : * added since parse time must be inserted in the right places. This code
4610 : * must match the parser, which will order a join's columns as merged
4611 : * columns first (in USING-clause order), then non-merged columns from the
4612 : * left input (in attnum order), then non-merged columns from the right
4613 : * input (ditto). If one of the inputs is itself a join, its columns will
4614 : * be ordered according to the same rule, which means newly-added columns
4615 : * might not be at the end. We can figure out what's what by consulting
4616 : * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
4617 : *
4618 : * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
4619 : * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
4620 : * meanings for the current child RTE.
4621 : */
4622 :
4623 : /* Handle merged columns; they are first and can't be new */
4624 1170 : i = j = 0;
4625 1750 : while (i < noldcolumns &&
4626 1750 : colinfo->leftattnos[i] != 0 &&
4627 1750 : colinfo->rightattnos[i] != 0)
4628 : {
4629 : /* column name is already determined and known unique */
4630 580 : colinfo->new_colnames[j] = colinfo->colnames[i];
4631 580 : colinfo->is_new_col[j] = false;
4632 :
4633 : /* build bitmapsets of child attnums of merged columns */
4634 580 : if (colinfo->leftattnos[i] > 0)
4635 580 : leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
4636 580 : if (colinfo->rightattnos[i] > 0)
4637 580 : rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);
4638 :
4639 580 : i++, j++;
4640 : }
4641 :
4642 : /* Handle non-merged left-child columns */
4643 1170 : ic = 0;
4644 24802 : for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
4645 : {
4646 23632 : char *child_colname = leftcolinfo->new_colnames[jc];
4647 :
4648 23632 : if (!leftcolinfo->is_new_col[jc])
4649 : {
4650 : /* Advance ic to next non-dropped old column of left child */
4651 23224 : while (ic < leftcolinfo->num_cols &&
4652 23224 : leftcolinfo->colnames[ic] == NULL)
4653 84 : ic++;
4654 : Assert(ic < leftcolinfo->num_cols);
4655 23140 : ic++;
4656 : /* If it is a merged column, we already processed it */
4657 23140 : if (bms_is_member(ic, leftmerged))
4658 580 : continue;
4659 : /* Else, advance i to the corresponding existing join column */
4660 22566 : while (i < colinfo->num_cols &&
4661 22566 : colinfo->colnames[i] == NULL)
4662 6 : i++;
4663 : Assert(i < colinfo->num_cols);
4664 : Assert(ic == colinfo->leftattnos[i]);
4665 : /* Use the already-assigned name of this column */
4666 22560 : colinfo->new_colnames[j] = colinfo->colnames[i];
4667 22560 : i++;
4668 : }
4669 : else
4670 : {
4671 : /*
4672 : * Unique-ify the new child column name and assign, unless we're
4673 : * in an unnamed join, in which case just copy
4674 : */
4675 492 : if (rte->alias != NULL)
4676 : {
4677 264 : colinfo->new_colnames[j] =
4678 132 : make_colname_unique(child_colname, dpns, colinfo);
4679 132 : if (!changed_any &&
4680 108 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4681 12 : changed_any = true;
4682 : }
4683 : else
4684 360 : colinfo->new_colnames[j] = child_colname;
4685 : }
4686 :
4687 23052 : colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
4688 23052 : j++;
4689 : }
4690 :
4691 : /* Handle non-merged right-child columns in exactly the same way */
4692 1170 : ic = 0;
4693 11774 : for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
4694 : {
4695 10604 : char *child_colname = rightcolinfo->new_colnames[jc];
4696 :
4697 10604 : if (!rightcolinfo->is_new_col[jc])
4698 : {
4699 : /* Advance ic to next non-dropped old column of right child */
4700 10436 : while (ic < rightcolinfo->num_cols &&
4701 10436 : rightcolinfo->colnames[ic] == NULL)
4702 0 : ic++;
4703 : Assert(ic < rightcolinfo->num_cols);
4704 10436 : ic++;
4705 : /* If it is a merged column, we already processed it */
4706 10436 : if (bms_is_member(ic, rightmerged))
4707 580 : continue;
4708 : /* Else, advance i to the corresponding existing join column */
4709 9856 : while (i < colinfo->num_cols &&
4710 9856 : colinfo->colnames[i] == NULL)
4711 0 : i++;
4712 : Assert(i < colinfo->num_cols);
4713 : Assert(ic == colinfo->rightattnos[i]);
4714 : /* Use the already-assigned name of this column */
4715 9856 : colinfo->new_colnames[j] = colinfo->colnames[i];
4716 9856 : i++;
4717 : }
4718 : else
4719 : {
4720 : /*
4721 : * Unique-ify the new child column name and assign, unless we're
4722 : * in an unnamed join, in which case just copy
4723 : */
4724 168 : if (rte->alias != NULL)
4725 : {
4726 48 : colinfo->new_colnames[j] =
4727 24 : make_colname_unique(child_colname, dpns, colinfo);
4728 24 : if (!changed_any &&
4729 24 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4730 12 : changed_any = true;
4731 : }
4732 : else
4733 144 : colinfo->new_colnames[j] = child_colname;
4734 : }
4735 :
4736 10024 : colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
4737 10024 : j++;
4738 : }
4739 :
4740 : /* Assert we processed the right number of columns */
4741 : #ifdef USE_ASSERT_CHECKING
4742 : while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
4743 : i++;
4744 : Assert(i == colinfo->num_cols);
4745 : Assert(j == nnewcolumns);
4746 : #endif
4747 :
4748 : /*
4749 : * For a named join, print column aliases if we changed any from the child
4750 : * names. Unnamed joins cannot print aliases.
4751 : */
4752 1170 : if (rte->alias != NULL)
4753 108 : colinfo->printaliases = changed_any;
4754 : else
4755 1062 : colinfo->printaliases = false;
4756 1170 : }
4757 :
4758 : /*
4759 : * colname_is_unique: is colname distinct from already-chosen column names?
4760 : *
4761 : * dpns is query-wide info, colinfo is for the column's RTE
4762 : */
4763 : static bool
4764 445002 : colname_is_unique(const char *colname, deparse_namespace *dpns,
4765 : deparse_columns *colinfo)
4766 : {
4767 : int i;
4768 : ListCell *lc;
4769 :
4770 : /* Check against already-assigned column aliases within RTE */
4771 6839142 : for (i = 0; i < colinfo->num_cols; i++)
4772 : {
4773 6416168 : char *oldname = colinfo->colnames[i];
4774 :
4775 6416168 : if (oldname && strcmp(oldname, colname) == 0)
4776 22028 : return false;
4777 : }
4778 :
4779 : /*
4780 : * If we're building a new_colnames array, check that too (this will be
4781 : * partially but not completely redundant with the previous checks)
4782 : */
4783 424246 : for (i = 0; i < colinfo->num_new_cols; i++)
4784 : {
4785 1296 : char *oldname = colinfo->new_colnames[i];
4786 :
4787 1296 : if (oldname && strcmp(oldname, colname) == 0)
4788 24 : return false;
4789 : }
4790 :
4791 : /* Also check against USING-column names that must be globally unique */
4792 423790 : foreach(lc, dpns->using_names)
4793 : {
4794 882 : char *oldname = (char *) lfirst(lc);
4795 :
4796 882 : if (strcmp(oldname, colname) == 0)
4797 42 : return false;
4798 : }
4799 :
4800 : /* Also check against names already assigned for parent-join USING cols */
4801 425500 : foreach(lc, colinfo->parentUsing)
4802 : {
4803 2598 : char *oldname = (char *) lfirst(lc);
4804 :
4805 2598 : if (strcmp(oldname, colname) == 0)
4806 6 : return false;
4807 : }
4808 :
4809 422902 : return true;
4810 : }
4811 :
4812 : /*
4813 : * make_colname_unique: modify colname if necessary to make it unique
4814 : *
4815 : * dpns is query-wide info, colinfo is for the column's RTE
4816 : */
4817 : static char *
4818 422902 : make_colname_unique(char *colname, deparse_namespace *dpns,
4819 : deparse_columns *colinfo)
4820 : {
4821 : /*
4822 : * If the selected name isn't unique, append digits to make it so. For a
4823 : * very long input name, we might have to truncate to stay within
4824 : * NAMEDATALEN.
4825 : */
4826 422902 : if (!colname_is_unique(colname, dpns, colinfo))
4827 : {
4828 18522 : int colnamelen = strlen(colname);
4829 18522 : char *modname = (char *) palloc(colnamelen + 16);
4830 18522 : int i = 0;
4831 :
4832 : do
4833 : {
4834 22100 : i++;
4835 : for (;;)
4836 : {
4837 22100 : memcpy(modname, colname, colnamelen);
4838 22100 : sprintf(modname + colnamelen, "_%d", i);
4839 22100 : if (strlen(modname) < NAMEDATALEN)
4840 22100 : break;
4841 : /* drop chars from colname to keep all the digits */
4842 0 : colnamelen = pg_mbcliplen(colname, colnamelen,
4843 : colnamelen - 1);
4844 : }
4845 22100 : } while (!colname_is_unique(modname, dpns, colinfo));
4846 18522 : colname = modname;
4847 : }
4848 422902 : return colname;
4849 : }
4850 :
4851 : /*
4852 : * expand_colnames_array_to: make colinfo->colnames at least n items long
4853 : *
4854 : * Any added array entries are initialized to zero.
4855 : */
4856 : static void
4857 83286 : expand_colnames_array_to(deparse_columns *colinfo, int n)
4858 : {
4859 83286 : if (n > colinfo->num_cols)
4860 : {
4861 81196 : if (colinfo->colnames == NULL)
4862 79780 : colinfo->colnames = palloc0_array(char *, n);
4863 : else
4864 1416 : colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
4865 81196 : colinfo->num_cols = n;
4866 : }
4867 83286 : }
4868 :
4869 : /*
4870 : * identify_join_columns: figure out where columns of a join come from
4871 : *
4872 : * Fills the join-specific fields of the colinfo struct, except for
4873 : * usingNames which is filled later.
4874 : */
4875 : static void
4876 1170 : identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
4877 : deparse_columns *colinfo)
4878 : {
4879 : int numjoincols;
4880 : int jcolno;
4881 : int rcolno;
4882 : ListCell *lc;
4883 :
4884 : /* Extract left/right child RT indexes */
4885 1170 : if (IsA(j->larg, RangeTblRef))
4886 768 : colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
4887 402 : else if (IsA(j->larg, JoinExpr))
4888 402 : colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
4889 : else
4890 0 : elog(ERROR, "unrecognized node type in jointree: %d",
4891 : (int) nodeTag(j->larg));
4892 1170 : if (IsA(j->rarg, RangeTblRef))
4893 1170 : colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
4894 0 : else if (IsA(j->rarg, JoinExpr))
4895 0 : colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
4896 : else
4897 0 : elog(ERROR, "unrecognized node type in jointree: %d",
4898 : (int) nodeTag(j->rarg));
4899 :
4900 : /* Assert children will be processed earlier than join in second pass */
4901 : Assert(colinfo->leftrti < j->rtindex);
4902 : Assert(colinfo->rightrti < j->rtindex);
4903 :
4904 : /* Initialize result arrays with zeroes */
4905 1170 : numjoincols = list_length(jrte->joinaliasvars);
4906 : Assert(numjoincols == list_length(jrte->eref->colnames));
4907 1170 : colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
4908 1170 : colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
4909 :
4910 : /*
4911 : * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
4912 : * Recall that the column(s) merged due to USING are the first column(s)
4913 : * of the join output. We need not do anything special while scanning
4914 : * joinleftcols, but while scanning joinrightcols we must distinguish
4915 : * merged from unmerged columns.
4916 : */
4917 1170 : jcolno = 0;
4918 24316 : foreach(lc, jrte->joinleftcols)
4919 : {
4920 23146 : int leftattno = lfirst_int(lc);
4921 :
4922 23146 : colinfo->leftattnos[jcolno++] = leftattno;
4923 : }
4924 1170 : rcolno = 0;
4925 11606 : foreach(lc, jrte->joinrightcols)
4926 : {
4927 10436 : int rightattno = lfirst_int(lc);
4928 :
4929 10436 : if (rcolno < jrte->joinmergedcols) /* merged column? */
4930 580 : colinfo->rightattnos[rcolno] = rightattno;
4931 : else
4932 9856 : colinfo->rightattnos[jcolno++] = rightattno;
4933 10436 : rcolno++;
4934 : }
4935 : Assert(jcolno == numjoincols);
4936 1170 : }
4937 :
4938 : /*
4939 : * get_rtable_name: convenience function to get a previously assigned RTE alias
4940 : *
4941 : * The RTE must belong to the topmost namespace level in "context".
4942 : */
4943 : static char *
4944 5604 : get_rtable_name(int rtindex, deparse_context *context)
4945 : {
4946 5604 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
4947 :
4948 : Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
4949 5604 : return (char *) list_nth(dpns->rtable_names, rtindex - 1);
4950 : }
4951 :
4952 : /*
4953 : * set_deparse_plan: set up deparse_namespace to parse subexpressions
4954 : * of a given Plan node
4955 : *
4956 : * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
4957 : * and index_tlist fields. Caller must already have adjusted the ancestors
4958 : * list if necessary. Note that the rtable, subplans, and ctes fields do
4959 : * not need to change when shifting attention to different plan nodes in a
4960 : * single plan tree.
4961 : */
4962 : static void
4963 100944 : set_deparse_plan(deparse_namespace *dpns, Plan *plan)
4964 : {
4965 100944 : dpns->plan = plan;
4966 :
4967 : /*
4968 : * We special-case Append and MergeAppend to pretend that the first child
4969 : * plan is the OUTER referent; we have to interpret OUTER Vars in their
4970 : * tlists according to one of the children, and the first one is the most
4971 : * natural choice.
4972 : */
4973 100944 : if (IsA(plan, Append))
4974 3832 : dpns->outer_plan = linitial(((Append *) plan)->appendplans);
4975 97112 : else if (IsA(plan, MergeAppend))
4976 434 : dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
4977 : else
4978 96678 : dpns->outer_plan = outerPlan(plan);
4979 :
4980 100944 : if (dpns->outer_plan)
4981 44860 : dpns->outer_tlist = dpns->outer_plan->targetlist;
4982 : else
4983 56084 : dpns->outer_tlist = NIL;
4984 :
4985 : /*
4986 : * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
4987 : * use OUTER because that could someday conflict with the normal meaning.)
4988 : * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
4989 : * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
4990 : * that as INNER referent.
4991 : *
4992 : * For MERGE, make the inner tlist point to the merge source tlist, which
4993 : * is same as the targetlist that the ModifyTable's source plan provides.
4994 : * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the
4995 : * excluded expression's tlist. (Similar to the SubqueryScan we don't want
4996 : * to reuse OUTER, it's used for RETURNING in some modify table cases,
4997 : * although not INSERT .. CONFLICT).
4998 : */
4999 100944 : if (IsA(plan, SubqueryScan))
5000 584 : dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5001 100360 : else if (IsA(plan, CteScan))
5002 528 : dpns->inner_plan = list_nth(dpns->subplans,
5003 528 : ((CteScan *) plan)->ctePlanId - 1);
5004 99832 : else if (IsA(plan, WorkTableScan))
5005 174 : dpns->inner_plan = find_recursive_union(dpns,
5006 : (WorkTableScan *) plan);
5007 99658 : else if (IsA(plan, ModifyTable))
5008 214 : dpns->inner_plan = plan;
5009 : else
5010 99444 : dpns->inner_plan = innerPlan(plan);
5011 :
5012 100944 : if (IsA(plan, ModifyTable))
5013 : {
5014 214 : if (((ModifyTable *) plan)->operation == CMD_MERGE)
5015 6 : dpns->inner_tlist = dpns->outer_tlist;
5016 : else
5017 208 : dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
5018 : }
5019 100730 : else if (dpns->inner_plan)
5020 15856 : dpns->inner_tlist = dpns->inner_plan->targetlist;
5021 : else
5022 84874 : dpns->inner_tlist = NIL;
5023 :
5024 : /* Set up referent for INDEX_VAR Vars, if needed */
5025 100944 : if (IsA(plan, IndexOnlyScan))
5026 2568 : dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5027 98376 : else if (IsA(plan, ForeignScan))
5028 2714 : dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5029 95662 : else if (IsA(plan, CustomScan))
5030 0 : dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5031 : else
5032 95662 : dpns->index_tlist = NIL;
5033 100944 : }
5034 :
5035 : /*
5036 : * Locate the ancestor plan node that is the RecursiveUnion generating
5037 : * the WorkTableScan's work table. We can match on wtParam, since that
5038 : * should be unique within the plan tree.
5039 : */
5040 : static Plan *
5041 174 : find_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)
5042 : {
5043 : ListCell *lc;
5044 :
5045 438 : foreach(lc, dpns->ancestors)
5046 : {
5047 438 : Plan *ancestor = (Plan *) lfirst(lc);
5048 :
5049 438 : if (IsA(ancestor, RecursiveUnion) &&
5050 174 : ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5051 174 : return ancestor;
5052 : }
5053 0 : elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5054 : wtscan->wtParam);
5055 : return NULL;
5056 : }
5057 :
5058 : /*
5059 : * push_child_plan: temporarily transfer deparsing attention to a child plan
5060 : *
5061 : * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
5062 : * deparse context in case the referenced expression itself uses
5063 : * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
5064 : * affecting levelsup issues (although in a Plan tree there really shouldn't
5065 : * be any).
5066 : *
5067 : * Caller must provide a local deparse_namespace variable to save the
5068 : * previous state for pop_child_plan.
5069 : */
5070 : static void
5071 53654 : push_child_plan(deparse_namespace *dpns, Plan *plan,
5072 : deparse_namespace *save_dpns)
5073 : {
5074 : /* Save state for restoration later */
5075 53654 : *save_dpns = *dpns;
5076 :
5077 : /* Link current plan node into ancestors list */
5078 53654 : dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5079 :
5080 : /* Set attention on selected child */
5081 53654 : set_deparse_plan(dpns, plan);
5082 53654 : }
5083 :
5084 : /*
5085 : * pop_child_plan: undo the effects of push_child_plan
5086 : */
5087 : static void
5088 53654 : pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5089 : {
5090 : List *ancestors;
5091 :
5092 : /* Get rid of ancestors list cell added by push_child_plan */
5093 53654 : ancestors = list_delete_first(dpns->ancestors);
5094 :
5095 : /* Restore fields changed by push_child_plan */
5096 53654 : *dpns = *save_dpns;
5097 :
5098 : /* Make sure dpns->ancestors is right (may be unnecessary) */
5099 53654 : dpns->ancestors = ancestors;
5100 53654 : }
5101 :
5102 : /*
5103 : * push_ancestor_plan: temporarily transfer deparsing attention to an
5104 : * ancestor plan
5105 : *
5106 : * When expanding a Param reference, we must adjust the deparse context
5107 : * to match the plan node that contains the expression being printed;
5108 : * otherwise we'd fail if that expression itself contains a Param or
5109 : * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
5110 : *
5111 : * The target ancestor is conveniently identified by the ListCell holding it
5112 : * in dpns->ancestors.
5113 : *
5114 : * Caller must provide a local deparse_namespace variable to save the
5115 : * previous state for pop_ancestor_plan.
5116 : */
5117 : static void
5118 2914 : push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
5119 : deparse_namespace *save_dpns)
5120 : {
5121 2914 : Plan *plan = (Plan *) lfirst(ancestor_cell);
5122 :
5123 : /* Save state for restoration later */
5124 2914 : *save_dpns = *dpns;
5125 :
5126 : /* Build a new ancestor list with just this node's ancestors */
5127 2914 : dpns->ancestors =
5128 2914 : list_copy_tail(dpns->ancestors,
5129 2914 : list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5130 :
5131 : /* Set attention on selected ancestor */
5132 2914 : set_deparse_plan(dpns, plan);
5133 2914 : }
5134 :
5135 : /*
5136 : * pop_ancestor_plan: undo the effects of push_ancestor_plan
5137 : */
5138 : static void
5139 2914 : pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5140 : {
5141 : /* Free the ancestor list made in push_ancestor_plan */
5142 2914 : list_free(dpns->ancestors);
5143 :
5144 : /* Restore fields changed by push_ancestor_plan */
5145 2914 : *dpns = *save_dpns;
5146 2914 : }
5147 :
5148 :
5149 : /* ----------
5150 : * make_ruledef - reconstruct the CREATE RULE command
5151 : * for a given pg_rewrite tuple
5152 : * ----------
5153 : */
5154 : static void
5155 532 : make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5156 : int prettyFlags)
5157 : {
5158 : char *rulename;
5159 : char ev_type;
5160 : Oid ev_class;
5161 : bool is_instead;
5162 : char *ev_qual;
5163 : char *ev_action;
5164 : List *actions;
5165 : Relation ev_relation;
5166 532 : TupleDesc viewResultDesc = NULL;
5167 : int fno;
5168 : Datum dat;
5169 : bool isnull;
5170 :
5171 : /*
5172 : * Get the attribute values from the rules tuple
5173 : */
5174 532 : fno = SPI_fnumber(rulettc, "rulename");
5175 532 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5176 : Assert(!isnull);
5177 532 : rulename = NameStr(*(DatumGetName(dat)));
5178 :
5179 532 : fno = SPI_fnumber(rulettc, "ev_type");
5180 532 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5181 : Assert(!isnull);
5182 532 : ev_type = DatumGetChar(dat);
5183 :
5184 532 : fno = SPI_fnumber(rulettc, "ev_class");
5185 532 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5186 : Assert(!isnull);
5187 532 : ev_class = DatumGetObjectId(dat);
5188 :
5189 532 : fno = SPI_fnumber(rulettc, "is_instead");
5190 532 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5191 : Assert(!isnull);
5192 532 : is_instead = DatumGetBool(dat);
5193 :
5194 532 : fno = SPI_fnumber(rulettc, "ev_qual");
5195 532 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5196 : Assert(ev_qual != NULL);
5197 :
5198 532 : fno = SPI_fnumber(rulettc, "ev_action");
5199 532 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5200 : Assert(ev_action != NULL);
5201 532 : actions = (List *) stringToNode(ev_action);
5202 532 : if (actions == NIL)
5203 0 : elog(ERROR, "invalid empty ev_action list");
5204 :
5205 532 : ev_relation = table_open(ev_class, AccessShareLock);
5206 :
5207 : /*
5208 : * Build the rules definition text
5209 : */
5210 532 : appendStringInfo(buf, "CREATE RULE %s AS",
5211 : quote_identifier(rulename));
5212 :
5213 532 : if (prettyFlags & PRETTYFLAG_INDENT)
5214 532 : appendStringInfoString(buf, "\n ON ");
5215 : else
5216 0 : appendStringInfoString(buf, " ON ");
5217 :
5218 : /* The event the rule is fired for */
5219 532 : switch (ev_type)
5220 : {
5221 6 : case '1':
5222 6 : appendStringInfoString(buf, "SELECT");
5223 6 : viewResultDesc = RelationGetDescr(ev_relation);
5224 6 : break;
5225 :
5226 146 : case '2':
5227 146 : appendStringInfoString(buf, "UPDATE");
5228 146 : break;
5229 :
5230 276 : case '3':
5231 276 : appendStringInfoString(buf, "INSERT");
5232 276 : break;
5233 :
5234 104 : case '4':
5235 104 : appendStringInfoString(buf, "DELETE");
5236 104 : break;
5237 :
5238 0 : default:
5239 0 : ereport(ERROR,
5240 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5241 : errmsg("rule \"%s\" has unsupported event type %d",
5242 : rulename, ev_type)));
5243 : break;
5244 : }
5245 :
5246 : /* The relation the rule is fired on */
5247 532 : appendStringInfo(buf, " TO %s",
5248 532 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
5249 96 : generate_relation_name(ev_class, NIL) :
5250 436 : generate_qualified_relation_name(ev_class));
5251 :
5252 : /* If the rule has an event qualification, add it */
5253 532 : if (strcmp(ev_qual, "<>") != 0)
5254 : {
5255 : Node *qual;
5256 : Query *query;
5257 : deparse_context context;
5258 : deparse_namespace dpns;
5259 :
5260 118 : if (prettyFlags & PRETTYFLAG_INDENT)
5261 118 : appendStringInfoString(buf, "\n ");
5262 118 : appendStringInfoString(buf, " WHERE ");
5263 :
5264 118 : qual = stringToNode(ev_qual);
5265 :
5266 : /*
5267 : * We need to make a context for recognizing any Vars in the qual
5268 : * (which can only be references to OLD and NEW). Use the rtable of
5269 : * the first query in the action list for this purpose.
5270 : */
5271 118 : query = (Query *) linitial(actions);
5272 :
5273 : /*
5274 : * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5275 : * into the SELECT, and that's what we need to look at. (Ugly kluge
5276 : * ... try to fix this when we redesign querytrees.)
5277 : */
5278 118 : query = getInsertSelectQuery(query, NULL);
5279 :
5280 : /* Must acquire locks right away; see notes in get_query_def() */
5281 118 : AcquireRewriteLocks(query, false, false);
5282 :
5283 118 : context.buf = buf;
5284 118 : context.namespaces = list_make1(&dpns);
5285 118 : context.windowClause = NIL;
5286 118 : context.windowTList = NIL;
5287 118 : context.varprefix = (list_length(query->rtable) != 1);
5288 118 : context.prettyFlags = prettyFlags;
5289 118 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
5290 118 : context.indentLevel = PRETTYINDENT_STD;
5291 118 : context.special_exprkind = EXPR_KIND_NONE;
5292 118 : context.appendparents = NULL;
5293 :
5294 118 : set_deparse_for_query(&dpns, query, NIL);
5295 :
5296 118 : get_rule_expr(qual, &context, false);
5297 : }
5298 :
5299 532 : appendStringInfoString(buf, " DO ");
5300 :
5301 : /* The INSTEAD keyword (if so) */
5302 532 : if (is_instead)
5303 326 : appendStringInfoString(buf, "INSTEAD ");
5304 :
5305 : /* Finally the rules actions */
5306 532 : if (list_length(actions) > 1)
5307 : {
5308 : ListCell *action;
5309 : Query *query;
5310 :
5311 20 : appendStringInfoChar(buf, '(');
5312 60 : foreach(action, actions)
5313 : {
5314 40 : query = (Query *) lfirst(action);
5315 40 : get_query_def(query, buf, NIL, viewResultDesc, true,
5316 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5317 40 : if (prettyFlags)
5318 40 : appendStringInfoString(buf, ";\n");
5319 : else
5320 0 : appendStringInfoString(buf, "; ");
5321 : }
5322 20 : appendStringInfoString(buf, ");");
5323 : }
5324 : else
5325 : {
5326 : Query *query;
5327 :
5328 512 : query = (Query *) linitial(actions);
5329 512 : get_query_def(query, buf, NIL, viewResultDesc, true,
5330 : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5331 512 : appendStringInfoChar(buf, ';');
5332 : }
5333 :
5334 532 : table_close(ev_relation, AccessShareLock);
5335 532 : }
5336 :
5337 :
5338 : /* ----------
5339 : * make_viewdef - reconstruct the SELECT part of a
5340 : * view rewrite rule
5341 : * ----------
5342 : */
5343 : static void
5344 2984 : make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5345 : int prettyFlags, int wrapColumn)
5346 : {
5347 : Query *query;
5348 : char ev_type;
5349 : Oid ev_class;
5350 : bool is_instead;
5351 : char *ev_qual;
5352 : char *ev_action;
5353 : List *actions;
5354 : Relation ev_relation;
5355 : int fno;
5356 : Datum dat;
5357 : bool isnull;
5358 :
5359 : /*
5360 : * Get the attribute values from the rules tuple
5361 : */
5362 2984 : fno = SPI_fnumber(rulettc, "ev_type");
5363 2984 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5364 : Assert(!isnull);
5365 2984 : ev_type = DatumGetChar(dat);
5366 :
5367 2984 : fno = SPI_fnumber(rulettc, "ev_class");
5368 2984 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5369 : Assert(!isnull);
5370 2984 : ev_class = DatumGetObjectId(dat);
5371 :
5372 2984 : fno = SPI_fnumber(rulettc, "is_instead");
5373 2984 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5374 : Assert(!isnull);
5375 2984 : is_instead = DatumGetBool(dat);
5376 :
5377 2984 : fno = SPI_fnumber(rulettc, "ev_qual");
5378 2984 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5379 : Assert(ev_qual != NULL);
5380 :
5381 2984 : fno = SPI_fnumber(rulettc, "ev_action");
5382 2984 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5383 : Assert(ev_action != NULL);
5384 2984 : actions = (List *) stringToNode(ev_action);
5385 :
5386 2984 : if (list_length(actions) != 1)
5387 : {
5388 : /* keep output buffer empty and leave */
5389 0 : return;
5390 : }
5391 :
5392 2984 : query = (Query *) linitial(actions);
5393 :
5394 2984 : if (ev_type != '1' || !is_instead ||
5395 2984 : strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5396 : {
5397 : /* keep output buffer empty and leave */
5398 0 : return;
5399 : }
5400 :
5401 2984 : ev_relation = table_open(ev_class, AccessShareLock);
5402 :
5403 2984 : get_query_def(query, buf, NIL, RelationGetDescr(ev_relation), true,
5404 : prettyFlags, wrapColumn, 0);
5405 2984 : appendStringInfoChar(buf, ';');
5406 :
5407 2984 : table_close(ev_relation, AccessShareLock);
5408 : }
5409 :
5410 :
5411 : /* ----------
5412 : * get_query_def - Parse back one query parsetree
5413 : *
5414 : * query: parsetree to be displayed
5415 : * buf: output text is appended to buf
5416 : * parentnamespace: list (initially empty) of outer-level deparse_namespace's
5417 : * resultDesc: if not NULL, the output tuple descriptor for the view
5418 : * represented by a SELECT query. We use the column names from it
5419 : * to label SELECT output columns, in preference to names in the query
5420 : * colNamesVisible: true if the surrounding context cares about the output
5421 : * column names at all (as, for example, an EXISTS() context does not);
5422 : * when false, we can suppress dummy column labels such as "?column?"
5423 : * prettyFlags: bitmask of PRETTYFLAG_XXX options
5424 : * wrapColumn: maximum line length, or -1 to disable wrapping
5425 : * startIndent: initial indentation amount
5426 : * ----------
5427 : */
5428 : static void
5429 4916 : get_query_def(Query *query, StringInfo buf, List *parentnamespace,
5430 : TupleDesc resultDesc, bool colNamesVisible,
5431 : int prettyFlags, int wrapColumn, int startIndent)
5432 : {
5433 : deparse_context context;
5434 : deparse_namespace dpns;
5435 :
5436 : /* Guard against excessively long or deeply-nested queries */
5437 4916 : CHECK_FOR_INTERRUPTS();
5438 4916 : check_stack_depth();
5439 :
5440 : /*
5441 : * Before we begin to examine the query, acquire locks on referenced
5442 : * relations, and fix up deleted columns in JOIN RTEs. This ensures
5443 : * consistent results. Note we assume it's OK to scribble on the passed
5444 : * querytree!
5445 : *
5446 : * We are only deparsing the query (we are not about to execute it), so we
5447 : * only need AccessShareLock on the relations it mentions.
5448 : */
5449 4916 : AcquireRewriteLocks(query, false, false);
5450 :
5451 4916 : context.buf = buf;
5452 4916 : context.namespaces = lcons(&dpns, list_copy(parentnamespace));
5453 4916 : context.windowClause = NIL;
5454 4916 : context.windowTList = NIL;
5455 8452 : context.varprefix = (parentnamespace != NIL ||
5456 3536 : list_length(query->rtable) != 1);
5457 4916 : context.prettyFlags = prettyFlags;
5458 4916 : context.wrapColumn = wrapColumn;
5459 4916 : context.indentLevel = startIndent;
5460 4916 : context.special_exprkind = EXPR_KIND_NONE;
5461 4916 : context.appendparents = NULL;
5462 :
5463 4916 : set_deparse_for_query(&dpns, query, parentnamespace);
5464 :
5465 4916 : switch (query->commandType)
5466 : {
5467 4328 : case CMD_SELECT:
5468 4328 : get_select_query_def(query, &context, resultDesc, colNamesVisible);
5469 4328 : break;
5470 :
5471 130 : case CMD_UPDATE:
5472 130 : get_update_query_def(query, &context, colNamesVisible);
5473 130 : break;
5474 :
5475 322 : case CMD_INSERT:
5476 322 : get_insert_query_def(query, &context, colNamesVisible);
5477 322 : break;
5478 :
5479 76 : case CMD_DELETE:
5480 76 : get_delete_query_def(query, &context, colNamesVisible);
5481 76 : break;
5482 :
5483 6 : case CMD_MERGE:
5484 6 : get_merge_query_def(query, &context, colNamesVisible);
5485 6 : break;
5486 :
5487 38 : case CMD_NOTHING:
5488 38 : appendStringInfoString(buf, "NOTHING");
5489 38 : break;
5490 :
5491 16 : case CMD_UTILITY:
5492 16 : get_utility_query_def(query, &context);
5493 16 : break;
5494 :
5495 0 : default:
5496 0 : elog(ERROR, "unrecognized query command type: %d",
5497 : query->commandType);
5498 : break;
5499 : }
5500 4916 : }
5501 :
5502 : /* ----------
5503 : * get_values_def - Parse back a VALUES list
5504 : * ----------
5505 : */
5506 : static void
5507 260 : get_values_def(List *values_lists, deparse_context *context)
5508 : {
5509 260 : StringInfo buf = context->buf;
5510 260 : bool first_list = true;
5511 : ListCell *vtl;
5512 :
5513 260 : appendStringInfoString(buf, "VALUES ");
5514 :
5515 748 : foreach(vtl, values_lists)
5516 : {
5517 488 : List *sublist = (List *) lfirst(vtl);
5518 488 : bool first_col = true;
5519 : ListCell *lc;
5520 :
5521 488 : if (first_list)
5522 260 : first_list = false;
5523 : else
5524 228 : appendStringInfoString(buf, ", ");
5525 :
5526 488 : appendStringInfoChar(buf, '(');
5527 1910 : foreach(lc, sublist)
5528 : {
5529 1422 : Node *col = (Node *) lfirst(lc);
5530 :
5531 1422 : if (first_col)
5532 488 : first_col = false;
5533 : else
5534 934 : appendStringInfoChar(buf, ',');
5535 :
5536 : /*
5537 : * Print the value. Whole-row Vars need special treatment.
5538 : */
5539 1422 : get_rule_expr_toplevel(col, context, false);
5540 : }
5541 488 : appendStringInfoChar(buf, ')');
5542 : }
5543 260 : }
5544 :
5545 : /* ----------
5546 : * get_with_clause - Parse back a WITH clause
5547 : * ----------
5548 : */
5549 : static void
5550 4862 : get_with_clause(Query *query, deparse_context *context)
5551 : {
5552 4862 : StringInfo buf = context->buf;
5553 : const char *sep;
5554 : ListCell *l;
5555 :
5556 4862 : if (query->cteList == NIL)
5557 4772 : return;
5558 :
5559 90 : if (PRETTY_INDENT(context))
5560 : {
5561 90 : context->indentLevel += PRETTYINDENT_STD;
5562 90 : appendStringInfoChar(buf, ' ');
5563 : }
5564 :
5565 90 : if (query->hasRecursive)
5566 56 : sep = "WITH RECURSIVE ";
5567 : else
5568 34 : sep = "WITH ";
5569 218 : foreach(l, query->cteList)
5570 : {
5571 128 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
5572 :
5573 128 : appendStringInfoString(buf, sep);
5574 128 : appendStringInfoString(buf, quote_identifier(cte->ctename));
5575 128 : if (cte->aliascolnames)
5576 : {
5577 56 : bool first = true;
5578 : ListCell *col;
5579 :
5580 56 : appendStringInfoChar(buf, '(');
5581 148 : foreach(col, cte->aliascolnames)
5582 : {
5583 92 : if (first)
5584 56 : first = false;
5585 : else
5586 36 : appendStringInfoString(buf, ", ");
5587 92 : appendStringInfoString(buf,
5588 92 : quote_identifier(strVal(lfirst(col))));
5589 : }
5590 56 : appendStringInfoChar(buf, ')');
5591 : }
5592 128 : appendStringInfoString(buf, " AS ");
5593 128 : switch (cte->ctematerialized)
5594 : {
5595 110 : case CTEMaterializeDefault:
5596 110 : break;
5597 18 : case CTEMaterializeAlways:
5598 18 : appendStringInfoString(buf, "MATERIALIZED ");
5599 18 : break;
5600 0 : case CTEMaterializeNever:
5601 0 : appendStringInfoString(buf, "NOT MATERIALIZED ");
5602 0 : break;
5603 : }
5604 128 : appendStringInfoChar(buf, '(');
5605 128 : if (PRETTY_INDENT(context))
5606 128 : appendContextKeyword(context, "", 0, 0, 0);
5607 128 : get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
5608 : true,
5609 : context->prettyFlags, context->wrapColumn,
5610 : context->indentLevel);
5611 128 : if (PRETTY_INDENT(context))
5612 128 : appendContextKeyword(context, "", 0, 0, 0);
5613 128 : appendStringInfoChar(buf, ')');
5614 :
5615 128 : if (cte->search_clause)
5616 : {
5617 6 : bool first = true;
5618 : ListCell *lc;
5619 :
5620 6 : appendStringInfo(buf, " SEARCH %s FIRST BY ",
5621 6 : cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
5622 :
5623 18 : foreach(lc, cte->search_clause->search_col_list)
5624 : {
5625 12 : if (first)
5626 6 : first = false;
5627 : else
5628 6 : appendStringInfoString(buf, ", ");
5629 12 : appendStringInfoString(buf,
5630 12 : quote_identifier(strVal(lfirst(lc))));
5631 : }
5632 :
5633 6 : appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
5634 : }
5635 :
5636 128 : if (cte->cycle_clause)
5637 : {
5638 12 : bool first = true;
5639 : ListCell *lc;
5640 :
5641 12 : appendStringInfoString(buf, " CYCLE ");
5642 :
5643 36 : foreach(lc, cte->cycle_clause->cycle_col_list)
5644 : {
5645 24 : if (first)
5646 12 : first = false;
5647 : else
5648 12 : appendStringInfoString(buf, ", ");
5649 24 : appendStringInfoString(buf,
5650 24 : quote_identifier(strVal(lfirst(lc))));
5651 : }
5652 :
5653 12 : appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
5654 :
5655 : {
5656 12 : Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
5657 12 : Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
5658 :
5659 18 : if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
5660 6 : cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
5661 : {
5662 6 : appendStringInfoString(buf, " TO ");
5663 6 : get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
5664 6 : appendStringInfoString(buf, " DEFAULT ");
5665 6 : get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
5666 : }
5667 : }
5668 :
5669 12 : appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
5670 : }
5671 :
5672 128 : sep = ", ";
5673 : }
5674 :
5675 90 : if (PRETTY_INDENT(context))
5676 : {
5677 90 : context->indentLevel -= PRETTYINDENT_STD;
5678 90 : appendContextKeyword(context, "", 0, 0, 0);
5679 : }
5680 : else
5681 0 : appendStringInfoChar(buf, ' ');
5682 : }
5683 :
5684 : /* ----------
5685 : * get_select_query_def - Parse back a SELECT parsetree
5686 : * ----------
5687 : */
5688 : static void
5689 4328 : get_select_query_def(Query *query, deparse_context *context,
5690 : TupleDesc resultDesc, bool colNamesVisible)
5691 : {
5692 4328 : StringInfo buf = context->buf;
5693 : List *save_windowclause;
5694 : List *save_windowtlist;
5695 : bool force_colno;
5696 : ListCell *l;
5697 :
5698 : /* Insert the WITH clause if given */
5699 4328 : get_with_clause(query, context);
5700 :
5701 : /* Set up context for possible window functions */
5702 4328 : save_windowclause = context->windowClause;
5703 4328 : context->windowClause = query->windowClause;
5704 4328 : save_windowtlist = context->windowTList;
5705 4328 : context->windowTList = query->targetList;
5706 :
5707 : /*
5708 : * If the Query node has a setOperations tree, then it's the top level of
5709 : * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
5710 : * fields are interesting in the top query itself.
5711 : */
5712 4328 : if (query->setOperations)
5713 : {
5714 144 : get_setop_query(query->setOperations, query, context, resultDesc,
5715 : colNamesVisible);
5716 : /* ORDER BY clauses must be simple in this case */
5717 144 : force_colno = true;
5718 : }
5719 : else
5720 : {
5721 4184 : get_basic_select_query(query, context, resultDesc, colNamesVisible);
5722 4184 : force_colno = false;
5723 : }
5724 :
5725 : /* Add the ORDER BY clause if given */
5726 4328 : if (query->sortClause != NIL)
5727 : {
5728 126 : appendContextKeyword(context, " ORDER BY ",
5729 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5730 126 : get_rule_orderby(query->sortClause, query->targetList,
5731 : force_colno, context);
5732 : }
5733 :
5734 : /*
5735 : * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
5736 : * standard spelling of LIMIT.
5737 : */
5738 4328 : if (query->limitOffset != NULL)
5739 : {
5740 32 : appendContextKeyword(context, " OFFSET ",
5741 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5742 32 : get_rule_expr(query->limitOffset, context, false);
5743 : }
5744 4328 : if (query->limitCount != NULL)
5745 : {
5746 70 : if (query->limitOption == LIMIT_OPTION_WITH_TIES)
5747 : {
5748 32 : appendContextKeyword(context, " FETCH FIRST ",
5749 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5750 32 : get_rule_expr(query->limitCount, context, false);
5751 32 : appendStringInfoString(buf, " ROWS WITH TIES");
5752 : }
5753 : else
5754 : {
5755 38 : appendContextKeyword(context, " LIMIT ",
5756 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5757 38 : if (IsA(query->limitCount, Const) &&
5758 16 : ((Const *) query->limitCount)->constisnull)
5759 16 : appendStringInfoString(buf, "ALL");
5760 : else
5761 22 : get_rule_expr(query->limitCount, context, false);
5762 : }
5763 : }
5764 :
5765 : /* Add FOR [KEY] UPDATE/SHARE clauses if present */
5766 4328 : if (query->hasForUpdate)
5767 : {
5768 12 : foreach(l, query->rowMarks)
5769 : {
5770 6 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
5771 :
5772 : /* don't print implicit clauses */
5773 6 : if (rc->pushedDown)
5774 0 : continue;
5775 :
5776 6 : switch (rc->strength)
5777 : {
5778 0 : case LCS_NONE:
5779 : /* we intentionally throw an error for LCS_NONE */
5780 0 : elog(ERROR, "unrecognized LockClauseStrength %d",
5781 : (int) rc->strength);
5782 : break;
5783 0 : case LCS_FORKEYSHARE:
5784 0 : appendContextKeyword(context, " FOR KEY SHARE",
5785 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5786 0 : break;
5787 0 : case LCS_FORSHARE:
5788 0 : appendContextKeyword(context, " FOR SHARE",
5789 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5790 0 : break;
5791 0 : case LCS_FORNOKEYUPDATE:
5792 0 : appendContextKeyword(context, " FOR NO KEY UPDATE",
5793 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5794 0 : break;
5795 6 : case LCS_FORUPDATE:
5796 6 : appendContextKeyword(context, " FOR UPDATE",
5797 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5798 6 : break;
5799 : }
5800 :
5801 6 : appendStringInfo(buf, " OF %s",
5802 6 : quote_identifier(get_rtable_name(rc->rti,
5803 : context)));
5804 6 : if (rc->waitPolicy == LockWaitError)
5805 0 : appendStringInfoString(buf, " NOWAIT");
5806 6 : else if (rc->waitPolicy == LockWaitSkip)
5807 0 : appendStringInfoString(buf, " SKIP LOCKED");
5808 : }
5809 : }
5810 :
5811 4328 : context->windowClause = save_windowclause;
5812 4328 : context->windowTList = save_windowtlist;
5813 4328 : }
5814 :
5815 : /*
5816 : * Detect whether query looks like SELECT ... FROM VALUES(),
5817 : * with no need to rename the output columns of the VALUES RTE.
5818 : * If so, return the VALUES RTE. Otherwise return NULL.
5819 : */
5820 : static RangeTblEntry *
5821 4184 : get_simple_values_rte(Query *query, TupleDesc resultDesc)
5822 : {
5823 4184 : RangeTblEntry *result = NULL;
5824 : ListCell *lc;
5825 :
5826 : /*
5827 : * We want to detect a match even if the Query also contains OLD or NEW
5828 : * rule RTEs. So the idea is to scan the rtable and see if there is only
5829 : * one inFromCl RTE that is a VALUES RTE.
5830 : */
5831 4530 : foreach(lc, query->rtable)
5832 : {
5833 3802 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
5834 :
5835 3802 : if (rte->rtekind == RTE_VALUES && rte->inFromCl)
5836 : {
5837 222 : if (result)
5838 3456 : return NULL; /* multiple VALUES (probably not possible) */
5839 222 : result = rte;
5840 : }
5841 3580 : else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
5842 124 : continue; /* ignore rule entries */
5843 : else
5844 3456 : return NULL; /* something else -> not simple VALUES */
5845 : }
5846 :
5847 : /*
5848 : * We don't need to check the targetlist in any great detail, because
5849 : * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
5850 : * appear inside auto-generated sub-queries with very restricted
5851 : * structure. However, DefineView might have modified the tlist by
5852 : * injecting new column aliases, or we might have some other column
5853 : * aliases forced by a resultDesc. We can only simplify if the RTE's
5854 : * column names match the names that get_target_list() would select.
5855 : */
5856 728 : if (result)
5857 : {
5858 : ListCell *lcn;
5859 : int colno;
5860 :
5861 222 : if (list_length(query->targetList) != list_length(result->eref->colnames))
5862 0 : return NULL; /* this probably cannot happen */
5863 222 : colno = 0;
5864 830 : forboth(lc, query->targetList, lcn, result->eref->colnames)
5865 : {
5866 620 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
5867 620 : char *cname = strVal(lfirst(lcn));
5868 : char *colname;
5869 :
5870 620 : if (tle->resjunk)
5871 12 : return NULL; /* this probably cannot happen */
5872 :
5873 : /* compute name that get_target_list would use for column */
5874 620 : colno++;
5875 620 : if (resultDesc && colno <= resultDesc->natts)
5876 30 : colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
5877 : else
5878 590 : colname = tle->resname;
5879 :
5880 : /* does it match the VALUES RTE? */
5881 620 : if (colname == NULL || strcmp(colname, cname) != 0)
5882 12 : return NULL; /* column name has been changed */
5883 : }
5884 : }
5885 :
5886 716 : return result;
5887 : }
5888 :
5889 : static void
5890 4184 : get_basic_select_query(Query *query, deparse_context *context,
5891 : TupleDesc resultDesc, bool colNamesVisible)
5892 : {
5893 4184 : StringInfo buf = context->buf;
5894 : RangeTblEntry *values_rte;
5895 : char *sep;
5896 : ListCell *l;
5897 :
5898 4184 : if (PRETTY_INDENT(context))
5899 : {
5900 4138 : context->indentLevel += PRETTYINDENT_STD;
5901 4138 : appendStringInfoChar(buf, ' ');
5902 : }
5903 :
5904 : /*
5905 : * If the query looks like SELECT * FROM (VALUES ...), then print just the
5906 : * VALUES part. This reverses what transformValuesClause() did at parse
5907 : * time.
5908 : */
5909 4184 : values_rte = get_simple_values_rte(query, resultDesc);
5910 4184 : if (values_rte)
5911 : {
5912 210 : get_values_def(values_rte->values_lists, context);
5913 210 : return;
5914 : }
5915 :
5916 : /*
5917 : * Build up the query string - first we say SELECT
5918 : */
5919 3974 : if (query->isReturn)
5920 52 : appendStringInfoString(buf, "RETURN");
5921 : else
5922 3922 : appendStringInfoString(buf, "SELECT");
5923 :
5924 : /* Add the DISTINCT clause if given */
5925 3974 : if (query->distinctClause != NIL)
5926 : {
5927 0 : if (query->hasDistinctOn)
5928 : {
5929 0 : appendStringInfoString(buf, " DISTINCT ON (");
5930 0 : sep = "";
5931 0 : foreach(l, query->distinctClause)
5932 : {
5933 0 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
5934 :
5935 0 : appendStringInfoString(buf, sep);
5936 0 : get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
5937 : false, context);
5938 0 : sep = ", ";
5939 : }
5940 0 : appendStringInfoChar(buf, ')');
5941 : }
5942 : else
5943 0 : appendStringInfoString(buf, " DISTINCT");
5944 : }
5945 :
5946 : /* Then we tell what to select (the targetlist) */
5947 3974 : get_target_list(query->targetList, context, resultDesc, colNamesVisible);
5948 :
5949 : /* Add the FROM clause if needed */
5950 3974 : get_from_clause(query, " FROM ", context);
5951 :
5952 : /* Add the WHERE clause if given */
5953 3974 : if (query->jointree->quals != NULL)
5954 : {
5955 1172 : appendContextKeyword(context, " WHERE ",
5956 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5957 1172 : get_rule_expr(query->jointree->quals, context, false);
5958 : }
5959 :
5960 : /* Add the GROUP BY clause if given */
5961 3974 : if (query->groupClause != NULL || query->groupingSets != NULL)
5962 : {
5963 : ParseExprKind save_exprkind;
5964 :
5965 210 : appendContextKeyword(context, " GROUP BY ",
5966 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5967 210 : if (query->groupDistinct)
5968 0 : appendStringInfoString(buf, "DISTINCT ");
5969 :
5970 210 : save_exprkind = context->special_exprkind;
5971 210 : context->special_exprkind = EXPR_KIND_GROUP_BY;
5972 :
5973 210 : if (query->groupingSets == NIL)
5974 : {
5975 204 : sep = "";
5976 450 : foreach(l, query->groupClause)
5977 : {
5978 246 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
5979 :
5980 246 : appendStringInfoString(buf, sep);
5981 246 : get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
5982 : false, context);
5983 246 : sep = ", ";
5984 : }
5985 : }
5986 : else
5987 : {
5988 6 : sep = "";
5989 12 : foreach(l, query->groupingSets)
5990 : {
5991 6 : GroupingSet *grp = lfirst(l);
5992 :
5993 6 : appendStringInfoString(buf, sep);
5994 6 : get_rule_groupingset(grp, query->targetList, true, context);
5995 6 : sep = ", ";
5996 : }
5997 : }
5998 :
5999 210 : context->special_exprkind = save_exprkind;
6000 : }
6001 :
6002 : /* Add the HAVING clause if given */
6003 3974 : if (query->havingQual != NULL)
6004 : {
6005 10 : appendContextKeyword(context, " HAVING ",
6006 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6007 10 : get_rule_expr(query->havingQual, context, false);
6008 : }
6009 :
6010 : /* Add the WINDOW clause if needed */
6011 3974 : if (query->windowClause != NIL)
6012 42 : get_rule_windowclause(query, context);
6013 : }
6014 :
6015 : /* ----------
6016 : * get_target_list - Parse back a SELECT target list
6017 : *
6018 : * This is also used for RETURNING lists in INSERT/UPDATE/DELETE.
6019 : *
6020 : * resultDesc and colNamesVisible are as for get_query_def()
6021 : * ----------
6022 : */
6023 : static void
6024 4090 : get_target_list(List *targetList, deparse_context *context,
6025 : TupleDesc resultDesc, bool colNamesVisible)
6026 : {
6027 4090 : StringInfo buf = context->buf;
6028 : StringInfoData targetbuf;
6029 4090 : bool last_was_multiline = false;
6030 : char *sep;
6031 : int colno;
6032 : ListCell *l;
6033 :
6034 : /* we use targetbuf to hold each TLE's text temporarily */
6035 4090 : initStringInfo(&targetbuf);
6036 :
6037 4090 : sep = " ";
6038 4090 : colno = 0;
6039 18748 : foreach(l, targetList)
6040 : {
6041 14658 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6042 : char *colname;
6043 : char *attname;
6044 :
6045 14658 : if (tle->resjunk)
6046 30 : continue; /* ignore junk entries */
6047 :
6048 14628 : appendStringInfoString(buf, sep);
6049 14628 : sep = ", ";
6050 14628 : colno++;
6051 :
6052 : /*
6053 : * Put the new field text into targetbuf so we can decide after we've
6054 : * got it whether or not it needs to go on a new line.
6055 : */
6056 14628 : resetStringInfo(&targetbuf);
6057 14628 : context->buf = &targetbuf;
6058 :
6059 : /*
6060 : * We special-case Var nodes rather than using get_rule_expr. This is
6061 : * needed because get_rule_expr will display a whole-row Var as
6062 : * "foo.*", which is the preferred notation in most contexts, but at
6063 : * the top level of a SELECT list it's not right (the parser will
6064 : * expand that notation into multiple columns, yielding behavior
6065 : * different from a whole-row Var). We need to call get_variable
6066 : * directly so that we can tell it to do the right thing, and so that
6067 : * we can get the attribute name which is the default AS label.
6068 : */
6069 14628 : if (tle->expr && (IsA(tle->expr, Var)))
6070 : {
6071 11244 : attname = get_variable((Var *) tle->expr, 0, true, context);
6072 : }
6073 : else
6074 : {
6075 3384 : get_rule_expr((Node *) tle->expr, context, true);
6076 :
6077 : /*
6078 : * When colNamesVisible is true, we should always show the
6079 : * assigned column name explicitly. Otherwise, show it only if
6080 : * it's not FigureColname's fallback.
6081 : */
6082 3384 : attname = colNamesVisible ? NULL : "?column?";
6083 : }
6084 :
6085 : /*
6086 : * Figure out what the result column should be called. In the context
6087 : * of a view, use the view's tuple descriptor (so as to pick up the
6088 : * effects of any column RENAME that's been done on the view).
6089 : * Otherwise, just use what we can find in the TLE.
6090 : */
6091 14628 : if (resultDesc && colno <= resultDesc->natts)
6092 13238 : colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6093 : else
6094 1390 : colname = tle->resname;
6095 :
6096 : /* Show AS unless the column's name is correct as-is */
6097 14628 : if (colname) /* resname could be NULL */
6098 : {
6099 14576 : if (attname == NULL || strcmp(attname, colname) != 0)
6100 4664 : appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
6101 : }
6102 :
6103 : /* Restore context's output buffer */
6104 14628 : context->buf = buf;
6105 :
6106 : /* Consider line-wrapping if enabled */
6107 14628 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
6108 : {
6109 : int leading_nl_pos;
6110 :
6111 : /* Does the new field start with a new line? */
6112 14582 : if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
6113 318 : leading_nl_pos = 0;
6114 : else
6115 14264 : leading_nl_pos = -1;
6116 :
6117 : /* If so, we shouldn't add anything */
6118 14582 : if (leading_nl_pos >= 0)
6119 : {
6120 : /* instead, remove any trailing spaces currently in buf */
6121 318 : removeStringInfoSpaces(buf);
6122 : }
6123 : else
6124 : {
6125 : char *trailing_nl;
6126 :
6127 : /* Locate the start of the current line in the output buffer */
6128 14264 : trailing_nl = strrchr(buf->data, '\n');
6129 14264 : if (trailing_nl == NULL)
6130 4840 : trailing_nl = buf->data;
6131 : else
6132 9424 : trailing_nl++;
6133 :
6134 : /*
6135 : * Add a newline, plus some indentation, if the new field is
6136 : * not the first and either the new field would cause an
6137 : * overflow or the last field used more than one line.
6138 : */
6139 14264 : if (colno > 1 &&
6140 10236 : ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
6141 : last_was_multiline))
6142 10236 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
6143 : PRETTYINDENT_STD, PRETTYINDENT_VAR);
6144 : }
6145 :
6146 : /* Remember this field's multiline status for next iteration */
6147 14582 : last_was_multiline =
6148 14582 : (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
6149 : }
6150 :
6151 : /* Add the new field */
6152 14628 : appendBinaryStringInfo(buf, targetbuf.data, targetbuf.len);
6153 : }
6154 :
6155 : /* clean up */
6156 4090 : pfree(targetbuf.data);
6157 4090 : }
6158 :
6159 : static void
6160 608 : get_setop_query(Node *setOp, Query *query, deparse_context *context,
6161 : TupleDesc resultDesc, bool colNamesVisible)
6162 : {
6163 608 : StringInfo buf = context->buf;
6164 : bool need_paren;
6165 :
6166 : /* Guard against excessively long or deeply-nested queries */
6167 608 : CHECK_FOR_INTERRUPTS();
6168 608 : check_stack_depth();
6169 :
6170 608 : if (IsA(setOp, RangeTblRef))
6171 : {
6172 376 : RangeTblRef *rtr = (RangeTblRef *) setOp;
6173 376 : RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
6174 376 : Query *subquery = rte->subquery;
6175 :
6176 : Assert(subquery != NULL);
6177 : Assert(subquery->setOperations == NULL);
6178 : /* Need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y */
6179 1128 : need_paren = (subquery->cteList ||
6180 376 : subquery->sortClause ||
6181 376 : subquery->rowMarks ||
6182 1128 : subquery->limitOffset ||
6183 376 : subquery->limitCount);
6184 376 : if (need_paren)
6185 0 : appendStringInfoChar(buf, '(');
6186 376 : get_query_def(subquery, buf, context->namespaces, resultDesc,
6187 : colNamesVisible,
6188 : context->prettyFlags, context->wrapColumn,
6189 : context->indentLevel);
6190 376 : if (need_paren)
6191 0 : appendStringInfoChar(buf, ')');
6192 : }
6193 232 : else if (IsA(setOp, SetOperationStmt))
6194 : {
6195 232 : SetOperationStmt *op = (SetOperationStmt *) setOp;
6196 : int subindent;
6197 :
6198 : /*
6199 : * We force parens when nesting two SetOperationStmts, except when the
6200 : * lefthand input is another setop of the same kind. Syntactically,
6201 : * we could omit parens in rather more cases, but it seems best to use
6202 : * parens to flag cases where the setop operator changes. If we use
6203 : * parens, we also increase the indentation level for the child query.
6204 : *
6205 : * There are some cases in which parens are needed around a leaf query
6206 : * too, but those are more easily handled at the next level down (see
6207 : * code above).
6208 : */
6209 232 : if (IsA(op->larg, SetOperationStmt))
6210 : {
6211 88 : SetOperationStmt *lop = (SetOperationStmt *) op->larg;
6212 :
6213 88 : if (op->op == lop->op && op->all == lop->all)
6214 88 : need_paren = false;
6215 : else
6216 0 : need_paren = true;
6217 : }
6218 : else
6219 144 : need_paren = false;
6220 :
6221 232 : if (need_paren)
6222 : {
6223 0 : appendStringInfoChar(buf, '(');
6224 0 : subindent = PRETTYINDENT_STD;
6225 0 : appendContextKeyword(context, "", subindent, 0, 0);
6226 : }
6227 : else
6228 232 : subindent = 0;
6229 :
6230 232 : get_setop_query(op->larg, query, context, resultDesc, colNamesVisible);
6231 :
6232 232 : if (need_paren)
6233 0 : appendContextKeyword(context, ") ", -subindent, 0, 0);
6234 232 : else if (PRETTY_INDENT(context))
6235 232 : appendContextKeyword(context, "", -subindent, 0, 0);
6236 : else
6237 0 : appendStringInfoChar(buf, ' ');
6238 :
6239 232 : switch (op->op)
6240 : {
6241 232 : case SETOP_UNION:
6242 232 : appendStringInfoString(buf, "UNION ");
6243 232 : break;
6244 0 : case SETOP_INTERSECT:
6245 0 : appendStringInfoString(buf, "INTERSECT ");
6246 0 : break;
6247 0 : case SETOP_EXCEPT:
6248 0 : appendStringInfoString(buf, "EXCEPT ");
6249 0 : break;
6250 0 : default:
6251 0 : elog(ERROR, "unrecognized set op: %d",
6252 : (int) op->op);
6253 : }
6254 232 : if (op->all)
6255 220 : appendStringInfoString(buf, "ALL ");
6256 :
6257 : /* Always parenthesize if RHS is another setop */
6258 232 : need_paren = IsA(op->rarg, SetOperationStmt);
6259 :
6260 : /*
6261 : * The indentation code here is deliberately a bit different from that
6262 : * for the lefthand input, because we want the line breaks in
6263 : * different places.
6264 : */
6265 232 : if (need_paren)
6266 : {
6267 0 : appendStringInfoChar(buf, '(');
6268 0 : subindent = PRETTYINDENT_STD;
6269 : }
6270 : else
6271 232 : subindent = 0;
6272 232 : appendContextKeyword(context, "", subindent, 0, 0);
6273 :
6274 232 : get_setop_query(op->rarg, query, context, resultDesc, false);
6275 :
6276 232 : if (PRETTY_INDENT(context))
6277 232 : context->indentLevel -= subindent;
6278 232 : if (need_paren)
6279 0 : appendContextKeyword(context, ")", 0, 0, 0);
6280 : }
6281 : else
6282 : {
6283 0 : elog(ERROR, "unrecognized node type: %d",
6284 : (int) nodeTag(setOp));
6285 : }
6286 608 : }
6287 :
6288 : /*
6289 : * Display a sort/group clause.
6290 : *
6291 : * Also returns the expression tree, so caller need not find it again.
6292 : */
6293 : static Node *
6294 578 : get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
6295 : deparse_context *context)
6296 : {
6297 578 : StringInfo buf = context->buf;
6298 : TargetEntry *tle;
6299 : Node *expr;
6300 :
6301 578 : tle = get_sortgroupref_tle(ref, tlist);
6302 578 : expr = (Node *) tle->expr;
6303 :
6304 : /*
6305 : * Use column-number form if requested by caller. Otherwise, if
6306 : * expression is a constant, force it to be dumped with an explicit cast
6307 : * as decoration --- this is because a simple integer constant is
6308 : * ambiguous (and will be misinterpreted by findTargetlistEntry()) if we
6309 : * dump it without any decoration. If it's anything more complex than a
6310 : * simple Var, then force extra parens around it, to ensure it can't be
6311 : * misinterpreted as a cube() or rollup() construct.
6312 : */
6313 578 : if (force_colno)
6314 : {
6315 : Assert(!tle->resjunk);
6316 0 : appendStringInfo(buf, "%d", tle->resno);
6317 : }
6318 578 : else if (expr && IsA(expr, Const))
6319 0 : get_const_expr((Const *) expr, context, 1);
6320 578 : else if (!expr || IsA(expr, Var))
6321 554 : get_rule_expr(expr, context, true);
6322 : else
6323 : {
6324 : /*
6325 : * We must force parens for function-like expressions even if
6326 : * PRETTY_PAREN is off, since those are the ones in danger of
6327 : * misparsing. For other expressions we need to force them only if
6328 : * PRETTY_PAREN is on, since otherwise the expression will output them
6329 : * itself. (We can't skip the parens.)
6330 : */
6331 48 : bool need_paren = (PRETTY_PAREN(context)
6332 24 : || IsA(expr, FuncExpr)
6333 20 : || IsA(expr, Aggref)
6334 20 : || IsA(expr, WindowFunc)
6335 48 : || IsA(expr, JsonConstructorExpr));
6336 :
6337 24 : if (need_paren)
6338 4 : appendStringInfoChar(context->buf, '(');
6339 24 : get_rule_expr(expr, context, true);
6340 24 : if (need_paren)
6341 4 : appendStringInfoChar(context->buf, ')');
6342 : }
6343 :
6344 578 : return expr;
6345 : }
6346 :
6347 : /*
6348 : * Display a GroupingSet
6349 : */
6350 : static void
6351 18 : get_rule_groupingset(GroupingSet *gset, List *targetlist,
6352 : bool omit_parens, deparse_context *context)
6353 : {
6354 : ListCell *l;
6355 18 : StringInfo buf = context->buf;
6356 18 : bool omit_child_parens = true;
6357 18 : char *sep = "";
6358 :
6359 18 : switch (gset->kind)
6360 : {
6361 0 : case GROUPING_SET_EMPTY:
6362 0 : appendStringInfoString(buf, "()");
6363 0 : return;
6364 :
6365 12 : case GROUPING_SET_SIMPLE:
6366 : {
6367 12 : if (!omit_parens || list_length(gset->content) != 1)
6368 12 : appendStringInfoChar(buf, '(');
6369 :
6370 42 : foreach(l, gset->content)
6371 : {
6372 30 : Index ref = lfirst_int(l);
6373 :
6374 30 : appendStringInfoString(buf, sep);
6375 30 : get_rule_sortgroupclause(ref, targetlist,
6376 : false, context);
6377 30 : sep = ", ";
6378 : }
6379 :
6380 12 : if (!omit_parens || list_length(gset->content) != 1)
6381 12 : appendStringInfoChar(buf, ')');
6382 : }
6383 12 : return;
6384 :
6385 6 : case GROUPING_SET_ROLLUP:
6386 6 : appendStringInfoString(buf, "ROLLUP(");
6387 6 : break;
6388 0 : case GROUPING_SET_CUBE:
6389 0 : appendStringInfoString(buf, "CUBE(");
6390 0 : break;
6391 0 : case GROUPING_SET_SETS:
6392 0 : appendStringInfoString(buf, "GROUPING SETS (");
6393 0 : omit_child_parens = false;
6394 0 : break;
6395 : }
6396 :
6397 18 : foreach(l, gset->content)
6398 : {
6399 12 : appendStringInfoString(buf, sep);
6400 12 : get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
6401 12 : sep = ", ";
6402 : }
6403 :
6404 6 : appendStringInfoChar(buf, ')');
6405 : }
6406 :
6407 : /*
6408 : * Display an ORDER BY list.
6409 : */
6410 : static void
6411 270 : get_rule_orderby(List *orderList, List *targetList,
6412 : bool force_colno, deparse_context *context)
6413 : {
6414 270 : StringInfo buf = context->buf;
6415 : const char *sep;
6416 : ListCell *l;
6417 :
6418 270 : sep = "";
6419 572 : foreach(l, orderList)
6420 : {
6421 302 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6422 : Node *sortexpr;
6423 : Oid sortcoltype;
6424 : TypeCacheEntry *typentry;
6425 :
6426 302 : appendStringInfoString(buf, sep);
6427 302 : sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
6428 : force_colno, context);
6429 302 : sortcoltype = exprType(sortexpr);
6430 : /* See whether operator is default < or > for datatype */
6431 302 : typentry = lookup_type_cache(sortcoltype,
6432 : TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
6433 302 : if (srt->sortop == typentry->lt_opr)
6434 : {
6435 : /* ASC is default, so emit nothing for it */
6436 274 : if (srt->nulls_first)
6437 0 : appendStringInfoString(buf, " NULLS FIRST");
6438 : }
6439 28 : else if (srt->sortop == typentry->gt_opr)
6440 : {
6441 10 : appendStringInfoString(buf, " DESC");
6442 : /* DESC defaults to NULLS FIRST */
6443 10 : if (!srt->nulls_first)
6444 2 : appendStringInfoString(buf, " NULLS LAST");
6445 : }
6446 : else
6447 : {
6448 18 : appendStringInfo(buf, " USING %s",
6449 : generate_operator_name(srt->sortop,
6450 : sortcoltype,
6451 : sortcoltype));
6452 : /* be specific to eliminate ambiguity */
6453 18 : if (srt->nulls_first)
6454 0 : appendStringInfoString(buf, " NULLS FIRST");
6455 : else
6456 18 : appendStringInfoString(buf, " NULLS LAST");
6457 : }
6458 302 : sep = ", ";
6459 : }
6460 270 : }
6461 :
6462 : /*
6463 : * Display a WINDOW clause.
6464 : *
6465 : * Note that the windowClause list might contain only anonymous window
6466 : * specifications, in which case we should print nothing here.
6467 : */
6468 : static void
6469 42 : get_rule_windowclause(Query *query, deparse_context *context)
6470 : {
6471 42 : StringInfo buf = context->buf;
6472 : const char *sep;
6473 : ListCell *l;
6474 :
6475 42 : sep = NULL;
6476 84 : foreach(l, query->windowClause)
6477 : {
6478 42 : WindowClause *wc = (WindowClause *) lfirst(l);
6479 :
6480 42 : if (wc->name == NULL)
6481 42 : continue; /* ignore anonymous windows */
6482 :
6483 0 : if (sep == NULL)
6484 0 : appendContextKeyword(context, " WINDOW ",
6485 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6486 : else
6487 0 : appendStringInfoString(buf, sep);
6488 :
6489 0 : appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
6490 :
6491 0 : get_rule_windowspec(wc, query->targetList, context);
6492 :
6493 0 : sep = ", ";
6494 : }
6495 42 : }
6496 :
6497 : /*
6498 : * Display a window definition
6499 : */
6500 : static void
6501 42 : get_rule_windowspec(WindowClause *wc, List *targetList,
6502 : deparse_context *context)
6503 : {
6504 42 : StringInfo buf = context->buf;
6505 42 : bool needspace = false;
6506 : const char *sep;
6507 : ListCell *l;
6508 :
6509 42 : appendStringInfoChar(buf, '(');
6510 42 : if (wc->refname)
6511 : {
6512 0 : appendStringInfoString(buf, quote_identifier(wc->refname));
6513 0 : needspace = true;
6514 : }
6515 : /* partition clauses are always inherited, so only print if no refname */
6516 42 : if (wc->partitionClause && !wc->refname)
6517 : {
6518 0 : if (needspace)
6519 0 : appendStringInfoChar(buf, ' ');
6520 0 : appendStringInfoString(buf, "PARTITION BY ");
6521 0 : sep = "";
6522 0 : foreach(l, wc->partitionClause)
6523 : {
6524 0 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6525 :
6526 0 : appendStringInfoString(buf, sep);
6527 0 : get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
6528 : false, context);
6529 0 : sep = ", ";
6530 : }
6531 0 : needspace = true;
6532 : }
6533 : /* print ordering clause only if not inherited */
6534 42 : if (wc->orderClause && !wc->copiedOrder)
6535 : {
6536 42 : if (needspace)
6537 0 : appendStringInfoChar(buf, ' ');
6538 42 : appendStringInfoString(buf, "ORDER BY ");
6539 42 : get_rule_orderby(wc->orderClause, targetList, false, context);
6540 42 : needspace = true;
6541 : }
6542 : /* framing clause is never inherited, so print unless it's default */
6543 42 : if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
6544 : {
6545 42 : if (needspace)
6546 42 : appendStringInfoChar(buf, ' ');
6547 42 : if (wc->frameOptions & FRAMEOPTION_RANGE)
6548 6 : appendStringInfoString(buf, "RANGE ");
6549 36 : else if (wc->frameOptions & FRAMEOPTION_ROWS)
6550 30 : appendStringInfoString(buf, "ROWS ");
6551 6 : else if (wc->frameOptions & FRAMEOPTION_GROUPS)
6552 6 : appendStringInfoString(buf, "GROUPS ");
6553 : else
6554 : Assert(false);
6555 42 : if (wc->frameOptions & FRAMEOPTION_BETWEEN)
6556 42 : appendStringInfoString(buf, "BETWEEN ");
6557 42 : if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
6558 0 : appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
6559 42 : else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW)
6560 0 : appendStringInfoString(buf, "CURRENT ROW ");
6561 42 : else if (wc->frameOptions & FRAMEOPTION_START_OFFSET)
6562 : {
6563 42 : get_rule_expr(wc->startOffset, context, false);
6564 42 : if (wc->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
6565 42 : appendStringInfoString(buf, " PRECEDING ");
6566 0 : else if (wc->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
6567 0 : appendStringInfoString(buf, " FOLLOWING ");
6568 : else
6569 : Assert(false);
6570 : }
6571 : else
6572 : Assert(false);
6573 42 : if (wc->frameOptions & FRAMEOPTION_BETWEEN)
6574 : {
6575 42 : appendStringInfoString(buf, "AND ");
6576 42 : if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
6577 0 : appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
6578 42 : else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW)
6579 0 : appendStringInfoString(buf, "CURRENT ROW ");
6580 42 : else if (wc->frameOptions & FRAMEOPTION_END_OFFSET)
6581 : {
6582 42 : get_rule_expr(wc->endOffset, context, false);
6583 42 : if (wc->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
6584 0 : appendStringInfoString(buf, " PRECEDING ");
6585 42 : else if (wc->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
6586 42 : appendStringInfoString(buf, " FOLLOWING ");
6587 : else
6588 : Assert(false);
6589 : }
6590 : else
6591 : Assert(false);
6592 : }
6593 42 : if (wc->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
6594 6 : appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
6595 36 : else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
6596 6 : appendStringInfoString(buf, "EXCLUDE GROUP ");
6597 30 : else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES)
6598 6 : appendStringInfoString(buf, "EXCLUDE TIES ");
6599 : /* we will now have a trailing space; remove it */
6600 42 : buf->len--;
6601 : }
6602 42 : appendStringInfoChar(buf, ')');
6603 42 : }
6604 :
6605 : /* ----------
6606 : * get_insert_query_def - Parse back an INSERT parsetree
6607 : * ----------
6608 : */
6609 : static void
6610 322 : get_insert_query_def(Query *query, deparse_context *context,
6611 : bool colNamesVisible)
6612 : {
6613 322 : StringInfo buf = context->buf;
6614 322 : RangeTblEntry *select_rte = NULL;
6615 322 : RangeTblEntry *values_rte = NULL;
6616 : RangeTblEntry *rte;
6617 : char *sep;
6618 : ListCell *l;
6619 : List *strippedexprs;
6620 :
6621 : /* Insert the WITH clause if given */
6622 322 : get_with_clause(query, context);
6623 :
6624 : /*
6625 : * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
6626 : * single RTE for the SELECT or VALUES. Plain VALUES has neither.
6627 : */
6628 1250 : foreach(l, query->rtable)
6629 : {
6630 928 : rte = (RangeTblEntry *) lfirst(l);
6631 :
6632 928 : if (rte->rtekind == RTE_SUBQUERY)
6633 : {
6634 44 : if (select_rte)
6635 0 : elog(ERROR, "too many subquery RTEs in INSERT");
6636 44 : select_rte = rte;
6637 : }
6638 :
6639 928 : if (rte->rtekind == RTE_VALUES)
6640 : {
6641 38 : if (values_rte)
6642 0 : elog(ERROR, "too many values RTEs in INSERT");
6643 38 : values_rte = rte;
6644 : }
6645 : }
6646 322 : if (select_rte && values_rte)
6647 0 : elog(ERROR, "both subquery and values RTEs in INSERT");
6648 :
6649 : /*
6650 : * Start the query with INSERT INTO relname
6651 : */
6652 322 : rte = rt_fetch(query->resultRelation, query->rtable);
6653 : Assert(rte->rtekind == RTE_RELATION);
6654 :
6655 322 : if (PRETTY_INDENT(context))
6656 : {
6657 322 : context->indentLevel += PRETTYINDENT_STD;
6658 322 : appendStringInfoChar(buf, ' ');
6659 : }
6660 322 : appendStringInfo(buf, "INSERT INTO %s",
6661 : generate_relation_name(rte->relid, NIL));
6662 :
6663 : /* Print the relation alias, if needed; INSERT requires explicit AS */
6664 322 : get_rte_alias(rte, query->resultRelation, true, context);
6665 :
6666 : /* always want a space here */
6667 322 : appendStringInfoChar(buf, ' ');
6668 :
6669 : /*
6670 : * Add the insert-column-names list. Any indirection decoration needed on
6671 : * the column names can be inferred from the top targetlist.
6672 : */
6673 322 : strippedexprs = NIL;
6674 322 : sep = "";
6675 322 : if (query->targetList)
6676 322 : appendStringInfoChar(buf, '(');
6677 1188 : foreach(l, query->targetList)
6678 : {
6679 866 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6680 :
6681 866 : if (tle->resjunk)
6682 0 : continue; /* ignore junk entries */
6683 :
6684 866 : appendStringInfoString(buf, sep);
6685 866 : sep = ", ";
6686 :
6687 : /*
6688 : * Put out name of target column; look in the catalogs, not at
6689 : * tle->resname, since resname will fail to track RENAME.
6690 : */
6691 866 : appendStringInfoString(buf,
6692 866 : quote_identifier(get_attname(rte->relid,
6693 866 : tle->resno,
6694 : false)));
6695 :
6696 : /*
6697 : * Print any indirection needed (subfields or subscripts), and strip
6698 : * off the top-level nodes representing the indirection assignments.
6699 : * Add the stripped expressions to strippedexprs. (If it's a
6700 : * single-VALUES statement, the stripped expressions are the VALUES to
6701 : * print below. Otherwise they're just Vars and not really
6702 : * interesting.)
6703 : */
6704 866 : strippedexprs = lappend(strippedexprs,
6705 866 : processIndirection((Node *) tle->expr,
6706 : context));
6707 : }
6708 322 : if (query->targetList)
6709 322 : appendStringInfoString(buf, ") ");
6710 :
6711 322 : if (query->override)
6712 : {
6713 0 : if (query->override == OVERRIDING_SYSTEM_VALUE)
6714 0 : appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
6715 0 : else if (query->override == OVERRIDING_USER_VALUE)
6716 0 : appendStringInfoString(buf, "OVERRIDING USER VALUE ");
6717 : }
6718 :
6719 322 : if (select_rte)
6720 : {
6721 : /* Add the SELECT */
6722 44 : get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
6723 : false,
6724 : context->prettyFlags, context->wrapColumn,
6725 : context->indentLevel);
6726 : }
6727 278 : else if (values_rte)
6728 : {
6729 : /* Add the multi-VALUES expression lists */
6730 38 : get_values_def(values_rte->values_lists, context);
6731 : }
6732 240 : else if (strippedexprs)
6733 : {
6734 : /* Add the single-VALUES expression list */
6735 240 : appendContextKeyword(context, "VALUES (",
6736 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
6737 240 : get_rule_list_toplevel(strippedexprs, context, false);
6738 240 : appendStringInfoChar(buf, ')');
6739 : }
6740 : else
6741 : {
6742 : /* No expressions, so it must be DEFAULT VALUES */
6743 0 : appendStringInfoString(buf, "DEFAULT VALUES");
6744 : }
6745 :
6746 : /* Add ON CONFLICT if present */
6747 322 : if (query->onConflict)
6748 : {
6749 30 : OnConflictExpr *confl = query->onConflict;
6750 :
6751 30 : appendStringInfoString(buf, " ON CONFLICT");
6752 :
6753 30 : if (confl->arbiterElems)
6754 : {
6755 : /* Add the single-VALUES expression list */
6756 24 : appendStringInfoChar(buf, '(');
6757 24 : get_rule_expr((Node *) confl->arbiterElems, context, false);
6758 24 : appendStringInfoChar(buf, ')');
6759 :
6760 : /* Add a WHERE clause (for partial indexes) if given */
6761 24 : if (confl->arbiterWhere != NULL)
6762 : {
6763 : bool save_varprefix;
6764 :
6765 : /*
6766 : * Force non-prefixing of Vars, since parser assumes that they
6767 : * belong to target relation. WHERE clause does not use
6768 : * InferenceElem, so this is separately required.
6769 : */
6770 12 : save_varprefix = context->varprefix;
6771 12 : context->varprefix = false;
6772 :
6773 12 : appendContextKeyword(context, " WHERE ",
6774 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6775 12 : get_rule_expr(confl->arbiterWhere, context, false);
6776 :
6777 12 : context->varprefix = save_varprefix;
6778 : }
6779 : }
6780 6 : else if (OidIsValid(confl->constraint))
6781 : {
6782 0 : char *constraint = get_constraint_name(confl->constraint);
6783 :
6784 0 : if (!constraint)
6785 0 : elog(ERROR, "cache lookup failed for constraint %u",
6786 : confl->constraint);
6787 0 : appendStringInfo(buf, " ON CONSTRAINT %s",
6788 : quote_identifier(constraint));
6789 : }
6790 :
6791 30 : if (confl->action == ONCONFLICT_NOTHING)
6792 : {
6793 18 : appendStringInfoString(buf, " DO NOTHING");
6794 : }
6795 : else
6796 : {
6797 12 : appendStringInfoString(buf, " DO UPDATE SET ");
6798 : /* Deparse targetlist */
6799 12 : get_update_query_targetlist_def(query, confl->onConflictSet,
6800 : context, rte);
6801 :
6802 : /* Add a WHERE clause if given */
6803 12 : if (confl->onConflictWhere != NULL)
6804 : {
6805 12 : appendContextKeyword(context, " WHERE ",
6806 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6807 12 : get_rule_expr(confl->onConflictWhere, context, false);
6808 : }
6809 : }
6810 : }
6811 :
6812 : /* Add RETURNING if present */
6813 322 : if (query->returningList)
6814 : {
6815 78 : appendContextKeyword(context, " RETURNING",
6816 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6817 78 : get_target_list(query->returningList, context, NULL, colNamesVisible);
6818 : }
6819 322 : }
6820 :
6821 :
6822 : /* ----------
6823 : * get_update_query_def - Parse back an UPDATE parsetree
6824 : * ----------
6825 : */
6826 : static void
6827 130 : get_update_query_def(Query *query, deparse_context *context,
6828 : bool colNamesVisible)
6829 : {
6830 130 : StringInfo buf = context->buf;
6831 : RangeTblEntry *rte;
6832 :
6833 : /* Insert the WITH clause if given */
6834 130 : get_with_clause(query, context);
6835 :
6836 : /*
6837 : * Start the query with UPDATE relname SET
6838 : */
6839 130 : rte = rt_fetch(query->resultRelation, query->rtable);
6840 : Assert(rte->rtekind == RTE_RELATION);
6841 130 : if (PRETTY_INDENT(context))
6842 : {
6843 130 : appendStringInfoChar(buf, ' ');
6844 130 : context->indentLevel += PRETTYINDENT_STD;
6845 : }
6846 260 : appendStringInfo(buf, "UPDATE %s%s",
6847 130 : only_marker(rte),
6848 : generate_relation_name(rte->relid, NIL));
6849 :
6850 : /* Print the relation alias, if needed */
6851 130 : get_rte_alias(rte, query->resultRelation, false, context);
6852 :
6853 130 : appendStringInfoString(buf, " SET ");
6854 :
6855 : /* Deparse targetlist */
6856 130 : get_update_query_targetlist_def(query, query->targetList, context, rte);
6857 :
6858 : /* Add the FROM clause if needed */
6859 130 : get_from_clause(query, " FROM ", context);
6860 :
6861 : /* Add a WHERE clause if given */
6862 130 : if (query->jointree->quals != NULL)
6863 : {
6864 114 : appendContextKeyword(context, " WHERE ",
6865 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6866 114 : get_rule_expr(query->jointree->quals, context, false);
6867 : }
6868 :
6869 : /* Add RETURNING if present */
6870 130 : if (query->returningList)
6871 : {
6872 22 : appendContextKeyword(context, " RETURNING",
6873 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6874 22 : get_target_list(query->returningList, context, NULL, colNamesVisible);
6875 : }
6876 130 : }
6877 :
6878 :
6879 : /* ----------
6880 : * get_update_query_targetlist_def - Parse back an UPDATE targetlist
6881 : * ----------
6882 : */
6883 : static void
6884 160 : get_update_query_targetlist_def(Query *query, List *targetList,
6885 : deparse_context *context, RangeTblEntry *rte)
6886 : {
6887 160 : StringInfo buf = context->buf;
6888 : ListCell *l;
6889 : ListCell *next_ma_cell;
6890 : int remaining_ma_columns;
6891 : const char *sep;
6892 : SubLink *cur_ma_sublink;
6893 : List *ma_sublinks;
6894 :
6895 : /*
6896 : * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
6897 : * into a list. We expect them to appear, in ID order, in resjunk tlist
6898 : * entries.
6899 : */
6900 160 : ma_sublinks = NIL;
6901 160 : if (query->hasSubLinks) /* else there can't be any */
6902 : {
6903 30 : foreach(l, targetList)
6904 : {
6905 24 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6906 :
6907 24 : if (tle->resjunk && IsA(tle->expr, SubLink))
6908 : {
6909 6 : SubLink *sl = (SubLink *) tle->expr;
6910 :
6911 6 : if (sl->subLinkType == MULTIEXPR_SUBLINK)
6912 : {
6913 6 : ma_sublinks = lappend(ma_sublinks, sl);
6914 : Assert(sl->subLinkId == list_length(ma_sublinks));
6915 : }
6916 : }
6917 : }
6918 : }
6919 160 : next_ma_cell = list_head(ma_sublinks);
6920 160 : cur_ma_sublink = NULL;
6921 160 : remaining_ma_columns = 0;
6922 :
6923 : /* Add the comma separated list of 'attname = value' */
6924 160 : sep = "";
6925 428 : foreach(l, targetList)
6926 : {
6927 268 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6928 : Node *expr;
6929 :
6930 268 : if (tle->resjunk)
6931 6 : continue; /* ignore junk entries */
6932 :
6933 : /* Emit separator (OK whether we're in multiassignment or not) */
6934 262 : appendStringInfoString(buf, sep);
6935 262 : sep = ", ";
6936 :
6937 : /*
6938 : * Check to see if we're starting a multiassignment group: if so,
6939 : * output a left paren.
6940 : */
6941 262 : if (next_ma_cell != NULL && cur_ma_sublink == NULL)
6942 : {
6943 : /*
6944 : * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
6945 : * Param. That could be buried under FieldStores and
6946 : * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
6947 : * and underneath those there could be an implicit type coercion.
6948 : * Because we would ignore implicit type coercions anyway, we
6949 : * don't need to be as careful as processIndirection() is about
6950 : * descending past implicit CoerceToDomains.
6951 : */
6952 6 : expr = (Node *) tle->expr;
6953 12 : while (expr)
6954 : {
6955 12 : if (IsA(expr, FieldStore))
6956 : {
6957 0 : FieldStore *fstore = (FieldStore *) expr;
6958 :
6959 0 : expr = (Node *) linitial(fstore->newvals);
6960 : }
6961 12 : else if (IsA(expr, SubscriptingRef))
6962 : {
6963 6 : SubscriptingRef *sbsref = (SubscriptingRef *) expr;
6964 :
6965 6 : if (sbsref->refassgnexpr == NULL)
6966 0 : break;
6967 :
6968 6 : expr = (Node *) sbsref->refassgnexpr;
6969 : }
6970 6 : else if (IsA(expr, CoerceToDomain))
6971 : {
6972 0 : CoerceToDomain *cdomain = (CoerceToDomain *) expr;
6973 :
6974 0 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
6975 0 : break;
6976 0 : expr = (Node *) cdomain->arg;
6977 : }
6978 : else
6979 6 : break;
6980 : }
6981 6 : expr = strip_implicit_coercions(expr);
6982 :
6983 6 : if (expr && IsA(expr, Param) &&
6984 6 : ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
6985 : {
6986 6 : cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
6987 6 : next_ma_cell = lnext(ma_sublinks, next_ma_cell);
6988 6 : remaining_ma_columns = count_nonjunk_tlist_entries(((Query *) cur_ma_sublink->subselect)->targetList);
6989 : Assert(((Param *) expr)->paramid ==
6990 : ((cur_ma_sublink->subLinkId << 16) | 1));
6991 6 : appendStringInfoChar(buf, '(');
6992 : }
6993 : }
6994 :
6995 : /*
6996 : * Put out name of target column; look in the catalogs, not at
6997 : * tle->resname, since resname will fail to track RENAME.
6998 : */
6999 262 : appendStringInfoString(buf,
7000 262 : quote_identifier(get_attname(rte->relid,
7001 262 : tle->resno,
7002 : false)));
7003 :
7004 : /*
7005 : * Print any indirection needed (subfields or subscripts), and strip
7006 : * off the top-level nodes representing the indirection assignments.
7007 : */
7008 262 : expr = processIndirection((Node *) tle->expr, context);
7009 :
7010 : /*
7011 : * If we're in a multiassignment, skip printing anything more, unless
7012 : * this is the last column; in which case, what we print should be the
7013 : * sublink, not the Param.
7014 : */
7015 262 : if (cur_ma_sublink != NULL)
7016 : {
7017 18 : if (--remaining_ma_columns > 0)
7018 12 : continue; /* not the last column of multiassignment */
7019 6 : appendStringInfoChar(buf, ')');
7020 6 : expr = (Node *) cur_ma_sublink;
7021 6 : cur_ma_sublink = NULL;
7022 : }
7023 :
7024 250 : appendStringInfoString(buf, " = ");
7025 :
7026 250 : get_rule_expr(expr, context, false);
7027 : }
7028 160 : }
7029 :
7030 :
7031 : /* ----------
7032 : * get_delete_query_def - Parse back a DELETE parsetree
7033 : * ----------
7034 : */
7035 : static void
7036 76 : get_delete_query_def(Query *query, deparse_context *context,
7037 : bool colNamesVisible)
7038 : {
7039 76 : StringInfo buf = context->buf;
7040 : RangeTblEntry *rte;
7041 :
7042 : /* Insert the WITH clause if given */
7043 76 : get_with_clause(query, context);
7044 :
7045 : /*
7046 : * Start the query with DELETE FROM relname
7047 : */
7048 76 : rte = rt_fetch(query->resultRelation, query->rtable);
7049 : Assert(rte->rtekind == RTE_RELATION);
7050 76 : if (PRETTY_INDENT(context))
7051 : {
7052 76 : appendStringInfoChar(buf, ' ');
7053 76 : context->indentLevel += PRETTYINDENT_STD;
7054 : }
7055 152 : appendStringInfo(buf, "DELETE FROM %s%s",
7056 76 : only_marker(rte),
7057 : generate_relation_name(rte->relid, NIL));
7058 :
7059 : /* Print the relation alias, if needed */
7060 76 : get_rte_alias(rte, query->resultRelation, false, context);
7061 :
7062 : /* Add the USING clause if given */
7063 76 : get_from_clause(query, " USING ", context);
7064 :
7065 : /* Add a WHERE clause if given */
7066 76 : if (query->jointree->quals != NULL)
7067 : {
7068 76 : appendContextKeyword(context, " WHERE ",
7069 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7070 76 : get_rule_expr(query->jointree->quals, context, false);
7071 : }
7072 :
7073 : /* Add RETURNING if present */
7074 76 : if (query->returningList)
7075 : {
7076 16 : appendContextKeyword(context, " RETURNING",
7077 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7078 16 : get_target_list(query->returningList, context, NULL, colNamesVisible);
7079 : }
7080 76 : }
7081 :
7082 :
7083 : /* ----------
7084 : * get_merge_query_def - Parse back a MERGE parsetree
7085 : * ----------
7086 : */
7087 : static void
7088 6 : get_merge_query_def(Query *query, deparse_context *context,
7089 : bool colNamesVisible)
7090 : {
7091 6 : StringInfo buf = context->buf;
7092 : RangeTblEntry *rte;
7093 : ListCell *lc;
7094 :
7095 : /* Insert the WITH clause if given */
7096 6 : get_with_clause(query, context);
7097 :
7098 : /*
7099 : * Start the query with MERGE INTO relname
7100 : */
7101 6 : rte = rt_fetch(query->resultRelation, query->rtable);
7102 : Assert(rte->rtekind == RTE_RELATION);
7103 6 : if (PRETTY_INDENT(context))
7104 : {
7105 6 : appendStringInfoChar(buf, ' ');
7106 6 : context->indentLevel += PRETTYINDENT_STD;
7107 : }
7108 12 : appendStringInfo(buf, "MERGE INTO %s%s",
7109 6 : only_marker(rte),
7110 : generate_relation_name(rte->relid, NIL));
7111 :
7112 : /* Print the relation alias, if needed */
7113 6 : get_rte_alias(rte, query->resultRelation, false, context);
7114 :
7115 : /* Print the source relation and join clause */
7116 6 : get_from_clause(query, " USING ", context);
7117 6 : appendContextKeyword(context, " ON ",
7118 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7119 6 : get_rule_expr(query->jointree->quals, context, false);
7120 :
7121 : /* Print each merge action */
7122 66 : foreach(lc, query->mergeActionList)
7123 : {
7124 60 : MergeAction *action = lfirst_node(MergeAction, lc);
7125 :
7126 60 : appendContextKeyword(context, " WHEN ",
7127 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7128 60 : appendStringInfo(buf, "%sMATCHED", action->matched ? "" : "NOT ");
7129 :
7130 60 : if (action->qual)
7131 : {
7132 48 : appendContextKeyword(context, " AND ",
7133 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7134 48 : get_rule_expr(action->qual, context, false);
7135 : }
7136 60 : appendContextKeyword(context, " THEN ",
7137 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7138 :
7139 60 : if (action->commandType == CMD_INSERT)
7140 : {
7141 : /* This generally matches get_insert_query_def() */
7142 30 : List *strippedexprs = NIL;
7143 30 : const char *sep = "";
7144 : ListCell *lc2;
7145 :
7146 30 : appendStringInfoString(buf, "INSERT");
7147 :
7148 30 : if (action->targetList)
7149 24 : appendStringInfoString(buf, " (");
7150 84 : foreach(lc2, action->targetList)
7151 : {
7152 54 : TargetEntry *tle = (TargetEntry *) lfirst(lc2);
7153 :
7154 : Assert(!tle->resjunk);
7155 :
7156 54 : appendStringInfoString(buf, sep);
7157 54 : sep = ", ";
7158 :
7159 54 : appendStringInfoString(buf,
7160 54 : quote_identifier(get_attname(rte->relid,
7161 54 : tle->resno,
7162 : false)));
7163 54 : strippedexprs = lappend(strippedexprs,
7164 54 : processIndirection((Node *) tle->expr,
7165 : context));
7166 : }
7167 30 : if (action->targetList)
7168 24 : appendStringInfoChar(buf, ')');
7169 :
7170 30 : if (action->override)
7171 : {
7172 6 : if (action->override == OVERRIDING_SYSTEM_VALUE)
7173 0 : appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
7174 6 : else if (action->override == OVERRIDING_USER_VALUE)
7175 6 : appendStringInfoString(buf, " OVERRIDING USER VALUE");
7176 : }
7177 :
7178 30 : if (strippedexprs)
7179 : {
7180 24 : appendContextKeyword(context, " VALUES (",
7181 : -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);
7182 24 : get_rule_list_toplevel(strippedexprs, context, false);
7183 24 : appendStringInfoChar(buf, ')');
7184 : }
7185 : else
7186 6 : appendStringInfoString(buf, " DEFAULT VALUES");
7187 : }
7188 30 : else if (action->commandType == CMD_UPDATE)
7189 : {
7190 18 : appendStringInfoString(buf, "UPDATE SET ");
7191 18 : get_update_query_targetlist_def(query, action->targetList,
7192 : context, rte);
7193 : }
7194 12 : else if (action->commandType == CMD_DELETE)
7195 6 : appendStringInfoString(buf, "DELETE");
7196 6 : else if (action->commandType == CMD_NOTHING)
7197 6 : appendStringInfoString(buf, "DO NOTHING");
7198 : }
7199 :
7200 : /* No RETURNING support in MERGE yet */
7201 : Assert(query->returningList == NIL);
7202 6 : }
7203 :
7204 :
7205 : /* ----------
7206 : * get_utility_query_def - Parse back a UTILITY parsetree
7207 : * ----------
7208 : */
7209 : static void
7210 16 : get_utility_query_def(Query *query, deparse_context *context)
7211 : {
7212 16 : StringInfo buf = context->buf;
7213 :
7214 16 : if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7215 16 : {
7216 16 : NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7217 :
7218 16 : appendContextKeyword(context, "",
7219 : 0, PRETTYINDENT_STD, 1);
7220 16 : appendStringInfo(buf, "NOTIFY %s",
7221 16 : quote_identifier(stmt->conditionname));
7222 16 : if (stmt->payload)
7223 : {
7224 0 : appendStringInfoString(buf, ", ");
7225 0 : simple_quote_literal(buf, stmt->payload);
7226 : }
7227 : }
7228 : else
7229 : {
7230 : /* Currently only NOTIFY utility commands can appear in rules */
7231 0 : elog(ERROR, "unexpected utility statement type");
7232 : }
7233 16 : }
7234 :
7235 : /*
7236 : * Display a Var appropriately.
7237 : *
7238 : * In some cases (currently only when recursing into an unnamed join)
7239 : * the Var's varlevelsup has to be interpreted with respect to a context
7240 : * above the current one; levelsup indicates the offset.
7241 : *
7242 : * If istoplevel is true, the Var is at the top level of a SELECT's
7243 : * targetlist, which means we need special treatment of whole-row Vars.
7244 : * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
7245 : * dirty hack to prevent "tab.*" from being expanded into multiple columns.
7246 : * (The parser will strip the useless coercion, so no inefficiency is added in
7247 : * dump and reload.) We used to print just "tab" in such cases, but that is
7248 : * ambiguous and will yield the wrong result if "tab" is also a plain column
7249 : * name in the query.
7250 : *
7251 : * Returns the attname of the Var, or NULL if the Var has no attname (because
7252 : * it is a whole-row Var or a subplan output reference).
7253 : */
7254 : static char *
7255 135536 : get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
7256 : {
7257 135536 : StringInfo buf = context->buf;
7258 : RangeTblEntry *rte;
7259 : AttrNumber attnum;
7260 : int netlevelsup;
7261 : deparse_namespace *dpns;
7262 : int varno;
7263 : AttrNumber varattno;
7264 : deparse_columns *colinfo;
7265 : char *refname;
7266 : char *attname;
7267 :
7268 : /* Find appropriate nesting depth */
7269 135536 : netlevelsup = var->varlevelsup + levelsup;
7270 135536 : if (netlevelsup >= list_length(context->namespaces))
7271 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
7272 : var->varlevelsup, levelsup);
7273 135536 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7274 : netlevelsup);
7275 :
7276 : /*
7277 : * If we have a syntactic referent for the Var, and we're working from a
7278 : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
7279 : * on the semantic referent. (Forcing use of the semantic referent when
7280 : * printing plan trees is a design choice that's perhaps more motivated by
7281 : * backwards compatibility than anything else. But it does have the
7282 : * advantage of making plans more explicit.)
7283 : */
7284 135536 : if (var->varnosyn > 0 && dpns->plan == NULL)
7285 : {
7286 28480 : varno = var->varnosyn;
7287 28480 : varattno = var->varattnosyn;
7288 : }
7289 : else
7290 : {
7291 107056 : varno = var->varno;
7292 107056 : varattno = var->varattno;
7293 : }
7294 :
7295 : /*
7296 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
7297 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
7298 : * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
7299 : * find the aliases previously assigned for this RTE.
7300 : */
7301 135536 : if (varno >= 1 && varno <= list_length(dpns->rtable))
7302 : {
7303 : /*
7304 : * We might have been asked to map child Vars to some parent relation.
7305 : */
7306 102720 : if (context->appendparents && dpns->appendrels)
7307 : {
7308 3478 : int pvarno = varno;
7309 3478 : AttrNumber pvarattno = varattno;
7310 3478 : AppendRelInfo *appinfo = dpns->appendrels[pvarno];
7311 3478 : bool found = false;
7312 :
7313 : /* Only map up to inheritance parents, not UNION ALL appendrels */
7314 7070 : while (appinfo &&
7315 3866 : rt_fetch(appinfo->parent_relid,
7316 3866 : dpns->rtable)->rtekind == RTE_RELATION)
7317 : {
7318 3592 : found = false;
7319 3592 : if (pvarattno > 0) /* system columns stay as-is */
7320 : {
7321 3318 : if (pvarattno > appinfo->num_child_cols)
7322 0 : break; /* safety check */
7323 3318 : pvarattno = appinfo->parent_colnos[pvarattno - 1];
7324 3318 : if (pvarattno == 0)
7325 0 : break; /* Var is local to child */
7326 : }
7327 :
7328 3592 : pvarno = appinfo->parent_relid;
7329 3592 : found = true;
7330 :
7331 : /* If the parent is itself a child, continue up. */
7332 : Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
7333 3592 : appinfo = dpns->appendrels[pvarno];
7334 : }
7335 :
7336 : /*
7337 : * If we found an ancestral rel, and that rel is included in
7338 : * appendparents, print that column not the original one.
7339 : */
7340 3478 : if (found && bms_is_member(pvarno, context->appendparents))
7341 : {
7342 2874 : varno = pvarno;
7343 2874 : varattno = pvarattno;
7344 : }
7345 : }
7346 :
7347 102720 : rte = rt_fetch(varno, dpns->rtable);
7348 102720 : refname = (char *) list_nth(dpns->rtable_names, varno - 1);
7349 102720 : colinfo = deparse_columns_fetch(varno, dpns);
7350 102720 : attnum = varattno;
7351 : }
7352 : else
7353 : {
7354 32816 : resolve_special_varno((Node *) var, context,
7355 : get_special_variable, NULL);
7356 32816 : return NULL;
7357 : }
7358 :
7359 : /*
7360 : * The planner will sometimes emit Vars referencing resjunk elements of a
7361 : * subquery's target list (this is currently only possible if it chooses
7362 : * to generate a "physical tlist" for a SubqueryScan or CteScan node).
7363 : * Although we prefer to print subquery-referencing Vars using the
7364 : * subquery's alias, that's not possible for resjunk items since they have
7365 : * no alias. So in that case, drill down to the subplan and print the
7366 : * contents of the referenced tlist item. This works because in a plan
7367 : * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
7368 : * we'll have set dpns->inner_plan to reference the child plan node.
7369 : */
7370 106324 : if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
7371 3604 : attnum > list_length(rte->eref->colnames) &&
7372 2 : dpns->inner_plan)
7373 : {
7374 : TargetEntry *tle;
7375 : deparse_namespace save_dpns;
7376 :
7377 2 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
7378 2 : if (!tle)
7379 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
7380 : attnum, rte->eref->aliasname);
7381 :
7382 : Assert(netlevelsup == 0);
7383 2 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7384 :
7385 : /*
7386 : * Force parentheses because our caller probably assumed a Var is a
7387 : * simple expression.
7388 : */
7389 2 : if (!IsA(tle->expr, Var))
7390 0 : appendStringInfoChar(buf, '(');
7391 2 : get_rule_expr((Node *) tle->expr, context, true);
7392 2 : if (!IsA(tle->expr, Var))
7393 0 : appendStringInfoChar(buf, ')');
7394 :
7395 2 : pop_child_plan(dpns, &save_dpns);
7396 2 : return NULL;
7397 : }
7398 :
7399 : /*
7400 : * If it's an unnamed join, look at the expansion of the alias variable.
7401 : * If it's a simple reference to one of the input vars, then recursively
7402 : * print the name of that var instead. When it's not a simple reference,
7403 : * we have to just print the unqualified join column name. (This can only
7404 : * happen with "dangerous" merged columns in a JOIN USING; we took pains
7405 : * previously to make the unqualified column name unique in such cases.)
7406 : *
7407 : * This wouldn't work in decompiling plan trees, because we don't store
7408 : * joinaliasvars lists after planning; but a plan tree should never
7409 : * contain a join alias variable.
7410 : */
7411 102718 : if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
7412 : {
7413 96 : if (rte->joinaliasvars == NIL)
7414 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
7415 96 : if (attnum > 0)
7416 : {
7417 : Var *aliasvar;
7418 :
7419 96 : aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
7420 : /* we intentionally don't strip implicit coercions here */
7421 96 : if (aliasvar && IsA(aliasvar, Var))
7422 : {
7423 0 : return get_variable(aliasvar, var->varlevelsup + levelsup,
7424 : istoplevel, context);
7425 : }
7426 : }
7427 :
7428 : /*
7429 : * Unnamed join has no refname. (Note: since it's unnamed, there is
7430 : * no way the user could have referenced it to create a whole-row Var
7431 : * for it. So we don't have to cover that case below.)
7432 : */
7433 : Assert(refname == NULL);
7434 : }
7435 :
7436 102718 : if (attnum == InvalidAttrNumber)
7437 810 : attname = NULL;
7438 101908 : else if (attnum > 0)
7439 : {
7440 : /* Get column name to use from the colinfo struct */
7441 100632 : if (attnum > colinfo->num_cols)
7442 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
7443 : attnum, rte->eref->aliasname);
7444 100632 : attname = colinfo->colnames[attnum - 1];
7445 :
7446 : /*
7447 : * If we find a Var referencing a dropped column, it seems better to
7448 : * print something (anything) than to fail. In general this should
7449 : * not happen, but it used to be possible for some cases involving
7450 : * functions returning named composite types, and perhaps there are
7451 : * still bugs out there.
7452 : */
7453 100632 : if (attname == NULL)
7454 6 : attname = "?dropped?column?";
7455 : }
7456 : else
7457 : {
7458 : /* System column - name is fixed, get it from the catalog */
7459 1276 : attname = get_rte_attribute_name(rte, attnum);
7460 : }
7461 :
7462 102718 : if (refname && (context->varprefix || attname == NULL))
7463 : {
7464 48028 : appendStringInfoString(buf, quote_identifier(refname));
7465 48028 : appendStringInfoChar(buf, '.');
7466 : }
7467 102718 : if (attname)
7468 101908 : appendStringInfoString(buf, quote_identifier(attname));
7469 : else
7470 : {
7471 810 : appendStringInfoChar(buf, '*');
7472 810 : if (istoplevel)
7473 72 : appendStringInfo(buf, "::%s",
7474 : format_type_with_typemod(var->vartype,
7475 : var->vartypmod));
7476 : }
7477 :
7478 102718 : return attname;
7479 : }
7480 :
7481 : /*
7482 : * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
7483 : * routine is actually a callback for resolve_special_varno, which handles
7484 : * finding the correct TargetEntry. We get the expression contained in that
7485 : * TargetEntry and just need to deparse it, a job we can throw back on
7486 : * get_rule_expr.
7487 : */
7488 : static void
7489 32816 : get_special_variable(Node *node, deparse_context *context, void *callback_arg)
7490 : {
7491 32816 : StringInfo buf = context->buf;
7492 :
7493 : /*
7494 : * For a non-Var referent, force parentheses because our caller probably
7495 : * assumed a Var is a simple expression.
7496 : */
7497 32816 : if (!IsA(node, Var))
7498 2966 : appendStringInfoChar(buf, '(');
7499 32816 : get_rule_expr(node, context, true);
7500 32816 : if (!IsA(node, Var))
7501 2966 : appendStringInfoChar(buf, ')');
7502 32816 : }
7503 :
7504 : /*
7505 : * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
7506 : * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
7507 : * invoke the callback provided.
7508 : */
7509 : static void
7510 90600 : resolve_special_varno(Node *node, deparse_context *context,
7511 : rsv_callback callback, void *callback_arg)
7512 : {
7513 : Var *var;
7514 : deparse_namespace *dpns;
7515 :
7516 : /* This function is recursive, so let's be paranoid. */
7517 90600 : check_stack_depth();
7518 :
7519 : /* If it's not a Var, invoke the callback. */
7520 90600 : if (!IsA(node, Var))
7521 : {
7522 3226 : (*callback) (node, context, callback_arg);
7523 3226 : return;
7524 : }
7525 :
7526 : /* Find appropriate nesting depth */
7527 87374 : var = (Var *) node;
7528 87374 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7529 87374 : var->varlevelsup);
7530 :
7531 : /*
7532 : * If varno is special, recurse. (Don't worry about varnosyn; if we're
7533 : * here, we already decided not to use that.)
7534 : */
7535 87374 : if (var->varno == OUTER_VAR && dpns->outer_tlist)
7536 : {
7537 : TargetEntry *tle;
7538 : deparse_namespace save_dpns;
7539 : Bitmapset *save_appendparents;
7540 :
7541 42966 : tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
7542 42966 : if (!tle)
7543 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
7544 :
7545 : /*
7546 : * If we're descending to the first child of an Append or MergeAppend,
7547 : * update appendparents. This will affect deparsing of all Vars
7548 : * appearing within the eventually-resolved subexpression.
7549 : */
7550 42966 : save_appendparents = context->appendparents;
7551 :
7552 42966 : if (IsA(dpns->plan, Append))
7553 3880 : context->appendparents = bms_union(context->appendparents,
7554 3880 : ((Append *) dpns->plan)->apprelids);
7555 39086 : else if (IsA(dpns->plan, MergeAppend))
7556 494 : context->appendparents = bms_union(context->appendparents,
7557 494 : ((MergeAppend *) dpns->plan)->apprelids);
7558 :
7559 42966 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
7560 42966 : resolve_special_varno((Node *) tle->expr, context,
7561 : callback, callback_arg);
7562 42966 : pop_child_plan(dpns, &save_dpns);
7563 42966 : context->appendparents = save_appendparents;
7564 42966 : return;
7565 : }
7566 44408 : else if (var->varno == INNER_VAR && dpns->inner_tlist)
7567 : {
7568 : TargetEntry *tle;
7569 : deparse_namespace save_dpns;
7570 :
7571 10572 : tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
7572 10572 : if (!tle)
7573 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
7574 :
7575 10572 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7576 10572 : resolve_special_varno((Node *) tle->expr, context,
7577 : callback, callback_arg);
7578 10572 : pop_child_plan(dpns, &save_dpns);
7579 10572 : return;
7580 : }
7581 33836 : else if (var->varno == INDEX_VAR && dpns->index_tlist)
7582 : {
7583 : TargetEntry *tle;
7584 :
7585 3986 : tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
7586 3986 : if (!tle)
7587 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
7588 :
7589 3986 : resolve_special_varno((Node *) tle->expr, context,
7590 : callback, callback_arg);
7591 3986 : return;
7592 : }
7593 29850 : else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
7594 0 : elog(ERROR, "bogus varno: %d", var->varno);
7595 :
7596 : /* Not special. Just invoke the callback. */
7597 29850 : (*callback) (node, context, callback_arg);
7598 : }
7599 :
7600 : /*
7601 : * Get the name of a field of an expression of composite type. The
7602 : * expression is usually a Var, but we handle other cases too.
7603 : *
7604 : * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
7605 : *
7606 : * This is fairly straightforward when the expression has a named composite
7607 : * type; we need only look up the type in the catalogs. However, the type
7608 : * could also be RECORD. Since no actual table or view column is allowed to
7609 : * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
7610 : * or to a subquery output. We drill down to find the ultimate defining
7611 : * expression and attempt to infer the field name from it. We ereport if we
7612 : * can't determine the name.
7613 : *
7614 : * Similarly, a PARAM of type RECORD has to refer to some expression of
7615 : * a determinable composite type.
7616 : */
7617 : static const char *
7618 934 : get_name_for_var_field(Var *var, int fieldno,
7619 : int levelsup, deparse_context *context)
7620 : {
7621 : RangeTblEntry *rte;
7622 : AttrNumber attnum;
7623 : int netlevelsup;
7624 : deparse_namespace *dpns;
7625 : int varno;
7626 : AttrNumber varattno;
7627 : TupleDesc tupleDesc;
7628 : Node *expr;
7629 :
7630 : /*
7631 : * If it's a RowExpr that was expanded from a whole-row Var, use the
7632 : * column names attached to it. (We could let get_expr_result_tupdesc()
7633 : * handle this, but it's much cheaper to just pull out the name we need.)
7634 : */
7635 934 : if (IsA(var, RowExpr))
7636 : {
7637 36 : RowExpr *r = (RowExpr *) var;
7638 :
7639 36 : if (fieldno > 0 && fieldno <= list_length(r->colnames))
7640 36 : return strVal(list_nth(r->colnames, fieldno - 1));
7641 : }
7642 :
7643 : /*
7644 : * If it's a Param of type RECORD, try to find what the Param refers to.
7645 : */
7646 898 : if (IsA(var, Param))
7647 : {
7648 18 : Param *param = (Param *) var;
7649 : ListCell *ancestor_cell;
7650 :
7651 18 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
7652 18 : if (expr)
7653 : {
7654 : /* Found a match, so recurse to decipher the field name */
7655 : deparse_namespace save_dpns;
7656 : const char *result;
7657 :
7658 18 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
7659 18 : result = get_name_for_var_field((Var *) expr, fieldno,
7660 : 0, context);
7661 18 : pop_ancestor_plan(dpns, &save_dpns);
7662 18 : return result;
7663 : }
7664 : }
7665 :
7666 : /*
7667 : * If it's a Var of type RECORD, we have to find what the Var refers to;
7668 : * if not, we can use get_expr_result_tupdesc().
7669 : */
7670 880 : if (!IsA(var, Var) ||
7671 818 : var->vartype != RECORDOID)
7672 : {
7673 694 : tupleDesc = get_expr_result_tupdesc((Node *) var, false);
7674 : /* Got the tupdesc, so we can extract the field name */
7675 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
7676 694 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
7677 : }
7678 :
7679 : /* Find appropriate nesting depth */
7680 186 : netlevelsup = var->varlevelsup + levelsup;
7681 186 : if (netlevelsup >= list_length(context->namespaces))
7682 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
7683 : var->varlevelsup, levelsup);
7684 186 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7685 : netlevelsup);
7686 :
7687 : /*
7688 : * If we have a syntactic referent for the Var, and we're working from a
7689 : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
7690 : * on the semantic referent. (See comments in get_variable().)
7691 : */
7692 186 : if (var->varnosyn > 0 && dpns->plan == NULL)
7693 : {
7694 72 : varno = var->varnosyn;
7695 72 : varattno = var->varattnosyn;
7696 : }
7697 : else
7698 : {
7699 114 : varno = var->varno;
7700 114 : varattno = var->varattno;
7701 : }
7702 :
7703 : /*
7704 : * Try to find the relevant RTE in this rtable. In a plan tree, it's
7705 : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
7706 : * down into the subplans, or INDEX_VAR, which is resolved similarly.
7707 : *
7708 : * Note: unlike get_variable and resolve_special_varno, we need not worry
7709 : * about inheritance mapping: a child Var should have the same datatype as
7710 : * its parent, and here we're really only interested in the Var's type.
7711 : */
7712 186 : if (varno >= 1 && varno <= list_length(dpns->rtable))
7713 : {
7714 108 : rte = rt_fetch(varno, dpns->rtable);
7715 108 : attnum = varattno;
7716 : }
7717 78 : else if (varno == OUTER_VAR && dpns->outer_tlist)
7718 : {
7719 : TargetEntry *tle;
7720 : deparse_namespace save_dpns;
7721 : const char *result;
7722 :
7723 60 : tle = get_tle_by_resno(dpns->outer_tlist, varattno);
7724 60 : if (!tle)
7725 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
7726 :
7727 : Assert(netlevelsup == 0);
7728 60 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
7729 :
7730 60 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
7731 : levelsup, context);
7732 :
7733 60 : pop_child_plan(dpns, &save_dpns);
7734 60 : return result;
7735 : }
7736 18 : else if (varno == INNER_VAR && dpns->inner_tlist)
7737 : {
7738 : TargetEntry *tle;
7739 : deparse_namespace save_dpns;
7740 : const char *result;
7741 :
7742 18 : tle = get_tle_by_resno(dpns->inner_tlist, varattno);
7743 18 : if (!tle)
7744 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
7745 :
7746 : Assert(netlevelsup == 0);
7747 18 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7748 :
7749 18 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
7750 : levelsup, context);
7751 :
7752 18 : pop_child_plan(dpns, &save_dpns);
7753 18 : return result;
7754 : }
7755 0 : else if (varno == INDEX_VAR && dpns->index_tlist)
7756 : {
7757 : TargetEntry *tle;
7758 : const char *result;
7759 :
7760 0 : tle = get_tle_by_resno(dpns->index_tlist, varattno);
7761 0 : if (!tle)
7762 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
7763 :
7764 : Assert(netlevelsup == 0);
7765 :
7766 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
7767 : levelsup, context);
7768 :
7769 0 : return result;
7770 : }
7771 : else
7772 : {
7773 0 : elog(ERROR, "bogus varno: %d", varno);
7774 : return NULL; /* keep compiler quiet */
7775 : }
7776 :
7777 108 : if (attnum == InvalidAttrNumber)
7778 : {
7779 : /* Var is whole-row reference to RTE, so select the right field */
7780 24 : return get_rte_attribute_name(rte, fieldno);
7781 : }
7782 :
7783 : /*
7784 : * This part has essentially the same logic as the parser's
7785 : * expandRecordVariable() function, but we are dealing with a different
7786 : * representation of the input context, and we only need one field name
7787 : * not a TupleDesc. Also, we need special cases for finding subquery and
7788 : * CTE subplans when deparsing Plan trees.
7789 : */
7790 84 : expr = (Node *) var; /* default if we can't drill down */
7791 :
7792 84 : switch (rte->rtekind)
7793 : {
7794 0 : case RTE_RELATION:
7795 : case RTE_VALUES:
7796 : case RTE_NAMEDTUPLESTORE:
7797 : case RTE_RESULT:
7798 :
7799 : /*
7800 : * This case should not occur: a column of a table, values list,
7801 : * or ENR shouldn't have type RECORD. Fall through and fail (most
7802 : * likely) at the bottom.
7803 : */
7804 0 : break;
7805 24 : case RTE_SUBQUERY:
7806 : /* Subselect-in-FROM: examine sub-select's output expr */
7807 : {
7808 24 : if (rte->subquery)
7809 : {
7810 18 : TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
7811 : attnum);
7812 :
7813 18 : if (ste == NULL || ste->resjunk)
7814 0 : elog(ERROR, "subquery %s does not have attribute %d",
7815 : rte->eref->aliasname, attnum);
7816 18 : expr = (Node *) ste->expr;
7817 18 : if (IsA(expr, Var))
7818 : {
7819 : /*
7820 : * Recurse into the sub-select to see what its Var
7821 : * refers to. We have to build an additional level of
7822 : * namespace to keep in step with varlevelsup in the
7823 : * subselect; furthermore, the subquery RTE might be
7824 : * from an outer query level, in which case the
7825 : * namespace for the subselect must have that outer
7826 : * level as parent namespace.
7827 : */
7828 18 : List *save_nslist = context->namespaces;
7829 : List *parent_namespaces;
7830 : deparse_namespace mydpns;
7831 : const char *result;
7832 :
7833 18 : parent_namespaces = list_copy_tail(context->namespaces,
7834 : netlevelsup);
7835 :
7836 18 : set_deparse_for_query(&mydpns, rte->subquery,
7837 : parent_namespaces);
7838 :
7839 18 : context->namespaces = lcons(&mydpns, parent_namespaces);
7840 :
7841 18 : result = get_name_for_var_field((Var *) expr, fieldno,
7842 : 0, context);
7843 :
7844 18 : context->namespaces = save_nslist;
7845 :
7846 18 : return result;
7847 : }
7848 : /* else fall through to inspect the expression */
7849 : }
7850 : else
7851 : {
7852 : /*
7853 : * We're deparsing a Plan tree so we don't have complete
7854 : * RTE entries (in particular, rte->subquery is NULL). But
7855 : * the only place we'd see a Var directly referencing a
7856 : * SUBQUERY RTE is in a SubqueryScan plan node, and we can
7857 : * look into the child plan's tlist instead.
7858 : */
7859 : TargetEntry *tle;
7860 : deparse_namespace save_dpns;
7861 : const char *result;
7862 :
7863 6 : if (!dpns->inner_plan)
7864 0 : elog(ERROR, "failed to find plan for subquery %s",
7865 : rte->eref->aliasname);
7866 6 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
7867 6 : if (!tle)
7868 0 : elog(ERROR, "bogus varattno for subquery var: %d",
7869 : attnum);
7870 : Assert(netlevelsup == 0);
7871 6 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7872 :
7873 6 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
7874 : levelsup, context);
7875 :
7876 6 : pop_child_plan(dpns, &save_dpns);
7877 6 : return result;
7878 : }
7879 : }
7880 0 : break;
7881 0 : case RTE_JOIN:
7882 : /* Join RTE --- recursively inspect the alias variable */
7883 0 : if (rte->joinaliasvars == NIL)
7884 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
7885 : Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
7886 0 : expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
7887 : Assert(expr != NULL);
7888 : /* we intentionally don't strip implicit coercions here */
7889 0 : if (IsA(expr, Var))
7890 0 : return get_name_for_var_field((Var *) expr, fieldno,
7891 0 : var->varlevelsup + levelsup,
7892 : context);
7893 : /* else fall through to inspect the expression */
7894 0 : break;
7895 0 : case RTE_FUNCTION:
7896 : case RTE_TABLEFUNC:
7897 :
7898 : /*
7899 : * We couldn't get here unless a function is declared with one of
7900 : * its result columns as RECORD, which is not allowed.
7901 : */
7902 0 : break;
7903 60 : case RTE_CTE:
7904 : /* CTE reference: examine subquery's output expr */
7905 : {
7906 60 : CommonTableExpr *cte = NULL;
7907 : Index ctelevelsup;
7908 : ListCell *lc;
7909 :
7910 : /*
7911 : * Try to find the referenced CTE using the namespace stack.
7912 : */
7913 60 : ctelevelsup = rte->ctelevelsup + netlevelsup;
7914 60 : if (ctelevelsup >= list_length(context->namespaces))
7915 12 : lc = NULL;
7916 : else
7917 : {
7918 : deparse_namespace *ctedpns;
7919 :
7920 : ctedpns = (deparse_namespace *)
7921 48 : list_nth(context->namespaces, ctelevelsup);
7922 54 : foreach(lc, ctedpns->ctes)
7923 : {
7924 36 : cte = (CommonTableExpr *) lfirst(lc);
7925 36 : if (strcmp(cte->ctename, rte->ctename) == 0)
7926 30 : break;
7927 : }
7928 : }
7929 60 : if (lc != NULL)
7930 : {
7931 30 : Query *ctequery = (Query *) cte->ctequery;
7932 30 : TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
7933 : attnum);
7934 :
7935 30 : if (ste == NULL || ste->resjunk)
7936 0 : elog(ERROR, "CTE %s does not have attribute %d",
7937 : rte->eref->aliasname, attnum);
7938 30 : expr = (Node *) ste->expr;
7939 30 : if (IsA(expr, Var))
7940 : {
7941 : /*
7942 : * Recurse into the CTE to see what its Var refers to.
7943 : * We have to build an additional level of namespace
7944 : * to keep in step with varlevelsup in the CTE;
7945 : * furthermore it could be an outer CTE (compare
7946 : * SUBQUERY case above).
7947 : */
7948 18 : List *save_nslist = context->namespaces;
7949 : List *parent_namespaces;
7950 : deparse_namespace mydpns;
7951 : const char *result;
7952 :
7953 18 : parent_namespaces = list_copy_tail(context->namespaces,
7954 : ctelevelsup);
7955 :
7956 18 : set_deparse_for_query(&mydpns, ctequery,
7957 : parent_namespaces);
7958 :
7959 18 : context->namespaces = lcons(&mydpns, parent_namespaces);
7960 :
7961 18 : result = get_name_for_var_field((Var *) expr, fieldno,
7962 : 0, context);
7963 :
7964 18 : context->namespaces = save_nslist;
7965 :
7966 18 : return result;
7967 : }
7968 : /* else fall through to inspect the expression */
7969 : }
7970 : else
7971 : {
7972 : /*
7973 : * We're deparsing a Plan tree so we don't have a CTE
7974 : * list. But the only places we'd see a Var directly
7975 : * referencing a CTE RTE are in CteScan or WorkTableScan
7976 : * plan nodes. For those cases, set_deparse_plan arranged
7977 : * for dpns->inner_plan to be the plan node that emits the
7978 : * CTE or RecursiveUnion result, and we can look at its
7979 : * tlist instead.
7980 : */
7981 : TargetEntry *tle;
7982 : deparse_namespace save_dpns;
7983 : const char *result;
7984 :
7985 30 : if (!dpns->inner_plan)
7986 0 : elog(ERROR, "failed to find plan for CTE %s",
7987 : rte->eref->aliasname);
7988 30 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
7989 30 : if (!tle)
7990 0 : elog(ERROR, "bogus varattno for subquery var: %d",
7991 : attnum);
7992 : Assert(netlevelsup == 0);
7993 30 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7994 :
7995 30 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
7996 : levelsup, context);
7997 :
7998 30 : pop_child_plan(dpns, &save_dpns);
7999 30 : return result;
8000 : }
8001 : }
8002 12 : break;
8003 : }
8004 :
8005 : /*
8006 : * We now have an expression we can't expand any more, so see if
8007 : * get_expr_result_tupdesc() can do anything with it.
8008 : */
8009 12 : tupleDesc = get_expr_result_tupdesc(expr, false);
8010 : /* Got the tupdesc, so we can extract the field name */
8011 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
8012 12 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
8013 : }
8014 :
8015 : /*
8016 : * Try to find the referenced expression for a PARAM_EXEC Param that might
8017 : * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
8018 : *
8019 : * If successful, return the expression and set *dpns_p and *ancestor_cell_p
8020 : * appropriately for calling push_ancestor_plan(). If no referent can be
8021 : * found, return NULL.
8022 : */
8023 : static Node *
8024 4992 : find_param_referent(Param *param, deparse_context *context,
8025 : deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
8026 : {
8027 : /* Initialize output parameters to prevent compiler warnings */
8028 4992 : *dpns_p = NULL;
8029 4992 : *ancestor_cell_p = NULL;
8030 :
8031 : /*
8032 : * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
8033 : * SubPlan argument. This will necessarily be in some ancestor of the
8034 : * current expression's Plan node.
8035 : */
8036 4992 : if (param->paramkind == PARAM_EXEC)
8037 : {
8038 : deparse_namespace *dpns;
8039 : Plan *child_plan;
8040 : ListCell *lc;
8041 :
8042 4158 : dpns = (deparse_namespace *) linitial(context->namespaces);
8043 4158 : child_plan = dpns->plan;
8044 :
8045 7824 : foreach(lc, dpns->ancestors)
8046 : {
8047 6580 : Node *ancestor = (Node *) lfirst(lc);
8048 : ListCell *lc2;
8049 :
8050 : /*
8051 : * NestLoops transmit params to their inner child only.
8052 : */
8053 6580 : if (IsA(ancestor, NestLoop) &&
8054 2666 : child_plan == innerPlan(ancestor))
8055 : {
8056 2582 : NestLoop *nl = (NestLoop *) ancestor;
8057 :
8058 3142 : foreach(lc2, nl->nestParams)
8059 : {
8060 3052 : NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
8061 :
8062 3052 : if (nlp->paramno == param->paramid)
8063 : {
8064 : /* Found a match, so return it */
8065 2492 : *dpns_p = dpns;
8066 2492 : *ancestor_cell_p = lc;
8067 2492 : return (Node *) nlp->paramval;
8068 : }
8069 : }
8070 : }
8071 :
8072 : /*
8073 : * If ancestor is a SubPlan, check the arguments it provides.
8074 : */
8075 4088 : if (IsA(ancestor, SubPlan))
8076 : {
8077 506 : SubPlan *subplan = (SubPlan *) ancestor;
8078 : ListCell *lc3;
8079 : ListCell *lc4;
8080 :
8081 580 : forboth(lc3, subplan->parParam, lc4, subplan->args)
8082 : {
8083 496 : int paramid = lfirst_int(lc3);
8084 496 : Node *arg = (Node *) lfirst(lc4);
8085 :
8086 496 : if (paramid == param->paramid)
8087 : {
8088 : /*
8089 : * Found a match, so return it. But, since Vars in
8090 : * the arg are to be evaluated in the surrounding
8091 : * context, we have to point to the next ancestor item
8092 : * that is *not* a SubPlan.
8093 : */
8094 : ListCell *rest;
8095 :
8096 422 : for_each_cell(rest, dpns->ancestors,
8097 : lnext(dpns->ancestors, lc))
8098 : {
8099 422 : Node *ancestor2 = (Node *) lfirst(rest);
8100 :
8101 422 : if (!IsA(ancestor2, SubPlan))
8102 : {
8103 422 : *dpns_p = dpns;
8104 422 : *ancestor_cell_p = rest;
8105 422 : return arg;
8106 : }
8107 : }
8108 0 : elog(ERROR, "SubPlan cannot be outermost ancestor");
8109 : }
8110 : }
8111 :
8112 : /* SubPlan isn't a kind of Plan, so skip the rest */
8113 84 : continue;
8114 : }
8115 :
8116 : /*
8117 : * We need not consider the ancestor's initPlan list, since
8118 : * initplans never have any parParams.
8119 : */
8120 :
8121 : /* No luck, crawl up to next ancestor */
8122 3582 : child_plan = (Plan *) ancestor;
8123 : }
8124 : }
8125 :
8126 : /* No referent found */
8127 2078 : return NULL;
8128 : }
8129 :
8130 : /*
8131 : * Display a Param appropriately.
8132 : */
8133 : static void
8134 4974 : get_parameter(Param *param, deparse_context *context)
8135 : {
8136 : Node *expr;
8137 : deparse_namespace *dpns;
8138 : ListCell *ancestor_cell;
8139 :
8140 : /*
8141 : * If it's a PARAM_EXEC parameter, try to locate the expression from which
8142 : * the parameter was computed. Note that failing to find a referent isn't
8143 : * an error, since the Param might well be a subplan output rather than an
8144 : * input.
8145 : */
8146 4974 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8147 4974 : if (expr)
8148 : {
8149 : /* Found a match, so print it */
8150 : deparse_namespace save_dpns;
8151 : bool save_varprefix;
8152 : bool need_paren;
8153 :
8154 : /* Switch attention to the ancestor plan node */
8155 2896 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
8156 :
8157 : /*
8158 : * Force prefixing of Vars, since they won't belong to the relation
8159 : * being scanned in the original plan node.
8160 : */
8161 2896 : save_varprefix = context->varprefix;
8162 2896 : context->varprefix = true;
8163 :
8164 : /*
8165 : * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
8166 : * upper-level Param, which wouldn't need extra parentheses.
8167 : * Otherwise, insert parens to ensure the expression looks atomic.
8168 : */
8169 2902 : need_paren = !(IsA(expr, Var) ||
8170 6 : IsA(expr, Aggref) ||
8171 6 : IsA(expr, GroupingFunc) ||
8172 0 : IsA(expr, Param));
8173 2896 : if (need_paren)
8174 0 : appendStringInfoChar(context->buf, '(');
8175 :
8176 2896 : get_rule_expr(expr, context, false);
8177 :
8178 2896 : if (need_paren)
8179 0 : appendStringInfoChar(context->buf, ')');
8180 :
8181 2896 : context->varprefix = save_varprefix;
8182 :
8183 2896 : pop_ancestor_plan(dpns, &save_dpns);
8184 :
8185 2896 : return;
8186 : }
8187 :
8188 : /*
8189 : * If it's an external parameter, see if the outermost namespace provides
8190 : * function argument names.
8191 : */
8192 2078 : if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
8193 : {
8194 834 : dpns = llast(context->namespaces);
8195 834 : if (dpns->argnames &&
8196 66 : param->paramid > 0 &&
8197 66 : param->paramid <= dpns->numargs)
8198 : {
8199 66 : char *argname = dpns->argnames[param->paramid - 1];
8200 :
8201 66 : if (argname)
8202 : {
8203 66 : bool should_qualify = false;
8204 : ListCell *lc;
8205 :
8206 : /*
8207 : * Qualify the parameter name if there are any other deparse
8208 : * namespaces with range tables. This avoids qualifying in
8209 : * trivial cases like "RETURN a + b", but makes it safe in all
8210 : * other cases.
8211 : */
8212 150 : foreach(lc, context->namespaces)
8213 : {
8214 114 : deparse_namespace *depns = lfirst(lc);
8215 :
8216 114 : if (depns->rtable_names != NIL)
8217 : {
8218 30 : should_qualify = true;
8219 30 : break;
8220 : }
8221 : }
8222 66 : if (should_qualify)
8223 : {
8224 30 : appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
8225 30 : appendStringInfoChar(context->buf, '.');
8226 : }
8227 :
8228 66 : appendStringInfoString(context->buf, quote_identifier(argname));
8229 66 : return;
8230 : }
8231 : }
8232 : }
8233 :
8234 : /*
8235 : * Not PARAM_EXEC, or couldn't find referent: just print $N.
8236 : */
8237 2012 : appendStringInfo(context->buf, "$%d", param->paramid);
8238 : }
8239 :
8240 : /*
8241 : * get_simple_binary_op_name
8242 : *
8243 : * helper function for isSimpleNode
8244 : * will return single char binary operator name, or NULL if it's not
8245 : */
8246 : static const char *
8247 114 : get_simple_binary_op_name(OpExpr *expr)
8248 : {
8249 114 : List *args = expr->args;
8250 :
8251 114 : if (list_length(args) == 2)
8252 : {
8253 : /* binary operator */
8254 114 : Node *arg1 = (Node *) linitial(args);
8255 114 : Node *arg2 = (Node *) lsecond(args);
8256 : const char *op;
8257 :
8258 114 : op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
8259 114 : if (strlen(op) == 1)
8260 114 : return op;
8261 : }
8262 0 : return NULL;
8263 : }
8264 :
8265 :
8266 : /*
8267 : * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
8268 : *
8269 : * true : simple in the context of parent node's type
8270 : * false : not simple
8271 : */
8272 : static bool
8273 4508 : isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
8274 : {
8275 4508 : if (!node)
8276 0 : return false;
8277 :
8278 4508 : switch (nodeTag(node))
8279 : {
8280 3770 : case T_Var:
8281 : case T_Const:
8282 : case T_Param:
8283 : case T_CoerceToDomainValue:
8284 : case T_SetToDefault:
8285 : case T_CurrentOfExpr:
8286 : /* single words: always simple */
8287 3770 : return true;
8288 :
8289 378 : case T_SubscriptingRef:
8290 : case T_ArrayExpr:
8291 : case T_RowExpr:
8292 : case T_CoalesceExpr:
8293 : case T_MinMaxExpr:
8294 : case T_SQLValueFunction:
8295 : case T_XmlExpr:
8296 : case T_NextValueExpr:
8297 : case T_NullIfExpr:
8298 : case T_Aggref:
8299 : case T_GroupingFunc:
8300 : case T_WindowFunc:
8301 : case T_FuncExpr:
8302 : case T_JsonConstructorExpr:
8303 : /* function-like: name(..) or name[..] */
8304 378 : return true;
8305 :
8306 : /* CASE keywords act as parentheses */
8307 0 : case T_CaseExpr:
8308 0 : return true;
8309 :
8310 54 : case T_FieldSelect:
8311 :
8312 : /*
8313 : * appears simple since . has top precedence, unless parent is
8314 : * T_FieldSelect itself!
8315 : */
8316 54 : return !IsA(parentNode, FieldSelect);
8317 :
8318 0 : case T_FieldStore:
8319 :
8320 : /*
8321 : * treat like FieldSelect (probably doesn't matter)
8322 : */
8323 0 : return !IsA(parentNode, FieldStore);
8324 :
8325 0 : case T_CoerceToDomain:
8326 : /* maybe simple, check args */
8327 0 : return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
8328 : node, prettyFlags);
8329 6 : case T_RelabelType:
8330 6 : return isSimpleNode((Node *) ((RelabelType *) node)->arg,
8331 : node, prettyFlags);
8332 0 : case T_CoerceViaIO:
8333 0 : return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
8334 : node, prettyFlags);
8335 0 : case T_ArrayCoerceExpr:
8336 0 : return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
8337 : node, prettyFlags);
8338 0 : case T_ConvertRowtypeExpr:
8339 0 : return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
8340 : node, prettyFlags);
8341 :
8342 252 : case T_OpExpr:
8343 : {
8344 : /* depends on parent node type; needs further checking */
8345 252 : if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
8346 : {
8347 : const char *op;
8348 : const char *parentOp;
8349 : bool is_lopriop;
8350 : bool is_hipriop;
8351 : bool is_lopriparent;
8352 : bool is_hipriparent;
8353 :
8354 60 : op = get_simple_binary_op_name((OpExpr *) node);
8355 60 : if (!op)
8356 0 : return false;
8357 :
8358 : /* We know only the basic operators + - and * / % */
8359 60 : is_lopriop = (strchr("+-", *op) != NULL);
8360 60 : is_hipriop = (strchr("*/%", *op) != NULL);
8361 60 : if (!(is_lopriop || is_hipriop))
8362 6 : return false;
8363 :
8364 54 : parentOp = get_simple_binary_op_name((OpExpr *) parentNode);
8365 54 : if (!parentOp)
8366 0 : return false;
8367 :
8368 54 : is_lopriparent = (strchr("+-", *parentOp) != NULL);
8369 54 : is_hipriparent = (strchr("*/%", *parentOp) != NULL);
8370 54 : if (!(is_lopriparent || is_hipriparent))
8371 0 : return false;
8372 :
8373 54 : if (is_hipriop && is_lopriparent)
8374 12 : return true; /* op binds tighter than parent */
8375 :
8376 42 : if (is_lopriop && is_hipriparent)
8377 30 : return false;
8378 :
8379 : /*
8380 : * Operators are same priority --- can skip parens only if
8381 : * we have (a - b) - c, not a - (b - c).
8382 : */
8383 12 : if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
8384 6 : return true;
8385 :
8386 6 : return false;
8387 : }
8388 : /* else do the same stuff as for T_SubLink et al. */
8389 : }
8390 : /* FALLTHROUGH */
8391 :
8392 : case T_SubLink:
8393 : case T_NullTest:
8394 : case T_BooleanTest:
8395 : case T_DistinctExpr:
8396 : case T_JsonIsPredicate:
8397 222 : switch (nodeTag(parentNode))
8398 : {
8399 30 : case T_FuncExpr:
8400 : {
8401 : /* special handling for casts and COERCE_SQL_SYNTAX */
8402 30 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
8403 :
8404 30 : if (type == COERCE_EXPLICIT_CAST ||
8405 6 : type == COERCE_IMPLICIT_CAST ||
8406 : type == COERCE_SQL_SYNTAX)
8407 30 : return false;
8408 0 : return true; /* own parentheses */
8409 : }
8410 162 : case T_BoolExpr: /* lower precedence */
8411 : case T_SubscriptingRef: /* other separators */
8412 : case T_ArrayExpr: /* other separators */
8413 : case T_RowExpr: /* other separators */
8414 : case T_CoalesceExpr: /* own parentheses */
8415 : case T_MinMaxExpr: /* own parentheses */
8416 : case T_XmlExpr: /* own parentheses */
8417 : case T_NullIfExpr: /* other separators */
8418 : case T_Aggref: /* own parentheses */
8419 : case T_GroupingFunc: /* own parentheses */
8420 : case T_WindowFunc: /* own parentheses */
8421 : case T_CaseExpr: /* other separators */
8422 162 : return true;
8423 30 : default:
8424 30 : return false;
8425 : }
8426 :
8427 18 : case T_BoolExpr:
8428 18 : switch (nodeTag(parentNode))
8429 : {
8430 18 : case T_BoolExpr:
8431 18 : if (prettyFlags & PRETTYFLAG_PAREN)
8432 : {
8433 : BoolExprType type;
8434 : BoolExprType parentType;
8435 :
8436 18 : type = ((BoolExpr *) node)->boolop;
8437 18 : parentType = ((BoolExpr *) parentNode)->boolop;
8438 : switch (type)
8439 : {
8440 12 : case NOT_EXPR:
8441 : case AND_EXPR:
8442 12 : if (parentType == AND_EXPR || parentType == OR_EXPR)
8443 12 : return true;
8444 0 : break;
8445 6 : case OR_EXPR:
8446 6 : if (parentType == OR_EXPR)
8447 0 : return true;
8448 6 : break;
8449 : }
8450 0 : }
8451 6 : return false;
8452 0 : case T_FuncExpr:
8453 : {
8454 : /* special handling for casts and COERCE_SQL_SYNTAX */
8455 0 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
8456 :
8457 0 : if (type == COERCE_EXPLICIT_CAST ||
8458 0 : type == COERCE_IMPLICIT_CAST ||
8459 : type == COERCE_SQL_SYNTAX)
8460 0 : return false;
8461 0 : return true; /* own parentheses */
8462 : }
8463 0 : case T_SubscriptingRef: /* other separators */
8464 : case T_ArrayExpr: /* other separators */
8465 : case T_RowExpr: /* other separators */
8466 : case T_CoalesceExpr: /* own parentheses */
8467 : case T_MinMaxExpr: /* own parentheses */
8468 : case T_XmlExpr: /* own parentheses */
8469 : case T_NullIfExpr: /* other separators */
8470 : case T_Aggref: /* own parentheses */
8471 : case T_GroupingFunc: /* own parentheses */
8472 : case T_WindowFunc: /* own parentheses */
8473 : case T_CaseExpr: /* other separators */
8474 0 : return true;
8475 0 : default:
8476 0 : return false;
8477 : }
8478 :
8479 0 : case T_JsonValueExpr:
8480 : /* maybe simple, check args */
8481 0 : return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
8482 : node, prettyFlags);
8483 :
8484 0 : default:
8485 0 : break;
8486 : }
8487 : /* those we don't know: in dubio complexo */
8488 0 : return false;
8489 : }
8490 :
8491 :
8492 : /*
8493 : * appendContextKeyword - append a keyword to buffer
8494 : *
8495 : * If prettyPrint is enabled, perform a line break, and adjust indentation.
8496 : * Otherwise, just append the keyword.
8497 : */
8498 : static void
8499 20998 : appendContextKeyword(deparse_context *context, const char *str,
8500 : int indentBefore, int indentAfter, int indentPlus)
8501 : {
8502 20998 : StringInfo buf = context->buf;
8503 :
8504 20998 : if (PRETTY_INDENT(context))
8505 : {
8506 : int indentAmount;
8507 :
8508 20700 : context->indentLevel += indentBefore;
8509 :
8510 : /* remove any trailing spaces currently in the buffer ... */
8511 20700 : removeStringInfoSpaces(buf);
8512 : /* ... then add a newline and some spaces */
8513 20700 : appendStringInfoChar(buf, '\n');
8514 :
8515 20700 : if (context->indentLevel < PRETTYINDENT_LIMIT)
8516 20700 : indentAmount = Max(context->indentLevel, 0) + indentPlus;
8517 : else
8518 : {
8519 : /*
8520 : * If we're indented more than PRETTYINDENT_LIMIT characters, try
8521 : * to conserve horizontal space by reducing the per-level
8522 : * indentation. For best results the scale factor here should
8523 : * divide all the indent amounts that get added to indentLevel
8524 : * (PRETTYINDENT_STD, etc). It's important that the indentation
8525 : * not grow unboundedly, else deeply-nested trees use O(N^2)
8526 : * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
8527 : */
8528 0 : indentAmount = PRETTYINDENT_LIMIT +
8529 0 : (context->indentLevel - PRETTYINDENT_LIMIT) /
8530 : (PRETTYINDENT_STD / 2);
8531 0 : indentAmount %= PRETTYINDENT_LIMIT;
8532 : /* scale/wrap logic affects indentLevel, but not indentPlus */
8533 0 : indentAmount += indentPlus;
8534 : }
8535 20700 : appendStringInfoSpaces(buf, indentAmount);
8536 :
8537 20700 : appendStringInfoString(buf, str);
8538 :
8539 20700 : context->indentLevel += indentAfter;
8540 20700 : if (context->indentLevel < 0)
8541 0 : context->indentLevel = 0;
8542 : }
8543 : else
8544 298 : appendStringInfoString(buf, str);
8545 20998 : }
8546 :
8547 : /*
8548 : * removeStringInfoSpaces - delete trailing spaces from a buffer.
8549 : *
8550 : * Possibly this should move to stringinfo.c at some point.
8551 : */
8552 : static void
8553 21018 : removeStringInfoSpaces(StringInfo str)
8554 : {
8555 32372 : while (str->len > 0 && str->data[str->len - 1] == ' ')
8556 11354 : str->data[--(str->len)] = '\0';
8557 21018 : }
8558 :
8559 :
8560 : /*
8561 : * get_rule_expr_paren - deparse expr using get_rule_expr,
8562 : * embracing the string with parentheses if necessary for prettyPrint.
8563 : *
8564 : * Never embrace if prettyFlags=0, because it's done in the calling node.
8565 : *
8566 : * Any node that does *not* embrace its argument node by sql syntax (with
8567 : * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
8568 : * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
8569 : * added.
8570 : */
8571 : static void
8572 137214 : get_rule_expr_paren(Node *node, deparse_context *context,
8573 : bool showimplicit, Node *parentNode)
8574 : {
8575 : bool need_paren;
8576 :
8577 141716 : need_paren = PRETTY_PAREN(context) &&
8578 4502 : !isSimpleNode(node, parentNode, context->prettyFlags);
8579 :
8580 137214 : if (need_paren)
8581 108 : appendStringInfoChar(context->buf, '(');
8582 :
8583 137214 : get_rule_expr(node, context, showimplicit);
8584 :
8585 137214 : if (need_paren)
8586 108 : appendStringInfoChar(context->buf, ')');
8587 137214 : }
8588 :
8589 :
8590 : /* ----------
8591 : * get_rule_expr - Parse back an expression
8592 : *
8593 : * Note: showimplicit determines whether we display any implicit cast that
8594 : * is present at the top of the expression tree. It is a passed argument,
8595 : * not a field of the context struct, because we change the value as we
8596 : * recurse down into the expression. In general we suppress implicit casts
8597 : * when the result type is known with certainty (eg, the arguments of an
8598 : * OR must be boolean). We display implicit casts for arguments of functions
8599 : * and operators, since this is needed to be certain that the same function
8600 : * or operator will be chosen when the expression is re-parsed.
8601 : * ----------
8602 : */
8603 : static void
8604 272490 : get_rule_expr(Node *node, deparse_context *context,
8605 : bool showimplicit)
8606 : {
8607 272490 : StringInfo buf = context->buf;
8608 :
8609 272490 : if (node == NULL)
8610 96 : return;
8611 :
8612 : /* Guard against excessively long or deeply-nested queries */
8613 272394 : CHECK_FOR_INTERRUPTS();
8614 272394 : check_stack_depth();
8615 :
8616 : /*
8617 : * Each level of get_rule_expr must emit an indivisible term
8618 : * (parenthesized if necessary) to ensure result is reparsed into the same
8619 : * expression tree. The only exception is that when the input is a List,
8620 : * we emit the component items comma-separated with no surrounding
8621 : * decoration; this is convenient for most callers.
8622 : */
8623 272394 : switch (nodeTag(node))
8624 : {
8625 123222 : case T_Var:
8626 123222 : (void) get_variable((Var *) node, 0, false, context);
8627 123222 : break;
8628 :
8629 52458 : case T_Const:
8630 52458 : get_const_expr((Const *) node, context, 0);
8631 52458 : break;
8632 :
8633 4974 : case T_Param:
8634 4974 : get_parameter((Param *) node, context);
8635 4974 : break;
8636 :
8637 1552 : case T_Aggref:
8638 1552 : get_agg_expr((Aggref *) node, context, (Aggref *) node);
8639 1552 : break;
8640 :
8641 52 : case T_GroupingFunc:
8642 : {
8643 52 : GroupingFunc *gexpr = (GroupingFunc *) node;
8644 :
8645 52 : appendStringInfoString(buf, "GROUPING(");
8646 52 : get_rule_expr((Node *) gexpr->args, context, true);
8647 52 : appendStringInfoChar(buf, ')');
8648 : }
8649 52 : break;
8650 :
8651 234 : case T_WindowFunc:
8652 234 : get_windowfunc_expr((WindowFunc *) node, context);
8653 234 : break;
8654 :
8655 256 : case T_SubscriptingRef:
8656 : {
8657 256 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
8658 : bool need_parens;
8659 :
8660 : /*
8661 : * If the argument is a CaseTestExpr, we must be inside a
8662 : * FieldStore, ie, we are assigning to an element of an array
8663 : * within a composite column. Since we already punted on
8664 : * displaying the FieldStore's target information, just punt
8665 : * here too, and display only the assignment source
8666 : * expression.
8667 : */
8668 256 : if (IsA(sbsref->refexpr, CaseTestExpr))
8669 : {
8670 : Assert(sbsref->refassgnexpr);
8671 0 : get_rule_expr((Node *) sbsref->refassgnexpr,
8672 : context, showimplicit);
8673 0 : break;
8674 : }
8675 :
8676 : /*
8677 : * Parenthesize the argument unless it's a simple Var or a
8678 : * FieldSelect. (In particular, if it's another
8679 : * SubscriptingRef, we *must* parenthesize to avoid
8680 : * confusion.)
8681 : */
8682 378 : need_parens = !IsA(sbsref->refexpr, Var) &&
8683 122 : !IsA(sbsref->refexpr, FieldSelect);
8684 256 : if (need_parens)
8685 82 : appendStringInfoChar(buf, '(');
8686 256 : get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
8687 256 : if (need_parens)
8688 82 : appendStringInfoChar(buf, ')');
8689 :
8690 : /*
8691 : * If there's a refassgnexpr, we want to print the node in the
8692 : * format "container[subscripts] := refassgnexpr". This is
8693 : * not legal SQL, so decompilation of INSERT or UPDATE
8694 : * statements should always use processIndirection as part of
8695 : * the statement-level syntax. We should only see this when
8696 : * EXPLAIN tries to print the targetlist of a plan resulting
8697 : * from such a statement.
8698 : */
8699 256 : if (sbsref->refassgnexpr)
8700 : {
8701 : Node *refassgnexpr;
8702 :
8703 : /*
8704 : * Use processIndirection to print this node's subscripts
8705 : * as well as any additional field selections or
8706 : * subscripting in immediate descendants. It returns the
8707 : * RHS expr that is actually being "assigned".
8708 : */
8709 12 : refassgnexpr = processIndirection(node, context);
8710 12 : appendStringInfoString(buf, " := ");
8711 12 : get_rule_expr(refassgnexpr, context, showimplicit);
8712 : }
8713 : else
8714 : {
8715 : /* Just an ordinary container fetch, so print subscripts */
8716 244 : printSubscripts(sbsref, context);
8717 : }
8718 : }
8719 256 : break;
8720 :
8721 10240 : case T_FuncExpr:
8722 10240 : get_func_expr((FuncExpr *) node, context, showimplicit);
8723 10240 : break;
8724 :
8725 18 : case T_NamedArgExpr:
8726 : {
8727 18 : NamedArgExpr *na = (NamedArgExpr *) node;
8728 :
8729 18 : appendStringInfo(buf, "%s => ", quote_identifier(na->name));
8730 18 : get_rule_expr((Node *) na->arg, context, showimplicit);
8731 : }
8732 18 : break;
8733 :
8734 50986 : case T_OpExpr:
8735 50986 : get_oper_expr((OpExpr *) node, context);
8736 50986 : break;
8737 :
8738 18 : case T_DistinctExpr:
8739 : {
8740 18 : DistinctExpr *expr = (DistinctExpr *) node;
8741 18 : List *args = expr->args;
8742 18 : Node *arg1 = (Node *) linitial(args);
8743 18 : Node *arg2 = (Node *) lsecond(args);
8744 :
8745 18 : if (!PRETTY_PAREN(context))
8746 12 : appendStringInfoChar(buf, '(');
8747 18 : get_rule_expr_paren(arg1, context, true, node);
8748 18 : appendStringInfoString(buf, " IS DISTINCT FROM ");
8749 18 : get_rule_expr_paren(arg2, context, true, node);
8750 18 : if (!PRETTY_PAREN(context))
8751 12 : appendStringInfoChar(buf, ')');
8752 : }
8753 18 : break;
8754 :
8755 20 : case T_NullIfExpr:
8756 : {
8757 20 : NullIfExpr *nullifexpr = (NullIfExpr *) node;
8758 :
8759 20 : appendStringInfoString(buf, "NULLIF(");
8760 20 : get_rule_expr((Node *) nullifexpr->args, context, true);
8761 20 : appendStringInfoChar(buf, ')');
8762 : }
8763 20 : break;
8764 :
8765 2448 : case T_ScalarArrayOpExpr:
8766 : {
8767 2448 : ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
8768 2448 : List *args = expr->args;
8769 2448 : Node *arg1 = (Node *) linitial(args);
8770 2448 : Node *arg2 = (Node *) lsecond(args);
8771 :
8772 2448 : if (!PRETTY_PAREN(context))
8773 2436 : appendStringInfoChar(buf, '(');
8774 2448 : get_rule_expr_paren(arg1, context, true, node);
8775 2448 : appendStringInfo(buf, " %s %s (",
8776 : generate_operator_name(expr->opno,
8777 : exprType(arg1),
8778 : get_base_element_type(exprType(arg2))),
8779 2448 : expr->useOr ? "ANY" : "ALL");
8780 2448 : get_rule_expr_paren(arg2, context, true, node);
8781 :
8782 : /*
8783 : * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
8784 : * a bare sub-SELECT. Since we're here, the sub-SELECT must
8785 : * be meant as a scalar sub-SELECT yielding an array value to
8786 : * be used in ScalarArrayOpExpr; but the grammar will
8787 : * preferentially interpret such a construct as an ANY/ALL
8788 : * SubLink. To prevent misparsing the output that way, insert
8789 : * a dummy coercion (which will be stripped by parse analysis,
8790 : * so no inefficiency is added in dump and reload). This is
8791 : * indeed most likely what the user wrote to get the construct
8792 : * accepted in the first place.
8793 : */
8794 2448 : if (IsA(arg2, SubLink) &&
8795 6 : ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
8796 6 : appendStringInfo(buf, "::%s",
8797 : format_type_with_typemod(exprType(arg2),
8798 : exprTypmod(arg2)));
8799 2448 : appendStringInfoChar(buf, ')');
8800 2448 : if (!PRETTY_PAREN(context))
8801 2436 : appendStringInfoChar(buf, ')');
8802 : }
8803 2448 : break;
8804 :
8805 9762 : case T_BoolExpr:
8806 : {
8807 9762 : BoolExpr *expr = (BoolExpr *) node;
8808 9762 : Node *first_arg = linitial(expr->args);
8809 : ListCell *arg;
8810 :
8811 9762 : switch (expr->boolop)
8812 : {
8813 7960 : case AND_EXPR:
8814 7960 : if (!PRETTY_PAREN(context))
8815 7894 : appendStringInfoChar(buf, '(');
8816 7960 : get_rule_expr_paren(first_arg, context,
8817 : false, node);
8818 18182 : for_each_from(arg, expr->args, 1)
8819 : {
8820 10222 : appendStringInfoString(buf, " AND ");
8821 10222 : get_rule_expr_paren((Node *) lfirst(arg), context,
8822 : false, node);
8823 : }
8824 7960 : if (!PRETTY_PAREN(context))
8825 7894 : appendStringInfoChar(buf, ')');
8826 7960 : break;
8827 :
8828 1524 : case OR_EXPR:
8829 1524 : if (!PRETTY_PAREN(context))
8830 1512 : appendStringInfoChar(buf, '(');
8831 1524 : get_rule_expr_paren(first_arg, context,
8832 : false, node);
8833 3620 : for_each_from(arg, expr->args, 1)
8834 : {
8835 2096 : appendStringInfoString(buf, " OR ");
8836 2096 : get_rule_expr_paren((Node *) lfirst(arg), context,
8837 : false, node);
8838 : }
8839 1524 : if (!PRETTY_PAREN(context))
8840 1512 : appendStringInfoChar(buf, ')');
8841 1524 : break;
8842 :
8843 278 : case NOT_EXPR:
8844 278 : if (!PRETTY_PAREN(context))
8845 266 : appendStringInfoChar(buf, '(');
8846 278 : appendStringInfoString(buf, "NOT ");
8847 278 : get_rule_expr_paren(first_arg, context,
8848 : false, node);
8849 278 : if (!PRETTY_PAREN(context))
8850 266 : appendStringInfoChar(buf, ')');
8851 278 : break;
8852 :
8853 0 : default:
8854 0 : elog(ERROR, "unrecognized boolop: %d",
8855 : (int) expr->boolop);
8856 : }
8857 : }
8858 9762 : break;
8859 :
8860 406 : case T_SubLink:
8861 406 : get_sublink_expr((SubLink *) node, context);
8862 406 : break;
8863 :
8864 466 : case T_SubPlan:
8865 : {
8866 466 : SubPlan *subplan = (SubPlan *) node;
8867 :
8868 : /*
8869 : * We cannot see an already-planned subplan in rule deparsing,
8870 : * only while EXPLAINing a query plan. We don't try to
8871 : * reconstruct the original SQL, just reference the subplan
8872 : * that appears elsewhere in EXPLAIN's result.
8873 : */
8874 466 : if (subplan->useHashTable)
8875 116 : appendStringInfo(buf, "(hashed %s)", subplan->plan_name);
8876 : else
8877 350 : appendStringInfo(buf, "(%s)", subplan->plan_name);
8878 : }
8879 466 : break;
8880 :
8881 0 : case T_AlternativeSubPlan:
8882 : {
8883 0 : AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
8884 : ListCell *lc;
8885 :
8886 : /*
8887 : * This case cannot be reached in normal usage, since no
8888 : * AlternativeSubPlan can appear either in parsetrees or
8889 : * finished plan trees. We keep it just in case somebody
8890 : * wants to use this code to print planner data structures.
8891 : */
8892 0 : appendStringInfoString(buf, "(alternatives: ");
8893 0 : foreach(lc, asplan->subplans)
8894 |