Branch data 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-2026, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : *
11 : : * IDENTIFICATION
12 : : * src/backend/utils/adt/ruleutils.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : : #include "postgres.h"
17 : :
18 : : #include <ctype.h>
19 : : #include <unistd.h>
20 : : #include <fcntl.h>
21 : :
22 : : #include "access/amapi.h"
23 : : #include "access/htup_details.h"
24 : : #include "access/relation.h"
25 : : #include "access/table.h"
26 : : #include "catalog/pg_aggregate.h"
27 : : #include "catalog/pg_am.h"
28 : : #include "catalog/pg_authid.h"
29 : : #include "catalog/pg_collation.h"
30 : : #include "catalog/pg_constraint.h"
31 : : #include "catalog/pg_depend.h"
32 : : #include "catalog/pg_language.h"
33 : : #include "catalog/pg_opclass.h"
34 : : #include "catalog/pg_operator.h"
35 : : #include "catalog/pg_partitioned_table.h"
36 : : #include "catalog/pg_proc.h"
37 : : #include "catalog/pg_propgraph_element.h"
38 : : #include "catalog/pg_propgraph_element_label.h"
39 : : #include "catalog/pg_propgraph_label.h"
40 : : #include "catalog/pg_propgraph_label_property.h"
41 : : #include "catalog/pg_propgraph_property.h"
42 : : #include "catalog/pg_statistic_ext.h"
43 : : #include "catalog/pg_trigger.h"
44 : : #include "catalog/pg_type.h"
45 : : #include "commands/defrem.h"
46 : : #include "commands/tablespace.h"
47 : : #include "common/keywords.h"
48 : : #include "executor/spi.h"
49 : : #include "funcapi.h"
50 : : #include "mb/pg_wchar.h"
51 : : #include "miscadmin.h"
52 : : #include "nodes/makefuncs.h"
53 : : #include "nodes/nodeFuncs.h"
54 : : #include "nodes/pathnodes.h"
55 : : #include "optimizer/optimizer.h"
56 : : #include "parser/parse_agg.h"
57 : : #include "parser/parse_func.h"
58 : : #include "parser/parse_oper.h"
59 : : #include "parser/parse_relation.h"
60 : : #include "parser/parser.h"
61 : : #include "parser/parsetree.h"
62 : : #include "rewrite/rewriteHandler.h"
63 : : #include "rewrite/rewriteManip.h"
64 : : #include "rewrite/rewriteSupport.h"
65 : : #include "utils/array.h"
66 : : #include "utils/builtins.h"
67 : : #include "utils/fmgroids.h"
68 : : #include "utils/guc.h"
69 : : #include "utils/hsearch.h"
70 : : #include "utils/lsyscache.h"
71 : : #include "utils/partcache.h"
72 : : #include "utils/rel.h"
73 : : #include "utils/ruleutils.h"
74 : : #include "utils/snapmgr.h"
75 : : #include "utils/syscache.h"
76 : : #include "utils/typcache.h"
77 : : #include "utils/varlena.h"
78 : : #include "utils/xml.h"
79 : :
80 : : /* ----------
81 : : * Pretty formatting constants
82 : : * ----------
83 : : */
84 : :
85 : : /* Indent counts */
86 : : #define PRETTYINDENT_STD 8
87 : : #define PRETTYINDENT_JOIN 4
88 : : #define PRETTYINDENT_VAR 4
89 : :
90 : : #define PRETTYINDENT_LIMIT 40 /* wrap limit */
91 : :
92 : : /* Pretty flags */
93 : : #define PRETTYFLAG_PAREN 0x0001
94 : : #define PRETTYFLAG_INDENT 0x0002
95 : : #define PRETTYFLAG_SCHEMA 0x0004
96 : :
97 : : /* Standard conversion of a "bool pretty" option to detailed flags */
98 : : #define GET_PRETTY_FLAGS(pretty) \
99 : : ((pretty) ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) \
100 : : : PRETTYFLAG_INDENT)
101 : :
102 : : /* Default line length for pretty-print wrapping: 0 means wrap always */
103 : : #define WRAP_COLUMN_DEFAULT 0
104 : :
105 : : /* macros to test if pretty action needed */
106 : : #define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
107 : : #define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
108 : : #define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
109 : :
110 : :
111 : : /* ----------
112 : : * Local data types
113 : : * ----------
114 : : */
115 : :
116 : : /* Context info needed for invoking a recursive querytree display routine */
117 : : typedef struct
118 : : {
119 : : StringInfo buf; /* output buffer to append to */
120 : : List *namespaces; /* List of deparse_namespace nodes */
121 : : TupleDesc resultDesc; /* if top level of a view, the view's tupdesc */
122 : : List *targetList; /* Current query level's SELECT targetlist */
123 : : List *windowClause; /* Current query level's WINDOW clause */
124 : : int prettyFlags; /* enabling of pretty-print functions */
125 : : int wrapColumn; /* max line length, or -1 for no limit */
126 : : int indentLevel; /* current indent level for pretty-print */
127 : : bool varprefix; /* true to print prefixes on Vars */
128 : : bool colNamesVisible; /* do we care about output column names? */
129 : : bool inGroupBy; /* deparsing GROUP BY clause? */
130 : : bool varInOrderBy; /* deparsing simple Var in ORDER BY? */
131 : : Bitmapset *appendparents; /* if not null, map child Vars of these relids
132 : : * back to the parent rel */
133 : : } deparse_context;
134 : :
135 : : /*
136 : : * Each level of query context around a subtree needs a level of Var namespace.
137 : : * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
138 : : * the current context's namespaces list.
139 : : *
140 : : * rtable is the list of actual RTEs from the Query or PlannedStmt.
141 : : * rtable_names holds the alias name to be used for each RTE (either a C
142 : : * string, or NULL for nameless RTEs such as unnamed joins).
143 : : * rtable_columns holds the column alias names to be used for each RTE.
144 : : *
145 : : * subplans is a list of Plan trees for SubPlans and CTEs (it's only used
146 : : * in the PlannedStmt case).
147 : : * ctes is a list of CommonTableExpr nodes (only used in the Query case).
148 : : * appendrels, if not null (it's only used in the PlannedStmt case), is an
149 : : * array of AppendRelInfo nodes, indexed by child relid. We use that to map
150 : : * child-table Vars to their inheritance parents.
151 : : *
152 : : * In some cases we need to make names of merged JOIN USING columns unique
153 : : * across the whole query, not only per-RTE. If so, unique_using is true
154 : : * and using_names is a list of C strings representing names already assigned
155 : : * to USING columns.
156 : : *
157 : : * When deparsing plan trees, there is always just a single item in the
158 : : * deparse_namespace list (since a plan tree never contains Vars with
159 : : * varlevelsup > 0). We store the Plan node that is the immediate
160 : : * parent of the expression to be deparsed, as well as a list of that
161 : : * Plan's ancestors. In addition, we store its outer and inner subplan nodes,
162 : : * as well as their targetlists, and the index tlist if the current plan node
163 : : * might contain INDEX_VAR Vars. (These fields could be derived on-the-fly
164 : : * from the current Plan node, but it seems notationally clearer to set them
165 : : * up as separate fields.)
166 : : */
167 : : typedef struct
168 : : {
169 : : List *rtable; /* List of RangeTblEntry nodes */
170 : : List *rtable_names; /* Parallel list of names for RTEs */
171 : : List *rtable_columns; /* Parallel list of deparse_columns structs */
172 : : List *subplans; /* List of Plan trees for SubPlans */
173 : : List *ctes; /* List of CommonTableExpr nodes */
174 : : AppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */
175 : : char *ret_old_alias; /* alias for OLD in RETURNING list */
176 : : char *ret_new_alias; /* alias for NEW in RETURNING list */
177 : : /* Workspace for column alias assignment: */
178 : : bool unique_using; /* Are we making USING names globally unique */
179 : : List *using_names; /* List of assigned names for USING columns */
180 : : /* Remaining fields are used only when deparsing a Plan tree: */
181 : : Plan *plan; /* immediate parent of current expression */
182 : : List *ancestors; /* ancestors of plan */
183 : : Plan *outer_plan; /* outer subnode, or NULL if none */
184 : : Plan *inner_plan; /* inner subnode, or NULL if none */
185 : : List *outer_tlist; /* referent for OUTER_VAR Vars */
186 : : List *inner_tlist; /* referent for INNER_VAR Vars */
187 : : List *index_tlist; /* referent for INDEX_VAR Vars */
188 : : /* Special namespace representing a function signature: */
189 : : char *funcname;
190 : : int numargs;
191 : : char **argnames;
192 : : } deparse_namespace;
193 : :
194 : : /*
195 : : * Per-relation data about column alias names.
196 : : *
197 : : * Selecting aliases is unreasonably complicated because of the need to dump
198 : : * rules/views whose underlying tables may have had columns added, deleted, or
199 : : * renamed since the query was parsed. We must nonetheless print the rule/view
200 : : * in a form that can be reloaded and will produce the same results as before.
201 : : *
202 : : * For each RTE used in the query, we must assign column aliases that are
203 : : * unique within that RTE. SQL does not require this of the original query,
204 : : * but due to factors such as *-expansion we need to be able to uniquely
205 : : * reference every column in a decompiled query. As long as we qualify all
206 : : * column references, per-RTE uniqueness is sufficient for that.
207 : : *
208 : : * However, we can't ensure per-column name uniqueness for unnamed join RTEs,
209 : : * since they just inherit column names from their input RTEs, and we can't
210 : : * rename the columns at the join level. Most of the time this isn't an issue
211 : : * because we don't need to reference the join's output columns as such; we
212 : : * can reference the input columns instead. That approach can fail for merged
213 : : * JOIN USING columns, however, so when we have one of those in an unnamed
214 : : * join, we have to make that column's alias globally unique across the whole
215 : : * query to ensure it can be referenced unambiguously.
216 : : *
217 : : * Another problem is that a JOIN USING clause requires the columns to be
218 : : * merged to have the same aliases in both input RTEs, and that no other
219 : : * columns in those RTEs or their children conflict with the USING names.
220 : : * To handle that, we do USING-column alias assignment in a recursive
221 : : * traversal of the query's jointree. When descending through a JOIN with
222 : : * USING, we preassign the USING column names to the child columns, overriding
223 : : * other rules for column alias assignment. We also mark each RTE with a list
224 : : * of all USING column names selected for joins containing that RTE, so that
225 : : * when we assign other columns' aliases later, we can avoid conflicts.
226 : : *
227 : : * Another problem is that if a JOIN's input tables have had columns added or
228 : : * deleted since the query was parsed, we must generate a column alias list
229 : : * for the join that matches the current set of input columns --- otherwise, a
230 : : * change in the number of columns in the left input would throw off matching
231 : : * of aliases to columns of the right input. Thus, positions in the printable
232 : : * column alias list are not necessarily one-for-one with varattnos of the
233 : : * JOIN, so we need a separate new_colnames[] array for printing purposes.
234 : : *
235 : : * Finally, when dealing with wide tables we risk O(N^2) costs in assigning
236 : : * non-duplicate column names. We ameliorate that by using a hash table that
237 : : * holds all the strings appearing in colnames, new_colnames, and parentUsing.
238 : : */
239 : : typedef struct
240 : : {
241 : : /*
242 : : * colnames is an array containing column aliases to use for columns that
243 : : * existed when the query was parsed. Dropped columns have NULL entries.
244 : : * This array can be directly indexed by varattno to get a Var's name.
245 : : *
246 : : * Non-NULL entries are guaranteed unique within the RTE, *except* when
247 : : * this is for an unnamed JOIN RTE. In that case we merely copy up names
248 : : * from the two input RTEs.
249 : : *
250 : : * During the recursive descent in set_using_names(), forcible assignment
251 : : * of a child RTE's column name is represented by pre-setting that element
252 : : * of the child's colnames array. So at that stage, NULL entries in this
253 : : * array just mean that no name has been preassigned, not necessarily that
254 : : * the column is dropped.
255 : : */
256 : : int num_cols; /* length of colnames[] array */
257 : : char **colnames; /* array of C strings and NULLs */
258 : :
259 : : /*
260 : : * new_colnames is an array containing column aliases to use for columns
261 : : * that would exist if the query was re-parsed against the current
262 : : * definitions of its base tables. This is what to print as the column
263 : : * alias list for the RTE. This array does not include dropped columns,
264 : : * but it will include columns added since original parsing. Indexes in
265 : : * it therefore have little to do with current varattno values. As above,
266 : : * entries are unique unless this is for an unnamed JOIN RTE. (In such an
267 : : * RTE, we never actually print this array, but we must compute it anyway
268 : : * for possible use in computing column names of upper joins.) The
269 : : * parallel array is_new_col marks which of these columns are new since
270 : : * original parsing. Entries with is_new_col false must match the
271 : : * non-NULL colnames entries one-for-one.
272 : : */
273 : : int num_new_cols; /* length of new_colnames[] array */
274 : : char **new_colnames; /* array of C strings */
275 : : bool *is_new_col; /* array of bool flags */
276 : :
277 : : /* This flag tells whether we should actually print a column alias list */
278 : : bool printaliases;
279 : :
280 : : /* This list has all names used as USING names in joins above this RTE */
281 : : List *parentUsing; /* names assigned to parent merged columns */
282 : :
283 : : /*
284 : : * If this struct is for a JOIN RTE, we fill these fields during the
285 : : * set_using_names() pass to describe its relationship to its child RTEs.
286 : : *
287 : : * leftattnos and rightattnos are arrays with one entry per existing
288 : : * output column of the join (hence, indexable by join varattno). For a
289 : : * simple reference to a column of the left child, leftattnos[i] is the
290 : : * child RTE's attno and rightattnos[i] is zero; and conversely for a
291 : : * column of the right child. But for merged columns produced by JOIN
292 : : * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.
293 : : * Note that a simple reference might be to a child RTE column that's been
294 : : * dropped; but that's OK since the column could not be used in the query.
295 : : *
296 : : * If it's a JOIN USING, usingNames holds the alias names selected for the
297 : : * merged columns (these might be different from the original USING list,
298 : : * if we had to modify names to achieve uniqueness).
299 : : */
300 : : int leftrti; /* rangetable index of left child */
301 : : int rightrti; /* rangetable index of right child */
302 : : int *leftattnos; /* left-child varattnos of join cols, or 0 */
303 : : int *rightattnos; /* right-child varattnos of join cols, or 0 */
304 : : List *usingNames; /* names assigned to merged columns */
305 : :
306 : : /*
307 : : * Hash table holding copies of all the strings appearing in this struct's
308 : : * colnames, new_colnames, and parentUsing. We use a hash table only for
309 : : * sufficiently wide relations, and only during the colname-assignment
310 : : * functions set_relation_column_names and set_join_column_names;
311 : : * otherwise, names_hash is NULL.
312 : : */
313 : : HTAB *names_hash; /* entries are just strings */
314 : : } deparse_columns;
315 : :
316 : : /* This macro is analogous to rt_fetch(), but for deparse_columns structs */
317 : : #define deparse_columns_fetch(rangetable_index, dpns) \
318 : : ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
319 : :
320 : : /*
321 : : * Entry in set_rtable_names' hash table
322 : : */
323 : : typedef struct
324 : : {
325 : : char name[NAMEDATALEN]; /* Hash key --- must be first */
326 : : int counter; /* Largest addition used so far for name */
327 : : } NameHashEntry;
328 : :
329 : : /* Callback signature for resolve_special_varno() */
330 : : typedef void (*rsv_callback) (Node *node, deparse_context *context,
331 : : void *callback_arg);
332 : :
333 : :
334 : : /* ----------
335 : : * Global data
336 : : * ----------
337 : : */
338 : : static SPIPlanPtr plan_getrulebyoid = NULL;
339 : : static const char *const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
340 : : static SPIPlanPtr plan_getviewrule = NULL;
341 : : static const char *const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
342 : :
343 : : /* GUC parameters */
344 : : bool quote_all_identifiers = false;
345 : :
346 : :
347 : : /* ----------
348 : : * Local functions
349 : : *
350 : : * Most of these functions used to use fixed-size buffers to build their
351 : : * results. Now, they take an (already initialized) StringInfo object
352 : : * as a parameter, and append their text output to its contents.
353 : : * ----------
354 : : */
355 : : static char *deparse_expression_pretty(Node *expr, List *dpcontext,
356 : : bool forceprefix, bool showimplicit,
357 : : int prettyFlags, int startIndent);
358 : : static char *pg_get_viewdef_worker(Oid viewoid,
359 : : int prettyFlags, int wrapColumn);
360 : : static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
361 : : static int decompile_column_index_array(Datum column_index_array, Oid relId,
362 : : bool withPeriod, StringInfo buf);
363 : : static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
364 : : static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
365 : : const Oid *excludeOps,
366 : : bool attrsOnly, bool keysOnly,
367 : : bool showTblSpc, bool inherits,
368 : : int prettyFlags, bool missing_ok);
369 : : static void make_propgraphdef_elements(StringInfo buf, Oid pgrelid, char pgekind);
370 : : static void make_propgraphdef_labels(StringInfo buf, Oid elid, const char *elalias, Oid elrelid);
371 : : static void make_propgraphdef_properties(StringInfo buf, Oid ellabelid, Oid elrelid);
372 : : static char *pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
373 : : bool missing_ok);
374 : : static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
375 : : bool attrsOnly, bool missing_ok);
376 : : static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
377 : : int prettyFlags, bool missing_ok);
378 : : static text *pg_get_expr_worker(text *expr, Oid relid, int prettyFlags);
379 : : static int print_function_arguments(StringInfo buf, HeapTuple proctup,
380 : : bool print_table_args, bool print_defaults);
381 : : static void print_function_rettype(StringInfo buf, HeapTuple proctup);
382 : : static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
383 : : static void print_function_sqlbody(StringInfo buf, HeapTuple proctup);
384 : : static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
385 : : Bitmapset *rels_used);
386 : : static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
387 : : List *parent_namespaces);
388 : : static void set_simple_column_names(deparse_namespace *dpns);
389 : : static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
390 : : static void set_using_names(deparse_namespace *dpns, Node *jtnode,
391 : : List *parentUsing);
392 : : static void set_relation_column_names(deparse_namespace *dpns,
393 : : RangeTblEntry *rte,
394 : : deparse_columns *colinfo);
395 : : static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
396 : : deparse_columns *colinfo);
397 : : static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
398 : : deparse_columns *colinfo);
399 : : static char *make_colname_unique(char *colname, deparse_namespace *dpns,
400 : : deparse_columns *colinfo);
401 : : static void expand_colnames_array_to(deparse_columns *colinfo, int n);
402 : : static void build_colinfo_names_hash(deparse_columns *colinfo);
403 : : static void add_to_names_hash(deparse_columns *colinfo, const char *name);
404 : : static void destroy_colinfo_names_hash(deparse_columns *colinfo);
405 : : static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
406 : : deparse_columns *colinfo);
407 : : static char *get_rtable_name(int rtindex, deparse_context *context);
408 : : static void set_deparse_plan(deparse_namespace *dpns, Plan *plan);
409 : : static Plan *find_recursive_union(deparse_namespace *dpns,
410 : : WorkTableScan *wtscan);
411 : : static void push_child_plan(deparse_namespace *dpns, Plan *plan,
412 : : deparse_namespace *save_dpns);
413 : : static void pop_child_plan(deparse_namespace *dpns,
414 : : deparse_namespace *save_dpns);
415 : : static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
416 : : deparse_namespace *save_dpns);
417 : : static void pop_ancestor_plan(deparse_namespace *dpns,
418 : : deparse_namespace *save_dpns);
419 : : static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
420 : : int prettyFlags);
421 : : static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
422 : : int prettyFlags, int wrapColumn);
423 : : static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
424 : : TupleDesc resultDesc, bool colNamesVisible,
425 : : int prettyFlags, int wrapColumn, int startIndent);
426 : : static void get_values_def(List *values_lists, deparse_context *context);
427 : : static void get_with_clause(Query *query, deparse_context *context);
428 : : static void get_select_query_def(Query *query, deparse_context *context);
429 : : static void get_insert_query_def(Query *query, deparse_context *context);
430 : : static void get_update_query_def(Query *query, deparse_context *context);
431 : : static void get_update_query_targetlist_def(Query *query, List *targetList,
432 : : deparse_context *context,
433 : : RangeTblEntry *rte);
434 : : static void get_delete_query_def(Query *query, deparse_context *context);
435 : : static void get_merge_query_def(Query *query, deparse_context *context);
436 : : static void get_utility_query_def(Query *query, deparse_context *context);
437 : : static char *get_lock_clause_strength(LockClauseStrength strength);
438 : : static void get_basic_select_query(Query *query, deparse_context *context);
439 : : static void get_target_list(List *targetList, deparse_context *context);
440 : : static void get_returning_clause(Query *query, deparse_context *context);
441 : : static void get_setop_query(Node *setOp, Query *query,
442 : : deparse_context *context);
443 : : static Node *get_rule_sortgroupclause(Index ref, List *tlist,
444 : : bool force_colno,
445 : : deparse_context *context);
446 : : static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
447 : : bool omit_parens, deparse_context *context);
448 : : static void get_rule_orderby(List *orderList, List *targetList,
449 : : bool force_colno, deparse_context *context);
450 : : static void get_rule_windowclause(Query *query, deparse_context *context);
451 : : static void get_rule_windowspec(WindowClause *wc, List *targetList,
452 : : deparse_context *context);
453 : : static void get_window_frame_options(int frameOptions,
454 : : Node *startOffset, Node *endOffset,
455 : : deparse_context *context);
456 : : static char *get_variable(Var *var, int levelsup, bool istoplevel,
457 : : deparse_context *context);
458 : : static void get_special_variable(Node *node, deparse_context *context,
459 : : void *callback_arg);
460 : : static void resolve_special_varno(Node *node, deparse_context *context,
461 : : rsv_callback callback, void *callback_arg);
462 : : static Node *find_param_referent(Param *param, deparse_context *context,
463 : : deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
464 : : static SubPlan *find_param_generator(Param *param, deparse_context *context,
465 : : int *column_p);
466 : : static SubPlan *find_param_generator_initplan(Param *param, Plan *plan,
467 : : int *column_p);
468 : : static void get_parameter(Param *param, deparse_context *context);
469 : : static const char *get_simple_binary_op_name(OpExpr *expr);
470 : : static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
471 : : static void appendContextKeyword(deparse_context *context, const char *str,
472 : : int indentBefore, int indentAfter, int indentPlus);
473 : : static void removeStringInfoSpaces(StringInfo str);
474 : : static void get_rule_expr(Node *node, deparse_context *context,
475 : : bool showimplicit);
476 : : static void get_rule_expr_toplevel(Node *node, deparse_context *context,
477 : : bool showimplicit);
478 : : static void get_rule_list_toplevel(List *lst, deparse_context *context,
479 : : bool showimplicit);
480 : : static void get_rule_expr_funccall(Node *node, deparse_context *context,
481 : : bool showimplicit);
482 : : static bool looks_like_function(Node *node);
483 : : static void get_oper_expr(OpExpr *expr, deparse_context *context);
484 : : static void get_func_expr(FuncExpr *expr, deparse_context *context,
485 : : bool showimplicit);
486 : : static void get_agg_expr(Aggref *aggref, deparse_context *context,
487 : : Aggref *original_aggref);
488 : : static void get_agg_expr_helper(Aggref *aggref, deparse_context *context,
489 : : Aggref *original_aggref, const char *funcname,
490 : : const char *options, bool is_json_objectagg);
491 : : static void get_agg_combine_expr(Node *node, deparse_context *context,
492 : : void *callback_arg);
493 : : static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
494 : : static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
495 : : const char *funcname, const char *options,
496 : : bool is_json_objectagg);
497 : : static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
498 : : static void get_coercion_expr(Node *arg, deparse_context *context,
499 : : Oid resulttype, int32 resulttypmod,
500 : : Node *parentNode);
501 : : static void get_const_expr(Const *constval, deparse_context *context,
502 : : int showtype);
503 : : static void get_const_collation(Const *constval, deparse_context *context);
504 : : static void get_json_format(JsonFormat *format, StringInfo buf);
505 : : static void get_json_returning(JsonReturning *returning, StringInfo buf,
506 : : bool json_format_by_default);
507 : : static void get_json_constructor(JsonConstructorExpr *ctor,
508 : : deparse_context *context, bool showimplicit);
509 : : static void get_json_constructor_options(JsonConstructorExpr *ctor,
510 : : StringInfo buf);
511 : : static void get_json_agg_constructor(JsonConstructorExpr *ctor,
512 : : deparse_context *context,
513 : : const char *funcname,
514 : : bool is_json_objectagg);
515 : : static void get_json_agg_constructor_expr(Node *node, deparse_context *context,
516 : : void *callback_arg);
517 : : static void simple_quote_literal(StringInfo buf, const char *val);
518 : : static void get_sublink_expr(SubLink *sublink, deparse_context *context);
519 : : static void get_tablefunc(TableFunc *tf, deparse_context *context,
520 : : bool showimplicit);
521 : : static void get_from_clause(Query *query, const char *prefix,
522 : : deparse_context *context);
523 : : static void get_from_clause_item(Node *jtnode, Query *query,
524 : : deparse_context *context);
525 : : static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
526 : : deparse_context *context);
527 : : static void get_column_alias_list(deparse_columns *colinfo,
528 : : deparse_context *context);
529 : : static void get_for_portion_of(ForPortionOfExpr *forPortionOf,
530 : : RangeTblEntry *rte,
531 : : deparse_context *context);
532 : : static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
533 : : deparse_columns *colinfo,
534 : : deparse_context *context);
535 : : static void get_tablesample_def(TableSampleClause *tablesample,
536 : : deparse_context *context);
537 : : static void get_opclass_name(Oid opclass, Oid actual_datatype,
538 : : StringInfo buf);
539 : : static Node *processIndirection(Node *node, deparse_context *context);
540 : : static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
541 : : static char *get_relation_name(Oid relid);
542 : : static char *generate_relation_name(Oid relid, List *namespaces);
543 : : static char *generate_qualified_relation_name(Oid relid);
544 : : static char *generate_function_name(Oid funcid, int nargs,
545 : : List *argnames, Oid *argtypes,
546 : : bool has_variadic, bool *use_variadic_p,
547 : : bool inGroupBy);
548 : : static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
549 : : static void add_cast_to(StringInfo buf, Oid typid);
550 : : static char *generate_qualified_type_name(Oid typid);
551 : : static text *string_to_text(char *str);
552 : : static char *flatten_reloptions(Oid relid);
553 : : void get_reloptions(StringInfo buf, Datum reloptions);
554 : : static void get_json_path_spec(Node *path_spec, deparse_context *context,
555 : : bool showimplicit);
556 : : static void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
557 : : deparse_context *context,
558 : : bool showimplicit);
559 : : static void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
560 : : deparse_context *context,
561 : : bool showimplicit,
562 : : bool needcomma);
563 : :
564 : : #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
565 : :
566 : :
567 : : /* ----------
568 : : * pg_get_ruledef - Do it all and return a text
569 : : * that could be used as a statement
570 : : * to recreate the rule
571 : : * ----------
572 : : */
573 : : Datum
574 : 237 : pg_get_ruledef(PG_FUNCTION_ARGS)
575 : : {
576 : 237 : Oid ruleoid = PG_GETARG_OID(0);
577 : : int prettyFlags;
578 : : char *res;
579 : :
580 : 237 : prettyFlags = PRETTYFLAG_INDENT;
581 : :
582 : 237 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
583 : :
584 [ + + ]: 237 : if (res == NULL)
585 : 4 : PG_RETURN_NULL();
586 : :
587 : 233 : PG_RETURN_TEXT_P(string_to_text(res));
588 : : }
589 : :
590 : :
591 : : Datum
592 : 92 : pg_get_ruledef_ext(PG_FUNCTION_ARGS)
593 : : {
594 : 92 : Oid ruleoid = PG_GETARG_OID(0);
595 : 92 : bool pretty = PG_GETARG_BOOL(1);
596 : : int prettyFlags;
597 : : char *res;
598 : :
599 [ + - ]: 92 : prettyFlags = GET_PRETTY_FLAGS(pretty);
600 : :
601 : 92 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
602 : :
603 [ - + ]: 92 : if (res == NULL)
604 : 0 : PG_RETURN_NULL();
605 : :
606 : 92 : PG_RETURN_TEXT_P(string_to_text(res));
607 : : }
608 : :
609 : :
610 : : static char *
611 : 329 : pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
612 : : {
613 : : Datum args[1];
614 : : char nulls[1];
615 : : int spirc;
616 : : HeapTuple ruletup;
617 : : TupleDesc rulettc;
618 : : StringInfoData buf;
619 : :
620 : : /*
621 : : * Do this first so that string is alloc'd in outer context not SPI's.
622 : : */
623 : 329 : initStringInfo(&buf);
624 : :
625 : : /*
626 : : * Connect to SPI manager
627 : : */
628 : 329 : SPI_connect();
629 : :
630 : : /*
631 : : * On the first call prepare the plan to lookup pg_rewrite. We read
632 : : * pg_rewrite over the SPI manager instead of using the syscache to be
633 : : * checked for read access on pg_rewrite.
634 : : */
635 [ + + ]: 329 : if (plan_getrulebyoid == NULL)
636 : : {
637 : : Oid argtypes[1];
638 : : SPIPlanPtr plan;
639 : :
640 : 28 : argtypes[0] = OIDOID;
641 : 28 : plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
642 [ - + ]: 28 : if (plan == NULL)
643 [ # # ]: 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
644 : 28 : SPI_keepplan(plan);
645 : 28 : plan_getrulebyoid = plan;
646 : : }
647 : :
648 : : /*
649 : : * Get the pg_rewrite tuple for this rule
650 : : */
651 : 329 : args[0] = ObjectIdGetDatum(ruleoid);
652 : 329 : nulls[0] = ' ';
653 : 329 : spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
654 [ - + ]: 329 : if (spirc != SPI_OK_SELECT)
655 [ # # ]: 0 : elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
656 [ + + ]: 329 : if (SPI_processed != 1)
657 : : {
658 : : /*
659 : : * There is no tuple data available here, just keep the output buffer
660 : : * empty.
661 : : */
662 : : }
663 : : else
664 : : {
665 : : /*
666 : : * Get the rule's definition and put it into executor's memory
667 : : */
668 : 325 : ruletup = SPI_tuptable->vals[0];
669 : 325 : rulettc = SPI_tuptable->tupdesc;
670 : 325 : make_ruledef(&buf, ruletup, rulettc, prettyFlags);
671 : : }
672 : :
673 : : /*
674 : : * Disconnect from SPI manager
675 : : */
676 [ - + ]: 329 : if (SPI_finish() != SPI_OK_FINISH)
677 [ # # ]: 0 : elog(ERROR, "SPI_finish failed");
678 : :
679 [ + + ]: 329 : if (buf.len == 0)
680 : 4 : return NULL;
681 : :
682 : 325 : return buf.data;
683 : : }
684 : :
685 : :
686 : : /* ----------
687 : : * pg_get_viewdef - Mainly the same thing, but we
688 : : * only return the SELECT part of a view
689 : : * ----------
690 : : */
691 : : Datum
692 : 1442 : pg_get_viewdef(PG_FUNCTION_ARGS)
693 : : {
694 : : /* By OID */
695 : 1442 : Oid viewoid = PG_GETARG_OID(0);
696 : : int prettyFlags;
697 : : char *res;
698 : :
699 : 1442 : prettyFlags = PRETTYFLAG_INDENT;
700 : :
701 : 1442 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
702 : :
703 [ + + ]: 1442 : if (res == NULL)
704 : 4 : PG_RETURN_NULL();
705 : :
706 : 1438 : PG_RETURN_TEXT_P(string_to_text(res));
707 : : }
708 : :
709 : :
710 : : Datum
711 : 403 : pg_get_viewdef_ext(PG_FUNCTION_ARGS)
712 : : {
713 : : /* By OID */
714 : 403 : Oid viewoid = PG_GETARG_OID(0);
715 : 403 : bool pretty = PG_GETARG_BOOL(1);
716 : : int prettyFlags;
717 : : char *res;
718 : :
719 [ + - ]: 403 : prettyFlags = GET_PRETTY_FLAGS(pretty);
720 : :
721 : 403 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
722 : :
723 [ - + ]: 403 : if (res == NULL)
724 : 0 : PG_RETURN_NULL();
725 : :
726 : 403 : PG_RETURN_TEXT_P(string_to_text(res));
727 : : }
728 : :
729 : : Datum
730 : 4 : pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
731 : : {
732 : : /* By OID */
733 : 4 : Oid viewoid = PG_GETARG_OID(0);
734 : 4 : int wrap = PG_GETARG_INT32(1);
735 : : int prettyFlags;
736 : : char *res;
737 : :
738 : : /* calling this implies we want pretty printing */
739 : 4 : prettyFlags = GET_PRETTY_FLAGS(true);
740 : :
741 : 4 : res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
742 : :
743 [ - + ]: 4 : if (res == NULL)
744 : 0 : PG_RETURN_NULL();
745 : :
746 : 4 : PG_RETURN_TEXT_P(string_to_text(res));
747 : : }
748 : :
749 : : Datum
750 : 52 : pg_get_viewdef_name(PG_FUNCTION_ARGS)
751 : : {
752 : : /* By qualified name */
753 : 52 : text *viewname = PG_GETARG_TEXT_PP(0);
754 : : int prettyFlags;
755 : : RangeVar *viewrel;
756 : : Oid viewoid;
757 : : char *res;
758 : :
759 : 52 : prettyFlags = PRETTYFLAG_INDENT;
760 : :
761 : : /* Look up view name. Can't lock it - we might not have privileges. */
762 : 52 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
763 : 52 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
764 : :
765 : 52 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
766 : :
767 [ - + ]: 52 : if (res == NULL)
768 : 0 : PG_RETURN_NULL();
769 : :
770 : 52 : PG_RETURN_TEXT_P(string_to_text(res));
771 : : }
772 : :
773 : :
774 : : Datum
775 : 268 : pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
776 : : {
777 : : /* By qualified name */
778 : 268 : text *viewname = PG_GETARG_TEXT_PP(0);
779 : 268 : bool pretty = PG_GETARG_BOOL(1);
780 : : int prettyFlags;
781 : : RangeVar *viewrel;
782 : : Oid viewoid;
783 : : char *res;
784 : :
785 [ + - ]: 268 : prettyFlags = GET_PRETTY_FLAGS(pretty);
786 : :
787 : : /* Look up view name. Can't lock it - we might not have privileges. */
788 : 268 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
789 : 268 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
790 : :
791 : 268 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
792 : :
793 [ - + ]: 268 : if (res == NULL)
794 : 0 : PG_RETURN_NULL();
795 : :
796 : 268 : PG_RETURN_TEXT_P(string_to_text(res));
797 : : }
798 : :
799 : : /*
800 : : * Common code for by-OID and by-name variants of pg_get_viewdef
801 : : */
802 : : static char *
803 : 2169 : pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
804 : : {
805 : : Datum args[2];
806 : : char nulls[2];
807 : : int spirc;
808 : : HeapTuple ruletup;
809 : : TupleDesc rulettc;
810 : : StringInfoData buf;
811 : :
812 : : /*
813 : : * Do this first so that string is alloc'd in outer context not SPI's.
814 : : */
815 : 2169 : initStringInfo(&buf);
816 : :
817 : : /*
818 : : * Connect to SPI manager
819 : : */
820 : 2169 : SPI_connect();
821 : :
822 : : /*
823 : : * On the first call prepare the plan to lookup pg_rewrite. We read
824 : : * pg_rewrite over the SPI manager instead of using the syscache to be
825 : : * checked for read access on pg_rewrite.
826 : : */
827 [ + + ]: 2169 : if (plan_getviewrule == NULL)
828 : : {
829 : : Oid argtypes[2];
830 : : SPIPlanPtr plan;
831 : :
832 : 155 : argtypes[0] = OIDOID;
833 : 155 : argtypes[1] = NAMEOID;
834 : 155 : plan = SPI_prepare(query_getviewrule, 2, argtypes);
835 [ - + ]: 155 : if (plan == NULL)
836 [ # # ]: 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
837 : 155 : SPI_keepplan(plan);
838 : 155 : plan_getviewrule = plan;
839 : : }
840 : :
841 : : /*
842 : : * Get the pg_rewrite tuple for the view's SELECT rule
843 : : */
844 : 2169 : args[0] = ObjectIdGetDatum(viewoid);
845 : 2169 : args[1] = DirectFunctionCall1(namein, CStringGetDatum(ViewSelectRuleName));
846 : 2169 : nulls[0] = ' ';
847 : 2169 : nulls[1] = ' ';
848 : 2169 : spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
849 [ - + ]: 2169 : if (spirc != SPI_OK_SELECT)
850 [ # # ]: 0 : elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
851 [ + + ]: 2169 : if (SPI_processed != 1)
852 : : {
853 : : /*
854 : : * There is no tuple data available here, just keep the output buffer
855 : : * empty.
856 : : */
857 : : }
858 : : else
859 : : {
860 : : /*
861 : : * Get the rule's definition and put it into executor's memory
862 : : */
863 : 2165 : ruletup = SPI_tuptable->vals[0];
864 : 2165 : rulettc = SPI_tuptable->tupdesc;
865 : 2165 : make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
866 : : }
867 : :
868 : : /*
869 : : * Disconnect from SPI manager
870 : : */
871 [ - + ]: 2169 : if (SPI_finish() != SPI_OK_FINISH)
872 [ # # ]: 0 : elog(ERROR, "SPI_finish failed");
873 : :
874 [ + + ]: 2169 : if (buf.len == 0)
875 : 4 : return NULL;
876 : :
877 : 2165 : return buf.data;
878 : : }
879 : :
880 : : /* ----------
881 : : * pg_get_triggerdef - Get the definition of a trigger
882 : : * ----------
883 : : */
884 : : Datum
885 : 96 : pg_get_triggerdef(PG_FUNCTION_ARGS)
886 : : {
887 : 96 : Oid trigid = PG_GETARG_OID(0);
888 : : char *res;
889 : :
890 : 96 : res = pg_get_triggerdef_worker(trigid, false);
891 : :
892 [ + + ]: 96 : if (res == NULL)
893 : 4 : PG_RETURN_NULL();
894 : :
895 : 92 : PG_RETURN_TEXT_P(string_to_text(res));
896 : : }
897 : :
898 : : Datum
899 : 655 : pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
900 : : {
901 : 655 : Oid trigid = PG_GETARG_OID(0);
902 : 655 : bool pretty = PG_GETARG_BOOL(1);
903 : : char *res;
904 : :
905 : 655 : res = pg_get_triggerdef_worker(trigid, pretty);
906 : :
907 [ - + ]: 655 : if (res == NULL)
908 : 0 : PG_RETURN_NULL();
909 : :
910 : 655 : PG_RETURN_TEXT_P(string_to_text(res));
911 : : }
912 : :
913 : : static char *
914 : 751 : pg_get_triggerdef_worker(Oid trigid, bool pretty)
915 : : {
916 : : HeapTuple ht_trig;
917 : : Form_pg_trigger trigrec;
918 : : StringInfoData buf;
919 : : Relation tgrel;
920 : : ScanKeyData skey[1];
921 : : SysScanDesc tgscan;
922 : 751 : int findx = 0;
923 : : char *tgname;
924 : : char *tgoldtable;
925 : : char *tgnewtable;
926 : : Datum value;
927 : : bool isnull;
928 : :
929 : : /*
930 : : * Fetch the pg_trigger tuple by the Oid of the trigger
931 : : */
932 : 751 : tgrel = table_open(TriggerRelationId, AccessShareLock);
933 : :
934 : 751 : ScanKeyInit(&skey[0],
935 : : Anum_pg_trigger_oid,
936 : : BTEqualStrategyNumber, F_OIDEQ,
937 : : ObjectIdGetDatum(trigid));
938 : :
939 : 751 : tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
940 : : NULL, 1, skey);
941 : :
942 : 751 : ht_trig = systable_getnext(tgscan);
943 : :
944 [ + + ]: 751 : if (!HeapTupleIsValid(ht_trig))
945 : : {
946 : 4 : systable_endscan(tgscan);
947 : 4 : table_close(tgrel, AccessShareLock);
948 : 4 : return NULL;
949 : : }
950 : :
951 : 747 : trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
952 : :
953 : : /*
954 : : * Start the trigger definition. Note that the trigger's name should never
955 : : * be schema-qualified, but the trigger rel's name may be.
956 : : */
957 : 747 : initStringInfo(&buf);
958 : :
959 : 747 : tgname = NameStr(trigrec->tgname);
960 : 1494 : appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
961 [ - + ]: 747 : OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
962 : : quote_identifier(tgname));
963 : :
964 [ + + ]: 747 : if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
965 : 287 : appendStringInfoString(&buf, "BEFORE");
966 [ + + ]: 460 : else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
967 : 444 : appendStringInfoString(&buf, "AFTER");
968 [ + - ]: 16 : else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
969 : 16 : appendStringInfoString(&buf, "INSTEAD OF");
970 : : else
971 [ # # ]: 0 : elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
972 : :
973 [ + + ]: 747 : if (TRIGGER_FOR_INSERT(trigrec->tgtype))
974 : : {
975 : 512 : appendStringInfoString(&buf, " INSERT");
976 : 512 : findx++;
977 : : }
978 [ + + ]: 747 : if (TRIGGER_FOR_DELETE(trigrec->tgtype))
979 : : {
980 [ + + ]: 113 : if (findx > 0)
981 : 45 : appendStringInfoString(&buf, " OR DELETE");
982 : : else
983 : 68 : appendStringInfoString(&buf, " DELETE");
984 : 113 : findx++;
985 : : }
986 [ + + ]: 747 : if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
987 : : {
988 [ + + ]: 332 : if (findx > 0)
989 : 165 : appendStringInfoString(&buf, " OR UPDATE");
990 : : else
991 : 167 : appendStringInfoString(&buf, " UPDATE");
992 : 332 : findx++;
993 : : /* tgattr is first var-width field, so OK to access directly */
994 [ + + ]: 332 : if (trigrec->tgattr.dim1 > 0)
995 : : {
996 : : int i;
997 : :
998 : 44 : appendStringInfoString(&buf, " OF ");
999 [ + + ]: 97 : for (i = 0; i < trigrec->tgattr.dim1; i++)
1000 : : {
1001 : : char *attname;
1002 : :
1003 [ + + ]: 53 : if (i > 0)
1004 : 9 : appendStringInfoString(&buf, ", ");
1005 : 53 : attname = get_attname(trigrec->tgrelid,
1006 : 53 : trigrec->tgattr.values[i], false);
1007 : 53 : appendStringInfoString(&buf, quote_identifier(attname));
1008 : : }
1009 : : }
1010 : : }
1011 [ - + ]: 747 : if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
1012 : : {
1013 [ # # ]: 0 : if (findx > 0)
1014 : 0 : appendStringInfoString(&buf, " OR TRUNCATE");
1015 : : else
1016 : 0 : appendStringInfoString(&buf, " TRUNCATE");
1017 : 0 : findx++;
1018 : : }
1019 : :
1020 : : /*
1021 : : * In non-pretty mode, always schema-qualify the target table name for
1022 : : * safety. In pretty mode, schema-qualify only if not visible.
1023 : : */
1024 [ + + ]: 1494 : appendStringInfo(&buf, " ON %s ",
1025 : : pretty ?
1026 : 116 : generate_relation_name(trigrec->tgrelid, NIL) :
1027 : 631 : generate_qualified_relation_name(trigrec->tgrelid));
1028 : :
1029 [ - + ]: 747 : if (OidIsValid(trigrec->tgconstraint))
1030 : : {
1031 [ # # ]: 0 : if (OidIsValid(trigrec->tgconstrrelid))
1032 : 0 : appendStringInfo(&buf, "FROM %s ",
1033 : : generate_relation_name(trigrec->tgconstrrelid, NIL));
1034 [ # # ]: 0 : if (!trigrec->tgdeferrable)
1035 : 0 : appendStringInfoString(&buf, "NOT ");
1036 : 0 : appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
1037 [ # # ]: 0 : if (trigrec->tginitdeferred)
1038 : 0 : appendStringInfoString(&buf, "DEFERRED ");
1039 : : else
1040 : 0 : appendStringInfoString(&buf, "IMMEDIATE ");
1041 : : }
1042 : :
1043 : 747 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
1044 : : tgrel->rd_att, &isnull);
1045 [ + + ]: 747 : if (!isnull)
1046 : 57 : tgoldtable = NameStr(*DatumGetName(value));
1047 : : else
1048 : 690 : tgoldtable = NULL;
1049 : 747 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
1050 : : tgrel->rd_att, &isnull);
1051 [ + + ]: 747 : if (!isnull)
1052 : 62 : tgnewtable = NameStr(*DatumGetName(value));
1053 : : else
1054 : 685 : tgnewtable = NULL;
1055 [ + + + + ]: 747 : if (tgoldtable != NULL || tgnewtable != NULL)
1056 : : {
1057 : 88 : appendStringInfoString(&buf, "REFERENCING ");
1058 [ + + ]: 88 : if (tgoldtable != NULL)
1059 : 57 : appendStringInfo(&buf, "OLD TABLE AS %s ",
1060 : : quote_identifier(tgoldtable));
1061 [ + + ]: 88 : if (tgnewtable != NULL)
1062 : 62 : appendStringInfo(&buf, "NEW TABLE AS %s ",
1063 : : quote_identifier(tgnewtable));
1064 : : }
1065 : :
1066 [ + + ]: 747 : if (TRIGGER_FOR_ROW(trigrec->tgtype))
1067 : 558 : appendStringInfoString(&buf, "FOR EACH ROW ");
1068 : : else
1069 : 189 : appendStringInfoString(&buf, "FOR EACH STATEMENT ");
1070 : :
1071 : : /* If the trigger has a WHEN qualification, add that */
1072 : 747 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
1073 : : tgrel->rd_att, &isnull);
1074 [ + + ]: 747 : if (!isnull)
1075 : : {
1076 : : Node *qual;
1077 : : char relkind;
1078 : : deparse_context context;
1079 : : deparse_namespace dpns;
1080 : : RangeTblEntry *oldrte;
1081 : : RangeTblEntry *newrte;
1082 : :
1083 : 88 : appendStringInfoString(&buf, "WHEN (");
1084 : :
1085 : 88 : qual = stringToNode(TextDatumGetCString(value));
1086 : :
1087 : 88 : relkind = get_rel_relkind(trigrec->tgrelid);
1088 : :
1089 : : /* Build minimal OLD and NEW RTEs for the rel */
1090 : 88 : oldrte = makeNode(RangeTblEntry);
1091 : 88 : oldrte->rtekind = RTE_RELATION;
1092 : 88 : oldrte->relid = trigrec->tgrelid;
1093 : 88 : oldrte->relkind = relkind;
1094 : 88 : oldrte->rellockmode = AccessShareLock;
1095 : 88 : oldrte->alias = makeAlias("old", NIL);
1096 : 88 : oldrte->eref = oldrte->alias;
1097 : 88 : oldrte->lateral = false;
1098 : 88 : oldrte->inh = false;
1099 : 88 : oldrte->inFromCl = true;
1100 : :
1101 : 88 : newrte = makeNode(RangeTblEntry);
1102 : 88 : newrte->rtekind = RTE_RELATION;
1103 : 88 : newrte->relid = trigrec->tgrelid;
1104 : 88 : newrte->relkind = relkind;
1105 : 88 : newrte->rellockmode = AccessShareLock;
1106 : 88 : newrte->alias = makeAlias("new", NIL);
1107 : 88 : newrte->eref = newrte->alias;
1108 : 88 : newrte->lateral = false;
1109 : 88 : newrte->inh = false;
1110 : 88 : newrte->inFromCl = true;
1111 : :
1112 : : /* Build two-element rtable */
1113 : 88 : memset(&dpns, 0, sizeof(dpns));
1114 : 88 : dpns.rtable = list_make2(oldrte, newrte);
1115 : 88 : dpns.subplans = NIL;
1116 : 88 : dpns.ctes = NIL;
1117 : 88 : dpns.appendrels = NULL;
1118 : 88 : set_rtable_names(&dpns, NIL, NULL);
1119 : 88 : set_simple_column_names(&dpns);
1120 : :
1121 : : /* Set up context with one-deep namespace stack */
1122 : 88 : context.buf = &buf;
1123 : 88 : context.namespaces = list_make1(&dpns);
1124 : 88 : context.resultDesc = NULL;
1125 : 88 : context.targetList = NIL;
1126 : 88 : context.windowClause = NIL;
1127 : 88 : context.varprefix = true;
1128 [ + + ]: 88 : context.prettyFlags = GET_PRETTY_FLAGS(pretty);
1129 : 88 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
1130 : 88 : context.indentLevel = PRETTYINDENT_STD;
1131 : 88 : context.colNamesVisible = true;
1132 : 88 : context.inGroupBy = false;
1133 : 88 : context.varInOrderBy = false;
1134 : 88 : context.appendparents = NULL;
1135 : :
1136 : 88 : get_rule_expr(qual, &context, false);
1137 : :
1138 : 88 : appendStringInfoString(&buf, ") ");
1139 : : }
1140 : :
1141 : 747 : appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
1142 : : generate_function_name(trigrec->tgfoid, 0,
1143 : : NIL, NULL,
1144 : : false, NULL, false));
1145 : :
1146 [ + + ]: 747 : if (trigrec->tgnargs > 0)
1147 : : {
1148 : : char *p;
1149 : : int i;
1150 : :
1151 : 249 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
1152 : : tgrel->rd_att, &isnull);
1153 [ - + ]: 249 : if (isnull)
1154 [ # # ]: 0 : elog(ERROR, "tgargs is null for trigger %u", trigid);
1155 : 249 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
1156 [ + + ]: 508 : for (i = 0; i < trigrec->tgnargs; i++)
1157 : : {
1158 [ + + ]: 259 : if (i > 0)
1159 : 10 : appendStringInfoString(&buf, ", ");
1160 : 259 : simple_quote_literal(&buf, p);
1161 : : /* advance p to next string embedded in tgargs */
1162 [ + + ]: 2905 : while (*p)
1163 : 2646 : p++;
1164 : 259 : p++;
1165 : : }
1166 : : }
1167 : :
1168 : : /* We deliberately do not put semi-colon at end */
1169 : 747 : appendStringInfoChar(&buf, ')');
1170 : :
1171 : : /* Clean up */
1172 : 747 : systable_endscan(tgscan);
1173 : :
1174 : 747 : table_close(tgrel, AccessShareLock);
1175 : :
1176 : 747 : return buf.data;
1177 : : }
1178 : :
1179 : : /* ----------
1180 : : * pg_get_indexdef - Get the definition of an index
1181 : : *
1182 : : * In the extended version, there is a colno argument as well as pretty bool.
1183 : : * if colno == 0, we want a complete index definition.
1184 : : * if colno > 0, we only want the Nth index key's variable or expression.
1185 : : *
1186 : : * Note that the SQL-function versions of this omit any info about the
1187 : : * index tablespace; this is intentional because pg_dump wants it that way.
1188 : : * However pg_get_indexdef_string() includes the index tablespace.
1189 : : * ----------
1190 : : */
1191 : : Datum
1192 : 3189 : pg_get_indexdef(PG_FUNCTION_ARGS)
1193 : : {
1194 : 3189 : Oid indexrelid = PG_GETARG_OID(0);
1195 : : int prettyFlags;
1196 : : char *res;
1197 : :
1198 : 3189 : prettyFlags = PRETTYFLAG_INDENT;
1199 : :
1200 : 3189 : res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1201 : : false, false,
1202 : : false, false,
1203 : : prettyFlags, true);
1204 : :
1205 [ + + ]: 3189 : if (res == NULL)
1206 : 4 : PG_RETURN_NULL();
1207 : :
1208 : 3185 : PG_RETURN_TEXT_P(string_to_text(res));
1209 : : }
1210 : :
1211 : : Datum
1212 : 1355 : pg_get_indexdef_ext(PG_FUNCTION_ARGS)
1213 : : {
1214 : 1355 : Oid indexrelid = PG_GETARG_OID(0);
1215 : 1355 : int32 colno = PG_GETARG_INT32(1);
1216 : 1355 : bool pretty = PG_GETARG_BOOL(2);
1217 : : int prettyFlags;
1218 : : char *res;
1219 : :
1220 [ + - ]: 1355 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1221 : :
1222 : 1355 : res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1223 : : colno != 0, false,
1224 : : false, false,
1225 : : prettyFlags, true);
1226 : :
1227 [ - + ]: 1355 : if (res == NULL)
1228 : 0 : PG_RETURN_NULL();
1229 : :
1230 : 1355 : PG_RETURN_TEXT_P(string_to_text(res));
1231 : : }
1232 : :
1233 : : /*
1234 : : * Internal version for use by ALTER TABLE.
1235 : : * Includes a tablespace clause in the result.
1236 : : * Returns a palloc'd C string; no pretty-printing.
1237 : : */
1238 : : char *
1239 : 179 : pg_get_indexdef_string(Oid indexrelid)
1240 : : {
1241 : 179 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1242 : : false, false,
1243 : : true, true,
1244 : : 0, false);
1245 : : }
1246 : :
1247 : : /* Internal version that just reports the key-column definitions */
1248 : : char *
1249 : 787 : pg_get_indexdef_columns(Oid indexrelid, bool pretty)
1250 : : {
1251 : : int prettyFlags;
1252 : :
1253 [ + - ]: 787 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1254 : :
1255 : 787 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1256 : : true, true,
1257 : : false, false,
1258 : : prettyFlags, false);
1259 : : }
1260 : :
1261 : : /* Internal version, extensible with flags to control its behavior */
1262 : : char *
1263 : 4 : pg_get_indexdef_columns_extended(Oid indexrelid, uint16 flags)
1264 : : {
1265 : 4 : bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1266 : 4 : bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1267 : : int prettyFlags;
1268 : :
1269 [ + - ]: 4 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1270 : :
1271 : 4 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1272 : : true, keys_only,
1273 : : false, false,
1274 : : prettyFlags, false);
1275 : : }
1276 : :
1277 : : /*
1278 : : * Internal workhorse to decompile an index definition.
1279 : : *
1280 : : * This is now used for exclusion constraints as well: if excludeOps is not
1281 : : * NULL then it points to an array of exclusion operator OIDs.
1282 : : */
1283 : : static char *
1284 : 5590 : pg_get_indexdef_worker(Oid indexrelid, int colno,
1285 : : const Oid *excludeOps,
1286 : : bool attrsOnly, bool keysOnly,
1287 : : bool showTblSpc, bool inherits,
1288 : : int prettyFlags, bool missing_ok)
1289 : : {
1290 : : /* might want a separate isConstraint parameter later */
1291 : 5590 : bool isConstraint = (excludeOps != NULL);
1292 : : HeapTuple ht_idx;
1293 : : HeapTuple ht_idxrel;
1294 : : HeapTuple ht_am;
1295 : : Form_pg_index idxrec;
1296 : : Form_pg_class idxrelrec;
1297 : : Form_pg_am amrec;
1298 : : const IndexAmRoutine *amroutine;
1299 : : List *indexprs;
1300 : : ListCell *indexpr_item;
1301 : : List *context;
1302 : : Oid indrelid;
1303 : : int keyno;
1304 : : Datum indcollDatum;
1305 : : Datum indclassDatum;
1306 : : Datum indoptionDatum;
1307 : : oidvector *indcollation;
1308 : : oidvector *indclass;
1309 : : int2vector *indoption;
1310 : : StringInfoData buf;
1311 : : char *str;
1312 : : char *sep;
1313 : :
1314 : : /*
1315 : : * Fetch the pg_index tuple by the Oid of the index
1316 : : */
1317 : 5590 : ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
1318 [ + + ]: 5590 : if (!HeapTupleIsValid(ht_idx))
1319 : : {
1320 [ + - ]: 4 : if (missing_ok)
1321 : 4 : return NULL;
1322 [ # # ]: 0 : elog(ERROR, "cache lookup failed for index %u", indexrelid);
1323 : : }
1324 : 5586 : idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
1325 : :
1326 : 5586 : indrelid = idxrec->indrelid;
1327 : : Assert(indexrelid == idxrec->indexrelid);
1328 : :
1329 : : /* Must get indcollation, indclass, and indoption the hard way */
1330 : 5586 : indcollDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1331 : : Anum_pg_index_indcollation);
1332 : 5586 : indcollation = (oidvector *) DatumGetPointer(indcollDatum);
1333 : :
1334 : 5586 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1335 : : Anum_pg_index_indclass);
1336 : 5586 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
1337 : :
1338 : 5586 : indoptionDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1339 : : Anum_pg_index_indoption);
1340 : 5586 : indoption = (int2vector *) DatumGetPointer(indoptionDatum);
1341 : :
1342 : : /*
1343 : : * Fetch the pg_class tuple of the index relation
1344 : : */
1345 : 5586 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
1346 [ - + ]: 5586 : if (!HeapTupleIsValid(ht_idxrel))
1347 [ # # ]: 0 : elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1348 : 5586 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
1349 : :
1350 : : /*
1351 : : * Fetch the pg_am tuple of the index' access method
1352 : : */
1353 : 5586 : ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
1354 [ - + ]: 5586 : if (!HeapTupleIsValid(ht_am))
1355 [ # # ]: 0 : elog(ERROR, "cache lookup failed for access method %u",
1356 : : idxrelrec->relam);
1357 : 5586 : amrec = (Form_pg_am) GETSTRUCT(ht_am);
1358 : :
1359 : : /* Fetch the index AM's API struct */
1360 : 5586 : amroutine = GetIndexAmRoutine(amrec->amhandler);
1361 : :
1362 : : /*
1363 : : * Get the index expressions, if any. (NOTE: we do not use the relcache
1364 : : * versions of the expressions and predicate, because we want to display
1365 : : * non-const-folded expressions.)
1366 : : */
1367 [ + + ]: 5586 : if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs, NULL))
1368 : : {
1369 : : Datum exprsDatum;
1370 : : char *exprsString;
1371 : :
1372 : 426 : exprsDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1373 : : Anum_pg_index_indexprs);
1374 : 426 : exprsString = TextDatumGetCString(exprsDatum);
1375 : 426 : indexprs = (List *) stringToNode(exprsString);
1376 : 426 : pfree(exprsString);
1377 : : }
1378 : : else
1379 : 5160 : indexprs = NIL;
1380 : :
1381 : 5586 : indexpr_item = list_head(indexprs);
1382 : :
1383 : 5586 : context = deparse_context_for(get_relation_name(indrelid), indrelid);
1384 : :
1385 : : /*
1386 : : * Start the index definition. Note that the index's name should never be
1387 : : * schema-qualified, but the indexed rel's name may be.
1388 : : */
1389 : 5586 : initStringInfo(&buf);
1390 : :
1391 [ + + ]: 5586 : if (!attrsOnly)
1392 : : {
1393 [ + + ]: 4485 : if (!isConstraint)
1394 : 8818 : appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
1395 [ + + ]: 4409 : idxrec->indisunique ? "UNIQUE " : "",
1396 : 4409 : quote_identifier(NameStr(idxrelrec->relname)),
1397 [ + + ]: 4409 : idxrelrec->relkind == RELKIND_PARTITIONED_INDEX
1398 [ + + ]: 414 : && !inherits ? "ONLY " : "",
1399 [ + + ]: 4409 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
1400 : 1045 : generate_relation_name(indrelid, NIL) :
1401 : 3364 : generate_qualified_relation_name(indrelid),
1402 : 4409 : quote_identifier(NameStr(amrec->amname)));
1403 : : else /* currently, must be EXCLUDE constraint */
1404 : 76 : appendStringInfo(&buf, "EXCLUDE USING %s (",
1405 : 76 : quote_identifier(NameStr(amrec->amname)));
1406 : : }
1407 : :
1408 : : /*
1409 : : * Report the indexed attributes
1410 : : */
1411 : 5586 : sep = "";
1412 [ + + ]: 13856 : for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1413 : : {
1414 : 8335 : AttrNumber attnum = idxrec->indkey.values[keyno];
1415 : : Oid keycoltype;
1416 : : Oid keycolcollation;
1417 : :
1418 : : /*
1419 : : * Ignore non-key attributes if told to.
1420 : : */
1421 [ + + + + ]: 8335 : if (keysOnly && keyno >= idxrec->indnkeyatts)
1422 : 65 : break;
1423 : :
1424 : : /* Otherwise, print INCLUDE to divide key and non-key attrs. */
1425 [ + + + + ]: 8270 : if (!colno && keyno == idxrec->indnkeyatts)
1426 : : {
1427 : 148 : appendStringInfoString(&buf, ") INCLUDE (");
1428 : 148 : sep = "";
1429 : : }
1430 : :
1431 [ + + ]: 8270 : if (!colno)
1432 : 7844 : appendStringInfoString(&buf, sep);
1433 : 8270 : sep = ", ";
1434 : :
1435 [ + + ]: 8270 : if (attnum != 0)
1436 : : {
1437 : : /* Simple index column */
1438 : : char *attname;
1439 : : int32 keycoltypmod;
1440 : :
1441 : 7761 : attname = get_attname(indrelid, attnum, false);
1442 [ + + + + ]: 7761 : if (!colno || colno == keyno + 1)
1443 : 7653 : appendStringInfoString(&buf, quote_identifier(attname));
1444 : 7761 : get_atttypetypmodcoll(indrelid, attnum,
1445 : : &keycoltype, &keycoltypmod,
1446 : : &keycolcollation);
1447 : : }
1448 : : else
1449 : : {
1450 : : /* expressional index */
1451 : : Node *indexkey;
1452 : :
1453 [ - + ]: 509 : if (indexpr_item == NULL)
1454 [ # # ]: 0 : elog(ERROR, "too few entries in indexprs list");
1455 : 509 : indexkey = (Node *) lfirst(indexpr_item);
1456 : 509 : indexpr_item = lnext(indexprs, indexpr_item);
1457 : : /* Deparse */
1458 : 509 : str = deparse_expression_pretty(indexkey, context, false, false,
1459 : : prettyFlags, 0);
1460 [ + + + + ]: 509 : if (!colno || colno == keyno + 1)
1461 : : {
1462 : : /* Need parens if it's not a bare function call */
1463 [ + + ]: 501 : if (looks_like_function(indexkey))
1464 : 33 : appendStringInfoString(&buf, str);
1465 : : else
1466 : 468 : appendStringInfo(&buf, "(%s)", str);
1467 : : }
1468 : 509 : keycoltype = exprType(indexkey);
1469 : 509 : keycolcollation = exprCollation(indexkey);
1470 : : }
1471 : :
1472 : : /* Print additional decoration for (selected) key columns */
1473 [ + + + + : 8270 : if (!attrsOnly && keyno < idxrec->indnkeyatts &&
- + ]
1474 [ # # ]: 0 : (!colno || colno == keyno + 1))
1475 : : {
1476 : 6479 : int16 opt = indoption->values[keyno];
1477 : 6479 : Oid indcoll = indcollation->values[keyno];
1478 : 6479 : Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1479 : 6479 : bool has_options = attoptions != (Datum) 0;
1480 : :
1481 : : /* Add collation, if not default for column */
1482 [ + + + + ]: 6479 : if (OidIsValid(indcoll) && indcoll != keycolcollation)
1483 : 62 : appendStringInfo(&buf, " COLLATE %s",
1484 : : generate_collation_name((indcoll)));
1485 : :
1486 : : /* Add the operator class name, if not default */
1487 [ + + ]: 6479 : get_opclass_name(indclass->values[keyno],
1488 : : has_options ? InvalidOid : keycoltype, &buf);
1489 : :
1490 [ + + ]: 6479 : if (has_options)
1491 : : {
1492 : 22 : appendStringInfoString(&buf, " (");
1493 : 22 : get_reloptions(&buf, attoptions);
1494 : 22 : appendStringInfoChar(&buf, ')');
1495 : : }
1496 : :
1497 : : /* Add options if relevant */
1498 [ + + ]: 6479 : if (amroutine->amcanorder)
1499 : : {
1500 : : /* if it supports sort ordering, report DESC and NULLS opts */
1501 [ - + ]: 5293 : if (opt & INDOPTION_DESC)
1502 : : {
1503 : 0 : appendStringInfoString(&buf, " DESC");
1504 : : /* NULLS FIRST is the default in this case */
1505 [ # # ]: 0 : if (!(opt & INDOPTION_NULLS_FIRST))
1506 : 0 : appendStringInfoString(&buf, " NULLS LAST");
1507 : : }
1508 : : else
1509 : : {
1510 [ - + ]: 5293 : if (opt & INDOPTION_NULLS_FIRST)
1511 : 0 : appendStringInfoString(&buf, " NULLS FIRST");
1512 : : }
1513 : : }
1514 : :
1515 : : /* Add the exclusion operator if relevant */
1516 [ + + ]: 6479 : if (excludeOps != NULL)
1517 : 96 : appendStringInfo(&buf, " WITH %s",
1518 : 96 : generate_operator_name(excludeOps[keyno],
1519 : : keycoltype,
1520 : : keycoltype));
1521 : : }
1522 : : }
1523 : :
1524 [ + + ]: 5586 : if (!attrsOnly)
1525 : : {
1526 : 4485 : appendStringInfoChar(&buf, ')');
1527 : :
1528 [ + + ]: 4485 : if (idxrec->indnullsnotdistinct)
1529 : 8 : appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1530 : :
1531 : : /*
1532 : : * If it has options, append "WITH (options)"
1533 : : */
1534 : 4485 : str = flatten_reloptions(indexrelid);
1535 [ + + ]: 4485 : if (str)
1536 : : {
1537 : 105 : appendStringInfo(&buf, " WITH (%s)", str);
1538 : 105 : pfree(str);
1539 : : }
1540 : :
1541 : : /*
1542 : : * Print tablespace, but only if requested
1543 : : */
1544 [ + + ]: 4485 : if (showTblSpc)
1545 : : {
1546 : : Oid tblspc;
1547 : :
1548 : 179 : tblspc = get_rel_tablespace(indexrelid);
1549 [ + + ]: 179 : if (OidIsValid(tblspc))
1550 : : {
1551 [ - + ]: 36 : if (isConstraint)
1552 : 0 : appendStringInfoString(&buf, " USING INDEX");
1553 : 36 : appendStringInfo(&buf, " TABLESPACE %s",
1554 : 36 : quote_identifier(get_tablespace_name(tblspc)));
1555 : : }
1556 : : }
1557 : :
1558 : : /*
1559 : : * If it's a partial index, decompile and append the predicate
1560 : : */
1561 [ + + ]: 4485 : if (!heap_attisnull(ht_idx, Anum_pg_index_indpred, NULL))
1562 : : {
1563 : : Node *node;
1564 : : Datum predDatum;
1565 : : char *predString;
1566 : :
1567 : : /* Convert text string to node tree */
1568 : 230 : predDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1569 : : Anum_pg_index_indpred);
1570 : 230 : predString = TextDatumGetCString(predDatum);
1571 : 230 : node = (Node *) stringToNode(predString);
1572 : 230 : pfree(predString);
1573 : :
1574 : : /* Deparse */
1575 : 230 : str = deparse_expression_pretty(node, context, false, false,
1576 : : prettyFlags, 0);
1577 [ + + ]: 230 : if (isConstraint)
1578 : 28 : appendStringInfo(&buf, " WHERE (%s)", str);
1579 : : else
1580 : 202 : appendStringInfo(&buf, " WHERE %s", str);
1581 : : }
1582 : : }
1583 : :
1584 : : /* Clean up */
1585 : 5586 : ReleaseSysCache(ht_idx);
1586 : 5586 : ReleaseSysCache(ht_idxrel);
1587 : 5586 : ReleaseSysCache(ht_am);
1588 : :
1589 : 5586 : return buf.data;
1590 : : }
1591 : :
1592 : : /* ----------
1593 : : * pg_get_querydef
1594 : : *
1595 : : * Public entry point to deparse one query parsetree.
1596 : : * The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
1597 : : *
1598 : : * The result is a palloc'd C string.
1599 : : * ----------
1600 : : */
1601 : : char *
1602 : 0 : pg_get_querydef(Query *query, bool pretty)
1603 : : {
1604 : : StringInfoData buf;
1605 : : int prettyFlags;
1606 : :
1607 [ # # ]: 0 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1608 : :
1609 : 0 : initStringInfo(&buf);
1610 : :
1611 : 0 : get_query_def(query, &buf, NIL, NULL, true,
1612 : : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1613 : :
1614 : 0 : return buf.data;
1615 : : }
1616 : :
1617 : : /*
1618 : : * pg_get_propgraphdef - get the definition of a property graph
1619 : : */
1620 : : Datum
1621 : 132 : pg_get_propgraphdef(PG_FUNCTION_ARGS)
1622 : : {
1623 : 132 : Oid pgrelid = PG_GETARG_OID(0);
1624 : : StringInfoData buf;
1625 : : HeapTuple classtup;
1626 : : Form_pg_class classform;
1627 : : char *name;
1628 : : char *nsp;
1629 : :
1630 : 132 : initStringInfo(&buf);
1631 : :
1632 : 132 : classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(pgrelid));
1633 [ - + ]: 132 : if (!HeapTupleIsValid(classtup))
1634 : 0 : PG_RETURN_NULL();
1635 : :
1636 : 132 : classform = (Form_pg_class) GETSTRUCT(classtup);
1637 : 132 : name = NameStr(classform->relname);
1638 : :
1639 [ + + ]: 132 : if (classform->relkind != RELKIND_PROPGRAPH)
1640 [ + - ]: 4 : ereport(ERROR,
1641 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1642 : : errmsg("\"%s\" is not a property graph", name)));
1643 : :
1644 : 128 : nsp = get_namespace_name(classform->relnamespace);
1645 : :
1646 : 128 : appendStringInfo(&buf, "CREATE PROPERTY GRAPH %s",
1647 : : quote_qualified_identifier(nsp, name));
1648 : :
1649 : 128 : ReleaseSysCache(classtup);
1650 : :
1651 : 128 : make_propgraphdef_elements(&buf, pgrelid, PGEKIND_VERTEX);
1652 : 128 : make_propgraphdef_elements(&buf, pgrelid, PGEKIND_EDGE);
1653 : :
1654 : 128 : PG_RETURN_TEXT_P(string_to_text(buf.data));
1655 : : }
1656 : :
1657 : : /*
1658 : : * Generates a VERTEX TABLES (...) or EDGE TABLES (...) clause. Pass in the
1659 : : * property graph relation OID and the element kind (vertex or edge). Result
1660 : : * is appended to buf.
1661 : : */
1662 : : static void
1663 : 256 : make_propgraphdef_elements(StringInfo buf, Oid pgrelid, char pgekind)
1664 : : {
1665 : : Relation pgerel;
1666 : : ScanKeyData scankey[1];
1667 : : SysScanDesc scan;
1668 : : bool first;
1669 : : HeapTuple tup;
1670 : :
1671 : 256 : pgerel = table_open(PropgraphElementRelationId, AccessShareLock);
1672 : :
1673 : 256 : ScanKeyInit(&scankey[0],
1674 : : Anum_pg_propgraph_element_pgepgid,
1675 : : BTEqualStrategyNumber, F_OIDEQ,
1676 : : ObjectIdGetDatum(pgrelid));
1677 : :
1678 : 256 : scan = systable_beginscan(pgerel, PropgraphElementAliasIndexId, true, NULL, 1, scankey);
1679 : :
1680 : 256 : first = true;
1681 [ + + ]: 1016 : while ((tup = systable_getnext(scan)))
1682 : : {
1683 : 760 : Form_pg_propgraph_element pgeform = (Form_pg_propgraph_element) GETSTRUCT(tup);
1684 : : char *relname;
1685 : : Datum datum;
1686 : : bool isnull;
1687 : :
1688 [ + + ]: 760 : if (pgeform->pgekind != pgekind)
1689 : 380 : continue;
1690 : :
1691 [ + + ]: 380 : if (first)
1692 : : {
1693 [ + + ]: 166 : appendStringInfo(buf, "\n %s TABLES (\n", pgekind == PGEKIND_VERTEX ? "VERTEX" : "EDGE");
1694 : 166 : first = false;
1695 : : }
1696 : : else
1697 : 214 : appendStringInfoString(buf, ",\n");
1698 : :
1699 : 380 : relname = get_rel_name(pgeform->pgerelid);
1700 [ + - + - ]: 380 : if (relname && strcmp(relname, NameStr(pgeform->pgealias)) == 0)
1701 : 380 : appendStringInfo(buf, " %s",
1702 : : generate_relation_name(pgeform->pgerelid, NIL));
1703 : : else
1704 : 0 : appendStringInfo(buf, " %s AS %s",
1705 : : generate_relation_name(pgeform->pgerelid, NIL),
1706 : 0 : quote_identifier(NameStr(pgeform->pgealias)));
1707 : :
1708 : 380 : datum = heap_getattr(tup, Anum_pg_propgraph_element_pgekey, RelationGetDescr(pgerel), &isnull);
1709 [ + - ]: 380 : if (!isnull)
1710 : : {
1711 : 380 : appendStringInfoString(buf, " KEY (");
1712 : 380 : decompile_column_index_array(datum, pgeform->pgerelid, false, buf);
1713 : 380 : appendStringInfoChar(buf, ')');
1714 : : }
1715 : : else
1716 [ # # ]: 0 : elog(ERROR, "null pgekey for element %u", pgeform->oid);
1717 : :
1718 [ + + ]: 380 : if (pgekind == PGEKIND_EDGE)
1719 : : {
1720 : : Datum srckey;
1721 : : Datum srcref;
1722 : : Datum destkey;
1723 : : Datum destref;
1724 : : HeapTuple tup2;
1725 : : Form_pg_propgraph_element pgeform2;
1726 : :
1727 : 163 : datum = heap_getattr(tup, Anum_pg_propgraph_element_pgesrckey, RelationGetDescr(pgerel), &isnull);
1728 [ - + ]: 163 : srckey = isnull ? 0 : datum;
1729 : 163 : datum = heap_getattr(tup, Anum_pg_propgraph_element_pgesrcref, RelationGetDescr(pgerel), &isnull);
1730 [ - + ]: 163 : srcref = isnull ? 0 : datum;
1731 : 163 : datum = heap_getattr(tup, Anum_pg_propgraph_element_pgedestkey, RelationGetDescr(pgerel), &isnull);
1732 [ - + ]: 163 : destkey = isnull ? 0 : datum;
1733 : 163 : datum = heap_getattr(tup, Anum_pg_propgraph_element_pgedestref, RelationGetDescr(pgerel), &isnull);
1734 [ - + ]: 163 : destref = isnull ? 0 : datum;
1735 : :
1736 : 163 : appendStringInfoString(buf, " SOURCE");
1737 : 163 : tup2 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(pgeform->pgesrcvertexid));
1738 [ - + ]: 163 : if (!tup2)
1739 [ # # ]: 0 : elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgesrcvertexid);
1740 : 163 : pgeform2 = (Form_pg_propgraph_element) GETSTRUCT(tup2);
1741 [ + - ]: 163 : if (srckey)
1742 : : {
1743 : 163 : appendStringInfoString(buf, " KEY (");
1744 : 163 : decompile_column_index_array(srckey, pgeform->pgerelid, false, buf);
1745 : 163 : appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1746 : 163 : decompile_column_index_array(srcref, pgeform2->pgerelid, false, buf);
1747 : 163 : appendStringInfoChar(buf, ')');
1748 : : }
1749 : : else
1750 : 0 : appendStringInfo(buf, " %s ", quote_identifier(NameStr(pgeform2->pgealias)));
1751 : 163 : ReleaseSysCache(tup2);
1752 : :
1753 : 163 : appendStringInfoString(buf, " DESTINATION");
1754 : 163 : tup2 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(pgeform->pgedestvertexid));
1755 [ - + ]: 163 : if (!tup2)
1756 [ # # ]: 0 : elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgedestvertexid);
1757 : 163 : pgeform2 = (Form_pg_propgraph_element) GETSTRUCT(tup2);
1758 [ + - ]: 163 : if (destkey)
1759 : : {
1760 : 163 : appendStringInfoString(buf, " KEY (");
1761 : 163 : decompile_column_index_array(destkey, pgeform->pgerelid, false, buf);
1762 : 163 : appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1763 : 163 : decompile_column_index_array(destref, pgeform2->pgerelid, false, buf);
1764 : 163 : appendStringInfoChar(buf, ')');
1765 : : }
1766 : : else
1767 : 0 : appendStringInfo(buf, " %s", quote_identifier(NameStr(pgeform2->pgealias)));
1768 : 163 : ReleaseSysCache(tup2);
1769 : : }
1770 : :
1771 : 380 : make_propgraphdef_labels(buf, pgeform->oid, NameStr(pgeform->pgealias), pgeform->pgerelid);
1772 : : }
1773 [ + + ]: 256 : if (!first)
1774 : 166 : appendStringInfoString(buf, "\n )");
1775 : :
1776 : 256 : systable_endscan(scan);
1777 : 256 : table_close(pgerel, AccessShareLock);
1778 : 256 : }
1779 : :
1780 : : struct oid_str_pair
1781 : : {
1782 : : Oid oid;
1783 : : char *str;
1784 : : };
1785 : :
1786 : : static int
1787 : 97 : list_oid_str_pair_cmp_by_str(const ListCell *p1, const ListCell *p2)
1788 : : {
1789 : 97 : struct oid_str_pair *v1 = lfirst(p1);
1790 : 97 : struct oid_str_pair *v2 = lfirst(p2);
1791 : :
1792 : 97 : return strcmp(v1->str, v2->str);
1793 : : }
1794 : :
1795 : : /*
1796 : : * Generates label and properties list. Pass in the element OID, the element
1797 : : * alias, and the graph relation OID. Result is appended to buf.
1798 : : */
1799 : : static void
1800 : 380 : make_propgraphdef_labels(StringInfo buf, Oid elid, const char *elalias, Oid elrelid)
1801 : : {
1802 : : Relation pglrel;
1803 : : ScanKeyData scankey[1];
1804 : : SysScanDesc scan;
1805 : : int count;
1806 : : HeapTuple tup;
1807 : 380 : List *label_list = NIL;
1808 : :
1809 : 380 : pglrel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1810 : :
1811 : 380 : ScanKeyInit(&scankey[0],
1812 : : Anum_pg_propgraph_element_label_pgelelid,
1813 : : BTEqualStrategyNumber, F_OIDEQ,
1814 : : ObjectIdGetDatum(elid));
1815 : :
1816 : : /*
1817 : : * We want to output the labels in a deterministic order. So we first
1818 : : * read all the data, then sort, then print it.
1819 : : */
1820 : 380 : scan = systable_beginscan(pglrel, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, scankey);
1821 : :
1822 [ + + ]: 857 : while ((tup = systable_getnext(scan)))
1823 : : {
1824 : 477 : Form_pg_propgraph_element_label pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
1825 : : struct oid_str_pair *osp;
1826 : :
1827 : 477 : osp = palloc_object(struct oid_str_pair);
1828 : 477 : osp->oid = pgelform->oid;
1829 : 477 : osp->str = get_propgraph_label_name(pgelform->pgellabelid);
1830 : :
1831 : 477 : label_list = lappend(label_list, osp);
1832 : : }
1833 : :
1834 : 380 : systable_endscan(scan);
1835 : 380 : table_close(pglrel, AccessShareLock);
1836 : :
1837 : 380 : count = list_length(label_list);
1838 : :
1839 : : /* Each element has at least one label. */
1840 : : Assert(count > 0);
1841 : :
1842 : : /*
1843 : : * It is enough for the comparison function to compare just labels, since
1844 : : * all the labels of an element table should have distinct names.
1845 : : */
1846 : 380 : list_sort(label_list, list_oid_str_pair_cmp_by_str);
1847 : :
1848 [ + - + + : 1237 : foreach_ptr(struct oid_str_pair, osp, label_list)
+ + ]
1849 : : {
1850 [ + + ]: 477 : if (strcmp(osp->str, elalias) == 0)
1851 : : {
1852 : : /* If the default label is the only label, don't print anything. */
1853 [ + + ]: 259 : if (count != 1)
1854 : 30 : appendStringInfoString(buf, " DEFAULT LABEL");
1855 : : }
1856 : : else
1857 : 218 : appendStringInfo(buf, " LABEL %s", quote_identifier(osp->str));
1858 : :
1859 : 477 : make_propgraphdef_properties(buf, osp->oid, elrelid);
1860 : : }
1861 : 380 : }
1862 : :
1863 : : /*
1864 : : * Helper function for make_propgraphdef_properties(): Sort (propname, expr)
1865 : : * pairs by name.
1866 : : */
1867 : : static int
1868 : 773 : propdata_by_name_cmp(const ListCell *a, const ListCell *b)
1869 : : {
1870 : 773 : List *la = lfirst_node(List, a);
1871 : 773 : List *lb = lfirst_node(List, b);
1872 : 773 : char *pna = strVal(linitial(la));
1873 : 773 : char *pnb = strVal(linitial(lb));
1874 : :
1875 : 773 : return strcmp(pna, pnb);
1876 : : }
1877 : :
1878 : : /*
1879 : : * Generates element table properties clause (PROPERTIES (...) or NO
1880 : : * PROPERTIES). Pass in label OID and element table OID. Result is appended
1881 : : * to buf.
1882 : : */
1883 : : static void
1884 : 477 : make_propgraphdef_properties(StringInfo buf, Oid ellabelid, Oid elrelid)
1885 : : {
1886 : : Relation plprel;
1887 : : ScanKeyData scankey[1];
1888 : : SysScanDesc scan;
1889 : : HeapTuple tup;
1890 : 477 : List *outlist = NIL;
1891 : :
1892 : 477 : plprel = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
1893 : :
1894 : 477 : ScanKeyInit(&scankey[0],
1895 : : Anum_pg_propgraph_label_property_plpellabelid,
1896 : : BTEqualStrategyNumber, F_OIDEQ,
1897 : : ObjectIdGetDatum(ellabelid));
1898 : :
1899 : : /*
1900 : : * We want to output the properties in a deterministic order. So we first
1901 : : * read all the data, then sort, then print it.
1902 : : */
1903 : 477 : scan = systable_beginscan(plprel, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, scankey);
1904 : :
1905 [ + + ]: 1570 : while ((tup = systable_getnext(scan)))
1906 : : {
1907 : 1093 : Form_pg_propgraph_label_property plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
1908 : : Datum exprDatum;
1909 : : bool isnull;
1910 : : char *tmp;
1911 : : Node *expr;
1912 : : char *propname;
1913 : :
1914 : 1093 : exprDatum = heap_getattr(tup, Anum_pg_propgraph_label_property_plpexpr, RelationGetDescr(plprel), &isnull);
1915 : : Assert(!isnull);
1916 : 1093 : tmp = TextDatumGetCString(exprDatum);
1917 : 1093 : expr = stringToNode(tmp);
1918 : 1093 : pfree(tmp);
1919 : :
1920 : 1093 : propname = get_propgraph_property_name(plpform->plppropid);
1921 : :
1922 : 1093 : outlist = lappend(outlist, list_make2(makeString(propname), expr));
1923 : : }
1924 : :
1925 : 477 : systable_endscan(scan);
1926 : 477 : table_close(plprel, AccessShareLock);
1927 : :
1928 : 477 : list_sort(outlist, propdata_by_name_cmp);
1929 : :
1930 [ + + ]: 477 : if (outlist)
1931 : : {
1932 : : List *context;
1933 : : ListCell *lc;
1934 : 468 : bool first = true;
1935 : :
1936 : 468 : context = deparse_context_for(get_relation_name(elrelid), elrelid);
1937 : :
1938 : 468 : appendStringInfoString(buf, " PROPERTIES (");
1939 : :
1940 [ + - + + : 1561 : foreach(lc, outlist)
+ + ]
1941 : : {
1942 : 1093 : List *data = lfirst_node(List, lc);
1943 : 1093 : char *propname = strVal(linitial(data));
1944 : 1093 : Node *expr = lsecond(data);
1945 : :
1946 [ + + ]: 1093 : if (first)
1947 : 468 : first = false;
1948 : : else
1949 : 625 : appendStringInfoString(buf, ", ");
1950 : :
1951 [ + + + + ]: 1093 : if (IsA(expr, Var) && strcmp(propname, get_attname(elrelid, castNode(Var, expr)->varattno, false)) == 0)
1952 : 881 : appendStringInfoString(buf, quote_identifier(propname));
1953 : : else
1954 : 212 : appendStringInfo(buf, "%s AS %s",
1955 : : deparse_expression_pretty(expr, context, false, false, 0, 0),
1956 : : quote_identifier(propname));
1957 : : }
1958 : :
1959 : 468 : appendStringInfoChar(buf, ')');
1960 : : }
1961 : : else
1962 : 9 : appendStringInfoString(buf, " NO PROPERTIES");
1963 : 477 : }
1964 : :
1965 : : /*
1966 : : * pg_get_statisticsobjdef
1967 : : * Get the definition of an extended statistics object
1968 : : */
1969 : : Datum
1970 : 163 : pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
1971 : : {
1972 : 163 : Oid statextid = PG_GETARG_OID(0);
1973 : : char *res;
1974 : :
1975 : 163 : res = pg_get_statisticsobj_worker(statextid, false, true);
1976 : :
1977 [ + + ]: 163 : if (res == NULL)
1978 : 4 : PG_RETURN_NULL();
1979 : :
1980 : 159 : PG_RETURN_TEXT_P(string_to_text(res));
1981 : : }
1982 : :
1983 : : /*
1984 : : * Internal version for use by ALTER TABLE.
1985 : : * Returns a palloc'd C string; no pretty-printing.
1986 : : */
1987 : : char *
1988 : 57 : pg_get_statisticsobjdef_string(Oid statextid)
1989 : : {
1990 : 57 : return pg_get_statisticsobj_worker(statextid, false, false);
1991 : : }
1992 : :
1993 : : /*
1994 : : * pg_get_statisticsobjdef_columns
1995 : : * Get columns and expressions for an extended statistics object
1996 : : */
1997 : : Datum
1998 : 284 : pg_get_statisticsobjdef_columns(PG_FUNCTION_ARGS)
1999 : : {
2000 : 284 : Oid statextid = PG_GETARG_OID(0);
2001 : : char *res;
2002 : :
2003 : 284 : res = pg_get_statisticsobj_worker(statextid, true, true);
2004 : :
2005 [ - + ]: 284 : if (res == NULL)
2006 : 0 : PG_RETURN_NULL();
2007 : :
2008 : 284 : PG_RETURN_TEXT_P(string_to_text(res));
2009 : : }
2010 : :
2011 : : /*
2012 : : * Internal workhorse to decompile an extended statistics object.
2013 : : */
2014 : : static char *
2015 : 504 : pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
2016 : : {
2017 : : Form_pg_statistic_ext statextrec;
2018 : : HeapTuple statexttup;
2019 : : StringInfoData buf;
2020 : : int colno;
2021 : : char *nsp;
2022 : : ArrayType *arr;
2023 : : char *enabled;
2024 : : Datum datum;
2025 : : bool ndistinct_enabled;
2026 : : bool dependencies_enabled;
2027 : : bool mcv_enabled;
2028 : : int i;
2029 : : List *context;
2030 : : ListCell *lc;
2031 : 504 : List *exprs = NIL;
2032 : : bool has_exprs;
2033 : : int ncolumns;
2034 : :
2035 : 504 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
2036 : :
2037 [ + + ]: 504 : if (!HeapTupleIsValid(statexttup))
2038 : : {
2039 [ + - ]: 4 : if (missing_ok)
2040 : 4 : return NULL;
2041 [ # # ]: 0 : elog(ERROR, "cache lookup failed for statistics object %u", statextid);
2042 : : }
2043 : :
2044 : : /* has the statistics expressions? */
2045 : 500 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
2046 : :
2047 : 500 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
2048 : :
2049 : : /*
2050 : : * Get the statistics expressions, if any. (NOTE: we do not use the
2051 : : * relcache versions of the expressions, because we want to display
2052 : : * non-const-folded expressions.)
2053 : : */
2054 [ + + ]: 500 : if (has_exprs)
2055 : : {
2056 : : Datum exprsDatum;
2057 : : char *exprsString;
2058 : :
2059 : 129 : exprsDatum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
2060 : : Anum_pg_statistic_ext_stxexprs);
2061 : 129 : exprsString = TextDatumGetCString(exprsDatum);
2062 : 129 : exprs = (List *) stringToNode(exprsString);
2063 : 129 : pfree(exprsString);
2064 : : }
2065 : : else
2066 : 371 : exprs = NIL;
2067 : :
2068 : : /* count the number of columns (attributes and expressions) */
2069 : 500 : ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
2070 : :
2071 : 500 : initStringInfo(&buf);
2072 : :
2073 [ + + ]: 500 : if (!columns_only)
2074 : : {
2075 : 216 : nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
2076 : 216 : appendStringInfo(&buf, "CREATE STATISTICS %s",
2077 : : quote_qualified_identifier(nsp,
2078 : 216 : NameStr(statextrec->stxname)));
2079 : :
2080 : : /*
2081 : : * Decode the stxkind column so that we know which stats types to
2082 : : * print.
2083 : : */
2084 : 216 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
2085 : : Anum_pg_statistic_ext_stxkind);
2086 : 216 : arr = DatumGetArrayTypeP(datum);
2087 [ + - ]: 216 : if (ARR_NDIM(arr) != 1 ||
2088 [ + - ]: 216 : ARR_HASNULL(arr) ||
2089 [ - + ]: 216 : ARR_ELEMTYPE(arr) != CHAROID)
2090 [ # # ]: 0 : elog(ERROR, "stxkind is not a 1-D char array");
2091 [ - + ]: 216 : enabled = (char *) ARR_DATA_PTR(arr);
2092 : :
2093 : 216 : ndistinct_enabled = false;
2094 : 216 : dependencies_enabled = false;
2095 : 216 : mcv_enabled = false;
2096 : :
2097 [ + + ]: 685 : for (i = 0; i < ARR_DIMS(arr)[0]; i++)
2098 : : {
2099 [ + + ]: 469 : if (enabled[i] == STATS_EXT_NDISTINCT)
2100 : 141 : ndistinct_enabled = true;
2101 [ + + ]: 328 : else if (enabled[i] == STATS_EXT_DEPENDENCIES)
2102 : 117 : dependencies_enabled = true;
2103 [ + + ]: 211 : else if (enabled[i] == STATS_EXT_MCV)
2104 : 126 : mcv_enabled = true;
2105 : :
2106 : : /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
2107 : : }
2108 : :
2109 : : /*
2110 : : * If any option is disabled, then we'll need to append the types
2111 : : * clause to show which options are enabled. We omit the types clause
2112 : : * on purpose when all options are enabled, so a pg_dump/pg_restore
2113 : : * will create all statistics types on a newer postgres version, if
2114 : : * the statistics had all options enabled on the original version.
2115 : : *
2116 : : * But if the statistics is defined on just a single column, it has to
2117 : : * be an expression statistics. In that case we don't need to specify
2118 : : * kinds.
2119 : : */
2120 [ + + + + : 216 : if ((!ndistinct_enabled || !dependencies_enabled || !mcv_enabled) &&
- + + + ]
2121 : : (ncolumns > 1))
2122 : : {
2123 : 63 : bool gotone = false;
2124 : :
2125 : 63 : appendStringInfoString(&buf, " (");
2126 : :
2127 [ + + ]: 63 : if (ndistinct_enabled)
2128 : : {
2129 : 34 : appendStringInfoString(&buf, "ndistinct");
2130 : 34 : gotone = true;
2131 : : }
2132 : :
2133 [ + + ]: 63 : if (dependencies_enabled)
2134 : : {
2135 [ - + ]: 10 : appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
2136 : 10 : gotone = true;
2137 : : }
2138 : :
2139 [ + + ]: 63 : if (mcv_enabled)
2140 [ - + ]: 19 : appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
2141 : :
2142 : 63 : appendStringInfoChar(&buf, ')');
2143 : : }
2144 : :
2145 : 216 : appendStringInfoString(&buf, " ON ");
2146 : : }
2147 : :
2148 : : /* decode simple column references */
2149 [ + + ]: 1423 : for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
2150 : : {
2151 : 923 : AttrNumber attnum = statextrec->stxkeys.values[colno];
2152 : : char *attname;
2153 : :
2154 [ + + ]: 923 : if (colno > 0)
2155 : 510 : appendStringInfoString(&buf, ", ");
2156 : :
2157 : 923 : attname = get_attname(statextrec->stxrelid, attnum, false);
2158 : :
2159 : 923 : appendStringInfoString(&buf, quote_identifier(attname));
2160 : : }
2161 : :
2162 : 500 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
2163 : : statextrec->stxrelid);
2164 : :
2165 [ + + + + : 683 : foreach(lc, exprs)
+ + ]
2166 : : {
2167 : 183 : Node *expr = (Node *) lfirst(lc);
2168 : : char *str;
2169 : 183 : int prettyFlags = PRETTYFLAG_PAREN;
2170 : :
2171 : 183 : str = deparse_expression_pretty(expr, context, false, false,
2172 : : prettyFlags, 0);
2173 : :
2174 [ + + ]: 183 : if (colno > 0)
2175 : 96 : appendStringInfoString(&buf, ", ");
2176 : :
2177 : : /* Need parens if it's not a bare function call */
2178 [ + + ]: 183 : if (looks_like_function(expr))
2179 : 21 : appendStringInfoString(&buf, str);
2180 : : else
2181 : 162 : appendStringInfo(&buf, "(%s)", str);
2182 : :
2183 : 183 : colno++;
2184 : : }
2185 : :
2186 [ + + ]: 500 : if (!columns_only)
2187 : 216 : appendStringInfo(&buf, " FROM %s",
2188 : : generate_relation_name(statextrec->stxrelid, NIL));
2189 : :
2190 : 500 : ReleaseSysCache(statexttup);
2191 : :
2192 : 500 : return buf.data;
2193 : : }
2194 : :
2195 : : /*
2196 : : * Generate text array of expressions for statistics object.
2197 : : */
2198 : : Datum
2199 : 128 : pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
2200 : : {
2201 : 128 : Oid statextid = PG_GETARG_OID(0);
2202 : : Form_pg_statistic_ext statextrec;
2203 : : HeapTuple statexttup;
2204 : : Datum datum;
2205 : : List *context;
2206 : : ListCell *lc;
2207 : 128 : List *exprs = NIL;
2208 : : bool has_exprs;
2209 : : char *tmp;
2210 : 128 : ArrayBuildState *astate = NULL;
2211 : :
2212 : 128 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
2213 : :
2214 [ - + ]: 128 : if (!HeapTupleIsValid(statexttup))
2215 : 0 : PG_RETURN_NULL();
2216 : :
2217 : : /* Does the stats object have expressions? */
2218 : 128 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
2219 : :
2220 : : /* no expressions? we're done */
2221 [ + + ]: 128 : if (!has_exprs)
2222 : : {
2223 : 11 : ReleaseSysCache(statexttup);
2224 : 11 : PG_RETURN_NULL();
2225 : : }
2226 : :
2227 : 117 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
2228 : :
2229 : : /*
2230 : : * Get the statistics expressions, and deparse them into text values.
2231 : : */
2232 : 117 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
2233 : : Anum_pg_statistic_ext_stxexprs);
2234 : 117 : tmp = TextDatumGetCString(datum);
2235 : 117 : exprs = (List *) stringToNode(tmp);
2236 : 117 : pfree(tmp);
2237 : :
2238 : 117 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
2239 : : statextrec->stxrelid);
2240 : :
2241 [ + - + + : 280 : foreach(lc, exprs)
+ + ]
2242 : : {
2243 : 163 : Node *expr = (Node *) lfirst(lc);
2244 : : char *str;
2245 : 163 : int prettyFlags = PRETTYFLAG_INDENT;
2246 : :
2247 : 163 : str = deparse_expression_pretty(expr, context, false, false,
2248 : : prettyFlags, 0);
2249 : :
2250 : 163 : astate = accumArrayResult(astate,
2251 : 163 : PointerGetDatum(cstring_to_text(str)),
2252 : : false,
2253 : : TEXTOID,
2254 : : CurrentMemoryContext);
2255 : : }
2256 : :
2257 : 117 : ReleaseSysCache(statexttup);
2258 : :
2259 : 117 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
2260 : : }
2261 : :
2262 : : /*
2263 : : * pg_get_partkeydef
2264 : : *
2265 : : * Returns the partition key specification, ie, the following:
2266 : : *
2267 : : * { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
2268 : : */
2269 : : Datum
2270 : 834 : pg_get_partkeydef(PG_FUNCTION_ARGS)
2271 : : {
2272 : 834 : Oid relid = PG_GETARG_OID(0);
2273 : : char *res;
2274 : :
2275 : 834 : res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
2276 : :
2277 [ + + ]: 834 : if (res == NULL)
2278 : 4 : PG_RETURN_NULL();
2279 : :
2280 : 830 : PG_RETURN_TEXT_P(string_to_text(res));
2281 : : }
2282 : :
2283 : : /* Internal version that just reports the column definitions */
2284 : : char *
2285 : 94 : pg_get_partkeydef_columns(Oid relid, bool pretty)
2286 : : {
2287 : : int prettyFlags;
2288 : :
2289 [ + - ]: 94 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2290 : :
2291 : 94 : return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
2292 : : }
2293 : :
2294 : : /*
2295 : : * Internal workhorse to decompile a partition key definition.
2296 : : */
2297 : : static char *
2298 : 928 : pg_get_partkeydef_worker(Oid relid, int prettyFlags,
2299 : : bool attrsOnly, bool missing_ok)
2300 : : {
2301 : : Form_pg_partitioned_table form;
2302 : : HeapTuple tuple;
2303 : : oidvector *partclass;
2304 : : oidvector *partcollation;
2305 : : List *partexprs;
2306 : : ListCell *partexpr_item;
2307 : : List *context;
2308 : : Datum datum;
2309 : : StringInfoData buf;
2310 : : int keyno;
2311 : : char *str;
2312 : : char *sep;
2313 : :
2314 : 928 : tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
2315 [ + + ]: 928 : if (!HeapTupleIsValid(tuple))
2316 : : {
2317 [ + - ]: 4 : if (missing_ok)
2318 : 4 : return NULL;
2319 [ # # ]: 0 : elog(ERROR, "cache lookup failed for partition key of %u", relid);
2320 : : }
2321 : :
2322 : 924 : form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
2323 : :
2324 : : Assert(form->partrelid == relid);
2325 : :
2326 : : /* Must get partclass and partcollation the hard way */
2327 : 924 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2328 : : Anum_pg_partitioned_table_partclass);
2329 : 924 : partclass = (oidvector *) DatumGetPointer(datum);
2330 : :
2331 : 924 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2332 : : Anum_pg_partitioned_table_partcollation);
2333 : 924 : partcollation = (oidvector *) DatumGetPointer(datum);
2334 : :
2335 : :
2336 : : /*
2337 : : * Get the expressions, if any. (NOTE: we do not use the relcache
2338 : : * versions of the expressions, because we want to display
2339 : : * non-const-folded expressions.)
2340 : : */
2341 [ + + ]: 924 : if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprs, NULL))
2342 : : {
2343 : : Datum exprsDatum;
2344 : : char *exprsString;
2345 : :
2346 : 84 : exprsDatum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2347 : : Anum_pg_partitioned_table_partexprs);
2348 : 84 : exprsString = TextDatumGetCString(exprsDatum);
2349 : 84 : partexprs = (List *) stringToNode(exprsString);
2350 : :
2351 [ - + ]: 84 : if (!IsA(partexprs, List))
2352 [ # # ]: 0 : elog(ERROR, "unexpected node type found in partexprs: %d",
2353 : : (int) nodeTag(partexprs));
2354 : :
2355 : 84 : pfree(exprsString);
2356 : : }
2357 : : else
2358 : 840 : partexprs = NIL;
2359 : :
2360 : 924 : partexpr_item = list_head(partexprs);
2361 : 924 : context = deparse_context_for(get_relation_name(relid), relid);
2362 : :
2363 : 924 : initStringInfo(&buf);
2364 : :
2365 [ + + + - ]: 924 : switch (form->partstrat)
2366 : : {
2367 : 61 : case PARTITION_STRATEGY_HASH:
2368 [ + - ]: 61 : if (!attrsOnly)
2369 : 61 : appendStringInfoString(&buf, "HASH");
2370 : 61 : break;
2371 : 354 : case PARTITION_STRATEGY_LIST:
2372 [ + + ]: 354 : if (!attrsOnly)
2373 : 328 : appendStringInfoString(&buf, "LIST");
2374 : 354 : break;
2375 : 509 : case PARTITION_STRATEGY_RANGE:
2376 [ + + ]: 509 : if (!attrsOnly)
2377 : 441 : appendStringInfoString(&buf, "RANGE");
2378 : 509 : break;
2379 : 0 : default:
2380 [ # # ]: 0 : elog(ERROR, "unexpected partition strategy: %d",
2381 : : (int) form->partstrat);
2382 : : }
2383 : :
2384 [ + + ]: 924 : if (!attrsOnly)
2385 : 830 : appendStringInfoString(&buf, " (");
2386 : 924 : sep = "";
2387 [ + + ]: 1941 : for (keyno = 0; keyno < form->partnatts; keyno++)
2388 : : {
2389 : 1017 : AttrNumber attnum = form->partattrs.values[keyno];
2390 : : Oid keycoltype;
2391 : : Oid keycolcollation;
2392 : : Oid partcoll;
2393 : :
2394 : 1017 : appendStringInfoString(&buf, sep);
2395 : 1017 : sep = ", ";
2396 [ + + ]: 1017 : if (attnum != 0)
2397 : : {
2398 : : /* Simple attribute reference */
2399 : : char *attname;
2400 : : int32 keycoltypmod;
2401 : :
2402 : 925 : attname = get_attname(relid, attnum, false);
2403 : 925 : appendStringInfoString(&buf, quote_identifier(attname));
2404 : 925 : get_atttypetypmodcoll(relid, attnum,
2405 : : &keycoltype, &keycoltypmod,
2406 : : &keycolcollation);
2407 : : }
2408 : : else
2409 : : {
2410 : : /* Expression */
2411 : : Node *partkey;
2412 : :
2413 [ - + ]: 92 : if (partexpr_item == NULL)
2414 [ # # ]: 0 : elog(ERROR, "too few entries in partexprs list");
2415 : 92 : partkey = (Node *) lfirst(partexpr_item);
2416 : 92 : partexpr_item = lnext(partexprs, partexpr_item);
2417 : :
2418 : : /* Deparse */
2419 : 92 : str = deparse_expression_pretty(partkey, context, false, false,
2420 : : prettyFlags, 0);
2421 : : /* Need parens if it's not a bare function call */
2422 [ + + ]: 92 : if (looks_like_function(partkey))
2423 : 34 : appendStringInfoString(&buf, str);
2424 : : else
2425 : 58 : appendStringInfo(&buf, "(%s)", str);
2426 : :
2427 : 92 : keycoltype = exprType(partkey);
2428 : 92 : keycolcollation = exprCollation(partkey);
2429 : : }
2430 : :
2431 : : /* Add collation, if not default for column */
2432 : 1017 : partcoll = partcollation->values[keyno];
2433 [ + + + + : 1017 : if (!attrsOnly && OidIsValid(partcoll) && partcoll != keycolcollation)
+ + ]
2434 : 4 : appendStringInfo(&buf, " COLLATE %s",
2435 : : generate_collation_name((partcoll)));
2436 : :
2437 : : /* Add the operator class name, if not default */
2438 [ + + ]: 1017 : if (!attrsOnly)
2439 : 887 : get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2440 : : }
2441 : :
2442 [ + + ]: 924 : if (!attrsOnly)
2443 : 830 : appendStringInfoChar(&buf, ')');
2444 : :
2445 : : /* Clean up */
2446 : 924 : ReleaseSysCache(tuple);
2447 : :
2448 : 924 : return buf.data;
2449 : : }
2450 : :
2451 : : /*
2452 : : * pg_get_partition_constraintdef
2453 : : *
2454 : : * Returns partition constraint expression as a string for the input relation
2455 : : */
2456 : : Datum
2457 : 153 : pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
2458 : : {
2459 : 153 : Oid relationId = PG_GETARG_OID(0);
2460 : : Expr *constr_expr;
2461 : : int prettyFlags;
2462 : : List *context;
2463 : : char *consrc;
2464 : :
2465 : 153 : constr_expr = get_partition_qual_relid(relationId);
2466 : :
2467 : : /* Quick exit if no partition constraint */
2468 [ + + ]: 153 : if (constr_expr == NULL)
2469 : 16 : PG_RETURN_NULL();
2470 : :
2471 : : /*
2472 : : * Deparse and return the constraint expression.
2473 : : */
2474 : 137 : prettyFlags = PRETTYFLAG_INDENT;
2475 : 137 : context = deparse_context_for(get_relation_name(relationId), relationId);
2476 : 137 : consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2477 : : false, prettyFlags, 0);
2478 : :
2479 : 137 : PG_RETURN_TEXT_P(string_to_text(consrc));
2480 : : }
2481 : :
2482 : : /*
2483 : : * pg_get_partconstrdef_string
2484 : : *
2485 : : * Returns the partition constraint as a C-string for the input relation, with
2486 : : * the given alias. No pretty-printing.
2487 : : */
2488 : : char *
2489 : 65 : pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
2490 : : {
2491 : : Expr *constr_expr;
2492 : : List *context;
2493 : :
2494 : 65 : constr_expr = get_partition_qual_relid(partitionId);
2495 : 65 : context = deparse_context_for(aliasname, partitionId);
2496 : :
2497 : 65 : return deparse_expression((Node *) constr_expr, context, true, false);
2498 : : }
2499 : :
2500 : : /*
2501 : : * pg_get_constraintdef
2502 : : *
2503 : : * Returns the definition for the constraint, ie, everything that needs to
2504 : : * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
2505 : : */
2506 : : Datum
2507 : 1232 : pg_get_constraintdef(PG_FUNCTION_ARGS)
2508 : : {
2509 : 1232 : Oid constraintId = PG_GETARG_OID(0);
2510 : : int prettyFlags;
2511 : : char *res;
2512 : :
2513 : 1232 : prettyFlags = PRETTYFLAG_INDENT;
2514 : :
2515 : 1232 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2516 : :
2517 [ + + ]: 1232 : if (res == NULL)
2518 : 4 : PG_RETURN_NULL();
2519 : :
2520 : 1228 : PG_RETURN_TEXT_P(string_to_text(res));
2521 : : }
2522 : :
2523 : : Datum
2524 : 2959 : pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
2525 : : {
2526 : 2959 : Oid constraintId = PG_GETARG_OID(0);
2527 : 2959 : bool pretty = PG_GETARG_BOOL(1);
2528 : : int prettyFlags;
2529 : : char *res;
2530 : :
2531 [ + + ]: 2959 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2532 : :
2533 : 2959 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2534 : :
2535 [ - + ]: 2959 : if (res == NULL)
2536 : 0 : PG_RETURN_NULL();
2537 : :
2538 : 2959 : PG_RETURN_TEXT_P(string_to_text(res));
2539 : : }
2540 : :
2541 : : /*
2542 : : * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
2543 : : */
2544 : : char *
2545 : 468 : pg_get_constraintdef_command(Oid constraintId)
2546 : : {
2547 : 468 : return pg_get_constraintdef_worker(constraintId, true, 0, false);
2548 : : }
2549 : :
2550 : : /*
2551 : : * As of 9.4, we now use an MVCC snapshot for this.
2552 : : */
2553 : : static char *
2554 : 4659 : pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
2555 : : int prettyFlags, bool missing_ok)
2556 : : {
2557 : : HeapTuple tup;
2558 : : Form_pg_constraint conForm;
2559 : : StringInfoData buf;
2560 : : SysScanDesc scandesc;
2561 : : ScanKeyData scankey[1];
2562 : 4659 : Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());
2563 : 4659 : Relation relation = table_open(ConstraintRelationId, AccessShareLock);
2564 : :
2565 : 4659 : ScanKeyInit(&scankey[0],
2566 : : Anum_pg_constraint_oid,
2567 : : BTEqualStrategyNumber, F_OIDEQ,
2568 : : ObjectIdGetDatum(constraintId));
2569 : :
2570 : 4659 : scandesc = systable_beginscan(relation,
2571 : : ConstraintOidIndexId,
2572 : : true,
2573 : : snapshot,
2574 : : 1,
2575 : : scankey);
2576 : :
2577 : : /*
2578 : : * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2579 : : * via SearchSysCache, which works fine.
2580 : : */
2581 : 4659 : tup = systable_getnext(scandesc);
2582 : :
2583 : 4659 : UnregisterSnapshot(snapshot);
2584 : :
2585 [ + + ]: 4659 : if (!HeapTupleIsValid(tup))
2586 : : {
2587 [ + - ]: 4 : if (missing_ok)
2588 : : {
2589 : 4 : systable_endscan(scandesc);
2590 : 4 : table_close(relation, AccessShareLock);
2591 : 4 : return NULL;
2592 : : }
2593 [ # # ]: 0 : elog(ERROR, "could not find tuple for constraint %u", constraintId);
2594 : : }
2595 : :
2596 : 4655 : conForm = (Form_pg_constraint) GETSTRUCT(tup);
2597 : :
2598 : 4655 : initStringInfo(&buf);
2599 : :
2600 [ + + ]: 4655 : if (fullCommand)
2601 : : {
2602 [ + + ]: 468 : if (OidIsValid(conForm->conrelid))
2603 : : {
2604 : : /*
2605 : : * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2606 : : * constraints, and other types of constraints don't inherit
2607 : : * anyway so it doesn't matter whether we say ONLY or not. Someday
2608 : : * we might need to let callers specify whether to put ONLY in the
2609 : : * command.
2610 : : */
2611 : 455 : appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2612 : : generate_qualified_relation_name(conForm->conrelid),
2613 : 455 : quote_identifier(NameStr(conForm->conname)));
2614 : : }
2615 : : else
2616 : : {
2617 : : /* Must be a domain constraint */
2618 : : Assert(OidIsValid(conForm->contypid));
2619 : 13 : appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2620 : : generate_qualified_type_name(conForm->contypid),
2621 : 13 : quote_identifier(NameStr(conForm->conname)));
2622 : : }
2623 : : }
2624 : :
2625 [ + + + + : 4655 : switch (conForm->contype)
- + - ]
2626 : : {
2627 : 547 : case CONSTRAINT_FOREIGN:
2628 : : {
2629 : : Datum val;
2630 : : bool isnull;
2631 : : const char *string;
2632 : :
2633 : : /* Start off the constraint definition */
2634 : 547 : appendStringInfoString(&buf, "FOREIGN KEY (");
2635 : :
2636 : : /* Fetch and build referencing-column list */
2637 : 547 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2638 : : Anum_pg_constraint_conkey);
2639 : :
2640 : : /* If it is a temporal foreign key then it uses PERIOD. */
2641 : 547 : decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
2642 : :
2643 : : /* add foreign relation name */
2644 : 547 : appendStringInfo(&buf, ") REFERENCES %s(",
2645 : : generate_relation_name(conForm->confrelid,
2646 : : NIL));
2647 : :
2648 : : /* Fetch and build referenced-column list */
2649 : 547 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2650 : : Anum_pg_constraint_confkey);
2651 : :
2652 : 547 : decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
2653 : :
2654 : 547 : appendStringInfoChar(&buf, ')');
2655 : :
2656 : : /* Add match type */
2657 [ + - + - ]: 547 : switch (conForm->confmatchtype)
2658 : : {
2659 : 21 : case FKCONSTR_MATCH_FULL:
2660 : 21 : string = " MATCH FULL";
2661 : 21 : break;
2662 : 0 : case FKCONSTR_MATCH_PARTIAL:
2663 : 0 : string = " MATCH PARTIAL";
2664 : 0 : break;
2665 : 526 : case FKCONSTR_MATCH_SIMPLE:
2666 : 526 : string = "";
2667 : 526 : break;
2668 : 0 : default:
2669 [ # # ]: 0 : elog(ERROR, "unrecognized confmatchtype: %d",
2670 : : conForm->confmatchtype);
2671 : : string = ""; /* keep compiler quiet */
2672 : : break;
2673 : : }
2674 : 547 : appendStringInfoString(&buf, string);
2675 : :
2676 : : /* Add ON UPDATE and ON DELETE clauses, if needed */
2677 [ + - + + : 547 : switch (conForm->confupdtype)
- - ]
2678 : : {
2679 : 463 : case FKCONSTR_ACTION_NOACTION:
2680 : 463 : string = NULL; /* suppress default */
2681 : 463 : break;
2682 : 0 : case FKCONSTR_ACTION_RESTRICT:
2683 : 0 : string = "RESTRICT";
2684 : 0 : break;
2685 : 67 : case FKCONSTR_ACTION_CASCADE:
2686 : 67 : string = "CASCADE";
2687 : 67 : break;
2688 : 17 : case FKCONSTR_ACTION_SETNULL:
2689 : 17 : string = "SET NULL";
2690 : 17 : break;
2691 : 0 : case FKCONSTR_ACTION_SETDEFAULT:
2692 : 0 : string = "SET DEFAULT";
2693 : 0 : break;
2694 : 0 : default:
2695 [ # # ]: 0 : elog(ERROR, "unrecognized confupdtype: %d",
2696 : : conForm->confupdtype);
2697 : : string = NULL; /* keep compiler quiet */
2698 : : break;
2699 : : }
2700 [ + + ]: 547 : if (string)
2701 : 84 : appendStringInfo(&buf, " ON UPDATE %s", string);
2702 : :
2703 [ + - + + : 547 : switch (conForm->confdeltype)
+ - ]
2704 : : {
2705 : 464 : case FKCONSTR_ACTION_NOACTION:
2706 : 464 : string = NULL; /* suppress default */
2707 : 464 : break;
2708 : 0 : case FKCONSTR_ACTION_RESTRICT:
2709 : 0 : string = "RESTRICT";
2710 : 0 : break;
2711 : 67 : case FKCONSTR_ACTION_CASCADE:
2712 : 67 : string = "CASCADE";
2713 : 67 : break;
2714 : 12 : case FKCONSTR_ACTION_SETNULL:
2715 : 12 : string = "SET NULL";
2716 : 12 : break;
2717 : 4 : case FKCONSTR_ACTION_SETDEFAULT:
2718 : 4 : string = "SET DEFAULT";
2719 : 4 : break;
2720 : 0 : default:
2721 [ # # ]: 0 : elog(ERROR, "unrecognized confdeltype: %d",
2722 : : conForm->confdeltype);
2723 : : string = NULL; /* keep compiler quiet */
2724 : : break;
2725 : : }
2726 [ + + ]: 547 : if (string)
2727 : 83 : appendStringInfo(&buf, " ON DELETE %s", string);
2728 : :
2729 : : /*
2730 : : * Add columns specified to SET NULL or SET DEFAULT if
2731 : : * provided.
2732 : : */
2733 : 547 : val = SysCacheGetAttr(CONSTROID, tup,
2734 : : Anum_pg_constraint_confdelsetcols, &isnull);
2735 [ + + ]: 547 : if (!isnull)
2736 : : {
2737 : 8 : appendStringInfoString(&buf, " (");
2738 : 8 : decompile_column_index_array(val, conForm->conrelid, false, &buf);
2739 : 8 : appendStringInfoChar(&buf, ')');
2740 : : }
2741 : :
2742 : 547 : break;
2743 : : }
2744 : 2347 : case CONSTRAINT_PRIMARY:
2745 : : case CONSTRAINT_UNIQUE:
2746 : : {
2747 : : Datum val;
2748 : : Oid indexId;
2749 : : int keyatts;
2750 : : HeapTuple indtup;
2751 : :
2752 : : /* Start off the constraint definition */
2753 [ + + ]: 2347 : if (conForm->contype == CONSTRAINT_PRIMARY)
2754 : 1918 : appendStringInfoString(&buf, "PRIMARY KEY ");
2755 : : else
2756 : 429 : appendStringInfoString(&buf, "UNIQUE ");
2757 : :
2758 : 2347 : indexId = conForm->conindid;
2759 : :
2760 : 2347 : indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
2761 [ - + ]: 2347 : if (!HeapTupleIsValid(indtup))
2762 [ # # ]: 0 : elog(ERROR, "cache lookup failed for index %u", indexId);
2763 [ + + ]: 2347 : if (conForm->contype == CONSTRAINT_UNIQUE &&
2764 [ - + ]: 429 : ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
2765 : 0 : appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2766 : :
2767 : 2347 : appendStringInfoChar(&buf, '(');
2768 : :
2769 : : /* Fetch and build target column list */
2770 : 2347 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2771 : : Anum_pg_constraint_conkey);
2772 : :
2773 : 2347 : keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
2774 [ + + ]: 2347 : if (conForm->conperiod)
2775 : 226 : appendStringInfoString(&buf, " WITHOUT OVERLAPS");
2776 : :
2777 : 2347 : appendStringInfoChar(&buf, ')');
2778 : :
2779 : : /* Build including column list (from pg_index.indkeys) */
2780 : 2347 : val = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2781 : : Anum_pg_index_indnatts);
2782 [ + + ]: 2347 : if (DatumGetInt32(val) > keyatts)
2783 : : {
2784 : : Datum cols;
2785 : : Datum *keys;
2786 : : int nKeys;
2787 : : int j;
2788 : :
2789 : 48 : appendStringInfoString(&buf, " INCLUDE (");
2790 : :
2791 : 48 : cols = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2792 : : Anum_pg_index_indkey);
2793 : :
2794 : 48 : deconstruct_array_builtin(DatumGetArrayTypeP(cols), INT2OID,
2795 : : &keys, NULL, &nKeys);
2796 : :
2797 [ + + ]: 144 : for (j = keyatts; j < nKeys; j++)
2798 : : {
2799 : : char *colName;
2800 : :
2801 : 96 : colName = get_attname(conForm->conrelid,
2802 : 96 : DatumGetInt16(keys[j]), false);
2803 [ + + ]: 96 : if (j > keyatts)
2804 : 48 : appendStringInfoString(&buf, ", ");
2805 : 96 : appendStringInfoString(&buf, quote_identifier(colName));
2806 : : }
2807 : :
2808 : 48 : appendStringInfoChar(&buf, ')');
2809 : : }
2810 : 2347 : ReleaseSysCache(indtup);
2811 : :
2812 : : /* XXX why do we only print these bits if fullCommand? */
2813 [ + + + - ]: 2347 : if (fullCommand && OidIsValid(indexId))
2814 : : {
2815 : 136 : char *options = flatten_reloptions(indexId);
2816 : : Oid tblspc;
2817 : :
2818 [ - + ]: 136 : if (options)
2819 : : {
2820 : 0 : appendStringInfo(&buf, " WITH (%s)", options);
2821 : 0 : pfree(options);
2822 : : }
2823 : :
2824 : : /*
2825 : : * Print the tablespace, unless it's the database default.
2826 : : * This is to help ALTER TABLE usage of this facility,
2827 : : * which needs this behavior to recreate exact catalog
2828 : : * state.
2829 : : */
2830 : 136 : tblspc = get_rel_tablespace(indexId);
2831 [ + + ]: 136 : if (OidIsValid(tblspc))
2832 : 16 : appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2833 : 16 : quote_identifier(get_tablespace_name(tblspc)));
2834 : : }
2835 : :
2836 : 2347 : break;
2837 : : }
2838 : 1393 : case CONSTRAINT_CHECK:
2839 : : {
2840 : : Datum val;
2841 : : char *conbin;
2842 : : char *consrc;
2843 : : Node *expr;
2844 : : List *context;
2845 : :
2846 : : /* Fetch constraint expression in parsetree form */
2847 : 1393 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2848 : : Anum_pg_constraint_conbin);
2849 : :
2850 : 1393 : conbin = TextDatumGetCString(val);
2851 : 1393 : expr = stringToNode(conbin);
2852 : :
2853 : : /* Set up deparsing context for Var nodes in constraint */
2854 [ + + ]: 1393 : if (conForm->conrelid != InvalidOid)
2855 : : {
2856 : : /* relation constraint */
2857 : 1236 : context = deparse_context_for(get_relation_name(conForm->conrelid),
2858 : : conForm->conrelid);
2859 : : }
2860 : : else
2861 : : {
2862 : : /* domain constraint --- can't have Vars */
2863 : 157 : context = NIL;
2864 : : }
2865 : :
2866 : 1393 : consrc = deparse_expression_pretty(expr, context, false, false,
2867 : : prettyFlags, 0);
2868 : :
2869 : : /*
2870 : : * Now emit the constraint definition, adding NO INHERIT if
2871 : : * necessary.
2872 : : *
2873 : : * There are cases where the constraint expression will be
2874 : : * fully parenthesized and we don't need the outer parens ...
2875 : : * but there are other cases where we do need 'em. Be
2876 : : * conservative for now.
2877 : : *
2878 : : * Note that simply checking for leading '(' and trailing ')'
2879 : : * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2880 : : */
2881 : 1393 : appendStringInfo(&buf, "CHECK (%s)%s",
2882 : : consrc,
2883 [ + + ]: 1393 : conForm->connoinherit ? " NO INHERIT" : "");
2884 : 1393 : break;
2885 : : }
2886 : 292 : case CONSTRAINT_NOTNULL:
2887 : : {
2888 [ + + ]: 292 : if (conForm->conrelid)
2889 : : {
2890 : : AttrNumber attnum;
2891 : :
2892 : 232 : attnum = extractNotNullColumn(tup);
2893 : :
2894 : 232 : appendStringInfo(&buf, "NOT NULL %s",
2895 : 232 : quote_identifier(get_attname(conForm->conrelid,
2896 : : attnum, false)));
2897 [ - + ]: 232 : if (((Form_pg_constraint) GETSTRUCT(tup))->connoinherit)
2898 : 0 : appendStringInfoString(&buf, " NO INHERIT");
2899 : : }
2900 [ + - ]: 60 : else if (conForm->contypid)
2901 : : {
2902 : : /* conkey is null for domain not-null constraints */
2903 : 60 : appendStringInfoString(&buf, "NOT NULL");
2904 : : }
2905 : 292 : break;
2906 : : }
2907 : :
2908 : 0 : case CONSTRAINT_TRIGGER:
2909 : :
2910 : : /*
2911 : : * There isn't an ALTER TABLE syntax for creating a user-defined
2912 : : * constraint trigger, but it seems better to print something than
2913 : : * throw an error; if we throw error then this function couldn't
2914 : : * safely be applied to all rows of pg_constraint.
2915 : : */
2916 : 0 : appendStringInfoString(&buf, "TRIGGER");
2917 : 0 : break;
2918 : 76 : case CONSTRAINT_EXCLUSION:
2919 : : {
2920 : 76 : Oid indexOid = conForm->conindid;
2921 : : Datum val;
2922 : : Datum *elems;
2923 : : int nElems;
2924 : : int i;
2925 : : Oid *operators;
2926 : :
2927 : : /* Extract operator OIDs from the pg_constraint tuple */
2928 : 76 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2929 : : Anum_pg_constraint_conexclop);
2930 : :
2931 : 76 : deconstruct_array_builtin(DatumGetArrayTypeP(val), OIDOID,
2932 : : &elems, NULL, &nElems);
2933 : :
2934 : 76 : operators = (Oid *) palloc(nElems * sizeof(Oid));
2935 [ + + ]: 172 : for (i = 0; i < nElems; i++)
2936 : 96 : operators[i] = DatumGetObjectId(elems[i]);
2937 : :
2938 : : /* pg_get_indexdef_worker does the rest */
2939 : : /* suppress tablespace because pg_dump wants it that way */
2940 : 76 : appendStringInfoString(&buf,
2941 : 76 : pg_get_indexdef_worker(indexOid,
2942 : : 0,
2943 : : operators,
2944 : : false,
2945 : : false,
2946 : : false,
2947 : : false,
2948 : : prettyFlags,
2949 : : false));
2950 : 76 : break;
2951 : : }
2952 : 0 : default:
2953 [ # # ]: 0 : elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2954 : : break;
2955 : : }
2956 : :
2957 [ + + ]: 4655 : if (conForm->condeferrable)
2958 : 86 : appendStringInfoString(&buf, " DEFERRABLE");
2959 [ + + ]: 4655 : if (conForm->condeferred)
2960 : 39 : appendStringInfoString(&buf, " INITIALLY DEFERRED");
2961 : :
2962 : : /* Validated status is irrelevant when the constraint is NOT ENFORCED. */
2963 [ + + ]: 4655 : if (!conForm->conenforced)
2964 : 77 : appendStringInfoString(&buf, " NOT ENFORCED");
2965 [ + + ]: 4578 : else if (!conForm->convalidated)
2966 : 150 : appendStringInfoString(&buf, " NOT VALID");
2967 : :
2968 : : /* Cleanup */
2969 : 4655 : systable_endscan(scandesc);
2970 : 4655 : table_close(relation, AccessShareLock);
2971 : :
2972 : 4655 : return buf.data;
2973 : : }
2974 : :
2975 : :
2976 : : /*
2977 : : * Convert an int16[] Datum into a comma-separated list of column names
2978 : : * for the indicated relation; append the list to buf. Returns the number
2979 : : * of keys.
2980 : : */
2981 : : static int
2982 : 4481 : decompile_column_index_array(Datum column_index_array, Oid relId,
2983 : : bool withPeriod, StringInfo buf)
2984 : : {
2985 : : Datum *keys;
2986 : : int nKeys;
2987 : : int j;
2988 : :
2989 : : /* Extract data from array of int16 */
2990 : 4481 : deconstruct_array_builtin(DatumGetArrayTypeP(column_index_array), INT2OID,
2991 : : &keys, NULL, &nKeys);
2992 : :
2993 [ + + ]: 10526 : for (j = 0; j < nKeys; j++)
2994 : : {
2995 : : char *colName;
2996 : :
2997 : 6045 : colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2998 : :
2999 [ + + ]: 6045 : if (j == 0)
3000 : 4481 : appendStringInfoString(buf, quote_identifier(colName));
3001 : : else
3002 [ + + ]: 1698 : appendStringInfo(buf, ", %s%s",
3003 [ + + ]: 134 : (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
3004 : : quote_identifier(colName));
3005 : : }
3006 : :
3007 : 4481 : return nKeys;
3008 : : }
3009 : :
3010 : :
3011 : : /* ----------
3012 : : * pg_get_expr - Decompile an expression tree
3013 : : *
3014 : : * Input: an expression tree in nodeToString form, and a relation OID
3015 : : *
3016 : : * Output: reverse-listed expression
3017 : : *
3018 : : * Currently, the expression can only refer to a single relation, namely
3019 : : * the one specified by the second parameter. This is sufficient for
3020 : : * partial indexes, column default expressions, etc. We also support
3021 : : * Var-free expressions, for which the OID can be InvalidOid.
3022 : : *
3023 : : * If the OID is nonzero but not actually valid, don't throw an error,
3024 : : * just return NULL. This is a bit questionable, but it's what we've
3025 : : * done historically, and it can help avoid unwanted failures when
3026 : : * examining catalog entries for just-deleted relations.
3027 : : *
3028 : : * We expect this function to work, or throw a reasonably clean error,
3029 : : * for any node tree that can appear in a catalog pg_node_tree column.
3030 : : * Query trees, such as those appearing in pg_rewrite.ev_action, are
3031 : : * not supported. Nor are expressions in more than one relation, which
3032 : : * can appear in places like pg_rewrite.ev_qual.
3033 : : * ----------
3034 : : */
3035 : : Datum
3036 : 5974 : pg_get_expr(PG_FUNCTION_ARGS)
3037 : : {
3038 : 5974 : text *expr = PG_GETARG_TEXT_PP(0);
3039 : 5974 : Oid relid = PG_GETARG_OID(1);
3040 : : text *result;
3041 : : int prettyFlags;
3042 : :
3043 : 5974 : prettyFlags = PRETTYFLAG_INDENT;
3044 : :
3045 : 5974 : result = pg_get_expr_worker(expr, relid, prettyFlags);
3046 [ + + ]: 5974 : if (result)
3047 : 5973 : PG_RETURN_TEXT_P(result);
3048 : : else
3049 : 1 : PG_RETURN_NULL();
3050 : : }
3051 : :
3052 : : Datum
3053 : 572 : pg_get_expr_ext(PG_FUNCTION_ARGS)
3054 : : {
3055 : 572 : text *expr = PG_GETARG_TEXT_PP(0);
3056 : 572 : Oid relid = PG_GETARG_OID(1);
3057 : 572 : bool pretty = PG_GETARG_BOOL(2);
3058 : : text *result;
3059 : : int prettyFlags;
3060 : :
3061 [ + - ]: 572 : prettyFlags = GET_PRETTY_FLAGS(pretty);
3062 : :
3063 : 572 : result = pg_get_expr_worker(expr, relid, prettyFlags);
3064 [ + - ]: 572 : if (result)
3065 : 572 : PG_RETURN_TEXT_P(result);
3066 : : else
3067 : 0 : PG_RETURN_NULL();
3068 : : }
3069 : :
3070 : : static text *
3071 : 6546 : pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
3072 : : {
3073 : : Node *node;
3074 : : Node *tst;
3075 : : Relids relids;
3076 : : List *context;
3077 : : char *exprstr;
3078 : 6546 : Relation rel = NULL;
3079 : : char *str;
3080 : :
3081 : : /* Convert input pg_node_tree (really TEXT) object to C string */
3082 : 6546 : exprstr = text_to_cstring(expr);
3083 : :
3084 : : /* Convert expression to node tree */
3085 : 6546 : node = (Node *) stringToNode(exprstr);
3086 : :
3087 : 6546 : pfree(exprstr);
3088 : :
3089 : : /*
3090 : : * Throw error if the input is a querytree rather than an expression tree.
3091 : : * While we could support queries here, there seems no very good reason
3092 : : * to. In most such catalog columns, we'll see a List of Query nodes, or
3093 : : * even nested Lists, so drill down to a non-List node before checking.
3094 : : */
3095 : 6546 : tst = node;
3096 [ + - - + ]: 6546 : while (tst && IsA(tst, List))
3097 : 0 : tst = linitial((List *) tst);
3098 [ + - - + ]: 6546 : if (tst && IsA(tst, Query))
3099 [ # # ]: 0 : ereport(ERROR,
3100 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3101 : : errmsg("input is a query, not an expression")));
3102 : :
3103 : : /*
3104 : : * Throw error if the expression contains Vars we won't be able to
3105 : : * deparse.
3106 : : */
3107 : 6546 : relids = pull_varnos(NULL, node);
3108 [ + + ]: 6546 : if (OidIsValid(relid))
3109 : : {
3110 [ - + ]: 6474 : if (!bms_is_subset(relids, bms_make_singleton(1)))
3111 [ # # ]: 0 : ereport(ERROR,
3112 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3113 : : errmsg("expression contains variables of more than one relation")));
3114 : : }
3115 : : else
3116 : : {
3117 [ - + ]: 72 : if (!bms_is_empty(relids))
3118 [ # # ]: 0 : ereport(ERROR,
3119 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3120 : : errmsg("expression contains variables")));
3121 : : }
3122 : :
3123 : : /*
3124 : : * Prepare deparse context if needed. If we are deparsing with a relid,
3125 : : * we need to transiently open and lock the rel, to make sure it won't go
3126 : : * away underneath us. (set_relation_column_names would lock it anyway,
3127 : : * so this isn't really introducing any new behavior.)
3128 : : */
3129 [ + + ]: 6546 : if (OidIsValid(relid))
3130 : : {
3131 : 6474 : rel = try_relation_open(relid, AccessShareLock);
3132 [ + + ]: 6474 : if (rel == NULL)
3133 : 1 : return NULL;
3134 : 6473 : context = deparse_context_for(RelationGetRelationName(rel), relid);
3135 : : }
3136 : : else
3137 : 72 : context = NIL;
3138 : :
3139 : : /* Deparse */
3140 : 6545 : str = deparse_expression_pretty(node, context, false, false,
3141 : : prettyFlags, 0);
3142 : :
3143 [ + + ]: 6545 : if (rel != NULL)
3144 : 6473 : relation_close(rel, AccessShareLock);
3145 : :
3146 : 6545 : return string_to_text(str);
3147 : : }
3148 : :
3149 : :
3150 : : /* ----------
3151 : : * pg_get_userbyid - Get a user name by roleid and
3152 : : * fallback to 'unknown (OID=n)'
3153 : : * ----------
3154 : : */
3155 : : Datum
3156 : 1251 : pg_get_userbyid(PG_FUNCTION_ARGS)
3157 : : {
3158 : 1251 : Oid roleid = PG_GETARG_OID(0);
3159 : : Name result;
3160 : : HeapTuple roletup;
3161 : : Form_pg_authid role_rec;
3162 : :
3163 : : /*
3164 : : * Allocate space for the result
3165 : : */
3166 : 1251 : result = (Name) palloc(NAMEDATALEN);
3167 : 1251 : memset(NameStr(*result), 0, NAMEDATALEN);
3168 : :
3169 : : /*
3170 : : * Get the pg_authid entry and print the result
3171 : : */
3172 : 1251 : roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
3173 [ + - ]: 1251 : if (HeapTupleIsValid(roletup))
3174 : : {
3175 : 1251 : role_rec = (Form_pg_authid) GETSTRUCT(roletup);
3176 : 1251 : *result = role_rec->rolname;
3177 : 1251 : ReleaseSysCache(roletup);
3178 : : }
3179 : : else
3180 : 0 : sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
3181 : :
3182 : 1251 : PG_RETURN_NAME(result);
3183 : : }
3184 : :
3185 : :
3186 : : /*
3187 : : * pg_get_serial_sequence
3188 : : * Get the name of the sequence used by an identity or serial column,
3189 : : * formatted suitably for passing to setval, nextval or currval.
3190 : : * First parameter is not treated as double-quoted, second parameter
3191 : : * is --- see documentation for reason.
3192 : : */
3193 : : Datum
3194 : 8 : pg_get_serial_sequence(PG_FUNCTION_ARGS)
3195 : : {
3196 : 8 : text *tablename = PG_GETARG_TEXT_PP(0);
3197 : 8 : text *columnname = PG_GETARG_TEXT_PP(1);
3198 : : RangeVar *tablerv;
3199 : : Oid tableOid;
3200 : : char *column;
3201 : : AttrNumber attnum;
3202 : 8 : Oid sequenceId = InvalidOid;
3203 : : Relation depRel;
3204 : : ScanKeyData key[3];
3205 : : SysScanDesc scan;
3206 : : HeapTuple tup;
3207 : :
3208 : : /* Look up table name. Can't lock it - we might not have privileges. */
3209 : 8 : tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
3210 : 8 : tableOid = RangeVarGetRelid(tablerv, NoLock, false);
3211 : :
3212 : : /* Get the number of the column */
3213 : 8 : column = text_to_cstring(columnname);
3214 : :
3215 : 8 : attnum = get_attnum(tableOid, column);
3216 [ - + ]: 8 : if (attnum == InvalidAttrNumber)
3217 [ # # ]: 0 : ereport(ERROR,
3218 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
3219 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
3220 : : column, tablerv->relname)));
3221 : :
3222 : : /* Search the dependency table for the dependent sequence */
3223 : 8 : depRel = table_open(DependRelationId, AccessShareLock);
3224 : :
3225 : 8 : ScanKeyInit(&key[0],
3226 : : Anum_pg_depend_refclassid,
3227 : : BTEqualStrategyNumber, F_OIDEQ,
3228 : : ObjectIdGetDatum(RelationRelationId));
3229 : 8 : ScanKeyInit(&key[1],
3230 : : Anum_pg_depend_refobjid,
3231 : : BTEqualStrategyNumber, F_OIDEQ,
3232 : : ObjectIdGetDatum(tableOid));
3233 : 8 : ScanKeyInit(&key[2],
3234 : : Anum_pg_depend_refobjsubid,
3235 : : BTEqualStrategyNumber, F_INT4EQ,
3236 : : Int32GetDatum(attnum));
3237 : :
3238 : 8 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
3239 : : NULL, 3, key);
3240 : :
3241 [ + - ]: 20 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
3242 : : {
3243 : 20 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
3244 : :
3245 : : /*
3246 : : * Look for an auto dependency (serial column) or internal dependency
3247 : : * (identity column) of a sequence on a column. (We need the relkind
3248 : : * test because indexes can also have auto dependencies on columns.)
3249 : : */
3250 [ + + ]: 20 : if (deprec->classid == RelationRelationId &&
3251 [ + - ]: 8 : deprec->objsubid == 0 &&
3252 [ + + ]: 8 : (deprec->deptype == DEPENDENCY_AUTO ||
3253 [ + - + - ]: 12 : deprec->deptype == DEPENDENCY_INTERNAL) &&
3254 : 8 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
3255 : : {
3256 : 8 : sequenceId = deprec->objid;
3257 : 8 : break;
3258 : : }
3259 : : }
3260 : :
3261 : 8 : systable_endscan(scan);
3262 : 8 : table_close(depRel, AccessShareLock);
3263 : :
3264 [ + - ]: 8 : if (OidIsValid(sequenceId))
3265 : : {
3266 : : char *result;
3267 : :
3268 : 8 : result = generate_qualified_relation_name(sequenceId);
3269 : :
3270 : 8 : PG_RETURN_TEXT_P(string_to_text(result));
3271 : : }
3272 : :
3273 : 0 : PG_RETURN_NULL();
3274 : : }
3275 : :
3276 : :
3277 : : /*
3278 : : * pg_get_functiondef
3279 : : * Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
3280 : : * the specified function.
3281 : : *
3282 : : * Note: if you change the output format of this function, be careful not
3283 : : * to break psql's rules (in \ef and \sf) for identifying the start of the
3284 : : * function body. To wit: the function body starts on a line that begins with
3285 : : * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
3286 : : */
3287 : : Datum
3288 : 131 : pg_get_functiondef(PG_FUNCTION_ARGS)
3289 : : {
3290 : 131 : Oid funcid = PG_GETARG_OID(0);
3291 : : StringInfoData buf;
3292 : : StringInfoData dq;
3293 : : HeapTuple proctup;
3294 : : Form_pg_proc proc;
3295 : : bool isfunction;
3296 : : Datum tmp;
3297 : : bool isnull;
3298 : : const char *prosrc;
3299 : : const char *name;
3300 : : const char *nsp;
3301 : : float4 procost;
3302 : : int oldlen;
3303 : :
3304 : 131 : initStringInfo(&buf);
3305 : :
3306 : : /* Look up the function */
3307 : 131 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3308 [ + + ]: 131 : if (!HeapTupleIsValid(proctup))
3309 : 4 : PG_RETURN_NULL();
3310 : :
3311 : 127 : proc = (Form_pg_proc) GETSTRUCT(proctup);
3312 : 127 : name = NameStr(proc->proname);
3313 : :
3314 [ - + ]: 127 : if (proc->prokind == PROKIND_AGGREGATE)
3315 [ # # ]: 0 : ereport(ERROR,
3316 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3317 : : errmsg("\"%s\" is an aggregate function", name)));
3318 : :
3319 : 127 : isfunction = (proc->prokind != PROKIND_PROCEDURE);
3320 : :
3321 : : /*
3322 : : * We always qualify the function name, to ensure the right function gets
3323 : : * replaced.
3324 : : */
3325 : 127 : nsp = get_namespace_name_or_temp(proc->pronamespace);
3326 [ + + ]: 127 : appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
3327 : : isfunction ? "FUNCTION" : "PROCEDURE",
3328 : : quote_qualified_identifier(nsp, name));
3329 : 127 : (void) print_function_arguments(&buf, proctup, false, true);
3330 : 127 : appendStringInfoString(&buf, ")\n");
3331 [ + + ]: 127 : if (isfunction)
3332 : : {
3333 : 114 : appendStringInfoString(&buf, " RETURNS ");
3334 : 114 : print_function_rettype(&buf, proctup);
3335 : 114 : appendStringInfoChar(&buf, '\n');
3336 : : }
3337 : :
3338 : 127 : print_function_trftypes(&buf, proctup);
3339 : :
3340 : 127 : appendStringInfo(&buf, " LANGUAGE %s\n",
3341 : 127 : quote_identifier(get_language_name(proc->prolang, false)));
3342 : :
3343 : : /* Emit some miscellaneous options on one line */
3344 : 127 : oldlen = buf.len;
3345 : :
3346 [ - + ]: 127 : if (proc->prokind == PROKIND_WINDOW)
3347 : 0 : appendStringInfoString(&buf, " WINDOW");
3348 [ + + + - ]: 127 : switch (proc->provolatile)
3349 : : {
3350 : 8 : case PROVOLATILE_IMMUTABLE:
3351 : 8 : appendStringInfoString(&buf, " IMMUTABLE");
3352 : 8 : break;
3353 : 20 : case PROVOLATILE_STABLE:
3354 : 20 : appendStringInfoString(&buf, " STABLE");
3355 : 20 : break;
3356 : 99 : case PROVOLATILE_VOLATILE:
3357 : 99 : break;
3358 : : }
3359 : :
3360 [ + - + - ]: 127 : switch (proc->proparallel)
3361 : : {
3362 : 17 : case PROPARALLEL_SAFE:
3363 : 17 : appendStringInfoString(&buf, " PARALLEL SAFE");
3364 : 17 : break;
3365 : 0 : case PROPARALLEL_RESTRICTED:
3366 : 0 : appendStringInfoString(&buf, " PARALLEL RESTRICTED");
3367 : 0 : break;
3368 : 110 : case PROPARALLEL_UNSAFE:
3369 : 110 : break;
3370 : : }
3371 : :
3372 [ + + ]: 127 : if (proc->proisstrict)
3373 : 32 : appendStringInfoString(&buf, " STRICT");
3374 [ + + ]: 127 : if (proc->prosecdef)
3375 : 4 : appendStringInfoString(&buf, " SECURITY DEFINER");
3376 [ - + ]: 127 : if (proc->proleakproof)
3377 : 0 : appendStringInfoString(&buf, " LEAKPROOF");
3378 : :
3379 : : /* This code for the default cost and rows should match functioncmds.c */
3380 [ + - ]: 127 : if (proc->prolang == INTERNALlanguageId ||
3381 [ + + ]: 127 : proc->prolang == ClanguageId)
3382 : 5 : procost = 1;
3383 : : else
3384 : 122 : procost = 100;
3385 [ + + ]: 127 : if (proc->procost != procost)
3386 : 4 : appendStringInfo(&buf, " COST %g", proc->procost);
3387 : :
3388 [ + + - + ]: 127 : if (proc->prorows > 0 && proc->prorows != 1000)
3389 : 0 : appendStringInfo(&buf, " ROWS %g", proc->prorows);
3390 : :
3391 [ - + ]: 127 : if (proc->prosupport)
3392 : : {
3393 : : Oid argtypes[1];
3394 : :
3395 : : /*
3396 : : * We should qualify the support function's name if it wouldn't be
3397 : : * resolved by lookup in the current search path.
3398 : : */
3399 : 0 : argtypes[0] = INTERNALOID;
3400 : 0 : appendStringInfo(&buf, " SUPPORT %s",
3401 : : generate_function_name(proc->prosupport, 1,
3402 : : NIL, argtypes,
3403 : : false, NULL, false));
3404 : : }
3405 : :
3406 [ + + ]: 127 : if (oldlen != buf.len)
3407 : 41 : appendStringInfoChar(&buf, '\n');
3408 : :
3409 : : /* Emit any proconfig options, one per line */
3410 : 127 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
3411 [ + + ]: 127 : if (!isnull)
3412 : : {
3413 : 4 : ArrayType *a = DatumGetArrayTypeP(tmp);
3414 : : int i;
3415 : :
3416 : : Assert(ARR_ELEMTYPE(a) == TEXTOID);
3417 : : Assert(ARR_NDIM(a) == 1);
3418 : : Assert(ARR_LBOUND(a)[0] == 1);
3419 : :
3420 [ + + ]: 28 : for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3421 : : {
3422 : : Datum d;
3423 : :
3424 : 24 : d = array_ref(a, 1, &i,
3425 : : -1 /* varlenarray */ ,
3426 : : -1 /* TEXT's typlen */ ,
3427 : : false /* TEXT's typbyval */ ,
3428 : : TYPALIGN_INT /* TEXT's typalign */ ,
3429 : : &isnull);
3430 [ + - ]: 24 : if (!isnull)
3431 : : {
3432 : 24 : char *configitem = TextDatumGetCString(d);
3433 : : char *pos;
3434 : :
3435 : 24 : pos = strchr(configitem, '=');
3436 [ - + ]: 24 : if (pos == NULL)
3437 : 0 : continue;
3438 : 24 : *pos++ = '\0';
3439 : :
3440 : 24 : appendStringInfo(&buf, " SET %s TO ",
3441 : : quote_identifier(configitem));
3442 : :
3443 : : /*
3444 : : * Variables that are marked GUC_LIST_QUOTE were already fully
3445 : : * quoted by flatten_set_variable_args() before they were put
3446 : : * into the proconfig array. However, because the quoting
3447 : : * rules used there aren't exactly like SQL's, we have to
3448 : : * break the list value apart and then quote the elements as
3449 : : * string literals. (The elements may be double-quoted as-is,
3450 : : * but we can't just feed them to the SQL parser; it would do
3451 : : * the wrong thing with elements that are zero-length or
3452 : : * longer than NAMEDATALEN.) Also, we need a special case for
3453 : : * empty lists.
3454 : : *
3455 : : * Variables that are not so marked should just be emitted as
3456 : : * simple string literals. If the variable is not known to
3457 : : * guc.c, we'll do that; this makes it unsafe to use
3458 : : * GUC_LIST_QUOTE for extension variables.
3459 : : */
3460 [ + + ]: 24 : if (GetConfigOptionFlags(configitem, true) & GUC_LIST_QUOTE)
3461 : : {
3462 : : List *namelist;
3463 : : ListCell *lc;
3464 : :
3465 : : /* Parse string into list of identifiers */
3466 [ - + ]: 12 : if (!SplitGUCList(pos, ',', &namelist))
3467 : : {
3468 : : /* this shouldn't fail really */
3469 [ # # ]: 0 : elog(ERROR, "invalid list syntax in proconfig item");
3470 : : }
3471 : : /* Special case: represent an empty list as NULL */
3472 [ + + ]: 12 : if (namelist == NIL)
3473 : 4 : appendStringInfoString(&buf, "NULL");
3474 [ + + + + : 32 : foreach(lc, namelist)
+ + ]
3475 : : {
3476 : 20 : char *curname = (char *) lfirst(lc);
3477 : :
3478 : 20 : simple_quote_literal(&buf, curname);
3479 [ + + ]: 20 : if (lnext(namelist, lc))
3480 : 12 : appendStringInfoString(&buf, ", ");
3481 : : }
3482 : : }
3483 : : else
3484 : 12 : simple_quote_literal(&buf, pos);
3485 : 24 : appendStringInfoChar(&buf, '\n');
3486 : : }
3487 : : }
3488 : : }
3489 : :
3490 : : /* And finally the function definition ... */
3491 : 127 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3492 [ + + + + ]: 127 : if (proc->prolang == SQLlanguageId && !isnull)
3493 : : {
3494 : 95 : print_function_sqlbody(&buf, proctup);
3495 : : }
3496 : : else
3497 : : {
3498 : 32 : appendStringInfoString(&buf, "AS ");
3499 : :
3500 : 32 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
3501 [ + + ]: 32 : if (!isnull)
3502 : : {
3503 : 5 : simple_quote_literal(&buf, TextDatumGetCString(tmp));
3504 : 5 : appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3505 : : }
3506 : :
3507 : 32 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosrc);
3508 : 32 : prosrc = TextDatumGetCString(tmp);
3509 : :
3510 : : /*
3511 : : * We always use dollar quoting. Figure out a suitable delimiter.
3512 : : *
3513 : : * Since the user is likely to be editing the function body string, we
3514 : : * shouldn't use a short delimiter that he might easily create a
3515 : : * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3516 : : * if needed.
3517 : : */
3518 : 32 : initStringInfo(&dq);
3519 : 32 : appendStringInfoChar(&dq, '$');
3520 [ + + ]: 32 : appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3521 [ - + ]: 32 : while (strstr(prosrc, dq.data) != NULL)
3522 : 0 : appendStringInfoChar(&dq, 'x');
3523 : 32 : appendStringInfoChar(&dq, '$');
3524 : :
3525 : 32 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3526 : 32 : appendStringInfoString(&buf, prosrc);
3527 : 32 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3528 : : }
3529 : :
3530 : 127 : appendStringInfoChar(&buf, '\n');
3531 : :
3532 : 127 : ReleaseSysCache(proctup);
3533 : :
3534 : 127 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3535 : : }
3536 : :
3537 : : /*
3538 : : * pg_get_function_arguments
3539 : : * Get a nicely-formatted list of arguments for a function.
3540 : : * This is everything that would go between the parentheses in
3541 : : * CREATE FUNCTION.
3542 : : */
3543 : : Datum
3544 : 2509 : pg_get_function_arguments(PG_FUNCTION_ARGS)
3545 : : {
3546 : 2509 : Oid funcid = PG_GETARG_OID(0);
3547 : : StringInfoData buf;
3548 : : HeapTuple proctup;
3549 : :
3550 : 2509 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3551 [ + + ]: 2509 : if (!HeapTupleIsValid(proctup))
3552 : 4 : PG_RETURN_NULL();
3553 : :
3554 : 2505 : initStringInfo(&buf);
3555 : :
3556 : 2505 : (void) print_function_arguments(&buf, proctup, false, true);
3557 : :
3558 : 2505 : ReleaseSysCache(proctup);
3559 : :
3560 : 2505 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3561 : : }
3562 : :
3563 : : /*
3564 : : * pg_get_function_identity_arguments
3565 : : * Get a formatted list of arguments for a function.
3566 : : * This is everything that would go between the parentheses in
3567 : : * ALTER FUNCTION, etc. In particular, don't print defaults.
3568 : : */
3569 : : Datum
3570 : 2143 : pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
3571 : : {
3572 : 2143 : Oid funcid = PG_GETARG_OID(0);
3573 : : StringInfoData buf;
3574 : : HeapTuple proctup;
3575 : :
3576 : 2143 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3577 [ + + ]: 2143 : if (!HeapTupleIsValid(proctup))
3578 : 4 : PG_RETURN_NULL();
3579 : :
3580 : 2139 : initStringInfo(&buf);
3581 : :
3582 : 2139 : (void) print_function_arguments(&buf, proctup, false, false);
3583 : :
3584 : 2139 : ReleaseSysCache(proctup);
3585 : :
3586 : 2139 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3587 : : }
3588 : :
3589 : : /*
3590 : : * pg_get_function_result
3591 : : * Get a nicely-formatted version of the result type of a function.
3592 : : * This is what would appear after RETURNS in CREATE FUNCTION.
3593 : : */
3594 : : Datum
3595 : 2209 : pg_get_function_result(PG_FUNCTION_ARGS)
3596 : : {
3597 : 2209 : Oid funcid = PG_GETARG_OID(0);
3598 : : StringInfoData buf;
3599 : : HeapTuple proctup;
3600 : :
3601 : 2209 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3602 [ + + ]: 2209 : if (!HeapTupleIsValid(proctup))
3603 : 4 : PG_RETURN_NULL();
3604 : :
3605 [ + + ]: 2205 : if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
3606 : : {
3607 : 134 : ReleaseSysCache(proctup);
3608 : 134 : PG_RETURN_NULL();
3609 : : }
3610 : :
3611 : 2071 : initStringInfo(&buf);
3612 : :
3613 : 2071 : print_function_rettype(&buf, proctup);
3614 : :
3615 : 2071 : ReleaseSysCache(proctup);
3616 : :
3617 : 2071 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3618 : : }
3619 : :
3620 : : /*
3621 : : * Guts of pg_get_function_result: append the function's return type
3622 : : * to the specified buffer.
3623 : : */
3624 : : static void
3625 : 2185 : print_function_rettype(StringInfo buf, HeapTuple proctup)
3626 : : {
3627 : 2185 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3628 : 2185 : int ntabargs = 0;
3629 : : StringInfoData rbuf;
3630 : :
3631 : 2185 : initStringInfo(&rbuf);
3632 : :
3633 [ + + ]: 2185 : if (proc->proretset)
3634 : : {
3635 : : /* It might be a table function; try to print the arguments */
3636 : 214 : appendStringInfoString(&rbuf, "TABLE(");
3637 : 214 : ntabargs = print_function_arguments(&rbuf, proctup, true, false);
3638 [ + + ]: 214 : if (ntabargs > 0)
3639 : 44 : appendStringInfoChar(&rbuf, ')');
3640 : : else
3641 : 170 : resetStringInfo(&rbuf);
3642 : : }
3643 : :
3644 [ + + ]: 2185 : if (ntabargs == 0)
3645 : : {
3646 : : /* Not a table function, so do the normal thing */
3647 [ + + ]: 2141 : if (proc->proretset)
3648 : 170 : appendStringInfoString(&rbuf, "SETOF ");
3649 : 2141 : appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3650 : : }
3651 : :
3652 : 2185 : appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
3653 : 2185 : }
3654 : :
3655 : : /*
3656 : : * Common code for pg_get_function_arguments and pg_get_function_result:
3657 : : * append the desired subset of arguments to buf. We print only TABLE
3658 : : * arguments when print_table_args is true, and all the others when it's false.
3659 : : * We print argument defaults only if print_defaults is true.
3660 : : * Function return value is the number of arguments printed.
3661 : : */
3662 : : static int
3663 : 4985 : print_function_arguments(StringInfo buf, HeapTuple proctup,
3664 : : bool print_table_args, bool print_defaults)
3665 : : {
3666 : 4985 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3667 : : int numargs;
3668 : : Oid *argtypes;
3669 : : char **argnames;
3670 : : char *argmodes;
3671 : 4985 : int insertorderbyat = -1;
3672 : : int argsprinted;
3673 : : int inputargno;
3674 : : int nlackdefaults;
3675 : 4985 : List *argdefaults = NIL;
3676 : 4985 : ListCell *nextargdefault = NULL;
3677 : : int i;
3678 : :
3679 : 4985 : numargs = get_func_arg_info(proctup,
3680 : : &argtypes, &argnames, &argmodes);
3681 : :
3682 : 4985 : nlackdefaults = numargs;
3683 [ + + + + ]: 4985 : if (print_defaults && proc->pronargdefaults > 0)
3684 : : {
3685 : : Datum proargdefaults;
3686 : : bool isnull;
3687 : :
3688 : 21 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3689 : : Anum_pg_proc_proargdefaults,
3690 : : &isnull);
3691 [ + - ]: 21 : if (!isnull)
3692 : : {
3693 : : char *str;
3694 : :
3695 : 21 : str = TextDatumGetCString(proargdefaults);
3696 : 21 : argdefaults = castNode(List, stringToNode(str));
3697 : 21 : pfree(str);
3698 : 21 : nextargdefault = list_head(argdefaults);
3699 : : /* nlackdefaults counts only *input* arguments lacking defaults */
3700 : 21 : nlackdefaults = proc->pronargs - list_length(argdefaults);
3701 : : }
3702 : : }
3703 : :
3704 : : /* Check for special treatment of ordered-set aggregates */
3705 [ + + ]: 4985 : if (proc->prokind == PROKIND_AGGREGATE)
3706 : : {
3707 : : HeapTuple aggtup;
3708 : : Form_pg_aggregate agg;
3709 : :
3710 : 598 : aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(proc->oid));
3711 [ - + ]: 598 : if (!HeapTupleIsValid(aggtup))
3712 [ # # ]: 0 : elog(ERROR, "cache lookup failed for aggregate %u",
3713 : : proc->oid);
3714 : 598 : agg = (Form_pg_aggregate) GETSTRUCT(aggtup);
3715 [ + + ]: 598 : if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3716 : 28 : insertorderbyat = agg->aggnumdirectargs;
3717 : 598 : ReleaseSysCache(aggtup);
3718 : : }
3719 : :
3720 : 4985 : argsprinted = 0;
3721 : 4985 : inputargno = 0;
3722 [ + + ]: 10138 : for (i = 0; i < numargs; i++)
3723 : : {
3724 : 5153 : Oid argtype = argtypes[i];
3725 [ + + ]: 5153 : char *argname = argnames ? argnames[i] : NULL;
3726 [ + + ]: 5153 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
3727 : : const char *modename;
3728 : : bool isinput;
3729 : :
3730 [ + + + + : 5153 : switch (argmode)
+ - ]
3731 : : {
3732 : 4218 : case PROARGMODE_IN:
3733 : :
3734 : : /*
3735 : : * For procedures, explicitly mark all argument modes, so as
3736 : : * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3737 : : */
3738 [ + + ]: 4218 : if (proc->prokind == PROKIND_PROCEDURE)
3739 : 295 : modename = "IN ";
3740 : : else
3741 : 3923 : modename = "";
3742 : 4218 : isinput = true;
3743 : 4218 : break;
3744 : 50 : case PROARGMODE_INOUT:
3745 : 50 : modename = "INOUT ";
3746 : 50 : isinput = true;
3747 : 50 : break;
3748 : 517 : case PROARGMODE_OUT:
3749 : 517 : modename = "OUT ";
3750 : 517 : isinput = false;
3751 : 517 : break;
3752 : 92 : case PROARGMODE_VARIADIC:
3753 : 92 : modename = "VARIADIC ";
3754 : 92 : isinput = true;
3755 : 92 : break;
3756 : 276 : case PROARGMODE_TABLE:
3757 : 276 : modename = "";
3758 : 276 : isinput = false;
3759 : 276 : break;
3760 : 0 : default:
3761 [ # # ]: 0 : elog(ERROR, "invalid parameter mode '%c'", argmode);
3762 : : modename = NULL; /* keep compiler quiet */
3763 : : isinput = false;
3764 : : break;
3765 : : }
3766 [ + + ]: 5153 : if (isinput)
3767 : 4360 : inputargno++; /* this is a 1-based counter */
3768 : :
3769 [ + + ]: 5153 : if (print_table_args != (argmode == PROARGMODE_TABLE))
3770 : 441 : continue;
3771 : :
3772 [ + + ]: 4712 : if (argsprinted == insertorderbyat)
3773 : : {
3774 [ + - ]: 28 : if (argsprinted)
3775 : 28 : appendStringInfoChar(buf, ' ');
3776 : 28 : appendStringInfoString(buf, "ORDER BY ");
3777 : : }
3778 [ + + ]: 4684 : else if (argsprinted)
3779 : 1530 : appendStringInfoString(buf, ", ");
3780 : :
3781 : 4712 : appendStringInfoString(buf, modename);
3782 [ + + + + ]: 4712 : if (argname && argname[0])
3783 : 1733 : appendStringInfo(buf, "%s ", quote_identifier(argname));
3784 : 4712 : appendStringInfoString(buf, format_type_be(argtype));
3785 [ + + + + : 4712 : if (print_defaults && isinput && inputargno > nlackdefaults)
+ + ]
3786 : : {
3787 : : Node *expr;
3788 : :
3789 : : Assert(nextargdefault != NULL);
3790 : 32 : expr = (Node *) lfirst(nextargdefault);
3791 : 32 : nextargdefault = lnext(argdefaults, nextargdefault);
3792 : :
3793 : 32 : appendStringInfo(buf, " DEFAULT %s",
3794 : : deparse_expression(expr, NIL, false, false));
3795 : : }
3796 : 4712 : argsprinted++;
3797 : :
3798 : : /* nasty hack: print the last arg twice for variadic ordered-set agg */
3799 [ + + + + ]: 4712 : if (argsprinted == insertorderbyat && i == numargs - 1)
3800 : : {
3801 : 14 : i--;
3802 : : /* aggs shouldn't have defaults anyway, but just to be sure ... */
3803 : 14 : print_defaults = false;
3804 : : }
3805 : : }
3806 : :
3807 : 4985 : return argsprinted;
3808 : : }
3809 : :
3810 : : static bool
3811 : 64 : is_input_argument(int nth, const char *argmodes)
3812 : : {
3813 : : return (!argmodes
3814 [ + + ]: 28 : || argmodes[nth] == PROARGMODE_IN
3815 [ + - ]: 12 : || argmodes[nth] == PROARGMODE_INOUT
3816 [ + + - + ]: 92 : || argmodes[nth] == PROARGMODE_VARIADIC);
3817 : : }
3818 : :
3819 : : /*
3820 : : * Append used transformed types to specified buffer
3821 : : */
3822 : : static void
3823 : 127 : print_function_trftypes(StringInfo buf, HeapTuple proctup)
3824 : : {
3825 : : Oid *trftypes;
3826 : : int ntypes;
3827 : :
3828 : 127 : ntypes = get_func_trftypes(proctup, &trftypes);
3829 [ + + ]: 127 : if (ntypes > 0)
3830 : : {
3831 : : int i;
3832 : :
3833 : 3 : appendStringInfoString(buf, " TRANSFORM ");
3834 [ + + ]: 8 : for (i = 0; i < ntypes; i++)
3835 : : {
3836 [ + + ]: 5 : if (i != 0)
3837 : 2 : appendStringInfoString(buf, ", ");
3838 : 5 : appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3839 : : }
3840 : 3 : appendStringInfoChar(buf, '\n');
3841 : : }
3842 : 127 : }
3843 : :
3844 : : /*
3845 : : * Get textual representation of a function argument's default value. The
3846 : : * second argument of this function is the argument number among all arguments
3847 : : * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
3848 : : * how information_schema.sql uses it.
3849 : : */
3850 : : Datum
3851 : 36 : pg_get_function_arg_default(PG_FUNCTION_ARGS)
3852 : : {
3853 : 36 : Oid funcid = PG_GETARG_OID(0);
3854 : 36 : int32 nth_arg = PG_GETARG_INT32(1);
3855 : : HeapTuple proctup;
3856 : : Form_pg_proc proc;
3857 : : int numargs;
3858 : : Oid *argtypes;
3859 : : char **argnames;
3860 : : char *argmodes;
3861 : : int i;
3862 : : List *argdefaults;
3863 : : Node *node;
3864 : : char *str;
3865 : : int nth_inputarg;
3866 : : Datum proargdefaults;
3867 : : bool isnull;
3868 : : int nth_default;
3869 : :
3870 : 36 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3871 [ + + ]: 36 : if (!HeapTupleIsValid(proctup))
3872 : 8 : PG_RETURN_NULL();
3873 : :
3874 : 28 : numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3875 [ + - + - : 28 : if (nth_arg < 1 || nth_arg > numargs || !is_input_argument(nth_arg - 1, argmodes))
+ + ]
3876 : : {
3877 : 8 : ReleaseSysCache(proctup);
3878 : 8 : PG_RETURN_NULL();
3879 : : }
3880 : :
3881 : 20 : nth_inputarg = 0;
3882 [ + + ]: 56 : for (i = 0; i < nth_arg; i++)
3883 [ + + ]: 36 : if (is_input_argument(i, argmodes))
3884 : 32 : nth_inputarg++;
3885 : :
3886 : 20 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3887 : : Anum_pg_proc_proargdefaults,
3888 : : &isnull);
3889 [ - + ]: 20 : if (isnull)
3890 : : {
3891 : 0 : ReleaseSysCache(proctup);
3892 : 0 : PG_RETURN_NULL();
3893 : : }
3894 : :
3895 : 20 : str = TextDatumGetCString(proargdefaults);
3896 : 20 : argdefaults = castNode(List, stringToNode(str));
3897 : 20 : pfree(str);
3898 : :
3899 : 20 : proc = (Form_pg_proc) GETSTRUCT(proctup);
3900 : :
3901 : : /*
3902 : : * Calculate index into proargdefaults: proargdefaults corresponds to the
3903 : : * last N input arguments, where N = pronargdefaults.
3904 : : */
3905 : 20 : nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3906 : :
3907 [ + + - + ]: 20 : if (nth_default < 0 || nth_default >= list_length(argdefaults))
3908 : : {
3909 : 4 : ReleaseSysCache(proctup);
3910 : 4 : PG_RETURN_NULL();
3911 : : }
3912 : 16 : node = list_nth(argdefaults, nth_default);
3913 : 16 : str = deparse_expression(node, NIL, false, false);
3914 : :
3915 : 16 : ReleaseSysCache(proctup);
3916 : :
3917 : 16 : PG_RETURN_TEXT_P(string_to_text(str));
3918 : : }
3919 : :
3920 : : static void
3921 : 145 : print_function_sqlbody(StringInfo buf, HeapTuple proctup)
3922 : : {
3923 : : int numargs;
3924 : : Oid *argtypes;
3925 : : char **argnames;
3926 : : char *argmodes;
3927 : 145 : deparse_namespace dpns = {0};
3928 : : Datum tmp;
3929 : : Node *n;
3930 : :
3931 : 145 : dpns.funcname = pstrdup(NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname));
3932 : 145 : numargs = get_func_arg_info(proctup,
3933 : : &argtypes, &argnames, &argmodes);
3934 : 145 : dpns.numargs = numargs;
3935 : 145 : dpns.argnames = argnames;
3936 : :
3937 : 145 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosqlbody);
3938 : 145 : n = stringToNode(TextDatumGetCString(tmp));
3939 : :
3940 [ + + ]: 145 : if (IsA(n, List))
3941 : : {
3942 : : List *stmts;
3943 : : ListCell *lc;
3944 : :
3945 : 118 : stmts = linitial(castNode(List, n));
3946 : :
3947 : 118 : appendStringInfoString(buf, "BEGIN ATOMIC\n");
3948 : :
3949 [ + + + + : 231 : foreach(lc, stmts)
+ + ]
3950 : : {
3951 : 113 : Query *query = lfirst_node(Query, lc);
3952 : :
3953 : : /* It seems advisable to get at least AccessShareLock on rels */
3954 : 113 : AcquireRewriteLocks(query, false, false);
3955 : 113 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3956 : : PRETTYFLAG_INDENT, WRAP_COLUMN_DEFAULT, 1);
3957 : 113 : appendStringInfoChar(buf, ';');
3958 : 113 : appendStringInfoChar(buf, '\n');
3959 : : }
3960 : :
3961 : 118 : appendStringInfoString(buf, "END");
3962 : : }
3963 : : else
3964 : : {
3965 : 27 : Query *query = castNode(Query, n);
3966 : :
3967 : : /* It seems advisable to get at least AccessShareLock on rels */
3968 : 27 : AcquireRewriteLocks(query, false, false);
3969 : 27 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3970 : : 0, WRAP_COLUMN_DEFAULT, 0);
3971 : : }
3972 : 145 : }
3973 : :
3974 : : Datum
3975 : 1852 : pg_get_function_sqlbody(PG_FUNCTION_ARGS)
3976 : : {
3977 : 1852 : Oid funcid = PG_GETARG_OID(0);
3978 : : StringInfoData buf;
3979 : : HeapTuple proctup;
3980 : : bool isnull;
3981 : :
3982 : 1852 : initStringInfo(&buf);
3983 : :
3984 : : /* Look up the function */
3985 : 1852 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3986 [ - + ]: 1852 : if (!HeapTupleIsValid(proctup))
3987 : 0 : PG_RETURN_NULL();
3988 : :
3989 : 1852 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
3990 [ + + ]: 1852 : if (isnull)
3991 : : {
3992 : 1802 : ReleaseSysCache(proctup);
3993 : 1802 : PG_RETURN_NULL();
3994 : : }
3995 : :
3996 : 50 : print_function_sqlbody(&buf, proctup);
3997 : :
3998 : 50 : ReleaseSysCache(proctup);
3999 : :
4000 : 50 : PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
4001 : : }
4002 : :
4003 : :
4004 : : /*
4005 : : * deparse_expression - General utility for deparsing expressions
4006 : : *
4007 : : * calls deparse_expression_pretty with all prettyPrinting disabled
4008 : : */
4009 : : char *
4010 : 55195 : deparse_expression(Node *expr, List *dpcontext,
4011 : : bool forceprefix, bool showimplicit)
4012 : : {
4013 : 55195 : return deparse_expression_pretty(expr, dpcontext, forceprefix,
4014 : : showimplicit, 0, 0);
4015 : : }
4016 : :
4017 : : /* ----------
4018 : : * deparse_expression_pretty - General utility for deparsing expressions
4019 : : *
4020 : : * expr is the node tree to be deparsed. It must be a transformed expression
4021 : : * tree (ie, not the raw output of gram.y).
4022 : : *
4023 : : * dpcontext is a list of deparse_namespace nodes representing the context
4024 : : * for interpreting Vars in the node tree. It can be NIL if no Vars are
4025 : : * expected.
4026 : : *
4027 : : * forceprefix is true to force all Vars to be prefixed with their table names.
4028 : : *
4029 : : * showimplicit is true to force all implicit casts to be shown explicitly.
4030 : : *
4031 : : * Tries to pretty up the output according to prettyFlags and startIndent.
4032 : : *
4033 : : * The result is a palloc'd string.
4034 : : * ----------
4035 : : */
4036 : : static char *
4037 : 64659 : deparse_expression_pretty(Node *expr, List *dpcontext,
4038 : : bool forceprefix, bool showimplicit,
4039 : : int prettyFlags, int startIndent)
4040 : : {
4041 : : StringInfoData buf;
4042 : : deparse_context context;
4043 : :
4044 : 64659 : initStringInfo(&buf);
4045 : 64659 : context.buf = &buf;
4046 : 64659 : context.namespaces = dpcontext;
4047 : 64659 : context.resultDesc = NULL;
4048 : 64659 : context.targetList = NIL;
4049 : 64659 : context.windowClause = NIL;
4050 : 64659 : context.varprefix = forceprefix;
4051 : 64659 : context.prettyFlags = prettyFlags;
4052 : 64659 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
4053 : 64659 : context.indentLevel = startIndent;
4054 : 64659 : context.colNamesVisible = true;
4055 : 64659 : context.inGroupBy = false;
4056 : 64659 : context.varInOrderBy = false;
4057 : 64659 : context.appendparents = NULL;
4058 : :
4059 : 64659 : get_rule_expr(expr, &context, showimplicit);
4060 : :
4061 : 64659 : return buf.data;
4062 : : }
4063 : :
4064 : : /* ----------
4065 : : * deparse_context_for - Build deparse context for a single relation
4066 : : *
4067 : : * Given the reference name (alias) and OID of a relation, build deparsing
4068 : : * context for an expression referencing only that relation (as varno 1,
4069 : : * varlevelsup 0). This is sufficient for many uses of deparse_expression.
4070 : : * ----------
4071 : : */
4072 : : List *
4073 : 16179 : deparse_context_for(const char *aliasname, Oid relid)
4074 : : {
4075 : : deparse_namespace *dpns;
4076 : : RangeTblEntry *rte;
4077 : :
4078 : 16179 : dpns = palloc0_object(deparse_namespace);
4079 : :
4080 : : /* Build a minimal RTE for the rel */
4081 : 16179 : rte = makeNode(RangeTblEntry);
4082 : 16179 : rte->rtekind = RTE_RELATION;
4083 : 16179 : rte->relid = relid;
4084 : 16179 : rte->relkind = RELKIND_RELATION; /* no need for exactness here */
4085 : 16179 : rte->rellockmode = AccessShareLock;
4086 : 16179 : rte->alias = makeAlias(aliasname, NIL);
4087 : 16179 : rte->eref = rte->alias;
4088 : 16179 : rte->lateral = false;
4089 : 16179 : rte->inh = false;
4090 : 16179 : rte->inFromCl = true;
4091 : :
4092 : : /* Build one-element rtable */
4093 : 16179 : dpns->rtable = list_make1(rte);
4094 : 16179 : dpns->subplans = NIL;
4095 : 16179 : dpns->ctes = NIL;
4096 : 16179 : dpns->appendrels = NULL;
4097 : 16179 : set_rtable_names(dpns, NIL, NULL);
4098 : 16179 : set_simple_column_names(dpns);
4099 : :
4100 : : /* Return a one-deep namespace stack */
4101 : 16179 : return list_make1(dpns);
4102 : : }
4103 : :
4104 : : /*
4105 : : * deparse_context_for_plan_tree - Build deparse context for a Plan tree
4106 : : *
4107 : : * When deparsing an expression in a Plan tree, we use the plan's rangetable
4108 : : * to resolve names of simple Vars. The initialization of column names for
4109 : : * this is rather expensive if the rangetable is large, and it'll be the same
4110 : : * for every expression in the Plan tree; so we do it just once and re-use
4111 : : * the result of this function for each expression. (Note that the result
4112 : : * is not usable until set_deparse_context_plan() is applied to it.)
4113 : : *
4114 : : * In addition to the PlannedStmt, pass the per-RTE alias names
4115 : : * assigned by a previous call to select_rtable_names_for_explain.
4116 : : */
4117 : : List *
4118 : 16657 : deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
4119 : : {
4120 : : deparse_namespace *dpns;
4121 : :
4122 : 16657 : dpns = palloc0_object(deparse_namespace);
4123 : :
4124 : : /* Initialize fields that stay the same across the whole plan tree */
4125 : 16657 : dpns->rtable = pstmt->rtable;
4126 : 16657 : dpns->rtable_names = rtable_names;
4127 : 16657 : dpns->subplans = pstmt->subplans;
4128 : 16657 : dpns->ctes = NIL;
4129 [ + + ]: 16657 : if (pstmt->appendRelations)
4130 : : {
4131 : : /* Set up the array, indexed by child relid */
4132 : 2671 : int ntables = list_length(dpns->rtable);
4133 : : ListCell *lc;
4134 : :
4135 : 2671 : dpns->appendrels = (AppendRelInfo **)
4136 : 2671 : palloc0((ntables + 1) * sizeof(AppendRelInfo *));
4137 [ + - + + : 14662 : foreach(lc, pstmt->appendRelations)
+ + ]
4138 : : {
4139 : 11991 : AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
4140 : 11991 : Index crelid = appinfo->child_relid;
4141 : :
4142 : : Assert(crelid > 0 && crelid <= ntables);
4143 : : Assert(dpns->appendrels[crelid] == NULL);
4144 : 11991 : dpns->appendrels[crelid] = appinfo;
4145 : : }
4146 : : }
4147 : : else
4148 : 13986 : dpns->appendrels = NULL; /* don't need it */
4149 : :
4150 : : /*
4151 : : * Set up column name aliases, ignoring any join RTEs; they don't matter
4152 : : * because plan trees don't contain any join alias Vars.
4153 : : */
4154 : 16657 : set_simple_column_names(dpns);
4155 : :
4156 : : /* Return a one-deep namespace stack */
4157 : 16657 : return list_make1(dpns);
4158 : : }
4159 : :
4160 : : /*
4161 : : * set_deparse_context_plan - Specify Plan node containing expression
4162 : : *
4163 : : * When deparsing an expression in a Plan tree, we might have to resolve
4164 : : * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
4165 : : * provide the parent Plan node. Then OUTER_VAR and INNER_VAR references
4166 : : * can be resolved by drilling down into the left and right child plans.
4167 : : * Similarly, INDEX_VAR references can be resolved by reference to the
4168 : : * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
4169 : : * ForeignScan and CustomScan nodes. (Note that we don't currently support
4170 : : * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
4171 : : * for those, we can only deparse the indexqualorig fields, which won't
4172 : : * contain INDEX_VAR Vars.)
4173 : : *
4174 : : * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
4175 : : * the most-closely-nested first. This is needed to resolve PARAM_EXEC
4176 : : * Params. Note we assume that all the Plan nodes share the same rtable.
4177 : : *
4178 : : * For a ModifyTable plan, we might also need to resolve references to OLD/NEW
4179 : : * variables in the RETURNING list, so we copy the alias names of the OLD and
4180 : : * NEW rows from the ModifyTable plan node.
4181 : : *
4182 : : * Once this function has been called, deparse_expression() can be called on
4183 : : * subsidiary expression(s) of the specified Plan node. To deparse
4184 : : * expressions of a different Plan node in the same Plan tree, re-call this
4185 : : * function to identify the new parent Plan node.
4186 : : *
4187 : : * The result is the same List passed in; this is a notational convenience.
4188 : : */
4189 : : List *
4190 : 39880 : set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
4191 : : {
4192 : : deparse_namespace *dpns;
4193 : :
4194 : : /* Should always have one-entry namespace list for Plan deparsing */
4195 : : Assert(list_length(dpcontext) == 1);
4196 : 39880 : dpns = (deparse_namespace *) linitial(dpcontext);
4197 : :
4198 : : /* Set our attention on the specific plan node passed in */
4199 : 39880 : dpns->ancestors = ancestors;
4200 : 39880 : set_deparse_plan(dpns, plan);
4201 : :
4202 : : /* For ModifyTable, set aliases for OLD and NEW in RETURNING */
4203 [ + + ]: 39880 : if (IsA(plan, ModifyTable))
4204 : : {
4205 : 141 : dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias;
4206 : 141 : dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias;
4207 : : }
4208 : :
4209 : 39880 : return dpcontext;
4210 : : }
4211 : :
4212 : : /*
4213 : : * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
4214 : : *
4215 : : * Determine the relation aliases we'll use during an EXPLAIN operation.
4216 : : * This is just a frontend to set_rtable_names. We have to expose the aliases
4217 : : * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
4218 : : */
4219 : : List *
4220 : 16657 : select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
4221 : : {
4222 : : deparse_namespace dpns;
4223 : :
4224 : 16657 : memset(&dpns, 0, sizeof(dpns));
4225 : 16657 : dpns.rtable = rtable;
4226 : 16657 : dpns.subplans = NIL;
4227 : 16657 : dpns.ctes = NIL;
4228 : 16657 : dpns.appendrels = NULL;
4229 : 16657 : set_rtable_names(&dpns, NIL, rels_used);
4230 : : /* We needn't bother computing column aliases yet */
4231 : :
4232 : 16657 : return dpns.rtable_names;
4233 : : }
4234 : :
4235 : : /*
4236 : : * set_rtable_names: select RTE aliases to be used in printing a query
4237 : : *
4238 : : * We fill in dpns->rtable_names with a list of names that is one-for-one with
4239 : : * the already-filled dpns->rtable list. Each RTE name is unique among those
4240 : : * in the new namespace plus any ancestor namespaces listed in
4241 : : * parent_namespaces.
4242 : : *
4243 : : * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
4244 : : *
4245 : : * Note that this function is only concerned with relation names, not column
4246 : : * names.
4247 : : */
4248 : : static void
4249 : 36566 : set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
4250 : : Bitmapset *rels_used)
4251 : : {
4252 : : HASHCTL hash_ctl;
4253 : : HTAB *names_hash;
4254 : : NameHashEntry *hentry;
4255 : : bool found;
4256 : : int rtindex;
4257 : : ListCell *lc;
4258 : :
4259 : 36566 : dpns->rtable_names = NIL;
4260 : : /* nothing more to do if empty rtable */
4261 [ + + ]: 36566 : if (dpns->rtable == NIL)
4262 : 352 : return;
4263 : :
4264 : : /*
4265 : : * We use a hash table to hold known names, so that this process is O(N)
4266 : : * not O(N^2) for N names.
4267 : : */
4268 : 36214 : hash_ctl.keysize = NAMEDATALEN;
4269 : 36214 : hash_ctl.entrysize = sizeof(NameHashEntry);
4270 : 36214 : hash_ctl.hcxt = CurrentMemoryContext;
4271 : 36214 : names_hash = hash_create("set_rtable_names names",
4272 : 36214 : list_length(dpns->rtable),
4273 : : &hash_ctl,
4274 : : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
4275 : :
4276 : : /* Preload the hash table with names appearing in parent_namespaces */
4277 [ + + + + : 37349 : foreach(lc, parent_namespaces)
+ + ]
4278 : : {
4279 : 1135 : deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
4280 : : ListCell *lc2;
4281 : :
4282 [ + + + + : 3907 : foreach(lc2, olddpns->rtable_names)
+ + ]
4283 : : {
4284 : 2772 : char *oldname = (char *) lfirst(lc2);
4285 : :
4286 [ + + ]: 2772 : if (oldname == NULL)
4287 : 201 : continue;
4288 : 2571 : hentry = (NameHashEntry *) hash_search(names_hash,
4289 : : oldname,
4290 : : HASH_ENTER,
4291 : : &found);
4292 : : /* we do not complain about duplicate names in parent namespaces */
4293 : 2571 : hentry->counter = 0;
4294 : : }
4295 : : }
4296 : :
4297 : : /* Now we can scan the rtable */
4298 : 36214 : rtindex = 1;
4299 [ + - + + : 105354 : foreach(lc, dpns->rtable)
+ + ]
4300 : : {
4301 : 69140 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4302 : : char *refname;
4303 : :
4304 : : /* Just in case this takes an unreasonable amount of time ... */
4305 [ + + ]: 69140 : CHECK_FOR_INTERRUPTS();
4306 : :
4307 [ + + + + ]: 69140 : if (rels_used && !bms_is_member(rtindex, rels_used))
4308 : : {
4309 : : /* Ignore unreferenced RTE */
4310 : 12411 : refname = NULL;
4311 : : }
4312 [ + + ]: 56729 : else if (rte->alias)
4313 : : {
4314 : : /* If RTE has a user-defined alias, prefer that */
4315 : 36905 : refname = rte->alias->aliasname;
4316 : : }
4317 [ + + ]: 19824 : else if (rte->rtekind == RTE_RELATION)
4318 : : {
4319 : : /* Use the current actual name of the relation */
4320 : 15120 : refname = get_rel_name(rte->relid);
4321 : : }
4322 [ + + ]: 4704 : else if (rte->rtekind == RTE_JOIN)
4323 : : {
4324 : : /* Unnamed join has no refname */
4325 : 1149 : refname = NULL;
4326 : : }
4327 : : else
4328 : : {
4329 : : /* Otherwise use whatever the parser assigned */
4330 : 3555 : refname = rte->eref->aliasname;
4331 : : }
4332 : :
4333 : : /*
4334 : : * If the selected name isn't unique, append digits to make it so, and
4335 : : * make a new hash entry for it once we've got a unique name. For a
4336 : : * very long input name, we might have to truncate to stay within
4337 : : * NAMEDATALEN.
4338 : : */
4339 [ + + ]: 69140 : if (refname)
4340 : : {
4341 : 55580 : hentry = (NameHashEntry *) hash_search(names_hash,
4342 : : refname,
4343 : : HASH_ENTER,
4344 : : &found);
4345 [ + + ]: 55580 : if (found)
4346 : : {
4347 : : /* Name already in use, must choose a new one */
4348 : 10299 : int refnamelen = strlen(refname);
4349 : 10299 : char *modname = (char *) palloc(refnamelen + 16);
4350 : : NameHashEntry *hentry2;
4351 : :
4352 : : do
4353 : : {
4354 : 10303 : hentry->counter++;
4355 : : for (;;)
4356 : : {
4357 : 10311 : memcpy(modname, refname, refnamelen);
4358 : 10311 : sprintf(modname + refnamelen, "_%d", hentry->counter);
4359 [ + + ]: 10311 : if (strlen(modname) < NAMEDATALEN)
4360 : 10303 : break;
4361 : : /* drop chars from refname to keep all the digits */
4362 : 8 : refnamelen = pg_mbcliplen(refname, refnamelen,
4363 : : refnamelen - 1);
4364 : : }
4365 : 10303 : hentry2 = (NameHashEntry *) hash_search(names_hash,
4366 : : modname,
4367 : : HASH_ENTER,
4368 : : &found);
4369 [ + + ]: 10303 : } while (found);
4370 : 10299 : hentry2->counter = 0; /* init new hash entry */
4371 : 10299 : refname = modname;
4372 : : }
4373 : : else
4374 : : {
4375 : : /* Name not previously used, need only initialize hentry */
4376 : 45281 : hentry->counter = 0;
4377 : : }
4378 : : }
4379 : :
4380 : 69140 : dpns->rtable_names = lappend(dpns->rtable_names, refname);
4381 : 69140 : rtindex++;
4382 : : }
4383 : :
4384 : 36214 : hash_destroy(names_hash);
4385 : : }
4386 : :
4387 : : /*
4388 : : * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
4389 : : *
4390 : : * For convenience, this is defined to initialize the deparse_namespace struct
4391 : : * from scratch.
4392 : : */
4393 : : static void
4394 : 3642 : set_deparse_for_query(deparse_namespace *dpns, Query *query,
4395 : : List *parent_namespaces)
4396 : : {
4397 : : ListCell *lc;
4398 : : ListCell *lc2;
4399 : :
4400 : : /* Initialize *dpns and fill rtable/ctes links */
4401 : 3642 : memset(dpns, 0, sizeof(deparse_namespace));
4402 : 3642 : dpns->rtable = query->rtable;
4403 : 3642 : dpns->subplans = NIL;
4404 : 3642 : dpns->ctes = query->cteList;
4405 : 3642 : dpns->appendrels = NULL;
4406 : 3642 : dpns->ret_old_alias = query->returningOldAlias;
4407 : 3642 : dpns->ret_new_alias = query->returningNewAlias;
4408 : :
4409 : : /* Assign a unique relation alias to each RTE */
4410 : 3642 : set_rtable_names(dpns, parent_namespaces, NULL);
4411 : :
4412 : : /* Initialize dpns->rtable_columns to contain zeroed structs */
4413 : 3642 : dpns->rtable_columns = NIL;
4414 [ + + ]: 10125 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4415 : 6483 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4416 : : palloc0(sizeof(deparse_columns)));
4417 : :
4418 : : /* If it's a utility query, it won't have a jointree */
4419 [ + + ]: 3642 : if (query->jointree)
4420 : : {
4421 : : /* Detect whether global uniqueness of USING names is needed */
4422 : 3633 : dpns->unique_using =
4423 : 3633 : has_dangerous_join_using(dpns, (Node *) query->jointree);
4424 : :
4425 : : /*
4426 : : * Select names for columns merged by USING, via a recursive pass over
4427 : : * the query jointree.
4428 : : */
4429 : 3633 : set_using_names(dpns, (Node *) query->jointree, NIL);
4430 : : }
4431 : :
4432 : : /*
4433 : : * Now assign remaining column aliases for each RTE. We do this in a
4434 : : * linear scan of the rtable, so as to process RTEs whether or not they
4435 : : * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4436 : : * etc). JOIN RTEs must be processed after their children, but this is
4437 : : * okay because they appear later in the rtable list than their children
4438 : : * (cf Asserts in identify_join_columns()).
4439 : : */
4440 [ + + + + : 10125 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
+ + + + +
+ + - +
+ ]
4441 : : {
4442 : 6483 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4443 : 6483 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4444 : :
4445 [ + + ]: 6483 : if (rte->rtekind == RTE_JOIN)
4446 : 940 : set_join_column_names(dpns, rte, colinfo);
4447 : : else
4448 : 5543 : set_relation_column_names(dpns, rte, colinfo);
4449 : : }
4450 : 3642 : }
4451 : :
4452 : : /*
4453 : : * set_simple_column_names: fill in column aliases for non-query situations
4454 : : *
4455 : : * This handles EXPLAIN and cases where we only have relation RTEs. Without
4456 : : * a join tree, we can't do anything smart about join RTEs, but we don't
4457 : : * need to, because EXPLAIN should never see join alias Vars anyway.
4458 : : * If we find a join RTE we'll just skip it, leaving its deparse_columns
4459 : : * struct all-zero. If somehow we try to deparse a join alias Var, we'll
4460 : : * error out cleanly because the struct's num_cols will be zero.
4461 : : */
4462 : : static void
4463 : 32924 : set_simple_column_names(deparse_namespace *dpns)
4464 : : {
4465 : : ListCell *lc;
4466 : : ListCell *lc2;
4467 : :
4468 : : /* Initialize dpns->rtable_columns to contain zeroed structs */
4469 : 32924 : dpns->rtable_columns = NIL;
4470 [ + + ]: 95581 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4471 : 62657 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4472 : : palloc0(sizeof(deparse_columns)));
4473 : :
4474 : : /* Assign unique column aliases within each non-join RTE */
4475 [ + - + + : 95581 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
+ - + + +
+ + - +
+ ]
4476 : : {
4477 : 62657 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4478 : 62657 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4479 : :
4480 [ + + ]: 62657 : if (rte->rtekind != RTE_JOIN)
4481 : 58633 : set_relation_column_names(dpns, rte, colinfo);
4482 : : }
4483 : 32924 : }
4484 : :
4485 : : /*
4486 : : * has_dangerous_join_using: search jointree for unnamed JOIN USING
4487 : : *
4488 : : * Merged columns of a JOIN USING may act differently from either of the input
4489 : : * columns, either because they are merged with COALESCE (in a FULL JOIN) or
4490 : : * because an implicit coercion of the underlying input column is required.
4491 : : * In such a case the column must be referenced as a column of the JOIN not as
4492 : : * a column of either input. And this is problematic if the join is unnamed
4493 : : * (alias-less): we cannot qualify the column's name with an RTE name, since
4494 : : * there is none. (Forcibly assigning an alias to the join is not a solution,
4495 : : * since that will prevent legal references to tables below the join.)
4496 : : * To ensure that every column in the query is unambiguously referenceable,
4497 : : * we must assign such merged columns names that are globally unique across
4498 : : * the whole query, aliasing other columns out of the way as necessary.
4499 : : *
4500 : : * Because the ensuing re-aliasing is fairly damaging to the readability of
4501 : : * the query, we don't do this unless we have to. So, we must pre-scan
4502 : : * the join tree to see if we have to, before starting set_using_names().
4503 : : */
4504 : : static bool
4505 : 8615 : has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
4506 : : {
4507 [ + + ]: 8615 : if (IsA(jtnode, RangeTblRef))
4508 : : {
4509 : : /* nothing to do here */
4510 : : }
4511 [ + + ]: 4529 : else if (IsA(jtnode, FromExpr))
4512 : : {
4513 : 3633 : FromExpr *f = (FromExpr *) jtnode;
4514 : : ListCell *lc;
4515 : :
4516 [ + + + + : 6875 : foreach(lc, f->fromlist)
+ + ]
4517 : : {
4518 [ + + ]: 3294 : if (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))
4519 : 52 : return true;
4520 : : }
4521 : : }
4522 [ + - ]: 896 : else if (IsA(jtnode, JoinExpr))
4523 : : {
4524 : 896 : JoinExpr *j = (JoinExpr *) jtnode;
4525 : :
4526 : : /* Is it an unnamed JOIN with USING? */
4527 [ + + + + ]: 896 : if (j->alias == NULL && j->usingClause)
4528 : : {
4529 : : /*
4530 : : * Yes, so check each join alias var to see if any of them are not
4531 : : * simple references to underlying columns. If so, we have a
4532 : : * dangerous situation and must pick unique aliases.
4533 : : */
4534 : 188 : RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4535 : :
4536 : : /* We need only examine the merged columns */
4537 [ + + ]: 388 : for (int i = 0; i < jrte->joinmergedcols; i++)
4538 : : {
4539 : 252 : Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4540 : :
4541 [ + + ]: 252 : if (!IsA(aliasvar, Var))
4542 : 52 : return true;
4543 : : }
4544 : : }
4545 : :
4546 : : /* Nope, but inspect children */
4547 [ - + ]: 844 : if (has_dangerous_join_using(dpns, j->larg))
4548 : 0 : return true;
4549 [ - + ]: 844 : if (has_dangerous_join_using(dpns, j->rarg))
4550 : 0 : return true;
4551 : : }
4552 : : else
4553 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
4554 : : (int) nodeTag(jtnode));
4555 : 8511 : return false;
4556 : : }
4557 : :
4558 : : /*
4559 : : * set_using_names: select column aliases to be used for merged USING columns
4560 : : *
4561 : : * We do this during a recursive descent of the query jointree.
4562 : : * dpns->unique_using must already be set to determine the global strategy.
4563 : : *
4564 : : * Column alias info is saved in the dpns->rtable_columns list, which is
4565 : : * assumed to be filled with pre-zeroed deparse_columns structs.
4566 : : *
4567 : : * parentUsing is a list of all USING aliases assigned in parent joins of
4568 : : * the current jointree node. (The passed-in list must not be modified.)
4569 : : *
4570 : : * Note that we do not use per-deparse_columns hash tables in this function.
4571 : : * The number of names that need to be assigned should be small enough that
4572 : : * we don't need to trouble with that.
4573 : : */
4574 : : static void
4575 : 8835 : set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
4576 : : {
4577 [ + + ]: 8835 : if (IsA(jtnode, RangeTblRef))
4578 : : {
4579 : : /* nothing to do now */
4580 : : }
4581 [ + + ]: 4573 : else if (IsA(jtnode, FromExpr))
4582 : : {
4583 : 3633 : FromExpr *f = (FromExpr *) jtnode;
4584 : : ListCell *lc;
4585 : :
4586 [ + + + + : 6955 : foreach(lc, f->fromlist)
+ + ]
4587 : 3322 : set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4588 : : }
4589 [ + - ]: 940 : else if (IsA(jtnode, JoinExpr))
4590 : : {
4591 : 940 : JoinExpr *j = (JoinExpr *) jtnode;
4592 : 940 : RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4593 : 940 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
4594 : : int *leftattnos;
4595 : : int *rightattnos;
4596 : : deparse_columns *leftcolinfo;
4597 : : deparse_columns *rightcolinfo;
4598 : : int i;
4599 : : ListCell *lc;
4600 : :
4601 : : /* Get info about the shape of the join */
4602 : 940 : identify_join_columns(j, rte, colinfo);
4603 : 940 : leftattnos = colinfo->leftattnos;
4604 : 940 : rightattnos = colinfo->rightattnos;
4605 : :
4606 : : /* Look up the not-yet-filled-in child deparse_columns structs */
4607 : 940 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4608 : 940 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4609 : :
4610 : : /*
4611 : : * If this join is unnamed, then we cannot substitute new aliases at
4612 : : * this level, so any name requirements pushed down to here must be
4613 : : * pushed down again to the children.
4614 : : */
4615 [ + + ]: 940 : if (rte->alias == NULL)
4616 : : {
4617 [ + + ]: 960 : for (i = 0; i < colinfo->num_cols; i++)
4618 : : {
4619 : 92 : char *colname = colinfo->colnames[i];
4620 : :
4621 [ + + ]: 92 : if (colname == NULL)
4622 : 16 : continue;
4623 : :
4624 : : /* Push down to left column, unless it's a system column */
4625 [ + + ]: 76 : if (leftattnos[i] > 0)
4626 : : {
4627 : 68 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4628 : 68 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4629 : : }
4630 : :
4631 : : /* Same on the righthand side */
4632 [ + - ]: 76 : if (rightattnos[i] > 0)
4633 : : {
4634 : 76 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4635 : 76 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4636 : : }
4637 : : }
4638 : : }
4639 : :
4640 : : /*
4641 : : * If there's a USING clause, select the USING column names and push
4642 : : * those names down to the children. We have two strategies:
4643 : : *
4644 : : * If dpns->unique_using is true, we force all USING names to be
4645 : : * unique across the whole query level. In principle we'd only need
4646 : : * the names of dangerous USING columns to be globally unique, but to
4647 : : * safely assign all USING names in a single pass, we have to enforce
4648 : : * the same uniqueness rule for all of them. However, if a USING
4649 : : * column's name has been pushed down from the parent, we should use
4650 : : * it as-is rather than making a uniqueness adjustment. This is
4651 : : * necessary when we're at an unnamed join, and it creates no risk of
4652 : : * ambiguity. Also, if there's a user-written output alias for a
4653 : : * merged column, we prefer to use that rather than the input name;
4654 : : * this simplifies the logic and seems likely to lead to less aliasing
4655 : : * overall.
4656 : : *
4657 : : * If dpns->unique_using is false, we only need USING names to be
4658 : : * unique within their own join RTE. We still need to honor
4659 : : * pushed-down names, though.
4660 : : *
4661 : : * Though significantly different in results, these two strategies are
4662 : : * implemented by the same code, with only the difference of whether
4663 : : * to put assigned names into dpns->using_names.
4664 : : */
4665 [ + + ]: 940 : if (j->usingClause)
4666 : : {
4667 : : /* Copy the input parentUsing list so we don't modify it */
4668 : 280 : parentUsing = list_copy(parentUsing);
4669 : :
4670 : : /* USING names must correspond to the first join output columns */
4671 : 280 : expand_colnames_array_to(colinfo, list_length(j->usingClause));
4672 : 280 : i = 0;
4673 [ + - + + : 664 : foreach(lc, j->usingClause)
+ + ]
4674 : : {
4675 : 384 : char *colname = strVal(lfirst(lc));
4676 : :
4677 : : /* Assert it's a merged column */
4678 : : Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4679 : :
4680 : : /* Adopt passed-down name if any, else select unique name */
4681 [ + + ]: 384 : if (colinfo->colnames[i] != NULL)
4682 : 68 : colname = colinfo->colnames[i];
4683 : : else
4684 : : {
4685 : : /* Prefer user-written output alias if any */
4686 [ + + - + ]: 316 : if (rte->alias && i < list_length(rte->alias->colnames))
4687 : 0 : colname = strVal(list_nth(rte->alias->colnames, i));
4688 : : /* Make it appropriately unique */
4689 : 316 : colname = make_colname_unique(colname, dpns, colinfo);
4690 [ + + ]: 316 : if (dpns->unique_using)
4691 : 88 : dpns->using_names = lappend(dpns->using_names,
4692 : : colname);
4693 : : /* Save it as output column name, too */
4694 : 316 : colinfo->colnames[i] = colname;
4695 : : }
4696 : :
4697 : : /* Remember selected names for use later */
4698 : 384 : colinfo->usingNames = lappend(colinfo->usingNames, colname);
4699 : 384 : parentUsing = lappend(parentUsing, colname);
4700 : :
4701 : : /* Push down to left column, unless it's a system column */
4702 [ + - ]: 384 : if (leftattnos[i] > 0)
4703 : : {
4704 : 384 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4705 : 384 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4706 : : }
4707 : :
4708 : : /* Same on the righthand side */
4709 [ + - ]: 384 : if (rightattnos[i] > 0)
4710 : : {
4711 : 384 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4712 : 384 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4713 : : }
4714 : :
4715 : 384 : i++;
4716 : : }
4717 : : }
4718 : :
4719 : : /* Mark child deparse_columns structs with correct parentUsing info */
4720 : 940 : leftcolinfo->parentUsing = parentUsing;
4721 : 940 : rightcolinfo->parentUsing = parentUsing;
4722 : :
4723 : : /* Now recursively assign USING column names in children */
4724 : 940 : set_using_names(dpns, j->larg, parentUsing);
4725 : 940 : set_using_names(dpns, j->rarg, parentUsing);
4726 : : }
4727 : : else
4728 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
4729 : : (int) nodeTag(jtnode));
4730 : 8835 : }
4731 : :
4732 : : /*
4733 : : * set_relation_column_names: select column aliases for a non-join RTE
4734 : : *
4735 : : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4736 : : * If any colnames entries are already filled in, those override local
4737 : : * choices.
4738 : : */
4739 : : static void
4740 : 64176 : set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4741 : : deparse_columns *colinfo)
4742 : : {
4743 : : int ncolumns;
4744 : : char **real_colnames;
4745 : : bool changed_any;
4746 : : int noldcolumns;
4747 : : int i;
4748 : : int j;
4749 : :
4750 : : /*
4751 : : * Construct an array of the current "real" column names of the RTE.
4752 : : * real_colnames[] will be indexed by physical column number, with NULL
4753 : : * entries for dropped columns.
4754 : : */
4755 [ + + ]: 64176 : if (rte->rtekind == RTE_RELATION)
4756 : : {
4757 : : /* Relation --- look to the system catalogs for up-to-date info */
4758 : : Relation rel;
4759 : : TupleDesc tupdesc;
4760 : :
4761 : 53976 : rel = relation_open(rte->relid, AccessShareLock);
4762 : 53976 : tupdesc = RelationGetDescr(rel);
4763 : :
4764 : 53976 : ncolumns = tupdesc->natts;
4765 : 53976 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4766 : :
4767 [ + + ]: 337031 : for (i = 0; i < ncolumns; i++)
4768 : : {
4769 : 283055 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4770 : :
4771 [ + + ]: 283055 : if (attr->attisdropped)
4772 : 1908 : real_colnames[i] = NULL;
4773 : : else
4774 : 281147 : real_colnames[i] = pstrdup(NameStr(attr->attname));
4775 : : }
4776 : 53976 : relation_close(rel, AccessShareLock);
4777 : : }
4778 : : else
4779 : : {
4780 : : /* Otherwise get the column names from eref or expandRTE() */
4781 : : List *colnames;
4782 : : ListCell *lc;
4783 : :
4784 : : /*
4785 : : * Functions returning composites have the annoying property that some
4786 : : * of the composite type's columns might have been dropped since the
4787 : : * query was parsed. If possible, use expandRTE() to handle that
4788 : : * case, since it has the tedious logic needed to find out about
4789 : : * dropped columns. However, if we're explaining a plan, then we
4790 : : * don't have rte->functions because the planner thinks that won't be
4791 : : * needed later, and that breaks expandRTE(). So in that case we have
4792 : : * to rely on rte->eref, which may lead us to report a dropped
4793 : : * column's old name; that seems close enough for EXPLAIN's purposes.
4794 : : *
4795 : : * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4796 : : * which should be sufficiently up-to-date: no other RTE types can
4797 : : * have columns get dropped from under them after parsing.
4798 : : */
4799 [ + + + + ]: 10200 : if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4800 : : {
4801 : : /* Since we're not creating Vars, rtindex etc. don't matter */
4802 : 560 : expandRTE(rte, 1, 0, VAR_RETURNING_DEFAULT, -1,
4803 : : true /* include dropped */ , &colnames, NULL);
4804 : : }
4805 : : else
4806 : 9640 : colnames = rte->eref->colnames;
4807 : :
4808 : 10200 : ncolumns = list_length(colnames);
4809 : 10200 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4810 : :
4811 : 10200 : i = 0;
4812 [ + + + + : 32802 : foreach(lc, colnames)
+ + ]
4813 : : {
4814 : : /*
4815 : : * If the column name we find here is an empty string, then it's a
4816 : : * dropped column, so change to NULL.
4817 : : */
4818 : 22602 : char *cname = strVal(lfirst(lc));
4819 : :
4820 [ + + ]: 22602 : if (cname[0] == '\0')
4821 : 36 : cname = NULL;
4822 : 22602 : real_colnames[i] = cname;
4823 : 22602 : i++;
4824 : : }
4825 : : }
4826 : :
4827 : : /*
4828 : : * Ensure colinfo->colnames has a slot for each column. (It could be long
4829 : : * enough already, if we pushed down a name for the last column.) Note:
4830 : : * it's possible that there are now more columns than there were when the
4831 : : * query was parsed, ie colnames could be longer than rte->eref->colnames.
4832 : : * We must assign unique aliases to the new columns too, else there could
4833 : : * be unresolved conflicts when the view/rule is reloaded.
4834 : : */
4835 : 64176 : expand_colnames_array_to(colinfo, ncolumns);
4836 : : Assert(colinfo->num_cols == ncolumns);
4837 : :
4838 : : /*
4839 : : * Make sufficiently large new_colnames and is_new_col arrays, too.
4840 : : *
4841 : : * Note: because we leave colinfo->num_new_cols zero until after the loop,
4842 : : * colname_is_unique will not consult that array, which is fine because it
4843 : : * would only be duplicate effort.
4844 : : */
4845 : 64176 : colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4846 : 64176 : colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4847 : :
4848 : : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4849 : 64176 : build_colinfo_names_hash(colinfo);
4850 : :
4851 : : /*
4852 : : * Scan the columns, select a unique alias for each one, and store it in
4853 : : * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4854 : : * entries for dropped columns, the latter omits them. Also mark
4855 : : * new_colnames entries as to whether they are new since parse time; this
4856 : : * is the case for entries beyond the length of rte->eref->colnames.
4857 : : */
4858 : 64176 : noldcolumns = list_length(rte->eref->colnames);
4859 : 64176 : changed_any = false;
4860 : 64176 : j = 0;
4861 [ + + ]: 369833 : for (i = 0; i < ncolumns; i++)
4862 : : {
4863 : 305657 : char *real_colname = real_colnames[i];
4864 : 305657 : char *colname = colinfo->colnames[i];
4865 : :
4866 : : /* Skip dropped columns */
4867 [ + + ]: 305657 : if (real_colname == NULL)
4868 : : {
4869 : : Assert(colname == NULL); /* colnames[i] is already NULL */
4870 : 1944 : continue;
4871 : : }
4872 : :
4873 : : /* If alias already assigned, that's what to use */
4874 [ + + ]: 303713 : if (colname == NULL)
4875 : : {
4876 : : /* If user wrote an alias, prefer that over real column name */
4877 [ + + + + ]: 303013 : if (rte->alias && i < list_length(rte->alias->colnames))
4878 : 30060 : colname = strVal(list_nth(rte->alias->colnames, i));
4879 : : else
4880 : 272953 : colname = real_colname;
4881 : :
4882 : : /* Unique-ify and insert into colinfo */
4883 : 303013 : colname = make_colname_unique(colname, dpns, colinfo);
4884 : :
4885 : 303013 : colinfo->colnames[i] = colname;
4886 : 303013 : add_to_names_hash(colinfo, colname);
4887 : : }
4888 : :
4889 : : /* Put names of non-dropped columns in new_colnames[] too */
4890 : 303713 : colinfo->new_colnames[j] = colname;
4891 : : /* And mark them as new or not */
4892 : 303713 : colinfo->is_new_col[j] = (i >= noldcolumns);
4893 : 303713 : j++;
4894 : :
4895 : : /* Remember if any assigned aliases differ from "real" name */
4896 [ + + + + ]: 303713 : if (!changed_any && strcmp(colname, real_colname) != 0)
4897 : 793 : changed_any = true;
4898 : : }
4899 : :
4900 : : /* We're now done needing the colinfo's names_hash */
4901 : 64176 : destroy_colinfo_names_hash(colinfo);
4902 : :
4903 : : /*
4904 : : * Set correct length for new_colnames[] array. (Note: if columns have
4905 : : * been added, colinfo->num_cols includes them, which is not really quite
4906 : : * right but is harmless, since any new columns must be at the end where
4907 : : * they won't affect varattnos of pre-existing columns.)
4908 : : */
4909 : 64176 : colinfo->num_new_cols = j;
4910 : :
4911 : : /*
4912 : : * For a relation RTE, we need only print the alias column names if any
4913 : : * are different from the underlying "real" names. For a function RTE,
4914 : : * always emit a complete column alias list; this is to protect against
4915 : : * possible instability of the default column names (eg, from altering
4916 : : * parameter names). For tablefunc RTEs, we never print aliases, because
4917 : : * the column names are part of the clause itself. For other RTE types,
4918 : : * print if we changed anything OR if there were user-written column
4919 : : * aliases (since the latter would be part of the underlying "reality").
4920 : : */
4921 [ + + ]: 64176 : if (rte->rtekind == RTE_RELATION)
4922 : 53976 : colinfo->printaliases = changed_any;
4923 [ + + ]: 10200 : else if (rte->rtekind == RTE_FUNCTION)
4924 : 988 : colinfo->printaliases = true;
4925 [ + + ]: 9212 : else if (rte->rtekind == RTE_TABLEFUNC)
4926 : 126 : colinfo->printaliases = false;
4927 [ + + + + ]: 9086 : else if (rte->alias && rte->alias->colnames != NIL)
4928 : 536 : colinfo->printaliases = true;
4929 : : else
4930 : 8550 : colinfo->printaliases = changed_any;
4931 : 64176 : }
4932 : :
4933 : : /*
4934 : : * set_join_column_names: select column aliases for a join RTE
4935 : : *
4936 : : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4937 : : * If any colnames entries are already filled in, those override local
4938 : : * choices. Also, names for USING columns were already chosen by
4939 : : * set_using_names(). We further expect that column alias selection has been
4940 : : * completed for both input RTEs.
4941 : : */
4942 : : static void
4943 : 940 : set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4944 : : deparse_columns *colinfo)
4945 : : {
4946 : : deparse_columns *leftcolinfo;
4947 : : deparse_columns *rightcolinfo;
4948 : : bool changed_any;
4949 : : int noldcolumns;
4950 : : int nnewcolumns;
4951 : 940 : Bitmapset *leftmerged = NULL;
4952 : 940 : Bitmapset *rightmerged = NULL;
4953 : : int i;
4954 : : int j;
4955 : : int ic;
4956 : : int jc;
4957 : :
4958 : : /* Look up the previously-filled-in child deparse_columns structs */
4959 : 940 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4960 : 940 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4961 : :
4962 : : /*
4963 : : * Ensure colinfo->colnames has a slot for each column. (It could be long
4964 : : * enough already, if we pushed down a name for the last column.) Note:
4965 : : * it's possible that one or both inputs now have more columns than there
4966 : : * were when the query was parsed, but we'll deal with that below. We
4967 : : * only need entries in colnames for pre-existing columns.
4968 : : */
4969 : 940 : noldcolumns = list_length(rte->eref->colnames);
4970 : 940 : expand_colnames_array_to(colinfo, noldcolumns);
4971 : : Assert(colinfo->num_cols == noldcolumns);
4972 : :
4973 : : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4974 : 940 : build_colinfo_names_hash(colinfo);
4975 : :
4976 : : /*
4977 : : * Scan the join output columns, select an alias for each one, and store
4978 : : * it in colinfo->colnames. If there are USING columns, set_using_names()
4979 : : * already selected their names, so we can start the loop at the first
4980 : : * non-merged column.
4981 : : */
4982 : 940 : changed_any = false;
4983 [ + + ]: 30521 : for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4984 : : {
4985 : 29581 : char *colname = colinfo->colnames[i];
4986 : : char *real_colname;
4987 : :
4988 : : /* Join column must refer to at least one input column */
4989 : : Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4990 : :
4991 : : /* Get the child column name */
4992 [ + + ]: 29581 : if (colinfo->leftattnos[i] > 0)
4993 : 20735 : real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4994 [ + - ]: 8846 : else if (colinfo->rightattnos[i] > 0)
4995 : 8846 : real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4996 : : else
4997 : : {
4998 : : /* We're joining system columns --- use eref name */
4999 : 0 : real_colname = strVal(list_nth(rte->eref->colnames, i));
5000 : : }
5001 : :
5002 : : /* If child col has been dropped, no need to assign a join colname */
5003 [ + + ]: 29581 : if (real_colname == NULL)
5004 : : {
5005 : 4 : colinfo->colnames[i] = NULL;
5006 : 4 : continue;
5007 : : }
5008 : :
5009 : : /* In an unnamed join, just report child column names as-is */
5010 [ + + ]: 29577 : if (rte->alias == NULL)
5011 : : {
5012 : 29325 : colinfo->colnames[i] = real_colname;
5013 : 29325 : add_to_names_hash(colinfo, real_colname);
5014 : 29325 : continue;
5015 : : }
5016 : :
5017 : : /* If alias already assigned, that's what to use */
5018 [ + - ]: 252 : if (colname == NULL)
5019 : : {
5020 : : /* If user wrote an alias, prefer that over real column name */
5021 [ + - + + ]: 252 : if (rte->alias && i < list_length(rte->alias->colnames))
5022 : 64 : colname = strVal(list_nth(rte->alias->colnames, i));
5023 : : else
5024 : 188 : colname = real_colname;
5025 : :
5026 : : /* Unique-ify and insert into colinfo */
5027 : 252 : colname = make_colname_unique(colname, dpns, colinfo);
5028 : :
5029 : 252 : colinfo->colnames[i] = colname;
5030 : 252 : add_to_names_hash(colinfo, colname);
5031 : : }
5032 : :
5033 : : /* Remember if any assigned aliases differ from "real" name */
5034 [ + + + + ]: 252 : if (!changed_any && strcmp(colname, real_colname) != 0)
5035 : 16 : changed_any = true;
5036 : : }
5037 : :
5038 : : /*
5039 : : * Calculate number of columns the join would have if it were re-parsed
5040 : : * now, and create storage for the new_colnames and is_new_col arrays.
5041 : : *
5042 : : * Note: colname_is_unique will be consulting new_colnames[] during the
5043 : : * loops below, so its not-yet-filled entries must be zeroes.
5044 : : */
5045 : 1880 : nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
5046 : 940 : list_length(colinfo->usingNames);
5047 : 940 : colinfo->num_new_cols = nnewcolumns;
5048 : 940 : colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
5049 : 940 : colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
5050 : :
5051 : : /*
5052 : : * Generating the new_colnames array is a bit tricky since any new columns
5053 : : * added since parse time must be inserted in the right places. This code
5054 : : * must match the parser, which will order a join's columns as merged
5055 : : * columns first (in USING-clause order), then non-merged columns from the
5056 : : * left input (in attnum order), then non-merged columns from the right
5057 : : * input (ditto). If one of the inputs is itself a join, its columns will
5058 : : * be ordered according to the same rule, which means newly-added columns
5059 : : * might not be at the end. We can figure out what's what by consulting
5060 : : * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
5061 : : *
5062 : : * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
5063 : : * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
5064 : : * meanings for the current child RTE.
5065 : : */
5066 : :
5067 : : /* Handle merged columns; they are first and can't be new */
5068 : 940 : i = j = 0;
5069 : 940 : while (i < noldcolumns &&
5070 [ + - + - ]: 1324 : colinfo->leftattnos[i] != 0 &&
5071 [ + + ]: 1324 : colinfo->rightattnos[i] != 0)
5072 : : {
5073 : : /* column name is already determined and known unique */
5074 : 384 : colinfo->new_colnames[j] = colinfo->colnames[i];
5075 : 384 : colinfo->is_new_col[j] = false;
5076 : :
5077 : : /* build bitmapsets of child attnums of merged columns */
5078 [ + - ]: 384 : if (colinfo->leftattnos[i] > 0)
5079 : 384 : leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
5080 [ + - ]: 384 : if (colinfo->rightattnos[i] > 0)
5081 : 384 : rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);
5082 : :
5083 : 384 : i++, j++;
5084 : : }
5085 : :
5086 : : /* Handle non-merged left-child columns */
5087 : 940 : ic = 0;
5088 [ + + ]: 22383 : for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
5089 : : {
5090 : 21443 : char *child_colname = leftcolinfo->new_colnames[jc];
5091 : :
5092 [ + + ]: 21443 : if (!leftcolinfo->is_new_col[jc])
5093 : : {
5094 : : /* Advance ic to next non-dropped old column of left child */
5095 [ + - ]: 21171 : while (ic < leftcolinfo->num_cols &&
5096 [ + + ]: 21171 : leftcolinfo->colnames[ic] == NULL)
5097 : 56 : ic++;
5098 : : Assert(ic < leftcolinfo->num_cols);
5099 : 21115 : ic++;
5100 : : /* If it is a merged column, we already processed it */
5101 [ + + ]: 21115 : if (bms_is_member(ic, leftmerged))
5102 : 384 : continue;
5103 : : /* Else, advance i to the corresponding existing join column */
5104 [ + - ]: 20735 : while (i < colinfo->num_cols &&
5105 [ + + ]: 20735 : colinfo->colnames[i] == NULL)
5106 : 4 : i++;
5107 : : Assert(i < colinfo->num_cols);
5108 : : Assert(ic == colinfo->leftattnos[i]);
5109 : : /* Use the already-assigned name of this column */
5110 : 20731 : colinfo->new_colnames[j] = colinfo->colnames[i];
5111 : 20731 : i++;
5112 : : }
5113 : : else
5114 : : {
5115 : : /*
5116 : : * Unique-ify the new child column name and assign, unless we're
5117 : : * in an unnamed join, in which case just copy
5118 : : */
5119 [ + + ]: 328 : if (rte->alias != NULL)
5120 : : {
5121 : 176 : colinfo->new_colnames[j] =
5122 : 88 : make_colname_unique(child_colname, dpns, colinfo);
5123 [ + + ]: 88 : if (!changed_any &&
5124 [ + + ]: 72 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
5125 : 8 : changed_any = true;
5126 : : }
5127 : : else
5128 : 240 : colinfo->new_colnames[j] = child_colname;
5129 : 328 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
5130 : : }
5131 : :
5132 : 21059 : colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
5133 : 21059 : j++;
5134 : : }
5135 : :
5136 : : /* Handle non-merged right-child columns in exactly the same way */
5137 : 940 : ic = 0;
5138 [ + + ]: 10282 : for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
5139 : : {
5140 : 9342 : char *child_colname = rightcolinfo->new_colnames[jc];
5141 : :
5142 [ + + ]: 9342 : if (!rightcolinfo->is_new_col[jc])
5143 : : {
5144 : : /* Advance ic to next non-dropped old column of right child */
5145 [ + - ]: 9230 : while (ic < rightcolinfo->num_cols &&
5146 [ - + ]: 9230 : rightcolinfo->colnames[ic] == NULL)
5147 : 0 : ic++;
5148 : : Assert(ic < rightcolinfo->num_cols);
5149 : 9230 : ic++;
5150 : : /* If it is a merged column, we already processed it */
5151 [ + + ]: 9230 : if (bms_is_member(ic, rightmerged))
5152 : 384 : continue;
5153 : : /* Else, advance i to the corresponding existing join column */
5154 [ + - ]: 8846 : while (i < colinfo->num_cols &&
5155 [ - + ]: 8846 : colinfo->colnames[i] == NULL)
5156 : 0 : i++;
5157 : : Assert(i < colinfo->num_cols);
5158 : : Assert(ic == colinfo->rightattnos[i]);
5159 : : /* Use the already-assigned name of this column */
5160 : 8846 : colinfo->new_colnames[j] = colinfo->colnames[i];
5161 : 8846 : i++;
5162 : : }
5163 : : else
5164 : : {
5165 : : /*
5166 : : * Unique-ify the new child column name and assign, unless we're
5167 : : * in an unnamed join, in which case just copy
5168 : : */
5169 [ + + ]: 112 : if (rte->alias != NULL)
5170 : : {
5171 : 32 : colinfo->new_colnames[j] =
5172 : 16 : make_colname_unique(child_colname, dpns, colinfo);
5173 [ + - ]: 16 : if (!changed_any &&
5174 [ + + ]: 16 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
5175 : 8 : changed_any = true;
5176 : : }
5177 : : else
5178 : 96 : colinfo->new_colnames[j] = child_colname;
5179 : 112 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
5180 : : }
5181 : :
5182 : 8958 : colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
5183 : 8958 : j++;
5184 : : }
5185 : :
5186 : : /* Assert we processed the right number of columns */
5187 : : #ifdef USE_ASSERT_CHECKING
5188 : : while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
5189 : : i++;
5190 : : Assert(i == colinfo->num_cols);
5191 : : Assert(j == nnewcolumns);
5192 : : #endif
5193 : :
5194 : : /* We're now done needing the colinfo's names_hash */
5195 : 940 : destroy_colinfo_names_hash(colinfo);
5196 : :
5197 : : /*
5198 : : * For a named join, print column aliases if we changed any from the child
5199 : : * names. Unnamed joins cannot print aliases.
5200 : : */
5201 [ + + ]: 940 : if (rte->alias != NULL)
5202 : 72 : colinfo->printaliases = changed_any;
5203 : : else
5204 : 868 : colinfo->printaliases = false;
5205 : 940 : }
5206 : :
5207 : : /*
5208 : : * colname_is_unique: is colname distinct from already-chosen column names?
5209 : : *
5210 : : * dpns is query-wide info, colinfo is for the column's RTE
5211 : : */
5212 : : static bool
5213 : 305252 : colname_is_unique(const char *colname, deparse_namespace *dpns,
5214 : : deparse_columns *colinfo)
5215 : : {
5216 : : int i;
5217 : : ListCell *lc;
5218 : :
5219 : : /*
5220 : : * If we have a hash table, consult that instead of linearly scanning the
5221 : : * colinfo's strings.
5222 : : */
5223 [ + + ]: 305252 : if (colinfo->names_hash)
5224 : : {
5225 [ - + ]: 10726 : if (hash_search(colinfo->names_hash,
5226 : : colname,
5227 : : HASH_FIND,
5228 : : NULL) != NULL)
5229 : 0 : return false;
5230 : : }
5231 : : else
5232 : : {
5233 : : /* Check against already-assigned column aliases within RTE */
5234 [ + + ]: 4043872 : for (i = 0; i < colinfo->num_cols; i++)
5235 : : {
5236 : 3750865 : char *oldname = colinfo->colnames[i];
5237 : :
5238 [ + + + + ]: 3750865 : if (oldname && strcmp(oldname, colname) == 0)
5239 : 1519 : return false;
5240 : : }
5241 : :
5242 : : /*
5243 : : * If we're building a new_colnames array, check that too (this will
5244 : : * be partially but not completely redundant with the previous checks)
5245 : : */
5246 [ + + ]: 293855 : for (i = 0; i < colinfo->num_new_cols; i++)
5247 : : {
5248 : 864 : char *oldname = colinfo->new_colnames[i];
5249 : :
5250 [ + + + + ]: 864 : if (oldname && strcmp(oldname, colname) == 0)
5251 : 16 : return false;
5252 : : }
5253 : :
5254 : : /*
5255 : : * Also check against names already assigned for parent-join USING
5256 : : * cols
5257 : : */
5258 [ + + + + : 294687 : foreach(lc, colinfo->parentUsing)
+ + ]
5259 : : {
5260 : 1700 : char *oldname = (char *) lfirst(lc);
5261 : :
5262 [ + + ]: 1700 : if (strcmp(oldname, colname) == 0)
5263 : 4 : return false;
5264 : : }
5265 : : }
5266 : :
5267 : : /*
5268 : : * Also check against USING-column names that must be globally unique.
5269 : : * These are not hashed, but there should be few of them.
5270 : : */
5271 [ + + + + : 304285 : foreach(lc, dpns->using_names)
+ + ]
5272 : : {
5273 : 600 : char *oldname = (char *) lfirst(lc);
5274 : :
5275 [ + + ]: 600 : if (strcmp(oldname, colname) == 0)
5276 : 28 : return false;
5277 : : }
5278 : :
5279 : 303685 : return true;
5280 : : }
5281 : :
5282 : : /*
5283 : : * make_colname_unique: modify colname if necessary to make it unique
5284 : : *
5285 : : * dpns is query-wide info, colinfo is for the column's RTE
5286 : : */
5287 : : static char *
5288 : 303685 : make_colname_unique(char *colname, deparse_namespace *dpns,
5289 : : deparse_columns *colinfo)
5290 : : {
5291 : : /*
5292 : : * If the selected name isn't unique, append digits to make it so. For a
5293 : : * very long input name, we might have to truncate to stay within
5294 : : * NAMEDATALEN.
5295 : : */
5296 [ + + ]: 303685 : if (!colname_is_unique(colname, dpns, colinfo))
5297 : : {
5298 : 1086 : int colnamelen = strlen(colname);
5299 : 1086 : char *modname = (char *) palloc(colnamelen + 16);
5300 : 1086 : int i = 0;
5301 : :
5302 : : do
5303 : : {
5304 : 1567 : i++;
5305 : : for (;;)
5306 : : {
5307 : 1567 : memcpy(modname, colname, colnamelen);
5308 : 1567 : sprintf(modname + colnamelen, "_%d", i);
5309 [ + - ]: 1567 : if (strlen(modname) < NAMEDATALEN)
5310 : 1567 : break;
5311 : : /* drop chars from colname to keep all the digits */
5312 : 0 : colnamelen = pg_mbcliplen(colname, colnamelen,
5313 : : colnamelen - 1);
5314 : : }
5315 [ + + ]: 1567 : } while (!colname_is_unique(modname, dpns, colinfo));
5316 : 1086 : colname = modname;
5317 : : }
5318 : 303685 : return colname;
5319 : : }
5320 : :
5321 : : /*
5322 : : * expand_colnames_array_to: make colinfo->colnames at least n items long
5323 : : *
5324 : : * Any added array entries are initialized to zero.
5325 : : */
5326 : : static void
5327 : 66308 : expand_colnames_array_to(deparse_columns *colinfo, int n)
5328 : : {
5329 [ + + ]: 66308 : if (n > colinfo->num_cols)
5330 : : {
5331 [ + + ]: 64512 : if (colinfo->colnames == NULL)
5332 : 63576 : colinfo->colnames = palloc0_array(char *, n);
5333 : : else
5334 : 936 : colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
5335 : 64512 : colinfo->num_cols = n;
5336 : : }
5337 : 66308 : }
5338 : :
5339 : : /*
5340 : : * build_colinfo_names_hash: optionally construct a hash table for colinfo
5341 : : */
5342 : : static void
5343 : 65116 : build_colinfo_names_hash(deparse_columns *colinfo)
5344 : : {
5345 : : HASHCTL hash_ctl;
5346 : : int i;
5347 : : ListCell *lc;
5348 : :
5349 : : /*
5350 : : * Use a hash table only for RTEs with at least 32 columns. (The cutoff
5351 : : * is somewhat arbitrary, but let's choose it so that this code does get
5352 : : * exercised in the regression tests.)
5353 : : */
5354 [ + + ]: 65116 : if (colinfo->num_cols < 32)
5355 : 64300 : return;
5356 : :
5357 : : /*
5358 : : * Set up the hash table. The entries are just strings with no other
5359 : : * payload.
5360 : : */
5361 : 816 : hash_ctl.keysize = NAMEDATALEN;
5362 : 816 : hash_ctl.entrysize = NAMEDATALEN;
5363 : 816 : hash_ctl.hcxt = CurrentMemoryContext;
5364 : 1632 : colinfo->names_hash = hash_create("deparse_columns names",
5365 : 816 : colinfo->num_cols + colinfo->num_new_cols,
5366 : : &hash_ctl,
5367 : : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
5368 : :
5369 : : /*
5370 : : * Preload the hash table with any names already present (these would have
5371 : : * come from set_using_names).
5372 : : */
5373 [ + + ]: 38184 : for (i = 0; i < colinfo->num_cols; i++)
5374 : : {
5375 : 37368 : char *oldname = colinfo->colnames[i];
5376 : :
5377 [ - + ]: 37368 : if (oldname)
5378 : 0 : add_to_names_hash(colinfo, oldname);
5379 : : }
5380 : :
5381 [ - + ]: 816 : for (i = 0; i < colinfo->num_new_cols; i++)
5382 : : {
5383 : 0 : char *oldname = colinfo->new_colnames[i];
5384 : :
5385 [ # # ]: 0 : if (oldname)
5386 : 0 : add_to_names_hash(colinfo, oldname);
5387 : : }
5388 : :
5389 [ - + - - : 816 : foreach(lc, colinfo->parentUsing)
- + ]
5390 : : {
5391 : 0 : char *oldname = (char *) lfirst(lc);
5392 : :
5393 : 0 : add_to_names_hash(colinfo, oldname);
5394 : : }
5395 : : }
5396 : :
5397 : : /*
5398 : : * add_to_names_hash: add a string to the names_hash, if we're using one
5399 : : */
5400 : : static void
5401 : 333030 : add_to_names_hash(deparse_columns *colinfo, const char *name)
5402 : : {
5403 [ + + ]: 333030 : if (colinfo->names_hash)
5404 : 37368 : (void) hash_search(colinfo->names_hash,
5405 : : name,
5406 : : HASH_ENTER,
5407 : : NULL);
5408 : 333030 : }
5409 : :
5410 : : /*
5411 : : * destroy_colinfo_names_hash: destroy hash table when done with it
5412 : : */
5413 : : static void
5414 : 65116 : destroy_colinfo_names_hash(deparse_columns *colinfo)
5415 : : {
5416 [ + + ]: 65116 : if (colinfo->names_hash)
5417 : : {
5418 : 816 : hash_destroy(colinfo->names_hash);
5419 : 816 : colinfo->names_hash = NULL;
5420 : : }
5421 : 65116 : }
5422 : :
5423 : : /*
5424 : : * identify_join_columns: figure out where columns of a join come from
5425 : : *
5426 : : * Fills the join-specific fields of the colinfo struct, except for
5427 : : * usingNames which is filled later.
5428 : : */
5429 : : static void
5430 : 940 : identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
5431 : : deparse_columns *colinfo)
5432 : : {
5433 : : int numjoincols;
5434 : : int jcolno;
5435 : : int rcolno;
5436 : : ListCell *lc;
5437 : :
5438 : : /* Extract left/right child RT indexes */
5439 [ + + ]: 940 : if (IsA(j->larg, RangeTblRef))
5440 : 605 : colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
5441 [ + - ]: 335 : else if (IsA(j->larg, JoinExpr))
5442 : 335 : colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
5443 : : else
5444 [ # # ]: 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5445 : : (int) nodeTag(j->larg));
5446 [ + - ]: 940 : if (IsA(j->rarg, RangeTblRef))
5447 : 940 : colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
5448 [ # # ]: 0 : else if (IsA(j->rarg, JoinExpr))
5449 : 0 : colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
5450 : : else
5451 [ # # ]: 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5452 : : (int) nodeTag(j->rarg));
5453 : :
5454 : : /* Assert children will be processed earlier than join in second pass */
5455 : : Assert(colinfo->leftrti < j->rtindex);
5456 : : Assert(colinfo->rightrti < j->rtindex);
5457 : :
5458 : : /* Initialize result arrays with zeroes */
5459 : 940 : numjoincols = list_length(jrte->joinaliasvars);
5460 : : Assert(numjoincols == list_length(jrte->eref->colnames));
5461 : 940 : colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
5462 : 940 : colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
5463 : :
5464 : : /*
5465 : : * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
5466 : : * Recall that the column(s) merged due to USING are the first column(s)
5467 : : * of the join output. We need not do anything special while scanning
5468 : : * joinleftcols, but while scanning joinrightcols we must distinguish
5469 : : * merged from unmerged columns.
5470 : : */
5471 : 940 : jcolno = 0;
5472 [ + - + + : 22059 : foreach(lc, jrte->joinleftcols)
+ + ]
5473 : : {
5474 : 21119 : int leftattno = lfirst_int(lc);
5475 : :
5476 : 21119 : colinfo->leftattnos[jcolno++] = leftattno;
5477 : : }
5478 : 940 : rcolno = 0;
5479 [ + - + + : 10170 : foreach(lc, jrte->joinrightcols)
+ + ]
5480 : : {
5481 : 9230 : int rightattno = lfirst_int(lc);
5482 : :
5483 [ + + ]: 9230 : if (rcolno < jrte->joinmergedcols) /* merged column? */
5484 : 384 : colinfo->rightattnos[rcolno] = rightattno;
5485 : : else
5486 : 8846 : colinfo->rightattnos[jcolno++] = rightattno;
5487 : 9230 : rcolno++;
5488 : : }
5489 : : Assert(jcolno == numjoincols);
5490 : 940 : }
5491 : :
5492 : : /*
5493 : : * get_rtable_name: convenience function to get a previously assigned RTE alias
5494 : : *
5495 : : * The RTE must belong to the topmost namespace level in "context".
5496 : : */
5497 : : static char *
5498 : 4157 : get_rtable_name(int rtindex, deparse_context *context)
5499 : : {
5500 : 4157 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
5501 : :
5502 : : Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
5503 : 4157 : return (char *) list_nth(dpns->rtable_names, rtindex - 1);
5504 : : }
5505 : :
5506 : : /*
5507 : : * set_deparse_plan: set up deparse_namespace to parse subexpressions
5508 : : * of a given Plan node
5509 : : *
5510 : : * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
5511 : : * and index_tlist fields. Caller must already have adjusted the ancestors
5512 : : * list if necessary. Note that the rtable, subplans, and ctes fields do
5513 : : * not need to change when shifting attention to different plan nodes in a
5514 : : * single plan tree.
5515 : : */
5516 : : static void
5517 : 102133 : set_deparse_plan(deparse_namespace *dpns, Plan *plan)
5518 : : {
5519 : 102133 : dpns->plan = plan;
5520 : :
5521 : : /*
5522 : : * We special-case Append and MergeAppend to pretend that the first child
5523 : : * plan is the OUTER referent; we have to interpret OUTER Vars in their
5524 : : * tlists according to one of the children, and the first one is the most
5525 : : * natural choice.
5526 : : */
5527 [ + + ]: 102133 : if (IsA(plan, Append))
5528 : 2972 : dpns->outer_plan = linitial(((Append *) plan)->appendplans);
5529 [ + + ]: 99161 : else if (IsA(plan, MergeAppend))
5530 : 360 : dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
5531 : : else
5532 : 98801 : dpns->outer_plan = outerPlan(plan);
5533 : :
5534 [ + + ]: 102133 : if (dpns->outer_plan)
5535 : 49325 : dpns->outer_tlist = dpns->outer_plan->targetlist;
5536 : : else
5537 : 52808 : dpns->outer_tlist = NIL;
5538 : :
5539 : : /*
5540 : : * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
5541 : : * use OUTER because that could someday conflict with the normal meaning.)
5542 : : * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
5543 : : * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
5544 : : * that as INNER referent.
5545 : : *
5546 : : * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
5547 : : * INNER referent. This is the join from the target relation to the data
5548 : : * source, and all INNER_VAR Vars in other parts of the query refer to its
5549 : : * targetlist.
5550 : : *
5551 : : * For ON CONFLICT DO SELECT/UPDATE we just need the inner tlist to point
5552 : : * to the excluded expression's tlist. (Similar to the SubqueryScan we
5553 : : * don't want to reuse OUTER, it's used for RETURNING in some modify table
5554 : : * cases, although not INSERT .. CONFLICT).
5555 : : */
5556 [ + + ]: 102133 : if (IsA(plan, SubqueryScan))
5557 : 577 : dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5558 [ + + ]: 101556 : else if (IsA(plan, CteScan))
5559 : 405 : dpns->inner_plan = list_nth(dpns->subplans,
5560 : 405 : ((CteScan *) plan)->ctePlanId - 1);
5561 [ + + ]: 101151 : else if (IsA(plan, WorkTableScan))
5562 : 116 : dpns->inner_plan = find_recursive_union(dpns,
5563 : : (WorkTableScan *) plan);
5564 [ + + ]: 101035 : else if (IsA(plan, ModifyTable))
5565 : : {
5566 [ + + ]: 289 : if (((ModifyTable *) plan)->operation == CMD_MERGE)
5567 : 40 : dpns->inner_plan = outerPlan(plan);
5568 : : else
5569 : 249 : dpns->inner_plan = plan;
5570 : : }
5571 : : else
5572 : 100746 : dpns->inner_plan = innerPlan(plan);
5573 : :
5574 [ + + + + ]: 102133 : if (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)
5575 : 141 : dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
5576 [ + + ]: 101992 : else if (dpns->inner_plan)
5577 : 17693 : dpns->inner_tlist = dpns->inner_plan->targetlist;
5578 : : else
5579 : 84299 : dpns->inner_tlist = NIL;
5580 : :
5581 : : /* Set up referent for INDEX_VAR Vars, if needed */
5582 [ + + ]: 102133 : if (IsA(plan, IndexOnlyScan))
5583 : 2324 : dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5584 [ + + ]: 99809 : else if (IsA(plan, ForeignScan))
5585 : 1572 : dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5586 [ - + ]: 98237 : else if (IsA(plan, CustomScan))
5587 : 0 : dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5588 : : else
5589 : 98237 : dpns->index_tlist = NIL;
5590 : 102133 : }
5591 : :
5592 : : /*
5593 : : * Locate the ancestor plan node that is the RecursiveUnion generating
5594 : : * the WorkTableScan's work table. We can match on wtParam, since that
5595 : : * should be unique within the plan tree.
5596 : : */
5597 : : static Plan *
5598 : 116 : find_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)
5599 : : {
5600 : : ListCell *lc;
5601 : :
5602 [ + - + - : 292 : foreach(lc, dpns->ancestors)
+ - ]
5603 : : {
5604 : 292 : Plan *ancestor = (Plan *) lfirst(lc);
5605 : :
5606 [ + + ]: 292 : if (IsA(ancestor, RecursiveUnion) &&
5607 [ + - ]: 116 : ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5608 : 116 : return ancestor;
5609 : : }
5610 [ # # ]: 0 : elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5611 : : wtscan->wtParam);
5612 : : return NULL;
5613 : : }
5614 : :
5615 : : /*
5616 : : * push_child_plan: temporarily transfer deparsing attention to a child plan
5617 : : *
5618 : : * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
5619 : : * deparse context in case the referenced expression itself uses
5620 : : * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
5621 : : * affecting levelsup issues (although in a Plan tree there really shouldn't
5622 : : * be any).
5623 : : *
5624 : : * Caller must provide a local deparse_namespace variable to save the
5625 : : * previous state for pop_child_plan.
5626 : : */
5627 : : static void
5628 : 59152 : push_child_plan(deparse_namespace *dpns, Plan *plan,
5629 : : deparse_namespace *save_dpns)
5630 : : {
5631 : : /* Save state for restoration later */
5632 : 59152 : *save_dpns = *dpns;
5633 : :
5634 : : /* Link current plan node into ancestors list */
5635 : 59152 : dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5636 : :
5637 : : /* Set attention on selected child */
5638 : 59152 : set_deparse_plan(dpns, plan);
5639 : 59152 : }
5640 : :
5641 : : /*
5642 : : * pop_child_plan: undo the effects of push_child_plan
5643 : : */
5644 : : static void
5645 : 59152 : pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5646 : : {
5647 : : List *ancestors;
5648 : :
5649 : : /* Get rid of ancestors list cell added by push_child_plan */
5650 : 59152 : ancestors = list_delete_first(dpns->ancestors);
5651 : :
5652 : : /* Restore fields changed by push_child_plan */
5653 : 59152 : *dpns = *save_dpns;
5654 : :
5655 : : /* Make sure dpns->ancestors is right (may be unnecessary) */
5656 : 59152 : dpns->ancestors = ancestors;
5657 : 59152 : }
5658 : :
5659 : : /*
5660 : : * push_ancestor_plan: temporarily transfer deparsing attention to an
5661 : : * ancestor plan
5662 : : *
5663 : : * When expanding a Param reference, we must adjust the deparse context
5664 : : * to match the plan node that contains the expression being printed;
5665 : : * otherwise we'd fail if that expression itself contains a Param or
5666 : : * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
5667 : : *
5668 : : * The target ancestor is conveniently identified by the ListCell holding it
5669 : : * in dpns->ancestors.
5670 : : *
5671 : : * Caller must provide a local deparse_namespace variable to save the
5672 : : * previous state for pop_ancestor_plan.
5673 : : */
5674 : : static void
5675 : 3101 : push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
5676 : : deparse_namespace *save_dpns)
5677 : : {
5678 : 3101 : Plan *plan = (Plan *) lfirst(ancestor_cell);
5679 : :
5680 : : /* Save state for restoration later */
5681 : 3101 : *save_dpns = *dpns;
5682 : :
5683 : : /* Build a new ancestor list with just this node's ancestors */
5684 : 3101 : dpns->ancestors =
5685 : 3101 : list_copy_tail(dpns->ancestors,
5686 : 3101 : list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5687 : :
5688 : : /* Set attention on selected ancestor */
5689 : 3101 : set_deparse_plan(dpns, plan);
5690 : 3101 : }
5691 : :
5692 : : /*
5693 : : * pop_ancestor_plan: undo the effects of push_ancestor_plan
5694 : : */
5695 : : static void
5696 : 3101 : pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5697 : : {
5698 : : /* Free the ancestor list made in push_ancestor_plan */
5699 : 3101 : list_free(dpns->ancestors);
5700 : :
5701 : : /* Restore fields changed by push_ancestor_plan */
5702 : 3101 : *dpns = *save_dpns;
5703 : 3101 : }
5704 : :
5705 : :
5706 : : /* ----------
5707 : : * make_ruledef - reconstruct the CREATE RULE command
5708 : : * for a given pg_rewrite tuple
5709 : : * ----------
5710 : : */
5711 : : static void
5712 : 325 : make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5713 : : int prettyFlags)
5714 : : {
5715 : : char *rulename;
5716 : : char ev_type;
5717 : : Oid ev_class;
5718 : : bool is_instead;
5719 : : char *ev_qual;
5720 : : char *ev_action;
5721 : : List *actions;
5722 : : Relation ev_relation;
5723 : 325 : TupleDesc viewResultDesc = NULL;
5724 : : int fno;
5725 : : Datum dat;
5726 : : bool isnull;
5727 : :
5728 : : /*
5729 : : * Get the attribute values from the rules tuple
5730 : : */
5731 : 325 : fno = SPI_fnumber(rulettc, "rulename");
5732 : 325 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5733 : : Assert(!isnull);
5734 : 325 : rulename = NameStr(*(DatumGetName(dat)));
5735 : :
5736 : 325 : fno = SPI_fnumber(rulettc, "ev_type");
5737 : 325 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5738 : : Assert(!isnull);
5739 : 325 : ev_type = DatumGetChar(dat);
5740 : :
5741 : 325 : fno = SPI_fnumber(rulettc, "ev_class");
5742 : 325 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5743 : : Assert(!isnull);
5744 : 325 : ev_class = DatumGetObjectId(dat);
5745 : :
5746 : 325 : fno = SPI_fnumber(rulettc, "is_instead");
5747 : 325 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5748 : : Assert(!isnull);
5749 : 325 : is_instead = DatumGetBool(dat);
5750 : :
5751 : 325 : fno = SPI_fnumber(rulettc, "ev_qual");
5752 : 325 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5753 : : Assert(ev_qual != NULL);
5754 : :
5755 : 325 : fno = SPI_fnumber(rulettc, "ev_action");
5756 : 325 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5757 : : Assert(ev_action != NULL);
5758 : 325 : actions = (List *) stringToNode(ev_action);
5759 [ - + ]: 325 : if (actions == NIL)
5760 [ # # ]: 0 : elog(ERROR, "invalid empty ev_action list");
5761 : :
5762 : 325 : ev_relation = table_open(ev_class, AccessShareLock);
5763 : :
5764 : : /*
5765 : : * Build the rules definition text
5766 : : */
5767 : 325 : appendStringInfo(buf, "CREATE RULE %s AS",
5768 : : quote_identifier(rulename));
5769 : :
5770 [ + - ]: 325 : if (prettyFlags & PRETTYFLAG_INDENT)
5771 : 325 : appendStringInfoString(buf, "\n ON ");
5772 : : else
5773 : 0 : appendStringInfoString(buf, " ON ");
5774 : :
5775 : : /* The event the rule is fired for */
5776 [ + + + + : 325 : switch (ev_type)
- ]
5777 : : {
5778 : 4 : case '1':
5779 : 4 : appendStringInfoString(buf, "SELECT");
5780 : 4 : viewResultDesc = RelationGetDescr(ev_relation);
5781 : 4 : break;
5782 : :
5783 : 92 : case '2':
5784 : 92 : appendStringInfoString(buf, "UPDATE");
5785 : 92 : break;
5786 : :
5787 : 165 : case '3':
5788 : 165 : appendStringInfoString(buf, "INSERT");
5789 : 165 : break;
5790 : :
5791 : 64 : case '4':
5792 : 64 : appendStringInfoString(buf, "DELETE");
5793 : 64 : break;
5794 : :
5795 : 0 : default:
5796 [ # # ]: 0 : ereport(ERROR,
5797 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5798 : : errmsg("rule \"%s\" has unsupported event type %d",
5799 : : rulename, ev_type)));
5800 : : break;
5801 : : }
5802 : :
5803 : : /* The relation the rule is fired on */
5804 : 325 : appendStringInfo(buf, " TO %s",
5805 [ + + ]: 325 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
5806 : 92 : generate_relation_name(ev_class, NIL) :
5807 : 233 : generate_qualified_relation_name(ev_class));
5808 : :
5809 : : /* If the rule has an event qualification, add it */
5810 [ + + ]: 325 : if (strcmp(ev_qual, "<>") != 0)
5811 : : {
5812 : : Node *qual;
5813 : : Query *query;
5814 : : deparse_context context;
5815 : : deparse_namespace dpns;
5816 : :
5817 [ + - ]: 62 : if (prettyFlags & PRETTYFLAG_INDENT)
5818 : 62 : appendStringInfoString(buf, "\n ");
5819 : 62 : appendStringInfoString(buf, " WHERE ");
5820 : :
5821 : 62 : qual = stringToNode(ev_qual);
5822 : :
5823 : : /*
5824 : : * We need to make a context for recognizing any Vars in the qual
5825 : : * (which can only be references to OLD and NEW). Use the rtable of
5826 : : * the first query in the action list for this purpose.
5827 : : */
5828 : 62 : query = (Query *) linitial(actions);
5829 : :
5830 : : /*
5831 : : * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5832 : : * into the SELECT, and that's what we need to look at. (Ugly kluge
5833 : : * ... try to fix this when we redesign querytrees.)
5834 : : */
5835 : 62 : query = getInsertSelectQuery(query, NULL);
5836 : :
5837 : : /* Must acquire locks right away; see notes in get_query_def() */
5838 : 62 : AcquireRewriteLocks(query, false, false);
5839 : :
5840 : 62 : context.buf = buf;
5841 : 62 : context.namespaces = list_make1(&dpns);
5842 : 62 : context.resultDesc = NULL;
5843 : 62 : context.targetList = NIL;
5844 : 62 : context.windowClause = NIL;
5845 : 62 : context.varprefix = (list_length(query->rtable) != 1);
5846 : 62 : context.prettyFlags = prettyFlags;
5847 : 62 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
5848 : 62 : context.indentLevel = PRETTYINDENT_STD;
5849 : 62 : context.colNamesVisible = true;
5850 : 62 : context.inGroupBy = false;
5851 : 62 : context.varInOrderBy = false;
5852 : 62 : context.appendparents = NULL;
5853 : :
5854 : 62 : set_deparse_for_query(&dpns, query, NIL);
5855 : :
5856 : 62 : get_rule_expr(qual, &context, false);
5857 : : }
5858 : :
5859 : 325 : appendStringInfoString(buf, " DO ");
5860 : :
5861 : : /* The INSTEAD keyword (if so) */
5862 [ + + ]: 325 : if (is_instead)
5863 : 199 : appendStringInfoString(buf, "INSTEAD ");
5864 : :
5865 : : /* Finally the rules actions */
5866 [ + + ]: 325 : if (list_length(actions) > 1)
5867 : : {
5868 : : ListCell *action;
5869 : : Query *query;
5870 : :
5871 : 10 : appendStringInfoChar(buf, '(');
5872 [ + - + + : 30 : foreach(action, actions)
+ + ]
5873 : : {
5874 : 20 : query = (Query *) lfirst(action);
5875 : 20 : get_query_def(query, buf, NIL, viewResultDesc, true,
5876 : : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5877 [ + - ]: 20 : if (prettyFlags)
5878 : 20 : appendStringInfoString(buf, ";\n");
5879 : : else
5880 : 0 : appendStringInfoString(buf, "; ");
5881 : : }
5882 : 10 : appendStringInfoString(buf, ");");
5883 : : }
5884 : : else
5885 : : {
5886 : : Query *query;
5887 : :
5888 : 315 : query = (Query *) linitial(actions);
5889 : 315 : get_query_def(query, buf, NIL, viewResultDesc, true,
5890 : : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5891 : 315 : appendStringInfoChar(buf, ';');
5892 : : }
5893 : :
5894 : 325 : table_close(ev_relation, AccessShareLock);
5895 : 325 : }
5896 : :
5897 : :
5898 : : /* ----------
5899 : : * make_viewdef - reconstruct the SELECT part of a
5900 : : * view rewrite rule
5901 : : * ----------
5902 : : */
5903 : : static void
5904 : 2165 : make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5905 : : int prettyFlags, int wrapColumn)
5906 : : {
5907 : : Query *query;
5908 : : char ev_type;
5909 : : Oid ev_class;
5910 : : bool is_instead;
5911 : : char *ev_qual;
5912 : : char *ev_action;
5913 : : List *actions;
5914 : : Relation ev_relation;
5915 : : int fno;
5916 : : Datum dat;
5917 : : bool isnull;
5918 : :
5919 : : /*
5920 : : * Get the attribute values from the rules tuple
5921 : : */
5922 : 2165 : fno = SPI_fnumber(rulettc, "ev_type");
5923 : 2165 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5924 : : Assert(!isnull);
5925 : 2165 : ev_type = DatumGetChar(dat);
5926 : :
5927 : 2165 : fno = SPI_fnumber(rulettc, "ev_class");
5928 : 2165 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5929 : : Assert(!isnull);
5930 : 2165 : ev_class = DatumGetObjectId(dat);
5931 : :
5932 : 2165 : fno = SPI_fnumber(rulettc, "is_instead");
5933 : 2165 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5934 : : Assert(!isnull);
5935 : 2165 : is_instead = DatumGetBool(dat);
5936 : :
5937 : 2165 : fno = SPI_fnumber(rulettc, "ev_qual");
5938 : 2165 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
5939 : : Assert(ev_qual != NULL);
5940 : :
5941 : 2165 : fno = SPI_fnumber(rulettc, "ev_action");
5942 : 2165 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
5943 : : Assert(ev_action != NULL);
5944 : 2165 : actions = (List *) stringToNode(ev_action);
5945 : :
5946 [ - + ]: 2165 : if (list_length(actions) != 1)
5947 : : {
5948 : : /* keep output buffer empty and leave */
5949 : 0 : return;
5950 : : }
5951 : :
5952 : 2165 : query = (Query *) linitial(actions);
5953 : :
5954 [ + - + - ]: 2165 : if (ev_type != '1' || !is_instead ||
5955 [ + - - + ]: 2165 : strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5956 : : {
5957 : : /* keep output buffer empty and leave */
5958 : 0 : return;
5959 : : }
5960 : :
5961 : 2165 : ev_relation = table_open(ev_class, AccessShareLock);
5962 : :
5963 : 2165 : get_query_def(query, buf, NIL, RelationGetDescr(ev_relation), true,
5964 : : prettyFlags, wrapColumn, 0);
5965 : 2165 : appendStringInfoChar(buf, ';');
5966 : :
5967 : 2165 : table_close(ev_relation, AccessShareLock);
5968 : : }
5969 : :
5970 : :
5971 : : /* ----------
5972 : : * get_query_def - Parse back one query parsetree
5973 : : *
5974 : : * query: parsetree to be displayed
5975 : : * buf: output text is appended to buf
5976 : : * parentnamespace: list (initially empty) of outer-level deparse_namespace's
5977 : : * resultDesc: if not NULL, the output tuple descriptor for the view
5978 : : * represented by a SELECT query. We use the column names from it
5979 : : * to label SELECT output columns, in preference to names in the query
5980 : : * colNamesVisible: true if the surrounding context cares about the output
5981 : : * column names at all (as, for example, an EXISTS() context does not);
5982 : : * when false, we can suppress dummy column labels such as "?column?"
5983 : : * prettyFlags: bitmask of PRETTYFLAG_XXX options
5984 : : * wrapColumn: maximum line length, or -1 to disable wrapping
5985 : : * startIndent: initial indentation amount
5986 : : * ----------
5987 : : */
5988 : : static void
5989 : 3556 : get_query_def(Query *query, StringInfo buf, List *parentnamespace,
5990 : : TupleDesc resultDesc, bool colNamesVisible,
5991 : : int prettyFlags, int wrapColumn, int startIndent)
5992 : : {
5993 : : deparse_context context;
5994 : : deparse_namespace dpns;
5995 : : int rtable_size;
5996 : :
5997 : : /* Guard against excessively long or deeply-nested queries */
5998 [ - + ]: 3556 : CHECK_FOR_INTERRUPTS();
5999 : 3556 : check_stack_depth();
6000 : :
6001 : 7112 : rtable_size = query->hasGroupRTE ?
6002 [ + + ]: 3556 : list_length(query->rtable) - 1 :
6003 : 3437 : list_length(query->rtable);
6004 : :
6005 : : /*
6006 : : * Replace any Vars in the query's targetlist and havingQual that
6007 : : * reference GROUP outputs with the underlying grouping expressions.
6008 : : *
6009 : : * We can safely pass NULL for the root here. Preserving varnullingrels
6010 : : * makes no difference to the deparsed source text.
6011 : : */
6012 [ + + ]: 3556 : if (query->hasGroupRTE)
6013 : : {
6014 : 119 : query->targetList = (List *)
6015 : 119 : flatten_group_exprs(NULL, query, (Node *) query->targetList);
6016 : 119 : query->havingQual =
6017 : 119 : flatten_group_exprs(NULL, query, query->havingQual);
6018 : : }
6019 : :
6020 : : /*
6021 : : * Before we begin to examine the query, acquire locks on referenced
6022 : : * relations, and fix up deleted columns in JOIN RTEs. This ensures
6023 : : * consistent results. Note we assume it's OK to scribble on the passed
6024 : : * querytree!
6025 : : *
6026 : : * We are only deparsing the query (we are not about to execute it), so we
6027 : : * only need AccessShareLock on the relations it mentions.
6028 : : */
6029 : 3556 : AcquireRewriteLocks(query, false, false);
6030 : :
6031 : 3556 : context.buf = buf;
6032 : 3556 : context.namespaces = lcons(&dpns, list_copy(parentnamespace));
6033 : 3556 : context.resultDesc = NULL;
6034 : 3556 : context.targetList = NIL;
6035 : 3556 : context.windowClause = NIL;
6036 [ + + + + ]: 3556 : context.varprefix = (parentnamespace != NIL ||
6037 : 3556 : rtable_size != 1);
6038 : 3556 : context.prettyFlags = prettyFlags;
6039 : 3556 : context.wrapColumn = wrapColumn;
6040 : 3556 : context.indentLevel = startIndent;
6041 : 3556 : context.colNamesVisible = colNamesVisible;
6042 : 3556 : context.inGroupBy = false;
6043 : 3556 : context.varInOrderBy = false;
6044 : 3556 : context.appendparents = NULL;
6045 : :
6046 : 3556 : set_deparse_for_query(&dpns, query, parentnamespace);
6047 : :
6048 [ + + + + : 3556 : switch (query->commandType)
+ + + - ]
6049 : : {
6050 : 3166 : case CMD_SELECT:
6051 : : /* We set context.resultDesc only if it's a SELECT */
6052 : 3166 : context.resultDesc = resultDesc;
6053 : 3166 : get_select_query_def(query, &context);
6054 : 3166 : break;
6055 : :
6056 : 102 : case CMD_UPDATE:
6057 : 102 : get_update_query_def(query, &context);
6058 : 102 : break;
6059 : :
6060 : 194 : case CMD_INSERT:
6061 : 194 : get_insert_query_def(query, &context);
6062 : 194 : break;
6063 : :
6064 : 55 : case CMD_DELETE:
6065 : 55 : get_delete_query_def(query, &context);
6066 : 55 : break;
6067 : :
6068 : 8 : case CMD_MERGE:
6069 : 8 : get_merge_query_def(query, &context);
6070 : 8 : break;
6071 : :
6072 : 22 : case CMD_NOTHING:
6073 : 22 : appendStringInfoString(buf, "NOTHING");
6074 : 22 : break;
6075 : :
6076 : 9 : case CMD_UTILITY:
6077 : 9 : get_utility_query_def(query, &context);
6078 : 9 : break;
6079 : :
6080 : 0 : default:
6081 [ # # ]: 0 : elog(ERROR, "unrecognized query command type: %d",
6082 : : query->commandType);
6083 : : break;
6084 : : }
6085 : 3556 : }
6086 : :
6087 : : /* ----------
6088 : : * get_values_def - Parse back a VALUES list
6089 : : * ----------
6090 : : */
6091 : : static void
6092 : 193 : get_values_def(List *values_lists, deparse_context *context)
6093 : : {
6094 : 193 : StringInfo buf = context->buf;
6095 : 193 : bool first_list = true;
6096 : : ListCell *vtl;
6097 : :
6098 : 193 : appendStringInfoString(buf, "VALUES ");
6099 : :
6100 [ + - + + : 581 : foreach(vtl, values_lists)
+ + ]
6101 : : {
6102 : 388 : List *sublist = (List *) lfirst(vtl);
6103 : 388 : bool first_col = true;
6104 : : ListCell *lc;
6105 : :
6106 [ + + ]: 388 : if (first_list)
6107 : 193 : first_list = false;
6108 : : else
6109 : 195 : appendStringInfoString(buf, ", ");
6110 : :
6111 : 388 : appendStringInfoChar(buf, '(');
6112 [ + - + + : 1380 : foreach(lc, sublist)
+ + ]
6113 : : {
6114 : 992 : Node *col = (Node *) lfirst(lc);
6115 : :
6116 [ + + ]: 992 : if (first_col)
6117 : 388 : first_col = false;
6118 : : else
6119 : 604 : appendStringInfoChar(buf, ',');
6120 : :
6121 : : /*
6122 : : * Print the value. Whole-row Vars need special treatment.
6123 : : */
6124 : 992 : get_rule_expr_toplevel(col, context, false);
6125 : : }
6126 : 388 : appendStringInfoChar(buf, ')');
6127 : : }
6128 : 193 : }
6129 : :
6130 : : /* ----------
6131 : : * get_with_clause - Parse back a WITH clause
6132 : : * ----------
6133 : : */
6134 : : static void
6135 : 3525 : get_with_clause(Query *query, deparse_context *context)
6136 : : {
6137 : 3525 : StringInfo buf = context->buf;
6138 : : const char *sep;
6139 : : ListCell *l;
6140 : :
6141 [ + + ]: 3525 : if (query->cteList == NIL)
6142 : 3462 : return;
6143 : :
6144 [ + - ]: 63 : if (PRETTY_INDENT(context))
6145 : : {
6146 : 63 : context->indentLevel += PRETTYINDENT_STD;
6147 : 63 : appendStringInfoChar(buf, ' ');
6148 : : }
6149 : :
6150 [ + + ]: 63 : if (query->hasRecursive)
6151 : 34 : sep = "WITH RECURSIVE ";
6152 : : else
6153 : 29 : sep = "WITH ";
6154 [ + - + + : 156 : foreach(l, query->cteList)
+ + ]
6155 : : {
6156 : 93 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
6157 : :
6158 : 93 : appendStringInfoString(buf, sep);
6159 : 93 : appendStringInfoString(buf, quote_identifier(cte->ctename));
6160 [ + + ]: 93 : if (cte->aliascolnames)
6161 : : {
6162 : 38 : bool first = true;
6163 : : ListCell *col;
6164 : :
6165 : 38 : appendStringInfoChar(buf, '(');
6166 [ + - + + : 100 : foreach(col, cte->aliascolnames)
+ + ]
6167 : : {
6168 [ + + ]: 62 : if (first)
6169 : 38 : first = false;
6170 : : else
6171 : 24 : appendStringInfoString(buf, ", ");
6172 : 62 : appendStringInfoString(buf,
6173 : 62 : quote_identifier(strVal(lfirst(col))));
6174 : : }
6175 : 38 : appendStringInfoChar(buf, ')');
6176 : : }
6177 : 93 : appendStringInfoString(buf, " AS ");
6178 [ + + - - ]: 93 : switch (cte->ctematerialized)
6179 : : {
6180 : 81 : case CTEMaterializeDefault:
6181 : 81 : break;
6182 : 12 : case CTEMaterializeAlways:
6183 : 12 : appendStringInfoString(buf, "MATERIALIZED ");
6184 : 12 : break;
6185 : 0 : case CTEMaterializeNever:
6186 : 0 : appendStringInfoString(buf, "NOT MATERIALIZED ");
6187 : 0 : break;
6188 : : }
6189 : 93 : appendStringInfoChar(buf, '(');
6190 [ + - ]: 93 : if (PRETTY_INDENT(context))
6191 : 93 : appendContextKeyword(context, "", 0, 0, 0);
6192 : 93 : get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
6193 : : true,
6194 : : context->prettyFlags, context->wrapColumn,
6195 : : context->indentLevel);
6196 [ + - ]: 93 : if (PRETTY_INDENT(context))
6197 : 93 : appendContextKeyword(context, "", 0, 0, 0);
6198 : 93 : appendStringInfoChar(buf, ')');
6199 : :
6200 [ + + ]: 93 : if (cte->search_clause)
6201 : : {
6202 : 4 : bool first = true;
6203 : : ListCell *lc;
6204 : :
6205 : 4 : appendStringInfo(buf, " SEARCH %s FIRST BY ",
6206 [ - + ]: 4 : cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
6207 : :
6208 [ + - + + : 12 : foreach(lc, cte->search_clause->search_col_list)
+ + ]
6209 : : {
6210 [ + + ]: 8 : if (first)
6211 : 4 : first = false;
6212 : : else
6213 : 4 : appendStringInfoString(buf, ", ");
6214 : 8 : appendStringInfoString(buf,
6215 : 8 : quote_identifier(strVal(lfirst(lc))));
6216 : : }
6217 : :
6218 : 4 : appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
6219 : : }
6220 : :
6221 [ + + ]: 93 : if (cte->cycle_clause)
6222 : : {
6223 : 8 : bool first = true;
6224 : : ListCell *lc;
6225 : :
6226 : 8 : appendStringInfoString(buf, " CYCLE ");
6227 : :
6228 [ + - + + : 24 : foreach(lc, cte->cycle_clause->cycle_col_list)
+ + ]
6229 : : {
6230 [ + + ]: 16 : if (first)
6231 : 8 : first = false;
6232 : : else
6233 : 8 : appendStringInfoString(buf, ", ");
6234 : 16 : appendStringInfoString(buf,
6235 : 16 : quote_identifier(strVal(lfirst(lc))));
6236 : : }
6237 : :
6238 : 8 : appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
6239 : :
6240 : : {
6241 : 8 : Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
6242 : 8 : Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
6243 : :
6244 [ + + + - : 12 : if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
+ - - + ]
6245 [ + - + - ]: 4 : cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
6246 : : {
6247 : 4 : appendStringInfoString(buf, " TO ");
6248 : 4 : get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
6249 : 4 : appendStringInfoString(buf, " DEFAULT ");
6250 : 4 : get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
6251 : : }
6252 : : }
6253 : :
6254 : 8 : appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
6255 : : }
6256 : :
6257 : 93 : sep = ", ";
6258 : : }
6259 : :
6260 [ + - ]: 63 : if (PRETTY_INDENT(context))
6261 : : {
6262 : 63 : context->indentLevel -= PRETTYINDENT_STD;
6263 : 63 : appendContextKeyword(context, "", 0, 0, 0);
6264 : : }
6265 : : else
6266 : 0 : appendStringInfoChar(buf, ' ');
6267 : : }
6268 : :
6269 : : /* ----------
6270 : : * get_select_query_def - Parse back a SELECT parsetree
6271 : : * ----------
6272 : : */
6273 : : static void
6274 : 3166 : get_select_query_def(Query *query, deparse_context *context)
6275 : : {
6276 : 3166 : StringInfo buf = context->buf;
6277 : : bool force_colno;
6278 : : ListCell *l;
6279 : :
6280 : : /* Insert the WITH clause if given */
6281 : 3166 : get_with_clause(query, context);
6282 : :
6283 : : /* Subroutines may need to consult the SELECT targetlist and windowClause */
6284 : 3166 : context->targetList = query->targetList;
6285 : 3166 : context->windowClause = query->windowClause;
6286 : :
6287 : : /*
6288 : : * If the Query node has a setOperations tree, then it's the top level of
6289 : : * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
6290 : : * fields are interesting in the top query itself.
6291 : : */
6292 [ + + ]: 3166 : if (query->setOperations)
6293 : : {
6294 : 103 : get_setop_query(query->setOperations, query, context);
6295 : : /* ORDER BY clauses must be simple in this case */
6296 : 103 : force_colno = true;
6297 : : }
6298 : : else
6299 : : {
6300 : 3063 : get_basic_select_query(query, context);
6301 : 3063 : force_colno = false;
6302 : : }
6303 : :
6304 : : /* Add the ORDER BY clause if given */
6305 [ + + ]: 3166 : if (query->sortClause != NIL)
6306 : : {
6307 : 128 : appendContextKeyword(context, " ORDER BY ",
6308 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6309 : 128 : get_rule_orderby(query->sortClause, query->targetList,
6310 : : force_colno, context);
6311 : : }
6312 : :
6313 : : /*
6314 : : * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
6315 : : * standard spelling of LIMIT.
6316 : : */
6317 [ + + ]: 3166 : if (query->limitOffset != NULL)
6318 : : {
6319 : 18 : appendContextKeyword(context, " OFFSET ",
6320 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6321 : 18 : get_rule_expr(query->limitOffset, context, false);
6322 : : }
6323 [ + + ]: 3166 : if (query->limitCount != NULL)
6324 : : {
6325 [ + + ]: 53 : if (query->limitOption == LIMIT_OPTION_WITH_TIES)
6326 : : {
6327 : : /*
6328 : : * The limitCount arg is a c_expr, so it needs parens. Simple
6329 : : * literals and function expressions would not need parens, but
6330 : : * unfortunately it's hard to tell if the expression will be
6331 : : * printed as a simple literal like 123 or as a typecast
6332 : : * expression, like '-123'::int4. The grammar accepts the former
6333 : : * without quoting, but not the latter.
6334 : : */
6335 : 27 : appendContextKeyword(context, " FETCH FIRST ",
6336 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6337 : 27 : appendStringInfoChar(buf, '(');
6338 : 27 : get_rule_expr(query->limitCount, context, false);
6339 : 27 : appendStringInfoChar(buf, ')');
6340 : 27 : appendStringInfoString(buf, " ROWS WITH TIES");
6341 : : }
6342 : : else
6343 : : {
6344 : 26 : appendContextKeyword(context, " LIMIT ",
6345 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6346 [ + + ]: 26 : if (IsA(query->limitCount, Const) &&
6347 [ + - ]: 9 : ((Const *) query->limitCount)->constisnull)
6348 : 9 : appendStringInfoString(buf, "ALL");
6349 : : else
6350 : 17 : get_rule_expr(query->limitCount, context, false);
6351 : : }
6352 : : }
6353 : :
6354 : : /* Add FOR [KEY] UPDATE/SHARE clauses if present */
6355 [ + + ]: 3166 : if (query->hasForUpdate)
6356 : : {
6357 [ + - + + : 8 : foreach(l, query->rowMarks)
+ + ]
6358 : : {
6359 : 4 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
6360 : :
6361 : : /* don't print implicit clauses */
6362 [ - + ]: 4 : if (rc->pushedDown)
6363 : 0 : continue;
6364 : :
6365 : 4 : appendContextKeyword(context,
6366 : 4 : get_lock_clause_strength(rc->strength),
6367 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6368 : :
6369 : 4 : appendStringInfo(buf, " OF %s",
6370 : 4 : quote_identifier(get_rtable_name(rc->rti,
6371 : : context)));
6372 [ - + ]: 4 : if (rc->waitPolicy == LockWaitError)
6373 : 0 : appendStringInfoString(buf, " NOWAIT");
6374 [ - + ]: 4 : else if (rc->waitPolicy == LockWaitSkip)
6375 : 0 : appendStringInfoString(buf, " SKIP LOCKED");
6376 : : }
6377 : : }
6378 : 3166 : }
6379 : :
6380 : : static char *
6381 : 8 : get_lock_clause_strength(LockClauseStrength strength)
6382 : : {
6383 [ - - - - : 8 : switch (strength)
+ - ]
6384 : : {
6385 : 0 : case LCS_NONE:
6386 : : /* we intentionally throw an error for LCS_NONE */
6387 [ # # ]: 0 : elog(ERROR, "unrecognized LockClauseStrength %d",
6388 : : (int) strength);
6389 : : break;
6390 : 0 : case LCS_FORKEYSHARE:
6391 : 0 : return " FOR KEY SHARE";
6392 : 0 : case LCS_FORSHARE:
6393 : 0 : return " FOR SHARE";
6394 : 0 : case LCS_FORNOKEYUPDATE:
6395 : 0 : return " FOR NO KEY UPDATE";
6396 : 8 : case LCS_FORUPDATE:
6397 : 8 : return " FOR UPDATE";
6398 : : }
6399 : 0 : return NULL; /* keep compiler quiet */
6400 : : }
6401 : :
6402 : : /*
6403 : : * Detect whether query looks like SELECT ... FROM VALUES(),
6404 : : * with no need to rename the output columns of the VALUES RTE.
6405 : : * If so, return the VALUES RTE. Otherwise return NULL.
6406 : : */
6407 : : static RangeTblEntry *
6408 : 3063 : get_simple_values_rte(Query *query, TupleDesc resultDesc)
6409 : : {
6410 : 3063 : RangeTblEntry *result = NULL;
6411 : : ListCell *lc;
6412 : :
6413 : : /*
6414 : : * We want to detect a match even if the Query also contains OLD or NEW
6415 : : * rule RTEs. So the idea is to scan the rtable and see if there is only
6416 : : * one inFromCl RTE that is a VALUES RTE.
6417 : : */
6418 [ + + + + : 3314 : foreach(lc, query->rtable)
+ + ]
6419 : : {
6420 : 2784 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
6421 : :
6422 [ + + + - ]: 2784 : if (rte->rtekind == RTE_VALUES && rte->inFromCl)
6423 : : {
6424 [ - + ]: 167 : if (result)
6425 : 2533 : return NULL; /* multiple VALUES (probably not possible) */
6426 : 167 : result = rte;
6427 : : }
6428 [ + + + + ]: 2617 : else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
6429 : 84 : continue; /* ignore rule entries */
6430 : : else
6431 : 2533 : return NULL; /* something else -> not simple VALUES */
6432 : : }
6433 : :
6434 : : /*
6435 : : * We don't need to check the targetlist in any great detail, because
6436 : : * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
6437 : : * appear inside auto-generated sub-queries with very restricted
6438 : : * structure. However, DefineView might have modified the tlist by
6439 : : * injecting new column aliases, or we might have some other column
6440 : : * aliases forced by a resultDesc. We can only simplify if the RTE's
6441 : : * column names match the names that get_target_list() would select.
6442 : : */
6443 [ + + ]: 530 : if (result)
6444 : : {
6445 : : ListCell *lcn;
6446 : : int colno;
6447 : :
6448 [ - + ]: 167 : if (list_length(query->targetList) != list_length(result->eref->colnames))
6449 : 0 : return NULL; /* this probably cannot happen */
6450 : 167 : colno = 0;
6451 [ + - + + : 588 : forboth(lc, query->targetList, lcn, result->eref->colnames)
+ - + + +
+ + - +
+ ]
6452 : : {
6453 : 429 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
6454 : 429 : char *cname = strVal(lfirst(lcn));
6455 : : char *colname;
6456 : :
6457 [ - + ]: 429 : if (tle->resjunk)
6458 : 8 : return NULL; /* this probably cannot happen */
6459 : :
6460 : : /* compute name that get_target_list would use for column */
6461 : 429 : colno++;
6462 [ + + + - ]: 429 : if (resultDesc && colno <= resultDesc->natts)
6463 : 20 : colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6464 : : else
6465 : 409 : colname = tle->resname;
6466 : :
6467 : : /* does it match the VALUES RTE? */
6468 [ + - + + ]: 429 : if (colname == NULL || strcmp(colname, cname) != 0)
6469 : 8 : return NULL; /* column name has been changed */
6470 : : }
6471 : : }
6472 : :
6473 : 522 : return result;
6474 : : }
6475 : :
6476 : : static void
6477 : 3063 : get_basic_select_query(Query *query, deparse_context *context)
6478 : : {
6479 : 3063 : StringInfo buf = context->buf;
6480 : : RangeTblEntry *values_rte;
6481 : : char *sep;
6482 : : ListCell *l;
6483 : :
6484 [ + + ]: 3063 : if (PRETTY_INDENT(context))
6485 : : {
6486 : 3036 : context->indentLevel += PRETTYINDENT_STD;
6487 : 3036 : appendStringInfoChar(buf, ' ');
6488 : : }
6489 : :
6490 : : /*
6491 : : * If the query looks like SELECT * FROM (VALUES ...), then print just the
6492 : : * VALUES part. This reverses what transformValuesClause() did at parse
6493 : : * time.
6494 : : */
6495 : 3063 : values_rte = get_simple_values_rte(query, context->resultDesc);
6496 [ + + ]: 3063 : if (values_rte)
6497 : : {
6498 : 159 : get_values_def(values_rte->values_lists, context);
6499 : 159 : return;
6500 : : }
6501 : :
6502 : : /*
6503 : : * Build up the query string - first we say SELECT
6504 : : */
6505 [ + + ]: 2904 : if (query->isReturn)
6506 : 31 : appendStringInfoString(buf, "RETURN");
6507 : : else
6508 : 2873 : appendStringInfoString(buf, "SELECT");
6509 : :
6510 : : /* Add the DISTINCT clause if given */
6511 [ - + ]: 2904 : if (query->distinctClause != NIL)
6512 : : {
6513 [ # # ]: 0 : if (query->hasDistinctOn)
6514 : : {
6515 : 0 : appendStringInfoString(buf, " DISTINCT ON (");
6516 : 0 : sep = "";
6517 [ # # # # : 0 : foreach(l, query->distinctClause)
# # ]
6518 : : {
6519 : 0 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6520 : :
6521 : 0 : appendStringInfoString(buf, sep);
6522 : 0 : get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6523 : : false, context);
6524 : 0 : sep = ", ";
6525 : : }
6526 : 0 : appendStringInfoChar(buf, ')');
6527 : : }
6528 : : else
6529 : 0 : appendStringInfoString(buf, " DISTINCT");
6530 : : }
6531 : :
6532 : : /* Then we tell what to select (the targetlist) */
6533 : 2904 : get_target_list(query->targetList, context);
6534 : :
6535 : : /* Add the FROM clause if needed */
6536 : 2904 : get_from_clause(query, " FROM ", context);
6537 : :
6538 : : /* Add the WHERE clause if given */
6539 [ + + ]: 2904 : if (query->jointree->quals != NULL)
6540 : : {
6541 : 903 : appendContextKeyword(context, " WHERE ",
6542 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6543 : 903 : get_rule_expr(query->jointree->quals, context, false);
6544 : : }
6545 : :
6546 : : /* Add the GROUP BY clause if given */
6547 [ + + - + ]: 2904 : if (query->groupClause != NULL || query->groupingSets != NULL)
6548 : : {
6549 : : bool save_ingroupby;
6550 : :
6551 : 119 : appendContextKeyword(context, " GROUP BY ",
6552 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6553 [ - + ]: 119 : if (query->groupDistinct)
6554 : 0 : appendStringInfoString(buf, "DISTINCT ");
6555 : :
6556 : 119 : save_ingroupby = context->inGroupBy;
6557 : 119 : context->inGroupBy = true;
6558 : :
6559 [ + + ]: 119 : if (query->groupingSets == NIL)
6560 : : {
6561 : 115 : sep = "";
6562 [ + - + + : 263 : foreach(l, query->groupClause)
+ + ]
6563 : : {
6564 : 148 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6565 : :
6566 : 148 : appendStringInfoString(buf, sep);
6567 : 148 : get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
6568 : : false, context);
6569 : 148 : sep = ", ";
6570 : : }
6571 : : }
6572 : : else
6573 : : {
6574 : 4 : sep = "";
6575 [ + - + + : 8 : foreach(l, query->groupingSets)
+ + ]
6576 : : {
6577 : 4 : GroupingSet *grp = lfirst(l);
6578 : :
6579 : 4 : appendStringInfoString(buf, sep);
6580 : 4 : get_rule_groupingset(grp, query->targetList, true, context);
6581 : 4 : sep = ", ";
6582 : : }
6583 : : }
6584 : :
6585 : 119 : context->inGroupBy = save_ingroupby;
6586 : : }
6587 : :
6588 : : /* Add the HAVING clause if given */
6589 [ + + ]: 2904 : if (query->havingQual != NULL)
6590 : : {
6591 : 5 : appendContextKeyword(context, " HAVING ",
6592 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6593 : 5 : get_rule_expr(query->havingQual, context, false);
6594 : : }
6595 : :
6596 : : /* Add the WINDOW clause if needed */
6597 [ + + ]: 2904 : if (query->windowClause != NIL)
6598 : 32 : get_rule_windowclause(query, context);
6599 : : }
6600 : :
6601 : : /* ----------
6602 : : * get_target_list - Parse back a SELECT target list
6603 : : *
6604 : : * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.
6605 : : * ----------
6606 : : */
6607 : : static void
6608 : 3013 : get_target_list(List *targetList, deparse_context *context)
6609 : : {
6610 : 3013 : StringInfo buf = context->buf;
6611 : : StringInfoData targetbuf;
6612 : 3013 : bool last_was_multiline = false;
6613 : : char *sep;
6614 : : int colno;
6615 : : ListCell *l;
6616 : :
6617 : : /* we use targetbuf to hold each TLE's text temporarily */
6618 : 3013 : initStringInfo(&targetbuf);
6619 : :
6620 : 3013 : sep = " ";
6621 : 3013 : colno = 0;
6622 [ + - + + : 15853 : foreach(l, targetList)
+ + ]
6623 : : {
6624 : 12840 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6625 : : char *colname;
6626 : : char *attname;
6627 : :
6628 [ + + ]: 12840 : if (tle->resjunk)
6629 : 25 : continue; /* ignore junk entries */
6630 : :
6631 : 12815 : appendStringInfoString(buf, sep);
6632 : 12815 : sep = ", ";
6633 : 12815 : colno++;
6634 : :
6635 : : /*
6636 : : * Put the new field text into targetbuf so we can decide after we've
6637 : : * got it whether or not it needs to go on a new line.
6638 : : */
6639 : 12815 : resetStringInfo(&targetbuf);
6640 : 12815 : context->buf = &targetbuf;
6641 : :
6642 : : /*
6643 : : * We special-case Var nodes rather than using get_rule_expr. This is
6644 : : * needed because get_rule_expr will display a whole-row Var as
6645 : : * "foo.*", which is the preferred notation in most contexts, but at
6646 : : * the top level of a SELECT list it's not right (the parser will
6647 : : * expand that notation into multiple columns, yielding behavior
6648 : : * different from a whole-row Var). We need to call get_variable
6649 : : * directly so that we can tell it to do the right thing, and so that
6650 : : * we can get the attribute name which is the default AS label.
6651 : : */
6652 [ + - + + ]: 12815 : if (tle->expr && (IsA(tle->expr, Var)))
6653 : : {
6654 : 9868 : attname = get_variable((Var *) tle->expr, 0, true, context);
6655 : : }
6656 : : else
6657 : : {
6658 : 2947 : get_rule_expr((Node *) tle->expr, context, true);
6659 : :
6660 : : /*
6661 : : * When colNamesVisible is true, we should always show the
6662 : : * assigned column name explicitly. Otherwise, show it only if
6663 : : * it's not FigureColname's fallback.
6664 : : */
6665 [ + + ]: 2947 : attname = context->colNamesVisible ? NULL : "?column?";
6666 : : }
6667 : :
6668 : : /*
6669 : : * Figure out what the result column should be called. In the context
6670 : : * of a view, use the view's tuple descriptor (so as to pick up the
6671 : : * effects of any column RENAME that's been done on the view).
6672 : : * Otherwise, just use what we can find in the TLE.
6673 : : */
6674 [ + + + - ]: 12815 : if (context->resultDesc && colno <= context->resultDesc->natts)
6675 : 11640 : colname = NameStr(TupleDescAttr(context->resultDesc,
6676 : : colno - 1)->attname);
6677 : : else
6678 : 1175 : colname = tle->resname;
6679 : :
6680 : : /* Show AS unless the column's name is correct as-is */
6681 [ + + ]: 12815 : if (colname) /* resname could be NULL */
6682 : : {
6683 [ + + + + ]: 12784 : if (attname == NULL || strcmp(attname, colname) != 0)
6684 : 4166 : appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
6685 : : }
6686 : :
6687 : : /* Restore context's output buffer */
6688 : 12815 : context->buf = buf;
6689 : :
6690 : : /* Consider line-wrapping if enabled */
6691 [ + + + - ]: 12815 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
6692 : : {
6693 : : int leading_nl_pos;
6694 : :
6695 : : /* Does the new field start with a new line? */
6696 [ + - + + ]: 12788 : if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
6697 : 375 : leading_nl_pos = 0;
6698 : : else
6699 : 12413 : leading_nl_pos = -1;
6700 : :
6701 : : /* If so, we shouldn't add anything */
6702 [ + + ]: 12788 : if (leading_nl_pos >= 0)
6703 : : {
6704 : : /* instead, remove any trailing spaces currently in buf */
6705 : 375 : removeStringInfoSpaces(buf);
6706 : : }
6707 : : else
6708 : : {
6709 : : char *trailing_nl;
6710 : :
6711 : : /* Locate the start of the current line in the output buffer */
6712 : 12413 : trailing_nl = strrchr(buf->data, '\n');
6713 [ + + ]: 12413 : if (trailing_nl == NULL)
6714 : 3694 : trailing_nl = buf->data;
6715 : : else
6716 : 8719 : trailing_nl++;
6717 : :
6718 : : /*
6719 : : * Add a newline, plus some indentation, if the new field is
6720 : : * not the first and either the new field would cause an
6721 : : * overflow or the last field used more than one line.
6722 : : */
6723 [ + + ]: 12413 : if (colno > 1 &&
6724 [ - + - - ]: 9436 : ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
6725 : : last_was_multiline))
6726 : 9436 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
6727 : : PRETTYINDENT_STD, PRETTYINDENT_VAR);
6728 : : }
6729 : :
6730 : : /* Remember this field's multiline status for next iteration */
6731 : 12788 : last_was_multiline =
6732 : 12788 : (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
6733 : : }
6734 : :
6735 : : /* Add the new field */
6736 : 12815 : appendBinaryStringInfo(buf, targetbuf.data, targetbuf.len);
6737 : : }
6738 : :
6739 : : /* clean up */
6740 : 3013 : pfree(targetbuf.data);
6741 : 3013 : }
6742 : :
6743 : : static void
6744 : 109 : get_returning_clause(Query *query, deparse_context *context)
6745 : : {
6746 : 109 : StringInfo buf = context->buf;
6747 : :
6748 [ + - ]: 109 : if (query->returningList)
6749 : : {
6750 : 109 : bool have_with = false;
6751 : :
6752 : 109 : appendContextKeyword(context, " RETURNING",
6753 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6754 : :
6755 : : /* Add WITH (OLD/NEW) options, if they're not the defaults */
6756 [ + + + + ]: 109 : if (query->returningOldAlias && strcmp(query->returningOldAlias, "old") != 0)
6757 : : {
6758 : 12 : appendStringInfo(buf, " WITH (OLD AS %s",
6759 : 12 : quote_identifier(query->returningOldAlias));
6760 : 12 : have_with = true;
6761 : : }
6762 [ + + + + ]: 109 : if (query->returningNewAlias && strcmp(query->returningNewAlias, "new") != 0)
6763 : : {
6764 [ + + ]: 12 : if (have_with)
6765 : 8 : appendStringInfo(buf, ", NEW AS %s",
6766 : 8 : quote_identifier(query->returningNewAlias));
6767 : : else
6768 : : {
6769 : 4 : appendStringInfo(buf, " WITH (NEW AS %s",
6770 : 4 : quote_identifier(query->returningNewAlias));
6771 : 4 : have_with = true;
6772 : : }
6773 : : }
6774 [ + + ]: 109 : if (have_with)
6775 : 16 : appendStringInfoChar(buf, ')');
6776 : :
6777 : : /* Add the returning expressions themselves */
6778 : 109 : get_target_list(query->returningList, context);
6779 : : }
6780 : 109 : }
6781 : :
6782 : : static void
6783 : 463 : get_setop_query(Node *setOp, Query *query, deparse_context *context)
6784 : : {
6785 : 463 : StringInfo buf = context->buf;
6786 : : bool need_paren;
6787 : :
6788 : : /* Guard against excessively long or deeply-nested queries */
6789 [ - + ]: 463 : CHECK_FOR_INTERRUPTS();
6790 : 463 : check_stack_depth();
6791 : :
6792 [ + + ]: 463 : if (IsA(setOp, RangeTblRef))
6793 : : {
6794 : 283 : RangeTblRef *rtr = (RangeTblRef *) setOp;
6795 : 283 : RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
6796 : 283 : Query *subquery = rte->subquery;
6797 : :
6798 : : Assert(subquery != NULL);
6799 : :
6800 : : /*
6801 : : * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.
6802 : : * Also add parens if the leaf query contains its own set operations.
6803 : : * (That shouldn't happen unless one of the other clauses is also
6804 : : * present, see transformSetOperationTree; but let's be safe.)
6805 : : */
6806 : 849 : need_paren = (subquery->cteList ||
6807 [ + - ]: 283 : subquery->sortClause ||
6808 [ + - ]: 283 : subquery->rowMarks ||
6809 [ + - ]: 283 : subquery->limitOffset ||
6810 [ + - + - ]: 849 : subquery->limitCount ||
6811 [ - + ]: 283 : subquery->setOperations);
6812 [ - + ]: 283 : if (need_paren)
6813 : 0 : appendStringInfoChar(buf, '(');
6814 : 283 : get_query_def(subquery, buf, context->namespaces,
6815 : 283 : context->resultDesc, context->colNamesVisible,
6816 : : context->prettyFlags, context->wrapColumn,
6817 : : context->indentLevel);
6818 [ - + ]: 283 : if (need_paren)
6819 : 0 : appendStringInfoChar(buf, ')');
6820 : : }
6821 [ + - ]: 180 : else if (IsA(setOp, SetOperationStmt))
6822 : : {
6823 : 180 : SetOperationStmt *op = (SetOperationStmt *) setOp;
6824 : : int subindent;
6825 : : bool save_colnamesvisible;
6826 : :
6827 : : /*
6828 : : * We force parens when nesting two SetOperationStmts, except when the
6829 : : * lefthand input is another setop of the same kind. Syntactically,
6830 : : * we could omit parens in rather more cases, but it seems best to use
6831 : : * parens to flag cases where the setop operator changes. If we use
6832 : : * parens, we also increase the indentation level for the child query.
6833 : : *
6834 : : * There are some cases in which parens are needed around a leaf query
6835 : : * too, but those are more easily handled at the next level down (see
6836 : : * code above).
6837 : : */
6838 [ + + ]: 180 : if (IsA(op->larg, SetOperationStmt))
6839 : : {
6840 : 77 : SetOperationStmt *lop = (SetOperationStmt *) op->larg;
6841 : :
6842 [ + - + - ]: 77 : if (op->op == lop->op && op->all == lop->all)
6843 : 77 : need_paren = false;
6844 : : else
6845 : 0 : need_paren = true;
6846 : : }
6847 : : else
6848 : 103 : need_paren = false;
6849 : :
6850 [ - + ]: 180 : if (need_paren)
6851 : : {
6852 : 0 : appendStringInfoChar(buf, '(');
6853 : 0 : subindent = PRETTYINDENT_STD;
6854 : 0 : appendContextKeyword(context, "", subindent, 0, 0);
6855 : : }
6856 : : else
6857 : 180 : subindent = 0;
6858 : :
6859 : 180 : get_setop_query(op->larg, query, context);
6860 : :
6861 [ - + ]: 180 : if (need_paren)
6862 : 0 : appendContextKeyword(context, ") ", -subindent, 0, 0);
6863 [ + - ]: 180 : else if (PRETTY_INDENT(context))
6864 : 180 : appendContextKeyword(context, "", -subindent, 0, 0);
6865 : : else
6866 : 0 : appendStringInfoChar(buf, ' ');
6867 : :
6868 [ + - - - ]: 180 : switch (op->op)
6869 : : {
6870 : 180 : case SETOP_UNION:
6871 : 180 : appendStringInfoString(buf, "UNION ");
6872 : 180 : break;
6873 : 0 : case SETOP_INTERSECT:
6874 : 0 : appendStringInfoString(buf, "INTERSECT ");
6875 : 0 : break;
6876 : 0 : case SETOP_EXCEPT:
6877 : 0 : appendStringInfoString(buf, "EXCEPT ");
6878 : 0 : break;
6879 : 0 : default:
6880 [ # # ]: 0 : elog(ERROR, "unrecognized set op: %d",
6881 : : (int) op->op);
6882 : : }
6883 [ + + ]: 180 : if (op->all)
6884 : 172 : appendStringInfoString(buf, "ALL ");
6885 : :
6886 : : /* Always parenthesize if RHS is another setop */
6887 : 180 : need_paren = IsA(op->rarg, SetOperationStmt);
6888 : :
6889 : : /*
6890 : : * The indentation code here is deliberately a bit different from that
6891 : : * for the lefthand input, because we want the line breaks in
6892 : : * different places.
6893 : : */
6894 [ - + ]: 180 : if (need_paren)
6895 : : {
6896 : 0 : appendStringInfoChar(buf, '(');
6897 : 0 : subindent = PRETTYINDENT_STD;
6898 : : }
6899 : : else
6900 : 180 : subindent = 0;
6901 : 180 : appendContextKeyword(context, "", subindent, 0, 0);
6902 : :
6903 : : /*
6904 : : * The output column names of the RHS sub-select don't matter.
6905 : : */
6906 : 180 : save_colnamesvisible = context->colNamesVisible;
6907 : 180 : context->colNamesVisible = false;
6908 : :
6909 : 180 : get_setop_query(op->rarg, query, context);
6910 : :
6911 : 180 : context->colNamesVisible = save_colnamesvisible;
6912 : :
6913 [ + - ]: 180 : if (PRETTY_INDENT(context))
6914 : 180 : context->indentLevel -= subindent;
6915 [ - + ]: 180 : if (need_paren)
6916 : 0 : appendContextKeyword(context, ")", 0, 0, 0);
6917 : : }
6918 : : else
6919 : : {
6920 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
6921 : : (int) nodeTag(setOp));
6922 : : }
6923 : 463 : }
6924 : :
6925 : : /*
6926 : : * Display a sort/group clause.
6927 : : *
6928 : : * Also returns the expression tree, so caller need not find it again.
6929 : : */
6930 : : static Node *
6931 : 453 : get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
6932 : : deparse_context *context)
6933 : : {
6934 : 453 : StringInfo buf = context->buf;
6935 : : TargetEntry *tle;
6936 : : Node *expr;
6937 : :
6938 : 453 : tle = get_sortgroupref_tle(ref, tlist);
6939 : 453 : expr = (Node *) tle->expr;
6940 : :
6941 : : /*
6942 : : * Use column-number form if requested by caller. Otherwise, if
6943 : : * expression is a constant, force it to be dumped with an explicit cast
6944 : : * as decoration --- this is because a simple integer constant is
6945 : : * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
6946 : : * we dump it without any decoration. Similarly, if it's just a Var,
6947 : : * there is risk of misinterpretation if the column name is reassigned in
6948 : : * the SELECT list, so we may need to force table qualification. And, if
6949 : : * it's anything more complex than a simple Var, then force extra parens
6950 : : * around it, to ensure it can't be misinterpreted as a cube() or rollup()
6951 : : * construct.
6952 : : */
6953 [ + + ]: 453 : if (force_colno)
6954 : : {
6955 : : Assert(!tle->resjunk);
6956 : 7 : appendStringInfo(buf, "%d", tle->resno);
6957 : : }
6958 [ + - ]: 446 : else if (!expr)
6959 : : /* do nothing, probably can't happen */ ;
6960 [ - + ]: 446 : else if (IsA(expr, Const))
6961 : 0 : get_const_expr((Const *) expr, context, 1);
6962 [ + + ]: 446 : else if (IsA(expr, Var))
6963 : : {
6964 : : /* Tell get_variable to check for name conflict */
6965 : 429 : bool save_varinorderby = context->varInOrderBy;
6966 : :
6967 : 429 : context->varInOrderBy = true;
6968 : 429 : (void) get_variable((Var *) expr, 0, false, context);
6969 : 429 : context->varInOrderBy = save_varinorderby;
6970 : : }
6971 : : else
6972 : : {
6973 : : /*
6974 : : * We must force parens for function-like expressions even if
6975 : : * PRETTY_PAREN is off, since those are the ones in danger of
6976 : : * misparsing. For other expressions we need to force them only if
6977 : : * PRETTY_PAREN is on, since otherwise the expression will output them
6978 : : * itself. (We can't skip the parens.)
6979 : : */
6980 : 34 : bool need_paren = (PRETTY_PAREN(context)
6981 [ + + ]: 17 : || IsA(expr, FuncExpr)
6982 [ + - ]: 15 : || IsA(expr, Aggref)
6983 [ + - ]: 15 : || IsA(expr, WindowFunc)
6984 [ + - - + ]: 34 : || IsA(expr, JsonConstructorExpr));
6985 : :
6986 [ + + ]: 17 : if (need_paren)
6987 : 2 : appendStringInfoChar(context->buf, '(');
6988 : 17 : get_rule_expr(expr, context, true);
6989 [ + + ]: 17 : if (need_paren)
6990 : 2 : appendStringInfoChar(context->buf, ')');
6991 : : }
6992 : :
6993 : 453 : return expr;
6994 : : }
6995 : :
6996 : : /*
6997 : : * Display a GroupingSet
6998 : : */
6999 : : static void
7000 : 12 : get_rule_groupingset(GroupingSet *gset, List *targetlist,
7001 : : bool omit_parens, deparse_context *context)
7002 : : {
7003 : : ListCell *l;
7004 : 12 : StringInfo buf = context->buf;
7005 : 12 : bool omit_child_parens = true;
7006 : 12 : char *sep = "";
7007 : :
7008 [ - + + - : 12 : switch (gset->kind)
- - ]
7009 : : {
7010 : 0 : case GROUPING_SET_EMPTY:
7011 : 0 : appendStringInfoString(buf, "()");
7012 : 0 : return;
7013 : :
7014 : 8 : case GROUPING_SET_SIMPLE:
7015 : : {
7016 [ + - + - ]: 8 : if (!omit_parens || list_length(gset->content) != 1)
7017 : 8 : appendStringInfoChar(buf, '(');
7018 : :
7019 [ + - + + : 28 : foreach(l, gset->content)
+ + ]
7020 : : {
7021 : 20 : Index ref = lfirst_int(l);
7022 : :
7023 : 20 : appendStringInfoString(buf, sep);
7024 : 20 : get_rule_sortgroupclause(ref, targetlist,
7025 : : false, context);
7026 : 20 : sep = ", ";
7027 : : }
7028 : :
7029 [ + - + - ]: 8 : if (!omit_parens || list_length(gset->content) != 1)
7030 : 8 : appendStringInfoChar(buf, ')');
7031 : : }
7032 : 8 : return;
7033 : :
7034 : 4 : case GROUPING_SET_ROLLUP:
7035 : 4 : appendStringInfoString(buf, "ROLLUP(");
7036 : 4 : break;
7037 : 0 : case GROUPING_SET_CUBE:
7038 : 0 : appendStringInfoString(buf, "CUBE(");
7039 : 0 : break;
7040 : 0 : case GROUPING_SET_SETS:
7041 : 0 : appendStringInfoString(buf, "GROUPING SETS (");
7042 : 0 : omit_child_parens = false;
7043 : 0 : break;
7044 : : }
7045 : :
7046 [ + - + + : 12 : foreach(l, gset->content)
+ + ]
7047 : : {
7048 : 8 : appendStringInfoString(buf, sep);
7049 : 8 : get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
7050 : 8 : sep = ", ";
7051 : : }
7052 : :
7053 : 4 : appendStringInfoChar(buf, ')');
7054 : : }
7055 : :
7056 : : /*
7057 : : * Display an ORDER BY list.
7058 : : */
7059 : : static void
7060 : 255 : get_rule_orderby(List *orderList, List *targetList,
7061 : : bool force_colno, deparse_context *context)
7062 : : {
7063 : 255 : StringInfo buf = context->buf;
7064 : : const char *sep;
7065 : : ListCell *l;
7066 : :
7067 : 255 : sep = "";
7068 [ + - + + : 540 : foreach(l, orderList)
+ + ]
7069 : : {
7070 : 285 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
7071 : : Node *sortexpr;
7072 : : Oid sortcoltype;
7073 : : TypeCacheEntry *typentry;
7074 : :
7075 : 285 : appendStringInfoString(buf, sep);
7076 : 285 : sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
7077 : : force_colno, context);
7078 : 285 : sortcoltype = exprType(sortexpr);
7079 : : /* See whether operator is default < or > for datatype */
7080 : 285 : typentry = lookup_type_cache(sortcoltype,
7081 : : TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
7082 [ + + ]: 285 : if (srt->sortop == typentry->lt_opr)
7083 : : {
7084 : : /* ASC is default, so emit nothing for it */
7085 [ - + ]: 268 : if (srt->nulls_first)
7086 : 0 : appendStringInfoString(buf, " NULLS FIRST");
7087 : : }
7088 [ + + ]: 17 : else if (srt->sortop == typentry->gt_opr)
7089 : : {
7090 : 6 : appendStringInfoString(buf, " DESC");
7091 : : /* DESC defaults to NULLS FIRST */
7092 [ + + ]: 6 : if (!srt->nulls_first)
7093 : 1 : appendStringInfoString(buf, " NULLS LAST");
7094 : : }
7095 : : else
7096 : : {
7097 : 11 : appendStringInfo(buf, " USING %s",
7098 : : generate_operator_name(srt->sortop,
7099 : : sortcoltype,
7100 : : sortcoltype));
7101 : : /* be specific to eliminate ambiguity */
7102 [ - + ]: 11 : if (srt->nulls_first)
7103 : 0 : appendStringInfoString(buf, " NULLS FIRST");
7104 : : else
7105 : 11 : appendStringInfoString(buf, " NULLS LAST");
7106 : : }
7107 : 285 : sep = ", ";
7108 : : }
7109 : 255 : }
7110 : :
7111 : : /*
7112 : : * Display a WINDOW clause.
7113 : : *
7114 : : * Note that the windowClause list might contain only anonymous window
7115 : : * specifications, in which case we should print nothing here.
7116 : : */
7117 : : static void
7118 : 32 : get_rule_windowclause(Query *query, deparse_context *context)
7119 : : {
7120 : 32 : StringInfo buf = context->buf;
7121 : : const char *sep;
7122 : : ListCell *l;
7123 : :
7124 : 32 : sep = NULL;
7125 [ + - + + : 64 : foreach(l, query->windowClause)
+ + ]
7126 : : {
7127 : 32 : WindowClause *wc = (WindowClause *) lfirst(l);
7128 : :
7129 [ + + ]: 32 : if (wc->name == NULL)
7130 : 28 : continue; /* ignore anonymous windows */
7131 : :
7132 [ + - ]: 4 : if (sep == NULL)
7133 : 4 : appendContextKeyword(context, " WINDOW ",
7134 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7135 : : else
7136 : 0 : appendStringInfoString(buf, sep);
7137 : :
7138 : 4 : appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
7139 : :
7140 : 4 : get_rule_windowspec(wc, query->targetList, context);
7141 : :
7142 : 4 : sep = ", ";
7143 : : }
7144 : 32 : }
7145 : :
7146 : : /*
7147 : : * Display a window definition
7148 : : */
7149 : : static void
7150 : 32 : get_rule_windowspec(WindowClause *wc, List *targetList,
7151 : : deparse_context *context)
7152 : : {
7153 : 32 : StringInfo buf = context->buf;
7154 : 32 : bool needspace = false;
7155 : : const char *sep;
7156 : : ListCell *l;
7157 : :
7158 : 32 : appendStringInfoChar(buf, '(');
7159 [ - + ]: 32 : if (wc->refname)
7160 : : {
7161 : 0 : appendStringInfoString(buf, quote_identifier(wc->refname));
7162 : 0 : needspace = true;
7163 : : }
7164 : : /* partition clauses are always inherited, so only print if no refname */
7165 [ - + - - ]: 32 : if (wc->partitionClause && !wc->refname)
7166 : : {
7167 [ # # ]: 0 : if (needspace)
7168 : 0 : appendStringInfoChar(buf, ' ');
7169 : 0 : appendStringInfoString(buf, "PARTITION BY ");
7170 : 0 : sep = "";
7171 [ # # # # : 0 : foreach(l, wc->partitionClause)
# # ]
7172 : : {
7173 : 0 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
7174 : :
7175 : 0 : appendStringInfoString(buf, sep);
7176 : 0 : get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
7177 : : false, context);
7178 : 0 : sep = ", ";
7179 : : }
7180 : 0 : needspace = true;
7181 : : }
7182 : : /* print ordering clause only if not inherited */
7183 [ + - + - ]: 32 : if (wc->orderClause && !wc->copiedOrder)
7184 : : {
7185 [ - + ]: 32 : if (needspace)
7186 : 0 : appendStringInfoChar(buf, ' ');
7187 : 32 : appendStringInfoString(buf, "ORDER BY ");
7188 : 32 : get_rule_orderby(wc->orderClause, targetList, false, context);
7189 : 32 : needspace = true;
7190 : : }
7191 : : /* framing clause is never inherited, so print unless it's default */
7192 [ + + ]: 32 : if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
7193 : : {
7194 [ + - ]: 28 : if (needspace)
7195 : 28 : appendStringInfoChar(buf, ' ');
7196 : 28 : get_window_frame_options(wc->frameOptions,
7197 : : wc->startOffset, wc->endOffset,
7198 : : context);
7199 : : }
7200 : 32 : appendStringInfoChar(buf, ')');
7201 : 32 : }
7202 : :
7203 : : /*
7204 : : * Append the description of a window's framing options to context->buf
7205 : : */
7206 : : static void
7207 : 214 : get_window_frame_options(int frameOptions,
7208 : : Node *startOffset, Node *endOffset,
7209 : : deparse_context *context)
7210 : : {
7211 : 214 : StringInfo buf = context->buf;
7212 : :
7213 [ + - ]: 214 : if (frameOptions & FRAMEOPTION_NONDEFAULT)
7214 : : {
7215 [ + + ]: 214 : if (frameOptions & FRAMEOPTION_RANGE)
7216 : 25 : appendStringInfoString(buf, "RANGE ");
7217 [ + + ]: 189 : else if (frameOptions & FRAMEOPTION_ROWS)
7218 : 169 : appendStringInfoString(buf, "ROWS ");
7219 [ + - ]: 20 : else if (frameOptions & FRAMEOPTION_GROUPS)
7220 : 20 : appendStringInfoString(buf, "GROUPS ");
7221 : : else
7222 : : Assert(false);
7223 [ + + ]: 214 : if (frameOptions & FRAMEOPTION_BETWEEN)
7224 : 109 : appendStringInfoString(buf, "BETWEEN ");
7225 [ + + ]: 214 : if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
7226 : 145 : appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
7227 [ + + ]: 69 : else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
7228 : 29 : appendStringInfoString(buf, "CURRENT ROW ");
7229 [ + - ]: 40 : else if (frameOptions & FRAMEOPTION_START_OFFSET)
7230 : : {
7231 : 40 : get_rule_expr(startOffset, context, false);
7232 [ + - ]: 40 : if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
7233 : 40 : appendStringInfoString(buf, " PRECEDING ");
7234 [ # # ]: 0 : else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
7235 : 0 : appendStringInfoString(buf, " FOLLOWING ");
7236 : : else
7237 : : Assert(false);
7238 : : }
7239 : : else
7240 : : Assert(false);
7241 [ + + ]: 214 : if (frameOptions & FRAMEOPTION_BETWEEN)
7242 : : {
7243 : 109 : appendStringInfoString(buf, "AND ");
7244 [ + + ]: 109 : if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
7245 : 13 : appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
7246 [ + + ]: 96 : else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
7247 : 24 : appendStringInfoString(buf, "CURRENT ROW ");
7248 [ + - ]: 72 : else if (frameOptions & FRAMEOPTION_END_OFFSET)
7249 : : {
7250 : 72 : get_rule_expr(endOffset, context, false);
7251 [ + + ]: 72 : if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
7252 : 28 : appendStringInfoString(buf, " PRECEDING ");
7253 [ + - ]: 44 : else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
7254 : 44 : appendStringInfoString(buf, " FOLLOWING ");
7255 : : else
7256 : : Assert(false);
7257 : : }
7258 : : else
7259 : : Assert(false);
7260 : : }
7261 [ + + ]: 214 : if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
7262 : 24 : appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
7263 [ + + ]: 190 : else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
7264 : 12 : appendStringInfoString(buf, "EXCLUDE GROUP ");
7265 [ + + ]: 178 : else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
7266 : 12 : appendStringInfoString(buf, "EXCLUDE TIES ");
7267 : : /* we will now have a trailing space; remove it */
7268 : 214 : buf->data[--(buf->len)] = '\0';
7269 : : }
7270 : 214 : }
7271 : :
7272 : : /*
7273 : : * Return the description of a window's framing options as a palloc'd string
7274 : : */
7275 : : char *
7276 : 186 : get_window_frame_options_for_explain(int frameOptions,
7277 : : Node *startOffset, Node *endOffset,
7278 : : List *dpcontext, bool forceprefix)
7279 : : {
7280 : : StringInfoData buf;
7281 : : deparse_context context;
7282 : :
7283 : 186 : initStringInfo(&buf);
7284 : 186 : context.buf = &buf;
7285 : 186 : context.namespaces = dpcontext;
7286 : 186 : context.resultDesc = NULL;
7287 : 186 : context.targetList = NIL;
7288 : 186 : context.windowClause = NIL;
7289 : 186 : context.varprefix = forceprefix;
7290 : 186 : context.prettyFlags = 0;
7291 : 186 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
7292 : 186 : context.indentLevel = 0;
7293 : 186 : context.colNamesVisible = true;
7294 : 186 : context.inGroupBy = false;
7295 : 186 : context.varInOrderBy = false;
7296 : 186 : context.appendparents = NULL;
7297 : :
7298 : 186 : get_window_frame_options(frameOptions, startOffset, endOffset, &context);
7299 : :
7300 : 186 : return buf.data;
7301 : : }
7302 : :
7303 : : /* ----------
7304 : : * get_insert_query_def - Parse back an INSERT parsetree
7305 : : * ----------
7306 : : */
7307 : : static void
7308 : 194 : get_insert_query_def(Query *query, deparse_context *context)
7309 : : {
7310 : 194 : StringInfo buf = context->buf;
7311 : 194 : RangeTblEntry *select_rte = NULL;
7312 : 194 : RangeTblEntry *values_rte = NULL;
7313 : : RangeTblEntry *rte;
7314 : : char *sep;
7315 : : ListCell *l;
7316 : : List *strippedexprs;
7317 : :
7318 : : /* Insert the WITH clause if given */
7319 : 194 : get_with_clause(query, context);
7320 : :
7321 : : /*
7322 : : * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
7323 : : * single RTE for the SELECT or VALUES. Plain VALUES has neither.
7324 : : */
7325 [ + - + + : 758 : foreach(l, query->rtable)
+ + ]
7326 : : {
7327 : 564 : rte = (RangeTblEntry *) lfirst(l);
7328 : :
7329 [ + + ]: 564 : if (rte->rtekind == RTE_SUBQUERY)
7330 : : {
7331 [ - + ]: 30 : if (select_rte)
7332 [ # # ]: 0 : elog(ERROR, "too many subquery RTEs in INSERT");
7333 : 30 : select_rte = rte;
7334 : : }
7335 : :
7336 [ + + ]: 564 : if (rte->rtekind == RTE_VALUES)
7337 : : {
7338 [ - + ]: 26 : if (values_rte)
7339 [ # # ]: 0 : elog(ERROR, "too many values RTEs in INSERT");
7340 : 26 : values_rte = rte;
7341 : : }
7342 : : }
7343 [ + + - + ]: 194 : if (select_rte && values_rte)
7344 [ # # ]: 0 : elog(ERROR, "both subquery and values RTEs in INSERT");
7345 : :
7346 : : /*
7347 : : * Start the query with INSERT INTO relname
7348 : : */
7349 : 194 : rte = rt_fetch(query->resultRelation, query->rtable);
7350 : : Assert(rte->rtekind == RTE_RELATION);
7351 : :
7352 [ + - ]: 194 : if (PRETTY_INDENT(context))
7353 : : {
7354 : 194 : context->indentLevel += PRETTYINDENT_STD;
7355 : 194 : appendStringInfoChar(buf, ' ');
7356 : : }
7357 : 194 : appendStringInfo(buf, "INSERT INTO %s",
7358 : : generate_relation_name(rte->relid, NIL));
7359 : :
7360 : : /* Print the relation alias, if needed; INSERT requires explicit AS */
7361 : 194 : get_rte_alias(rte, query->resultRelation, true, context);
7362 : :
7363 : : /* always want a space here */
7364 : 194 : appendStringInfoChar(buf, ' ');
7365 : :
7366 : : /*
7367 : : * Add the insert-column-names list. Any indirection decoration needed on
7368 : : * the column names can be inferred from the top targetlist.
7369 : : */
7370 : 194 : strippedexprs = NIL;
7371 : 194 : sep = "";
7372 [ + - ]: 194 : if (query->targetList)
7373 : 194 : appendStringInfoChar(buf, '(');
7374 [ + - + + : 695 : foreach(l, query->targetList)
+ + ]
7375 : : {
7376 : 501 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7377 : :
7378 [ - + ]: 501 : if (tle->resjunk)
7379 : 0 : continue; /* ignore junk entries */
7380 : :
7381 : 501 : appendStringInfoString(buf, sep);
7382 : 501 : sep = ", ";
7383 : :
7384 : : /*
7385 : : * Put out name of target column; look in the catalogs, not at
7386 : : * tle->resname, since resname will fail to track RENAME.
7387 : : */
7388 : 501 : appendStringInfoString(buf,
7389 : 501 : quote_identifier(get_attname(rte->relid,
7390 : 501 : tle->resno,
7391 : : false)));
7392 : :
7393 : : /*
7394 : : * Print any indirection needed (subfields or subscripts), and strip
7395 : : * off the top-level nodes representing the indirection assignments.
7396 : : * Add the stripped expressions to strippedexprs. (If it's a
7397 : : * single-VALUES statement, the stripped expressions are the VALUES to
7398 : : * print below. Otherwise they're just Vars and not really
7399 : : * interesting.)
7400 : : */
7401 : 501 : strippedexprs = lappend(strippedexprs,
7402 : 501 : processIndirection((Node *) tle->expr,
7403 : : context));
7404 : : }
7405 [ + - ]: 194 : if (query->targetList)
7406 : 194 : appendStringInfoString(buf, ") ");
7407 : :
7408 [ - + ]: 194 : if (query->override)
7409 : : {
7410 [ # # ]: 0 : if (query->override == OVERRIDING_SYSTEM_VALUE)
7411 : 0 : appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
7412 [ # # ]: 0 : else if (query->override == OVERRIDING_USER_VALUE)
7413 : 0 : appendStringInfoString(buf, "OVERRIDING USER VALUE ");
7414 : : }
7415 : :
7416 [ + + ]: 194 : if (select_rte)
7417 : : {
7418 : : /* Add the SELECT */
7419 : 30 : get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
7420 : : false,
7421 : : context->prettyFlags, context->wrapColumn,
7422 : : context->indentLevel);
7423 : : }
7424 [ + + ]: 164 : else if (values_rte)
7425 : : {
7426 : : /* Add the multi-VALUES expression lists */
7427 : 26 : get_values_def(values_rte->values_lists, context);
7428 : : }
7429 [ + - ]: 138 : else if (strippedexprs)
7430 : : {
7431 : : /* Add the single-VALUES expression list */
7432 : 138 : appendContextKeyword(context, "VALUES (",
7433 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7434 : 138 : get_rule_list_toplevel(strippedexprs, context, false);
7435 : 138 : appendStringInfoChar(buf, ')');
7436 : : }
7437 : : else
7438 : : {
7439 : : /* No expressions, so it must be DEFAULT VALUES */
7440 : 0 : appendStringInfoString(buf, "DEFAULT VALUES");
7441 : : }
7442 : :
7443 : : /* Add ON CONFLICT if present */
7444 [ + + ]: 194 : if (query->onConflict)
7445 : : {
7446 : 24 : OnConflictExpr *confl = query->onConflict;
7447 : :
7448 : 24 : appendStringInfoString(buf, " ON CONFLICT");
7449 : :
7450 [ + + ]: 24 : if (confl->arbiterElems)
7451 : : {
7452 : : /* Add the single-VALUES expression list */
7453 : 20 : appendStringInfoChar(buf, '(');
7454 : 20 : get_rule_expr((Node *) confl->arbiterElems, context, false);
7455 : 20 : appendStringInfoChar(buf, ')');
7456 : :
7457 : : /* Add a WHERE clause (for partial indexes) if given */
7458 [ + + ]: 20 : if (confl->arbiterWhere != NULL)
7459 : : {
7460 : : bool save_varprefix;
7461 : :
7462 : : /*
7463 : : * Force non-prefixing of Vars, since parser assumes that they
7464 : : * belong to target relation. WHERE clause does not use
7465 : : * InferenceElem, so this is separately required.
7466 : : */
7467 : 8 : save_varprefix = context->varprefix;
7468 : 8 : context->varprefix = false;
7469 : :
7470 : 8 : appendContextKeyword(context, " WHERE ",
7471 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7472 : 8 : get_rule_expr(confl->arbiterWhere, context, false);
7473 : :
7474 : 8 : context->varprefix = save_varprefix;
7475 : : }
7476 : : }
7477 [ - + ]: 4 : else if (OidIsValid(confl->constraint))
7478 : : {
7479 : 0 : char *constraint = get_constraint_name(confl->constraint);
7480 : :
7481 [ # # ]: 0 : if (!constraint)
7482 [ # # ]: 0 : elog(ERROR, "cache lookup failed for constraint %u",
7483 : : confl->constraint);
7484 : 0 : appendStringInfo(buf, " ON CONSTRAINT %s",
7485 : : quote_identifier(constraint));
7486 : : }
7487 : :
7488 [ + + ]: 24 : if (confl->action == ONCONFLICT_NOTHING)
7489 : : {
7490 : 12 : appendStringInfoString(buf, " DO NOTHING");
7491 : : }
7492 [ + + ]: 12 : else if (confl->action == ONCONFLICT_UPDATE)
7493 : : {
7494 : 8 : appendStringInfoString(buf, " DO UPDATE SET ");
7495 : : /* Deparse targetlist */
7496 : 8 : get_update_query_targetlist_def(query, confl->onConflictSet,
7497 : : context, rte);
7498 : :
7499 : : /* Add a WHERE clause if given */
7500 [ + - ]: 8 : if (confl->onConflictWhere != NULL)
7501 : : {
7502 : 8 : appendContextKeyword(context, " WHERE ",
7503 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7504 : 8 : get_rule_expr(confl->onConflictWhere, context, false);
7505 : : }
7506 : : }
7507 : : else
7508 : : {
7509 : : Assert(confl->action == ONCONFLICT_SELECT);
7510 : 4 : appendStringInfoString(buf, " DO SELECT");
7511 : :
7512 : : /* Add FOR [KEY] UPDATE/SHARE clause if present */
7513 [ + - ]: 4 : if (confl->lockStrength != LCS_NONE)
7514 : 4 : appendStringInfoString(buf, get_lock_clause_strength(confl->lockStrength));
7515 : :
7516 : : /* Add a WHERE clause if given */
7517 [ + - ]: 4 : if (confl->onConflictWhere != NULL)
7518 : : {
7519 : 4 : appendContextKeyword(context, " WHERE ",
7520 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7521 : 4 : get_rule_expr(confl->onConflictWhere, context, false);
7522 : : }
7523 : : }
7524 : : }
7525 : :
7526 : : /* Add RETURNING if present */
7527 [ + + ]: 194 : if (query->returningList)
7528 : 51 : get_returning_clause(query, context);
7529 : 194 : }
7530 : :
7531 : :
7532 : : /* ----------
7533 : : * get_update_query_def - Parse back an UPDATE parsetree
7534 : : * ----------
7535 : : */
7536 : : static void
7537 : 102 : get_update_query_def(Query *query, deparse_context *context)
7538 : : {
7539 : 102 : StringInfo buf = context->buf;
7540 : : RangeTblEntry *rte;
7541 : :
7542 : : /* Insert the WITH clause if given */
7543 : 102 : get_with_clause(query, context);
7544 : :
7545 : : /*
7546 : : * Start the query with UPDATE relname SET
7547 : : */
7548 : 102 : rte = rt_fetch(query->resultRelation, query->rtable);
7549 : : Assert(rte->rtekind == RTE_RELATION);
7550 [ + - ]: 102 : if (PRETTY_INDENT(context))
7551 : : {
7552 : 102 : appendStringInfoChar(buf, ' ');
7553 : 102 : context->indentLevel += PRETTYINDENT_STD;
7554 : : }
7555 : 204 : appendStringInfo(buf, "UPDATE %s%s",
7556 [ + - ]: 102 : only_marker(rte),
7557 : : generate_relation_name(rte->relid, NIL));
7558 : :
7559 : : /* Print the FOR PORTION OF, if needed */
7560 : 102 : get_for_portion_of(query->forPortionOf, rte, context);
7561 : :
7562 : : /* Print the relation alias, if needed */
7563 : 102 : get_rte_alias(rte, query->resultRelation, false, context);
7564 : :
7565 : 102 : appendStringInfoString(buf, " SET ");
7566 : :
7567 : : /* Deparse targetlist */
7568 : 102 : get_update_query_targetlist_def(query, query->targetList, context, rte);
7569 : :
7570 : : /* Add the FROM clause if needed */
7571 : 102 : get_from_clause(query, " FROM ", context);
7572 : :
7573 : : /* Add a WHERE clause if given */
7574 [ + + ]: 102 : if (query->jointree->quals != NULL)
7575 : : {
7576 : 61 : appendContextKeyword(context, " WHERE ",
7577 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7578 : 61 : get_rule_expr(query->jointree->quals, context, false);
7579 : : }
7580 : :
7581 : : /* Add RETURNING if present */
7582 [ + + ]: 102 : if (query->returningList)
7583 : 37 : get_returning_clause(query, context);
7584 : 102 : }
7585 : :
7586 : :
7587 : : /* ----------
7588 : : * get_update_query_targetlist_def - Parse back an UPDATE targetlist
7589 : : * ----------
7590 : : */
7591 : : static void
7592 : 126 : get_update_query_targetlist_def(Query *query, List *targetList,
7593 : : deparse_context *context, RangeTblEntry *rte)
7594 : : {
7595 : 126 : StringInfo buf = context->buf;
7596 : : ListCell *l;
7597 : : ListCell *next_ma_cell;
7598 : : int remaining_ma_columns;
7599 : : const char *sep;
7600 : : SubLink *cur_ma_sublink;
7601 : : List *ma_sublinks;
7602 : :
7603 : : /*
7604 : : * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
7605 : : * into a list. We expect them to appear, in ID order, in resjunk tlist
7606 : : * entries.
7607 : : */
7608 : 126 : ma_sublinks = NIL;
7609 [ + + ]: 126 : if (query->hasSubLinks) /* else there can't be any */
7610 : : {
7611 [ + - + + : 28 : foreach(l, targetList)
+ + ]
7612 : : {
7613 : 20 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7614 : :
7615 [ + + + - ]: 20 : if (tle->resjunk && IsA(tle->expr, SubLink))
7616 : : {
7617 : 4 : SubLink *sl = (SubLink *) tle->expr;
7618 : :
7619 [ + - ]: 4 : if (sl->subLinkType == MULTIEXPR_SUBLINK)
7620 : : {
7621 : 4 : ma_sublinks = lappend(ma_sublinks, sl);
7622 : : Assert(sl->subLinkId == list_length(ma_sublinks));
7623 : : }
7624 : : }
7625 : : }
7626 : : }
7627 : 126 : next_ma_cell = list_head(ma_sublinks);
7628 : 126 : cur_ma_sublink = NULL;
7629 : 126 : remaining_ma_columns = 0;
7630 : :
7631 : : /* Add the comma separated list of 'attname = value' */
7632 : 126 : sep = "";
7633 [ + - + + : 314 : foreach(l, targetList)
+ + ]
7634 : : {
7635 : 188 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7636 : : Node *expr;
7637 : :
7638 [ + + ]: 188 : if (tle->resjunk)
7639 : 4 : continue; /* ignore junk entries */
7640 : :
7641 : : /* Emit separator (OK whether we're in multiassignment or not) */
7642 : 184 : appendStringInfoString(buf, sep);
7643 : 184 : sep = ", ";
7644 : :
7645 : : /*
7646 : : * Check to see if we're starting a multiassignment group: if so,
7647 : : * output a left paren.
7648 : : */
7649 [ + + + - ]: 184 : if (next_ma_cell != NULL && cur_ma_sublink == NULL)
7650 : : {
7651 : : /*
7652 : : * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
7653 : : * Param. That could be buried under FieldStores and
7654 : : * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
7655 : : * and underneath those there could be an implicit type coercion.
7656 : : * Because we would ignore implicit type coercions anyway, we
7657 : : * don't need to be as careful as processIndirection() is about
7658 : : * descending past implicit CoerceToDomains.
7659 : : */
7660 : 4 : expr = (Node *) tle->expr;
7661 [ + - ]: 8 : while (expr)
7662 : : {
7663 [ - + ]: 8 : if (IsA(expr, FieldStore))
7664 : : {
7665 : 0 : FieldStore *fstore = (FieldStore *) expr;
7666 : :
7667 : 0 : expr = (Node *) linitial(fstore->newvals);
7668 : : }
7669 [ + + ]: 8 : else if (IsA(expr, SubscriptingRef))
7670 : : {
7671 : 4 : SubscriptingRef *sbsref = (SubscriptingRef *) expr;
7672 : :
7673 [ - + ]: 4 : if (sbsref->refassgnexpr == NULL)
7674 : 0 : break;
7675 : :
7676 : 4 : expr = (Node *) sbsref->refassgnexpr;
7677 : : }
7678 [ - + ]: 4 : else if (IsA(expr, CoerceToDomain))
7679 : : {
7680 : 0 : CoerceToDomain *cdomain = (CoerceToDomain *) expr;
7681 : :
7682 [ # # ]: 0 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
7683 : 0 : break;
7684 : 0 : expr = (Node *) cdomain->arg;
7685 : : }
7686 : : else
7687 : 4 : break;
7688 : : }
7689 : 4 : expr = strip_implicit_coercions(expr);
7690 : :
7691 [ + - + - ]: 4 : if (expr && IsA(expr, Param) &&
7692 [ + - ]: 4 : ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
7693 : : {
7694 : 4 : cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
7695 : 4 : next_ma_cell = lnext(ma_sublinks, next_ma_cell);
7696 : 4 : remaining_ma_columns = count_nonjunk_tlist_entries(((Query *) cur_ma_sublink->subselect)->targetList);
7697 : : Assert(((Param *) expr)->paramid ==
7698 : : ((cur_ma_sublink->subLinkId << 16) | 1));
7699 : 4 : appendStringInfoChar(buf, '(');
7700 : : }
7701 : : }
7702 : :
7703 : : /*
7704 : : * Put out name of target column; look in the catalogs, not at
7705 : : * tle->resname, since resname will fail to track RENAME.
7706 : : */
7707 : 184 : appendStringInfoString(buf,
7708 : 184 : quote_identifier(get_attname(rte->relid,
7709 : 184 : tle->resno,
7710 : : false)));
7711 : :
7712 : : /*
7713 : : * Print any indirection needed (subfields or subscripts), and strip
7714 : : * off the top-level nodes representing the indirection assignments.
7715 : : */
7716 : 184 : expr = processIndirection((Node *) tle->expr, context);
7717 : :
7718 : : /*
7719 : : * If we're in a multiassignment, skip printing anything more, unless
7720 : : * this is the last column; in which case, what we print should be the
7721 : : * sublink, not the Param.
7722 : : */
7723 [ + + ]: 184 : if (cur_ma_sublink != NULL)
7724 : : {
7725 [ + + ]: 12 : if (--remaining_ma_columns > 0)
7726 : 8 : continue; /* not the last column of multiassignment */
7727 : 4 : appendStringInfoChar(buf, ')');
7728 : 4 : expr = (Node *) cur_ma_sublink;
7729 : 4 : cur_ma_sublink = NULL;
7730 : : }
7731 : :
7732 : 176 : appendStringInfoString(buf, " = ");
7733 : :
7734 : 176 : get_rule_expr(expr, context, false);
7735 : : }
7736 : 126 : }
7737 : :
7738 : :
7739 : : /* ----------
7740 : : * get_delete_query_def - Parse back a DELETE parsetree
7741 : : * ----------
7742 : : */
7743 : : static void
7744 : 55 : get_delete_query_def(Query *query, deparse_context *context)
7745 : : {
7746 : 55 : StringInfo buf = context->buf;
7747 : : RangeTblEntry *rte;
7748 : :
7749 : : /* Insert the WITH clause if given */
7750 : 55 : get_with_clause(query, context);
7751 : :
7752 : : /*
7753 : : * Start the query with DELETE FROM relname
7754 : : */
7755 : 55 : rte = rt_fetch(query->resultRelation, query->rtable);
7756 : : Assert(rte->rtekind == RTE_RELATION);
7757 [ + - ]: 55 : if (PRETTY_INDENT(context))
7758 : : {
7759 : 55 : appendStringInfoChar(buf, ' ');
7760 : 55 : context->indentLevel += PRETTYINDENT_STD;
7761 : : }
7762 : 110 : appendStringInfo(buf, "DELETE FROM %s%s",
7763 [ + - ]: 55 : only_marker(rte),
7764 : : generate_relation_name(rte->relid, NIL));
7765 : :
7766 : : /* Print the FOR PORTION OF, if needed */
7767 : 55 : get_for_portion_of(query->forPortionOf, rte, context);
7768 : :
7769 : : /* Print the relation alias, if needed */
7770 : 55 : get_rte_alias(rte, query->resultRelation, false, context);
7771 : :
7772 : : /* Add the USING clause if given */
7773 : 55 : get_from_clause(query, " USING ", context);
7774 : :
7775 : : /* Add a WHERE clause if given */
7776 [ + + ]: 55 : if (query->jointree->quals != NULL)
7777 : : {
7778 : 39 : appendContextKeyword(context, " WHERE ",
7779 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7780 : 39 : get_rule_expr(query->jointree->quals, context, false);
7781 : : }
7782 : :
7783 : : /* Add RETURNING if present */
7784 [ + + ]: 55 : if (query->returningList)
7785 : 17 : get_returning_clause(query, context);
7786 : 55 : }
7787 : :
7788 : :
7789 : : /* ----------
7790 : : * get_merge_query_def - Parse back a MERGE parsetree
7791 : : * ----------
7792 : : */
7793 : : static void
7794 : 8 : get_merge_query_def(Query *query, deparse_context *context)
7795 : : {
7796 : 8 : StringInfo buf = context->buf;
7797 : : RangeTblEntry *rte;
7798 : : ListCell *lc;
7799 : : bool haveNotMatchedBySource;
7800 : :
7801 : : /* Insert the WITH clause if given */
7802 : 8 : get_with_clause(query, context);
7803 : :
7804 : : /*
7805 : : * Start the query with MERGE INTO relname
7806 : : */
7807 : 8 : rte = rt_fetch(query->resultRelation, query->rtable);
7808 : : Assert(rte->rtekind == RTE_RELATION);
7809 [ + - ]: 8 : if (PRETTY_INDENT(context))
7810 : : {
7811 : 8 : appendStringInfoChar(buf, ' ');
7812 : 8 : context->indentLevel += PRETTYINDENT_STD;
7813 : : }
7814 : 16 : appendStringInfo(buf, "MERGE INTO %s%s",
7815 [ + - ]: 8 : only_marker(rte),
7816 : : generate_relation_name(rte->relid, NIL));
7817 : :
7818 : : /* Print the relation alias, if needed */
7819 : 8 : get_rte_alias(rte, query->resultRelation, false, context);
7820 : :
7821 : : /* Print the source relation and join clause */
7822 : 8 : get_from_clause(query, " USING ", context);
7823 : 8 : appendContextKeyword(context, " ON ",
7824 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7825 : 8 : get_rule_expr(query->mergeJoinCondition, context, false);
7826 : :
7827 : : /*
7828 : : * Test for any NOT MATCHED BY SOURCE actions. If there are none, then
7829 : : * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
7830 : : * SQL standard. Otherwise, we have a non-SQL-standard query, so output
7831 : : * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
7832 : : * more explicit.
7833 : : */
7834 : 8 : haveNotMatchedBySource = false;
7835 [ + - + + : 56 : foreach(lc, query->mergeActionList)
+ + ]
7836 : : {
7837 : 52 : MergeAction *action = lfirst_node(MergeAction, lc);
7838 : :
7839 [ + + ]: 52 : if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
7840 : : {
7841 : 4 : haveNotMatchedBySource = true;
7842 : 4 : break;
7843 : : }
7844 : : }
7845 : :
7846 : : /* Print each merge action */
7847 [ + - + + : 60 : foreach(lc, query->mergeActionList)
+ + ]
7848 : : {
7849 : 52 : MergeAction *action = lfirst_node(MergeAction, lc);
7850 : :
7851 : 52 : appendContextKeyword(context, " WHEN ",
7852 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
7853 [ + + + - ]: 52 : switch (action->matchKind)
7854 : : {
7855 : 24 : case MERGE_WHEN_MATCHED:
7856 : 24 : appendStringInfoString(buf, "MATCHED");
7857 : 24 : break;
7858 : 4 : case MERGE_WHEN_NOT_MATCHED_BY_SOURCE:
7859 : 4 : appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
7860 : 4 : break;
7861 : 24 : case MERGE_WHEN_NOT_MATCHED_BY_TARGET:
7862 [ + + ]: 24 : if (haveNotMatchedBySource)
7863 : 4 : appendStringInfoString(buf, "NOT MATCHED BY TARGET");
7864 : : else
7865 : 20 : appendStringInfoString(buf, "NOT MATCHED");
7866 : 24 : break;
7867 : 0 : default:
7868 [ # # ]: 0 : elog(ERROR, "unrecognized matchKind: %d",
7869 : : (int) action->matchKind);
7870 : : }
7871 : :
7872 [ + + ]: 52 : if (action->qual)
7873 : : {
7874 : 32 : appendContextKeyword(context, " AND ",
7875 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7876 : 32 : get_rule_expr(action->qual, context, false);
7877 : : }
7878 : 52 : appendContextKeyword(context, " THEN ",
7879 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7880 : :
7881 [ + + ]: 52 : if (action->commandType == CMD_INSERT)
7882 : : {
7883 : : /* This generally matches get_insert_query_def() */
7884 : 24 : List *strippedexprs = NIL;
7885 : 24 : const char *sep = "";
7886 : : ListCell *lc2;
7887 : :
7888 : 24 : appendStringInfoString(buf, "INSERT");
7889 : :
7890 [ + + ]: 24 : if (action->targetList)
7891 : 20 : appendStringInfoString(buf, " (");
7892 [ + + + + : 68 : foreach(lc2, action->targetList)
+ + ]
7893 : : {
7894 : 44 : TargetEntry *tle = (TargetEntry *) lfirst(lc2);
7895 : :
7896 : : Assert(!tle->resjunk);
7897 : :
7898 : 44 : appendStringInfoString(buf, sep);
7899 : 44 : sep = ", ";
7900 : :
7901 : 44 : appendStringInfoString(buf,
7902 : 44 : quote_identifier(get_attname(rte->relid,
7903 : 44 : tle->resno,
7904 : : false)));
7905 : 44 : strippedexprs = lappend(strippedexprs,
7906 : 44 : processIndirection((Node *) tle->expr,
7907 : : context));
7908 : : }
7909 [ + + ]: 24 : if (action->targetList)
7910 : 20 : appendStringInfoChar(buf, ')');
7911 : :
7912 [ + + ]: 24 : if (action->override)
7913 : : {
7914 [ - + ]: 4 : if (action->override == OVERRIDING_SYSTEM_VALUE)
7915 : 0 : appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
7916 [ + - ]: 4 : else if (action->override == OVERRIDING_USER_VALUE)
7917 : 4 : appendStringInfoString(buf, " OVERRIDING USER VALUE");
7918 : : }
7919 : :
7920 [ + + ]: 24 : if (strippedexprs)
7921 : : {
7922 : 20 : appendContextKeyword(context, " VALUES (",
7923 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);
7924 : 20 : get_rule_list_toplevel(strippedexprs, context, false);
7925 : 20 : appendStringInfoChar(buf, ')');
7926 : : }
7927 : : else
7928 : 4 : appendStringInfoString(buf, " DEFAULT VALUES");
7929 : : }
7930 [ + + ]: 28 : else if (action->commandType == CMD_UPDATE)
7931 : : {
7932 : 16 : appendStringInfoString(buf, "UPDATE SET ");
7933 : 16 : get_update_query_targetlist_def(query, action->targetList,
7934 : : context, rte);
7935 : : }
7936 [ + + ]: 12 : else if (action->commandType == CMD_DELETE)
7937 : 8 : appendStringInfoString(buf, "DELETE");
7938 [ + - ]: 4 : else if (action->commandType == CMD_NOTHING)
7939 : 4 : appendStringInfoString(buf, "DO NOTHING");
7940 : : }
7941 : :
7942 : : /* Add RETURNING if present */
7943 [ + + ]: 8 : if (query->returningList)
7944 : 4 : get_returning_clause(query, context);
7945 : 8 : }
7946 : :
7947 : :
7948 : : /* ----------
7949 : : * get_utility_query_def - Parse back a UTILITY parsetree
7950 : : * ----------
7951 : : */
7952 : : static void
7953 : 9 : get_utility_query_def(Query *query, deparse_context *context)
7954 : : {
7955 : 9 : StringInfo buf = context->buf;
7956 : :
7957 [ + - + - ]: 9 : if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7958 : 9 : {
7959 : 9 : NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7960 : :
7961 : 9 : appendContextKeyword(context, "",
7962 : : 0, PRETTYINDENT_STD, 1);
7963 : 9 : appendStringInfo(buf, "NOTIFY %s",
7964 : 9 : quote_identifier(stmt->conditionname));
7965 [ - + ]: 9 : if (stmt->payload)
7966 : : {
7967 : 0 : appendStringInfoString(buf, ", ");
7968 : 0 : simple_quote_literal(buf, stmt->payload);
7969 : : }
7970 : : }
7971 : : else
7972 : : {
7973 : : /* Currently only NOTIFY utility commands can appear in rules */
7974 [ # # ]: 0 : elog(ERROR, "unexpected utility statement type");
7975 : : }
7976 : 9 : }
7977 : :
7978 : :
7979 : : /*
7980 : : * Parse back a graph label expression
7981 : : */
7982 : : static void
7983 : 96 : get_graph_label_expr(Node *label_expr, deparse_context *context)
7984 : : {
7985 : 96 : StringInfo buf = context->buf;
7986 : :
7987 : 96 : check_stack_depth();
7988 : :
7989 [ + + - ]: 96 : switch (nodeTag(label_expr))
7990 : : {
7991 : 78 : case T_GraphLabelRef:
7992 : : {
7993 : 78 : GraphLabelRef *lref = (GraphLabelRef *) label_expr;
7994 : :
7995 : 78 : appendStringInfoString(buf, quote_identifier(get_propgraph_label_name(lref->labelid)));
7996 : 78 : break;
7997 : : }
7998 : :
7999 : 18 : case T_BoolExpr:
8000 : : {
8001 : 18 : BoolExpr *be = (BoolExpr *) label_expr;
8002 : : ListCell *lc;
8003 : 18 : bool first = true;
8004 : :
8005 : : Assert(be->boolop == OR_EXPR);
8006 : :
8007 [ + - + + : 54 : foreach(lc, be->args)
+ + ]
8008 : : {
8009 [ + + ]: 36 : if (!first)
8010 : : {
8011 [ + - ]: 18 : if (be->boolop == OR_EXPR)
8012 : 18 : appendStringInfoChar(buf, '|');
8013 : : }
8014 : : else
8015 : 18 : first = false;
8016 : 36 : get_graph_label_expr(lfirst(lc), context);
8017 : : }
8018 : :
8019 : 18 : break;
8020 : : }
8021 : :
8022 : 0 : default:
8023 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(label_expr));
8024 : : break;
8025 : : }
8026 : 96 : }
8027 : :
8028 : : /*
8029 : : * Parse back a path pattern expression
8030 : : */
8031 : : static void
8032 : 14 : get_path_pattern_expr_def(List *path_pattern_expr, deparse_context *context)
8033 : : {
8034 : 14 : StringInfo buf = context->buf;
8035 : : ListCell *lc;
8036 : :
8037 [ + - + + : 74 : foreach(lc, path_pattern_expr)
+ + ]
8038 : : {
8039 : 60 : GraphElementPattern *gep = lfirst_node(GraphElementPattern, lc);
8040 : 60 : const char *sep = "";
8041 : :
8042 [ + - + - : 60 : switch (gep->kind)
- ]
8043 : : {
8044 : 37 : case VERTEX_PATTERN:
8045 : 37 : appendStringInfoChar(buf, '(');
8046 : 37 : break;
8047 : 0 : case EDGE_PATTERN_LEFT:
8048 : 0 : appendStringInfoString(buf, "<-[");
8049 : 0 : break;
8050 : 23 : case EDGE_PATTERN_RIGHT:
8051 : : case EDGE_PATTERN_ANY:
8052 : 23 : appendStringInfoString(buf, "-[");
8053 : 23 : break;
8054 : 0 : case PAREN_EXPR:
8055 : 0 : appendStringInfoChar(buf, '(');
8056 : 0 : break;
8057 : : }
8058 : :
8059 [ + + ]: 60 : if (gep->variable)
8060 : : {
8061 : 37 : appendStringInfoString(buf, quote_identifier(gep->variable));
8062 : 37 : sep = " ";
8063 : : }
8064 : :
8065 [ + - ]: 60 : if (gep->labelexpr)
8066 : : {
8067 : 60 : appendStringInfoString(buf, sep);
8068 : 60 : appendStringInfoString(buf, "IS ");
8069 : 60 : get_graph_label_expr(gep->labelexpr, context);
8070 : 60 : sep = " ";
8071 : : }
8072 : :
8073 [ - + ]: 60 : if (gep->subexpr)
8074 : : {
8075 : 0 : appendStringInfoString(buf, sep);
8076 : 0 : get_path_pattern_expr_def(gep->subexpr, context);
8077 : 0 : sep = " ";
8078 : : }
8079 : :
8080 [ + + ]: 60 : if (gep->whereClause)
8081 : : {
8082 : 14 : appendStringInfoString(buf, sep);
8083 : 14 : appendStringInfoString(buf, "WHERE ");
8084 : 14 : get_rule_expr(gep->whereClause, context, false);
8085 : : }
8086 : :
8087 [ + - + - : 60 : switch (gep->kind)
- ]
8088 : : {
8089 : 37 : case VERTEX_PATTERN:
8090 : 37 : appendStringInfoChar(buf, ')');
8091 : 37 : break;
8092 : 0 : case EDGE_PATTERN_LEFT:
8093 : : case EDGE_PATTERN_ANY:
8094 : 0 : appendStringInfoString(buf, "]-");
8095 : 0 : break;
8096 : 23 : case EDGE_PATTERN_RIGHT:
8097 : 23 : appendStringInfoString(buf, "]->");
8098 : 23 : break;
8099 : 0 : case PAREN_EXPR:
8100 : 0 : appendStringInfoChar(buf, ')');
8101 : 0 : break;
8102 : : }
8103 : :
8104 [ - + ]: 60 : if (gep->quantifier)
8105 : : {
8106 : 0 : int lower = linitial_int(gep->quantifier);
8107 : 0 : int upper = lsecond_int(gep->quantifier);
8108 : :
8109 : 0 : appendStringInfo(buf, "{%d,%d}", lower, upper);
8110 : : }
8111 : : }
8112 : 14 : }
8113 : :
8114 : : /*
8115 : : * Parse back a graph pattern
8116 : : */
8117 : : static void
8118 : 14 : get_graph_pattern_def(GraphPattern *graph_pattern, deparse_context *context)
8119 : : {
8120 : 14 : StringInfo buf = context->buf;
8121 : : ListCell *lc;
8122 : 14 : bool first = true;
8123 : :
8124 [ + - + + : 28 : foreach(lc, graph_pattern->path_pattern_list)
+ + ]
8125 : : {
8126 : 14 : List *path_pattern_expr = lfirst_node(List, lc);
8127 : :
8128 [ - + ]: 14 : if (!first)
8129 : 0 : appendStringInfoString(buf, ", ");
8130 : : else
8131 : 14 : first = false;
8132 : :
8133 : 14 : get_path_pattern_expr_def(path_pattern_expr, context);
8134 : : }
8135 : :
8136 [ + + ]: 14 : if (graph_pattern->whereClause)
8137 : : {
8138 : 9 : appendStringInfoString(buf, " WHERE ");
8139 : 9 : get_rule_expr(graph_pattern->whereClause, context, false);
8140 : : }
8141 : 14 : }
8142 : :
8143 : : /*
8144 : : * Display a Var appropriately.
8145 : : *
8146 : : * In some cases (currently only when recursing into an unnamed join)
8147 : : * the Var's varlevelsup has to be interpreted with respect to a context
8148 : : * above the current one; levelsup indicates the offset.
8149 : : *
8150 : : * If istoplevel is true, the Var is at the top level of a SELECT's
8151 : : * targetlist, which means we need special treatment of whole-row Vars.
8152 : : * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
8153 : : * dirty hack to prevent "tab.*" from being expanded into multiple columns.
8154 : : * (The parser will strip the useless coercion, so no inefficiency is added in
8155 : : * dump and reload.) We used to print just "tab" in such cases, but that is
8156 : : * ambiguous and will yield the wrong result if "tab" is also a plain column
8157 : : * name in the query.
8158 : : *
8159 : : * Returns the attname of the Var, or NULL if the Var has no attname (because
8160 : : * it is a whole-row Var or a subplan output reference).
8161 : : */
8162 : : static char *
8163 : 127403 : get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
8164 : : {
8165 : 127403 : StringInfo buf = context->buf;
8166 : : RangeTblEntry *rte;
8167 : : AttrNumber attnum;
8168 : : int netlevelsup;
8169 : : deparse_namespace *dpns;
8170 : : int varno;
8171 : : AttrNumber varattno;
8172 : : deparse_columns *colinfo;
8173 : : char *refname;
8174 : : char *attname;
8175 : : bool need_prefix;
8176 : :
8177 : : /* Find appropriate nesting depth */
8178 : 127403 : netlevelsup = var->varlevelsup + levelsup;
8179 [ - + ]: 127403 : if (netlevelsup >= list_length(context->namespaces))
8180 [ # # ]: 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
8181 : : var->varlevelsup, levelsup);
8182 : 127403 : dpns = (deparse_namespace *) list_nth(context->namespaces,
8183 : : netlevelsup);
8184 : :
8185 : : /*
8186 : : * If we have a syntactic referent for the Var, and we're working from a
8187 : : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8188 : : * on the semantic referent. (Forcing use of the semantic referent when
8189 : : * printing plan trees is a design choice that's perhaps more motivated by
8190 : : * backwards compatibility than anything else. But it does have the
8191 : : * advantage of making plans more explicit.)
8192 : : */
8193 [ + + + + ]: 127403 : if (var->varnosyn > 0 && dpns->plan == NULL)
8194 : : {
8195 : 25128 : varno = var->varnosyn;
8196 : 25128 : varattno = var->varattnosyn;
8197 : : }
8198 : : else
8199 : : {
8200 : 102275 : varno = var->varno;
8201 : 102275 : varattno = var->varattno;
8202 : : }
8203 : :
8204 : : /*
8205 : : * Try to find the relevant RTE in this rtable. In a plan tree, it's
8206 : : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8207 : : * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
8208 : : * find the aliases previously assigned for this RTE.
8209 : : */
8210 [ + + + - ]: 127403 : if (varno >= 1 && varno <= list_length(dpns->rtable))
8211 : : {
8212 : : /*
8213 : : * We might have been asked to map child Vars to some parent relation.
8214 : : */
8215 [ + + + + ]: 92673 : if (context->appendparents && dpns->appendrels)
8216 : : {
8217 : 2517 : int pvarno = varno;
8218 : 2517 : AttrNumber pvarattno = varattno;
8219 : 2517 : AppendRelInfo *appinfo = dpns->appendrels[pvarno];
8220 : 2517 : bool found = false;
8221 : :
8222 : : /* Only map up to inheritance parents, not UNION ALL appendrels */
8223 [ + + ]: 5080 : while (appinfo &&
8224 : 2794 : rt_fetch(appinfo->parent_relid,
8225 [ + + ]: 2794 : dpns->rtable)->rtekind == RTE_RELATION)
8226 : : {
8227 : 2563 : found = false;
8228 [ + + ]: 2563 : if (pvarattno > 0) /* system columns stay as-is */
8229 : : {
8230 [ - + ]: 2411 : if (pvarattno > appinfo->num_child_cols)
8231 : 0 : break; /* safety check */
8232 : 2411 : pvarattno = appinfo->parent_colnos[pvarattno - 1];
8233 [ - + ]: 2411 : if (pvarattno == 0)
8234 : 0 : break; /* Var is local to child */
8235 : : }
8236 : :
8237 : 2563 : pvarno = appinfo->parent_relid;
8238 : 2563 : found = true;
8239 : :
8240 : : /* If the parent is itself a child, continue up. */
8241 : : Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
8242 : 2563 : appinfo = dpns->appendrels[pvarno];
8243 : : }
8244 : :
8245 : : /*
8246 : : * If we found an ancestral rel, and that rel is included in
8247 : : * appendparents, print that column not the original one.
8248 : : */
8249 [ + + + + ]: 2517 : if (found && bms_is_member(pvarno, context->appendparents))
8250 : : {
8251 : 2038 : varno = pvarno;
8252 : 2038 : varattno = pvarattno;
8253 : : }
8254 : : }
8255 : :
8256 : 92673 : rte = rt_fetch(varno, dpns->rtable);
8257 : :
8258 : : /* might be returning old/new column value */
8259 [ + + ]: 92673 : if (var->varreturningtype == VAR_RETURNING_OLD)
8260 : 274 : refname = dpns->ret_old_alias;
8261 [ + + ]: 92399 : else if (var->varreturningtype == VAR_RETURNING_NEW)
8262 : 273 : refname = dpns->ret_new_alias;
8263 : : else
8264 : 92126 : refname = (char *) list_nth(dpns->rtable_names, varno - 1);
8265 : :
8266 : 92673 : colinfo = deparse_columns_fetch(varno, dpns);
8267 : 92673 : attnum = varattno;
8268 : : }
8269 : : else
8270 : : {
8271 : 34730 : resolve_special_varno((Node *) var, context,
8272 : : get_special_variable, NULL);
8273 : 34730 : return NULL;
8274 : : }
8275 : :
8276 : : /*
8277 : : * The planner will sometimes emit Vars referencing resjunk elements of a
8278 : : * subquery's target list (this is currently only possible if it chooses
8279 : : * to generate a "physical tlist" for a SubqueryScan or CteScan node).
8280 : : * Although we prefer to print subquery-referencing Vars using the
8281 : : * subquery's alias, that's not possible for resjunk items since they have
8282 : : * no alias. So in that case, drill down to the subplan and print the
8283 : : * contents of the referenced tlist item. This works because in a plan
8284 : : * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
8285 : : * we'll have set dpns->inner_plan to reference the child plan node.
8286 : : */
8287 [ + + + + : 95852 : if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
+ + ]
8288 : 3179 : attnum > list_length(rte->eref->colnames) &&
8289 [ + - ]: 1 : dpns->inner_plan)
8290 : : {
8291 : : TargetEntry *tle;
8292 : : deparse_namespace save_dpns;
8293 : :
8294 : 1 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8295 [ - + ]: 1 : if (!tle)
8296 [ # # ]: 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
8297 : : attnum, rte->eref->aliasname);
8298 : :
8299 : : Assert(netlevelsup == 0);
8300 : 1 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8301 : :
8302 : : /*
8303 : : * Force parentheses because our caller probably assumed a Var is a
8304 : : * simple expression.
8305 : : */
8306 [ - + ]: 1 : if (!IsA(tle->expr, Var))
8307 : 0 : appendStringInfoChar(buf, '(');
8308 : 1 : get_rule_expr((Node *) tle->expr, context, true);
8309 [ - + ]: 1 : if (!IsA(tle->expr, Var))
8310 : 0 : appendStringInfoChar(buf, ')');
8311 : :
8312 : 1 : pop_child_plan(dpns, &save_dpns);
8313 : 1 : return NULL;
8314 : : }
8315 : :
8316 : : /*
8317 : : * If it's an unnamed join, look at the expansion of the alias variable.
8318 : : * If it's a simple reference to one of the input vars, then recursively
8319 : : * print the name of that var instead. When it's not a simple reference,
8320 : : * we have to just print the unqualified join column name. (This can only
8321 : : * happen with "dangerous" merged columns in a JOIN USING; we took pains
8322 : : * previously to make the unqualified column name unique in such cases.)
8323 : : *
8324 : : * This wouldn't work in decompiling plan trees, because we don't store
8325 : : * joinaliasvars lists after planning; but a plan tree should never
8326 : : * contain a join alias variable.
8327 : : */
8328 [ + + + + ]: 92672 : if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
8329 : : {
8330 [ - + ]: 72 : if (rte->joinaliasvars == NIL)
8331 [ # # ]: 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
8332 [ + - ]: 72 : if (attnum > 0)
8333 : : {
8334 : : Var *aliasvar;
8335 : :
8336 : 72 : aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
8337 : : /* we intentionally don't strip implicit coercions here */
8338 [ + - - + ]: 72 : if (aliasvar && IsA(aliasvar, Var))
8339 : : {
8340 : 0 : return get_variable(aliasvar, var->varlevelsup + levelsup,
8341 : : istoplevel, context);
8342 : : }
8343 : : }
8344 : :
8345 : : /*
8346 : : * Unnamed join has no refname. (Note: since it's unnamed, there is
8347 : : * no way the user could have referenced it to create a whole-row Var
8348 : : * for it. So we don't have to cover that case below.)
8349 : : */
8350 : : Assert(refname == NULL);
8351 : : }
8352 : :
8353 [ + + ]: 92672 : if (attnum == InvalidAttrNumber)
8354 : 693 : attname = NULL;
8355 [ + + ]: 91979 : else if (attnum > 0)
8356 : : {
8357 : : /* Get column name to use from the colinfo struct */
8358 [ - + ]: 90755 : if (attnum > colinfo->num_cols)
8359 [ # # ]: 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
8360 : : attnum, rte->eref->aliasname);
8361 : 90755 : attname = colinfo->colnames[attnum - 1];
8362 : :
8363 : : /*
8364 : : * If we find a Var referencing a dropped column, it seems better to
8365 : : * print something (anything) than to fail. In general this should
8366 : : * not happen, but it used to be possible for some cases involving
8367 : : * functions returning named composite types, and perhaps there are
8368 : : * still bugs out there.
8369 : : */
8370 [ + + ]: 90755 : if (attname == NULL)
8371 : 4 : attname = "?dropped?column?";
8372 : : }
8373 : : else
8374 : : {
8375 : : /* System column - name is fixed, get it from the catalog */
8376 : 1224 : attname = get_rte_attribute_name(rte, attnum);
8377 : : }
8378 : :
8379 [ + + + + ]: 136576 : need_prefix = (context->varprefix || attname == NULL ||
8380 [ + + ]: 43904 : var->varreturningtype != VAR_RETURNING_DEFAULT);
8381 : :
8382 : : /*
8383 : : * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
8384 : : * clause, we may need to add a table-name prefix to prevent
8385 : : * findTargetlistEntrySQL92 from misinterpreting the name as an
8386 : : * output-column name. To avoid cluttering the output with unnecessary
8387 : : * prefixes, do so only if there is a name match to a SELECT tlist item
8388 : : * that is different from the Var.
8389 : : */
8390 [ + + + + : 92672 : if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
+ + ]
8391 : : {
8392 : 179 : int colno = 0;
8393 : :
8394 [ + + + + : 661 : foreach_node(TargetEntry, tle, context->targetList)
+ + ]
8395 : : {
8396 : : char *colname;
8397 : :
8398 [ - + ]: 311 : if (tle->resjunk)
8399 : 0 : continue; /* ignore junk entries */
8400 : 311 : colno++;
8401 : :
8402 : : /* This must match colname-choosing logic in get_target_list() */
8403 [ + - + - ]: 311 : if (context->resultDesc && colno <= context->resultDesc->natts)
8404 : 311 : colname = NameStr(TupleDescAttr(context->resultDesc,
8405 : : colno - 1)->attname);
8406 : : else
8407 : 0 : colname = tle->resname;
8408 : :
8409 [ + - + + ]: 311 : if (colname && strcmp(colname, attname) == 0 &&
8410 [ + + ]: 106 : !equal(var, tle->expr))
8411 : : {
8412 : 8 : need_prefix = true;
8413 : 8 : break;
8414 : : }
8415 : : }
8416 : : }
8417 : :
8418 [ + + + + ]: 92672 : if (refname && need_prefix)
8419 : : {
8420 : 48717 : appendStringInfoString(buf, quote_identifier(refname));
8421 : 48717 : appendStringInfoChar(buf, '.');
8422 : : }
8423 [ + + ]: 92672 : if (attname)
8424 : 91979 : appendStringInfoString(buf, quote_identifier(attname));
8425 : : else
8426 : : {
8427 : 693 : appendStringInfoChar(buf, '*');
8428 [ + + ]: 693 : if (istoplevel)
8429 : 56 : appendStringInfo(buf, "::%s",
8430 : : format_type_with_typemod(var->vartype,
8431 : : var->vartypmod));
8432 : : }
8433 : :
8434 : 92672 : return attname;
8435 : : }
8436 : :
8437 : : /*
8438 : : * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
8439 : : * routine is actually a callback for resolve_special_varno, which handles
8440 : : * finding the correct TargetEntry. We get the expression contained in that
8441 : : * TargetEntry and just need to deparse it, a job we can throw back on
8442 : : * get_rule_expr.
8443 : : */
8444 : : static void
8445 : 34730 : get_special_variable(Node *node, deparse_context *context, void *callback_arg)
8446 : : {
8447 : 34730 : StringInfo buf = context->buf;
8448 : :
8449 : : /*
8450 : : * For a non-Var referent, force parentheses because our caller probably
8451 : : * assumed a Var is a simple expression.
8452 : : */
8453 [ + + ]: 34730 : if (!IsA(node, Var))
8454 : 3559 : appendStringInfoChar(buf, '(');
8455 : 34730 : get_rule_expr(node, context, true);
8456 [ + + ]: 34730 : if (!IsA(node, Var))
8457 : 3559 : appendStringInfoChar(buf, ')');
8458 : 34730 : }
8459 : :
8460 : : /*
8461 : : * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
8462 : : * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
8463 : : * invoke the callback provided.
8464 : : */
8465 : : static void
8466 : 97815 : resolve_special_varno(Node *node, deparse_context *context,
8467 : : rsv_callback callback, void *callback_arg)
8468 : : {
8469 : : Var *var;
8470 : : deparse_namespace *dpns;
8471 : :
8472 : : /* This function is recursive, so let's be paranoid. */
8473 : 97815 : check_stack_depth();
8474 : :
8475 : : /* If it's not a Var, invoke the callback. */
8476 [ + + ]: 97815 : if (!IsA(node, Var))
8477 : : {
8478 : 4111 : (*callback) (node, context, callback_arg);
8479 : 4111 : return;
8480 : : }
8481 : :
8482 : : /* Find appropriate nesting depth */
8483 : 93704 : var = (Var *) node;
8484 : 93704 : dpns = (deparse_namespace *) list_nth(context->namespaces,
8485 : 93704 : var->varlevelsup);
8486 : :
8487 : : /*
8488 : : * If varno is special, recurse. (Don't worry about varnosyn; if we're
8489 : : * here, we already decided not to use that.)
8490 : : */
8491 [ + + + - ]: 93704 : if (var->varno == OUTER_VAR && dpns->outer_tlist)
8492 : : {
8493 : : TargetEntry *tle;
8494 : : deparse_namespace save_dpns;
8495 : : Bitmapset *save_appendparents;
8496 : :
8497 : 47606 : tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
8498 [ - + ]: 47606 : if (!tle)
8499 [ # # ]: 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
8500 : :
8501 : : /*
8502 : : * If we're descending to the first child of an Append or MergeAppend,
8503 : : * update appendparents. This will affect deparsing of all Vars
8504 : : * appearing within the eventually-resolved subexpression.
8505 : : */
8506 : 47606 : save_appendparents = context->appendparents;
8507 : :
8508 [ + + ]: 47606 : if (IsA(dpns->plan, Append))
8509 : 2991 : context->appendparents = bms_union(context->appendparents,
8510 : 2991 : ((Append *) dpns->plan)->apprelids);
8511 [ + + ]: 44615 : else if (IsA(dpns->plan, MergeAppend))
8512 : 413 : context->appendparents = bms_union(context->appendparents,
8513 : 413 : ((MergeAppend *) dpns->plan)->apprelids);
8514 : :
8515 : 47606 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8516 : 47606 : resolve_special_varno((Node *) tle->expr, context,
8517 : : callback, callback_arg);
8518 : 47606 : pop_child_plan(dpns, &save_dpns);
8519 : 47606 : context->appendparents = save_appendparents;
8520 : 47606 : return;
8521 : : }
8522 [ + + + - ]: 46098 : else if (var->varno == INNER_VAR && dpns->inner_tlist)
8523 : : {
8524 : : TargetEntry *tle;
8525 : : deparse_namespace save_dpns;
8526 : :
8527 : 11457 : tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
8528 [ - + ]: 11457 : if (!tle)
8529 [ # # ]: 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
8530 : :
8531 : 11457 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8532 : 11457 : resolve_special_varno((Node *) tle->expr, context,
8533 : : callback, callback_arg);
8534 : 11457 : pop_child_plan(dpns, &save_dpns);
8535 : 11457 : return;
8536 : : }
8537 [ + + + - ]: 34641 : else if (var->varno == INDEX_VAR && dpns->index_tlist)
8538 : : {
8539 : : TargetEntry *tle;
8540 : :
8541 : 3470 : tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
8542 [ - + ]: 3470 : if (!tle)
8543 [ # # ]: 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
8544 : :
8545 : 3470 : resolve_special_varno((Node *) tle->expr, context,
8546 : : callback, callback_arg);
8547 : 3470 : return;
8548 : : }
8549 [ + - - + ]: 31171 : else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
8550 [ # # ]: 0 : elog(ERROR, "bogus varno: %d", var->varno);
8551 : :
8552 : : /* Not special. Just invoke the callback. */
8553 : 31171 : (*callback) (node, context, callback_arg);
8554 : : }
8555 : :
8556 : : /*
8557 : : * Get the name of a field of an expression of composite type. The
8558 : : * expression is usually a Var, but we handle other cases too.
8559 : : *
8560 : : * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
8561 : : *
8562 : : * This is fairly straightforward when the expression has a named composite
8563 : : * type; we need only look up the type in the catalogs. However, the type
8564 : : * could also be RECORD. Since no actual table or view column is allowed to
8565 : : * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
8566 : : * or to a subquery output. We drill down to find the ultimate defining
8567 : : * expression and attempt to infer the field name from it. We ereport if we
8568 : : * can't determine the name.
8569 : : *
8570 : : * Similarly, a PARAM of type RECORD has to refer to some expression of
8571 : : * a determinable composite type.
8572 : : */
8573 : : static const char *
8574 : 1058 : get_name_for_var_field(Var *var, int fieldno,
8575 : : int levelsup, deparse_context *context)
8576 : : {
8577 : : RangeTblEntry *rte;
8578 : : AttrNumber attnum;
8579 : : int netlevelsup;
8580 : : deparse_namespace *dpns;
8581 : : int varno;
8582 : : AttrNumber varattno;
8583 : : TupleDesc tupleDesc;
8584 : : Node *expr;
8585 : :
8586 : : /*
8587 : : * If it's a RowExpr that was expanded from a whole-row Var, use the
8588 : : * column names attached to it. (We could let get_expr_result_tupdesc()
8589 : : * handle this, but it's much cheaper to just pull out the name we need.)
8590 : : */
8591 [ + + ]: 1058 : if (IsA(var, RowExpr))
8592 : : {
8593 : 24 : RowExpr *r = (RowExpr *) var;
8594 : :
8595 [ + - + - ]: 24 : if (fieldno > 0 && fieldno <= list_length(r->colnames))
8596 : 24 : return strVal(list_nth(r->colnames, fieldno - 1));
8597 : : }
8598 : :
8599 : : /*
8600 : : * If it's a Param of type RECORD, try to find what the Param refers to.
8601 : : */
8602 [ + + ]: 1034 : if (IsA(var, Param))
8603 : : {
8604 : 12 : Param *param = (Param *) var;
8605 : : ListCell *ancestor_cell;
8606 : :
8607 : 12 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8608 [ + - ]: 12 : if (expr)
8609 : : {
8610 : : /* Found a match, so recurse to decipher the field name */
8611 : : deparse_namespace save_dpns;
8612 : : const char *result;
8613 : :
8614 : 12 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
8615 : 12 : result = get_name_for_var_field((Var *) expr, fieldno,
8616 : : 0, context);
8617 : 12 : pop_ancestor_plan(dpns, &save_dpns);
8618 : 12 : return result;
8619 : : }
8620 : : }
8621 : :
8622 : : /*
8623 : : * If it's a Var of type RECORD, we have to find what the Var refers to;
8624 : : * if not, we can use get_expr_result_tupdesc().
8625 : : */
8626 [ + + ]: 1022 : if (!IsA(var, Var) ||
8627 [ + + ]: 961 : var->vartype != RECORDOID)
8628 : : {
8629 : 858 : tupleDesc = get_expr_result_tupdesc((Node *) var, false);
8630 : : /* Got the tupdesc, so we can extract the field name */
8631 : : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
8632 : 858 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
8633 : : }
8634 : :
8635 : : /* Find appropriate nesting depth */
8636 : 164 : netlevelsup = var->varlevelsup + levelsup;
8637 [ - + ]: 164 : if (netlevelsup >= list_length(context->namespaces))
8638 [ # # ]: 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
8639 : : var->varlevelsup, levelsup);
8640 : 164 : dpns = (deparse_namespace *) list_nth(context->namespaces,
8641 : : netlevelsup);
8642 : :
8643 : : /*
8644 : : * If we have a syntactic referent for the Var, and we're working from a
8645 : : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8646 : : * on the semantic referent. (See comments in get_variable().)
8647 : : */
8648 [ + + + + ]: 164 : if (var->varnosyn > 0 && dpns->plan == NULL)
8649 : : {
8650 : 64 : varno = var->varnosyn;
8651 : 64 : varattno = var->varattnosyn;
8652 : : }
8653 : : else
8654 : : {
8655 : 100 : varno = var->varno;
8656 : 100 : varattno = var->varattno;
8657 : : }
8658 : :
8659 : : /*
8660 : : * Try to find the relevant RTE in this rtable. In a plan tree, it's
8661 : : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8662 : : * down into the subplans, or INDEX_VAR, which is resolved similarly.
8663 : : *
8664 : : * Note: unlike get_variable and resolve_special_varno, we need not worry
8665 : : * about inheritance mapping: a child Var should have the same datatype as
8666 : : * its parent, and here we're really only interested in the Var's type.
8667 : : */
8668 [ + + + - ]: 164 : if (varno >= 1 && varno <= list_length(dpns->rtable))
8669 : : {
8670 : 112 : rte = rt_fetch(varno, dpns->rtable);
8671 : 112 : attnum = varattno;
8672 : : }
8673 [ + + + - ]: 52 : else if (varno == OUTER_VAR && dpns->outer_tlist)
8674 : : {
8675 : : TargetEntry *tle;
8676 : : deparse_namespace save_dpns;
8677 : : const char *result;
8678 : :
8679 : 40 : tle = get_tle_by_resno(dpns->outer_tlist, varattno);
8680 [ - + ]: 40 : if (!tle)
8681 [ # # ]: 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
8682 : :
8683 : : Assert(netlevelsup == 0);
8684 : 40 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8685 : :
8686 : 40 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8687 : : levelsup, context);
8688 : :
8689 : 40 : pop_child_plan(dpns, &save_dpns);
8690 : 40 : return result;
8691 : : }
8692 [ + - + - ]: 12 : else if (varno == INNER_VAR && dpns->inner_tlist)
8693 : : {
8694 : : TargetEntry *tle;
8695 : : deparse_namespace save_dpns;
8696 : : const char *result;
8697 : :
8698 : 12 : tle = get_tle_by_resno(dpns->inner_tlist, varattno);
8699 [ - + ]: 12 : if (!tle)
8700 [ # # ]: 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
8701 : :
8702 : : Assert(netlevelsup == 0);
8703 : 12 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8704 : :
8705 : 12 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8706 : : levelsup, context);
8707 : :
8708 : 12 : pop_child_plan(dpns, &save_dpns);
8709 : 12 : return result;
8710 : : }
8711 [ # # # # ]: 0 : else if (varno == INDEX_VAR && dpns->index_tlist)
8712 : : {
8713 : : TargetEntry *tle;
8714 : : const char *result;
8715 : :
8716 : 0 : tle = get_tle_by_resno(dpns->index_tlist, varattno);
8717 [ # # ]: 0 : if (!tle)
8718 [ # # ]: 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
8719 : :
8720 : : Assert(netlevelsup == 0);
8721 : :
8722 : 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8723 : : levelsup, context);
8724 : :
8725 : 0 : return result;
8726 : : }
8727 : : else
8728 : : {
8729 [ # # ]: 0 : elog(ERROR, "bogus varno: %d", varno);
8730 : : return NULL; /* keep compiler quiet */
8731 : : }
8732 : :
8733 [ + + ]: 112 : if (attnum == InvalidAttrNumber)
8734 : : {
8735 : : /* Var is whole-row reference to RTE, so select the right field */
8736 : 16 : return get_rte_attribute_name(rte, fieldno);
8737 : : }
8738 : :
8739 : : /*
8740 : : * This part has essentially the same logic as the parser's
8741 : : * expandRecordVariable() function, but we are dealing with a different
8742 : : * representation of the input context, and we only need one field name
8743 : : * not a TupleDesc. Also, we need special cases for finding subquery and
8744 : : * CTE subplans when deparsing Plan trees.
8745 : : */
8746 : 96 : expr = (Node *) var; /* default if we can't drill down */
8747 : :
8748 [ - + - - : 96 : switch (rte->rtekind)
+ - - ]
8749 : : {
8750 : 0 : case RTE_RELATION:
8751 : : case RTE_VALUES:
8752 : : case RTE_NAMEDTUPLESTORE:
8753 : : case RTE_GRAPH_TABLE:
8754 : : case RTE_RESULT:
8755 : :
8756 : : /*
8757 : : * This case should not occur: a column of a table, values list,
8758 : : * or ENR shouldn't have type RECORD. Fall through and fail (most
8759 : : * likely) at the bottom.
8760 : : */
8761 : 0 : break;
8762 : 48 : case RTE_SUBQUERY:
8763 : : /* Subselect-in-FROM: examine sub-select's output expr */
8764 : : {
8765 [ + + ]: 48 : if (rte->subquery)
8766 : : {
8767 : 28 : TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
8768 : : attnum);
8769 : :
8770 [ + - - + ]: 28 : if (ste == NULL || ste->resjunk)
8771 [ # # ]: 0 : elog(ERROR, "subquery %s does not have attribute %d",
8772 : : rte->eref->aliasname, attnum);
8773 : 28 : expr = (Node *) ste->expr;
8774 [ + + ]: 28 : if (IsA(expr, Var))
8775 : : {
8776 : : /*
8777 : : * Recurse into the sub-select to see what its Var
8778 : : * refers to. We have to build an additional level of
8779 : : * namespace to keep in step with varlevelsup in the
8780 : : * subselect; furthermore, the subquery RTE might be
8781 : : * from an outer query level, in which case the
8782 : : * namespace for the subselect must have that outer
8783 : : * level as parent namespace.
8784 : : */
8785 : 12 : List *save_nslist = context->namespaces;
8786 : : List *parent_namespaces;
8787 : : deparse_namespace mydpns;
8788 : : const char *result;
8789 : :
8790 : 12 : parent_namespaces = list_copy_tail(context->namespaces,
8791 : : netlevelsup);
8792 : :
8793 : 12 : set_deparse_for_query(&mydpns, rte->subquery,
8794 : : parent_namespaces);
8795 : :
8796 : 12 : context->namespaces = lcons(&mydpns, parent_namespaces);
8797 : :
8798 : 12 : result = get_name_for_var_field((Var *) expr, fieldno,
8799 : : 0, context);
8800 : :
8801 : 12 : context->namespaces = save_nslist;
8802 : :
8803 : 12 : return result;
8804 : : }
8805 : : /* else fall through to inspect the expression */
8806 : : }
8807 : : else
8808 : : {
8809 : : /*
8810 : : * We're deparsing a Plan tree so we don't have complete
8811 : : * RTE entries (in particular, rte->subquery is NULL). But
8812 : : * the only place we'd normally see a Var directly
8813 : : * referencing a SUBQUERY RTE is in a SubqueryScan plan
8814 : : * node, and we can look into the child plan's tlist
8815 : : * instead. An exception occurs if the subquery was
8816 : : * proven empty and optimized away: then we'd find such a
8817 : : * Var in a childless Result node, and there's nothing in
8818 : : * the plan tree that would let us figure out what it had
8819 : : * originally referenced. In that case, fall back on
8820 : : * printing "fN", analogously to the default column names
8821 : : * for RowExprs.
8822 : : */
8823 : : TargetEntry *tle;
8824 : : deparse_namespace save_dpns;
8825 : : const char *result;
8826 : :
8827 [ + + ]: 20 : if (!dpns->inner_plan)
8828 : : {
8829 : 8 : char *dummy_name = palloc(32);
8830 : :
8831 : : Assert(dpns->plan && IsA(dpns->plan, Result));
8832 : 8 : snprintf(dummy_name, 32, "f%d", fieldno);
8833 : 8 : return dummy_name;
8834 : : }
8835 : : Assert(dpns->plan && IsA(dpns->plan, SubqueryScan));
8836 : :
8837 : 12 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8838 [ - + ]: 12 : if (!tle)
8839 [ # # ]: 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8840 : : attnum);
8841 : : Assert(netlevelsup == 0);
8842 : 12 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8843 : :
8844 : 12 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8845 : : levelsup, context);
8846 : :
8847 : 12 : pop_child_plan(dpns, &save_dpns);
8848 : 12 : return result;
8849 : : }
8850 : : }
8851 : 16 : break;
8852 : 0 : case RTE_JOIN:
8853 : : /* Join RTE --- recursively inspect the alias variable */
8854 [ # # ]: 0 : if (rte->joinaliasvars == NIL)
8855 [ # # ]: 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
8856 : : Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
8857 : 0 : expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
8858 : : Assert(expr != NULL);
8859 : : /* we intentionally don't strip implicit coercions here */
8860 [ # # ]: 0 : if (IsA(expr, Var))
8861 : 0 : return get_name_for_var_field((Var *) expr, fieldno,
8862 : 0 : var->varlevelsup + levelsup,
8863 : : context);
8864 : : /* else fall through to inspect the expression */
8865 : 0 : break;
8866 : 0 : case RTE_FUNCTION:
8867 : : case RTE_TABLEFUNC:
8868 : :
8869 : : /*
8870 : : * We couldn't get here unless a function is declared with one of
8871 : : * its result columns as RECORD, which is not allowed.
8872 : : */
8873 : 0 : break;
8874 : 48 : case RTE_CTE:
8875 : : /* CTE reference: examine subquery's output expr */
8876 : : {
8877 : 48 : CommonTableExpr *cte = NULL;
8878 : : Index ctelevelsup;
8879 : : ListCell *lc;
8880 : :
8881 : : /*
8882 : : * Try to find the referenced CTE using the namespace stack.
8883 : : */
8884 : 48 : ctelevelsup = rte->ctelevelsup + netlevelsup;
8885 [ + + ]: 48 : if (ctelevelsup >= list_length(context->namespaces))
8886 : 8 : lc = NULL;
8887 : : else
8888 : : {
8889 : : deparse_namespace *ctedpns;
8890 : :
8891 : : ctedpns = (deparse_namespace *)
8892 : 40 : list_nth(context->namespaces, ctelevelsup);
8893 [ + + + - : 44 : foreach(lc, ctedpns->ctes)
+ + ]
8894 : : {
8895 : 24 : cte = (CommonTableExpr *) lfirst(lc);
8896 [ + + ]: 24 : if (strcmp(cte->ctename, rte->ctename) == 0)
8897 : 20 : break;
8898 : : }
8899 : : }
8900 [ + + ]: 48 : if (lc != NULL)
8901 : : {
8902 : 20 : Query *ctequery = (Query *) cte->ctequery;
8903 [ + - ]: 20 : TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
8904 : : attnum);
8905 : :
8906 [ + - - + ]: 20 : if (ste == NULL || ste->resjunk)
8907 [ # # ]: 0 : elog(ERROR, "CTE %s does not have attribute %d",
8908 : : rte->eref->aliasname, attnum);
8909 : 20 : expr = (Node *) ste->expr;
8910 [ + + ]: 20 : if (IsA(expr, Var))
8911 : : {
8912 : : /*
8913 : : * Recurse into the CTE to see what its Var refers to.
8914 : : * We have to build an additional level of namespace
8915 : : * to keep in step with varlevelsup in the CTE;
8916 : : * furthermore it could be an outer CTE (compare
8917 : : * SUBQUERY case above).
8918 : : */
8919 : 12 : List *save_nslist = context->namespaces;
8920 : : List *parent_namespaces;
8921 : : deparse_namespace mydpns;
8922 : : const char *result;
8923 : :
8924 : 12 : parent_namespaces = list_copy_tail(context->namespaces,
8925 : : ctelevelsup);
8926 : :
8927 : 12 : set_deparse_for_query(&mydpns, ctequery,
8928 : : parent_namespaces);
8929 : :
8930 : 12 : context->namespaces = lcons(&mydpns, parent_namespaces);
8931 : :
8932 : 12 : result = get_name_for_var_field((Var *) expr, fieldno,
8933 : : 0, context);
8934 : :
8935 : 12 : context->namespaces = save_nslist;
8936 : :
8937 : 12 : return result;
8938 : : }
8939 : : /* else fall through to inspect the expression */
8940 : : }
8941 : : else
8942 : : {
8943 : : /*
8944 : : * We're deparsing a Plan tree so we don't have a CTE
8945 : : * list. But the only places we'd normally see a Var
8946 : : * directly referencing a CTE RTE are in CteScan or
8947 : : * WorkTableScan plan nodes. For those cases,
8948 : : * set_deparse_plan arranged for dpns->inner_plan to be
8949 : : * the plan node that emits the CTE or RecursiveUnion
8950 : : * result, and we can look at its tlist instead. As
8951 : : * above, this can fail if the CTE has been proven empty,
8952 : : * in which case fall back to "fN".
8953 : : */
8954 : : TargetEntry *tle;
8955 : : deparse_namespace save_dpns;
8956 : : const char *result;
8957 : :
8958 [ + + ]: 28 : if (!dpns->inner_plan)
8959 : : {
8960 : 4 : char *dummy_name = palloc(32);
8961 : :
8962 : : Assert(dpns->plan && IsA(dpns->plan, Result));
8963 : 4 : snprintf(dummy_name, 32, "f%d", fieldno);
8964 : 4 : return dummy_name;
8965 : : }
8966 : : Assert(dpns->plan && (IsA(dpns->plan, CteScan) ||
8967 : : IsA(dpns->plan, WorkTableScan)));
8968 : :
8969 : 24 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8970 [ - + ]: 24 : if (!tle)
8971 [ # # ]: 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8972 : : attnum);
8973 : : Assert(netlevelsup == 0);
8974 : 24 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8975 : :
8976 : 24 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8977 : : levelsup, context);
8978 : :
8979 : 24 : pop_child_plan(dpns, &save_dpns);
8980 : 24 : return result;
8981 : : }
8982 : : }
8983 : 8 : break;
8984 : 0 : case RTE_GROUP:
8985 : :
8986 : : /*
8987 : : * We couldn't get here: any Vars that reference the RTE_GROUP RTE
8988 : : * should have been replaced with the underlying grouping
8989 : : * expressions.
8990 : : */
8991 : 0 : break;
8992 : : }
8993 : :
8994 : : /*
8995 : : * We now have an expression we can't expand any more, so see if
8996 : : * get_expr_result_tupdesc() can do anything with it.
8997 : : */
8998 : 24 : tupleDesc = get_expr_result_tupdesc(expr, false);
8999 : : /* Got the tupdesc, so we can extract the field name */
9000 : : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
9001 : 24 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
9002 : : }
9003 : :
9004 : : /*
9005 : : * Try to find the referenced expression for a PARAM_EXEC Param that might
9006 : : * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
9007 : : *
9008 : : * If successful, return the expression and set *dpns_p and *ancestor_cell_p
9009 : : * appropriately for calling push_ancestor_plan(). If no referent can be
9010 : : * found, return NULL.
9011 : : */
9012 : : static Node *
9013 : 4818 : find_param_referent(Param *param, deparse_context *context,
9014 : : deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
9015 : : {
9016 : : /* Initialize output parameters to prevent compiler warnings */
9017 : 4818 : *dpns_p = NULL;
9018 : 4818 : *ancestor_cell_p = NULL;
9019 : :
9020 : : /*
9021 : : * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
9022 : : * SubPlan argument. This will necessarily be in some ancestor of the
9023 : : * current expression's Plan node.
9024 : : */
9025 [ + + ]: 4818 : if (param->paramkind == PARAM_EXEC)
9026 : : {
9027 : : deparse_namespace *dpns;
9028 : : Plan *child_plan;
9029 : : ListCell *lc;
9030 : :
9031 : 4225 : dpns = (deparse_namespace *) linitial(context->namespaces);
9032 : 4225 : child_plan = dpns->plan;
9033 : :
9034 [ + + + + : 7500 : foreach(lc, dpns->ancestors)
+ + ]
9035 : : {
9036 : 6376 : Node *ancestor = (Node *) lfirst(lc);
9037 : : ListCell *lc2;
9038 : :
9039 : : /*
9040 : : * NestLoops transmit params to their inner child only.
9041 : : */
9042 [ + + ]: 6376 : if (IsA(ancestor, NestLoop) &&
9043 [ + + ]: 2895 : child_plan == innerPlan(ancestor))
9044 : : {
9045 : 2773 : NestLoop *nl = (NestLoop *) ancestor;
9046 : :
9047 [ + + + + : 3440 : foreach(lc2, nl->nestParams)
+ + ]
9048 : : {
9049 : 3323 : NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
9050 : :
9051 [ + + ]: 3323 : if (nlp->paramno == param->paramid)
9052 : : {
9053 : : /* Found a match, so return it */
9054 : 2656 : *dpns_p = dpns;
9055 : 2656 : *ancestor_cell_p = lc;
9056 : 2656 : return (Node *) nlp->paramval;
9057 : : }
9058 : : }
9059 : : }
9060 : :
9061 : : /*
9062 : : * If ancestor is a SubPlan, check the arguments it provides.
9063 : : */
9064 [ + + ]: 3720 : if (IsA(ancestor, SubPlan))
9065 : 297 : {
9066 : 742 : SubPlan *subplan = (SubPlan *) ancestor;
9067 : : ListCell *lc3;
9068 : : ListCell *lc4;
9069 : :
9070 [ + + + + : 969 : forboth(lc3, subplan->parParam, lc4, subplan->args)
+ + + + +
+ + - +
+ ]
9071 : : {
9072 : 672 : int paramid = lfirst_int(lc3);
9073 : 672 : Node *arg = (Node *) lfirst(lc4);
9074 : :
9075 [ + + ]: 672 : if (paramid == param->paramid)
9076 : : {
9077 : : /*
9078 : : * Found a match, so return it. But, since Vars in
9079 : : * the arg are to be evaluated in the surrounding
9080 : : * context, we have to point to the next ancestor item
9081 : : * that is *not* a SubPlan.
9082 : : */
9083 : : ListCell *rest;
9084 : :
9085 [ + - + - : 445 : for_each_cell(rest, dpns->ancestors,
+ - ]
9086 : : lnext(dpns->ancestors, lc))
9087 : : {
9088 : 445 : Node *ancestor2 = (Node *) lfirst(rest);
9089 : :
9090 [ + - ]: 445 : if (!IsA(ancestor2, SubPlan))
9091 : : {
9092 : 445 : *dpns_p = dpns;
9093 : 445 : *ancestor_cell_p = rest;
9094 : 445 : return arg;
9095 : : }
9096 : : }
9097 [ # # ]: 0 : elog(ERROR, "SubPlan cannot be outermost ancestor");
9098 : : }
9099 : : }
9100 : :
9101 : : /* SubPlan isn't a kind of Plan, so skip the rest */
9102 : 297 : continue;
9103 : : }
9104 : :
9105 : : /*
9106 : : * We need not consider the ancestor's initPlan list, since
9107 : : * initplans never have any parParams.
9108 : : */
9109 : :
9110 : : /* No luck, crawl up to next ancestor */
9111 : 2978 : child_plan = (Plan *) ancestor;
9112 : : }
9113 : : }
9114 : :
9115 : : /* No referent found */
9116 : 1717 : return NULL;
9117 : : }
9118 : :
9119 : : /*
9120 : : * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.
9121 : : *
9122 : : * If successful, return the generating subplan/initplan and set *column_p
9123 : : * to the subplan's 0-based output column number.
9124 : : * Otherwise, return NULL.
9125 : : */
9126 : : static SubPlan *
9127 : 1717 : find_param_generator(Param *param, deparse_context *context, int *column_p)
9128 : : {
9129 : : /* Initialize output parameter to prevent compiler warnings */
9130 : 1717 : *column_p = 0;
9131 : :
9132 : : /*
9133 : : * If it's a PARAM_EXEC parameter, search the current plan node as well as
9134 : : * ancestor nodes looking for a subplan or initplan that emits the value
9135 : : * for the Param. It could appear in the setParams of an initplan or
9136 : : * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
9137 : : */
9138 [ + + ]: 1717 : if (param->paramkind == PARAM_EXEC)
9139 : : {
9140 : : SubPlan *result;
9141 : : deparse_namespace *dpns;
9142 : : ListCell *lc;
9143 : :
9144 : 1124 : dpns = (deparse_namespace *) linitial(context->namespaces);
9145 : :
9146 : : /* First check the innermost plan node's initplans */
9147 : 1124 : result = find_param_generator_initplan(param, dpns->plan, column_p);
9148 [ + + ]: 1124 : if (result)
9149 : 297 : return result;
9150 : :
9151 : : /*
9152 : : * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
9153 : : * which can be referenced by Params elsewhere in the targetlist.
9154 : : * (Such Params should always be in the same targetlist, so there's no
9155 : : * need to do this work at upper plan nodes.)
9156 : : */
9157 [ + + + + : 4151 : foreach_node(TargetEntry, tle, dpns->plan->targetlist)
+ + ]
9158 : : {
9159 [ + - + + ]: 2565 : if (tle->expr && IsA(tle->expr, SubPlan))
9160 : : {
9161 : 66 : SubPlan *subplan = (SubPlan *) tle->expr;
9162 : :
9163 [ + + ]: 66 : if (subplan->subLinkType == MULTIEXPR_SUBLINK)
9164 : : {
9165 [ + - + - : 51 : foreach_int(paramid, subplan->setParam)
+ - ]
9166 : : {
9167 [ + + ]: 51 : if (paramid == param->paramid)
9168 : : {
9169 : : /* Found a match, so return it. */
9170 : 34 : *column_p = foreach_current_index(paramid);
9171 : 34 : return subplan;
9172 : : }
9173 : : }
9174 : : }
9175 : : }
9176 : : }
9177 : :
9178 : : /* No luck, so check the ancestor nodes */
9179 [ + - + - : 1028 : foreach(lc, dpns->ancestors)
+ - ]
9180 : : {
9181 : 1028 : Node *ancestor = (Node *) lfirst(lc);
9182 : :
9183 : : /*
9184 : : * If ancestor is a SubPlan, check the paramIds it provides.
9185 : : */
9186 [ + + ]: 1028 : if (IsA(ancestor, SubPlan))
9187 : 0 : {
9188 : 201 : SubPlan *subplan = (SubPlan *) ancestor;
9189 : :
9190 [ + - + - : 226 : foreach_int(paramid, subplan->paramIds)
+ - ]
9191 : : {
9192 [ + + ]: 226 : if (paramid == param->paramid)
9193 : : {
9194 : : /* Found a match, so return it. */
9195 : 201 : *column_p = foreach_current_index(paramid);
9196 : 201 : return subplan;
9197 : : }
9198 : : }
9199 : :
9200 : : /* SubPlan isn't a kind of Plan, so skip the rest */
9201 : 0 : continue;
9202 : : }
9203 : :
9204 : : /*
9205 : : * Otherwise, it's some kind of Plan node, so check its initplans.
9206 : : */
9207 : 827 : result = find_param_generator_initplan(param, (Plan *) ancestor,
9208 : : column_p);
9209 [ + + ]: 827 : if (result)
9210 : 592 : return result;
9211 : :
9212 : : /* No luck, crawl up to next ancestor */
9213 : : }
9214 : : }
9215 : :
9216 : : /* No generator found */
9217 : 593 : return NULL;
9218 : : }
9219 : :
9220 : : /*
9221 : : * Subroutine for find_param_generator: search one Plan node's initplans
9222 : : */
9223 : : static SubPlan *
9224 : 1951 : find_param_generator_initplan(Param *param, Plan *plan, int *column_p)
9225 : : {
9226 [ + + + - : 3105 : foreach_node(SubPlan, subplan, plan->initPlan)
+ + ]
9227 : : {
9228 [ + - + + : 1169 : foreach_int(paramid, subplan->setParam)
+ + ]
9229 : : {
9230 [ + + ]: 985 : if (paramid == param->paramid)
9231 : : {
9232 : : /* Found a match, so return it. */
9233 : 889 : *column_p = foreach_current_index(paramid);
9234 : 889 : return subplan;
9235 : : }
9236 : : }
9237 : : }
9238 : 1062 : return NULL;
9239 : : }
9240 : :
9241 : : /*
9242 : : * Display a Param appropriately.
9243 : : */
9244 : : static void
9245 : 4806 : get_parameter(Param *param, deparse_context *context)
9246 : : {
9247 : : Node *expr;
9248 : : deparse_namespace *dpns;
9249 : : ListCell *ancestor_cell;
9250 : : SubPlan *subplan;
9251 : : int column;
9252 : :
9253 : : /*
9254 : : * If it's a PARAM_EXEC parameter, try to locate the expression from which
9255 : : * the parameter was computed. This stanza handles only cases in which
9256 : : * the Param represents an input to the subplan we are currently in.
9257 : : */
9258 : 4806 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
9259 [ + + ]: 4806 : if (expr)
9260 : : {
9261 : : /* Found a match, so print it */
9262 : : deparse_namespace save_dpns;
9263 : : bool save_varprefix;
9264 : : bool need_paren;
9265 : :
9266 : : /* Switch attention to the ancestor plan node */
9267 : 3089 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
9268 : :
9269 : : /*
9270 : : * Force prefixing of Vars, since they won't belong to the relation
9271 : : * being scanned in the original plan node.
9272 : : */
9273 : 3089 : save_varprefix = context->varprefix;
9274 : 3089 : context->varprefix = true;
9275 : :
9276 : : /*
9277 : : * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
9278 : : * upper-level Param, which wouldn't need extra parentheses.
9279 : : * Otherwise, insert parens to ensure the expression looks atomic.
9280 : : */
9281 [ + + ]: 3105 : need_paren = !(IsA(expr, Var) ||
9282 [ + + ]: 16 : IsA(expr, Aggref) ||
9283 [ + + ]: 12 : IsA(expr, GroupingFunc) ||
9284 [ - + ]: 8 : IsA(expr, Param));
9285 [ - + ]: 3089 : if (need_paren)
9286 : 0 : appendStringInfoChar(context->buf, '(');
9287 : :
9288 : 3089 : get_rule_expr(expr, context, false);
9289 : :
9290 [ - + ]: 3089 : if (need_paren)
9291 : 0 : appendStringInfoChar(context->buf, ')');
9292 : :
9293 : 3089 : context->varprefix = save_varprefix;
9294 : :
9295 : 3089 : pop_ancestor_plan(dpns, &save_dpns);
9296 : :
9297 : 3089 : return;
9298 : : }
9299 : :
9300 : : /*
9301 : : * Alternatively, maybe it's a subplan output, which we print as a
9302 : : * reference to the subplan. (We could drill down into the subplan and
9303 : : * print the relevant targetlist expression, but that has been deemed too
9304 : : * confusing since it would violate normal SQL scope rules. Also, we're
9305 : : * relying on this reference to show that the testexpr containing the
9306 : : * Param has anything to do with that subplan at all.)
9307 : : */
9308 : 1717 : subplan = find_param_generator(param, context, &column);
9309 [ + + ]: 1717 : if (subplan)
9310 : : {
9311 : : const char *nameprefix;
9312 : :
9313 [ + + ]: 1124 : if (subplan->isInitPlan)
9314 : 889 : nameprefix = "InitPlan ";
9315 : : else
9316 : 235 : nameprefix = "SubPlan ";
9317 : :
9318 : 1124 : appendStringInfo(context->buf, "(%s%s%s).col%d",
9319 [ + + ]: 1124 : subplan->useHashTable ? "hashed " : "",
9320 : : nameprefix,
9321 : : subplan->plan_name, column + 1);
9322 : :
9323 : 1124 : return;
9324 : : }
9325 : :
9326 : : /*
9327 : : * If it's an external parameter, see if the outermost namespace provides
9328 : : * function argument names.
9329 : : */
9330 [ + - + - ]: 593 : if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
9331 : : {
9332 : 593 : dpns = llast(context->namespaces);
9333 [ + + ]: 593 : if (dpns->argnames &&
9334 [ + - ]: 45 : param->paramid > 0 &&
9335 [ + - ]: 45 : param->paramid <= dpns->numargs)
9336 : : {
9337 : 45 : char *argname = dpns->argnames[param->paramid - 1];
9338 : :
9339 [ + - ]: 45 : if (argname)
9340 : : {
9341 : 45 : bool should_qualify = false;
9342 : : ListCell *lc;
9343 : :
9344 : : /*
9345 : : * Qualify the parameter name if there are any other deparse
9346 : : * namespaces with range tables. This avoids qualifying in
9347 : : * trivial cases like "RETURN a + b", but makes it safe in all
9348 : : * other cases.
9349 : : */
9350 [ + - + + : 103 : foreach(lc, context->namespaces)
+ + ]
9351 : : {
9352 : 78 : deparse_namespace *depns = lfirst(lc);
9353 : :
9354 [ + + ]: 78 : if (depns->rtable_names != NIL)
9355 : : {
9356 : 20 : should_qualify = true;
9357 : 20 : break;
9358 : : }
9359 : : }
9360 [ + + ]: 45 : if (should_qualify)
9361 : : {
9362 : 20 : appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
9363 : 20 : appendStringInfoChar(context->buf, '.');
9364 : : }
9365 : :
9366 : 45 : appendStringInfoString(context->buf, quote_identifier(argname));
9367 : 45 : return;
9368 : : }
9369 : : }
9370 : : }
9371 : :
9372 : : /*
9373 : : * Not PARAM_EXEC, or couldn't find referent: just print $N.
9374 : : *
9375 : : * It's a bug if we get here for anything except PARAM_EXTERN Params, but
9376 : : * in production builds printing $N seems more useful than failing.
9377 : : */
9378 : : Assert(param->paramkind == PARAM_EXTERN);
9379 : :
9380 : 548 : appendStringInfo(context->buf, "$%d", param->paramid);
9381 : : }
9382 : :
9383 : : /*
9384 : : * get_simple_binary_op_name
9385 : : *
9386 : : * helper function for isSimpleNode
9387 : : * will return single char binary operator name, or NULL if it's not
9388 : : */
9389 : : static const char *
9390 : 100 : get_simple_binary_op_name(OpExpr *expr)
9391 : : {
9392 : 100 : List *args = expr->args;
9393 : :
9394 [ + - ]: 100 : if (list_length(args) == 2)
9395 : : {
9396 : : /* binary operator */
9397 : 100 : Node *arg1 = (Node *) linitial(args);
9398 : 100 : Node *arg2 = (Node *) lsecond(args);
9399 : : const char *op;
9400 : :
9401 : 100 : op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
9402 [ + - ]: 100 : if (strlen(op) == 1)
9403 : 100 : return op;
9404 : : }
9405 : 0 : return NULL;
9406 : : }
9407 : :
9408 : :
9409 : : /*
9410 : : * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
9411 : : *
9412 : : * true : simple in the context of parent node's type
9413 : : * false : not simple
9414 : : */
9415 : : static bool
9416 : 3981 : isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
9417 : : {
9418 [ - + ]: 3981 : if (!node)
9419 : 0 : return false;
9420 : :
9421 [ + + - + : 3981 : switch (nodeTag(node))
- + + + -
- - + + +
+ + ]
9422 : : {
9423 : 3346 : case T_Var:
9424 : : case T_Const:
9425 : : case T_Param:
9426 : : case T_CoerceToDomainValue:
9427 : : case T_SetToDefault:
9428 : : case T_CurrentOfExpr:
9429 : : /* single words: always simple */
9430 : 3346 : return true;
9431 : :
9432 : 331 : case T_SubscriptingRef:
9433 : : case T_ArrayExpr:
9434 : : case T_RowExpr:
9435 : : case T_CoalesceExpr:
9436 : : case T_MinMaxExpr:
9437 : : case T_SQLValueFunction:
9438 : : case T_XmlExpr:
9439 : : case T_NextValueExpr:
9440 : : case T_NullIfExpr:
9441 : : case T_Aggref:
9442 : : case T_GroupingFunc:
9443 : : case T_WindowFunc:
9444 : : case T_MergeSupportFunc:
9445 : : case T_FuncExpr:
9446 : : case T_JsonConstructorExpr:
9447 : : case T_JsonExpr:
9448 : : /* function-like: name(..) or name[..] */
9449 : 331 : return true;
9450 : :
9451 : : /* CASE keywords act as parentheses */
9452 : 0 : case T_CaseExpr:
9453 : 0 : return true;
9454 : :
9455 : 48 : case T_FieldSelect:
9456 : :
9457 : : /*
9458 : : * appears simple since . has top precedence, unless parent is
9459 : : * T_FieldSelect itself!
9460 : : */
9461 : 48 : return !IsA(parentNode, FieldSelect);
9462 : :
9463 : 0 : case T_FieldStore:
9464 : :
9465 : : /*
9466 : : * treat like FieldSelect (probably doesn't matter)
9467 : : */
9468 : 0 : return !IsA(parentNode, FieldStore);
9469 : :
9470 : 12 : case T_CoerceToDomain:
9471 : : /* maybe simple, check args */
9472 : 12 : return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
9473 : : node, prettyFlags);
9474 : 12 : case T_RelabelType:
9475 : 12 : return isSimpleNode((Node *) ((RelabelType *) node)->arg,
9476 : : node, prettyFlags);
9477 : 16 : case T_CoerceViaIO:
9478 : 16 : return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
9479 : : node, prettyFlags);
9480 : 0 : case T_ArrayCoerceExpr:
9481 : 0 : return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
9482 : : node, prettyFlags);
9483 : 0 : case T_ConvertRowtypeExpr:
9484 : 0 : return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
9485 : : node, prettyFlags);
9486 : 0 : case T_ReturningExpr:
9487 : 0 : return isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr,
9488 : : node, prettyFlags);
9489 : :
9490 : 184 : case T_OpExpr:
9491 : : {
9492 : : /* depends on parent node type; needs further checking */
9493 [ + - + + ]: 184 : if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
9494 : : {
9495 : : const char *op;
9496 : : const char *parentOp;
9497 : : bool is_lopriop;
9498 : : bool is_hipriop;
9499 : : bool is_lopriparent;
9500 : : bool is_hipriparent;
9501 : :
9502 : 52 : op = get_simple_binary_op_name((OpExpr *) node);
9503 [ - + ]: 52 : if (!op)
9504 : 0 : return false;
9505 : :
9506 : : /* We know only the basic operators + - and * / % */
9507 : 52 : is_lopriop = (strchr("+-", *op) != NULL);
9508 : 52 : is_hipriop = (strchr("*/%", *op) != NULL);
9509 [ + + + + ]: 52 : if (!(is_lopriop || is_hipriop))
9510 : 4 : return false;
9511 : :
9512 : 48 : parentOp = get_simple_binary_op_name((OpExpr *) parentNode);
9513 [ - + ]: 48 : if (!parentOp)
9514 : 0 : return false;
9515 : :
9516 : 48 : is_lopriparent = (strchr("+-", *parentOp) != NULL);
9517 : 48 : is_hipriparent = (strchr("*/%", *parentOp) != NULL);
9518 [ + + - + ]: 48 : if (!(is_lopriparent || is_hipriparent))
9519 : 0 : return false;
9520 : :
9521 [ + + + - ]: 48 : if (is_hipriop && is_lopriparent)
9522 : 8 : return true; /* op binds tighter than parent */
9523 : :
9524 [ + - + + ]: 40 : if (is_lopriop && is_hipriparent)
9525 : 32 : return false;
9526 : :
9527 : : /*
9528 : : * Operators are same priority --- can skip parens only if
9529 : : * we have (a - b) - c, not a - (b - c).
9530 : : */
9531 [ + + ]: 8 : if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
9532 : 4 : return true;
9533 : :
9534 : 4 : return false;
9535 : : }
9536 : : /* else do the same stuff as for T_SubLink et al. */
9537 : : }
9538 : : pg_fallthrough;
9539 : :
9540 : : case T_SubLink:
9541 : : case T_NullTest:
9542 : : case T_BooleanTest:
9543 : : case T_DistinctExpr:
9544 : : case T_JsonIsPredicate:
9545 [ + + + ]: 144 : switch (nodeTag(parentNode))
9546 : : {
9547 : 40 : case T_FuncExpr:
9548 : : {
9549 : : /* special handling for casts and COERCE_SQL_SYNTAX */
9550 : 40 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9551 : :
9552 [ + + + + ]: 40 : if (type == COERCE_EXPLICIT_CAST ||
9553 [ + - ]: 4 : type == COERCE_IMPLICIT_CAST ||
9554 : : type == COERCE_SQL_SYNTAX)
9555 : 40 : return false;
9556 : 0 : return true; /* own parentheses */
9557 : : }
9558 : 84 : case T_BoolExpr: /* lower precedence */
9559 : : case T_SubscriptingRef: /* other separators */
9560 : : case T_ArrayExpr: /* other separators */
9561 : : case T_RowExpr: /* other separators */
9562 : : case T_CoalesceExpr: /* own parentheses */
9563 : : case T_MinMaxExpr: /* own parentheses */
9564 : : case T_XmlExpr: /* own parentheses */
9565 : : case T_NullIfExpr: /* other separators */
9566 : : case T_Aggref: /* own parentheses */
9567 : : case T_GroupingFunc: /* own parentheses */
9568 : : case T_WindowFunc: /* own parentheses */
9569 : : case T_CaseExpr: /* other separators */
9570 : 84 : return true;
9571 : 20 : default:
9572 : 20 : return false;
9573 : : }
9574 : :
9575 : 12 : case T_BoolExpr:
9576 [ + - - - ]: 12 : switch (nodeTag(parentNode))
9577 : : {
9578 : 12 : case T_BoolExpr:
9579 [ + - ]: 12 : if (prettyFlags & PRETTYFLAG_PAREN)
9580 : : {
9581 : : BoolExprType type;
9582 : : BoolExprType parentType;
9583 : :
9584 : 12 : type = ((BoolExpr *) node)->boolop;
9585 : 12 : parentType = ((BoolExpr *) parentNode)->boolop;
9586 [ + + - ]: 12 : switch (type)
9587 : : {
9588 : 8 : case NOT_EXPR:
9589 : : case AND_EXPR:
9590 [ + + + - ]: 8 : if (parentType == AND_EXPR || parentType == OR_EXPR)
9591 : 8 : return true;
9592 : 0 : break;
9593 : 4 : case OR_EXPR:
9594 [ - + ]: 4 : if (parentType == OR_EXPR)
9595 : 0 : return true;
9596 : 4 : break;
9597 : : }
9598 : : }
9599 : 4 : return false;
9600 : 0 : case T_FuncExpr:
9601 : : {
9602 : : /* special handling for casts and COERCE_SQL_SYNTAX */
9603 : 0 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9604 : :
9605 [ # # # # ]: 0 : if (type == COERCE_EXPLICIT_CAST ||
9606 [ # # ]: 0 : type == COERCE_IMPLICIT_CAST ||
9607 : : type == COERCE_SQL_SYNTAX)
9608 : 0 : return false;
9609 : 0 : return true; /* own parentheses */
9610 : : }
9611 : 0 : case T_SubscriptingRef: /* other separators */
9612 : : case T_ArrayExpr: /* other separators */
9613 : : case T_RowExpr: /* other separators */
9614 : : case T_CoalesceExpr: /* own parentheses */
9615 : : case T_MinMaxExpr: /* own parentheses */
9616 : : case T_XmlExpr: /* own parentheses */
9617 : : case T_NullIfExpr: /* other separators */
9618 : : case T_Aggref: /* own parentheses */
9619 : : case T_GroupingFunc: /* own parentheses */
9620 : : case T_WindowFunc: /* own parentheses */
9621 : : case T_CaseExpr: /* other separators */
9622 : : case T_JsonExpr: /* own parentheses */
9623 : 0 : return true;
9624 : 0 : default:
9625 : 0 : return false;
9626 : : }
9627 : :
9628 : 4 : case T_JsonValueExpr:
9629 : : /* maybe simple, check args */
9630 : 4 : return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
9631 : : node, prettyFlags);
9632 : :
9633 : 4 : default:
9634 : 4 : break;
9635 : : }
9636 : : /* those we don't know: in dubio complexo */
9637 : 4 : return false;
9638 : : }
9639 : :
9640 : :
9641 : : /*
9642 : : * appendContextKeyword - append a keyword to buffer
9643 : : *
9644 : : * If prettyPrint is enabled, perform a line break, and adjust indentation.
9645 : : * Otherwise, just append the keyword.
9646 : : */
9647 : : static void
9648 : 19626 : appendContextKeyword(deparse_context *context, const char *str,
9649 : : int indentBefore, int indentAfter, int indentPlus)
9650 : : {
9651 : 19626 : StringInfo buf = context->buf;
9652 : :
9653 [ + + ]: 19626 : if (PRETTY_INDENT(context))
9654 : : {
9655 : : int indentAmount;
9656 : :
9657 : 18733 : context->indentLevel += indentBefore;
9658 : :
9659 : : /* remove any trailing spaces currently in the buffer ... */
9660 : 18733 : removeStringInfoSpaces(buf);
9661 : : /* ... then add a newline and some spaces */
9662 : 18733 : appendStringInfoChar(buf, '\n');
9663 : :
9664 [ + - ]: 18733 : if (context->indentLevel < PRETTYINDENT_LIMIT)
9665 : 18733 : indentAmount = Max(context->indentLevel, 0) + indentPlus;
9666 : : else
9667 : : {
9668 : : /*
9669 : : * If we're indented more than PRETTYINDENT_LIMIT characters, try
9670 : : * to conserve horizontal space by reducing the per-level
9671 : : * indentation. For best results the scale factor here should
9672 : : * divide all the indent amounts that get added to indentLevel
9673 : : * (PRETTYINDENT_STD, etc). It's important that the indentation
9674 : : * not grow unboundedly, else deeply-nested trees use O(N^2)
9675 : : * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
9676 : : */
9677 : 0 : indentAmount = PRETTYINDENT_LIMIT +
9678 : 0 : (context->indentLevel - PRETTYINDENT_LIMIT) /
9679 : : (PRETTYINDENT_STD / 2);
9680 : 0 : indentAmount %= PRETTYINDENT_LIMIT;
9681 : : /* scale/wrap logic affects indentLevel, but not indentPlus */
9682 : 0 : indentAmount += indentPlus;
9683 : : }
9684 : 18733 : appendStringInfoSpaces(buf, indentAmount);
9685 : :
9686 : 18733 : appendStringInfoString(buf, str);
9687 : :
9688 : 18733 : context->indentLevel += indentAfter;
9689 [ - + ]: 18733 : if (context->indentLevel < 0)
9690 : 0 : context->indentLevel = 0;
9691 : : }
9692 : : else
9693 : 893 : appendStringInfoString(buf, str);
9694 : 19626 : }
9695 : :
9696 : : /*
9697 : : * removeStringInfoSpaces - delete trailing spaces from a buffer.
9698 : : *
9699 : : * Possibly this should move to stringinfo.c at some point.
9700 : : */
9701 : : static void
9702 : 19108 : removeStringInfoSpaces(StringInfo str)
9703 : : {
9704 [ + + + + ]: 29811 : while (str->len > 0 && str->data[str->len - 1] == ' ')
9705 : 10703 : str->data[--(str->len)] = '\0';
9706 : 19108 : }
9707 : :
9708 : :
9709 : : /*
9710 : : * get_rule_expr_paren - deparse expr using get_rule_expr,
9711 : : * embracing the string with parentheses if necessary for prettyPrint.
9712 : : *
9713 : : * Never embrace if prettyFlags=0, because it's done in the calling node.
9714 : : *
9715 : : * Any node that does *not* embrace its argument node by sql syntax (with
9716 : : * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
9717 : : * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
9718 : : * added.
9719 : : */
9720 : : static void
9721 : 110941 : get_rule_expr_paren(Node *node, deparse_context *context,
9722 : : bool showimplicit, Node *parentNode)
9723 : : {
9724 : : bool need_paren;
9725 : :
9726 [ + + ]: 114878 : need_paren = PRETTY_PAREN(context) &&
9727 [ + + ]: 3937 : !isSimpleNode(node, parentNode, context->prettyFlags);
9728 : :
9729 [ + + ]: 110941 : if (need_paren)
9730 : 108 : appendStringInfoChar(context->buf, '(');
9731 : :
9732 : 110941 : get_rule_expr(node, context, showimplicit);
9733 : :
9734 [ + + ]: 110941 : if (need_paren)
9735 : 108 : appendStringInfoChar(context->buf, ')');
9736 : 110941 : }
9737 : :
9738 : : static void
9739 : 72 : get_json_behavior(JsonBehavior *behavior, deparse_context *context,
9740 : : const char *on)
9741 : : {
9742 : : /*
9743 : : * The order of array elements must correspond to the order of
9744 : : * JsonBehaviorType members.
9745 : : */
9746 : 72 : const char *behavior_names[] =
9747 : : {
9748 : : " NULL",
9749 : : " ERROR",
9750 : : " EMPTY",
9751 : : " TRUE",
9752 : : " FALSE",
9753 : : " UNKNOWN",
9754 : : " EMPTY ARRAY",
9755 : : " EMPTY OBJECT",
9756 : : " DEFAULT "
9757 : : };
9758 : :
9759 [ + - - + ]: 72 : if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
9760 [ # # ]: 0 : elog(ERROR, "invalid json behavior type: %d", behavior->btype);
9761 : :
9762 : 72 : appendStringInfoString(context->buf, behavior_names[behavior->btype]);
9763 : :
9764 [ + + ]: 72 : if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
9765 : 12 : get_rule_expr(behavior->expr, context, false);
9766 : :
9767 : 72 : appendStringInfo(context->buf, " ON %s", on);
9768 : 72 : }
9769 : :
9770 : : /*
9771 : : * get_json_expr_options
9772 : : *
9773 : : * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and
9774 : : * JSON_TABLE columns.
9775 : : */
9776 : : static void
9777 : 428 : get_json_expr_options(JsonExpr *jsexpr, deparse_context *context,
9778 : : JsonBehaviorType default_behavior)
9779 : : {
9780 [ + + ]: 428 : if (jsexpr->op == JSON_QUERY_OP)
9781 : : {
9782 [ + + ]: 192 : if (jsexpr->wrapper == JSW_CONDITIONAL)
9783 : 8 : appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
9784 [ + + ]: 184 : else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
9785 : 24 : appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
9786 : : /* The default */
9787 [ + + + - ]: 160 : else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
9788 : 160 : appendStringInfoString(context->buf, " WITHOUT WRAPPER");
9789 : :
9790 [ + + ]: 192 : if (jsexpr->omit_quotes)
9791 : 36 : appendStringInfoString(context->buf, " OMIT QUOTES");
9792 : : /* The default */
9793 : : else
9794 : 156 : appendStringInfoString(context->buf, " KEEP QUOTES");
9795 : : }
9796 : :
9797 [ + + + + ]: 428 : if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
9798 : 24 : get_json_behavior(jsexpr->on_empty, context, "EMPTY");
9799 : :
9800 [ + - + + ]: 428 : if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
9801 : 40 : get_json_behavior(jsexpr->on_error, context, "ERROR");
9802 : 428 : }
9803 : :
9804 : : /* ----------
9805 : : * get_rule_expr - Parse back an expression
9806 : : *
9807 : : * Note: showimplicit determines whether we display any implicit cast that
9808 : : * is present at the top of the expression tree. It is a passed argument,
9809 : : * not a field of the context struct, because we change the value as we
9810 : : * recurse down into the expression. In general we suppress implicit casts
9811 : : * when the result type is known with certainty (eg, the arguments of an
9812 : : * OR must be boolean). We display implicit casts for arguments of functions
9813 : : * and operators, since this is needed to be certain that the same function
9814 : : * or operator will be chosen when the expression is re-parsed.
9815 : : * ----------
9816 : : */
9817 : : static void
9818 : 241059 : get_rule_expr(Node *node, deparse_context *context,
9819 : : bool showimplicit)
9820 : : {
9821 : 241059 : StringInfo buf = context->buf;
9822 : :
9823 [ + + ]: 241059 : if (node == NULL)
9824 : 68 : return;
9825 : :
9826 : : /* Guard against excessively long or deeply-nested queries */
9827 [ - + ]: 240991 : CHECK_FOR_INTERRUPTS();
9828 : 240991 : check_stack_depth();
9829 : :
9830 : : /*
9831 : : * Each level of get_rule_expr must emit an indivisible term
9832 : : * (parenthesized if necessary) to ensure result is reparsed into the same
9833 : : * expression tree. The only exception is that when the input is a List,
9834 : : * we emit the component items comma-separated with no surrounding
9835 : : * decoration; this is convenient for most callers.
9836 : : */
9837 [ + + + + : 240991 : switch (nodeTag(node))
+ + + + +
+ + + + +
+ + + - +
+ + + + +
+ + - + +
+ + + + +
+ + + + +
+ - + + +
+ + + + +
+ + - ]
9838 : : {
9839 : 116276 : case T_Var:
9840 : 116276 : (void) get_variable((Var *) node, 0, false, context);
9841 : 116276 : break;
9842 : :
9843 : 42458 : case T_Const:
9844 : 42458 : get_const_expr((Const *) node, context, 0);
9845 : 42458 : break;
9846 : :
9847 : 4806 : case T_Param:
9848 : 4806 : get_parameter((Param *) node, context);
9849 : 4806 : break;
9850 : :
9851 : 2640 : case T_Aggref:
9852 : 2640 : get_agg_expr((Aggref *) node, context, (Aggref *) node);
9853 : 2640 : break;
9854 : :
9855 : 74 : case T_GroupingFunc:
9856 : : {
9857 : 74 : GroupingFunc *gexpr = (GroupingFunc *) node;
9858 : :
9859 : 74 : appendStringInfoString(buf, "GROUPING(");
9860 : 74 : get_rule_expr((Node *) gexpr->args, context, true);
9861 : 74 : appendStringInfoChar(buf, ')');
9862 : : }
9863 : 74 : break;
9864 : :
9865 : 246 : case T_WindowFunc:
9866 : 246 : get_windowfunc_expr((WindowFunc *) node, context);
9867 : 246 : break;
9868 : :
9869 : 4 : case T_MergeSupportFunc:
9870 : 4 : appendStringInfoString(buf, "MERGE_ACTION()");
9871 : 4 : break;
9872 : :
9873 : 232 : case T_SubscriptingRef:
9874 : : {
9875 : 232 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
9876 : : bool need_parens;
9877 : :
9878 : : /*
9879 : : * If the argument is a CaseTestExpr, we must be inside a
9880 : : * FieldStore, ie, we are assigning to an element of an array
9881 : : * within a composite column. Since we already punted on
9882 : : * displaying the FieldStore's target information, just punt
9883 : : * here too, and display only the assignment source
9884 : : * expression.
9885 : : */
9886 [ - + ]: 232 : if (IsA(sbsref->refexpr, CaseTestExpr))
9887 : : {
9888 : : Assert(sbsref->refassgnexpr);
9889 : 0 : get_rule_expr((Node *) sbsref->refassgnexpr,
9890 : : context, showimplicit);
9891 : 0 : break;
9892 : : }
9893 : :
9894 : : /*
9895 : : * Parenthesize the argument unless it's a simple Var or a
9896 : : * FieldSelect. (In particular, if it's another
9897 : : * SubscriptingRef, we *must* parenthesize to avoid
9898 : : * confusion.)
9899 : : */
9900 [ + + ]: 358 : need_parens = !IsA(sbsref->refexpr, Var) &&
9901 [ + + ]: 126 : !IsA(sbsref->refexpr, FieldSelect);
9902 [ + + ]: 232 : if (need_parens)
9903 : 56 : appendStringInfoChar(buf, '(');
9904 : 232 : get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
9905 [ + + ]: 232 : if (need_parens)
9906 : 56 : appendStringInfoChar(buf, ')');
9907 : :
9908 : : /*
9909 : : * If there's a refassgnexpr, we want to print the node in the
9910 : : * format "container[subscripts] := refassgnexpr". This is
9911 : : * not legal SQL, so decompilation of INSERT or UPDATE
9912 : : * statements should always use processIndirection as part of
9913 : : * the statement-level syntax. We should only see this when
9914 : : * EXPLAIN tries to print the targetlist of a plan resulting
9915 : : * from such a statement.
9916 : : */
9917 [ + + ]: 232 : if (sbsref->refassgnexpr)
9918 : : {
9919 : : Node *refassgnexpr;
9920 : :
9921 : : /*
9922 : : * Use processIndirection to print this node's subscripts
9923 : : * as well as any additional field selections or
9924 : : * subscripting in immediate descendants. It returns the
9925 : : * RHS expr that is actually being "assigned".
9926 : : */
9927 : 8 : refassgnexpr = processIndirection(node, context);
9928 : 8 : appendStringInfoString(buf, " := ");
9929 : 8 : get_rule_expr(refassgnexpr, context, showimplicit);
9930 : : }
9931 : : else
9932 : : {
9933 : : /* Just an ordinary container fetch, so print subscripts */
9934 : 224 : printSubscripts(sbsref, context);
9935 : : }
9936 : : }
9937 : 232 : break;
9938 : :
9939 : 8237 : case T_FuncExpr:
9940 : 8237 : get_func_expr((FuncExpr *) node, context, showimplicit);
9941 : 8237 : break;
9942 : :
9943 : 20 : case T_NamedArgExpr:
9944 : : {
9945 : 20 : NamedArgExpr *na = (NamedArgExpr *) node;
9946 : :
9947 : 20 : appendStringInfo(buf, "%s => ", quote_identifier(na->name));
9948 : 20 : get_rule_expr((Node *) na->arg, context, showimplicit);
9949 : : }
9950 : 20 : break;
9951 : :
9952 : 41520 : case T_OpExpr:
9953 : 41520 : get_oper_expr((OpExpr *) node, context);
9954 : 41520 : break;
9955 : :
9956 : 16 : case T_DistinctExpr:
9957 : : {
9958 : 16 : DistinctExpr *expr = (DistinctExpr *) node;
9959 : 16 : List *args = expr->args;
9960 : 16 : Node *arg1 = (Node *) linitial(args);
9961 : 16 : Node *arg2 = (Node *) lsecond(args);
9962 : :
9963 [ + + ]: 16 : if (!PRETTY_PAREN(context))
9964 : 12 : appendStringInfoChar(buf, '(');
9965 : 16 : get_rule_expr_paren(arg1, context, true, node);
9966 : 16 : appendStringInfoString(buf, " IS DISTINCT FROM ");
9967 : 16 : get_rule_expr_paren(arg2, context, true, node);
9968 [ + + ]: 16 : if (!PRETTY_PAREN(context))
9969 : 12 : appendStringInfoChar(buf, ')');
9970 : : }
9971 : 16 : break;
9972 : :
9973 : 140 : case T_NullIfExpr:
9974 : : {
9975 : 140 : NullIfExpr *nullifexpr = (NullIfExpr *) node;
9976 : :
9977 : 140 : appendStringInfoString(buf, "NULLIF(");
9978 : 140 : get_rule_expr((Node *) nullifexpr->args, context, true);
9979 : 140 : appendStringInfoChar(buf, ')');
9980 : : }
9981 : 140 : break;
9982 : :
9983 : 2067 : case T_ScalarArrayOpExpr:
9984 : : {
9985 : 2067 : ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
9986 : 2067 : List *args = expr->args;
9987 : 2067 : Node *arg1 = (Node *) linitial(args);
9988 : 2067 : Node *arg2 = (Node *) lsecond(args);
9989 : :
9990 [ + + ]: 2067 : if (!PRETTY_PAREN(context))
9991 : 2059 : appendStringInfoChar(buf, '(');
9992 : 2067 : get_rule_expr_paren(arg1, context, true, node);
9993 : 2067 : appendStringInfo(buf, " %s %s (",
9994 : : generate_operator_name(expr->opno,
9995 : : exprType(arg1),
9996 : : get_base_element_type(exprType(arg2))),
9997 [ + + ]: 2067 : expr->useOr ? "ANY" : "ALL");
9998 : 2067 : get_rule_expr_paren(arg2, context, true, node);
9999 : :
10000 : : /*
10001 : : * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
10002 : : * a bare sub-SELECT. Since we're here, the sub-SELECT must
10003 : : * be meant as a scalar sub-SELECT yielding an array value to
10004 : : * be used in ScalarArrayOpExpr; but the grammar will
10005 : : * preferentially interpret such a construct as an ANY/ALL
10006 : : * SubLink. To prevent misparsing the output that way, insert
10007 : : * a dummy coercion (which will be stripped by parse analysis,
10008 : : * so no inefficiency is added in dump and reload). This is
10009 : : * indeed most likely what the user wrote to get the construct
10010 : : * accepted in the first place.
10011 : : */
10012 [ + + ]: 2067 : if (IsA(arg2, SubLink) &&
10013 [ + - ]: 4 : ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
10014 : 4 : appendStringInfo(buf, "::%s",
10015 : : format_type_with_typemod(exprType(arg2),
10016 : : exprTypmod(arg2)));
10017 : 2067 : appendStringInfoChar(buf, ')');
10018 [ + + ]: 2067 : if (!PRETTY_PAREN(context))
10019 : 2059 : appendStringInfoChar(buf, ')');
10020 : : }
10021 : 2067 : break;
10022 : :
10023 : 7527 : case T_BoolExpr:
10024 : : {
10025 : 7527 : BoolExpr *expr = (BoolExpr *) node;
10026 : 7527 : Node *first_arg = linitial(expr->args);
10027 : : ListCell *arg;
10028 : :
10029 [ + + + - ]: 7527 : switch (expr->boolop)
10030 : : {
10031 : 5969 : case AND_EXPR:
10032 [ + + ]: 5969 : if (!PRETTY_PAREN(context))
10033 : 5933 : appendStringInfoChar(buf, '(');
10034 : 5969 : get_rule_expr_paren(first_arg, context,
10035 : : false, node);
10036 [ + - + + : 13611 : for_each_from(arg, expr->args, 1)
+ + ]
10037 : : {
10038 : 7642 : appendStringInfoString(buf, " AND ");
10039 : 7642 : get_rule_expr_paren((Node *) lfirst(arg), context,
10040 : : false, node);
10041 : : }
10042 [ + + ]: 5969 : if (!PRETTY_PAREN(context))
10043 : 5933 : appendStringInfoChar(buf, ')');
10044 : 5969 : break;
10045 : :
10046 : 1254 : case OR_EXPR:
10047 [ + + ]: 1254 : if (!PRETTY_PAREN(context))
10048 : 1246 : appendStringInfoChar(buf, '(');
10049 : 1254 : get_rule_expr_paren(first_arg, context,
10050 : : false, node);
10051 [ + - + + : 2983 : for_each_from(arg, expr->args, 1)
+ + ]
10052 : : {
10053 : 1729 : appendStringInfoString(buf, " OR ");
10054 : 1729 : get_rule_expr_paren((Node *) lfirst(arg), context,
10055 : : false, node);
10056 : : }
10057 [ + + ]: 1254 : if (!PRETTY_PAREN(context))
10058 : 1246 : appendStringInfoChar(buf, ')');
10059 : 1254 : break;
10060 : :
10061 : 304 : case NOT_EXPR:
10062 [ + + ]: 304 : if (!PRETTY_PAREN(context))
10063 : 296 : appendStringInfoChar(buf, '(');
10064 : 304 : appendStringInfoString(buf, "NOT ");
10065 : 304 : get_rule_expr_paren(first_arg, context,
10066 : : false, node);
10067 [ + + ]: 304 : if (!PRETTY_PAREN(context))
10068 : 296 : appendStringInfoChar(buf, ')');
10069 : 304 : break;
10070 : :
10071 : 0 : default:
10072 [ # # ]: 0 : elog(ERROR, "unrecognized boolop: %d",
10073 : : (int) expr->boolop);
10074 : : }
10075 : : }
10076 : 7527 : break;
10077 : :
10078 : 290 : case T_SubLink:
10079 : 290 : get_sublink_expr((SubLink *) node, context);
10080 : 290 : break;
10081 : :
10082 : 540 : case T_SubPlan:
10083 : : {
10084 : 540 : SubPlan *subplan = (SubPlan *) node;
10085 : :
10086 : : /*
10087 : : * We cannot see an already-planned subplan in rule deparsing,
10088 : : * only while EXPLAINing a query plan. We don't try to
10089 : : * reconstruct the original SQL, just reference the subplan
10090 : : * that appears elsewhere in EXPLAIN's result. It does seem
10091 : : * useful to show the subLinkType and testexpr (if any), and
10092 : : * we also note whether the subplan will be hashed.
10093 : : */
10094 [ + + + + : 540 : switch (subplan->subLinkType)
+ + + -
- ]
10095 : : {
10096 : 69 : case EXISTS_SUBLINK:
10097 : 69 : appendStringInfoString(buf, "EXISTS(");
10098 : : Assert(subplan->testexpr == NULL);
10099 : 69 : break;
10100 : 4 : case ALL_SUBLINK:
10101 : 4 : appendStringInfoString(buf, "(ALL ");
10102 : : Assert(subplan->testexpr != NULL);
10103 : 4 : break;
10104 : 164 : case ANY_SUBLINK:
10105 : 164 : appendStringInfoString(buf, "(ANY ");
10106 : : Assert(subplan->testexpr != NULL);
10107 : 164 : break;
10108 : 4 : case ROWCOMPARE_SUBLINK:
10109 : : /* Parenthesizing the testexpr seems sufficient */
10110 : 4 : appendStringInfoChar(buf, '(');
10111 : : Assert(subplan->testexpr != NULL);
10112 : 4 : break;
10113 : 274 : case EXPR_SUBLINK:
10114 : : /* No need to decorate these subplan references */
10115 : 274 : appendStringInfoChar(buf, '(');
10116 : : Assert(subplan->testexpr == NULL);
10117 : 274 : break;
10118 : 17 : case MULTIEXPR_SUBLINK:
10119 : : /* MULTIEXPR isn't executed in the normal way */
10120 : 17 : appendStringInfoString(buf, "(rescan ");
10121 : : Assert(subplan->testexpr == NULL);
10122 : 17 : break;
10123 : 8 : case ARRAY_SUBLINK:
10124 : 8 : appendStringInfoString(buf, "ARRAY(");
10125 : : Assert(subplan->testexpr == NULL);
10126 : 8 : break;
10127 : 0 : case CTE_SUBLINK:
10128 : : /* This case is unreachable within expressions */
10129 : 0 : appendStringInfoString(buf, "CTE(");
10130 : : Assert(subplan->testexpr == NULL);
10131 : 0 : break;
10132 : : }
10133 : :
10134 [ + + ]: 540 : if (subplan->testexpr != NULL)
10135 : : {
10136 : : deparse_namespace *dpns;
10137 : :
10138 : : /*
10139 : : * Push SubPlan into ancestors list while deparsing
10140 : : * testexpr, so that we can handle PARAM_EXEC references
10141 : : * to the SubPlan's paramIds. (This makes it look like
10142 : : * the SubPlan is an "ancestor" of the current plan node,
10143 : : * which is a little weird, but it does no harm.) In this
10144 : : * path, we don't need to mention the SubPlan explicitly,
10145 : : * because the referencing Params will show its existence.
10146 : : */
10147 : 172 : dpns = (deparse_namespace *) linitial(context->namespaces);
10148 : 172 : dpns->ancestors = lcons(subplan, dpns->ancestors);
10149 : :
10150 : 172 : get_rule_expr(subplan->testexpr, context, showimplicit);
10151 : 172 : appendStringInfoChar(buf, ')');
10152 : :
10153 : 172 : dpns->ancestors = list_delete_first(dpns->ancestors);
10154 : : }
10155 : : else
10156 : : {
10157 : : const char *nameprefix;
10158 : :
10159 : : /* No referencing Params, so show the SubPlan's name */
10160 [ - + ]: 368 : if (subplan->isInitPlan)
10161 : 0 : nameprefix = "InitPlan ";
10162 : : else
10163 : 368 : nameprefix = "SubPlan ";
10164 [ - + ]: 368 : if (subplan->useHashTable)
10165 : 0 : appendStringInfo(buf, "hashed %s%s)",
10166 : : nameprefix, subplan->plan_name);
10167 : : else
10168 : 368 : appendStringInfo(buf, "%s%s)",
10169 : : nameprefix, subplan->plan_name);
10170 : : }
10171 : : }
10172 : 540 : break;
10173 : :
10174 : 0 : case T_AlternativeSubPlan:
10175 : : {
10176 : 0 : AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
10177 : : ListCell *lc;
10178 : :
10179 : : /*
10180 : : * This case cannot be reached in normal usage, since no
10181 : : * AlternativeSubPlan can appear either in parsetrees or
10182 : : * finished plan trees. We keep it just in case somebody
10183 : : * wants to use this code to print planner data structures.
10184 : : */
10185 : 0 : appendStringInfoString(buf, "(alternatives: ");
10186 [ # # # # : 0 : foreach(lc, asplan->subplans)
# # ]
10187 : : {
10188 : 0 : SubPlan *splan = lfirst_node(SubPlan, lc);
10189 : : const char *nameprefix;
10190 : :
10191 [ # # ]: 0 : if (splan->isInitPlan)
10192 : 0 : nameprefix = "InitPlan ";
10193 : : else
10194 : 0 : nameprefix = "SubPlan ";
10195 [ # # ]: 0 : if (splan->useHashTable)
10196 : 0 : appendStringInfo(buf, "hashed %s%s", nameprefix,
10197 : : splan->plan_name);
10198 : : else
10199 : 0 : appendStringInfo(buf, "%s%s", nameprefix,
10200 : : splan->plan_name);
10201 [ # # ]: 0 : if (lnext(asplan->subplans, lc))
10202 : 0 : appendStringInfoString(buf, " or ");
10203 : : }
10204 : 0 : appendStringInfoChar(buf, ')');
10205 : : }
10206 : 0 : break;
10207 : :
10208 : 934 : case T_FieldSelect:
10209 : : {
10210 : 934 : FieldSelect *fselect = (FieldSelect *) node;
10211 : 934 : Node *arg = (Node *) fselect->arg;
10212 : 934 : int fno = fselect->fieldnum;
10213 : : const char *fieldname;
10214 : : bool need_parens;
10215 : :
10216 : : /*
10217 : : * Parenthesize the argument unless it's a SubscriptingRef or
10218 : : * another FieldSelect. Note in particular that it would be
10219 : : * WRONG to not parenthesize a Var argument; simplicity is not
10220 : : * the issue here, having the right number of names is.
10221 : : */
10222 [ + + ]: 1844 : need_parens = !IsA(arg, SubscriptingRef) &&
10223 [ + - ]: 910 : !IsA(arg, FieldSelect);
10224 [ + + ]: 934 : if (need_parens)
10225 : 910 : appendStringInfoChar(buf, '(');
10226 : 934 : get_rule_expr(arg, context, true);
10227 [ + + ]: 934 : if (need_parens)
10228 : 910 : appendStringInfoChar(buf, ')');
10229 : :
10230 : : /*
10231 : : * Get and print the field name.
10232 : : */
10233 : 934 : fieldname = get_name_for_var_field((Var *) arg, fno,
10234 : : 0, context);
10235 : 934 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
10236 : : }
10237 : 934 : break;
10238 : :
10239 : 4 : case T_FieldStore:
10240 : : {
10241 : 4 : FieldStore *fstore = (FieldStore *) node;
10242 : : bool need_parens;
10243 : :
10244 : : /*
10245 : : * There is no good way to represent a FieldStore as real SQL,
10246 : : * so decompilation of INSERT or UPDATE statements should
10247 : : * always use processIndirection as part of the
10248 : : * statement-level syntax. We should only get here when
10249 : : * EXPLAIN tries to print the targetlist of a plan resulting
10250 : : * from such a statement. The plan case is even harder than
10251 : : * ordinary rules would be, because the planner tries to
10252 : : * collapse multiple assignments to the same field or subfield
10253 : : * into one FieldStore; so we can see a list of target fields
10254 : : * not just one, and the arguments could be FieldStores
10255 : : * themselves. We don't bother to try to print the target
10256 : : * field names; we just print the source arguments, with a
10257 : : * ROW() around them if there's more than one. This isn't
10258 : : * terribly complete, but it's probably good enough for
10259 : : * EXPLAIN's purposes; especially since anything more would be
10260 : : * either hopelessly confusing or an even poorer
10261 : : * representation of what the plan is actually doing.
10262 : : */
10263 : 4 : need_parens = (list_length(fstore->newvals) != 1);
10264 [ + - ]: 4 : if (need_parens)
10265 : 4 : appendStringInfoString(buf, "ROW(");
10266 : 4 : get_rule_expr((Node *) fstore->newvals, context, showimplicit);
10267 [ + - ]: 4 : if (need_parens)
10268 : 4 : appendStringInfoChar(buf, ')');
10269 : : }
10270 : 4 : break;
10271 : :
10272 : 2015 : case T_RelabelType:
10273 : : {
10274 : 2015 : RelabelType *relabel = (RelabelType *) node;
10275 : 2015 : Node *arg = (Node *) relabel->arg;
10276 : :
10277 [ + + ]: 2015 : if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
10278 [ + + ]: 1815 : !showimplicit)
10279 : : {
10280 : : /* don't show the implicit cast */
10281 : 45 : get_rule_expr_paren(arg, context, false, node);
10282 : : }
10283 : : else
10284 : : {
10285 : 1970 : get_coercion_expr(arg, context,
10286 : : relabel->resulttype,
10287 : : relabel->resulttypmod,
10288 : : node);
10289 : : }
10290 : : }
10291 : 2015 : break;
10292 : :
10293 : 501 : case T_CoerceViaIO:
10294 : : {
10295 : 501 : CoerceViaIO *iocoerce = (CoerceViaIO *) node;
10296 : 501 : Node *arg = (Node *) iocoerce->arg;
10297 : :
10298 [ + + ]: 501 : if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
10299 [ + - ]: 24 : !showimplicit)
10300 : : {
10301 : : /* don't show the implicit cast */
10302 : 24 : get_rule_expr_paren(arg, context, false, node);
10303 : : }
10304 : : else
10305 : : {
10306 : 477 : get_coercion_expr(arg, context,
10307 : : iocoerce->resulttype,
10308 : : -1,
10309 : : node);
10310 : : }
10311 : : }
10312 : 501 : break;
10313 : :
10314 : 39 : case T_ArrayCoerceExpr:
10315 : : {
10316 : 39 : ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
10317 : 39 : Node *arg = (Node *) acoerce->arg;
10318 : :
10319 [ + + ]: 39 : if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
10320 [ + + ]: 37 : !showimplicit)
10321 : : {
10322 : : /* don't show the implicit cast */
10323 : 4 : get_rule_expr_paren(arg, context, false, node);
10324 : : }
10325 : : else
10326 : : {
10327 : 35 : get_coercion_expr(arg, context,
10328 : : acoerce->resulttype,
10329 : : acoerce->resulttypmod,
10330 : : node);
10331 : : }
10332 : : }
10333 : 39 : break;
10334 : :
10335 : 53 : case T_ConvertRowtypeExpr:
10336 : : {
10337 : 53 : ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
10338 : 53 : Node *arg = (Node *) convert->arg;
10339 : :
10340 [ + + ]: 53 : if (convert->convertformat == COERCE_IMPLICIT_CAST &&
10341 [ + + ]: 49 : !showimplicit)
10342 : : {
10343 : : /* don't show the implicit cast */
10344 : 12 : get_rule_expr_paren(arg, context, false, node);
10345 : : }
10346 : : else
10347 : : {
10348 : 41 : get_coercion_expr(arg, context,
10349 : : convert->resulttype, -1,
10350 : : node);
10351 : : }
10352 : : }
10353 : 53 : break;
10354 : :
10355 : 122 : case T_CollateExpr:
10356 : : {
10357 : 122 : CollateExpr *collate = (CollateExpr *) node;
10358 : 122 : Node *arg = (Node *) collate->arg;
10359 : :
10360 [ + + ]: 122 : if (!PRETTY_PAREN(context))
10361 : 118 : appendStringInfoChar(buf, '(');
10362 : 122 : get_rule_expr_paren(arg, context, showimplicit, node);
10363 : 122 : appendStringInfo(buf, " COLLATE %s",
10364 : : generate_collation_name(collate->collOid));
10365 [ + + ]: 122 : if (!PRETTY_PAREN(context))
10366 : 118 : appendStringInfoChar(buf, ')');
10367 : : }
10368 : 122 : break;
10369 : :
10370 : 480 : case T_CaseExpr:
10371 : : {
10372 : 480 : CaseExpr *caseexpr = (CaseExpr *) node;
10373 : : ListCell *temp;
10374 : :
10375 : 480 : appendContextKeyword(context, "CASE",
10376 : : 0, PRETTYINDENT_VAR, 0);
10377 [ + + ]: 480 : if (caseexpr->arg)
10378 : : {
10379 : 191 : appendStringInfoChar(buf, ' ');
10380 : 191 : get_rule_expr((Node *) caseexpr->arg, context, true);
10381 : : }
10382 [ + - + + : 1973 : foreach(temp, caseexpr->args)
+ + ]
10383 : : {
10384 : 1493 : CaseWhen *when = (CaseWhen *) lfirst(temp);
10385 : 1493 : Node *w = (Node *) when->expr;
10386 : :
10387 [ + + ]: 1493 : if (caseexpr->arg)
10388 : : {
10389 : : /*
10390 : : * The parser should have produced WHEN clauses of the
10391 : : * form "CaseTestExpr = RHS", possibly with an
10392 : : * implicit coercion inserted above the CaseTestExpr.
10393 : : * For accurate decompilation of rules it's essential
10394 : : * that we show just the RHS. However in an
10395 : : * expression that's been through the optimizer, the
10396 : : * WHEN clause could be almost anything (since the
10397 : : * equality operator could have been expanded into an
10398 : : * inline function). If we don't recognize the form
10399 : : * of the WHEN clause, just punt and display it as-is.
10400 : : */
10401 [ + - ]: 609 : if (IsA(w, OpExpr))
10402 : : {
10403 : 609 : List *args = ((OpExpr *) w)->args;
10404 : :
10405 [ + - ]: 609 : if (list_length(args) == 2 &&
10406 [ + - ]: 609 : IsA(strip_implicit_coercions(linitial(args)),
10407 : : CaseTestExpr))
10408 : 609 : w = (Node *) lsecond(args);
10409 : : }
10410 : : }
10411 : :
10412 [ + + ]: 1493 : if (!PRETTY_INDENT(context))
10413 : 96 : appendStringInfoChar(buf, ' ');
10414 : 1493 : appendContextKeyword(context, "WHEN ",
10415 : : 0, 0, 0);
10416 : 1493 : get_rule_expr(w, context, false);
10417 : 1493 : appendStringInfoString(buf, " THEN ");
10418 : 1493 : get_rule_expr((Node *) when->result, context, true);
10419 : : }
10420 [ + + ]: 480 : if (!PRETTY_INDENT(context))
10421 : 91 : appendStringInfoChar(buf, ' ');
10422 : 480 : appendContextKeyword(context, "ELSE ",
10423 : : 0, 0, 0);
10424 : 480 : get_rule_expr((Node *) caseexpr->defresult, context, true);
10425 [ + + ]: 480 : if (!PRETTY_INDENT(context))
10426 : 91 : appendStringInfoChar(buf, ' ');
10427 : 480 : appendContextKeyword(context, "END",
10428 : : -PRETTYINDENT_VAR, 0, 0);
10429 : : }
10430 : 480 : break;
10431 : :
10432 : 0 : case T_CaseTestExpr:
10433 : : {
10434 : : /*
10435 : : * Normally we should never get here, since for expressions
10436 : : * that can contain this node type we attempt to avoid
10437 : : * recursing to it. But in an optimized expression we might
10438 : : * be unable to avoid that (see comments for CaseExpr). If we
10439 : : * do see one, print it as CASE_TEST_EXPR.
10440 : : */
10441 : 0 : appendStringInfoString(buf, "CASE_TEST_EXPR");
10442 : : }
10443 : 0 : break;
10444 : :
10445 : 367 : case T_ArrayExpr:
10446 : : {
10447 : 367 : ArrayExpr *arrayexpr = (ArrayExpr *) node;
10448 : :
10449 : 367 : appendStringInfoString(buf, "ARRAY[");
10450 : 367 : get_rule_expr((Node *) arrayexpr->elements, context, true);
10451 : 367 : appendStringInfoChar(buf, ']');
10452 : :
10453 : : /*
10454 : : * If the array isn't empty, we assume its elements are
10455 : : * coerced to the desired type. If it's empty, though, we
10456 : : * need an explicit coercion to the array type.
10457 : : */
10458 [ + + ]: 367 : if (arrayexpr->elements == NIL)
10459 : 4 : appendStringInfo(buf, "::%s",
10460 : : format_type_with_typemod(arrayexpr->array_typeid, -1));
10461 : : }
10462 : 367 : break;
10463 : :
10464 : 186 : case T_RowExpr:
10465 : : {
10466 : 186 : RowExpr *rowexpr = (RowExpr *) node;
10467 : 186 : TupleDesc tupdesc = NULL;
10468 : : ListCell *arg;
10469 : : int i;
10470 : : char *sep;
10471 : :
10472 : : /*
10473 : : * If it's a named type and not RECORD, we may have to skip
10474 : : * dropped columns and/or claim there are NULLs for added
10475 : : * columns.
10476 : : */
10477 [ + + ]: 186 : if (rowexpr->row_typeid != RECORDOID)
10478 : : {
10479 : 44 : tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
10480 : : Assert(list_length(rowexpr->args) <= tupdesc->natts);
10481 : : }
10482 : :
10483 : : /*
10484 : : * SQL99 allows "ROW" to be omitted when there is more than
10485 : : * one column, but for simplicity we always print it.
10486 : : */
10487 : 186 : appendStringInfoString(buf, "ROW(");
10488 : 186 : sep = "";
10489 : 186 : i = 0;
10490 [ + - + + : 564 : foreach(arg, rowexpr->args)
+ + ]
10491 : : {
10492 : 378 : Node *e = (Node *) lfirst(arg);
10493 : :
10494 [ + + ]: 378 : if (tupdesc == NULL ||
10495 [ + - ]: 104 : !TupleDescCompactAttr(tupdesc, i)->attisdropped)
10496 : : {
10497 : 378 : appendStringInfoString(buf, sep);
10498 : : /* Whole-row Vars need special treatment here */
10499 : 378 : get_rule_expr_toplevel(e, context, true);
10500 : 378 : sep = ", ";
10501 : : }
10502 : 378 : i++;
10503 : : }
10504 [ + + ]: 186 : if (tupdesc != NULL)
10505 : : {
10506 [ - + ]: 44 : while (i < tupdesc->natts)
10507 : : {
10508 [ # # ]: 0 : if (!TupleDescCompactAttr(tupdesc, i)->attisdropped)
10509 : : {
10510 : 0 : appendStringInfoString(buf, sep);
10511 : 0 : appendStringInfoString(buf, "NULL");
10512 : 0 : sep = ", ";
10513 : : }
10514 : 0 : i++;
10515 : : }
10516 : :
10517 [ + - ]: 44 : ReleaseTupleDesc(tupdesc);
10518 : : }
10519 : 186 : appendStringInfoChar(buf, ')');
10520 [ + + ]: 186 : if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
10521 : 24 : appendStringInfo(buf, "::%s",
10522 : : format_type_with_typemod(rowexpr->row_typeid, -1));
10523 : : }
10524 : 186 : break;
10525 : :
10526 : 100 : case T_RowCompareExpr:
10527 : : {
10528 : 100 : RowCompareExpr *rcexpr = (RowCompareExpr *) node;
10529 : :
10530 : : /*
10531 : : * SQL99 allows "ROW" to be omitted when there is more than
10532 : : * one column, but for simplicity we always print it. Within
10533 : : * a ROW expression, whole-row Vars need special treatment, so
10534 : : * use get_rule_list_toplevel.
10535 : : */
10536 : 100 : appendStringInfoString(buf, "(ROW(");
10537 : 100 : get_rule_list_toplevel(rcexpr->largs, context, true);
10538 : :
10539 : : /*
10540 : : * We assume that the name of the first-column operator will
10541 : : * do for all the rest too. This is definitely open to
10542 : : * failure, eg if some but not all operators were renamed
10543 : : * since the construct was parsed, but there seems no way to
10544 : : * be perfect.
10545 : : */
10546 : 100 : appendStringInfo(buf, ") %s ROW(",
10547 : 100 : generate_operator_name(linitial_oid(rcexpr->opnos),
10548 : 100 : exprType(linitial(rcexpr->largs)),
10549 : 100 : exprType(linitial(rcexpr->rargs))));
10550 : 100 : get_rule_list_toplevel(rcexpr->rargs, context, true);
10551 : 100 : appendStringInfoString(buf, "))");
10552 : : }
10553 : 100 : break;
10554 : :
10555 : 837 : case T_CoalesceExpr:
10556 : : {
10557 : 837 : CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
10558 : :
10559 : 837 : appendStringInfoString(buf, "COALESCE(");
10560 : 837 : get_rule_expr((Node *) coalesceexpr->args, context, true);
10561 : 837 : appendStringInfoChar(buf, ')');
10562 : : }
10563 : 837 : break;
10564 : :
10565 : 28 : case T_MinMaxExpr:
10566 : : {
10567 : 28 : MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
10568 : :
10569 [ + + - ]: 28 : switch (minmaxexpr->op)
10570 : : {
10571 : 8 : case IS_GREATEST:
10572 : 8 : appendStringInfoString(buf, "GREATEST(");
10573 : 8 : break;
10574 : 20 : case IS_LEAST:
10575 : 20 : appendStringInfoString(buf, "LEAST(");
10576 : 20 : break;
10577 : : }
10578 : 28 : get_rule_expr((Node *) minmaxexpr->args, context, true);
10579 : 28 : appendStringInfoChar(buf, ')');
10580 : : }
10581 : 28 : break;
10582 : :
10583 : 449 : case T_SQLValueFunction:
10584 : : {
10585 : 449 : SQLValueFunction *svf = (SQLValueFunction *) node;
10586 : :
10587 : : /*
10588 : : * Note: this code knows that typmod for time, timestamp, and
10589 : : * timestamptz just prints as integer.
10590 : : */
10591 [ + + + + : 449 : switch (svf->op)
+ + + + +
+ + + + +
+ - ]
10592 : : {
10593 : 69 : case SVFOP_CURRENT_DATE:
10594 : 69 : appendStringInfoString(buf, "CURRENT_DATE");
10595 : 69 : break;
10596 : 8 : case SVFOP_CURRENT_TIME:
10597 : 8 : appendStringInfoString(buf, "CURRENT_TIME");
10598 : 8 : break;
10599 : 8 : case SVFOP_CURRENT_TIME_N:
10600 : 8 : appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
10601 : 8 : break;
10602 : 8 : case SVFOP_CURRENT_TIMESTAMP:
10603 : 8 : appendStringInfoString(buf, "CURRENT_TIMESTAMP");
10604 : 8 : break;
10605 : 75 : case SVFOP_CURRENT_TIMESTAMP_N:
10606 : 75 : appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
10607 : : svf->typmod);
10608 : 75 : break;
10609 : 8 : case SVFOP_LOCALTIME:
10610 : 8 : appendStringInfoString(buf, "LOCALTIME");
10611 : 8 : break;
10612 : 8 : case SVFOP_LOCALTIME_N:
10613 : 8 : appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
10614 : 8 : break;
10615 : 20 : case SVFOP_LOCALTIMESTAMP:
10616 : 20 : appendStringInfoString(buf, "LOCALTIMESTAMP");
10617 : 20 : break;
10618 : 12 : case SVFOP_LOCALTIMESTAMP_N:
10619 : 12 : appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
10620 : : svf->typmod);
10621 : 12 : break;
10622 : 8 : case SVFOP_CURRENT_ROLE:
10623 : 8 : appendStringInfoString(buf, "CURRENT_ROLE");
10624 : 8 : break;
10625 : 181 : case SVFOP_CURRENT_USER:
10626 : 181 : appendStringInfoString(buf, "CURRENT_USER");
10627 : 181 : break;
10628 : 8 : case SVFOP_USER:
10629 : 8 : appendStringInfoString(buf, "USER");
10630 : 8 : break;
10631 : 20 : case SVFOP_SESSION_USER:
10632 : 20 : appendStringInfoString(buf, "SESSION_USER");
10633 : 20 : break;
10634 : 8 : case SVFOP_CURRENT_CATALOG:
10635 : 8 : appendStringInfoString(buf, "CURRENT_CATALOG");
10636 : 8 : break;
10637 : 8 : case SVFOP_CURRENT_SCHEMA:
10638 : 8 : appendStringInfoString(buf, "CURRENT_SCHEMA");
10639 : 8 : break;
10640 : : }
10641 : : }
10642 : 449 : break;
10643 : :
10644 : 99 : case T_XmlExpr:
10645 : : {
10646 : 99 : XmlExpr *xexpr = (XmlExpr *) node;
10647 : 99 : bool needcomma = false;
10648 : : ListCell *arg;
10649 : : ListCell *narg;
10650 : : Const *con;
10651 : :
10652 [ + + + + : 99 : switch (xexpr->op)
+ + + -
- ]
10653 : : {
10654 : 9 : case IS_XMLCONCAT:
10655 : 9 : appendStringInfoString(buf, "XMLCONCAT(");
10656 : 9 : break;
10657 : 18 : case IS_XMLELEMENT:
10658 : 18 : appendStringInfoString(buf, "XMLELEMENT(");
10659 : 18 : break;
10660 : 9 : case IS_XMLFOREST:
10661 : 9 : appendStringInfoString(buf, "XMLFOREST(");
10662 : 9 : break;
10663 : 9 : case IS_XMLPARSE:
10664 : 9 : appendStringInfoString(buf, "XMLPARSE(");
10665 : 9 : break;
10666 : 9 : case IS_XMLPI:
10667 : 9 : appendStringInfoString(buf, "XMLPI(");
10668 : 9 : break;
10669 : 9 : case IS_XMLROOT:
10670 : 9 : appendStringInfoString(buf, "XMLROOT(");
10671 : 9 : break;
10672 : 36 : case IS_XMLSERIALIZE:
10673 : 36 : appendStringInfoString(buf, "XMLSERIALIZE(");
10674 : 36 : break;
10675 : 0 : case IS_DOCUMENT:
10676 : 0 : break;
10677 : : }
10678 [ + + + + ]: 99 : if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
10679 : : {
10680 [ + + ]: 45 : if (xexpr->xmloption == XMLOPTION_DOCUMENT)
10681 : 18 : appendStringInfoString(buf, "DOCUMENT ");
10682 : : else
10683 : 27 : appendStringInfoString(buf, "CONTENT ");
10684 : : }
10685 [ + + ]: 99 : if (xexpr->name)
10686 : : {
10687 : 27 : appendStringInfo(buf, "NAME %s",
10688 : 27 : quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));
10689 : 27 : needcomma = true;
10690 : : }
10691 [ + + ]: 99 : if (xexpr->named_args)
10692 : : {
10693 [ + + ]: 18 : if (xexpr->op != IS_XMLFOREST)
10694 : : {
10695 [ + - ]: 9 : if (needcomma)
10696 : 9 : appendStringInfoString(buf, ", ");
10697 : 9 : appendStringInfoString(buf, "XMLATTRIBUTES(");
10698 : 9 : needcomma = false;
10699 : : }
10700 [ + - + + : 63 : forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
+ - + + +
+ + - +
+ ]
10701 : : {
10702 : 45 : Node *e = (Node *) lfirst(arg);
10703 : 45 : char *argname = strVal(lfirst(narg));
10704 : :
10705 [ + + ]: 45 : if (needcomma)
10706 : 27 : appendStringInfoString(buf, ", ");
10707 : 45 : get_rule_expr(e, context, true);
10708 : 45 : appendStringInfo(buf, " AS %s",
10709 : 45 : quote_identifier(map_xml_name_to_sql_identifier(argname)));
10710 : 45 : needcomma = true;
10711 : : }
10712 [ + + ]: 18 : if (xexpr->op != IS_XMLFOREST)
10713 : 9 : appendStringInfoChar(buf, ')');
10714 : : }
10715 [ + + ]: 99 : if (xexpr->args)
10716 : : {
10717 [ + + ]: 90 : if (needcomma)
10718 : 27 : appendStringInfoString(buf, ", ");
10719 [ + + + - : 90 : switch (xexpr->op)
- ]
10720 : : {
10721 : 72 : case IS_XMLCONCAT:
10722 : : case IS_XMLELEMENT:
10723 : : case IS_XMLFOREST:
10724 : : case IS_XMLPI:
10725 : : case IS_XMLSERIALIZE:
10726 : : /* no extra decoration needed */
10727 : 72 : get_rule_expr((Node *) xexpr->args, context, true);
10728 : 72 : break;
10729 : 9 : case IS_XMLPARSE:
10730 : : Assert(list_length(xexpr->args) == 2);
10731 : :
10732 : 9 : get_rule_expr((Node *) linitial(xexpr->args),
10733 : : context, true);
10734 : :
10735 : 9 : con = lsecond_node(Const, xexpr->args);
10736 : : Assert(!con->constisnull);
10737 [ - + ]: 9 : if (DatumGetBool(con->constvalue))
10738 : 0 : appendStringInfoString(buf,
10739 : : " PRESERVE WHITESPACE");
10740 : : else
10741 : 9 : appendStringInfoString(buf,
10742 : : " STRIP WHITESPACE");
10743 : 9 : break;
10744 : 9 : case IS_XMLROOT:
10745 : : Assert(list_length(xexpr->args) == 3);
10746 : :
10747 : 9 : get_rule_expr((Node *) linitial(xexpr->args),
10748 : : context, true);
10749 : :
10750 : 9 : appendStringInfoString(buf, ", VERSION ");
10751 : 9 : con = (Const *) lsecond(xexpr->args);
10752 [ + - ]: 9 : if (IsA(con, Const) &&
10753 [ + - ]: 9 : con->constisnull)
10754 : 9 : appendStringInfoString(buf, "NO VALUE");
10755 : : else
10756 : 0 : get_rule_expr((Node *) con, context, false);
10757 : :
10758 : 9 : con = lthird_node(Const, xexpr->args);
10759 [ + - ]: 9 : if (con->constisnull)
10760 : : /* suppress STANDALONE NO VALUE */ ;
10761 : : else
10762 : : {
10763 [ + - - - ]: 9 : switch (DatumGetInt32(con->constvalue))
10764 : : {
10765 : 9 : case XML_STANDALONE_YES:
10766 : 9 : appendStringInfoString(buf,
10767 : : ", STANDALONE YES");
10768 : 9 : break;
10769 : 0 : case XML_STANDALONE_NO:
10770 : 0 : appendStringInfoString(buf,
10771 : : ", STANDALONE NO");
10772 : 0 : break;
10773 : 0 : case XML_STANDALONE_NO_VALUE:
10774 : 0 : appendStringInfoString(buf,
10775 : : ", STANDALONE NO VALUE");
10776 : 0 : break;
10777 : 0 : default:
10778 : 0 : break;
10779 : : }
10780 : : }
10781 : 9 : break;
10782 : 0 : case IS_DOCUMENT:
10783 : 0 : get_rule_expr_paren((Node *) xexpr->args, context, false, node);
10784 : 0 : break;
10785 : : }
10786 : : }
10787 [ + + ]: 99 : if (xexpr->op == IS_XMLSERIALIZE)
10788 : : {
10789 : 36 : appendStringInfo(buf, " AS %s",
10790 : : format_type_with_typemod(xexpr->type,
10791 : : xexpr->typmod));
10792 [ + + ]: 36 : if (xexpr->indent)
10793 : 9 : appendStringInfoString(buf, " INDENT");
10794 : : else
10795 : 27 : appendStringInfoString(buf, " NO INDENT");
10796 : : }
10797 : :
10798 [ - + ]: 99 : if (xexpr->op == IS_DOCUMENT)
10799 : 0 : appendStringInfoString(buf, " IS DOCUMENT");
10800 : : else
10801 : 99 : appendStringInfoChar(buf, ')');
10802 : : }
10803 : 99 : break;
10804 : :
10805 : 1862 : case T_NullTest:
10806 : : {
10807 : 1862 : NullTest *ntest = (NullTest *) node;
10808 : :
10809 [ + + ]: 1862 : if (!PRETTY_PAREN(context))
10810 : 1822 : appendStringInfoChar(buf, '(');
10811 : 1862 : get_rule_expr_paren((Node *) ntest->arg, context, true, node);
10812 : :
10813 : : /*
10814 : : * For scalar inputs, we prefer to print as IS [NOT] NULL,
10815 : : * which is shorter and traditional. If it's a rowtype input
10816 : : * but we're applying a scalar test, must print IS [NOT]
10817 : : * DISTINCT FROM NULL to be semantically correct.
10818 : : */
10819 [ + + ]: 1862 : if (ntest->argisrow ||
10820 [ + + ]: 1812 : !type_is_rowtype(exprType((Node *) ntest->arg)))
10821 : : {
10822 [ + + - ]: 1842 : switch (ntest->nulltesttype)
10823 : : {
10824 : 580 : case IS_NULL:
10825 : 580 : appendStringInfoString(buf, " IS NULL");
10826 : 580 : break;
10827 : 1262 : case IS_NOT_NULL:
10828 : 1262 : appendStringInfoString(buf, " IS NOT NULL");
10829 : 1262 : break;
10830 : 0 : default:
10831 [ # # ]: 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10832 : : (int) ntest->nulltesttype);
10833 : : }
10834 : : }
10835 : : else
10836 : : {
10837 [ + + - ]: 20 : switch (ntest->nulltesttype)
10838 : : {
10839 : 8 : case IS_NULL:
10840 : 8 : appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
10841 : 8 : break;
10842 : 12 : case IS_NOT_NULL:
10843 : 12 : appendStringInfoString(buf, " IS DISTINCT FROM NULL");
10844 : 12 : break;
10845 : 0 : default:
10846 [ # # ]: 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10847 : : (int) ntest->nulltesttype);
10848 : : }
10849 : : }
10850 [ + + ]: 1862 : if (!PRETTY_PAREN(context))
10851 : 1822 : appendStringInfoChar(buf, ')');
10852 : : }
10853 : 1862 : break;
10854 : :
10855 : 212 : case T_BooleanTest:
10856 : : {
10857 : 212 : BooleanTest *btest = (BooleanTest *) node;
10858 : :
10859 [ + - ]: 212 : if (!PRETTY_PAREN(context))
10860 : 212 : appendStringInfoChar(buf, '(');
10861 : 212 : get_rule_expr_paren((Node *) btest->arg, context, false, node);
10862 [ + + - + : 212 : switch (btest->booltesttype)
+ + - ]
10863 : : {
10864 : 28 : case IS_TRUE:
10865 : 28 : appendStringInfoString(buf, " IS TRUE");
10866 : 28 : break;
10867 : 92 : case IS_NOT_TRUE:
10868 : 92 : appendStringInfoString(buf, " IS NOT TRUE");
10869 : 92 : break;
10870 : 0 : case IS_FALSE:
10871 : 0 : appendStringInfoString(buf, " IS FALSE");
10872 : 0 : break;
10873 : 36 : case IS_NOT_FALSE:
10874 : 36 : appendStringInfoString(buf, " IS NOT FALSE");
10875 : 36 : break;
10876 : 20 : case IS_UNKNOWN:
10877 : 20 : appendStringInfoString(buf, " IS UNKNOWN");
10878 : 20 : break;
10879 : 36 : case IS_NOT_UNKNOWN:
10880 : 36 : appendStringInfoString(buf, " IS NOT UNKNOWN");
10881 : 36 : break;
10882 : 0 : default:
10883 [ # # ]: 0 : elog(ERROR, "unrecognized booltesttype: %d",
10884 : : (int) btest->booltesttype);
10885 : : }
10886 [ + - ]: 212 : if (!PRETTY_PAREN(context))
10887 : 212 : appendStringInfoChar(buf, ')');
10888 : : }
10889 : 212 : break;
10890 : :
10891 : 79 : case T_CoerceToDomain:
10892 : : {
10893 : 79 : CoerceToDomain *ctest = (CoerceToDomain *) node;
10894 : 79 : Node *arg = (Node *) ctest->arg;
10895 : :
10896 [ + + ]: 79 : if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
10897 [ + + ]: 36 : !showimplicit)
10898 : : {
10899 : : /* don't show the implicit cast */
10900 : 28 : get_rule_expr(arg, context, false);
10901 : : }
10902 : : else
10903 : : {
10904 : 51 : get_coercion_expr(arg, context,
10905 : : ctest->resulttype,
10906 : : ctest->resulttypmod,
10907 : : node);
10908 : : }
10909 : : }
10910 : 79 : break;
10911 : :
10912 : 284 : case T_CoerceToDomainValue:
10913 : 284 : appendStringInfoString(buf, "VALUE");
10914 : 284 : break;
10915 : :
10916 : 44 : case T_SetToDefault:
10917 : 44 : appendStringInfoString(buf, "DEFAULT");
10918 : 44 : break;
10919 : :
10920 : 16 : case T_CurrentOfExpr:
10921 : : {
10922 : 16 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
10923 : :
10924 [ + - ]: 16 : if (cexpr->cursor_name)
10925 : 16 : appendStringInfo(buf, "CURRENT OF %s",
10926 : 16 : quote_identifier(cexpr->cursor_name));
10927 : : else
10928 : 0 : appendStringInfo(buf, "CURRENT OF $%d",
10929 : : cexpr->cursor_param);
10930 : : }
10931 : 16 : break;
10932 : :
10933 : 0 : case T_NextValueExpr:
10934 : : {
10935 : 0 : NextValueExpr *nvexpr = (NextValueExpr *) node;
10936 : :
10937 : : /*
10938 : : * This isn't exactly nextval(), but that seems close enough
10939 : : * for EXPLAIN's purposes.
10940 : : */
10941 : 0 : appendStringInfoString(buf, "nextval(");
10942 : 0 : simple_quote_literal(buf,
10943 : 0 : generate_relation_name(nvexpr->seqid,
10944 : : NIL));
10945 : 0 : appendStringInfoChar(buf, ')');
10946 : : }
10947 : 0 : break;
10948 : :
10949 : 20 : case T_InferenceElem:
10950 : : {
10951 : 20 : InferenceElem *iexpr = (InferenceElem *) node;
10952 : : bool save_varprefix;
10953 : : bool need_parens;
10954 : :
10955 : : /*
10956 : : * InferenceElem can only refer to target relation, so a
10957 : : * prefix is not useful, and indeed would cause parse errors.
10958 : : */
10959 : 20 : save_varprefix = context->varprefix;
10960 : 20 : context->varprefix = false;
10961 : :
10962 : : /*
10963 : : * Parenthesize the element unless it's a simple Var or a bare
10964 : : * function call. Follows pg_get_indexdef_worker().
10965 : : */
10966 : 20 : need_parens = !IsA(iexpr->expr, Var);
10967 [ - + ]: 20 : if (IsA(iexpr->expr, FuncExpr) &&
10968 [ # # ]: 0 : ((FuncExpr *) iexpr->expr)->funcformat ==
10969 : : COERCE_EXPLICIT_CALL)
10970 : 0 : need_parens = false;
10971 : :
10972 [ - + ]: 20 : if (need_parens)
10973 : 0 : appendStringInfoChar(buf, '(');
10974 : 20 : get_rule_expr((Node *) iexpr->expr,
10975 : : context, false);
10976 [ - + ]: 20 : if (need_parens)
10977 : 0 : appendStringInfoChar(buf, ')');
10978 : :
10979 : 20 : context->varprefix = save_varprefix;
10980 : :
10981 [ + + ]: 20 : if (iexpr->infercollid)
10982 : 8 : appendStringInfo(buf, " COLLATE %s",
10983 : : generate_collation_name(iexpr->infercollid));
10984 : :
10985 : : /* Add the operator class name, if not default */
10986 [ + + ]: 20 : if (iexpr->inferopclass)
10987 : : {
10988 : 8 : Oid inferopclass = iexpr->inferopclass;
10989 : 8 : Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
10990 : :
10991 : 8 : get_opclass_name(inferopclass, inferopcinputtype, buf);
10992 : : }
10993 : : }
10994 : 20 : break;
10995 : :
10996 : 8 : case T_ReturningExpr:
10997 : : {
10998 : 8 : ReturningExpr *retExpr = (ReturningExpr *) node;
10999 : :
11000 : : /*
11001 : : * We cannot see a ReturningExpr in rule deparsing, only while
11002 : : * EXPLAINing a query plan (ReturningExpr nodes are only ever
11003 : : * adding during query rewriting). Just display the expression
11004 : : * returned (an expanded view column).
11005 : : */
11006 : 8 : get_rule_expr((Node *) retExpr->retexpr, context, showimplicit);
11007 : : }
11008 : 8 : break;
11009 : :
11010 : 2793 : case T_PartitionBoundSpec:
11011 : : {
11012 : 2793 : PartitionBoundSpec *spec = (PartitionBoundSpec *) node;
11013 : : ListCell *cell;
11014 : : char *sep;
11015 : :
11016 [ + + ]: 2793 : if (spec->is_default)
11017 : : {
11018 : 161 : appendStringInfoString(buf, "DEFAULT");
11019 : 161 : break;
11020 : : }
11021 : :
11022 [ + + + - ]: 2632 : switch (spec->strategy)
11023 : : {
11024 : 165 : case PARTITION_STRATEGY_HASH:
11025 : : Assert(spec->modulus > 0 && spec->remainder >= 0);
11026 : : Assert(spec->modulus > spec->remainder);
11027 : :
11028 : 165 : appendStringInfoString(buf, "FOR VALUES");
11029 : 165 : appendStringInfo(buf, " WITH (modulus %d, remainder %d)",
11030 : : spec->modulus, spec->remainder);
11031 : 165 : break;
11032 : :
11033 : 898 : case PARTITION_STRATEGY_LIST:
11034 : : Assert(spec->listdatums != NIL);
11035 : :
11036 : 898 : appendStringInfoString(buf, "FOR VALUES IN (");
11037 : 898 : sep = "";
11038 [ + - + + : 2424 : foreach(cell, spec->listdatums)
+ + ]
11039 : : {
11040 : 1526 : Const *val = lfirst_node(Const, cell);
11041 : :
11042 : 1526 : appendStringInfoString(buf, sep);
11043 : 1526 : get_const_expr(val, context, -1);
11044 : 1526 : sep = ", ";
11045 : : }
11046 : :
11047 : 898 : appendStringInfoChar(buf, ')');
11048 : 898 : break;
11049 : :
11050 : 1569 : case PARTITION_STRATEGY_RANGE:
11051 : : Assert(spec->lowerdatums != NIL &&
11052 : : spec->upperdatums != NIL &&
11053 : : list_length(spec->lowerdatums) ==
11054 : : list_length(spec->upperdatums));
11055 : :
11056 : 1569 : appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
11057 : : get_range_partbound_string(spec->lowerdatums),
11058 : : get_range_partbound_string(spec->upperdatums));
11059 : 1569 : break;
11060 : :
11061 : 0 : default:
11062 [ # # ]: 0 : elog(ERROR, "unrecognized partition strategy: %d",
11063 : : (int) spec->strategy);
11064 : : break;
11065 : : }
11066 : : }
11067 : 2632 : break;
11068 : :
11069 : 104 : case T_JsonValueExpr:
11070 : : {
11071 : 104 : JsonValueExpr *jve = (JsonValueExpr *) node;
11072 : :
11073 : 104 : get_rule_expr((Node *) jve->raw_expr, context, false);
11074 : 104 : get_json_format(jve->format, context->buf);
11075 : : }
11076 : 104 : break;
11077 : :
11078 : 180 : case T_JsonConstructorExpr:
11079 : 180 : get_json_constructor((JsonConstructorExpr *) node, context, false);
11080 : 180 : break;
11081 : :
11082 : 52 : case T_JsonIsPredicate:
11083 : : {
11084 : 52 : JsonIsPredicate *pred = (JsonIsPredicate *) node;
11085 : :
11086 [ + + ]: 52 : if (!PRETTY_PAREN(context))
11087 : 20 : appendStringInfoChar(context->buf, '(');
11088 : :
11089 : 52 : get_rule_expr_paren(pred->expr, context, true, node);
11090 : :
11091 : 52 : appendStringInfoString(context->buf, " IS JSON");
11092 : :
11093 : : /* TODO: handle FORMAT clause */
11094 : :
11095 [ + + + + ]: 52 : switch (pred->item_type)
11096 : : {
11097 : 8 : case JS_TYPE_SCALAR:
11098 : 8 : appendStringInfoString(context->buf, " SCALAR");
11099 : 8 : break;
11100 : 8 : case JS_TYPE_ARRAY:
11101 : 8 : appendStringInfoString(context->buf, " ARRAY");
11102 : 8 : break;
11103 : 8 : case JS_TYPE_OBJECT:
11104 : 8 : appendStringInfoString(context->buf, " OBJECT");
11105 : 8 : break;
11106 : 28 : default:
11107 : 28 : break;
11108 : : }
11109 : :
11110 [ + + ]: 52 : if (pred->unique_keys)
11111 : 20 : appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
11112 : :
11113 [ + + ]: 52 : if (!PRETTY_PAREN(context))
11114 : 20 : appendStringInfoChar(context->buf, ')');
11115 : : }
11116 : 52 : break;
11117 : :
11118 : 40 : case T_JsonExpr:
11119 : : {
11120 : 40 : JsonExpr *jexpr = (JsonExpr *) node;
11121 : :
11122 [ + + + - ]: 40 : switch (jexpr->op)
11123 : : {
11124 : 8 : case JSON_EXISTS_OP:
11125 : 8 : appendStringInfoString(buf, "JSON_EXISTS(");
11126 : 8 : break;
11127 : 24 : case JSON_QUERY_OP:
11128 : 24 : appendStringInfoString(buf, "JSON_QUERY(");
11129 : 24 : break;
11130 : 8 : case JSON_VALUE_OP:
11131 : 8 : appendStringInfoString(buf, "JSON_VALUE(");
11132 : 8 : break;
11133 : 0 : default:
11134 [ # # ]: 0 : elog(ERROR, "unrecognized JsonExpr op: %d",
11135 : : (int) jexpr->op);
11136 : : }
11137 : :
11138 : 40 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
11139 : :
11140 : 40 : appendStringInfoString(buf, ", ");
11141 : :
11142 : 40 : get_json_path_spec(jexpr->path_spec, context, showimplicit);
11143 : :
11144 [ + + ]: 40 : if (jexpr->passing_values)
11145 : : {
11146 : : ListCell *lc1,
11147 : : *lc2;
11148 : 8 : bool needcomma = false;
11149 : :
11150 : 8 : appendStringInfoString(buf, " PASSING ");
11151 : :
11152 [ + - + + : 32 : forboth(lc1, jexpr->passing_names,
+ - + + +
+ + - +
+ ]
11153 : : lc2, jexpr->passing_values)
11154 : : {
11155 [ + + ]: 24 : if (needcomma)
11156 : 16 : appendStringInfoString(buf, ", ");
11157 : 24 : needcomma = true;
11158 : :
11159 : 24 : get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
11160 : 24 : appendStringInfo(buf, " AS %s",
11161 : 24 : quote_identifier(lfirst_node(String, lc1)->sval));
11162 : : }
11163 : : }
11164 : :
11165 [ + + ]: 40 : if (jexpr->op != JSON_EXISTS_OP ||
11166 [ - + ]: 8 : jexpr->returning->typid != BOOLOID)
11167 : 32 : get_json_returning(jexpr->returning, context->buf,
11168 : 32 : jexpr->op == JSON_QUERY_OP);
11169 : :
11170 : 40 : get_json_expr_options(jexpr, context,
11171 [ + + ]: 40 : jexpr->op != JSON_EXISTS_OP ?
11172 : : JSON_BEHAVIOR_NULL :
11173 : : JSON_BEHAVIOR_FALSE);
11174 : :
11175 : 40 : appendStringInfoChar(buf, ')');
11176 : : }
11177 : 40 : break;
11178 : :
11179 : 1874 : case T_List:
11180 : : {
11181 : : char *sep;
11182 : : ListCell *l;
11183 : :
11184 : 1874 : sep = "";
11185 [ + - + + : 5296 : foreach(l, (List *) node)
+ + ]
11186 : : {
11187 : 3422 : appendStringInfoString(buf, sep);
11188 : 3422 : get_rule_expr((Node *) lfirst(l), context, showimplicit);
11189 : 3422 : sep = ", ";
11190 : : }
11191 : : }
11192 : 1874 : break;
11193 : :
11194 : 52 : case T_TableFunc:
11195 : 52 : get_tablefunc((TableFunc *) node, context, showimplicit);
11196 : 52 : break;
11197 : :
11198 : 64 : case T_GraphPropertyRef:
11199 : : {
11200 : 64 : GraphPropertyRef *gpr = (GraphPropertyRef *) node;
11201 : :
11202 : 64 : appendStringInfo(buf, "%s.%s", quote_identifier(gpr->elvarname), quote_identifier(get_propgraph_property_name(gpr->propid)));
11203 : 64 : break;
11204 : : }
11205 : :
11206 : 0 : default:
11207 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
11208 : : break;
11209 : : }
11210 : : }
11211 : :
11212 : : /*
11213 : : * get_rule_expr_toplevel - Parse back a toplevel expression
11214 : : *
11215 : : * Same as get_rule_expr(), except that if the expr is just a Var, we pass
11216 : : * istoplevel = true not false to get_variable(). This causes whole-row Vars
11217 : : * to get printed with decoration that will prevent expansion of "*".
11218 : : * We need to use this in contexts such as ROW() and VALUES(), where the
11219 : : * parser would expand "foo.*" appearing at top level. (In principle we'd
11220 : : * use this in get_target_list() too, but that has additional worries about
11221 : : * whether to print AS, so it needs to invoke get_variable() directly anyway.)
11222 : : */
11223 : : static void
11224 : 2195 : get_rule_expr_toplevel(Node *node, deparse_context *context,
11225 : : bool showimplicit)
11226 : : {
11227 [ + - + + ]: 2195 : if (node && IsA(node, Var))
11228 : 830 : (void) get_variable((Var *) node, 0, true, context);
11229 : : else
11230 : 1365 : get_rule_expr(node, context, showimplicit);
11231 : 2195 : }
11232 : :
11233 : : /*
11234 : : * get_rule_list_toplevel - Parse back a list of toplevel expressions
11235 : : *
11236 : : * Apply get_rule_expr_toplevel() to each element of a List.
11237 : : *
11238 : : * This adds commas between the expressions, but caller is responsible
11239 : : * for printing surrounding decoration.
11240 : : */
11241 : : static void
11242 : 358 : get_rule_list_toplevel(List *lst, deparse_context *context,
11243 : : bool showimplicit)
11244 : : {
11245 : : const char *sep;
11246 : : ListCell *lc;
11247 : :
11248 : 358 : sep = "";
11249 [ + - + + : 1183 : foreach(lc, lst)
+ + ]
11250 : : {
11251 : 825 : Node *e = (Node *) lfirst(lc);
11252 : :
11253 : 825 : appendStringInfoString(context->buf, sep);
11254 : 825 : get_rule_expr_toplevel(e, context, showimplicit);
11255 : 825 : sep = ", ";
11256 : : }
11257 : 358 : }
11258 : :
11259 : : /*
11260 : : * get_rule_expr_funccall - Parse back a function-call expression
11261 : : *
11262 : : * Same as get_rule_expr(), except that we guarantee that the output will
11263 : : * look like a function call, or like one of the things the grammar treats as
11264 : : * equivalent to a function call (see the func_expr_windowless production).
11265 : : * This is needed in places where the grammar uses func_expr_windowless and
11266 : : * you can't substitute a parenthesized a_expr. If what we have isn't going
11267 : : * to look like a function call, wrap it in a dummy CAST() expression, which
11268 : : * will satisfy the grammar --- and, indeed, is likely what the user wrote to
11269 : : * produce such a thing.
11270 : : */
11271 : : static void
11272 : 572 : get_rule_expr_funccall(Node *node, deparse_context *context,
11273 : : bool showimplicit)
11274 : : {
11275 [ + + ]: 572 : if (looks_like_function(node))
11276 : 564 : get_rule_expr(node, context, showimplicit);
11277 : : else
11278 : : {
11279 : 8 : StringInfo buf = context->buf;
11280 : :
11281 : 8 : appendStringInfoString(buf, "CAST(");
11282 : : /* no point in showing any top-level implicit cast */
11283 : 8 : get_rule_expr(node, context, false);
11284 : 8 : appendStringInfo(buf, " AS %s)",
11285 : : format_type_with_typemod(exprType(node),
11286 : : exprTypmod(node)));
11287 : : }
11288 : 572 : }
11289 : :
11290 : : /*
11291 : : * Helper function to identify node types that satisfy func_expr_windowless.
11292 : : * If in doubt, "false" is always a safe answer.
11293 : : */
11294 : : static bool
11295 : 1348 : looks_like_function(Node *node)
11296 : : {
11297 [ - + ]: 1348 : if (node == NULL)
11298 : 0 : return false; /* probably shouldn't happen */
11299 [ + + + ]: 1348 : switch (nodeTag(node))
11300 : : {
11301 : 584 : case T_FuncExpr:
11302 : : /* OK, unless it's going to deparse as a cast */
11303 [ + + ]: 596 : return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
11304 [ + + ]: 12 : ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
11305 : 72 : case T_NullIfExpr:
11306 : : case T_CoalesceExpr:
11307 : : case T_MinMaxExpr:
11308 : : case T_SQLValueFunction:
11309 : : case T_XmlExpr:
11310 : : case T_JsonExpr:
11311 : : /* these are all accepted by func_expr_common_subexpr */
11312 : 72 : return true;
11313 : 692 : default:
11314 : 692 : break;
11315 : : }
11316 : 692 : return false;
11317 : : }
11318 : :
11319 : :
11320 : : /*
11321 : : * get_oper_expr - Parse back an OpExpr node
11322 : : */
11323 : : static void
11324 : 41520 : get_oper_expr(OpExpr *expr, deparse_context *context)
11325 : : {
11326 : 41520 : StringInfo buf = context->buf;
11327 : 41520 : Oid opno = expr->opno;
11328 : 41520 : List *args = expr->args;
11329 : :
11330 [ + + ]: 41520 : if (!PRETTY_PAREN(context))
11331 : 39896 : appendStringInfoChar(buf, '(');
11332 [ + + ]: 41520 : if (list_length(args) == 2)
11333 : : {
11334 : : /* binary operator */
11335 : 41500 : Node *arg1 = (Node *) linitial(args);
11336 : 41500 : Node *arg2 = (Node *) lsecond(args);
11337 : :
11338 : 41500 : get_rule_expr_paren(arg1, context, true, (Node *) expr);
11339 : 41500 : appendStringInfo(buf, " %s ",
11340 : : generate_operator_name(opno,
11341 : : exprType(arg1),
11342 : : exprType(arg2)));
11343 : 41500 : get_rule_expr_paren(arg2, context, true, (Node *) expr);
11344 : : }
11345 : : else
11346 : : {
11347 : : /* prefix operator */
11348 : 20 : Node *arg = (Node *) linitial(args);
11349 : :
11350 : 20 : appendStringInfo(buf, "%s ",
11351 : : generate_operator_name(opno,
11352 : : InvalidOid,
11353 : : exprType(arg)));
11354 : 20 : get_rule_expr_paren(arg, context, true, (Node *) expr);
11355 : : }
11356 [ + + ]: 41520 : if (!PRETTY_PAREN(context))
11357 : 39896 : appendStringInfoChar(buf, ')');
11358 : 41520 : }
11359 : :
11360 : : /*
11361 : : * get_func_expr - Parse back a FuncExpr node
11362 : : */
11363 : : static void
11364 : 8237 : get_func_expr(FuncExpr *expr, deparse_context *context,
11365 : : bool showimplicit)
11366 : : {
11367 : 8237 : StringInfo buf = context->buf;
11368 : 8237 : Oid funcoid = expr->funcid;
11369 : : Oid argtypes[FUNC_MAX_ARGS];
11370 : : int nargs;
11371 : : List *argnames;
11372 : : bool use_variadic;
11373 : : ListCell *l;
11374 : :
11375 : : /*
11376 : : * If the function call came from an implicit coercion, then just show the
11377 : : * first argument --- unless caller wants to see implicit coercions.
11378 : : */
11379 [ + + + + ]: 8237 : if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
11380 : : {
11381 : 789 : get_rule_expr_paren((Node *) linitial(expr->args), context,
11382 : : false, (Node *) expr);
11383 : 2055 : return;
11384 : : }
11385 : :
11386 : : /*
11387 : : * If the function call came from a cast, then show the first argument
11388 : : * plus an explicit cast operation.
11389 : : */
11390 [ + + ]: 7448 : if (expr->funcformat == COERCE_EXPLICIT_CAST ||
11391 [ + + ]: 6989 : expr->funcformat == COERCE_IMPLICIT_CAST)
11392 : : {
11393 : 1150 : Node *arg = linitial(expr->args);
11394 : 1150 : Oid rettype = expr->funcresulttype;
11395 : : int32 coercedTypmod;
11396 : :
11397 : : /* Get the typmod if this is a length-coercion function */
11398 : 1150 : (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
11399 : :
11400 : 1150 : get_coercion_expr(arg, context,
11401 : : rettype, coercedTypmod,
11402 : : (Node *) expr);
11403 : :
11404 : 1150 : return;
11405 : : }
11406 : :
11407 : : /*
11408 : : * If the function was called using one of the SQL spec's random special
11409 : : * syntaxes, try to reproduce that. If we don't recognize the function,
11410 : : * fall through.
11411 : : */
11412 [ + + ]: 6298 : if (expr->funcformat == COERCE_SQL_SYNTAX)
11413 : : {
11414 [ + + ]: 120 : if (get_func_sql_syntax(expr, context))
11415 : 116 : return;
11416 : : }
11417 : :
11418 : : /*
11419 : : * Normal function: display as proname(args). First we need to extract
11420 : : * the argument datatypes.
11421 : : */
11422 [ - + ]: 6182 : if (list_length(expr->args) > FUNC_MAX_ARGS)
11423 [ # # ]: 0 : ereport(ERROR,
11424 : : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
11425 : : errmsg("too many arguments")));
11426 : 6182 : nargs = 0;
11427 : 6182 : argnames = NIL;
11428 [ + + + + : 12948 : foreach(l, expr->args)
+ + ]
11429 : : {
11430 : 6766 : Node *arg = (Node *) lfirst(l);
11431 : :
11432 [ + + ]: 6766 : if (IsA(arg, NamedArgExpr))
11433 : 20 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
11434 : 6766 : argtypes[nargs] = exprType(arg);
11435 : 6766 : nargs++;
11436 : : }
11437 : :
11438 : 6182 : appendStringInfo(buf, "%s(",
11439 : : generate_function_name(funcoid, nargs,
11440 : : argnames, argtypes,
11441 : 6182 : expr->funcvariadic,
11442 : : &use_variadic,
11443 : 6182 : context->inGroupBy));
11444 : 6182 : nargs = 0;
11445 [ + + + + : 12948 : foreach(l, expr->args)
+ + ]
11446 : : {
11447 [ + + ]: 6766 : if (nargs++ > 0)
11448 : 1342 : appendStringInfoString(buf, ", ");
11449 [ + + + - ]: 6766 : if (use_variadic && lnext(expr->args, l) == NULL)
11450 : 7 : appendStringInfoString(buf, "VARIADIC ");
11451 : 6766 : get_rule_expr((Node *) lfirst(l), context, true);
11452 : : }
11453 : 6182 : appendStringInfoChar(buf, ')');
11454 : : }
11455 : :
11456 : : /*
11457 : : * get_agg_expr - Parse back an Aggref node
11458 : : */
11459 : : static void
11460 : 3164 : get_agg_expr(Aggref *aggref, deparse_context *context,
11461 : : Aggref *original_aggref)
11462 : : {
11463 : 3164 : get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
11464 : : false);
11465 : 3164 : }
11466 : :
11467 : : /*
11468 : : * get_agg_expr_helper - subroutine for get_agg_expr and
11469 : : * get_json_agg_constructor
11470 : : */
11471 : : static void
11472 : 3232 : get_agg_expr_helper(Aggref *aggref, deparse_context *context,
11473 : : Aggref *original_aggref, const char *funcname,
11474 : : const char *options, bool is_json_objectagg)
11475 : : {
11476 : 3232 : StringInfo buf = context->buf;
11477 : : Oid argtypes[FUNC_MAX_ARGS];
11478 : : int nargs;
11479 : 3232 : bool use_variadic = false;
11480 : :
11481 : : /*
11482 : : * For a combining aggregate, we look up and deparse the corresponding
11483 : : * partial aggregate instead. This is necessary because our input
11484 : : * argument list has been replaced; the new argument list always has just
11485 : : * one element, which will point to a partial Aggref that supplies us with
11486 : : * transition states to combine.
11487 : : */
11488 [ + + ]: 3232 : if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
11489 : : {
11490 : : TargetEntry *tle;
11491 : :
11492 : : Assert(list_length(aggref->args) == 1);
11493 : 524 : tle = linitial_node(TargetEntry, aggref->args);
11494 : 524 : resolve_special_varno((Node *) tle->expr, context,
11495 : : get_agg_combine_expr, original_aggref);
11496 : 524 : return;
11497 : : }
11498 : :
11499 : : /*
11500 : : * Mark as PARTIAL, if appropriate. We look to the original aggref so as
11501 : : * to avoid printing this when recursing from the code just above.
11502 : : */
11503 [ + + ]: 2708 : if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))
11504 : 1164 : appendStringInfoString(buf, "PARTIAL ");
11505 : :
11506 : : /* Extract the argument types as seen by the parser */
11507 : 2708 : nargs = get_aggregate_argtypes(aggref, argtypes);
11508 : :
11509 [ + + ]: 2708 : if (!funcname)
11510 : 2640 : funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
11511 : 2640 : argtypes, aggref->aggvariadic,
11512 : : &use_variadic,
11513 : 2640 : context->inGroupBy);
11514 : :
11515 : : /* Print the aggregate name, schema-qualified if needed */
11516 : 2708 : appendStringInfo(buf, "%s(%s", funcname,
11517 [ + + ]: 2708 : (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
11518 : :
11519 [ + + ]: 2708 : if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
11520 : : {
11521 : : /*
11522 : : * Ordered-set aggregates do not use "*" syntax. Also, we needn't
11523 : : * worry about inserting VARIADIC. So we can just dump the direct
11524 : : * args as-is.
11525 : : */
11526 : : Assert(!aggref->aggvariadic);
11527 : 17 : get_rule_expr((Node *) aggref->aggdirectargs, context, true);
11528 : : Assert(aggref->aggorder != NIL);
11529 : 17 : appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
11530 : 17 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
11531 : : }
11532 : : else
11533 : : {
11534 : : /* aggstar can be set only in zero-argument aggregates */
11535 [ + + ]: 2691 : if (aggref->aggstar)
11536 : 817 : appendStringInfoChar(buf, '*');
11537 : : else
11538 : : {
11539 : : ListCell *l;
11540 : : int i;
11541 : :
11542 : 1874 : i = 0;
11543 [ + - + + : 3887 : foreach(l, aggref->args)
+ + ]
11544 : : {
11545 : 2013 : TargetEntry *tle = (TargetEntry *) lfirst(l);
11546 : 2013 : Node *arg = (Node *) tle->expr;
11547 : :
11548 : : Assert(!IsA(arg, NamedArgExpr));
11549 [ + + ]: 2013 : if (tle->resjunk)
11550 : 31 : continue;
11551 [ + + ]: 1982 : if (i++ > 0)
11552 : : {
11553 [ + + ]: 108 : if (is_json_objectagg)
11554 : : {
11555 : : /*
11556 : : * the ABSENT ON NULL and WITH UNIQUE args are printed
11557 : : * separately, so ignore them here
11558 : : */
11559 [ - + ]: 28 : if (i > 2)
11560 : 0 : break;
11561 : :
11562 : 28 : appendStringInfoString(buf, " : ");
11563 : : }
11564 : : else
11565 : 80 : appendStringInfoString(buf, ", ");
11566 : : }
11567 [ + + + - ]: 1982 : if (use_variadic && i == nargs)
11568 : 4 : appendStringInfoString(buf, "VARIADIC ");
11569 : 1982 : get_rule_expr(arg, context, true);
11570 : : }
11571 : : }
11572 : :
11573 [ + + ]: 2691 : if (aggref->aggorder != NIL)
11574 : : {
11575 : 78 : appendStringInfoString(buf, " ORDER BY ");
11576 : 78 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
11577 : : }
11578 : : }
11579 : :
11580 [ + + ]: 2708 : if (options)
11581 : 68 : appendStringInfoString(buf, options);
11582 : :
11583 [ + + ]: 2708 : if (aggref->aggfilter != NULL)
11584 : : {
11585 : 28 : appendStringInfoString(buf, ") FILTER (WHERE ");
11586 : 28 : get_rule_expr((Node *) aggref->aggfilter, context, false);
11587 : : }
11588 : :
11589 : 2708 : appendStringInfoChar(buf, ')');
11590 : : }
11591 : :
11592 : : /*
11593 : : * This is a helper function for get_agg_expr(). It's used when we deparse
11594 : : * a combining Aggref; resolve_special_varno locates the corresponding partial
11595 : : * Aggref and then calls this.
11596 : : */
11597 : : static void
11598 : 524 : get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
11599 : : {
11600 : : Aggref *aggref;
11601 : 524 : Aggref *original_aggref = callback_arg;
11602 : :
11603 [ - + ]: 524 : if (!IsA(node, Aggref))
11604 [ # # ]: 0 : elog(ERROR, "combining Aggref does not point to an Aggref");
11605 : :
11606 : 524 : aggref = (Aggref *) node;
11607 : 524 : get_agg_expr(aggref, context, original_aggref);
11608 : 524 : }
11609 : :
11610 : : /*
11611 : : * get_windowfunc_expr - Parse back a WindowFunc node
11612 : : */
11613 : : static void
11614 : 246 : get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
11615 : : {
11616 : 246 : get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
11617 : 246 : }
11618 : :
11619 : :
11620 : : /*
11621 : : * get_windowfunc_expr_helper - subroutine for get_windowfunc_expr and
11622 : : * get_json_agg_constructor
11623 : : */
11624 : : static void
11625 : 258 : get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
11626 : : const char *funcname, const char *options,
11627 : : bool is_json_objectagg)
11628 : : {
11629 : 258 : StringInfo buf = context->buf;
11630 : : Oid argtypes[FUNC_MAX_ARGS];
11631 : : int nargs;
11632 : : List *argnames;
11633 : : ListCell *l;
11634 : :
11635 [ - + ]: 258 : if (list_length(wfunc->args) > FUNC_MAX_ARGS)
11636 [ # # ]: 0 : ereport(ERROR,
11637 : : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
11638 : : errmsg("too many arguments")));
11639 : 258 : nargs = 0;
11640 : 258 : argnames = NIL;
11641 [ + + + + : 428 : foreach(l, wfunc->args)
+ + ]
11642 : : {
11643 : 170 : Node *arg = (Node *) lfirst(l);
11644 : :
11645 [ - + ]: 170 : if (IsA(arg, NamedArgExpr))
11646 : 0 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
11647 : 170 : argtypes[nargs] = exprType(arg);
11648 : 170 : nargs++;
11649 : : }
11650 : :
11651 [ + + ]: 258 : if (!funcname)
11652 : 246 : funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
11653 : : argtypes, false, NULL,
11654 : 246 : context->inGroupBy);
11655 : :
11656 : 258 : appendStringInfo(buf, "%s(", funcname);
11657 : :
11658 : : /* winstar can be set only in zero-argument aggregates */
11659 [ + + ]: 258 : if (wfunc->winstar)
11660 : 28 : appendStringInfoChar(buf, '*');
11661 : : else
11662 : : {
11663 [ + + ]: 230 : if (is_json_objectagg)
11664 : : {
11665 : 4 : get_rule_expr((Node *) linitial(wfunc->args), context, false);
11666 : 4 : appendStringInfoString(buf, " : ");
11667 : 4 : get_rule_expr((Node *) lsecond(wfunc->args), context, false);
11668 : : }
11669 : : else
11670 : 226 : get_rule_expr((Node *) wfunc->args, context, true);
11671 : : }
11672 : :
11673 [ + + ]: 258 : if (options)
11674 : 12 : appendStringInfoString(buf, options);
11675 : :
11676 [ - + ]: 258 : if (wfunc->aggfilter != NULL)
11677 : : {
11678 : 0 : appendStringInfoString(buf, ") FILTER (WHERE ");
11679 : 0 : get_rule_expr((Node *) wfunc->aggfilter, context, false);
11680 : : }
11681 : :
11682 : 258 : appendStringInfoString(buf, ") ");
11683 : :
11684 [ + + ]: 258 : if (wfunc->ignore_nulls == PARSER_IGNORE_NULLS)
11685 : 4 : appendStringInfoString(buf, "IGNORE NULLS ");
11686 : :
11687 : 258 : appendStringInfoString(buf, "OVER ");
11688 : :
11689 [ + + ]: 258 : if (context->windowClause)
11690 : : {
11691 : : /* Query-decompilation case: search the windowClause list */
11692 [ + - + - : 40 : foreach(l, context->windowClause)
+ - ]
11693 : : {
11694 : 40 : WindowClause *wc = (WindowClause *) lfirst(l);
11695 : :
11696 [ + - ]: 40 : if (wc->winref == wfunc->winref)
11697 : : {
11698 [ + + ]: 40 : if (wc->name)
11699 : 12 : appendStringInfoString(buf, quote_identifier(wc->name));
11700 : : else
11701 : 28 : get_rule_windowspec(wc, context->targetList, context);
11702 : 40 : break;
11703 : : }
11704 : : }
11705 [ - + ]: 40 : if (l == NULL)
11706 [ # # ]: 0 : elog(ERROR, "could not find window clause for winref %u",
11707 : : wfunc->winref);
11708 : : }
11709 : : else
11710 : : {
11711 : : /*
11712 : : * In EXPLAIN, search the namespace stack for a matching WindowAgg
11713 : : * node (probably it's always the first entry), and print winname.
11714 : : */
11715 [ + - + - : 218 : foreach(l, context->namespaces)
+ - ]
11716 : : {
11717 : 218 : deparse_namespace *dpns = (deparse_namespace *) lfirst(l);
11718 : :
11719 [ + - + - ]: 218 : if (dpns->plan && IsA(dpns->plan, WindowAgg))
11720 : : {
11721 : 218 : WindowAgg *wagg = (WindowAgg *) dpns->plan;
11722 : :
11723 [ + - ]: 218 : if (wagg->winref == wfunc->winref)
11724 : : {
11725 : 218 : appendStringInfoString(buf, quote_identifier(wagg->winname));
11726 : 218 : break;
11727 : : }
11728 : : }
11729 : : }
11730 [ - + ]: 218 : if (l == NULL)
11731 [ # # ]: 0 : elog(ERROR, "could not find window clause for winref %u",
11732 : : wfunc->winref);
11733 : : }
11734 : 258 : }
11735 : :
11736 : : /*
11737 : : * get_func_sql_syntax - Parse back a SQL-syntax function call
11738 : : *
11739 : : * Returns true if we successfully deparsed, false if we did not
11740 : : * recognize the function.
11741 : : */
11742 : : static bool
11743 : 120 : get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
11744 : : {
11745 : 120 : StringInfo buf = context->buf;
11746 : 120 : Oid funcoid = expr->funcid;
11747 : :
11748 [ + + + + : 120 : switch (funcoid)
+ + + + +
+ + + + +
+ - + ]
11749 : : {
11750 : 16 : case F_TIMEZONE_INTERVAL_TIMESTAMP:
11751 : : case F_TIMEZONE_INTERVAL_TIMESTAMPTZ:
11752 : : case F_TIMEZONE_INTERVAL_TIMETZ:
11753 : : case F_TIMEZONE_TEXT_TIMESTAMP:
11754 : : case F_TIMEZONE_TEXT_TIMESTAMPTZ:
11755 : : case F_TIMEZONE_TEXT_TIMETZ:
11756 : : /* AT TIME ZONE ... note reversed argument order */
11757 : 16 : appendStringInfoChar(buf, '(');
11758 : 16 : get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
11759 : : (Node *) expr);
11760 : 16 : appendStringInfoString(buf, " AT TIME ZONE ");
11761 : 16 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11762 : : (Node *) expr);
11763 : 16 : appendStringInfoChar(buf, ')');
11764 : 16 : return true;
11765 : :
11766 : 12 : case F_TIMEZONE_TIMESTAMP:
11767 : : case F_TIMEZONE_TIMESTAMPTZ:
11768 : : case F_TIMEZONE_TIMETZ:
11769 : : /* AT LOCAL */
11770 : 12 : appendStringInfoChar(buf, '(');
11771 : 12 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11772 : : (Node *) expr);
11773 : 12 : appendStringInfoString(buf, " AT LOCAL)");
11774 : 12 : return true;
11775 : :
11776 : 4 : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL:
11777 : : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ:
11778 : : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL:
11779 : : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ:
11780 : : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_INTERVAL:
11781 : : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_TIMESTAMP:
11782 : : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_INTERVAL:
11783 : : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_TIMESTAMP:
11784 : : case F_OVERLAPS_TIMETZ_TIMETZ_TIMETZ_TIMETZ:
11785 : : case F_OVERLAPS_TIME_INTERVAL_TIME_INTERVAL:
11786 : : case F_OVERLAPS_TIME_INTERVAL_TIME_TIME:
11787 : : case F_OVERLAPS_TIME_TIME_TIME_INTERVAL:
11788 : : case F_OVERLAPS_TIME_TIME_TIME_TIME:
11789 : : /* (x1, x2) OVERLAPS (y1, y2) */
11790 : 4 : appendStringInfoString(buf, "((");
11791 : 4 : get_rule_expr((Node *) linitial(expr->args), context, false);
11792 : 4 : appendStringInfoString(buf, ", ");
11793 : 4 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11794 : 4 : appendStringInfoString(buf, ") OVERLAPS (");
11795 : 4 : get_rule_expr((Node *) lthird(expr->args), context, false);
11796 : 4 : appendStringInfoString(buf, ", ");
11797 : 4 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11798 : 4 : appendStringInfoString(buf, "))");
11799 : 4 : return true;
11800 : :
11801 : 12 : case F_EXTRACT_TEXT_DATE:
11802 : : case F_EXTRACT_TEXT_TIME:
11803 : : case F_EXTRACT_TEXT_TIMETZ:
11804 : : case F_EXTRACT_TEXT_TIMESTAMP:
11805 : : case F_EXTRACT_TEXT_TIMESTAMPTZ:
11806 : : case F_EXTRACT_TEXT_INTERVAL:
11807 : : /* EXTRACT (x FROM y) */
11808 : 12 : appendStringInfoString(buf, "EXTRACT(");
11809 : : {
11810 : 12 : Const *con = (Const *) linitial(expr->args);
11811 : :
11812 : : Assert(IsA(con, Const) &&
11813 : : con->consttype == TEXTOID &&
11814 : : !con->constisnull);
11815 : 12 : appendStringInfoString(buf, quote_identifier(TextDatumGetCString(con->constvalue)));
11816 : : }
11817 : 12 : appendStringInfoString(buf, " FROM ");
11818 : 12 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11819 : 12 : appendStringInfoChar(buf, ')');
11820 : 12 : return true;
11821 : :
11822 : 8 : case F_IS_NORMALIZED:
11823 : : /* IS xxx NORMALIZED */
11824 : 8 : appendStringInfoChar(buf, '(');
11825 : 8 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11826 : : (Node *) expr);
11827 : 8 : appendStringInfoString(buf, " IS");
11828 [ + + ]: 8 : if (list_length(expr->args) == 2)
11829 : : {
11830 : 4 : Const *con = (Const *) lsecond(expr->args);
11831 : :
11832 : : Assert(IsA(con, Const) &&
11833 : : con->consttype == TEXTOID &&
11834 : : !con->constisnull);
11835 : : /* NB: safe because no allowed words need quoted/escaped */
11836 : 4 : appendStringInfo(buf, " %s",
11837 : 4 : TextDatumGetCString(con->constvalue));
11838 : : }
11839 : 8 : appendStringInfoString(buf, " NORMALIZED)");
11840 : 8 : return true;
11841 : :
11842 : 4 : case F_PG_COLLATION_FOR:
11843 : : /* COLLATION FOR */
11844 : 4 : appendStringInfoString(buf, "COLLATION FOR (");
11845 : 4 : get_rule_expr((Node *) linitial(expr->args), context, false);
11846 : 4 : appendStringInfoChar(buf, ')');
11847 : 4 : return true;
11848 : :
11849 : 8 : case F_NORMALIZE:
11850 : : /* NORMALIZE() */
11851 : 8 : appendStringInfoString(buf, "NORMALIZE(");
11852 : 8 : get_rule_expr((Node *) linitial(expr->args), context, false);
11853 [ + + ]: 8 : if (list_length(expr->args) == 2)
11854 : : {
11855 : 4 : Const *con = (Const *) lsecond(expr->args);
11856 : :
11857 : : Assert(IsA(con, Const) &&
11858 : : con->consttype == TEXTOID &&
11859 : : !con->constisnull);
11860 : 4 : appendStringInfo(buf, ", %s",
11861 : 4 : TextDatumGetCString(con->constvalue));
11862 : : }
11863 : 8 : appendStringInfoChar(buf, ')');
11864 : 8 : return true;
11865 : :
11866 : 8 : case F_OVERLAY_BIT_BIT_INT4:
11867 : : case F_OVERLAY_BIT_BIT_INT4_INT4:
11868 : : case F_OVERLAY_BYTEA_BYTEA_INT4:
11869 : : case F_OVERLAY_BYTEA_BYTEA_INT4_INT4:
11870 : : case F_OVERLAY_TEXT_TEXT_INT4:
11871 : : case F_OVERLAY_TEXT_TEXT_INT4_INT4:
11872 : : /* OVERLAY() */
11873 : 8 : appendStringInfoString(buf, "OVERLAY(");
11874 : 8 : get_rule_expr((Node *) linitial(expr->args), context, false);
11875 : 8 : appendStringInfoString(buf, " PLACING ");
11876 : 8 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11877 : 8 : appendStringInfoString(buf, " FROM ");
11878 : 8 : get_rule_expr((Node *) lthird(expr->args), context, false);
11879 [ + + ]: 8 : if (list_length(expr->args) == 4)
11880 : : {
11881 : 4 : appendStringInfoString(buf, " FOR ");
11882 : 4 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11883 : : }
11884 : 8 : appendStringInfoChar(buf, ')');
11885 : 8 : return true;
11886 : :
11887 : 4 : case F_POSITION_BIT_BIT:
11888 : : case F_POSITION_BYTEA_BYTEA:
11889 : : case F_POSITION_TEXT_TEXT:
11890 : : /* POSITION() ... extra parens since args are b_expr not a_expr */
11891 : 4 : appendStringInfoString(buf, "POSITION((");
11892 : 4 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11893 : 4 : appendStringInfoString(buf, ") IN (");
11894 : 4 : get_rule_expr((Node *) linitial(expr->args), context, false);
11895 : 4 : appendStringInfoString(buf, "))");
11896 : 4 : return true;
11897 : :
11898 : 4 : case F_SUBSTRING_BIT_INT4:
11899 : : case F_SUBSTRING_BIT_INT4_INT4:
11900 : : case F_SUBSTRING_BYTEA_INT4:
11901 : : case F_SUBSTRING_BYTEA_INT4_INT4:
11902 : : case F_SUBSTRING_TEXT_INT4:
11903 : : case F_SUBSTRING_TEXT_INT4_INT4:
11904 : : /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
11905 : 4 : appendStringInfoString(buf, "SUBSTRING(");
11906 : 4 : get_rule_expr((Node *) linitial(expr->args), context, false);
11907 : 4 : appendStringInfoString(buf, " FROM ");
11908 : 4 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11909 [ + - ]: 4 : if (list_length(expr->args) == 3)
11910 : : {
11911 : 4 : appendStringInfoString(buf, " FOR ");
11912 : 4 : get_rule_expr((Node *) lthird(expr->args), context, false);
11913 : : }
11914 : 4 : appendStringInfoChar(buf, ')');
11915 : 4 : return true;
11916 : :
11917 : 4 : case F_SUBSTRING_TEXT_TEXT_TEXT:
11918 : : /* SUBSTRING SIMILAR/ESCAPE */
11919 : 4 : appendStringInfoString(buf, "SUBSTRING(");
11920 : 4 : get_rule_expr((Node *) linitial(expr->args), context, false);
11921 : 4 : appendStringInfoString(buf, " SIMILAR ");
11922 : 4 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11923 : 4 : appendStringInfoString(buf, " ESCAPE ");
11924 : 4 : get_rule_expr((Node *) lthird(expr->args), context, false);
11925 : 4 : appendStringInfoChar(buf, ')');
11926 : 4 : return true;
11927 : :
11928 : 8 : case F_BTRIM_BYTEA_BYTEA:
11929 : : case F_BTRIM_TEXT:
11930 : : case F_BTRIM_TEXT_TEXT:
11931 : : /* TRIM() */
11932 : 8 : appendStringInfoString(buf, "TRIM(BOTH");
11933 [ + - ]: 8 : if (list_length(expr->args) == 2)
11934 : : {
11935 : 8 : appendStringInfoChar(buf, ' ');
11936 : 8 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11937 : : }
11938 : 8 : appendStringInfoString(buf, " FROM ");
11939 : 8 : get_rule_expr((Node *) linitial(expr->args), context, false);
11940 : 8 : appendStringInfoChar(buf, ')');
11941 : 8 : return true;
11942 : :
11943 : 8 : case F_LTRIM_BYTEA_BYTEA:
11944 : : case F_LTRIM_TEXT:
11945 : : case F_LTRIM_TEXT_TEXT:
11946 : : /* TRIM() */
11947 : 8 : appendStringInfoString(buf, "TRIM(LEADING");
11948 [ + - ]: 8 : if (list_length(expr->args) == 2)
11949 : : {
11950 : 8 : appendStringInfoChar(buf, ' ');
11951 : 8 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11952 : : }
11953 : 8 : appendStringInfoString(buf, " FROM ");
11954 : 8 : get_rule_expr((Node *) linitial(expr->args), context, false);
11955 : 8 : appendStringInfoChar(buf, ')');
11956 : 8 : return true;
11957 : :
11958 : 8 : case F_RTRIM_BYTEA_BYTEA:
11959 : : case F_RTRIM_TEXT:
11960 : : case F_RTRIM_TEXT_TEXT:
11961 : : /* TRIM() */
11962 : 8 : appendStringInfoString(buf, "TRIM(TRAILING");
11963 [ + + ]: 8 : if (list_length(expr->args) == 2)
11964 : : {
11965 : 4 : appendStringInfoChar(buf, ' ');
11966 : 4 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11967 : : }
11968 : 8 : appendStringInfoString(buf, " FROM ");
11969 : 8 : get_rule_expr((Node *) linitial(expr->args), context, false);
11970 : 8 : appendStringInfoChar(buf, ')');
11971 : 8 : return true;
11972 : :
11973 : 8 : case F_SYSTEM_USER:
11974 : 8 : appendStringInfoString(buf, "SYSTEM_USER");
11975 : 8 : return true;
11976 : :
11977 : 0 : case F_XMLEXISTS:
11978 : : /* XMLEXISTS ... extra parens because args are c_expr */
11979 : 0 : appendStringInfoString(buf, "XMLEXISTS((");
11980 : 0 : get_rule_expr((Node *) linitial(expr->args), context, false);
11981 : 0 : appendStringInfoString(buf, ") PASSING (");
11982 : 0 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11983 : 0 : appendStringInfoString(buf, "))");
11984 : 0 : return true;
11985 : : }
11986 : 4 : return false;
11987 : : }
11988 : :
11989 : : /* ----------
11990 : : * get_coercion_expr
11991 : : *
11992 : : * Make a string representation of a value coerced to a specific type
11993 : : * ----------
11994 : : */
11995 : : static void
11996 : 3724 : get_coercion_expr(Node *arg, deparse_context *context,
11997 : : Oid resulttype, int32 resulttypmod,
11998 : : Node *parentNode)
11999 : : {
12000 : 3724 : StringInfo buf = context->buf;
12001 : :
12002 : : /*
12003 : : * Since parse_coerce.c doesn't immediately collapse application of
12004 : : * length-coercion functions to constants, what we'll typically see in
12005 : : * such cases is a Const with typmod -1 and a length-coercion function
12006 : : * right above it. Avoid generating redundant output. However, beware of
12007 : : * suppressing casts when the user actually wrote something like
12008 : : * 'foo'::text::char(3).
12009 : : *
12010 : : * Note: it might seem that we are missing the possibility of needing to
12011 : : * print a COLLATE clause for such a Const. However, a Const could only
12012 : : * have nondefault collation in a post-constant-folding tree, in which the
12013 : : * length coercion would have been folded too. See also the special
12014 : : * handling of CollateExpr in coerce_to_target_type(): any collation
12015 : : * marking will be above the coercion node, not below it.
12016 : : */
12017 [ + - + + ]: 3724 : if (arg && IsA(arg, Const) &&
12018 [ + + ]: 439 : ((Const *) arg)->consttype == resulttype &&
12019 [ + - ]: 41 : ((Const *) arg)->consttypmod == -1)
12020 : : {
12021 : : /* Show the constant without normal ::typename decoration */
12022 : 41 : get_const_expr((Const *) arg, context, -1);
12023 : : }
12024 : : else
12025 : : {
12026 [ + + ]: 3683 : if (!PRETTY_PAREN(context))
12027 : 3418 : appendStringInfoChar(buf, '(');
12028 : 3683 : get_rule_expr_paren(arg, context, false, parentNode);
12029 [ + + ]: 3683 : if (!PRETTY_PAREN(context))
12030 : 3418 : appendStringInfoChar(buf, ')');
12031 : : }
12032 : :
12033 : : /*
12034 : : * Never emit resulttype(arg) functional notation. A pg_proc entry could
12035 : : * take precedence, and a resulttype in pg_temp would require schema
12036 : : * qualification that format_type_with_typemod() would usually omit. We've
12037 : : * standardized on arg::resulttype, but CAST(arg AS resulttype) notation
12038 : : * would work fine.
12039 : : */
12040 : 3724 : appendStringInfo(buf, "::%s",
12041 : : format_type_with_typemod(resulttype, resulttypmod));
12042 : 3724 : }
12043 : :
12044 : : /* ----------
12045 : : * get_const_expr
12046 : : *
12047 : : * Make a string representation of a Const
12048 : : *
12049 : : * showtype can be -1 to never show "::typename" decoration, or +1 to always
12050 : : * show it, or 0 to show it only if the constant wouldn't be assumed to be
12051 : : * the right type by default.
12052 : : *
12053 : : * If the Const's collation isn't default for its type, show that too.
12054 : : * We mustn't do this when showtype is -1 (since that means the caller will
12055 : : * print "::typename", and we can't put a COLLATE clause in between). It's
12056 : : * caller's responsibility that collation isn't missed in such cases.
12057 : : * ----------
12058 : : */
12059 : : static void
12060 : 48025 : get_const_expr(Const *constval, deparse_context *context, int showtype)
12061 : : {
12062 : 48025 : StringInfo buf = context->buf;
12063 : : Oid typoutput;
12064 : : bool typIsVarlena;
12065 : : char *extval;
12066 : 48025 : bool needlabel = false;
12067 : :
12068 [ + + ]: 48025 : if (constval->constisnull)
12069 : : {
12070 : : /*
12071 : : * Always label the type of a NULL constant to prevent misdecisions
12072 : : * about type when reparsing.
12073 : : */
12074 : 848 : appendStringInfoString(buf, "NULL");
12075 [ + + ]: 848 : if (showtype >= 0)
12076 : : {
12077 : 817 : appendStringInfo(buf, "::%s",
12078 : : format_type_with_typemod(constval->consttype,
12079 : : constval->consttypmod));
12080 : 817 : get_const_collation(constval, context);
12081 : : }
12082 : 6384 : return;
12083 : : }
12084 : :
12085 : 47177 : getTypeOutputInfo(constval->consttype,
12086 : : &typoutput, &typIsVarlena);
12087 : :
12088 : 47177 : extval = OidOutputFunctionCall(typoutput, constval->constvalue);
12089 : :
12090 [ + + + + ]: 47177 : switch (constval->consttype)
12091 : : {
12092 : 27085 : case INT4OID:
12093 : :
12094 : : /*
12095 : : * INT4 can be printed without any decoration, unless it is
12096 : : * negative; in that case print it as '-nnn'::integer to ensure
12097 : : * that the output will re-parse as a constant, not as a constant
12098 : : * plus operator. In most cases we could get away with printing
12099 : : * (-nnn) instead, because of the way that gram.y handles negative
12100 : : * literals; but that doesn't work for INT_MIN, and it doesn't
12101 : : * seem that much prettier anyway.
12102 : : */
12103 [ + + ]: 27085 : if (extval[0] != '-')
12104 : 26747 : appendStringInfoString(buf, extval);
12105 : : else
12106 : : {
12107 : 338 : appendStringInfo(buf, "'%s'", extval);
12108 : 338 : needlabel = true; /* we must attach a cast */
12109 : : }
12110 : 27085 : break;
12111 : :
12112 : 729 : case NUMERICOID:
12113 : :
12114 : : /*
12115 : : * NUMERIC can be printed without quotes if it looks like a float
12116 : : * constant (not an integer, and not Infinity or NaN) and doesn't
12117 : : * have a leading sign (for the same reason as for INT4).
12118 : : */
12119 [ + - ]: 729 : if (isdigit((unsigned char) extval[0]) &&
12120 [ + + ]: 729 : strcspn(extval, "eE.") != strlen(extval))
12121 : : {
12122 : 260 : appendStringInfoString(buf, extval);
12123 : : }
12124 : : else
12125 : : {
12126 : 469 : appendStringInfo(buf, "'%s'", extval);
12127 : 469 : needlabel = true; /* we must attach a cast */
12128 : : }
12129 : 729 : break;
12130 : :
12131 : 1115 : case BOOLOID:
12132 [ + + ]: 1115 : if (strcmp(extval, "t") == 0)
12133 : 449 : appendStringInfoString(buf, "true");
12134 : : else
12135 : 666 : appendStringInfoString(buf, "false");
12136 : 1115 : break;
12137 : :
12138 : 18248 : default:
12139 : 18248 : simple_quote_literal(buf, extval);
12140 : 18248 : break;
12141 : : }
12142 : :
12143 : 47177 : pfree(extval);
12144 : :
12145 [ + + ]: 47177 : if (showtype < 0)
12146 : 5536 : return;
12147 : :
12148 : : /*
12149 : : * For showtype == 0, append ::typename unless the constant will be
12150 : : * implicitly typed as the right type when it is read in.
12151 : : *
12152 : : * XXX this code has to be kept in sync with the behavior of the parser,
12153 : : * especially make_const.
12154 : : */
12155 [ + + + + ]: 41641 : switch (constval->consttype)
12156 : : {
12157 : 1173 : case BOOLOID:
12158 : : case UNKNOWNOID:
12159 : : /* These types can be left unlabeled */
12160 : 1173 : needlabel = false;
12161 : 1173 : break;
12162 : 24294 : case INT4OID:
12163 : : /* We determined above whether a label is needed */
12164 : 24294 : break;
12165 : 729 : case NUMERICOID:
12166 : :
12167 : : /*
12168 : : * Float-looking constants will be typed as numeric, which we
12169 : : * checked above; but if there's a nondefault typmod we need to
12170 : : * show it.
12171 : : */
12172 : 729 : needlabel |= (constval->consttypmod >= 0);
12173 : 729 : break;
12174 : 15445 : default:
12175 : 15445 : needlabel = true;
12176 : 15445 : break;
12177 : : }
12178 [ + + - + ]: 41641 : if (needlabel || showtype > 0)
12179 : 16246 : appendStringInfo(buf, "::%s",
12180 : : format_type_with_typemod(constval->consttype,
12181 : : constval->consttypmod));
12182 : :
12183 : 41641 : get_const_collation(constval, context);
12184 : : }
12185 : :
12186 : : /*
12187 : : * helper for get_const_expr: append COLLATE if needed
12188 : : */
12189 : : static void
12190 : 42458 : get_const_collation(Const *constval, deparse_context *context)
12191 : : {
12192 : 42458 : StringInfo buf = context->buf;
12193 : :
12194 [ + + ]: 42458 : if (OidIsValid(constval->constcollid))
12195 : : {
12196 : 6080 : Oid typcollation = get_typcollation(constval->consttype);
12197 : :
12198 [ + + ]: 6080 : if (constval->constcollid != typcollation)
12199 : : {
12200 : 160 : appendStringInfo(buf, " COLLATE %s",
12201 : : generate_collation_name(constval->constcollid));
12202 : : }
12203 : : }
12204 : 42458 : }
12205 : :
12206 : : /*
12207 : : * get_json_path_spec - Parse back a JSON path specification
12208 : : */
12209 : : static void
12210 : 428 : get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
12211 : : {
12212 [ + - ]: 428 : if (IsA(path_spec, Const))
12213 : 428 : get_const_expr((Const *) path_spec, context, -1);
12214 : : else
12215 : 0 : get_rule_expr(path_spec, context, showimplicit);
12216 : 428 : }
12217 : :
12218 : : /*
12219 : : * get_json_format - Parse back a JsonFormat node
12220 : : */
12221 : : static void
12222 : 148 : get_json_format(JsonFormat *format, StringInfo buf)
12223 : : {
12224 [ + + ]: 148 : if (format->format_type == JS_FORMAT_DEFAULT)
12225 : 92 : return;
12226 : :
12227 : 56 : appendStringInfoString(buf,
12228 [ - + ]: 56 : format->format_type == JS_FORMAT_JSONB ?
12229 : : " FORMAT JSONB" : " FORMAT JSON");
12230 : :
12231 [ + + ]: 56 : if (format->encoding != JS_ENC_DEFAULT)
12232 : : {
12233 : : const char *encoding;
12234 : :
12235 : 4 : encoding =
12236 [ + - ]: 8 : format->encoding == JS_ENC_UTF16 ? "UTF16" :
12237 [ - + ]: 4 : format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
12238 : :
12239 : 4 : appendStringInfo(buf, " ENCODING %s", encoding);
12240 : : }
12241 : : }
12242 : :
12243 : : /*
12244 : : * get_json_returning - Parse back a JsonReturning structure
12245 : : */
12246 : : static void
12247 : 204 : get_json_returning(JsonReturning *returning, StringInfo buf,
12248 : : bool json_format_by_default)
12249 : : {
12250 [ - + ]: 204 : if (!OidIsValid(returning->typid))
12251 : 0 : return;
12252 : :
12253 : 204 : appendStringInfo(buf, " RETURNING %s",
12254 : : format_type_with_typemod(returning->typid,
12255 : : returning->typmod));
12256 : :
12257 [ + + + + ]: 400 : if (!json_format_by_default ||
12258 : 196 : returning->format->format_type !=
12259 [ + + ]: 196 : (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
12260 : 24 : get_json_format(returning->format, buf);
12261 : : }
12262 : :
12263 : : /*
12264 : : * get_json_constructor - Parse back a JsonConstructorExpr node
12265 : : */
12266 : : static void
12267 : 208 : get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
12268 : : bool showimplicit)
12269 : : {
12270 : 208 : StringInfo buf = context->buf;
12271 : : const char *funcname;
12272 : : bool is_json_object;
12273 : : int curridx;
12274 : : ListCell *lc;
12275 : :
12276 [ + + ]: 208 : if (ctor->type == JSCTOR_JSON_OBJECTAGG)
12277 : : {
12278 : 40 : get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
12279 : 40 : return;
12280 : : }
12281 [ + + ]: 168 : else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
12282 : : {
12283 : 68 : get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
12284 : 68 : return;
12285 : : }
12286 [ + + ]: 100 : else if (ctor->type == JSCTOR_JSON_ARRAY_QUERY)
12287 : : {
12288 : 20 : Query *query = castNode(Query, ctor->orig_query);
12289 : :
12290 : 20 : appendStringInfo(buf, "JSON_ARRAY(");
12291 : :
12292 : 20 : get_query_def(query, buf, context->namespaces, NULL, false,
12293 : : context->prettyFlags, context->wrapColumn,
12294 : : context->indentLevel);
12295 : :
12296 : 20 : get_json_format(ctor->format, buf);
12297 : 20 : get_json_constructor_options(ctor, buf);
12298 : 20 : appendStringInfoChar(buf, ')');
12299 : :
12300 : 20 : return;
12301 : : }
12302 : :
12303 [ + + + + : 80 : switch (ctor->type)
+ - ]
12304 : : {
12305 : 20 : case JSCTOR_JSON_OBJECT:
12306 : 20 : funcname = "JSON_OBJECT";
12307 : 20 : break;
12308 : 16 : case JSCTOR_JSON_ARRAY:
12309 : 16 : funcname = "JSON_ARRAY";
12310 : 16 : break;
12311 : 28 : case JSCTOR_JSON_PARSE:
12312 : 28 : funcname = "JSON";
12313 : 28 : break;
12314 : 8 : case JSCTOR_JSON_SCALAR:
12315 : 8 : funcname = "JSON_SCALAR";
12316 : 8 : break;
12317 : 8 : case JSCTOR_JSON_SERIALIZE:
12318 : 8 : funcname = "JSON_SERIALIZE";
12319 : 8 : break;
12320 : 0 : default:
12321 [ # # ]: 0 : elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
12322 : : }
12323 : :
12324 : 80 : appendStringInfo(buf, "%s(", funcname);
12325 : :
12326 : 80 : is_json_object = ctor->type == JSCTOR_JSON_OBJECT;
12327 [ + - + + : 212 : foreach(lc, ctor->args)
+ + ]
12328 : : {
12329 : 132 : curridx = foreach_current_index(lc);
12330 [ + + ]: 132 : if (curridx > 0)
12331 : : {
12332 : : const char *sep;
12333 : :
12334 [ + + + + ]: 52 : sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
12335 : 52 : appendStringInfoString(buf, sep);
12336 : : }
12337 : :
12338 : 132 : get_rule_expr((Node *) lfirst(lc), context, true);
12339 : : }
12340 : :
12341 : 80 : get_json_constructor_options(ctor, buf);
12342 : 80 : appendStringInfoChar(buf, ')');
12343 : : }
12344 : :
12345 : : /*
12346 : : * Append options, if any, to the JSON constructor being deparsed
12347 : : */
12348 : : static void
12349 : 208 : get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
12350 : : {
12351 [ + + ]: 208 : if (ctor->absent_on_null)
12352 : : {
12353 [ + - ]: 84 : if (ctor->type == JSCTOR_JSON_OBJECT ||
12354 [ + + ]: 84 : ctor->type == JSCTOR_JSON_OBJECTAGG)
12355 : 8 : appendStringInfoString(buf, " ABSENT ON NULL");
12356 : : }
12357 : : else
12358 : : {
12359 [ + - ]: 124 : if (ctor->type == JSCTOR_JSON_ARRAY ||
12360 [ + + ]: 124 : ctor->type == JSCTOR_JSON_ARRAYAGG)
12361 : 28 : appendStringInfoString(buf, " NULL ON NULL");
12362 : : }
12363 : :
12364 [ + + ]: 208 : if (ctor->unique)
12365 : 24 : appendStringInfoString(buf, " WITH UNIQUE KEYS");
12366 : :
12367 : : /*
12368 : : * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
12369 : : * support one.
12370 : : */
12371 [ + + + + ]: 208 : if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
12372 : 172 : get_json_returning(ctor->returning, buf, true);
12373 : 208 : }
12374 : :
12375 : : /*
12376 : : * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
12377 : : */
12378 : : static void
12379 : 108 : get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
12380 : : const char *funcname, bool is_json_objectagg)
12381 : : {
12382 : : StringInfoData options;
12383 : :
12384 : 108 : initStringInfo(&options);
12385 : 108 : get_json_constructor_options(ctor, &options);
12386 : :
12387 [ + + ]: 108 : if (IsA(ctor->func, Aggref))
12388 : 68 : get_agg_expr_helper((Aggref *) ctor->func, context,
12389 : 68 : (Aggref *) ctor->func,
12390 : 68 : funcname, options.data, is_json_objectagg);
12391 [ + + ]: 40 : else if (IsA(ctor->func, WindowFunc))
12392 : 12 : get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
12393 : 12 : funcname, options.data,
12394 : : is_json_objectagg);
12395 [ + - ]: 28 : else if (IsA(ctor->func, Var))
12396 : : {
12397 : : /*
12398 : : * If the aggregate is computed by a lower plan node, setrefs.c will
12399 : : * have replaced the Aggref or WindowFunc with a Var referencing that
12400 : : * node's output. Chase the Var back to it so we can still print the
12401 : : * original JSON aggregate syntax. This only happens in EXPLAIN.
12402 : : */
12403 : 28 : resolve_special_varno((Node *) ctor->func, context,
12404 : : get_json_agg_constructor_expr, ctor);
12405 : : }
12406 : : else
12407 [ # # ]: 0 : elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
12408 : : nodeTag(ctor->func));
12409 : 108 : }
12410 : :
12411 : : /*
12412 : : * Deparse a JsonConstructorExpr whose aggregate is computed by a lower plan
12413 : : * node; resolve_special_varno has located the underlying Aggref/WindowFunc.
12414 : : */
12415 : : static void
12416 : 28 : get_json_agg_constructor_expr(Node *node, deparse_context *context,
12417 : : void *callback_arg)
12418 : : {
12419 : : JsonConstructorExpr ctor;
12420 : :
12421 [ + + - + ]: 28 : if (!IsA(node, Aggref) && !IsA(node, WindowFunc))
12422 [ # # ]: 0 : elog(ERROR, "JSON aggregate constructor does not point to an Aggref or WindowFunc");
12423 : :
12424 : : /* Flat copy suffices; we only replace func. */
12425 : 28 : ctor = *(JsonConstructorExpr *) callback_arg;
12426 : 28 : ctor.func = (Expr *) node;
12427 : 28 : get_json_constructor(&ctor, context, false);
12428 : 28 : }
12429 : :
12430 : : /*
12431 : : * simple_quote_literal - Format a string as a SQL literal, append to buf
12432 : : */
12433 : : static void
12434 : 18693 : simple_quote_literal(StringInfo buf, const char *val)
12435 : : {
12436 : : const char *valptr;
12437 : :
12438 : : /*
12439 : : * We always form the string literal according to standard SQL rules.
12440 : : */
12441 : 18693 : appendStringInfoChar(buf, '\'');
12442 [ + + ]: 187555 : for (valptr = val; *valptr; valptr++)
12443 : : {
12444 : 168862 : char ch = *valptr;
12445 : :
12446 [ + + ]: 168862 : if (SQL_STR_DOUBLE(ch, false))
12447 : 204 : appendStringInfoChar(buf, ch);
12448 : 168862 : appendStringInfoChar(buf, ch);
12449 : : }
12450 : 18693 : appendStringInfoChar(buf, '\'');
12451 : 18693 : }
12452 : :
12453 : :
12454 : : /* ----------
12455 : : * get_sublink_expr - Parse back a sublink
12456 : : * ----------
12457 : : */
12458 : : static void
12459 : 290 : get_sublink_expr(SubLink *sublink, deparse_context *context)
12460 : : {
12461 : 290 : StringInfo buf = context->buf;
12462 : 290 : Query *query = (Query *) (sublink->subselect);
12463 : 290 : char *opname = NULL;
12464 : : bool need_paren;
12465 : :
12466 [ + + ]: 290 : if (sublink->subLinkType == ARRAY_SUBLINK)
12467 : 14 : appendStringInfoString(buf, "ARRAY(");
12468 : : else
12469 : 276 : appendStringInfoChar(buf, '(');
12470 : :
12471 : : /*
12472 : : * Note that we print the name of only the first operator, when there are
12473 : : * multiple combining operators. This is an approximation that could go
12474 : : * wrong in various scenarios (operators in different schemas, renamed
12475 : : * operators, etc) but there is not a whole lot we can do about it, since
12476 : : * the syntax allows only one operator to be shown.
12477 : : */
12478 [ + + ]: 290 : if (sublink->testexpr)
12479 : : {
12480 [ + + ]: 12 : if (IsA(sublink->testexpr, OpExpr))
12481 : : {
12482 : : /* single combining operator */
12483 : 4 : OpExpr *opexpr = (OpExpr *) sublink->testexpr;
12484 : :
12485 : 4 : get_rule_expr(linitial(opexpr->args), context, true);
12486 : 4 : opname = generate_operator_name(opexpr->opno,
12487 : 4 : exprType(linitial(opexpr->args)),
12488 : 4 : exprType(lsecond(opexpr->args)));
12489 : : }
12490 [ + + ]: 8 : else if (IsA(sublink->testexpr, BoolExpr))
12491 : : {
12492 : : /* multiple combining operators, = or <> cases */
12493 : : char *sep;
12494 : : ListCell *l;
12495 : :
12496 : 4 : appendStringInfoChar(buf, '(');
12497 : 4 : sep = "";
12498 [ + - + + : 12 : foreach(l, ((BoolExpr *) sublink->testexpr)->args)
+ + ]
12499 : : {
12500 : 8 : OpExpr *opexpr = lfirst_node(OpExpr, l);
12501 : :
12502 : 8 : appendStringInfoString(buf, sep);
12503 : 8 : get_rule_expr(linitial(opexpr->args), context, true);
12504 [ + + ]: 8 : if (!opname)
12505 : 4 : opname = generate_operator_name(opexpr->opno,
12506 : 4 : exprType(linitial(opexpr->args)),
12507 : 4 : exprType(lsecond(opexpr->args)));
12508 : 8 : sep = ", ";
12509 : : }
12510 : 4 : appendStringInfoChar(buf, ')');
12511 : : }
12512 [ + - ]: 4 : else if (IsA(sublink->testexpr, RowCompareExpr))
12513 : : {
12514 : : /* multiple combining operators, < <= > >= cases */
12515 : 4 : RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
12516 : :
12517 : 4 : appendStringInfoChar(buf, '(');
12518 : 4 : get_rule_expr((Node *) rcexpr->largs, context, true);
12519 : 4 : opname = generate_operator_name(linitial_oid(rcexpr->opnos),
12520 : 4 : exprType(linitial(rcexpr->largs)),
12521 : 4 : exprType(linitial(rcexpr->rargs)));
12522 : 4 : appendStringInfoChar(buf, ')');
12523 : : }
12524 : : else
12525 [ # # ]: 0 : elog(ERROR, "unrecognized testexpr type: %d",
12526 : : (int) nodeTag(sublink->testexpr));
12527 : : }
12528 : :
12529 : 290 : need_paren = true;
12530 : :
12531 [ + + + - : 290 : switch (sublink->subLinkType)
+ - ]
12532 : : {
12533 : 113 : case EXISTS_SUBLINK:
12534 : 113 : appendStringInfoString(buf, "EXISTS ");
12535 : 113 : break;
12536 : :
12537 : 8 : case ANY_SUBLINK:
12538 [ + + ]: 8 : if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
12539 : 4 : appendStringInfoString(buf, " IN ");
12540 : : else
12541 : 4 : appendStringInfo(buf, " %s ANY ", opname);
12542 : 8 : break;
12543 : :
12544 : 4 : case ALL_SUBLINK:
12545 : 4 : appendStringInfo(buf, " %s ALL ", opname);
12546 : 4 : break;
12547 : :
12548 : 0 : case ROWCOMPARE_SUBLINK:
12549 : 0 : appendStringInfo(buf, " %s ", opname);
12550 : 0 : break;
12551 : :
12552 : 165 : case EXPR_SUBLINK:
12553 : : case MULTIEXPR_SUBLINK:
12554 : : case ARRAY_SUBLINK:
12555 : 165 : need_paren = false;
12556 : 165 : break;
12557 : :
12558 : 0 : case CTE_SUBLINK: /* shouldn't occur in a SubLink */
12559 : : default:
12560 [ # # ]: 0 : elog(ERROR, "unrecognized sublink type: %d",
12561 : : (int) sublink->subLinkType);
12562 : : break;
12563 : : }
12564 : :
12565 [ + + ]: 290 : if (need_paren)
12566 : 125 : appendStringInfoChar(buf, '(');
12567 : :
12568 : 290 : get_query_def(query, buf, context->namespaces, NULL, false,
12569 : : context->prettyFlags, context->wrapColumn,
12570 : : context->indentLevel);
12571 : :
12572 [ + + ]: 290 : if (need_paren)
12573 : 125 : appendStringInfoString(buf, "))");
12574 : : else
12575 : 165 : appendStringInfoChar(buf, ')');
12576 : 290 : }
12577 : :
12578 : :
12579 : : /* ----------
12580 : : * get_xmltable - Parse back a XMLTABLE function
12581 : : * ----------
12582 : : */
12583 : : static void
12584 : 38 : get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
12585 : : {
12586 : 38 : StringInfo buf = context->buf;
12587 : :
12588 : 38 : appendStringInfoString(buf, "XMLTABLE(");
12589 : :
12590 [ + + ]: 38 : if (tf->ns_uris != NIL)
12591 : : {
12592 : : ListCell *lc1,
12593 : : *lc2;
12594 : 9 : bool first = true;
12595 : :
12596 : 9 : appendStringInfoString(buf, "XMLNAMESPACES (");
12597 [ + - + + : 18 : forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
+ - + + +
+ + - +
+ ]
12598 : : {
12599 : 9 : Node *expr = (Node *) lfirst(lc1);
12600 : 9 : String *ns_node = lfirst_node(String, lc2);
12601 : :
12602 [ - + ]: 9 : if (!first)
12603 : 0 : appendStringInfoString(buf, ", ");
12604 : : else
12605 : 9 : first = false;
12606 : :
12607 [ + - ]: 9 : if (ns_node != NULL)
12608 : : {
12609 : 9 : get_rule_expr(expr, context, showimplicit);
12610 : 9 : appendStringInfo(buf, " AS %s",
12611 : 9 : quote_identifier(strVal(ns_node)));
12612 : : }
12613 : : else
12614 : : {
12615 : 0 : appendStringInfoString(buf, "DEFAULT ");
12616 : 0 : get_rule_expr(expr, context, showimplicit);
12617 : : }
12618 : : }
12619 : 9 : appendStringInfoString(buf, "), ");
12620 : : }
12621 : :
12622 : 38 : appendStringInfoChar(buf, '(');
12623 : 38 : get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
12624 : 38 : appendStringInfoString(buf, ") PASSING (");
12625 : 38 : get_rule_expr((Node *) tf->docexpr, context, showimplicit);
12626 : 38 : appendStringInfoChar(buf, ')');
12627 : :
12628 [ + - ]: 38 : if (tf->colexprs != NIL)
12629 : : {
12630 : : ListCell *l1;
12631 : : ListCell *l2;
12632 : : ListCell *l3;
12633 : : ListCell *l4;
12634 : : ListCell *l5;
12635 : 38 : int colnum = 0;
12636 : :
12637 : 38 : appendStringInfoString(buf, " COLUMNS ");
12638 [ + - + + : 231 : forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
+ - + + +
- + + + -
+ + + - +
+ + + + -
+ - + - +
- + + ]
12639 : : l4, tf->colexprs, l5, tf->coldefexprs)
12640 : : {
12641 : 193 : char *colname = strVal(lfirst(l1));
12642 : 193 : Oid typid = lfirst_oid(l2);
12643 : 193 : int32 typmod = lfirst_int(l3);
12644 : 193 : Node *colexpr = (Node *) lfirst(l4);
12645 : 193 : Node *coldefexpr = (Node *) lfirst(l5);
12646 : 193 : bool ordinality = (tf->ordinalitycol == colnum);
12647 : 193 : bool notnull = bms_is_member(colnum, tf->notnulls);
12648 : :
12649 [ + + ]: 193 : if (colnum > 0)
12650 : 155 : appendStringInfoString(buf, ", ");
12651 : 193 : colnum++;
12652 : :
12653 [ + + ]: 365 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
12654 : : ordinality ? "FOR ORDINALITY" :
12655 : 172 : format_type_with_typemod(typid, typmod));
12656 [ + + ]: 193 : if (ordinality)
12657 : 21 : continue;
12658 : :
12659 [ + + ]: 172 : if (coldefexpr != NULL)
12660 : : {
12661 : 21 : appendStringInfoString(buf, " DEFAULT (");
12662 : 21 : get_rule_expr((Node *) coldefexpr, context, showimplicit);
12663 : 21 : appendStringInfoChar(buf, ')');
12664 : : }
12665 [ + + ]: 172 : if (colexpr != NULL)
12666 : : {
12667 : 156 : appendStringInfoString(buf, " PATH (");
12668 : 156 : get_rule_expr((Node *) colexpr, context, showimplicit);
12669 : 156 : appendStringInfoChar(buf, ')');
12670 : : }
12671 [ + + ]: 172 : if (notnull)
12672 : 21 : appendStringInfoString(buf, " NOT NULL");
12673 : : }
12674 : : }
12675 : :
12676 : 38 : appendStringInfoChar(buf, ')');
12677 : 38 : }
12678 : :
12679 : : /*
12680 : : * get_json_table_nested_columns - Parse back nested JSON_TABLE columns
12681 : : */
12682 : : static void
12683 : 104 : get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
12684 : : deparse_context *context, bool showimplicit,
12685 : : bool needcomma)
12686 : : {
12687 [ + + ]: 104 : if (IsA(plan, JsonTablePathScan))
12688 : : {
12689 : 76 : JsonTablePathScan *scan = castNode(JsonTablePathScan, plan);
12690 : :
12691 [ + + ]: 76 : if (needcomma)
12692 : 48 : appendStringInfoChar(context->buf, ',');
12693 : :
12694 : 76 : appendStringInfoChar(context->buf, ' ');
12695 : 76 : appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
12696 : 76 : get_const_expr(scan->path->value, context, -1);
12697 : 76 : appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
12698 : 76 : get_json_table_columns(tf, scan, context, showimplicit);
12699 : : }
12700 [ + - ]: 28 : else if (IsA(plan, JsonTableSiblingJoin))
12701 : : {
12702 : 28 : JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
12703 : :
12704 : 28 : get_json_table_nested_columns(tf, join->lplan, context, showimplicit,
12705 : : needcomma);
12706 : 28 : get_json_table_nested_columns(tf, join->rplan, context, showimplicit,
12707 : : true);
12708 : : }
12709 : 104 : }
12710 : :
12711 : : /*
12712 : : * json_table_plan_is_default - does this plan match the implicit default?
12713 : : *
12714 : : * When no PLAN clause is given, JSON_TABLE builds a default plan that joins
12715 : : * every nested path to its parent with OUTER and every set of sibling paths
12716 : : * with UNION (see transformJsonTableColumns()). Such a plan is fully implied
12717 : : * by the NESTED COLUMNS structure, so we need not (and, to match the input,
12718 : : * should not) print a PLAN clause for it; we only deparse a PLAN clause when
12719 : : * the plan deviates from the default, i.e. uses an INNER or CROSS join
12720 : : * somewhere. This follows the usual ruleutils convention of omitting a clause
12721 : : * that merely restates the default (cf. get_json_expr_options() for ON
12722 : : * EMPTY/ON ERROR, or the NULLS FIRST/LAST handling in get_rule_orderby()).
12723 : : */
12724 : : static bool
12725 : 116 : json_table_plan_is_default(JsonTablePlan *plan)
12726 : : {
12727 [ + + ]: 116 : if (IsA(plan, JsonTablePathScan))
12728 : : {
12729 : 88 : JsonTablePathScan *scan = castNode(JsonTablePathScan, plan);
12730 : :
12731 [ + + ]: 88 : if (scan->child)
12732 : : {
12733 [ + + ]: 48 : if (!scan->outerJoin)
12734 : 4 : return false; /* INNER is not the default */
12735 : 44 : return json_table_plan_is_default(scan->child);
12736 : : }
12737 : :
12738 : 40 : return true;
12739 : : }
12740 : : else
12741 : : {
12742 : 28 : JsonTableSiblingJoin *join = castNode(JsonTableSiblingJoin, plan);
12743 : :
12744 [ - + ]: 28 : if (join->cross)
12745 : 0 : return false; /* CROSS is not the default */
12746 [ + - + - ]: 56 : return json_table_plan_is_default(join->lplan) &&
12747 : 28 : json_table_plan_is_default(join->rplan);
12748 : : }
12749 : : }
12750 : :
12751 : : /*
12752 : : * get_json_table_plan - Parse back a JSON_TABLE plan
12753 : : */
12754 : : static void
12755 : 12 : get_json_table_plan(TableFunc *tf, JsonTablePlan *plan, deparse_context *context,
12756 : : bool parenthesize)
12757 : : {
12758 [ + + ]: 12 : if (parenthesize)
12759 : 8 : appendStringInfoChar(context->buf, '(');
12760 : :
12761 [ + - ]: 12 : if (IsA(plan, JsonTablePathScan))
12762 : : {
12763 : 12 : JsonTablePathScan *s = castNode(JsonTablePathScan, plan);
12764 : :
12765 : 12 : appendStringInfoString(context->buf, quote_identifier(s->path->name));
12766 : :
12767 [ + + ]: 12 : if (s->child)
12768 : : {
12769 : 8 : appendStringInfoString(context->buf,
12770 [ + + ]: 8 : s->outerJoin ? " OUTER " : " INNER ");
12771 : 8 : get_json_table_plan(tf, s->child, context,
12772 [ + - ]: 16 : IsA(s->child, JsonTableSiblingJoin) ||
12773 [ + + ]: 16 : castNode(JsonTablePathScan, s->child)->child);
12774 : : }
12775 : : }
12776 [ # # ]: 0 : else if (IsA(plan, JsonTableSiblingJoin))
12777 : : {
12778 : 0 : JsonTableSiblingJoin *j = (JsonTableSiblingJoin *) plan;
12779 : :
12780 : 0 : get_json_table_plan(tf, j->lplan, context,
12781 [ # # ]: 0 : IsA(j->lplan, JsonTableSiblingJoin) ||
12782 [ # # ]: 0 : castNode(JsonTablePathScan, j->lplan)->child);
12783 : :
12784 [ # # ]: 0 : appendStringInfoString(context->buf, j->cross ? " CROSS " : " UNION ");
12785 : :
12786 : 0 : get_json_table_plan(tf, j->rplan, context,
12787 [ # # ]: 0 : IsA(j->rplan, JsonTableSiblingJoin) ||
12788 [ # # ]: 0 : castNode(JsonTablePathScan, j->rplan)->child);
12789 : : }
12790 : :
12791 [ + + ]: 12 : if (parenthesize)
12792 : 8 : appendStringInfoChar(context->buf, ')');
12793 : 12 : }
12794 : :
12795 : : /*
12796 : : * get_json_table_columns - Parse back JSON_TABLE columns
12797 : : */
12798 : : static void
12799 : 160 : get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
12800 : : deparse_context *context,
12801 : : bool showimplicit)
12802 : : {
12803 : 160 : StringInfo buf = context->buf;
12804 : : ListCell *lc_colname;
12805 : : ListCell *lc_coltype;
12806 : : ListCell *lc_coltypmod;
12807 : : ListCell *lc_colvalexpr;
12808 : 160 : int colnum = 0;
12809 : :
12810 : 160 : appendStringInfoChar(buf, ' ');
12811 : 160 : appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12812 : :
12813 [ + + ]: 160 : if (PRETTY_INDENT(context))
12814 : 108 : context->indentLevel += PRETTYINDENT_VAR;
12815 : :
12816 [ + - + + : 1304 : forfour(lc_colname, tf->colnames,
+ - + + +
- + + + -
+ + + + +
- + - + -
+ + ]
12817 : : lc_coltype, tf->coltypes,
12818 : : lc_coltypmod, tf->coltypmods,
12819 : : lc_colvalexpr, tf->colvalexprs)
12820 : : {
12821 : 1192 : char *colname = strVal(lfirst(lc_colname));
12822 : : JsonExpr *colexpr;
12823 : : Oid typid;
12824 : : int32 typmod;
12825 : : bool ordinality;
12826 : : JsonBehaviorType default_behavior;
12827 : :
12828 : 1192 : typid = lfirst_oid(lc_coltype);
12829 : 1192 : typmod = lfirst_int(lc_coltypmod);
12830 : 1192 : colexpr = castNode(JsonExpr, lfirst(lc_colvalexpr));
12831 : :
12832 : : /* Skip columns that don't belong to this scan. */
12833 [ + + + + ]: 1192 : if (scan->colMin < 0 || colnum < scan->colMin)
12834 : : {
12835 : 740 : colnum++;
12836 : 740 : continue;
12837 : : }
12838 [ + + ]: 452 : if (colnum > scan->colMax)
12839 : 48 : break;
12840 : :
12841 [ + + ]: 404 : if (colnum > scan->colMin)
12842 : 272 : appendStringInfoString(buf, ", ");
12843 : :
12844 : 404 : colnum++;
12845 : :
12846 : 404 : ordinality = !colexpr;
12847 : :
12848 : 404 : appendContextKeyword(context, "", 0, 0, 0);
12849 : :
12850 [ + + ]: 792 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
12851 : : ordinality ? "FOR ORDINALITY" :
12852 : 388 : format_type_with_typemod(typid, typmod));
12853 [ + + ]: 404 : if (ordinality)
12854 : 16 : continue;
12855 : :
12856 : : /*
12857 : : * Set default_behavior to guide get_json_expr_options() on whether to
12858 : : * emit the ON ERROR / EMPTY clauses.
12859 : : */
12860 [ + + ]: 388 : if (colexpr->op == JSON_EXISTS_OP)
12861 : : {
12862 : 36 : appendStringInfoString(buf, " EXISTS");
12863 : 36 : default_behavior = JSON_BEHAVIOR_FALSE;
12864 : : }
12865 : : else
12866 : : {
12867 [ + + ]: 352 : if (colexpr->op == JSON_QUERY_OP)
12868 : : {
12869 : : char typcategory;
12870 : : bool typispreferred;
12871 : :
12872 : 168 : get_type_category_preferred(typid, &typcategory, &typispreferred);
12873 : :
12874 [ + + ]: 168 : if (typcategory == TYPCATEGORY_STRING)
12875 : 36 : appendStringInfoString(buf,
12876 [ - + ]: 36 : colexpr->format->format_type == JS_FORMAT_JSONB ?
12877 : : " FORMAT JSONB" : " FORMAT JSON");
12878 : : }
12879 : :
12880 : 352 : default_behavior = JSON_BEHAVIOR_NULL;
12881 : : }
12882 : :
12883 : 388 : appendStringInfoString(buf, " PATH ");
12884 : :
12885 : 388 : get_json_path_spec(colexpr->path_spec, context, showimplicit);
12886 : :
12887 : 388 : get_json_expr_options(colexpr, context, default_behavior);
12888 : : }
12889 : :
12890 [ + + ]: 160 : if (scan->child)
12891 : 48 : get_json_table_nested_columns(tf, scan->child, context, showimplicit,
12892 : 48 : scan->colMin >= 0);
12893 : :
12894 [ + + ]: 160 : if (PRETTY_INDENT(context))
12895 : 108 : context->indentLevel -= PRETTYINDENT_VAR;
12896 : :
12897 : 160 : appendContextKeyword(context, ")", 0, 0, 0);
12898 : 160 : }
12899 : :
12900 : : /* ----------
12901 : : * get_json_table - Parse back a JSON_TABLE function
12902 : : * ----------
12903 : : */
12904 : : static void
12905 : 84 : get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
12906 : : {
12907 : 84 : StringInfo buf = context->buf;
12908 : 84 : JsonExpr *jexpr = castNode(JsonExpr, tf->docexpr);
12909 : 84 : JsonTablePathScan *root = castNode(JsonTablePathScan, tf->plan);
12910 : :
12911 : 84 : appendStringInfoString(buf, "JSON_TABLE(");
12912 : :
12913 [ + + ]: 84 : if (PRETTY_INDENT(context))
12914 : 52 : context->indentLevel += PRETTYINDENT_VAR;
12915 : :
12916 : 84 : appendContextKeyword(context, "", 0, 0, 0);
12917 : :
12918 : 84 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12919 : :
12920 : 84 : appendStringInfoString(buf, ", ");
12921 : :
12922 : 84 : get_const_expr(root->path->value, context, -1);
12923 : :
12924 : 84 : appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12925 : :
12926 [ + + ]: 84 : if (jexpr->passing_values)
12927 : : {
12928 : : ListCell *lc1,
12929 : : *lc2;
12930 : 60 : bool needcomma = false;
12931 : :
12932 : 60 : appendStringInfoChar(buf, ' ');
12933 : 60 : appendContextKeyword(context, "PASSING ", 0, 0, 0);
12934 : :
12935 [ + + ]: 60 : if (PRETTY_INDENT(context))
12936 : 28 : context->indentLevel += PRETTYINDENT_VAR;
12937 : :
12938 [ + - + + : 180 : forboth(lc1, jexpr->passing_names,
+ - + + +
+ + - +
+ ]
12939 : : lc2, jexpr->passing_values)
12940 : : {
12941 [ + + ]: 120 : if (needcomma)
12942 : 60 : appendStringInfoString(buf, ", ");
12943 : 120 : needcomma = true;
12944 : :
12945 : 120 : appendContextKeyword(context, "", 0, 0, 0);
12946 : :
12947 : 120 : get_rule_expr((Node *) lfirst(lc2), context, false);
12948 : 120 : appendStringInfo(buf, " AS %s",
12949 : 120 : quote_identifier((lfirst_node(String, lc1))->sval)
12950 : : );
12951 : : }
12952 : :
12953 [ + + ]: 60 : if (PRETTY_INDENT(context))
12954 : 28 : context->indentLevel -= PRETTYINDENT_VAR;
12955 : : }
12956 : :
12957 : 84 : get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12958 : : showimplicit);
12959 : :
12960 : : /*
12961 : : * Deparse a PLAN clause only for a non-default plan; the default plan is
12962 : : * implied by the NESTED COLUMNS structure (see
12963 : : * json_table_plan_is_default).
12964 : : */
12965 [ + + + + ]: 84 : if (root->child && !json_table_plan_is_default((JsonTablePlan *) root))
12966 : : {
12967 : 4 : appendStringInfoChar(buf, ' ');
12968 : 4 : appendContextKeyword(context, "PLAN ", 0, 0, 0);
12969 : 4 : get_json_table_plan(tf, (JsonTablePlan *) root, context, true);
12970 : : }
12971 : :
12972 [ + + ]: 84 : if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12973 : 8 : get_json_behavior(jexpr->on_error, context, "ERROR");
12974 : :
12975 [ + + ]: 84 : if (PRETTY_INDENT(context))
12976 : 52 : context->indentLevel -= PRETTYINDENT_VAR;
12977 : :
12978 : 84 : appendContextKeyword(context, ")", 0, 0, 0);
12979 : 84 : }
12980 : :
12981 : : /* ----------
12982 : : * get_tablefunc - Parse back a table function
12983 : : * ----------
12984 : : */
12985 : : static void
12986 : 122 : get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
12987 : : {
12988 : : /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12989 : :
12990 [ + + ]: 122 : if (tf->functype == TFT_XMLTABLE)
12991 : 38 : get_xmltable(tf, context, showimplicit);
12992 [ + - ]: 84 : else if (tf->functype == TFT_JSON_TABLE)
12993 : 84 : get_json_table(tf, context, showimplicit);
12994 : 122 : }
12995 : :
12996 : : /* ----------
12997 : : * get_from_clause - Parse back a FROM clause
12998 : : *
12999 : : * "prefix" is the keyword that denotes the start of the list of FROM
13000 : : * elements. It is FROM when used to parse back SELECT and UPDATE, but
13001 : : * is USING when parsing back DELETE.
13002 : : * ----------
13003 : : */
13004 : : static void
13005 : 3069 : get_from_clause(Query *query, const char *prefix, deparse_context *context)
13006 : : {
13007 : 3069 : StringInfo buf = context->buf;
13008 : 3069 : bool first = true;
13009 : : ListCell *l;
13010 : :
13011 : : /*
13012 : : * We use the query's jointree as a guide to what to print. However, we
13013 : : * must ignore auto-added RTEs that are marked not inFromCl. (These can
13014 : : * only appear at the top level of the jointree, so it's sufficient to
13015 : : * check here.) This check also ensures we ignore the rule pseudo-RTEs
13016 : : * for NEW and OLD.
13017 : : */
13018 [ + + + + : 6097 : foreach(l, query->jointree->fromlist)
+ + ]
13019 : : {
13020 : 3028 : Node *jtnode = (Node *) lfirst(l);
13021 : :
13022 [ + + ]: 3028 : if (IsA(jtnode, RangeTblRef))
13023 : : {
13024 : 2423 : int varno = ((RangeTblRef *) jtnode)->rtindex;
13025 : 2423 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
13026 : :
13027 [ + + ]: 2423 : if (!rte->inFromCl)
13028 : 246 : continue;
13029 : : }
13030 : :
13031 [ + + ]: 2782 : if (first)
13032 : : {
13033 : 2549 : appendContextKeyword(context, prefix,
13034 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
13035 : 2549 : first = false;
13036 : :
13037 : 2549 : get_from_clause_item(jtnode, query, context);
13038 : : }
13039 : : else
13040 : : {
13041 : : StringInfoData itembuf;
13042 : :
13043 : 233 : appendStringInfoString(buf, ", ");
13044 : :
13045 : : /*
13046 : : * Put the new FROM item's text into itembuf so we can decide
13047 : : * after we've got it whether or not it needs to go on a new line.
13048 : : */
13049 : 233 : initStringInfo(&itembuf);
13050 : 233 : context->buf = &itembuf;
13051 : :
13052 : 233 : get_from_clause_item(jtnode, query, context);
13053 : :
13054 : : /* Restore context's output buffer */
13055 : 233 : context->buf = buf;
13056 : :
13057 : : /* Consider line-wrapping if enabled */
13058 [ + - + - ]: 233 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
13059 : : {
13060 : : /* Does the new item start with a new line? */
13061 [ + - - + ]: 233 : if (itembuf.len > 0 && itembuf.data[0] == '\n')
13062 : : {
13063 : : /* If so, we shouldn't add anything */
13064 : : /* instead, remove any trailing spaces currently in buf */
13065 : 0 : removeStringInfoSpaces(buf);
13066 : : }
13067 : : else
13068 : : {
13069 : : char *trailing_nl;
13070 : :
13071 : : /* Locate the start of the current line in the buffer */
13072 : 233 : trailing_nl = strrchr(buf->data, '\n');
13073 [ - + ]: 233 : if (trailing_nl == NULL)
13074 : 0 : trailing_nl = buf->data;
13075 : : else
13076 : 233 : trailing_nl++;
13077 : :
13078 : : /*
13079 : : * Add a newline, plus some indentation, if the new item
13080 : : * would cause an overflow.
13081 : : */
13082 [ + - ]: 233 : if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
13083 : 233 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
13084 : : PRETTYINDENT_STD,
13085 : : PRETTYINDENT_VAR);
13086 : : }
13087 : : }
13088 : :
13089 : : /* Add the new item */
13090 : 233 : appendBinaryStringInfo(buf, itembuf.data, itembuf.len);
13091 : :
13092 : : /* clean up */
13093 : 233 : pfree(itembuf.data);
13094 : : }
13095 : : }
13096 : 3069 : }
13097 : :
13098 : : static void
13099 : 4662 : get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
13100 : : {
13101 : 4662 : StringInfo buf = context->buf;
13102 : 4662 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
13103 : :
13104 [ + + ]: 4662 : if (IsA(jtnode, RangeTblRef))
13105 : : {
13106 : 3722 : int varno = ((RangeTblRef *) jtnode)->rtindex;
13107 : 3722 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
13108 : 3722 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
13109 : 3722 : RangeTblFunction *rtfunc1 = NULL;
13110 : :
13111 [ + + ]: 3722 : if (rte->lateral)
13112 : 73 : appendStringInfoString(buf, "LATERAL ");
13113 : :
13114 : : /* Print the FROM item proper */
13115 [ + + + + : 3722 : switch (rte->rtekind)
+ + + - ]
13116 : : {
13117 : 2755 : case RTE_RELATION:
13118 : : /* Normal relation RTE */
13119 : 5510 : appendStringInfo(buf, "%s%s",
13120 [ + + ]: 2755 : only_marker(rte),
13121 : : generate_relation_name(rte->relid,
13122 : : context->namespaces));
13123 : 2755 : break;
13124 : 200 : case RTE_SUBQUERY:
13125 : : /* Subquery RTE */
13126 : 200 : appendStringInfoChar(buf, '(');
13127 : 200 : get_query_def(rte->subquery, buf, context->namespaces, NULL,
13128 : : true,
13129 : : context->prettyFlags, context->wrapColumn,
13130 : : context->indentLevel);
13131 : 200 : appendStringInfoChar(buf, ')');
13132 : 200 : break;
13133 : 560 : case RTE_FUNCTION:
13134 : : /* Function RTE */
13135 : 560 : rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
13136 : :
13137 : : /*
13138 : : * Omit ROWS FROM() syntax for just one function, unless it
13139 : : * has both a coldeflist and WITH ORDINALITY. If it has both,
13140 : : * we must use ROWS FROM() syntax to avoid ambiguity about
13141 : : * whether the coldeflist includes the ordinality column.
13142 : : */
13143 [ + + ]: 560 : if (list_length(rte->functions) == 1 &&
13144 [ - + - - ]: 540 : (rtfunc1->funccolnames == NIL || !rte->funcordinality))
13145 : : {
13146 : 540 : get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
13147 : : /* we'll print the coldeflist below, if it has one */
13148 : : }
13149 : : else
13150 : : {
13151 : : bool all_unnest;
13152 : : ListCell *lc;
13153 : :
13154 : : /*
13155 : : * If all the function calls in the list are to unnest,
13156 : : * and none need a coldeflist, then collapse the list back
13157 : : * down to UNNEST(args). (If we had more than one
13158 : : * built-in unnest function, this would get more
13159 : : * difficult.)
13160 : : *
13161 : : * XXX This is pretty ugly, since it makes not-terribly-
13162 : : * future-proof assumptions about what the parser would do
13163 : : * with the output; but the alternative is to emit our
13164 : : * nonstandard ROWS FROM() notation for what might have
13165 : : * been a perfectly spec-compliant multi-argument
13166 : : * UNNEST().
13167 : : */
13168 : 20 : all_unnest = true;
13169 [ + - + + : 52 : foreach(lc, rte->functions)
+ + ]
13170 : : {
13171 : 44 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
13172 : :
13173 [ + - ]: 44 : if (!IsA(rtfunc->funcexpr, FuncExpr) ||
13174 [ + + ]: 44 : ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
13175 [ - + ]: 32 : rtfunc->funccolnames != NIL)
13176 : : {
13177 : 12 : all_unnest = false;
13178 : 12 : break;
13179 : : }
13180 : : }
13181 : :
13182 [ + + ]: 20 : if (all_unnest)
13183 : : {
13184 : 8 : List *allargs = NIL;
13185 : :
13186 [ + - + + : 32 : foreach(lc, rte->functions)
+ + ]
13187 : : {
13188 : 24 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
13189 : 24 : List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
13190 : :
13191 : 24 : allargs = list_concat(allargs, args);
13192 : : }
13193 : :
13194 : 8 : appendStringInfoString(buf, "UNNEST(");
13195 : 8 : get_rule_expr((Node *) allargs, context, true);
13196 : 8 : appendStringInfoChar(buf, ')');
13197 : : }
13198 : : else
13199 : : {
13200 : 12 : int funcno = 0;
13201 : :
13202 : 12 : appendStringInfoString(buf, "ROWS FROM(");
13203 [ + - + + : 44 : foreach(lc, rte->functions)
+ + ]
13204 : : {
13205 : 32 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
13206 : :
13207 [ + + ]: 32 : if (funcno > 0)
13208 : 20 : appendStringInfoString(buf, ", ");
13209 : 32 : get_rule_expr_funccall(rtfunc->funcexpr, context, true);
13210 [ + + ]: 32 : if (rtfunc->funccolnames != NIL)
13211 : : {
13212 : : /* Reconstruct the column definition list */
13213 : 4 : appendStringInfoString(buf, " AS ");
13214 : 4 : get_from_clause_coldeflist(rtfunc,
13215 : : NULL,
13216 : : context);
13217 : : }
13218 : 32 : funcno++;
13219 : : }
13220 : 12 : appendStringInfoChar(buf, ')');
13221 : : }
13222 : : /* prevent printing duplicate coldeflist below */
13223 : 20 : rtfunc1 = NULL;
13224 : : }
13225 [ + + ]: 560 : if (rte->funcordinality)
13226 : 12 : appendStringInfoString(buf, " WITH ORDINALITY");
13227 : 560 : break;
13228 : 70 : case RTE_TABLEFUNC:
13229 : 70 : get_tablefunc(rte->tablefunc, context, true);
13230 : 70 : break;
13231 : 14 : case RTE_GRAPH_TABLE:
13232 : 14 : appendStringInfoString(buf, "GRAPH_TABLE (");
13233 : 14 : appendStringInfoString(buf, generate_relation_name(rte->relid, context->namespaces));
13234 : 14 : appendStringInfoString(buf, " MATCH ");
13235 : 14 : get_graph_pattern_def(rte->graph_pattern, context);
13236 : 14 : appendStringInfoString(buf, " COLUMNS (");
13237 : : {
13238 : 14 : bool first = true;
13239 : :
13240 [ + - + + : 69 : foreach_node(TargetEntry, te, rte->graph_table_columns)
+ + ]
13241 : : {
13242 [ + + ]: 41 : if (!first)
13243 : 27 : appendStringInfoString(buf, ", ");
13244 : : else
13245 : 14 : first = false;
13246 : :
13247 : 41 : get_rule_expr((Node *) te->expr, context, false);
13248 : 41 : appendStringInfoString(buf, " AS ");
13249 : 41 : appendStringInfoString(buf, quote_identifier(te->resname));
13250 : : }
13251 : : }
13252 : 14 : appendStringInfoString(buf, "))");
13253 : 14 : break;
13254 : 8 : case RTE_VALUES:
13255 : : /* Values list RTE */
13256 : 8 : appendStringInfoChar(buf, '(');
13257 : 8 : get_values_def(rte->values_lists, context);
13258 : 8 : appendStringInfoChar(buf, ')');
13259 : 8 : break;
13260 : 115 : case RTE_CTE:
13261 : 115 : appendStringInfoString(buf, quote_identifier(rte->ctename));
13262 : 115 : break;
13263 : 0 : default:
13264 [ # # ]: 0 : elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
13265 : : break;
13266 : : }
13267 : :
13268 : : /* Print the relation alias, if needed */
13269 : 3722 : get_rte_alias(rte, varno, false, context);
13270 : :
13271 : : /* Print the column definitions or aliases, if needed */
13272 [ + + - + ]: 3722 : if (rtfunc1 && rtfunc1->funccolnames != NIL)
13273 : : {
13274 : : /* Reconstruct the columndef list, which is also the aliases */
13275 : 0 : get_from_clause_coldeflist(rtfunc1, colinfo, context);
13276 : : }
13277 : : else
13278 : : {
13279 : : /* Else print column aliases as needed */
13280 : 3722 : get_column_alias_list(colinfo, context);
13281 : : }
13282 : :
13283 : : /* Tablesample clause must go after any alias */
13284 [ + + + + ]: 3722 : if (rte->rtekind == RTE_RELATION && rte->tablesample)
13285 : 18 : get_tablesample_def(rte->tablesample, context);
13286 : : }
13287 [ + - ]: 940 : else if (IsA(jtnode, JoinExpr))
13288 : : {
13289 : 940 : JoinExpr *j = (JoinExpr *) jtnode;
13290 : 940 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
13291 : : bool need_paren_on_right;
13292 : :
13293 : 2172 : need_paren_on_right = PRETTY_PAREN(context) &&
13294 [ + + - + ]: 940 : !IsA(j->rarg, RangeTblRef) &&
13295 [ # # # # ]: 0 : !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
13296 : :
13297 [ + + + + ]: 940 : if (!PRETTY_PAREN(context) || j->alias != NULL)
13298 : 720 : appendStringInfoChar(buf, '(');
13299 : :
13300 : 940 : get_from_clause_item(j->larg, query, context);
13301 : :
13302 [ + + + - : 940 : switch (j->jointype)
- ]
13303 : : {
13304 : 517 : case JOIN_INNER:
13305 [ + + ]: 517 : if (j->quals)
13306 : 489 : appendContextKeyword(context, " JOIN ",
13307 : : -PRETTYINDENT_STD,
13308 : : PRETTYINDENT_STD,
13309 : : PRETTYINDENT_JOIN);
13310 : : else
13311 : 28 : appendContextKeyword(context, " CROSS JOIN ",
13312 : : -PRETTYINDENT_STD,
13313 : : PRETTYINDENT_STD,
13314 : : PRETTYINDENT_JOIN);
13315 : 517 : break;
13316 : 355 : case JOIN_LEFT:
13317 : 355 : appendContextKeyword(context, " LEFT JOIN ",
13318 : : -PRETTYINDENT_STD,
13319 : : PRETTYINDENT_STD,
13320 : : PRETTYINDENT_JOIN);
13321 : 355 : break;
13322 : 68 : case JOIN_FULL:
13323 : 68 : appendContextKeyword(context, " FULL JOIN ",
13324 : : -PRETTYINDENT_STD,
13325 : : PRETTYINDENT_STD,
13326 : : PRETTYINDENT_JOIN);
13327 : 68 : break;
13328 : 0 : case JOIN_RIGHT:
13329 : 0 : appendContextKeyword(context, " RIGHT JOIN ",
13330 : : -PRETTYINDENT_STD,
13331 : : PRETTYINDENT_STD,
13332 : : PRETTYINDENT_JOIN);
13333 : 0 : break;
13334 : 0 : default:
13335 [ # # ]: 0 : elog(ERROR, "unrecognized join type: %d",
13336 : : (int) j->jointype);
13337 : : }
13338 : :
13339 [ - + ]: 940 : if (need_paren_on_right)
13340 : 0 : appendStringInfoChar(buf, '(');
13341 : 940 : get_from_clause_item(j->rarg, query, context);
13342 [ - + ]: 940 : if (need_paren_on_right)
13343 : 0 : appendStringInfoChar(buf, ')');
13344 : :
13345 [ + + ]: 940 : if (j->usingClause)
13346 : : {
13347 : : ListCell *lc;
13348 : 280 : bool first = true;
13349 : :
13350 : 280 : appendStringInfoString(buf, " USING (");
13351 : : /* Use the assigned names, not what's in usingClause */
13352 [ + - + + : 664 : foreach(lc, colinfo->usingNames)
+ + ]
13353 : : {
13354 : 384 : char *colname = (char *) lfirst(lc);
13355 : :
13356 [ + + ]: 384 : if (first)
13357 : 280 : first = false;
13358 : : else
13359 : 104 : appendStringInfoString(buf, ", ");
13360 : 384 : appendStringInfoString(buf, quote_identifier(colname));
13361 : : }
13362 : 280 : appendStringInfoChar(buf, ')');
13363 : :
13364 [ + + ]: 280 : if (j->join_using_alias)
13365 : 8 : appendStringInfo(buf, " AS %s",
13366 : 8 : quote_identifier(j->join_using_alias->aliasname));
13367 : : }
13368 [ + + ]: 660 : else if (j->quals)
13369 : : {
13370 : 628 : appendStringInfoString(buf, " ON ");
13371 [ + + ]: 628 : if (!PRETTY_PAREN(context))
13372 : 624 : appendStringInfoChar(buf, '(');
13373 : 628 : get_rule_expr(j->quals, context, false);
13374 [ + + ]: 628 : if (!PRETTY_PAREN(context))
13375 : 624 : appendStringInfoChar(buf, ')');
13376 : : }
13377 [ + + ]: 32 : else if (j->jointype != JOIN_INNER)
13378 : : {
13379 : : /* If we didn't say CROSS JOIN above, we must provide an ON */
13380 : 4 : appendStringInfoString(buf, " ON TRUE");
13381 : : }
13382 : :
13383 [ + + + + ]: 940 : if (!PRETTY_PAREN(context) || j->alias != NULL)
13384 : 720 : appendStringInfoChar(buf, ')');
13385 : :
13386 : : /* Yes, it's correct to put alias after the right paren ... */
13387 [ + + ]: 940 : if (j->alias != NULL)
13388 : : {
13389 : : /*
13390 : : * Note that it's correct to emit an alias clause if and only if
13391 : : * there was one originally. Otherwise we'd be converting a named
13392 : : * join to unnamed or vice versa, which creates semantic
13393 : : * subtleties we don't want. However, we might print a different
13394 : : * alias name than was there originally.
13395 : : */
13396 : 72 : appendStringInfo(buf, " %s",
13397 : 72 : quote_identifier(get_rtable_name(j->rtindex,
13398 : : context)));
13399 : 72 : get_column_alias_list(colinfo, context);
13400 : : }
13401 : : }
13402 : : else
13403 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
13404 : : (int) nodeTag(jtnode));
13405 : 4662 : }
13406 : :
13407 : : /*
13408 : : * get_rte_alias - print the relation's alias, if needed
13409 : : *
13410 : : * If printed, the alias is preceded by a space, or by " AS " if use_as is true.
13411 : : */
13412 : : static void
13413 : 4081 : get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
13414 : : deparse_context *context)
13415 : : {
13416 : 4081 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
13417 : 4081 : char *refname = get_rtable_name(varno, context);
13418 : 4081 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
13419 : 4081 : bool printalias = false;
13420 : :
13421 [ + + ]: 4081 : if (rte->alias != NULL)
13422 : : {
13423 : : /* Always print alias if user provided one */
13424 : 1912 : printalias = true;
13425 : : }
13426 [ + + ]: 2169 : else if (colinfo->printaliases)
13427 : : {
13428 : : /* Always print alias if we need to print column aliases */
13429 : 210 : printalias = true;
13430 : : }
13431 [ + + ]: 1959 : else if (rte->rtekind == RTE_RELATION)
13432 : : {
13433 : : /*
13434 : : * No need to print alias if it's same as relation name (this would
13435 : : * normally be the case, but not if set_rtable_names had to resolve a
13436 : : * conflict).
13437 : : */
13438 [ + + ]: 1775 : if (strcmp(refname, get_relation_name(rte->relid)) != 0)
13439 : 52 : printalias = true;
13440 : : }
13441 [ - + ]: 184 : else if (rte->rtekind == RTE_FUNCTION)
13442 : : {
13443 : : /*
13444 : : * For a function RTE, always print alias. This covers possible
13445 : : * renaming of the function and/or instability of the FigureColname
13446 : : * rules for things that aren't simple functions. Note we'd need to
13447 : : * force it anyway for the columndef list case.
13448 : : */
13449 : 0 : printalias = true;
13450 : : }
13451 [ + + ]: 184 : else if (rte->rtekind == RTE_SUBQUERY ||
13452 [ + + ]: 168 : rte->rtekind == RTE_VALUES)
13453 : : {
13454 : : /*
13455 : : * For a subquery, always print alias. This makes the output
13456 : : * SQL-spec-compliant, even though we allow such aliases to be omitted
13457 : : * on input.
13458 : : */
13459 : 24 : printalias = true;
13460 : : }
13461 [ + + ]: 160 : else if (rte->rtekind == RTE_CTE)
13462 : : {
13463 : : /*
13464 : : * No need to print alias if it's same as CTE name (this would
13465 : : * normally be the case, but not if set_rtable_names had to resolve a
13466 : : * conflict).
13467 : : */
13468 [ + + ]: 89 : if (strcmp(refname, rte->ctename) != 0)
13469 : 12 : printalias = true;
13470 : : }
13471 : :
13472 [ + + ]: 4081 : if (printalias)
13473 [ + + ]: 2210 : appendStringInfo(context->buf, "%s%s",
13474 : : use_as ? " AS " : " ",
13475 : : quote_identifier(refname));
13476 : 4081 : }
13477 : :
13478 : : /*
13479 : : * get_for_portion_of - print FOR PORTION OF if needed
13480 : : * XXX: Newlines would help here, at least when pretty-printing. But then the
13481 : : * alias and SET will be on their own line with a leading space.
13482 : : */
13483 : : static void
13484 : 157 : get_for_portion_of(ForPortionOfExpr *forPortionOf, RangeTblEntry *rte,
13485 : : deparse_context *context)
13486 : : {
13487 [ + + ]: 157 : if (forPortionOf)
13488 : : {
13489 : : char *range_name;
13490 : :
13491 : 32 : range_name = get_attname(rte->relid,
13492 : 32 : forPortionOf->rangeVar->varattno,
13493 : : false);
13494 : 32 : appendStringInfo(context->buf, " FOR PORTION OF %s",
13495 : : quote_identifier(range_name));
13496 : :
13497 : : /*
13498 : : * Try to write it as FROM ... TO ... if we received it that way,
13499 : : * otherwise (targetRange).
13500 : : */
13501 [ + + + - ]: 32 : if (forPortionOf->targetFrom && forPortionOf->targetTo)
13502 : : {
13503 : 16 : appendStringInfoString(context->buf, " FROM ");
13504 : 16 : get_rule_expr(forPortionOf->targetFrom, context, false);
13505 : 16 : appendStringInfoString(context->buf, " TO ");
13506 : 16 : get_rule_expr(forPortionOf->targetTo, context, false);
13507 : : }
13508 : : else
13509 : : {
13510 : 16 : appendStringInfoString(context->buf, " (");
13511 : 16 : get_rule_expr(forPortionOf->targetRange, context, false);
13512 : 16 : appendStringInfoChar(context->buf, ')');
13513 : : }
13514 : : }
13515 : 157 : }
13516 : :
13517 : : /*
13518 : : * get_column_alias_list - print column alias list for an RTE
13519 : : *
13520 : : * Caller must already have printed the relation's alias name.
13521 : : */
13522 : : static void
13523 : 3794 : get_column_alias_list(deparse_columns *colinfo, deparse_context *context)
13524 : : {
13525 : 3794 : StringInfo buf = context->buf;
13526 : : int i;
13527 : 3794 : bool first = true;
13528 : :
13529 : : /* Don't print aliases if not needed */
13530 [ + + ]: 3794 : if (!colinfo->printaliases)
13531 : 2982 : return;
13532 : :
13533 [ + + ]: 6378 : for (i = 0; i < colinfo->num_new_cols; i++)
13534 : : {
13535 : 5566 : char *colname = colinfo->new_colnames[i];
13536 : :
13537 [ + + ]: 5566 : if (first)
13538 : : {
13539 : 812 : appendStringInfoChar(buf, '(');
13540 : 812 : first = false;
13541 : : }
13542 : : else
13543 : 4754 : appendStringInfoString(buf, ", ");
13544 : 5566 : appendStringInfoString(buf, quote_identifier(colname));
13545 : : }
13546 [ + - ]: 812 : if (!first)
13547 : 812 : appendStringInfoChar(buf, ')');
13548 : : }
13549 : :
13550 : : /*
13551 : : * get_from_clause_coldeflist - reproduce FROM clause coldeflist
13552 : : *
13553 : : * When printing a top-level coldeflist (which is syntactically also the
13554 : : * relation's column alias list), use column names from colinfo. But when
13555 : : * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
13556 : : * original coldeflist's names, which are available in rtfunc->funccolnames.
13557 : : * Pass NULL for colinfo to select the latter behavior.
13558 : : *
13559 : : * The coldeflist is appended immediately (no space) to buf. Caller is
13560 : : * responsible for ensuring that an alias or AS is present before it.
13561 : : */
13562 : : static void
13563 : 4 : get_from_clause_coldeflist(RangeTblFunction *rtfunc,
13564 : : deparse_columns *colinfo,
13565 : : deparse_context *context)
13566 : : {
13567 : 4 : StringInfo buf = context->buf;
13568 : : ListCell *l1;
13569 : : ListCell *l2;
13570 : : ListCell *l3;
13571 : : ListCell *l4;
13572 : : int i;
13573 : :
13574 : 4 : appendStringInfoChar(buf, '(');
13575 : :
13576 : 4 : i = 0;
13577 [ + - + + : 16 : forfour(l1, rtfunc->funccoltypes,
+ - + + +
- + + + -
+ + + + +
- + - + -
+ + ]
13578 : : l2, rtfunc->funccoltypmods,
13579 : : l3, rtfunc->funccolcollations,
13580 : : l4, rtfunc->funccolnames)
13581 : : {
13582 : 12 : Oid atttypid = lfirst_oid(l1);
13583 : 12 : int32 atttypmod = lfirst_int(l2);
13584 : 12 : Oid attcollation = lfirst_oid(l3);
13585 : : char *attname;
13586 : :
13587 [ - + ]: 12 : if (colinfo)
13588 : 0 : attname = colinfo->colnames[i];
13589 : : else
13590 : 12 : attname = strVal(lfirst(l4));
13591 : :
13592 : : Assert(attname); /* shouldn't be any dropped columns here */
13593 : :
13594 [ + + ]: 12 : if (i > 0)
13595 : 8 : appendStringInfoString(buf, ", ");
13596 : 12 : appendStringInfo(buf, "%s %s",
13597 : : quote_identifier(attname),
13598 : : format_type_with_typemod(atttypid, atttypmod));
13599 [ + + - + ]: 16 : if (OidIsValid(attcollation) &&
13600 : 4 : attcollation != get_typcollation(atttypid))
13601 : 0 : appendStringInfo(buf, " COLLATE %s",
13602 : : generate_collation_name(attcollation));
13603 : :
13604 : 12 : i++;
13605 : : }
13606 : :
13607 : 4 : appendStringInfoChar(buf, ')');
13608 : 4 : }
13609 : :
13610 : : /*
13611 : : * get_tablesample_def - print a TableSampleClause
13612 : : */
13613 : : static void
13614 : 18 : get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
13615 : : {
13616 : 18 : StringInfo buf = context->buf;
13617 : : Oid argtypes[1];
13618 : : int nargs;
13619 : : ListCell *l;
13620 : :
13621 : : /*
13622 : : * We should qualify the handler's function name if it wouldn't be
13623 : : * resolved by lookup in the current search path.
13624 : : */
13625 : 18 : argtypes[0] = INTERNALOID;
13626 : 18 : appendStringInfo(buf, " TABLESAMPLE %s (",
13627 : : generate_function_name(tablesample->tsmhandler, 1,
13628 : : NIL, argtypes,
13629 : : false, NULL, false));
13630 : :
13631 : 18 : nargs = 0;
13632 [ + - + + : 36 : foreach(l, tablesample->args)
+ + ]
13633 : : {
13634 [ - + ]: 18 : if (nargs++ > 0)
13635 : 0 : appendStringInfoString(buf, ", ");
13636 : 18 : get_rule_expr((Node *) lfirst(l), context, false);
13637 : : }
13638 : 18 : appendStringInfoChar(buf, ')');
13639 : :
13640 [ + + ]: 18 : if (tablesample->repeatable != NULL)
13641 : : {
13642 : 9 : appendStringInfoString(buf, " REPEATABLE (");
13643 : 9 : get_rule_expr((Node *) tablesample->repeatable, context, false);
13644 : 9 : appendStringInfoChar(buf, ')');
13645 : : }
13646 : 18 : }
13647 : :
13648 : : /*
13649 : : * get_opclass_name - fetch name of an index operator class
13650 : : *
13651 : : * The opclass name is appended (after a space) to buf.
13652 : : *
13653 : : * Output is suppressed if the opclass is the default for the given
13654 : : * actual_datatype. (If you don't want this behavior, just pass
13655 : : * InvalidOid for actual_datatype.)
13656 : : */
13657 : : static void
13658 : 7378 : get_opclass_name(Oid opclass, Oid actual_datatype,
13659 : : StringInfo buf)
13660 : : {
13661 : : HeapTuple ht_opc;
13662 : : Form_pg_opclass opcrec;
13663 : : char *opcname;
13664 : : char *nspname;
13665 : :
13666 : 7378 : ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
13667 [ - + ]: 7378 : if (!HeapTupleIsValid(ht_opc))
13668 [ # # ]: 0 : elog(ERROR, "cache lookup failed for opclass %u", opclass);
13669 : 7378 : opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
13670 : :
13671 [ + + + + ]: 14730 : if (!OidIsValid(actual_datatype) ||
13672 : 7352 : GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
13673 : : {
13674 : : /* Okay, we need the opclass name. Do we need to qualify it? */
13675 : 293 : opcname = NameStr(opcrec->opcname);
13676 [ + - ]: 293 : if (OpclassIsVisible(opclass))
13677 : 293 : appendStringInfo(buf, " %s", quote_identifier(opcname));
13678 : : else
13679 : : {
13680 : 0 : nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
13681 : 0 : appendStringInfo(buf, " %s.%s",
13682 : : quote_identifier(nspname),
13683 : : quote_identifier(opcname));
13684 : : }
13685 : : }
13686 : 7378 : ReleaseSysCache(ht_opc);
13687 : 7378 : }
13688 : :
13689 : : /*
13690 : : * generate_opclass_name
13691 : : * Compute the name to display for an opclass specified by OID
13692 : : *
13693 : : * The result includes all necessary quoting and schema-prefixing.
13694 : : */
13695 : : char *
13696 : 4 : generate_opclass_name(Oid opclass)
13697 : : {
13698 : : StringInfoData buf;
13699 : :
13700 : 4 : initStringInfo(&buf);
13701 : 4 : get_opclass_name(opclass, InvalidOid, &buf);
13702 : :
13703 : 4 : return &buf.data[1]; /* get_opclass_name() prepends space */
13704 : : }
13705 : :
13706 : : /*
13707 : : * processIndirection - take care of array and subfield assignment
13708 : : *
13709 : : * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
13710 : : * appear in the input, printing them as decoration for the base column
13711 : : * name (which we assume the caller just printed). We might also need to
13712 : : * strip CoerceToDomain nodes, but only ones that appear above assignment
13713 : : * nodes.
13714 : : *
13715 : : * Returns the subexpression that's to be assigned.
13716 : : */
13717 : : static Node *
13718 : 737 : processIndirection(Node *node, deparse_context *context)
13719 : : {
13720 : 737 : StringInfo buf = context->buf;
13721 : 737 : CoerceToDomain *cdomain = NULL;
13722 : :
13723 : : for (;;)
13724 : : {
13725 [ - + ]: 941 : if (node == NULL)
13726 : 0 : break;
13727 [ + + ]: 941 : if (IsA(node, FieldStore))
13728 : : {
13729 : 72 : FieldStore *fstore = (FieldStore *) node;
13730 : : Oid typrelid;
13731 : : char *fieldname;
13732 : :
13733 : : /* lookup tuple type */
13734 : 72 : typrelid = get_typ_typrelid(fstore->resulttype);
13735 [ - + ]: 72 : if (!OidIsValid(typrelid))
13736 [ # # ]: 0 : elog(ERROR, "argument type %s of FieldStore is not a tuple type",
13737 : : format_type_be(fstore->resulttype));
13738 : :
13739 : : /*
13740 : : * Print the field name. There should only be one target field in
13741 : : * stored rules. There could be more than that in executable
13742 : : * target lists, but this function cannot be used for that case.
13743 : : */
13744 : : Assert(list_length(fstore->fieldnums) == 1);
13745 : 72 : fieldname = get_attname(typrelid,
13746 : 72 : linitial_int(fstore->fieldnums), false);
13747 : 72 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
13748 : :
13749 : : /*
13750 : : * We ignore arg since it should be an uninteresting reference to
13751 : : * the target column or subcolumn.
13752 : : */
13753 : 72 : node = (Node *) linitial(fstore->newvals);
13754 : : }
13755 [ + + ]: 869 : else if (IsA(node, SubscriptingRef))
13756 : : {
13757 : 92 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
13758 : :
13759 [ - + ]: 92 : if (sbsref->refassgnexpr == NULL)
13760 : 0 : break;
13761 : :
13762 : 92 : printSubscripts(sbsref, context);
13763 : :
13764 : : /*
13765 : : * We ignore refexpr since it should be an uninteresting reference
13766 : : * to the target column or subcolumn.
13767 : : */
13768 : 92 : node = (Node *) sbsref->refassgnexpr;
13769 : : }
13770 [ + + ]: 777 : else if (IsA(node, CoerceToDomain))
13771 : : {
13772 : 40 : cdomain = (CoerceToDomain *) node;
13773 : : /* If it's an explicit domain coercion, we're done */
13774 [ - + ]: 40 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
13775 : 0 : break;
13776 : : /* Tentatively descend past the CoerceToDomain */
13777 : 40 : node = (Node *) cdomain->arg;
13778 : : }
13779 : : else
13780 : 737 : break;
13781 : : }
13782 : :
13783 : : /*
13784 : : * If we descended past a CoerceToDomain whose argument turned out not to
13785 : : * be a FieldStore or array assignment, back up to the CoerceToDomain.
13786 : : * (This is not enough to be fully correct if there are nested implicit
13787 : : * CoerceToDomains, but such cases shouldn't ever occur.)
13788 : : */
13789 [ + + - + ]: 737 : if (cdomain && node == (Node *) cdomain->arg)
13790 : 0 : node = (Node *) cdomain;
13791 : :
13792 : 737 : return node;
13793 : : }
13794 : :
13795 : : static void
13796 : 316 : printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
13797 : : {
13798 : 316 : StringInfo buf = context->buf;
13799 : : ListCell *lowlist_item;
13800 : : ListCell *uplist_item;
13801 : :
13802 : 316 : lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
13803 [ + - + + : 632 : foreach(uplist_item, sbsref->refupperindexpr)
+ + ]
13804 : : {
13805 : 316 : appendStringInfoChar(buf, '[');
13806 [ - + ]: 316 : if (lowlist_item)
13807 : : {
13808 : : /* If subexpression is NULL, get_rule_expr prints nothing */
13809 : 0 : get_rule_expr((Node *) lfirst(lowlist_item), context, false);
13810 : 0 : appendStringInfoChar(buf, ':');
13811 : 0 : lowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item);
13812 : : }
13813 : : /* If subexpression is NULL, get_rule_expr prints nothing */
13814 : 316 : get_rule_expr((Node *) lfirst(uplist_item), context, false);
13815 : 316 : appendStringInfoChar(buf, ']');
13816 : : }
13817 : 316 : }
13818 : :
13819 : : /*
13820 : : * quote_identifier - Quote an identifier only if needed
13821 : : *
13822 : : * When quotes are needed, we palloc the required space; slightly
13823 : : * space-wasteful but well worth it for notational simplicity.
13824 : : */
13825 : : const char *
13826 : 1916799 : quote_identifier(const char *ident)
13827 : : {
13828 : : /*
13829 : : * Can avoid quoting if ident starts with a lowercase letter or underscore
13830 : : * and contains only lowercase letters, digits, and underscores, *and* is
13831 : : * not any SQL keyword. Otherwise, supply quotes.
13832 : : */
13833 : 1916799 : int nquotes = 0;
13834 : : bool safe;
13835 : : const char *ptr;
13836 : : char *result;
13837 : : char *optr;
13838 : :
13839 : : /*
13840 : : * would like to use <ctype.h> macros here, but they might yield unwanted
13841 : : * locale-specific results...
13842 : : */
13843 [ + + - + : 1916799 : safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
+ + ]
13844 : :
13845 [ + + ]: 17091852 : for (ptr = ident; *ptr; ptr++)
13846 : : {
13847 : 15175053 : char ch = *ptr;
13848 : :
13849 [ + + + - : 15175053 : if ((ch >= 'a' && ch <= 'z') ||
+ + ]
13850 [ + + + + ]: 2315503 : (ch >= '0' && ch <= '9') ||
13851 : : (ch == '_'))
13852 : : {
13853 : : /* okay */
13854 : : }
13855 : : else
13856 : : {
13857 : 371305 : safe = false;
13858 [ + + ]: 371305 : if (ch == '"')
13859 : 89 : nquotes++;
13860 : : }
13861 : : }
13862 : :
13863 [ + + ]: 1916799 : if (quote_all_identifiers)
13864 : 7845 : safe = false;
13865 : :
13866 [ + + ]: 1916799 : if (safe)
13867 : : {
13868 : : /*
13869 : : * Check for keyword. We quote keywords except for unreserved ones.
13870 : : * (In some cases we could avoid quoting a col_name or type_func_name
13871 : : * keyword, but it seems much harder than it's worth to tell that.)
13872 : : *
13873 : : * Note: ScanKeywordLookup() does case-insensitive comparison, but
13874 : : * that's fine, since we already know we have all-lower-case.
13875 : : */
13876 : 1852922 : int kwnum = ScanKeywordLookup(ident, &ScanKeywords);
13877 : :
13878 [ + + + + ]: 1852922 : if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
13879 : 2551 : safe = false;
13880 : : }
13881 : :
13882 [ + + ]: 1916799 : if (safe)
13883 : 1850371 : return ident; /* no change needed */
13884 : :
13885 : 66428 : result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13886 : :
13887 : 66428 : optr = result;
13888 : 66428 : *optr++ = '"';
13889 [ + + ]: 536222 : for (ptr = ident; *ptr; ptr++)
13890 : : {
13891 : 469794 : char ch = *ptr;
13892 : :
13893 [ + + ]: 469794 : if (ch == '"')
13894 : 89 : *optr++ = '"';
13895 : 469794 : *optr++ = ch;
13896 : : }
13897 : 66428 : *optr++ = '"';
13898 : 66428 : *optr = '\0';
13899 : :
13900 : 66428 : return result;
13901 : : }
13902 : :
13903 : : /*
13904 : : * quote_qualified_identifier - Quote a possibly-qualified identifier
13905 : : *
13906 : : * Return a name of the form qualifier.ident, or just ident if qualifier
13907 : : * is NULL, quoting each component if necessary. The result is palloc'd.
13908 : : */
13909 : : char *
13910 : 747732 : quote_qualified_identifier(const char *qualifier,
13911 : : const char *ident)
13912 : : {
13913 : : StringInfoData buf;
13914 : :
13915 : 747732 : initStringInfo(&buf);
13916 [ + + ]: 747732 : if (qualifier)
13917 : 260345 : appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
13918 : 747732 : appendStringInfoString(&buf, quote_identifier(ident));
13919 : 747732 : return buf.data;
13920 : : }
13921 : :
13922 : : /*
13923 : : * get_relation_name
13924 : : * Get the unqualified name of a relation specified by OID
13925 : : *
13926 : : * This differs from the underlying get_rel_name() function in that it will
13927 : : * throw error instead of silently returning NULL if the OID is bad.
13928 : : */
13929 : : static char *
13930 : 10743 : get_relation_name(Oid relid)
13931 : : {
13932 : 10743 : char *relname = get_rel_name(relid);
13933 : :
13934 [ - + ]: 10743 : if (!relname)
13935 [ # # ]: 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13936 : 10743 : return relname;
13937 : : }
13938 : :
13939 : : /*
13940 : : * generate_relation_name
13941 : : * Compute the name to display for a relation specified by OID
13942 : : *
13943 : : * The result includes all necessary quoting and schema-prefixing.
13944 : : *
13945 : : * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
13946 : : * We will forcibly qualify the relation name if it equals any CTE name
13947 : : * visible in the namespace list.
13948 : : */
13949 : : static char *
13950 : 5524 : generate_relation_name(Oid relid, List *namespaces)
13951 : : {
13952 : : HeapTuple tp;
13953 : : Form_pg_class reltup;
13954 : : bool need_qual;
13955 : : ListCell *nslist;
13956 : : char *relname;
13957 : : char *nspname;
13958 : : char *result;
13959 : :
13960 : 5524 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13961 [ - + ]: 5524 : if (!HeapTupleIsValid(tp))
13962 [ # # ]: 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13963 : 5524 : reltup = (Form_pg_class) GETSTRUCT(tp);
13964 : 5524 : relname = NameStr(reltup->relname);
13965 : :
13966 : : /* Check for conflicting CTE name */
13967 : 5524 : need_qual = false;
13968 [ + + + + : 9053 : foreach(nslist, namespaces)
+ + ]
13969 : : {
13970 : 3529 : deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
13971 : : ListCell *ctlist;
13972 : :
13973 [ + + + + : 3617 : foreach(ctlist, dpns->ctes)
+ + ]
13974 : : {
13975 : 88 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);
13976 : :
13977 [ - + ]: 88 : if (strcmp(cte->ctename, relname) == 0)
13978 : : {
13979 : 0 : need_qual = true;
13980 : 0 : break;
13981 : : }
13982 : : }
13983 [ - + ]: 3529 : if (need_qual)
13984 : 0 : break;
13985 : : }
13986 : :
13987 : : /* Otherwise, qualify the name if not visible in search path */
13988 [ + - ]: 5524 : if (!need_qual)
13989 : 5524 : need_qual = !RelationIsVisible(relid);
13990 : :
13991 [ + + ]: 5524 : if (need_qual)
13992 : 1657 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
13993 : : else
13994 : 3867 : nspname = NULL;
13995 : :
13996 : 5524 : result = quote_qualified_identifier(nspname, relname);
13997 : :
13998 : 5524 : ReleaseSysCache(tp);
13999 : :
14000 : 5524 : return result;
14001 : : }
14002 : :
14003 : : /*
14004 : : * generate_qualified_relation_name
14005 : : * Compute the name to display for a relation specified by OID
14006 : : *
14007 : : * As above, but unconditionally schema-qualify the name.
14008 : : */
14009 : : static char *
14010 : 4691 : generate_qualified_relation_name(Oid relid)
14011 : : {
14012 : : HeapTuple tp;
14013 : : Form_pg_class reltup;
14014 : : char *result;
14015 : :
14016 : 4691 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14017 [ - + ]: 4691 : if (!HeapTupleIsValid(tp))
14018 [ # # ]: 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
14019 : 4691 : reltup = (Form_pg_class) GETSTRUCT(tp);
14020 : :
14021 : 4691 : result = get_qualified_objname(reltup->relnamespace,
14022 : 4691 : NameStr(reltup->relname));
14023 : 4691 : ReleaseSysCache(tp);
14024 : :
14025 : 4691 : return result;
14026 : : }
14027 : :
14028 : : /*
14029 : : * generate_function_name
14030 : : * Compute the name to display for a function specified by OID,
14031 : : * given that it is being called with the specified actual arg names and
14032 : : * types. (Those matter because of ambiguous-function resolution rules.)
14033 : : *
14034 : : * If we're dealing with a potentially variadic function (in practice, this
14035 : : * means a FuncExpr or Aggref, not some other way of calling a function), then
14036 : : * has_variadic must specify whether variadic arguments have been merged,
14037 : : * and *use_variadic_p will be set to indicate whether to print VARIADIC in
14038 : : * the output. For non-FuncExpr cases, has_variadic should be false and
14039 : : * use_variadic_p can be NULL.
14040 : : *
14041 : : * inGroupBy must be true if we're deparsing a GROUP BY clause.
14042 : : *
14043 : : * The result includes all necessary quoting and schema-prefixing.
14044 : : */
14045 : : static char *
14046 : 9833 : generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
14047 : : bool has_variadic, bool *use_variadic_p,
14048 : : bool inGroupBy)
14049 : : {
14050 : : char *result;
14051 : : HeapTuple proctup;
14052 : : Form_pg_proc procform;
14053 : : char *proname;
14054 : : bool use_variadic;
14055 : : char *nspname;
14056 : : FuncDetailCode p_result;
14057 : : int fgc_flags;
14058 : : Oid p_funcid;
14059 : : Oid p_rettype;
14060 : : bool p_retset;
14061 : : int p_nvargs;
14062 : : Oid p_vatype;
14063 : : Oid *p_true_typeids;
14064 : 9833 : bool force_qualify = false;
14065 : :
14066 : 9833 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
14067 [ - + ]: 9833 : if (!HeapTupleIsValid(proctup))
14068 [ # # ]: 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
14069 : 9833 : procform = (Form_pg_proc) GETSTRUCT(proctup);
14070 : 9833 : proname = NameStr(procform->proname);
14071 : :
14072 : : /*
14073 : : * Due to parser hacks to avoid needing to reserve CUBE, we need to force
14074 : : * qualification of some function names within GROUP BY.
14075 : : */
14076 [ - + ]: 9833 : if (inGroupBy)
14077 : : {
14078 [ # # # # ]: 0 : if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
14079 : 0 : force_qualify = true;
14080 : : }
14081 : :
14082 : : /*
14083 : : * Determine whether VARIADIC should be printed. We must do this first
14084 : : * since it affects the lookup rules in func_get_detail().
14085 : : *
14086 : : * We always print VARIADIC if the function has a merged variadic-array
14087 : : * argument. Note that this is always the case for functions taking a
14088 : : * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
14089 : : * and printed the array elements as separate arguments, the call could
14090 : : * match a newer non-VARIADIC function.
14091 : : */
14092 [ + + ]: 9833 : if (use_variadic_p)
14093 : : {
14094 : : /* Parser should not have set funcvariadic unless fn is variadic */
14095 : : Assert(!has_variadic || OidIsValid(procform->provariadic));
14096 : 8822 : use_variadic = has_variadic;
14097 : 8822 : *use_variadic_p = use_variadic;
14098 : : }
14099 : : else
14100 : : {
14101 : : Assert(!has_variadic);
14102 : 1011 : use_variadic = false;
14103 : : }
14104 : :
14105 : : /*
14106 : : * The idea here is to schema-qualify only if the parser would fail to
14107 : : * resolve the correct function given the unqualified func name with the
14108 : : * specified argtypes and VARIADIC flag. But if we already decided to
14109 : : * force qualification, then we can skip the lookup and pretend we didn't
14110 : : * find it.
14111 : : */
14112 [ + - ]: 9833 : if (!force_qualify)
14113 : 9833 : p_result = func_get_detail(list_make1(makeString(proname)),
14114 : : NIL, argnames, nargs, argtypes,
14115 : : !use_variadic, true, false,
14116 : : &fgc_flags,
14117 : : &p_funcid, &p_rettype,
14118 : : &p_retset, &p_nvargs, &p_vatype,
14119 : 9833 : &p_true_typeids, NULL);
14120 : : else
14121 : : {
14122 : 0 : p_result = FUNCDETAIL_NOTFOUND;
14123 : 0 : p_funcid = InvalidOid;
14124 : : }
14125 : :
14126 [ + + + + ]: 9833 : if ((p_result == FUNCDETAIL_NORMAL ||
14127 [ + + ]: 684 : p_result == FUNCDETAIL_AGGREGATE ||
14128 : 9241 : p_result == FUNCDETAIL_WINDOWFUNC) &&
14129 [ + - ]: 9241 : p_funcid == funcid)
14130 : 9241 : nspname = NULL;
14131 : : else
14132 : 592 : nspname = get_namespace_name_or_temp(procform->pronamespace);
14133 : :
14134 : 9833 : result = quote_qualified_identifier(nspname, proname);
14135 : :
14136 : 9833 : ReleaseSysCache(proctup);
14137 : :
14138 : 9833 : return result;
14139 : : }
14140 : :
14141 : : /*
14142 : : * generate_operator_name
14143 : : * Compute the name to display for an operator specified by OID,
14144 : : * given that it is being called with the specified actual arg types.
14145 : : * (Arg types matter because of ambiguous-operator resolution rules.
14146 : : * Pass InvalidOid for unused arg of a unary operator.)
14147 : : *
14148 : : * The result includes all necessary quoting and schema-prefixing,
14149 : : * plus the OPERATOR() decoration needed to use a qualified operator name
14150 : : * in an expression.
14151 : : */
14152 : : static char *
14153 : 43906 : generate_operator_name(Oid operid, Oid arg1, Oid arg2)
14154 : : {
14155 : : StringInfoData buf;
14156 : : HeapTuple opertup;
14157 : : Form_pg_operator operform;
14158 : : char *oprname;
14159 : : char *nspname;
14160 : : Operator p_result;
14161 : :
14162 : 43906 : initStringInfo(&buf);
14163 : :
14164 : 43906 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
14165 [ - + ]: 43906 : if (!HeapTupleIsValid(opertup))
14166 [ # # ]: 0 : elog(ERROR, "cache lookup failed for operator %u", operid);
14167 : 43906 : operform = (Form_pg_operator) GETSTRUCT(opertup);
14168 : 43906 : oprname = NameStr(operform->oprname);
14169 : :
14170 : : /*
14171 : : * The idea here is to schema-qualify only if the parser would fail to
14172 : : * resolve the correct operator given the unqualified op name with the
14173 : : * specified argtypes.
14174 : : */
14175 [ + + - ]: 43906 : switch (operform->oprkind)
14176 : : {
14177 : 43886 : case 'b':
14178 : 43886 : p_result = oper(NULL, list_make1(makeString(oprname)), arg1, arg2,
14179 : : true, -1);
14180 : 43886 : break;
14181 : 20 : case 'l':
14182 : 20 : p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2,
14183 : : true, -1);
14184 : 20 : break;
14185 : 0 : default:
14186 [ # # ]: 0 : elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
14187 : : p_result = NULL; /* keep compiler quiet */
14188 : : break;
14189 : : }
14190 : :
14191 [ + + + - ]: 43906 : if (p_result != NULL && oprid(p_result) == operid)
14192 : 43901 : nspname = NULL;
14193 : : else
14194 : : {
14195 : 5 : nspname = get_namespace_name_or_temp(operform->oprnamespace);
14196 : 5 : appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
14197 : : }
14198 : :
14199 : 43906 : appendStringInfoString(&buf, oprname);
14200 : :
14201 [ + + ]: 43906 : if (nspname)
14202 : 5 : appendStringInfoChar(&buf, ')');
14203 : :
14204 [ + + ]: 43906 : if (p_result != NULL)
14205 : 43901 : ReleaseSysCache(p_result);
14206 : :
14207 : 43906 : ReleaseSysCache(opertup);
14208 : :
14209 : 43906 : return buf.data;
14210 : : }
14211 : :
14212 : : /*
14213 : : * generate_operator_clause --- generate a binary-operator WHERE clause
14214 : : *
14215 : : * This is used for internally-generated-and-executed SQL queries, where
14216 : : * precision is essential and readability is secondary. The basic
14217 : : * requirement is to append "leftop op rightop" to buf, where leftop and
14218 : : * rightop are given as strings and are assumed to yield types leftoptype
14219 : : * and rightoptype; the operator is identified by OID. The complexity
14220 : : * comes from needing to be sure that the parser will select the desired
14221 : : * operator when the query is parsed. We always name the operator using
14222 : : * OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
14223 : : * We have to emit casts too, if either input isn't already the input type
14224 : : * of the operator; else we are at the mercy of the parser's heuristics for
14225 : : * ambiguous-operator resolution. The caller must ensure that leftop and
14226 : : * rightop are suitable arguments for a cast operation; it's best to insert
14227 : : * parentheses if they aren't just variables or parameters.
14228 : : */
14229 : : void
14230 : 3194 : generate_operator_clause(StringInfo buf,
14231 : : const char *leftop, Oid leftoptype,
14232 : : Oid opoid,
14233 : : const char *rightop, Oid rightoptype)
14234 : : {
14235 : : HeapTuple opertup;
14236 : : Form_pg_operator operform;
14237 : : char *oprname;
14238 : : char *nspname;
14239 : :
14240 : 3194 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
14241 [ - + ]: 3194 : if (!HeapTupleIsValid(opertup))
14242 [ # # ]: 0 : elog(ERROR, "cache lookup failed for operator %u", opoid);
14243 : 3194 : operform = (Form_pg_operator) GETSTRUCT(opertup);
14244 : : Assert(operform->oprkind == 'b');
14245 : 3194 : oprname = NameStr(operform->oprname);
14246 : :
14247 : 3194 : nspname = get_namespace_name(operform->oprnamespace);
14248 : :
14249 : 3194 : appendStringInfoString(buf, leftop);
14250 [ + + ]: 3194 : if (leftoptype != operform->oprleft)
14251 : 791 : add_cast_to(buf, operform->oprleft);
14252 : 3194 : appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
14253 : 3194 : appendStringInfoString(buf, oprname);
14254 : 3194 : appendStringInfo(buf, ") %s", rightop);
14255 [ + + ]: 3194 : if (rightoptype != operform->oprright)
14256 : 633 : add_cast_to(buf, operform->oprright);
14257 : :
14258 : 3194 : ReleaseSysCache(opertup);
14259 : 3194 : }
14260 : :
14261 : : /*
14262 : : * Add a cast specification to buf. We spell out the type name the hard way,
14263 : : * intentionally not using format_type_be(). This is to avoid corner cases
14264 : : * for CHARACTER, BIT, and perhaps other types, where specifying the type
14265 : : * using SQL-standard syntax results in undesirable data truncation. By
14266 : : * doing it this way we can be certain that the cast will have default (-1)
14267 : : * target typmod.
14268 : : */
14269 : : static void
14270 : 1424 : add_cast_to(StringInfo buf, Oid typid)
14271 : : {
14272 : : HeapTuple typetup;
14273 : : Form_pg_type typform;
14274 : : char *typname;
14275 : : char *nspname;
14276 : :
14277 : 1424 : typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
14278 [ - + ]: 1424 : if (!HeapTupleIsValid(typetup))
14279 [ # # ]: 0 : elog(ERROR, "cache lookup failed for type %u", typid);
14280 : 1424 : typform = (Form_pg_type) GETSTRUCT(typetup);
14281 : :
14282 : 1424 : typname = NameStr(typform->typname);
14283 : 1424 : nspname = get_namespace_name_or_temp(typform->typnamespace);
14284 : :
14285 : 1424 : appendStringInfo(buf, "::%s.%s",
14286 : : quote_identifier(nspname), quote_identifier(typname));
14287 : :
14288 : 1424 : ReleaseSysCache(typetup);
14289 : 1424 : }
14290 : :
14291 : : /*
14292 : : * generate_qualified_type_name
14293 : : * Compute the name to display for a type specified by OID
14294 : : *
14295 : : * This is different from format_type_be() in that we unconditionally
14296 : : * schema-qualify the name. That also means no special syntax for
14297 : : * SQL-standard type names ... although in current usage, this should
14298 : : * only get used for domains, so such cases wouldn't occur anyway.
14299 : : */
14300 : : static char *
14301 : 13 : generate_qualified_type_name(Oid typid)
14302 : : {
14303 : : HeapTuple tp;
14304 : : Form_pg_type typtup;
14305 : : char *typname;
14306 : : char *nspname;
14307 : : char *result;
14308 : :
14309 : 13 : tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
14310 [ - + ]: 13 : if (!HeapTupleIsValid(tp))
14311 [ # # ]: 0 : elog(ERROR, "cache lookup failed for type %u", typid);
14312 : 13 : typtup = (Form_pg_type) GETSTRUCT(tp);
14313 : 13 : typname = NameStr(typtup->typname);
14314 : :
14315 : 13 : nspname = get_namespace_name_or_temp(typtup->typnamespace);
14316 [ - + ]: 13 : if (!nspname)
14317 [ # # ]: 0 : elog(ERROR, "cache lookup failed for namespace %u",
14318 : : typtup->typnamespace);
14319 : :
14320 : 13 : result = quote_qualified_identifier(nspname, typname);
14321 : :
14322 : 13 : ReleaseSysCache(tp);
14323 : :
14324 : 13 : return result;
14325 : : }
14326 : :
14327 : : /*
14328 : : * generate_collation_name
14329 : : * Compute the name to display for a collation specified by OID
14330 : : *
14331 : : * The result includes all necessary quoting and schema-prefixing.
14332 : : */
14333 : : char *
14334 : 368 : generate_collation_name(Oid collid)
14335 : : {
14336 : : HeapTuple tp;
14337 : : Form_pg_collation colltup;
14338 : : char *collname;
14339 : : char *nspname;
14340 : : char *result;
14341 : :
14342 : 368 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
14343 [ - + ]: 368 : if (!HeapTupleIsValid(tp))
14344 [ # # ]: 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
14345 : 368 : colltup = (Form_pg_collation) GETSTRUCT(tp);
14346 : 368 : collname = NameStr(colltup->collname);
14347 : :
14348 [ - + ]: 368 : if (!CollationIsVisible(collid))
14349 : 0 : nspname = get_namespace_name_or_temp(colltup->collnamespace);
14350 : : else
14351 : 368 : nspname = NULL;
14352 : :
14353 : 368 : result = quote_qualified_identifier(nspname, collname);
14354 : :
14355 : 368 : ReleaseSysCache(tp);
14356 : :
14357 : 368 : return result;
14358 : : }
14359 : :
14360 : : /*
14361 : : * Given a C string, produce a TEXT datum.
14362 : : *
14363 : : * We assume that the input was palloc'd and may be freed.
14364 : : */
14365 : : static text *
14366 : 26913 : string_to_text(char *str)
14367 : : {
14368 : : text *result;
14369 : :
14370 : 26913 : result = cstring_to_text(str);
14371 : 26913 : pfree(str);
14372 : 26913 : return result;
14373 : : }
14374 : :
14375 : : /*
14376 : : * Generate a C string representing a relation options from text[] datum.
14377 : : */
14378 : : void
14379 : 131 : get_reloptions(StringInfo buf, Datum reloptions)
14380 : : {
14381 : : Datum *options;
14382 : : int noptions;
14383 : : int i;
14384 : :
14385 : 131 : deconstruct_array_builtin(DatumGetArrayTypeP(reloptions), TEXTOID,
14386 : : &options, NULL, &noptions);
14387 : :
14388 [ + + ]: 284 : for (i = 0; i < noptions; i++)
14389 : : {
14390 : 153 : char *option = TextDatumGetCString(options[i]);
14391 : : char *name;
14392 : : char *separator;
14393 : : char *value;
14394 : :
14395 : : /*
14396 : : * Each array element should have the form name=value. If the "=" is
14397 : : * missing for some reason, treat it like an empty value.
14398 : : */
14399 : 153 : name = option;
14400 : 153 : separator = strchr(option, '=');
14401 [ + - ]: 153 : if (separator)
14402 : : {
14403 : 153 : *separator = '\0';
14404 : 153 : value = separator + 1;
14405 : : }
14406 : : else
14407 : 0 : value = "";
14408 : :
14409 [ + + ]: 153 : if (i > 0)
14410 : 22 : appendStringInfoString(buf, ", ");
14411 : 153 : appendStringInfo(buf, "%s=", quote_identifier(name));
14412 : :
14413 : : /*
14414 : : * In general we need to quote the value; but to avoid unnecessary
14415 : : * clutter, do not quote if it is an identifier that would not need
14416 : : * quoting. (We could also allow numbers, but that is a bit trickier
14417 : : * than it looks --- for example, are leading zeroes significant? We
14418 : : * don't want to assume very much here about what custom reloptions
14419 : : * might mean.)
14420 : : */
14421 [ + + ]: 153 : if (quote_identifier(value) == value)
14422 : 4 : appendStringInfoString(buf, value);
14423 : : else
14424 : 149 : simple_quote_literal(buf, value);
14425 : :
14426 : 153 : pfree(option);
14427 : : }
14428 : 131 : }
14429 : :
14430 : : /*
14431 : : * Generate a C string representing a relation's reloptions, or NULL if none.
14432 : : */
14433 : : static char *
14434 : 4621 : flatten_reloptions(Oid relid)
14435 : : {
14436 : 4621 : char *result = NULL;
14437 : : HeapTuple tuple;
14438 : : Datum reloptions;
14439 : : bool isnull;
14440 : :
14441 : 4621 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14442 [ - + ]: 4621 : if (!HeapTupleIsValid(tuple))
14443 [ # # ]: 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
14444 : :
14445 : 4621 : reloptions = SysCacheGetAttr(RELOID, tuple,
14446 : : Anum_pg_class_reloptions, &isnull);
14447 [ + + ]: 4621 : if (!isnull)
14448 : : {
14449 : : StringInfoData buf;
14450 : :
14451 : 105 : initStringInfo(&buf);
14452 : 105 : get_reloptions(&buf, reloptions);
14453 : :
14454 : 105 : result = buf.data;
14455 : : }
14456 : :
14457 : 4621 : ReleaseSysCache(tuple);
14458 : :
14459 : 4621 : return result;
14460 : : }
14461 : :
14462 : : /*
14463 : : * get_range_partbound_string
14464 : : * A C string representation of one range partition bound
14465 : : */
14466 : : char *
14467 : 3162 : get_range_partbound_string(List *bound_datums)
14468 : : {
14469 : : deparse_context context;
14470 : : StringInfoData buf;
14471 : : ListCell *cell;
14472 : : char *sep;
14473 : :
14474 : 3162 : initStringInfo(&buf);
14475 : 3162 : memset(&context, 0, sizeof(deparse_context));
14476 : 3162 : context.buf = &buf;
14477 : :
14478 : 3162 : appendStringInfoChar(&buf, '(');
14479 : 3162 : sep = "";
14480 [ + - + + : 6802 : foreach(cell, bound_datums)
+ + ]
14481 : : {
14482 : 3640 : PartitionRangeDatum *datum =
14483 : : lfirst_node(PartitionRangeDatum, cell);
14484 : :
14485 : 3640 : appendStringInfoString(&buf, sep);
14486 [ + + ]: 3640 : if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
14487 : 148 : appendStringInfoString(&buf, "MINVALUE");
14488 [ + + ]: 3492 : else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
14489 : 80 : appendStringInfoString(&buf, "MAXVALUE");
14490 : : else
14491 : : {
14492 : 3412 : Const *val = castNode(Const, datum->value);
14493 : :
14494 : 3412 : get_const_expr(val, &context, -1);
14495 : : }
14496 : 3640 : sep = ", ";
14497 : : }
14498 : 3162 : appendStringInfoChar(&buf, ')');
14499 : :
14500 : 3162 : return buf.data;
14501 : : }
|