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