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