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