Age Owner Branch data TLA 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-2025, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : *
11 : : * IDENTIFICATION
12 : : * src/backend/utils/adt/ruleutils.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : : #include "postgres.h"
17 : :
18 : : #include <ctype.h>
19 : : #include <unistd.h>
20 : : #include <fcntl.h>
21 : :
22 : : #include "access/amapi.h"
23 : : #include "access/htup_details.h"
24 : : #include "access/relation.h"
25 : : #include "access/table.h"
26 : : #include "catalog/pg_aggregate.h"
27 : : #include "catalog/pg_am.h"
28 : : #include "catalog/pg_authid.h"
29 : : #include "catalog/pg_collation.h"
30 : : #include "catalog/pg_constraint.h"
31 : : #include "catalog/pg_depend.h"
32 : : #include "catalog/pg_language.h"
33 : : #include "catalog/pg_opclass.h"
34 : : #include "catalog/pg_operator.h"
35 : : #include "catalog/pg_partitioned_table.h"
36 : : #include "catalog/pg_proc.h"
37 : : #include "catalog/pg_statistic_ext.h"
38 : : #include "catalog/pg_trigger.h"
39 : : #include "catalog/pg_type.h"
40 : : #include "commands/defrem.h"
41 : : #include "commands/tablespace.h"
42 : : #include "common/keywords.h"
43 : : #include "executor/spi.h"
44 : : #include "funcapi.h"
45 : : #include "mb/pg_wchar.h"
46 : : #include "miscadmin.h"
47 : : #include "nodes/makefuncs.h"
48 : : #include "nodes/nodeFuncs.h"
49 : : #include "nodes/pathnodes.h"
50 : : #include "optimizer/optimizer.h"
51 : : #include "parser/parse_agg.h"
52 : : #include "parser/parse_func.h"
53 : : #include "parser/parse_oper.h"
54 : : #include "parser/parse_relation.h"
55 : : #include "parser/parser.h"
56 : : #include "parser/parsetree.h"
57 : : #include "rewrite/rewriteHandler.h"
58 : : #include "rewrite/rewriteManip.h"
59 : : #include "rewrite/rewriteSupport.h"
60 : : #include "utils/array.h"
61 : : #include "utils/builtins.h"
62 : : #include "utils/fmgroids.h"
63 : : #include "utils/guc.h"
64 : : #include "utils/hsearch.h"
65 : : #include "utils/lsyscache.h"
66 : : #include "utils/partcache.h"
67 : : #include "utils/rel.h"
68 : : #include "utils/ruleutils.h"
69 : : #include "utils/snapmgr.h"
70 : : #include "utils/syscache.h"
71 : : #include "utils/typcache.h"
72 : : #include "utils/varlena.h"
73 : : #include "utils/xml.h"
74 : :
75 : : /* ----------
76 : : * Pretty formatting constants
77 : : * ----------
78 : : */
79 : :
80 : : /* Indent counts */
81 : : #define PRETTYINDENT_STD 8
82 : : #define PRETTYINDENT_JOIN 4
83 : : #define PRETTYINDENT_VAR 4
84 : :
85 : : #define PRETTYINDENT_LIMIT 40 /* wrap limit */
86 : :
87 : : /* Pretty flags */
88 : : #define PRETTYFLAG_PAREN 0x0001
89 : : #define PRETTYFLAG_INDENT 0x0002
90 : : #define PRETTYFLAG_SCHEMA 0x0004
91 : :
92 : : /* Standard conversion of a "bool pretty" option to detailed flags */
93 : : #define GET_PRETTY_FLAGS(pretty) \
94 : : ((pretty) ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) \
95 : : : PRETTYFLAG_INDENT)
96 : :
97 : : /* Default line length for pretty-print wrapping: 0 means wrap always */
98 : : #define WRAP_COLUMN_DEFAULT 0
99 : :
100 : : /* macros to test if pretty action needed */
101 : : #define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
102 : : #define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
103 : : #define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
104 : :
105 : :
106 : : /* ----------
107 : : * Local data types
108 : : * ----------
109 : : */
110 : :
111 : : /* Context info needed for invoking a recursive querytree display routine */
112 : : typedef struct
113 : : {
114 : : StringInfo buf; /* output buffer to append to */
115 : : List *namespaces; /* List of deparse_namespace nodes */
116 : : TupleDesc resultDesc; /* if top level of a view, the view's tupdesc */
117 : : List *targetList; /* Current query level's SELECT targetlist */
118 : : List *windowClause; /* Current query level's WINDOW clause */
119 : : int prettyFlags; /* enabling of pretty-print functions */
120 : : int wrapColumn; /* max line length, or -1 for no limit */
121 : : int indentLevel; /* current indent level for pretty-print */
122 : : bool varprefix; /* true to print prefixes on Vars */
123 : : bool colNamesVisible; /* do we care about output column names? */
124 : : bool inGroupBy; /* deparsing GROUP BY clause? */
125 : : bool varInOrderBy; /* deparsing simple Var in ORDER BY? */
126 : : Bitmapset *appendparents; /* if not null, map child Vars of these relids
127 : : * back to the parent rel */
128 : : } deparse_context;
129 : :
130 : : /*
131 : : * Each level of query context around a subtree needs a level of Var namespace.
132 : : * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
133 : : * the current context's namespaces list.
134 : : *
135 : : * rtable is the list of actual RTEs from the Query or PlannedStmt.
136 : : * rtable_names holds the alias name to be used for each RTE (either a C
137 : : * string, or NULL for nameless RTEs such as unnamed joins).
138 : : * rtable_columns holds the column alias names to be used for each RTE.
139 : : *
140 : : * subplans is a list of Plan trees for SubPlans and CTEs (it's only used
141 : : * in the PlannedStmt case).
142 : : * ctes is a list of CommonTableExpr nodes (only used in the Query case).
143 : : * appendrels, if not null (it's only used in the PlannedStmt case), is an
144 : : * array of AppendRelInfo nodes, indexed by child relid. We use that to map
145 : : * child-table Vars to their inheritance parents.
146 : : *
147 : : * In some cases we need to make names of merged JOIN USING columns unique
148 : : * across the whole query, not only per-RTE. If so, unique_using is true
149 : : * and using_names is a list of C strings representing names already assigned
150 : : * to USING columns.
151 : : *
152 : : * When deparsing plan trees, there is always just a single item in the
153 : : * deparse_namespace list (since a plan tree never contains Vars with
154 : : * varlevelsup > 0). We store the Plan node that is the immediate
155 : : * parent of the expression to be deparsed, as well as a list of that
156 : : * Plan's ancestors. In addition, we store its outer and inner subplan nodes,
157 : : * as well as their targetlists, and the index tlist if the current plan node
158 : : * might contain INDEX_VAR Vars. (These fields could be derived on-the-fly
159 : : * from the current Plan node, but it seems notationally clearer to set them
160 : : * up as separate fields.)
161 : : */
162 : : typedef struct
163 : : {
164 : : List *rtable; /* List of RangeTblEntry nodes */
165 : : List *rtable_names; /* Parallel list of names for RTEs */
166 : : List *rtable_columns; /* Parallel list of deparse_columns structs */
167 : : List *subplans; /* List of Plan trees for SubPlans */
168 : : List *ctes; /* List of CommonTableExpr nodes */
169 : : AppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */
170 : : char *ret_old_alias; /* alias for OLD in RETURNING list */
171 : : char *ret_new_alias; /* alias for NEW in RETURNING list */
172 : : /* Workspace for column alias assignment: */
173 : : bool unique_using; /* Are we making USING names globally unique */
174 : : List *using_names; /* List of assigned names for USING columns */
175 : : /* Remaining fields are used only when deparsing a Plan tree: */
176 : : Plan *plan; /* immediate parent of current expression */
177 : : List *ancestors; /* ancestors of plan */
178 : : Plan *outer_plan; /* outer subnode, or NULL if none */
179 : : Plan *inner_plan; /* inner subnode, or NULL if none */
180 : : List *outer_tlist; /* referent for OUTER_VAR Vars */
181 : : List *inner_tlist; /* referent for INNER_VAR Vars */
182 : : List *index_tlist; /* referent for INDEX_VAR Vars */
183 : : /* Special namespace representing a function signature: */
184 : : char *funcname;
185 : : int numargs;
186 : : char **argnames;
187 : : } deparse_namespace;
188 : :
189 : : /*
190 : : * Per-relation data about column alias names.
191 : : *
192 : : * Selecting aliases is unreasonably complicated because of the need to dump
193 : : * rules/views whose underlying tables may have had columns added, deleted, or
194 : : * renamed since the query was parsed. We must nonetheless print the rule/view
195 : : * in a form that can be reloaded and will produce the same results as before.
196 : : *
197 : : * For each RTE used in the query, we must assign column aliases that are
198 : : * unique within that RTE. SQL does not require this of the original query,
199 : : * but due to factors such as *-expansion we need to be able to uniquely
200 : : * reference every column in a decompiled query. As long as we qualify all
201 : : * column references, per-RTE uniqueness is sufficient for that.
202 : : *
203 : : * However, we can't ensure per-column name uniqueness for unnamed join RTEs,
204 : : * since they just inherit column names from their input RTEs, and we can't
205 : : * rename the columns at the join level. Most of the time this isn't an issue
206 : : * because we don't need to reference the join's output columns as such; we
207 : : * can reference the input columns instead. That approach can fail for merged
208 : : * JOIN USING columns, however, so when we have one of those in an unnamed
209 : : * join, we have to make that column's alias globally unique across the whole
210 : : * query to ensure it can be referenced unambiguously.
211 : : *
212 : : * Another problem is that a JOIN USING clause requires the columns to be
213 : : * merged to have the same aliases in both input RTEs, and that no other
214 : : * columns in those RTEs or their children conflict with the USING names.
215 : : * To handle that, we do USING-column alias assignment in a recursive
216 : : * traversal of the query's jointree. When descending through a JOIN with
217 : : * USING, we preassign the USING column names to the child columns, overriding
218 : : * other rules for column alias assignment. We also mark each RTE with a list
219 : : * of all USING column names selected for joins containing that RTE, so that
220 : : * when we assign other columns' aliases later, we can avoid conflicts.
221 : : *
222 : : * Another problem is that if a JOIN's input tables have had columns added or
223 : : * deleted since the query was parsed, we must generate a column alias list
224 : : * for the join that matches the current set of input columns --- otherwise, a
225 : : * change in the number of columns in the left input would throw off matching
226 : : * of aliases to columns of the right input. Thus, positions in the printable
227 : : * column alias list are not necessarily one-for-one with varattnos of the
228 : : * JOIN, so we need a separate new_colnames[] array for printing purposes.
229 : : *
230 : : * Finally, when dealing with wide tables we risk O(N^2) costs in assigning
231 : : * non-duplicate column names. We ameliorate that by using a hash table that
232 : : * holds all the strings appearing in colnames, new_colnames, and parentUsing.
233 : : */
234 : : typedef struct
235 : : {
236 : : /*
237 : : * colnames is an array containing column aliases to use for columns that
238 : : * existed when the query was parsed. Dropped columns have NULL entries.
239 : : * This array can be directly indexed by varattno to get a Var's name.
240 : : *
241 : : * Non-NULL entries are guaranteed unique within the RTE, *except* when
242 : : * this is for an unnamed JOIN RTE. In that case we merely copy up names
243 : : * from the two input RTEs.
244 : : *
245 : : * During the recursive descent in set_using_names(), forcible assignment
246 : : * of a child RTE's column name is represented by pre-setting that element
247 : : * of the child's colnames array. So at that stage, NULL entries in this
248 : : * array just mean that no name has been preassigned, not necessarily that
249 : : * the column is dropped.
250 : : */
251 : : int num_cols; /* length of colnames[] array */
252 : : char **colnames; /* array of C strings and NULLs */
253 : :
254 : : /*
255 : : * new_colnames is an array containing column aliases to use for columns
256 : : * that would exist if the query was re-parsed against the current
257 : : * definitions of its base tables. This is what to print as the column
258 : : * alias list for the RTE. This array does not include dropped columns,
259 : : * but it will include columns added since original parsing. Indexes in
260 : : * it therefore have little to do with current varattno values. As above,
261 : : * entries are unique unless this is for an unnamed JOIN RTE. (In such an
262 : : * RTE, we never actually print this array, but we must compute it anyway
263 : : * for possible use in computing column names of upper joins.) The
264 : : * parallel array is_new_col marks which of these columns are new since
265 : : * original parsing. Entries with is_new_col false must match the
266 : : * non-NULL colnames entries one-for-one.
267 : : */
268 : : int num_new_cols; /* length of new_colnames[] array */
269 : : char **new_colnames; /* array of C strings */
270 : : bool *is_new_col; /* array of bool flags */
271 : :
272 : : /* This flag tells whether we should actually print a column alias list */
273 : : bool printaliases;
274 : :
275 : : /* This list has all names used as USING names in joins above this RTE */
276 : : List *parentUsing; /* names assigned to parent merged columns */
277 : :
278 : : /*
279 : : * If this struct is for a JOIN RTE, we fill these fields during the
280 : : * set_using_names() pass to describe its relationship to its child RTEs.
281 : : *
282 : : * leftattnos and rightattnos are arrays with one entry per existing
283 : : * output column of the join (hence, indexable by join varattno). For a
284 : : * simple reference to a column of the left child, leftattnos[i] is the
285 : : * child RTE's attno and rightattnos[i] is zero; and conversely for a
286 : : * column of the right child. But for merged columns produced by JOIN
287 : : * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.
288 : : * Note that a simple reference might be to a child RTE column that's been
289 : : * dropped; but that's OK since the column could not be used in the query.
290 : : *
291 : : * If it's a JOIN USING, usingNames holds the alias names selected for the
292 : : * merged columns (these might be different from the original USING list,
293 : : * if we had to modify names to achieve uniqueness).
294 : : */
295 : : int leftrti; /* rangetable index of left child */
296 : : int rightrti; /* rangetable index of right child */
297 : : int *leftattnos; /* left-child varattnos of join cols, or 0 */
298 : : int *rightattnos; /* right-child varattnos of join cols, or 0 */
299 : : List *usingNames; /* names assigned to merged columns */
300 : :
301 : : /*
302 : : * Hash table holding copies of all the strings appearing in this struct's
303 : : * colnames, new_colnames, and parentUsing. We use a hash table only for
304 : : * sufficiently wide relations, and only during the colname-assignment
305 : : * functions set_relation_column_names and set_join_column_names;
306 : : * otherwise, names_hash is NULL.
307 : : */
308 : : HTAB *names_hash; /* entries are just strings */
309 : : } deparse_columns;
310 : :
311 : : /* This macro is analogous to rt_fetch(), but for deparse_columns structs */
312 : : #define deparse_columns_fetch(rangetable_index, dpns) \
313 : : ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
314 : :
315 : : /*
316 : : * Entry in set_rtable_names' hash table
317 : : */
318 : : typedef struct
319 : : {
320 : : char name[NAMEDATALEN]; /* Hash key --- must be first */
321 : : int counter; /* Largest addition used so far for name */
322 : : } NameHashEntry;
323 : :
324 : : /* Callback signature for resolve_special_varno() */
325 : : typedef void (*rsv_callback) (Node *node, deparse_context *context,
326 : : void *callback_arg);
327 : :
328 : :
329 : : /* ----------
330 : : * Global data
331 : : * ----------
332 : : */
333 : : static SPIPlanPtr plan_getrulebyoid = NULL;
334 : : static const char *const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
335 : : static SPIPlanPtr plan_getviewrule = NULL;
336 : : static const char *const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
337 : :
338 : : /* GUC parameters */
339 : : bool quote_all_identifiers = false;
340 : :
341 : :
342 : : /* ----------
343 : : * Local functions
344 : : *
345 : : * Most of these functions used to use fixed-size buffers to build their
346 : : * results. Now, they take an (already initialized) StringInfo object
347 : : * as a parameter, and append their text output to its contents.
348 : : * ----------
349 : : */
350 : : static char *deparse_expression_pretty(Node *expr, List *dpcontext,
351 : : bool forceprefix, bool showimplicit,
352 : : int prettyFlags, int startIndent);
353 : : static char *pg_get_viewdef_worker(Oid viewoid,
354 : : int prettyFlags, int wrapColumn);
355 : : static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
356 : : static int decompile_column_index_array(Datum column_index_array, Oid relId,
357 : : bool withPeriod, StringInfo buf);
358 : : static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
359 : : static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
360 : : const Oid *excludeOps,
361 : : bool attrsOnly, bool keysOnly,
362 : : bool showTblSpc, bool inherits,
363 : : int prettyFlags, bool missing_ok);
364 : : static char *pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
365 : : bool missing_ok);
366 : : static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
367 : : bool attrsOnly, bool missing_ok);
368 : : static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
369 : : int prettyFlags, bool missing_ok);
370 : : static text *pg_get_expr_worker(text *expr, Oid relid, int prettyFlags);
371 : : static int print_function_arguments(StringInfo buf, HeapTuple proctup,
372 : : bool print_table_args, bool print_defaults);
373 : : static void print_function_rettype(StringInfo buf, HeapTuple proctup);
374 : : static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
375 : : static void print_function_sqlbody(StringInfo buf, HeapTuple proctup);
376 : : static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
377 : : Bitmapset *rels_used);
378 : : static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
379 : : List *parent_namespaces);
380 : : static void set_simple_column_names(deparse_namespace *dpns);
381 : : static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
382 : : static void set_using_names(deparse_namespace *dpns, Node *jtnode,
383 : : List *parentUsing);
384 : : static void set_relation_column_names(deparse_namespace *dpns,
385 : : RangeTblEntry *rte,
386 : : deparse_columns *colinfo);
387 : : static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
388 : : deparse_columns *colinfo);
389 : : static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
390 : : deparse_columns *colinfo);
391 : : static char *make_colname_unique(char *colname, deparse_namespace *dpns,
392 : : deparse_columns *colinfo);
393 : : static void expand_colnames_array_to(deparse_columns *colinfo, int n);
394 : : static void build_colinfo_names_hash(deparse_columns *colinfo);
395 : : static void add_to_names_hash(deparse_columns *colinfo, const char *name);
396 : : static void destroy_colinfo_names_hash(deparse_columns *colinfo);
397 : : static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
398 : : deparse_columns *colinfo);
399 : : static char *get_rtable_name(int rtindex, deparse_context *context);
400 : : static void set_deparse_plan(deparse_namespace *dpns, Plan *plan);
401 : : static Plan *find_recursive_union(deparse_namespace *dpns,
402 : : WorkTableScan *wtscan);
403 : : static void push_child_plan(deparse_namespace *dpns, Plan *plan,
404 : : deparse_namespace *save_dpns);
405 : : static void pop_child_plan(deparse_namespace *dpns,
406 : : deparse_namespace *save_dpns);
407 : : static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
408 : : deparse_namespace *save_dpns);
409 : : static void pop_ancestor_plan(deparse_namespace *dpns,
410 : : deparse_namespace *save_dpns);
411 : : static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
412 : : int prettyFlags);
413 : : static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
414 : : int prettyFlags, int wrapColumn);
415 : : static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
416 : : TupleDesc resultDesc, bool colNamesVisible,
417 : : int prettyFlags, int wrapColumn, int startIndent);
418 : : static void get_values_def(List *values_lists, deparse_context *context);
419 : : static void get_with_clause(Query *query, deparse_context *context);
420 : : static void get_select_query_def(Query *query, deparse_context *context);
421 : : static void get_insert_query_def(Query *query, deparse_context *context);
422 : : static void get_update_query_def(Query *query, deparse_context *context);
423 : : static void get_update_query_targetlist_def(Query *query, List *targetList,
424 : : deparse_context *context,
425 : : RangeTblEntry *rte);
426 : : static void get_delete_query_def(Query *query, deparse_context *context);
427 : : static void get_merge_query_def(Query *query, deparse_context *context);
428 : : static void get_utility_query_def(Query *query, deparse_context *context);
429 : : static void get_basic_select_query(Query *query, deparse_context *context);
430 : : static void get_target_list(List *targetList, deparse_context *context);
431 : : static void get_returning_clause(Query *query, deparse_context *context);
432 : : static void get_setop_query(Node *setOp, Query *query,
433 : : deparse_context *context);
434 : : static Node *get_rule_sortgroupclause(Index ref, List *tlist,
435 : : bool force_colno,
436 : : deparse_context *context);
437 : : static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
438 : : bool omit_parens, deparse_context *context);
439 : : static void get_rule_orderby(List *orderList, List *targetList,
440 : : bool force_colno, deparse_context *context);
441 : : static void get_rule_windowclause(Query *query, deparse_context *context);
442 : : static void get_rule_windowspec(WindowClause *wc, List *targetList,
443 : : deparse_context *context);
444 : : static void get_window_frame_options(int frameOptions,
445 : : Node *startOffset, Node *endOffset,
446 : : deparse_context *context);
447 : : static char *get_variable(Var *var, int levelsup, bool istoplevel,
448 : : deparse_context *context);
449 : : static void get_special_variable(Node *node, deparse_context *context,
450 : : void *callback_arg);
451 : : static void resolve_special_varno(Node *node, deparse_context *context,
452 : : rsv_callback callback, void *callback_arg);
453 : : static Node *find_param_referent(Param *param, deparse_context *context,
454 : : deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
455 : : static SubPlan *find_param_generator(Param *param, deparse_context *context,
456 : : int *column_p);
457 : : static SubPlan *find_param_generator_initplan(Param *param, Plan *plan,
458 : : int *column_p);
459 : : static void get_parameter(Param *param, deparse_context *context);
460 : : static const char *get_simple_binary_op_name(OpExpr *expr);
461 : : static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
462 : : static void appendContextKeyword(deparse_context *context, const char *str,
463 : : int indentBefore, int indentAfter, int indentPlus);
464 : : static void removeStringInfoSpaces(StringInfo str);
465 : : static void get_rule_expr(Node *node, deparse_context *context,
466 : : bool showimplicit);
467 : : static void get_rule_expr_toplevel(Node *node, deparse_context *context,
468 : : bool showimplicit);
469 : : static void get_rule_list_toplevel(List *lst, deparse_context *context,
470 : : bool showimplicit);
471 : : static void get_rule_expr_funccall(Node *node, deparse_context *context,
472 : : bool showimplicit);
473 : : static bool looks_like_function(Node *node);
474 : : static void get_oper_expr(OpExpr *expr, deparse_context *context);
475 : : static void get_func_expr(FuncExpr *expr, deparse_context *context,
476 : : bool showimplicit);
477 : : static void get_agg_expr(Aggref *aggref, deparse_context *context,
478 : : Aggref *original_aggref);
479 : : static void get_agg_expr_helper(Aggref *aggref, deparse_context *context,
480 : : Aggref *original_aggref, const char *funcname,
481 : : const char *options, bool is_json_objectagg);
482 : : static void get_agg_combine_expr(Node *node, deparse_context *context,
483 : : void *callback_arg);
484 : : static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
485 : : static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
486 : : const char *funcname, const char *options,
487 : : bool is_json_objectagg);
488 : : static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
489 : : static void get_coercion_expr(Node *arg, deparse_context *context,
490 : : Oid resulttype, int32 resulttypmod,
491 : : Node *parentNode);
492 : : static void get_const_expr(Const *constval, deparse_context *context,
493 : : int showtype);
494 : : static void get_const_collation(Const *constval, deparse_context *context);
495 : : static void get_json_format(JsonFormat *format, StringInfo buf);
496 : : static void get_json_returning(JsonReturning *returning, StringInfo buf,
497 : : bool json_format_by_default);
498 : : static void get_json_constructor(JsonConstructorExpr *ctor,
499 : : deparse_context *context, bool showimplicit);
500 : : static void get_json_constructor_options(JsonConstructorExpr *ctor,
501 : : StringInfo buf);
502 : : static void get_json_agg_constructor(JsonConstructorExpr *ctor,
503 : : deparse_context *context,
504 : : const char *funcname,
505 : : bool is_json_objectagg);
506 : : static void simple_quote_literal(StringInfo buf, const char *val);
507 : : static void get_sublink_expr(SubLink *sublink, deparse_context *context);
508 : : static void get_tablefunc(TableFunc *tf, deparse_context *context,
509 : : bool showimplicit);
510 : : static void get_from_clause(Query *query, const char *prefix,
511 : : deparse_context *context);
512 : : static void get_from_clause_item(Node *jtnode, Query *query,
513 : : deparse_context *context);
514 : : static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
515 : : deparse_context *context);
516 : : static void get_column_alias_list(deparse_columns *colinfo,
517 : : deparse_context *context);
518 : : static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
519 : : deparse_columns *colinfo,
520 : : deparse_context *context);
521 : : static void get_tablesample_def(TableSampleClause *tablesample,
522 : : deparse_context *context);
523 : : static void get_opclass_name(Oid opclass, Oid actual_datatype,
524 : : StringInfo buf);
525 : : static Node *processIndirection(Node *node, deparse_context *context);
526 : : static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
527 : : static char *get_relation_name(Oid relid);
528 : : static char *generate_relation_name(Oid relid, List *namespaces);
529 : : static char *generate_qualified_relation_name(Oid relid);
530 : : static char *generate_function_name(Oid funcid, int nargs,
531 : : List *argnames, Oid *argtypes,
532 : : bool has_variadic, bool *use_variadic_p,
533 : : bool inGroupBy);
534 : : static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
535 : : static void add_cast_to(StringInfo buf, Oid typid);
536 : : static char *generate_qualified_type_name(Oid typid);
537 : : static text *string_to_text(char *str);
538 : : static char *flatten_reloptions(Oid relid);
539 : : static void get_reloptions(StringInfo buf, Datum reloptions);
540 : : static void get_json_path_spec(Node *path_spec, deparse_context *context,
541 : : bool showimplicit);
542 : : static void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
543 : : deparse_context *context,
544 : : bool showimplicit);
545 : : static void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
546 : : deparse_context *context,
547 : : bool showimplicit,
548 : : bool needcomma);
549 : :
550 : : #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
551 : :
552 : :
553 : : /* ----------
554 : : * pg_get_ruledef - Do it all and return a text
555 : : * that could be used as a statement
556 : : * to recreate the rule
557 : : * ----------
558 : : */
559 : : Datum
9295 tgl@sss.pgh.pa.us 560 :CBC 225 : pg_get_ruledef(PG_FUNCTION_ARGS)
561 : : {
8644 562 : 225 : Oid ruleoid = PG_GETARG_OID(0);
563 : : int prettyFlags;
564 : : char *res;
565 : :
4700 566 : 225 : prettyFlags = PRETTYFLAG_INDENT;
567 : :
3431 rhaas@postgresql.org 568 : 225 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
569 : :
570 [ + + ]: 225 : if (res == NULL)
571 : 3 : PG_RETURN_NULL();
572 : :
573 : 222 : PG_RETURN_TEXT_P(string_to_text(res));
574 : : }
575 : :
576 : :
577 : : Datum
8176 tgl@sss.pgh.pa.us 578 : 57 : pg_get_ruledef_ext(PG_FUNCTION_ARGS)
579 : : {
580 : 57 : Oid ruleoid = PG_GETARG_OID(0);
581 : 57 : bool pretty = PG_GETARG_BOOL(1);
582 : : int prettyFlags;
583 : : char *res;
584 : :
1360 585 [ + - ]: 57 : prettyFlags = GET_PRETTY_FLAGS(pretty);
586 : :
3431 rhaas@postgresql.org 587 : 57 : res = pg_get_ruledef_worker(ruleoid, prettyFlags);
588 : :
589 [ - + ]: 57 : if (res == NULL)
3431 rhaas@postgresql.org 590 :UBC 0 : PG_RETURN_NULL();
591 : :
3431 rhaas@postgresql.org 592 :CBC 57 : PG_RETURN_TEXT_P(string_to_text(res));
593 : : }
594 : :
595 : :
596 : : static char *
8176 tgl@sss.pgh.pa.us 597 : 282 : pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
598 : : {
599 : : Datum args[1];
600 : : char nulls[1];
601 : : int spirc;
602 : : HeapTuple ruletup;
603 : : TupleDesc rulettc;
604 : : StringInfoData buf;
605 : :
606 : : /*
607 : : * Do this first so that string is alloc'd in outer context not SPI's.
608 : : */
7896 609 : 282 : initStringInfo(&buf);
610 : :
611 : : /*
612 : : * Connect to SPI manager
613 : : */
464 614 : 282 : SPI_connect();
615 : :
616 : : /*
617 : : * On the first call prepare the plan to lookup pg_rewrite. We read
618 : : * pg_rewrite over the SPI manager instead of using the syscache to be
619 : : * checked for read access on pg_rewrite.
620 : : */
8644 621 [ + + ]: 282 : if (plan_getrulebyoid == NULL)
622 : : {
623 : : Oid argtypes[1];
624 : : SPIPlanPtr plan;
625 : :
626 : 20 : argtypes[0] = OIDOID;
627 : 20 : plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
9969 bruce@momjian.us 628 [ - + ]: 20 : if (plan == NULL)
8179 tgl@sss.pgh.pa.us 629 [ # # ]:UBC 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
5206 tgl@sss.pgh.pa.us 630 :CBC 20 : SPI_keepplan(plan);
631 : 20 : plan_getrulebyoid = plan;
632 : : }
633 : :
634 : : /*
635 : : * Get the pg_rewrite tuple for this rule
636 : : */
8644 637 : 282 : args[0] = ObjectIdGetDatum(ruleoid);
638 : 282 : nulls[0] = ' ';
4407 peter_e@gmx.net 639 : 282 : spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
9969 bruce@momjian.us 640 [ - + ]: 282 : if (spirc != SPI_OK_SELECT)
8179 tgl@sss.pgh.pa.us 641 [ # # ]:UBC 0 : elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
9969 bruce@momjian.us 642 [ + + ]:CBC 282 : if (SPI_processed != 1)
643 : : {
644 : : /*
645 : : * There is no tuple data available here, just keep the output buffer
646 : : * empty.
647 : : */
648 : : }
649 : : else
650 : : {
651 : : /*
652 : : * Get the rule's definition and put it into executor's memory
653 : : */
7896 tgl@sss.pgh.pa.us 654 : 279 : ruletup = SPI_tuptable->vals[0];
655 : 279 : rulettc = SPI_tuptable->tupdesc;
656 : 279 : make_ruledef(&buf, ruletup, rulettc, prettyFlags);
657 : : }
658 : :
659 : : /*
660 : : * Disconnect from SPI manager
661 : : */
9969 bruce@momjian.us 662 [ - + ]: 282 : if (SPI_finish() != SPI_OK_FINISH)
8179 tgl@sss.pgh.pa.us 663 [ # # ]:UBC 0 : elog(ERROR, "SPI_finish failed");
664 : :
3431 rhaas@postgresql.org 665 [ + + ]:CBC 282 : if (buf.len == 0)
666 : 3 : return NULL;
667 : :
7896 tgl@sss.pgh.pa.us 668 : 279 : return buf.data;
669 : : }
670 : :
671 : :
672 : : /* ----------
673 : : * pg_get_viewdef - Mainly the same thing, but we
674 : : * only return the SELECT part of a view
675 : : * ----------
676 : : */
677 : : Datum
9295 678 : 1237 : pg_get_viewdef(PG_FUNCTION_ARGS)
679 : : {
680 : : /* By OID */
8644 681 : 1237 : Oid viewoid = PG_GETARG_OID(0);
682 : : int prettyFlags;
683 : : char *res;
684 : :
4700 685 : 1237 : prettyFlags = PRETTYFLAG_INDENT;
686 : :
3431 rhaas@postgresql.org 687 : 1237 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
688 : :
689 [ + + ]: 1237 : if (res == NULL)
690 : 3 : PG_RETURN_NULL();
691 : :
692 : 1234 : PG_RETURN_TEXT_P(string_to_text(res));
693 : : }
694 : :
695 : :
696 : : Datum
8176 tgl@sss.pgh.pa.us 697 : 282 : pg_get_viewdef_ext(PG_FUNCTION_ARGS)
698 : : {
699 : : /* By OID */
700 : 282 : Oid viewoid = PG_GETARG_OID(0);
701 : 282 : bool pretty = PG_GETARG_BOOL(1);
702 : : int prettyFlags;
703 : : char *res;
704 : :
1360 705 [ + - ]: 282 : prettyFlags = GET_PRETTY_FLAGS(pretty);
706 : :
3431 rhaas@postgresql.org 707 : 282 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
708 : :
709 [ - + ]: 282 : if (res == NULL)
3431 rhaas@postgresql.org 710 :UBC 0 : PG_RETURN_NULL();
711 : :
3431 rhaas@postgresql.org 712 :CBC 282 : PG_RETURN_TEXT_P(string_to_text(res));
713 : : }
714 : :
715 : : Datum
5050 andrew@dunslane.net 716 : 3 : pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
717 : : {
718 : : /* By OID */
719 : 3 : Oid viewoid = PG_GETARG_OID(0);
4938 bruce@momjian.us 720 : 3 : int wrap = PG_GETARG_INT32(1);
721 : : int prettyFlags;
722 : : char *res;
723 : :
724 : : /* calling this implies we want pretty printing */
1360 tgl@sss.pgh.pa.us 725 : 3 : prettyFlags = GET_PRETTY_FLAGS(true);
726 : :
3431 rhaas@postgresql.org 727 : 3 : res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
728 : :
729 [ - + ]: 3 : if (res == NULL)
3431 rhaas@postgresql.org 730 :UBC 0 : PG_RETURN_NULL();
731 : :
3431 rhaas@postgresql.org 732 :CBC 3 : PG_RETURN_TEXT_P(string_to_text(res));
733 : : }
734 : :
735 : : Datum
8644 tgl@sss.pgh.pa.us 736 : 39 : pg_get_viewdef_name(PG_FUNCTION_ARGS)
737 : : {
738 : : /* By qualified name */
3202 noah@leadboat.com 739 : 39 : text *viewname = PG_GETARG_TEXT_PP(0);
740 : : int prettyFlags;
741 : : RangeVar *viewrel;
742 : : Oid viewoid;
743 : : char *res;
744 : :
4700 tgl@sss.pgh.pa.us 745 : 39 : prettyFlags = PRETTYFLAG_INDENT;
746 : :
747 : : /* Look up view name. Can't lock it - we might not have privileges. */
7509 neilc@samurai.com 748 : 39 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
5131 rhaas@postgresql.org 749 : 39 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
750 : :
3431 751 : 39 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
752 : :
753 [ - + ]: 39 : if (res == NULL)
3431 rhaas@postgresql.org 754 :UBC 0 : PG_RETURN_NULL();
755 : :
3431 rhaas@postgresql.org 756 :CBC 39 : PG_RETURN_TEXT_P(string_to_text(res));
757 : : }
758 : :
759 : :
760 : : Datum
8176 tgl@sss.pgh.pa.us 761 : 201 : pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
762 : : {
763 : : /* By qualified name */
3202 noah@leadboat.com 764 : 201 : text *viewname = PG_GETARG_TEXT_PP(0);
8176 tgl@sss.pgh.pa.us 765 : 201 : bool pretty = PG_GETARG_BOOL(1);
766 : : int prettyFlags;
767 : : RangeVar *viewrel;
768 : : Oid viewoid;
769 : : char *res;
770 : :
1360 771 [ + - ]: 201 : prettyFlags = GET_PRETTY_FLAGS(pretty);
772 : :
773 : : /* Look up view name. Can't lock it - we might not have privileges. */
7509 neilc@samurai.com 774 : 201 : viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
5131 rhaas@postgresql.org 775 : 201 : viewoid = RangeVarGetRelid(viewrel, NoLock, false);
776 : :
3419 tgl@sss.pgh.pa.us 777 : 201 : res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
778 : :
779 [ - + ]: 201 : if (res == NULL)
3419 tgl@sss.pgh.pa.us 780 :UBC 0 : PG_RETURN_NULL();
781 : :
3419 tgl@sss.pgh.pa.us 782 :CBC 201 : PG_RETURN_TEXT_P(string_to_text(res));
783 : : }
784 : :
785 : : /*
786 : : * Common code for by-OID and by-name variants of pg_get_viewdef
787 : : */
788 : : static char *
4741 789 : 1762 : pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
790 : : {
791 : : Datum args[2];
792 : : char nulls[2];
793 : : int spirc;
794 : : HeapTuple ruletup;
795 : : TupleDesc rulettc;
796 : : StringInfoData buf;
797 : :
798 : : /*
799 : : * Do this first so that string is alloc'd in outer context not SPI's.
800 : : */
7896 801 : 1762 : initStringInfo(&buf);
802 : :
803 : : /*
804 : : * Connect to SPI manager
805 : : */
464 806 : 1762 : SPI_connect();
807 : :
808 : : /*
809 : : * On the first call prepare the plan to lookup pg_rewrite. We read
810 : : * pg_rewrite over the SPI manager instead of using the syscache to be
811 : : * checked for read access on pg_rewrite.
812 : : */
8644 813 [ + + ]: 1762 : if (plan_getviewrule == NULL)
814 : : {
815 : : Oid argtypes[2];
816 : : SPIPlanPtr plan;
817 : :
818 : 127 : argtypes[0] = OIDOID;
819 : 127 : argtypes[1] = NAMEOID;
820 : 127 : plan = SPI_prepare(query_getviewrule, 2, argtypes);
9969 bruce@momjian.us 821 [ - + ]: 127 : if (plan == NULL)
8179 tgl@sss.pgh.pa.us 822 [ # # ]:UBC 0 : elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
5206 tgl@sss.pgh.pa.us 823 :CBC 127 : SPI_keepplan(plan);
824 : 127 : plan_getviewrule = plan;
825 : : }
826 : :
827 : : /*
828 : : * Get the pg_rewrite tuple for the view's SELECT rule
829 : : */
8644 830 : 1762 : args[0] = ObjectIdGetDatum(viewoid);
4407 peter_e@gmx.net 831 : 1762 : args[1] = DirectFunctionCall1(namein, CStringGetDatum(ViewSelectRuleName));
9969 bruce@momjian.us 832 : 1762 : nulls[0] = ' ';
8644 tgl@sss.pgh.pa.us 833 : 1762 : nulls[1] = ' ';
4407 peter_e@gmx.net 834 : 1762 : spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
9969 bruce@momjian.us 835 [ - + ]: 1762 : if (spirc != SPI_OK_SELECT)
8629 tgl@sss.pgh.pa.us 836 [ # # ]:UBC 0 : elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
9969 bruce@momjian.us 837 [ + + ]:CBC 1762 : if (SPI_processed != 1)
838 : : {
839 : : /*
840 : : * There is no tuple data available here, just keep the output buffer
841 : : * empty.
842 : : */
843 : : }
844 : : else
845 : : {
846 : : /*
847 : : * Get the rule's definition and put it into executor's memory
848 : : */
849 : 1759 : ruletup = SPI_tuptable->vals[0];
850 : 1759 : rulettc = SPI_tuptable->tupdesc;
4741 tgl@sss.pgh.pa.us 851 : 1759 : make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
852 : : }
853 : :
854 : : /*
855 : : * Disconnect from SPI manager
856 : : */
9969 bruce@momjian.us 857 [ - + ]: 1762 : if (SPI_finish() != SPI_OK_FINISH)
8179 tgl@sss.pgh.pa.us 858 [ # # ]:UBC 0 : elog(ERROR, "SPI_finish failed");
859 : :
3431 rhaas@postgresql.org 860 [ + + ]:CBC 1762 : if (buf.len == 0)
861 : 3 : return NULL;
862 : :
7896 tgl@sss.pgh.pa.us 863 : 1759 : return buf.data;
864 : : }
865 : :
866 : : /* ----------
867 : : * pg_get_triggerdef - Get the definition of a trigger
868 : : * ----------
869 : : */
870 : : Datum
8308 bruce@momjian.us 871 : 82 : pg_get_triggerdef(PG_FUNCTION_ARGS)
872 : : {
873 : 82 : Oid trigid = PG_GETARG_OID(0);
874 : : char *res;
875 : :
3431 rhaas@postgresql.org 876 : 82 : res = pg_get_triggerdef_worker(trigid, false);
877 : :
878 [ + + ]: 82 : if (res == NULL)
879 : 3 : PG_RETURN_NULL();
880 : :
881 : 79 : PG_RETURN_TEXT_P(string_to_text(res));
882 : : }
883 : :
884 : : Datum
5913 peter_e@gmx.net 885 : 613 : pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
886 : : {
887 : 613 : Oid trigid = PG_GETARG_OID(0);
888 : 613 : bool pretty = PG_GETARG_BOOL(1);
889 : : char *res;
890 : :
3431 rhaas@postgresql.org 891 : 613 : res = pg_get_triggerdef_worker(trigid, pretty);
892 : :
893 [ - + ]: 613 : if (res == NULL)
3431 rhaas@postgresql.org 894 :UBC 0 : PG_RETURN_NULL();
895 : :
3431 rhaas@postgresql.org 896 :CBC 613 : PG_RETURN_TEXT_P(string_to_text(res));
897 : : }
898 : :
899 : : static char *
5913 peter_e@gmx.net 900 : 695 : pg_get_triggerdef_worker(Oid trigid, bool pretty)
901 : : {
902 : : HeapTuple ht_trig;
903 : : Form_pg_trigger trigrec;
904 : : StringInfoData buf;
905 : : Relation tgrel;
906 : : ScanKeyData skey[1];
907 : : SysScanDesc tgscan;
8247 tgl@sss.pgh.pa.us 908 : 695 : int findx = 0;
909 : : char *tgname;
910 : : char *tgoldtable;
911 : : char *tgnewtable;
912 : : Datum value;
913 : : bool isnull;
914 : :
915 : : /*
916 : : * Fetch the pg_trigger tuple by the Oid of the trigger
917 : : */
2522 andres@anarazel.de 918 : 695 : tgrel = table_open(TriggerRelationId, AccessShareLock);
919 : :
8071 tgl@sss.pgh.pa.us 920 : 695 : ScanKeyInit(&skey[0],
921 : : Anum_pg_trigger_oid,
922 : : BTEqualStrategyNumber, F_OIDEQ,
923 : : ObjectIdGetDatum(trigid));
924 : :
7552 925 : 695 : tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
926 : : NULL, 1, skey);
927 : :
8247 928 : 695 : ht_trig = systable_getnext(tgscan);
929 : :
930 [ + + ]: 695 : if (!HeapTupleIsValid(ht_trig))
931 : : {
3431 rhaas@postgresql.org 932 : 3 : systable_endscan(tgscan);
2522 andres@anarazel.de 933 : 3 : table_close(tgrel, AccessShareLock);
3431 rhaas@postgresql.org 934 : 3 : return NULL;
935 : : }
936 : :
8308 bruce@momjian.us 937 : 692 : trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
938 : :
939 : : /*
940 : : * Start the trigger definition. Note that the trigger's name should never
941 : : * be schema-qualified, but the trigger rel's name may be.
942 : : */
943 : 692 : initStringInfo(&buf);
944 : :
945 : 692 : tgname = NameStr(trigrec->tgname);
5809 itagaki.takahiro@gma 946 : 1384 : appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
5813 tgl@sss.pgh.pa.us 947 [ - + ]: 692 : OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
948 : : quote_identifier(tgname));
949 : :
8308 bruce@momjian.us 950 [ + + ]: 692 : if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
4430 rhaas@postgresql.org 951 : 265 : appendStringInfoString(&buf, "BEFORE");
5547 tgl@sss.pgh.pa.us 952 [ + + ]: 427 : else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
4430 rhaas@postgresql.org 953 : 415 : appendStringInfoString(&buf, "AFTER");
5547 tgl@sss.pgh.pa.us 954 [ + - ]: 12 : else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
4430 rhaas@postgresql.org 955 : 12 : appendStringInfoString(&buf, "INSTEAD OF");
956 : : else
5547 tgl@sss.pgh.pa.us 957 [ # # ]:UBC 0 : elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
958 : :
8308 bruce@momjian.us 959 [ + + ]:CBC 692 : if (TRIGGER_FOR_INSERT(trigrec->tgtype))
960 : : {
4430 rhaas@postgresql.org 961 : 483 : appendStringInfoString(&buf, " INSERT");
8308 bruce@momjian.us 962 : 483 : findx++;
963 : : }
964 [ + + ]: 692 : if (TRIGGER_FOR_DELETE(trigrec->tgtype))
965 : : {
966 [ + + ]: 105 : if (findx > 0)
4430 rhaas@postgresql.org 967 : 45 : appendStringInfoString(&buf, " OR DELETE");
968 : : else
969 : 60 : appendStringInfoString(&buf, " DELETE");
8308 bruce@momjian.us 970 : 105 : findx++;
971 : : }
972 [ + + ]: 692 : if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
973 : : {
974 [ + + ]: 324 : if (findx > 0)
4430 rhaas@postgresql.org 975 : 175 : appendStringInfoString(&buf, " OR UPDATE");
976 : : else
977 : 149 : appendStringInfoString(&buf, " UPDATE");
5871 tgl@sss.pgh.pa.us 978 : 324 : findx++;
979 : : /* tgattr is first var-width field, so OK to access directly */
5908 980 [ + + ]: 324 : if (trigrec->tgattr.dim1 > 0)
981 : : {
982 : : int i;
983 : :
984 : 38 : appendStringInfoString(&buf, " OF ");
985 [ + + ]: 84 : for (i = 0; i < trigrec->tgattr.dim1; i++)
986 : : {
987 : : char *attname;
988 : :
989 [ + + ]: 46 : if (i > 0)
990 : 8 : appendStringInfoString(&buf, ", ");
2865 alvherre@alvh.no-ip. 991 : 46 : attname = get_attname(trigrec->tgrelid,
992 : 46 : trigrec->tgattr.values[i], false);
5908 tgl@sss.pgh.pa.us 993 : 46 : appendStringInfoString(&buf, quote_identifier(attname));
994 : : }
995 : : }
996 : : }
6473 997 [ - + ]: 692 : if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
998 : : {
6473 tgl@sss.pgh.pa.us 999 [ # # ]:UBC 0 : if (findx > 0)
4430 rhaas@postgresql.org 1000 : 0 : appendStringInfoString(&buf, " OR TRUNCATE");
1001 : : else
1002 : 0 : appendStringInfoString(&buf, " TRUNCATE");
5871 tgl@sss.pgh.pa.us 1003 : 0 : findx++;
1004 : : }
1005 : :
1006 : : /*
1007 : : * In non-pretty mode, always schema-qualify the target table name for
1008 : : * safety. In pretty mode, schema-qualify only if not visible.
1009 : : */
5809 itagaki.takahiro@gma 1010 [ + + ]:CBC 1384 : appendStringInfo(&buf, " ON %s ",
1011 : : pretty ?
2851 tgl@sss.pgh.pa.us 1012 : 87 : generate_relation_name(trigrec->tgrelid, NIL) :
1013 : 605 : generate_qualified_relation_name(trigrec->tgrelid));
1014 : :
5813 1015 [ - + ]: 692 : if (OidIsValid(trigrec->tgconstraint))
1016 : : {
5871 tgl@sss.pgh.pa.us 1017 [ # # ]:UBC 0 : if (OidIsValid(trigrec->tgconstrrelid))
5809 itagaki.takahiro@gma 1018 : 0 : appendStringInfo(&buf, "FROM %s ",
1019 : : generate_relation_name(trigrec->tgconstrrelid, NIL));
8308 bruce@momjian.us 1020 [ # # ]: 0 : if (!trigrec->tgdeferrable)
4430 rhaas@postgresql.org 1021 : 0 : appendStringInfoString(&buf, "NOT ");
1022 : 0 : appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
8308 bruce@momjian.us 1023 [ # # ]: 0 : if (trigrec->tginitdeferred)
4430 rhaas@postgresql.org 1024 : 0 : appendStringInfoString(&buf, "DEFERRED ");
1025 : : else
1026 : 0 : appendStringInfoString(&buf, "IMMEDIATE ");
1027 : : }
1028 : :
3330 kgrittn@postgresql.o 1029 :CBC 692 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
1030 : : tgrel->rd_att, &isnull);
1031 [ + + ]: 692 : if (!isnull)
2616 tgl@sss.pgh.pa.us 1032 : 49 : tgoldtable = NameStr(*DatumGetName(value));
1033 : : else
3330 kgrittn@postgresql.o 1034 : 643 : tgoldtable = NULL;
1035 : 692 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
1036 : : tgrel->rd_att, &isnull);
1037 [ + + ]: 692 : if (!isnull)
2616 tgl@sss.pgh.pa.us 1038 : 54 : tgnewtable = NameStr(*DatumGetName(value));
1039 : : else
3330 kgrittn@postgresql.o 1040 : 638 : tgnewtable = NULL;
1041 [ + + + + ]: 692 : if (tgoldtable != NULL || tgnewtable != NULL)
1042 : : {
1043 : 76 : appendStringInfoString(&buf, "REFERENCING ");
1044 [ + + ]: 76 : if (tgoldtable != NULL)
2616 tgl@sss.pgh.pa.us 1045 : 49 : appendStringInfo(&buf, "OLD TABLE AS %s ",
1046 : : quote_identifier(tgoldtable));
3330 kgrittn@postgresql.o 1047 [ + + ]: 76 : if (tgnewtable != NULL)
2616 tgl@sss.pgh.pa.us 1048 : 54 : appendStringInfo(&buf, "NEW TABLE AS %s ",
1049 : : quote_identifier(tgnewtable));
1050 : : }
1051 : :
8308 bruce@momjian.us 1052 [ + + ]: 692 : if (TRIGGER_FOR_ROW(trigrec->tgtype))
4430 rhaas@postgresql.org 1053 : 533 : appendStringInfoString(&buf, "FOR EACH ROW ");
1054 : : else
1055 : 159 : appendStringInfoString(&buf, "FOR EACH STATEMENT ");
1056 : :
1057 : : /* If the trigger has a WHEN qualification, add that */
5871 tgl@sss.pgh.pa.us 1058 : 692 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
1059 : : tgrel->rd_att, &isnull);
1060 [ + + ]: 692 : if (!isnull)
1061 : : {
1062 : : Node *qual;
1063 : : char relkind;
1064 : : deparse_context context;
1065 : : deparse_namespace dpns;
1066 : : RangeTblEntry *oldrte;
1067 : : RangeTblEntry *newrte;
1068 : :
1069 : 76 : appendStringInfoString(&buf, "WHEN (");
1070 : :
1071 : 76 : qual = stringToNode(TextDatumGetCString(value));
1072 : :
5412 1073 : 76 : relkind = get_rel_relkind(trigrec->tgrelid);
1074 : :
1075 : : /* Build minimal OLD and NEW RTEs for the rel */
5871 1076 : 76 : oldrte = makeNode(RangeTblEntry);
1077 : 76 : oldrte->rtekind = RTE_RELATION;
1078 : 76 : oldrte->relid = trigrec->tgrelid;
5412 1079 : 76 : oldrte->relkind = relkind;
2635 1080 : 76 : oldrte->rellockmode = AccessShareLock;
4835 1081 : 76 : oldrte->alias = makeAlias("old", NIL);
1082 : 76 : oldrte->eref = oldrte->alias;
4880 1083 : 76 : oldrte->lateral = false;
5871 1084 : 76 : oldrte->inh = false;
1085 : 76 : oldrte->inFromCl = true;
1086 : :
1087 : 76 : newrte = makeNode(RangeTblEntry);
1088 : 76 : newrte->rtekind = RTE_RELATION;
1089 : 76 : newrte->relid = trigrec->tgrelid;
5412 1090 : 76 : newrte->relkind = relkind;
2635 1091 : 76 : newrte->rellockmode = AccessShareLock;
4835 1092 : 76 : newrte->alias = makeAlias("new", NIL);
1093 : 76 : newrte->eref = newrte->alias;
4880 1094 : 76 : newrte->lateral = false;
5871 1095 : 76 : newrte->inh = false;
1096 : 76 : newrte->inFromCl = true;
1097 : :
1098 : : /* Build two-element rtable */
5636 1099 : 76 : memset(&dpns, 0, sizeof(dpns));
5871 1100 : 76 : dpns.rtable = list_make2(oldrte, newrte);
2198 1101 : 76 : dpns.subplans = NIL;
5871 1102 : 76 : dpns.ctes = NIL;
2198 1103 : 76 : dpns.appendrels = NULL;
4835 1104 : 76 : set_rtable_names(&dpns, NIL, NULL);
4734 1105 : 76 : set_simple_column_names(&dpns);
1106 : :
1107 : : /* Set up context with one-deep namespace stack */
5871 1108 : 76 : context.buf = &buf;
1109 : 76 : context.namespaces = list_make1(&dpns);
475 1110 : 76 : context.resultDesc = NULL;
1111 : 76 : context.targetList = NIL;
5871 1112 : 76 : context.windowClause = NIL;
1113 : 76 : context.varprefix = true;
1360 1114 [ + + ]: 76 : context.prettyFlags = GET_PRETTY_FLAGS(pretty);
4741 1115 : 76 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
5871 1116 : 76 : context.indentLevel = PRETTYINDENT_STD;
475 1117 : 76 : context.colNamesVisible = true;
1118 : 76 : context.inGroupBy = false;
1119 : 76 : context.varInOrderBy = false;
2198 1120 : 76 : context.appendparents = NULL;
1121 : :
5871 1122 : 76 : get_rule_expr(qual, &context, false);
1123 : :
4430 rhaas@postgresql.org 1124 : 76 : appendStringInfoString(&buf, ") ");
1125 : : }
1126 : :
2505 peter@eisentraut.org 1127 : 692 : appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
1128 : : generate_function_name(trigrec->tgfoid, 0,
1129 : : NIL, NULL,
1130 : : false, NULL, false));
1131 : :
8247 tgl@sss.pgh.pa.us 1132 [ + + ]: 692 : if (trigrec->tgnargs > 0)
1133 : : {
1134 : : char *p;
1135 : : int i;
1136 : :
5871 1137 : 223 : value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
1138 : : tgrel->rd_att, &isnull);
8247 1139 [ - + ]: 223 : if (isnull)
8247 tgl@sss.pgh.pa.us 1140 [ # # ]:UBC 0 : elog(ERROR, "tgargs is null for trigger %u", trigid);
3202 noah@leadboat.com 1141 [ + - ]:CBC 223 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
8247 tgl@sss.pgh.pa.us 1142 [ + + ]: 502 : for (i = 0; i < trigrec->tgnargs; i++)
1143 : : {
1144 [ + + ]: 279 : if (i > 0)
4430 rhaas@postgresql.org 1145 : 56 : appendStringInfoString(&buf, ", ");
6311 tgl@sss.pgh.pa.us 1146 : 279 : simple_quote_literal(&buf, p);
1147 : : /* advance p to next string embedded in tgargs */
1148 [ + + ]: 2746 : while (*p)
1149 : 2467 : p++;
7143 1150 : 279 : p++;
1151 : : }
1152 : : }
1153 : :
1154 : : /* We deliberately do not put semi-colon at end */
4430 rhaas@postgresql.org 1155 : 692 : appendStringInfoChar(&buf, ')');
1156 : :
1157 : : /* Clean up */
8247 tgl@sss.pgh.pa.us 1158 : 692 : systable_endscan(tgscan);
1159 : :
2522 andres@anarazel.de 1160 : 692 : table_close(tgrel, AccessShareLock);
1161 : :
5913 peter_e@gmx.net 1162 : 692 : return buf.data;
1163 : : }
1164 : :
1165 : : /* ----------
1166 : : * pg_get_indexdef - Get the definition of an index
1167 : : *
1168 : : * In the extended version, there is a colno argument as well as pretty bool.
1169 : : * if colno == 0, we want a complete index definition.
1170 : : * if colno > 0, we only want the Nth index key's variable or expression.
1171 : : *
1172 : : * Note that the SQL-function versions of this omit any info about the
1173 : : * index tablespace; this is intentional because pg_dump wants it that way.
1174 : : * However pg_get_indexdef_string() includes the index tablespace.
1175 : : * ----------
1176 : : */
1177 : : Datum
9322 tgl@sss.pgh.pa.us 1178 : 2855 : pg_get_indexdef(PG_FUNCTION_ARGS)
1179 : : {
1180 : 2855 : Oid indexrelid = PG_GETARG_OID(0);
1181 : : int prettyFlags;
1182 : : char *res;
1183 : :
4700 1184 : 2855 : prettyFlags = PRETTYFLAG_INDENT;
1185 : :
2708 1186 : 2855 : res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1187 : : false, false,
1188 : : false, false,
1189 : : prettyFlags, true);
1190 : :
3431 rhaas@postgresql.org 1191 [ + + ]: 2855 : if (res == NULL)
1192 : 3 : PG_RETURN_NULL();
1193 : :
1194 : 2852 : PG_RETURN_TEXT_P(string_to_text(res));
1195 : : }
1196 : :
1197 : : Datum
8176 tgl@sss.pgh.pa.us 1198 : 1009 : pg_get_indexdef_ext(PG_FUNCTION_ARGS)
1199 : : {
1200 : 1009 : Oid indexrelid = PG_GETARG_OID(0);
8171 bruce@momjian.us 1201 : 1009 : int32 colno = PG_GETARG_INT32(1);
8176 tgl@sss.pgh.pa.us 1202 : 1009 : bool pretty = PG_GETARG_BOOL(2);
1203 : : int prettyFlags;
1204 : : char *res;
1205 : :
1360 1206 [ + - ]: 1009 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1207 : :
2708 1208 : 1009 : res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1209 : : colno != 0, false,
1210 : : false, false,
1211 : : prettyFlags, true);
1212 : :
3431 rhaas@postgresql.org 1213 [ - + ]: 1009 : if (res == NULL)
3431 rhaas@postgresql.org 1214 :UBC 0 : PG_RETURN_NULL();
1215 : :
3431 rhaas@postgresql.org 1216 :CBC 1009 : PG_RETURN_TEXT_P(string_to_text(res));
1217 : : }
1218 : :
1219 : : /*
1220 : : * Internal version for use by ALTER TABLE.
1221 : : * Includes a tablespace clause in the result.
1222 : : * Returns a palloc'd C string; no pretty-printing.
1223 : : */
1224 : : char *
7896 tgl@sss.pgh.pa.us 1225 : 114 : pg_get_indexdef_string(Oid indexrelid)
1226 : : {
2708 1227 : 114 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1228 : : false, false,
1229 : : true, true,
1230 : : 0, false);
1231 : : }
1232 : :
1233 : : /* Internal version that just reports the key-column definitions */
1234 : : char *
5982 1235 : 531 : pg_get_indexdef_columns(Oid indexrelid, bool pretty)
1236 : : {
1237 : : int prettyFlags;
1238 : :
1360 1239 [ + - ]: 531 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1240 : :
2708 1241 : 531 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1242 : : true, true,
1243 : : false, false,
1244 : : prettyFlags, false);
1245 : : }
1246 : :
1247 : : /* Internal version, extensible with flags to control its behavior */
1248 : : char *
943 michael@paquier.xyz 1249 : 4 : pg_get_indexdef_columns_extended(Oid indexrelid, bits16 flags)
1250 : : {
1251 : 4 : bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1252 : 4 : bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1253 : : int prettyFlags;
1254 : :
1255 [ + - ]: 4 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1256 : :
1257 : 4 : return pg_get_indexdef_worker(indexrelid, 0, NULL,
1258 : : true, keys_only,
1259 : : false, false,
1260 : : prettyFlags, false);
1261 : : }
1262 : :
1263 : : /*
1264 : : * Internal workhorse to decompile an index definition.
1265 : : *
1266 : : * This is now used for exclusion constraints as well: if excludeOps is not
1267 : : * NULL then it points to an array of exclusion operator OIDs.
1268 : : */
1269 : : static char *
5982 tgl@sss.pgh.pa.us 1270 : 4565 : pg_get_indexdef_worker(Oid indexrelid, int colno,
1271 : : const Oid *excludeOps,
1272 : : bool attrsOnly, bool keysOnly,
1273 : : bool showTblSpc, bool inherits,
1274 : : int prettyFlags, bool missing_ok)
1275 : : {
1276 : : /* might want a separate isConstraint parameter later */
5854 1277 : 4565 : bool isConstraint = (excludeOps != NULL);
1278 : : HeapTuple ht_idx;
1279 : : HeapTuple ht_idxrel;
1280 : : HeapTuple ht_am;
1281 : : Form_pg_index idxrec;
1282 : : Form_pg_class idxrelrec;
1283 : : Form_pg_am amrec;
1284 : : IndexAmRoutine *amroutine;
1285 : : List *indexprs;
1286 : : ListCell *indexpr_item;
1287 : : List *context;
1288 : : Oid indrelid;
1289 : : int keyno;
1290 : : Datum indcollDatum;
1291 : : Datum indclassDatum;
1292 : : Datum indoptionDatum;
1293 : : oidvector *indcollation;
1294 : : oidvector *indclass;
1295 : : int2vector *indoption;
1296 : : StringInfoData buf;
1297 : : char *str;
1298 : : char *sep;
1299 : :
1300 : : /*
1301 : : * Fetch the pg_index tuple by the Oid of the index
1302 : : */
5785 rhaas@postgresql.org 1303 : 4565 : ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
9938 bruce@momjian.us 1304 [ + + ]: 4565 : if (!HeapTupleIsValid(ht_idx))
1305 : : {
3431 rhaas@postgresql.org 1306 [ + - ]: 3 : if (missing_ok)
1307 : 3 : return NULL;
8179 tgl@sss.pgh.pa.us 1308 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for index %u", indexrelid);
1309 : : }
9703 bruce@momjian.us 1310 :CBC 4562 : idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
1311 : :
8629 tgl@sss.pgh.pa.us 1312 : 4562 : indrelid = idxrec->indrelid;
1313 [ - + ]: 4562 : Assert(indexrelid == idxrec->indexrelid);
1314 : :
1315 : : /* Must get indcollation, indclass, and indoption the hard way */
998 dgustafsson@postgres 1316 : 4562 : indcollDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1317 : : Anum_pg_index_indcollation);
5426 peter_e@gmx.net 1318 : 4562 : indcollation = (oidvector *) DatumGetPointer(indcollDatum);
1319 : :
998 dgustafsson@postgres 1320 : 4562 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1321 : : Anum_pg_index_indclass);
7568 tgl@sss.pgh.pa.us 1322 : 4562 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
1323 : :
998 dgustafsson@postgres 1324 : 4562 : indoptionDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1325 : : Anum_pg_index_indoption);
6917 tgl@sss.pgh.pa.us 1326 : 4562 : indoption = (int2vector *) DatumGetPointer(indoptionDatum);
1327 : :
1328 : : /*
1329 : : * Fetch the pg_class tuple of the index relation
1330 : : */
5785 rhaas@postgresql.org 1331 : 4562 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
9938 bruce@momjian.us 1332 [ - + ]: 4562 : if (!HeapTupleIsValid(ht_idxrel))
8179 tgl@sss.pgh.pa.us 1333 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", indexrelid);
9703 bruce@momjian.us 1334 :CBC 4562 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
1335 : :
1336 : : /*
1337 : : * Fetch the pg_am tuple of the index' access method
1338 : : */
5785 rhaas@postgresql.org 1339 : 4562 : ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
8702 tgl@sss.pgh.pa.us 1340 [ - + ]: 4562 : if (!HeapTupleIsValid(ht_am))
8179 tgl@sss.pgh.pa.us 1341 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for access method %u",
1342 : : idxrelrec->relam);
8702 tgl@sss.pgh.pa.us 1343 :CBC 4562 : amrec = (Form_pg_am) GETSTRUCT(ht_am);
1344 : :
1345 : : /* Fetch the index AM's API struct */
3622 1346 : 4562 : amroutine = GetIndexAmRoutine(amrec->amhandler);
1347 : :
1348 : : /*
1349 : : * Get the index expressions, if any. (NOTE: we do not use the relcache
1350 : : * versions of the expressions and predicate, because we want to display
1351 : : * non-const-folded expressions.)
1352 : : */
2821 andrew@dunslane.net 1353 [ + + ]: 4562 : if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs, NULL))
1354 : : {
1355 : : Datum exprsDatum;
1356 : : char *exprsString;
1357 : :
998 dgustafsson@postgres 1358 : 326 : exprsDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1359 : : Anum_pg_index_indexprs);
6476 tgl@sss.pgh.pa.us 1360 : 326 : exprsString = TextDatumGetCString(exprsDatum);
8239 1361 : 326 : indexprs = (List *) stringToNode(exprsString);
1362 : 326 : pfree(exprsString);
1363 : : }
1364 : : else
1365 : 4236 : indexprs = NIL;
1366 : :
7875 neilc@samurai.com 1367 : 4562 : indexpr_item = list_head(indexprs);
1368 : :
5524 tgl@sss.pgh.pa.us 1369 : 4562 : context = deparse_context_for(get_relation_name(indrelid), indrelid);
1370 : :
1371 : : /*
1372 : : * Start the index definition. Note that the index's name should never be
1373 : : * schema-qualified, but the indexed rel's name may be.
1374 : : */
9573 1375 : 4562 : initStringInfo(&buf);
1376 : :
5982 1377 [ + + ]: 4562 : if (!attrsOnly)
1378 : : {
5854 1379 [ + + ]: 3796 : if (!isConstraint)
2889 alvherre@alvh.no-ip. 1380 : 7488 : appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
5854 tgl@sss.pgh.pa.us 1381 [ + + ]: 3744 : idxrec->indisunique ? "UNIQUE " : "",
1382 : 3744 : quote_identifier(NameStr(idxrelrec->relname)),
2889 alvherre@alvh.no-ip. 1383 [ + + ]: 3744 : idxrelrec->relkind == RELKIND_PARTITIONED_INDEX
1384 [ + + ]: 350 : && !inherits ? "ONLY " : "",
2851 tgl@sss.pgh.pa.us 1385 [ + + ]: 3744 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
1386 : 778 : generate_relation_name(indrelid, NIL) :
1387 : 2966 : generate_qualified_relation_name(indrelid),
5854 1388 : 3744 : quote_identifier(NameStr(amrec->amname)));
1389 : : else /* currently, must be EXCLUDE constraint */
1390 : 52 : appendStringInfo(&buf, "EXCLUDE USING %s (",
1391 : 52 : quote_identifier(NameStr(amrec->amname)));
1392 : : }
1393 : :
1394 : : /*
1395 : : * Report the indexed attributes
1396 : : */
9938 bruce@momjian.us 1397 : 4562 : sep = "";
8239 tgl@sss.pgh.pa.us 1398 [ + + ]: 11426 : for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1399 : : {
7568 1400 : 6913 : AttrNumber attnum = idxrec->indkey.values[keyno];
1401 : : Oid keycoltype;
1402 : : Oid keycolcollation;
1403 : :
1404 : : /*
1405 : : * Ignore non-key attributes if told to.
1406 : : */
2708 1407 [ + + + + ]: 6913 : if (keysOnly && keyno >= idxrec->indnkeyatts)
2811 teodor@sigaev.ru 1408 : 49 : break;
1409 : :
1410 : : /* Otherwise, print INCLUDE to divide key and non-key attrs. */
2708 tgl@sss.pgh.pa.us 1411 [ + + + + ]: 6864 : if (!colno && keyno == idxrec->indnkeyatts)
1412 : : {
2811 teodor@sigaev.ru 1413 : 125 : appendStringInfoString(&buf, ") INCLUDE (");
1414 : 125 : sep = "";
1415 : : }
1416 : :
8176 tgl@sss.pgh.pa.us 1417 [ + + ]: 6864 : if (!colno)
7536 neilc@samurai.com 1418 : 6543 : appendStringInfoString(&buf, sep);
9938 bruce@momjian.us 1419 : 6864 : sep = ", ";
1420 : :
8239 tgl@sss.pgh.pa.us 1421 [ + + ]: 6864 : if (attnum != 0)
1422 : : {
1423 : : /* Simple index column */
1424 : : char *attname;
1425 : : int32 keycoltypmod;
1426 : :
2865 alvherre@alvh.no-ip. 1427 : 6461 : attname = get_attname(indrelid, attnum, false);
8171 bruce@momjian.us 1428 [ + + + + ]: 6461 : if (!colno || colno == keyno + 1)
7991 neilc@samurai.com 1429 : 6377 : appendStringInfoString(&buf, quote_identifier(attname));
5380 tgl@sss.pgh.pa.us 1430 : 6461 : get_atttypetypmodcoll(indrelid, attnum,
1431 : : &keycoltype, &keycoltypmod,
1432 : : &keycolcollation);
1433 : : }
1434 : : else
1435 : : {
1436 : : /* expressional index */
1437 : : Node *indexkey;
1438 : :
7875 neilc@samurai.com 1439 [ - + ]: 403 : if (indexpr_item == NULL)
8239 tgl@sss.pgh.pa.us 1440 [ # # ]:UBC 0 : elog(ERROR, "too few entries in indexprs list");
7875 neilc@samurai.com 1441 :CBC 403 : indexkey = (Node *) lfirst(indexpr_item);
2347 tgl@sss.pgh.pa.us 1442 : 403 : indexpr_item = lnext(indexprs, indexpr_item);
1443 : : /* Deparse */
8176 1444 : 403 : str = deparse_expression_pretty(indexkey, context, false, false,
1445 : : prettyFlags, 0);
8171 bruce@momjian.us 1446 [ + + + + ]: 403 : if (!colno || colno == keyno + 1)
1447 : : {
1448 : : /* Need parens if it's not a bare function call */
3079 tgl@sss.pgh.pa.us 1449 [ + + ]: 397 : if (looks_like_function(indexkey))
7991 neilc@samurai.com 1450 : 26 : appendStringInfoString(&buf, str);
1451 : : else
8176 tgl@sss.pgh.pa.us 1452 : 371 : appendStringInfo(&buf, "(%s)", str);
1453 : : }
8239 1454 : 403 : keycoltype = exprType(indexkey);
5382 1455 : 403 : keycolcollation = exprCollation(indexkey);
1456 : : }
1457 : :
1458 : : /* Print additional decoration for (selected) key columns */
2708 1459 [ + + + + : 6864 : if (!attrsOnly && keyno < idxrec->indnkeyatts &&
- + ]
2708 tgl@sss.pgh.pa.us 1460 [ # # ]:UBC 0 : (!colno || colno == keyno + 1))
1461 : : {
2446 tgl@sss.pgh.pa.us 1462 :CBC 5596 : int16 opt = indoption->values[keyno];
1463 : 5596 : Oid indcoll = indcollation->values[keyno];
2088 akorotkov@postgresql 1464 : 5596 : Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1465 : 5596 : bool has_options = attoptions != (Datum) 0;
1466 : :
1467 : : /* Add collation, if not default for column */
5382 tgl@sss.pgh.pa.us 1468 [ + + + + ]: 5596 : if (OidIsValid(indcoll) && indcoll != keycolcollation)
1469 : 47 : appendStringInfo(&buf, " COLLATE %s",
1470 : : generate_collation_name((indcoll)));
1471 : :
1472 : : /* Add the operator class name, if not default */
2088 akorotkov@postgresql 1473 [ + + ]: 5596 : get_opclass_name(indclass->values[keyno],
1474 : : has_options ? InvalidOid : keycoltype, &buf);
1475 : :
1476 [ + + ]: 5596 : if (has_options)
1477 : : {
1478 : 17 : appendStringInfoString(&buf, " (");
1479 : 17 : get_reloptions(&buf, attoptions);
1480 : 17 : appendStringInfoChar(&buf, ')');
1481 : : }
1482 : :
1483 : : /* Add options if relevant */
3622 tgl@sss.pgh.pa.us 1484 [ + + ]: 5596 : if (amroutine->amcanorder)
1485 : : {
1486 : : /* if it supports sort ordering, report DESC and NULLS opts */
6572 1487 [ - + ]: 4543 : if (opt & INDOPTION_DESC)
1488 : : {
4430 rhaas@postgresql.org 1489 :UBC 0 : appendStringInfoString(&buf, " DESC");
1490 : : /* NULLS FIRST is the default in this case */
6572 tgl@sss.pgh.pa.us 1491 [ # # ]: 0 : if (!(opt & INDOPTION_NULLS_FIRST))
4430 rhaas@postgresql.org 1492 : 0 : appendStringInfoString(&buf, " NULLS LAST");
1493 : : }
1494 : : else
1495 : : {
6572 tgl@sss.pgh.pa.us 1496 [ - + ]:CBC 4543 : if (opt & INDOPTION_NULLS_FIRST)
4430 rhaas@postgresql.org 1497 :UBC 0 : appendStringInfoString(&buf, " NULLS FIRST");
1498 : : }
1499 : : }
1500 : :
1501 : : /* Add the exclusion operator if relevant */
5854 tgl@sss.pgh.pa.us 1502 [ + + ]:CBC 5596 : if (excludeOps != NULL)
1503 : 62 : appendStringInfo(&buf, " WITH %s",
1504 : 62 : generate_operator_name(excludeOps[keyno],
1505 : : keycoltype,
1506 : : keycoltype));
1507 : : }
1508 : : }
1509 : :
5982 1510 [ + + ]: 4562 : if (!attrsOnly)
1511 : : {
8171 bruce@momjian.us 1512 : 3796 : appendStringInfoChar(&buf, ')');
1513 : :
1413 peter@eisentraut.org 1514 [ + + ]: 3796 : if (idxrec->indnullsnotdistinct)
1198 drowley@postgresql.o 1515 : 6 : appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1516 : :
1517 : : /*
1518 : : * If it has options, append "WITH (options)"
1519 : : */
7107 tgl@sss.pgh.pa.us 1520 : 3796 : str = flatten_reloptions(indexrelid);
1521 [ + + ]: 3796 : if (str)
1522 : : {
1523 : 105 : appendStringInfo(&buf, " WITH (%s)", str);
1524 : 105 : pfree(str);
1525 : : }
1526 : :
1527 : : /*
1528 : : * Print tablespace, but only if requested
1529 : : */
6640 1530 [ + + ]: 3796 : if (showTblSpc)
1531 : : {
1532 : : Oid tblspc;
1533 : :
1534 : 114 : tblspc = get_rel_tablespace(indexrelid);
2428 alvherre@alvh.no-ip. 1535 [ + + ]: 114 : if (OidIsValid(tblspc))
1536 : : {
1537 [ - + ]: 27 : if (isConstraint)
2428 alvherre@alvh.no-ip. 1538 :UBC 0 : appendStringInfoString(&buf, " USING INDEX");
2428 alvherre@alvh.no-ip. 1539 :CBC 27 : appendStringInfo(&buf, " TABLESPACE %s",
1540 : 27 : quote_identifier(get_tablespace_name(tblspc)));
1541 : : }
1542 : : }
1543 : :
1544 : : /*
1545 : : * If it's a partial index, decompile and append the predicate
1546 : : */
2821 andrew@dunslane.net 1547 [ + + ]: 3796 : if (!heap_attisnull(ht_idx, Anum_pg_index_indpred, NULL))
1548 : : {
1549 : : Node *node;
1550 : : Datum predDatum;
1551 : : char *predString;
1552 : :
1553 : : /* Convert text string to node tree */
998 dgustafsson@postgres 1554 : 162 : predDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1555 : : Anum_pg_index_indpred);
6476 tgl@sss.pgh.pa.us 1556 : 162 : predString = TextDatumGetCString(predDatum);
8176 1557 : 162 : node = (Node *) stringToNode(predString);
1558 : 162 : pfree(predString);
1559 : :
1560 : : /* Deparse */
1561 : 162 : str = deparse_expression_pretty(node, context, false, false,
1562 : : prettyFlags, 0);
5854 1563 [ + + ]: 162 : if (isConstraint)
1564 : 21 : appendStringInfo(&buf, " WHERE (%s)", str);
1565 : : else
1566 : 141 : appendStringInfo(&buf, " WHERE %s", str);
1567 : : }
1568 : : }
1569 : :
1570 : : /* Clean up */
9162 1571 : 4562 : ReleaseSysCache(ht_idx);
1572 : 4562 : ReleaseSysCache(ht_idxrel);
8702 1573 : 4562 : ReleaseSysCache(ht_am);
1574 : :
7896 1575 : 4562 : return buf.data;
1576 : : }
1577 : :
1578 : : /* ----------
1579 : : * pg_get_querydef
1580 : : *
1581 : : * Public entry point to deparse one query parsetree.
1582 : : * The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
1583 : : *
1584 : : * The result is a palloc'd C string.
1585 : : * ----------
1586 : : */
1587 : : char *
1360 tgl@sss.pgh.pa.us 1588 :UBC 0 : pg_get_querydef(Query *query, bool pretty)
1589 : : {
1590 : : StringInfoData buf;
1591 : : int prettyFlags;
1592 : :
1593 [ # # ]: 0 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1594 : :
1595 : 0 : initStringInfo(&buf);
1596 : :
1306 1597 : 0 : get_query_def(query, &buf, NIL, NULL, true,
1598 : : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1599 : :
1360 1600 : 0 : return buf.data;
1601 : : }
1602 : :
1603 : : /*
1604 : : * pg_get_statisticsobjdef
1605 : : * Get the definition of an extended statistics object
1606 : : */
1607 : : Datum
3139 tgl@sss.pgh.pa.us 1608 :CBC 121 : pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
1609 : : {
3190 alvherre@alvh.no-ip. 1610 : 121 : Oid statextid = PG_GETARG_OID(0);
1611 : : char *res;
1612 : :
1727 tomas.vondra@postgre 1613 : 121 : res = pg_get_statisticsobj_worker(statextid, false, true);
1614 : :
1615 [ + + ]: 121 : if (res == NULL)
1616 : 3 : PG_RETURN_NULL();
1617 : :
1618 : 118 : PG_RETURN_TEXT_P(string_to_text(res));
1619 : : }
1620 : :
1621 : : /*
1622 : : * Internal version for use by ALTER TABLE.
1623 : : * Returns a palloc'd C string; no pretty-printing.
1624 : : */
1625 : : char *
1626 : 37 : pg_get_statisticsobjdef_string(Oid statextid)
1627 : : {
1628 : 37 : return pg_get_statisticsobj_worker(statextid, false, false);
1629 : : }
1630 : :
1631 : : /*
1632 : : * pg_get_statisticsobjdef_columns
1633 : : * Get columns and expressions for an extended statistics object
1634 : : */
1635 : : Datum
1636 : 213 : pg_get_statisticsobjdef_columns(PG_FUNCTION_ARGS)
1637 : : {
1638 : 213 : Oid statextid = PG_GETARG_OID(0);
1639 : : char *res;
1640 : :
1641 : 213 : res = pg_get_statisticsobj_worker(statextid, true, true);
1642 : :
3190 alvherre@alvh.no-ip. 1643 [ - + ]: 213 : if (res == NULL)
3190 alvherre@alvh.no-ip. 1644 :UBC 0 : PG_RETURN_NULL();
1645 : :
3190 alvherre@alvh.no-ip. 1646 :CBC 213 : PG_RETURN_TEXT_P(string_to_text(res));
1647 : : }
1648 : :
1649 : : /*
1650 : : * Internal workhorse to decompile an extended statistics object.
1651 : : */
1652 : : static char *
1727 tomas.vondra@postgre 1653 : 371 : pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
1654 : : {
1655 : : Form_pg_statistic_ext statextrec;
1656 : : HeapTuple statexttup;
1657 : : StringInfoData buf;
1658 : : int colno;
1659 : : char *nsp;
1660 : : ArrayType *arr;
1661 : : char *enabled;
1662 : : Datum datum;
1663 : : bool ndistinct_enabled;
1664 : : bool dependencies_enabled;
1665 : : bool mcv_enabled;
1666 : : int i;
1667 : : List *context;
1668 : : ListCell *lc;
1669 : 371 : List *exprs = NIL;
1670 : : bool has_exprs;
1671 : : int ncolumns;
1672 : :
3190 alvherre@alvh.no-ip. 1673 : 371 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1674 : :
1675 [ + + ]: 371 : if (!HeapTupleIsValid(statexttup))
1676 : : {
1677 [ + - ]: 3 : if (missing_ok)
1678 : 3 : return NULL;
3139 tgl@sss.pgh.pa.us 1679 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for statistics object %u", statextid);
1680 : : }
1681 : :
1682 : : /* has the statistics expressions? */
1727 tomas.vondra@postgre 1683 :CBC 368 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
1684 : :
1685 : 368 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1686 : :
1687 : : /*
1688 : : * Get the statistics expressions, if any. (NOTE: we do not use the
1689 : : * relcache versions of the expressions, because we want to display
1690 : : * non-const-folded expressions.)
1691 : : */
1692 [ + + ]: 368 : if (has_exprs)
1693 : : {
1694 : : Datum exprsDatum;
1695 : : char *exprsString;
1696 : :
998 dgustafsson@postgres 1697 : 76 : exprsDatum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1698 : : Anum_pg_statistic_ext_stxexprs);
1727 tomas.vondra@postgre 1699 : 76 : exprsString = TextDatumGetCString(exprsDatum);
1700 : 76 : exprs = (List *) stringToNode(exprsString);
1701 : 76 : pfree(exprsString);
1702 : : }
1703 : : else
1704 : 292 : exprs = NIL;
1705 : :
1706 : : /* count the number of columns (attributes and expressions) */
1707 : 368 : ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
1708 : :
1709 : 368 : initStringInfo(&buf);
1710 : :
1711 [ + + ]: 368 : if (!columns_only)
1712 : : {
1604 tgl@sss.pgh.pa.us 1713 : 155 : nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
1727 tomas.vondra@postgre 1714 : 155 : appendStringInfo(&buf, "CREATE STATISTICS %s",
1715 : : quote_qualified_identifier(nsp,
1716 : 155 : NameStr(statextrec->stxname)));
1717 : :
1718 : : /*
1719 : : * Decode the stxkind column so that we know which stats types to
1720 : : * print.
1721 : : */
998 dgustafsson@postgres 1722 : 155 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1723 : : Anum_pg_statistic_ext_stxkind);
1727 tomas.vondra@postgre 1724 : 155 : arr = DatumGetArrayTypeP(datum);
1725 [ + - ]: 155 : if (ARR_NDIM(arr) != 1 ||
1726 [ + - ]: 155 : ARR_HASNULL(arr) ||
1727 [ - + ]: 155 : ARR_ELEMTYPE(arr) != CHAROID)
1727 tomas.vondra@postgre 1728 [ # # ]:UBC 0 : elog(ERROR, "stxkind is not a 1-D char array");
1727 tomas.vondra@postgre 1729 [ - + ]:CBC 155 : enabled = (char *) ARR_DATA_PTR(arr);
1730 : :
1731 : 155 : ndistinct_enabled = false;
1732 : 155 : dependencies_enabled = false;
1733 : 155 : mcv_enabled = false;
1734 : :
1735 [ + + ]: 431 : for (i = 0; i < ARR_DIMS(arr)[0]; i++)
1736 : : {
1737 [ + + ]: 276 : if (enabled[i] == STATS_EXT_NDISTINCT)
1738 : 90 : ndistinct_enabled = true;
1739 [ + + ]: 186 : else if (enabled[i] == STATS_EXT_DEPENDENCIES)
1740 : 67 : dependencies_enabled = true;
1741 [ + + ]: 119 : else if (enabled[i] == STATS_EXT_MCV)
1742 : 76 : mcv_enabled = true;
1743 : :
1744 : : /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
1745 : : }
1746 : :
1747 : : /*
1748 : : * If any option is disabled, then we'll need to append the types
1749 : : * clause to show which options are enabled. We omit the types clause
1750 : : * on purpose when all options are enabled, so a pg_dump/pg_restore
1751 : : * will create all statistics types on a newer postgres version, if
1752 : : * the statistics had all options enabled on the original version.
1753 : : *
1754 : : * But if the statistics is defined on just a single column, it has to
1755 : : * be an expression statistics. In that case we don't need to specify
1756 : : * kinds.
1757 : : */
1758 [ + + + + : 155 : if ((!ndistinct_enabled || !dependencies_enabled || !mcv_enabled) &&
- + + + ]
1759 : : (ncolumns > 1))
1760 : : {
1761 : 59 : bool gotone = false;
1762 : :
1763 : 59 : appendStringInfoString(&buf, " (");
1764 : :
1765 [ + + ]: 59 : if (ndistinct_enabled)
1766 : : {
1767 : 32 : appendStringInfoString(&buf, "ndistinct");
1768 : 32 : gotone = true;
1769 : : }
1770 : :
1771 [ + + ]: 59 : if (dependencies_enabled)
1772 : : {
1773 [ - + ]: 9 : appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
1774 : 9 : gotone = true;
1775 : : }
1776 : :
1777 [ + + ]: 59 : if (mcv_enabled)
1778 [ - + ]: 18 : appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
1779 : :
1780 : 59 : appendStringInfoChar(&buf, ')');
1781 : : }
1782 : :
1783 : 155 : appendStringInfoString(&buf, " ON ");
1784 : : }
1785 : :
1786 : : /* decode simple column references */
3166 alvherre@alvh.no-ip. 1787 [ + + ]: 1066 : for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
1788 : : {
1789 : 698 : AttrNumber attnum = statextrec->stxkeys.values[colno];
1790 : : char *attname;
1791 : :
3190 1792 [ + + ]: 698 : if (colno > 0)
1793 : 400 : appendStringInfoString(&buf, ", ");
1794 : :
2865 1795 : 698 : attname = get_attname(statextrec->stxrelid, attnum, false);
1796 : :
3190 1797 : 698 : appendStringInfoString(&buf, quote_identifier(attname));
1798 : : }
1799 : :
1727 tomas.vondra@postgre 1800 : 368 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
1801 : : statextrec->stxrelid);
1802 : :
1803 [ + + + + : 487 : foreach(lc, exprs)
+ + ]
1804 : : {
1805 : 119 : Node *expr = (Node *) lfirst(lc);
1806 : : char *str;
1568 1807 : 119 : int prettyFlags = PRETTYFLAG_PAREN;
1808 : :
1727 1809 : 119 : str = deparse_expression_pretty(expr, context, false, false,
1810 : : prettyFlags, 0);
1811 : :
1812 [ + + ]: 119 : if (colno > 0)
1813 : 49 : appendStringInfoString(&buf, ", ");
1814 : :
1815 : : /* Need parens if it's not a bare function call */
1816 [ + + ]: 119 : if (looks_like_function(expr))
1817 : 17 : appendStringInfoString(&buf, str);
1818 : : else
1819 : 102 : appendStringInfo(&buf, "(%s)", str);
1820 : :
1821 : 119 : colno++;
1822 : : }
1823 : :
1824 [ + + ]: 368 : if (!columns_only)
1825 : 155 : appendStringInfo(&buf, " FROM %s",
1826 : : generate_relation_name(statextrec->stxrelid, NIL));
1827 : :
3190 alvherre@alvh.no-ip. 1828 : 368 : ReleaseSysCache(statexttup);
1829 : :
1830 : 368 : return buf.data;
1831 : : }
1832 : :
1833 : : /*
1834 : : * Generate text array of expressions for statistics object.
1835 : : */
1836 : : Datum
1727 tomas.vondra@postgre 1837 : 12 : pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
1838 : : {
1839 : 12 : Oid statextid = PG_GETARG_OID(0);
1840 : : Form_pg_statistic_ext statextrec;
1841 : : HeapTuple statexttup;
1842 : : Datum datum;
1843 : : List *context;
1844 : : ListCell *lc;
1845 : 12 : List *exprs = NIL;
1846 : : bool has_exprs;
1847 : : char *tmp;
1848 : 12 : ArrayBuildState *astate = NULL;
1849 : :
1850 : 12 : statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
1851 : :
1852 [ - + ]: 12 : if (!HeapTupleIsValid(statexttup))
1685 tomas.vondra@postgre 1853 :UBC 0 : PG_RETURN_NULL();
1854 : :
1855 : : /* Does the stats object have expressions? */
1727 tomas.vondra@postgre 1856 :CBC 12 : has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
1857 : :
1858 : : /* no expressions? we're done */
1859 [ + + ]: 12 : if (!has_exprs)
1860 : : {
1861 : 6 : ReleaseSysCache(statexttup);
1862 : 6 : PG_RETURN_NULL();
1863 : : }
1864 : :
1865 : 6 : statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
1866 : :
1867 : : /*
1868 : : * Get the statistics expressions, and deparse them into text values.
1869 : : */
998 dgustafsson@postgres 1870 : 6 : datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
1871 : : Anum_pg_statistic_ext_stxexprs);
1727 tomas.vondra@postgre 1872 : 6 : tmp = TextDatumGetCString(datum);
1873 : 6 : exprs = (List *) stringToNode(tmp);
1874 : 6 : pfree(tmp);
1875 : :
1876 : 6 : context = deparse_context_for(get_relation_name(statextrec->stxrelid),
1877 : : statextrec->stxrelid);
1878 : :
1879 [ + - + + : 18 : foreach(lc, exprs)
+ + ]
1880 : : {
1881 : 12 : Node *expr = (Node *) lfirst(lc);
1882 : : char *str;
1883 : 12 : int prettyFlags = PRETTYFLAG_INDENT;
1884 : :
1885 : 12 : str = deparse_expression_pretty(expr, context, false, false,
1886 : : prettyFlags, 0);
1887 : :
1888 : 12 : astate = accumArrayResult(astate,
1889 : 12 : PointerGetDatum(cstring_to_text(str)),
1890 : : false,
1891 : : TEXTOID,
1892 : : CurrentMemoryContext);
1893 : : }
1894 : :
1895 : 6 : ReleaseSysCache(statexttup);
1896 : :
1897 : 6 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1898 : : }
1899 : :
1900 : : /*
1901 : : * pg_get_partkeydef
1902 : : *
1903 : : * Returns the partition key specification, ie, the following:
1904 : : *
1905 : : * { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
1906 : : */
1907 : : Datum
3297 rhaas@postgresql.org 1908 : 736 : pg_get_partkeydef(PG_FUNCTION_ARGS)
1909 : : {
1910 : 736 : Oid relid = PG_GETARG_OID(0);
1911 : : char *res;
1912 : :
3157 sfrost@snowman.net 1913 : 736 : res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
1914 : :
1915 [ + + ]: 736 : if (res == NULL)
1916 : 3 : PG_RETURN_NULL();
1917 : :
1918 : 733 : PG_RETURN_TEXT_P(string_to_text(res));
1919 : : }
1920 : :
1921 : : /* Internal version that just reports the column definitions */
1922 : : char *
3211 rhaas@postgresql.org 1923 : 71 : pg_get_partkeydef_columns(Oid relid, bool pretty)
1924 : : {
1925 : : int prettyFlags;
1926 : :
1360 tgl@sss.pgh.pa.us 1927 [ + - ]: 71 : prettyFlags = GET_PRETTY_FLAGS(pretty);
1928 : :
3157 sfrost@snowman.net 1929 : 71 : return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
1930 : : }
1931 : :
1932 : : /*
1933 : : * Internal workhorse to decompile a partition key definition.
1934 : : */
1935 : : static char *
3211 rhaas@postgresql.org 1936 : 807 : pg_get_partkeydef_worker(Oid relid, int prettyFlags,
1937 : : bool attrsOnly, bool missing_ok)
1938 : : {
1939 : : Form_pg_partitioned_table form;
1940 : : HeapTuple tuple;
1941 : : oidvector *partclass;
1942 : : oidvector *partcollation;
1943 : : List *partexprs;
1944 : : ListCell *partexpr_item;
1945 : : List *context;
1946 : : Datum datum;
1947 : : StringInfoData buf;
1948 : : int keyno;
1949 : : char *str;
1950 : : char *sep;
1951 : :
3297 1952 : 807 : tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
1953 [ + + ]: 807 : if (!HeapTupleIsValid(tuple))
1954 : : {
3157 sfrost@snowman.net 1955 [ + - ]: 3 : if (missing_ok)
1956 : 3 : return NULL;
3297 rhaas@postgresql.org 1957 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for partition key of %u", relid);
1958 : : }
1959 : :
3297 rhaas@postgresql.org 1960 :CBC 804 : form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
1961 : :
1962 [ - + ]: 804 : Assert(form->partrelid == relid);
1963 : :
1964 : : /* Must get partclass and partcollation the hard way */
998 dgustafsson@postgres 1965 : 804 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1966 : : Anum_pg_partitioned_table_partclass);
3297 rhaas@postgresql.org 1967 : 804 : partclass = (oidvector *) DatumGetPointer(datum);
1968 : :
998 dgustafsson@postgres 1969 : 804 : datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1970 : : Anum_pg_partitioned_table_partcollation);
3297 rhaas@postgresql.org 1971 : 804 : partcollation = (oidvector *) DatumGetPointer(datum);
1972 : :
1973 : :
1974 : : /*
1975 : : * Get the expressions, if any. (NOTE: we do not use the relcache
1976 : : * versions of the expressions, because we want to display
1977 : : * non-const-folded expressions.)
1978 : : */
2821 andrew@dunslane.net 1979 [ + + ]: 804 : if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprs, NULL))
1980 : : {
1981 : : Datum exprsDatum;
1982 : : char *exprsString;
1983 : :
998 dgustafsson@postgres 1984 : 73 : exprsDatum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1985 : : Anum_pg_partitioned_table_partexprs);
3297 rhaas@postgresql.org 1986 : 73 : exprsString = TextDatumGetCString(exprsDatum);
1987 : 73 : partexprs = (List *) stringToNode(exprsString);
1988 : :
1989 [ - + ]: 73 : if (!IsA(partexprs, List))
3297 rhaas@postgresql.org 1990 [ # # ]:UBC 0 : elog(ERROR, "unexpected node type found in partexprs: %d",
1991 : : (int) nodeTag(partexprs));
1992 : :
3297 rhaas@postgresql.org 1993 :CBC 73 : pfree(exprsString);
1994 : : }
1995 : : else
1996 : 731 : partexprs = NIL;
1997 : :
1998 : 804 : partexpr_item = list_head(partexprs);
1999 : 804 : context = deparse_context_for(get_relation_name(relid), relid);
2000 : :
2001 : 804 : initStringInfo(&buf);
2002 : :
2003 [ + + + - ]: 804 : switch (form->partstrat)
2004 : : {
2960 2005 : 58 : case PARTITION_STRATEGY_HASH:
2006 [ + - ]: 58 : if (!attrsOnly)
2358 drowley@postgresql.o 2007 : 58 : appendStringInfoString(&buf, "HASH");
2960 rhaas@postgresql.org 2008 : 58 : break;
3297 2009 : 293 : case PARTITION_STRATEGY_LIST:
3211 2010 [ + + ]: 293 : if (!attrsOnly)
3046 peter_e@gmx.net 2011 : 273 : appendStringInfoString(&buf, "LIST");
3297 rhaas@postgresql.org 2012 : 293 : break;
2013 : 453 : case PARTITION_STRATEGY_RANGE:
3211 2014 [ + + ]: 453 : if (!attrsOnly)
3046 peter_e@gmx.net 2015 : 402 : appendStringInfoString(&buf, "RANGE");
3297 rhaas@postgresql.org 2016 : 453 : break;
3297 rhaas@postgresql.org 2017 :UBC 0 : default:
2018 [ # # ]: 0 : elog(ERROR, "unexpected partition strategy: %d",
2019 : : (int) form->partstrat);
2020 : : }
2021 : :
3211 rhaas@postgresql.org 2022 [ + + ]:CBC 804 : if (!attrsOnly)
3046 peter_e@gmx.net 2023 : 733 : appendStringInfoString(&buf, " (");
3297 rhaas@postgresql.org 2024 : 804 : sep = "";
2025 [ + + ]: 1684 : for (keyno = 0; keyno < form->partnatts; keyno++)
2026 : : {
2027 : 880 : AttrNumber attnum = form->partattrs.values[keyno];
2028 : : Oid keycoltype;
2029 : : Oid keycolcollation;
2030 : : Oid partcoll;
2031 : :
2032 : 880 : appendStringInfoString(&buf, sep);
2033 : 880 : sep = ", ";
2034 [ + + ]: 880 : if (attnum != 0)
2035 : : {
2036 : : /* Simple attribute reference */
2037 : : char *attname;
2038 : : int32 keycoltypmod;
2039 : :
2865 alvherre@alvh.no-ip. 2040 : 801 : attname = get_attname(relid, attnum, false);
3297 rhaas@postgresql.org 2041 : 801 : appendStringInfoString(&buf, quote_identifier(attname));
2042 : 801 : get_atttypetypmodcoll(relid, attnum,
2043 : : &keycoltype, &keycoltypmod,
2044 : : &keycolcollation);
2045 : : }
2046 : : else
2047 : : {
2048 : : /* Expression */
2049 : : Node *partkey;
2050 : :
2051 [ - + ]: 79 : if (partexpr_item == NULL)
3297 rhaas@postgresql.org 2052 [ # # ]:UBC 0 : elog(ERROR, "too few entries in partexprs list");
3297 rhaas@postgresql.org 2053 :CBC 79 : partkey = (Node *) lfirst(partexpr_item);
2347 tgl@sss.pgh.pa.us 2054 : 79 : partexpr_item = lnext(partexprs, partexpr_item);
2055 : :
2056 : : /* Deparse */
3297 rhaas@postgresql.org 2057 : 79 : str = deparse_expression_pretty(partkey, context, false, false,
2058 : : prettyFlags, 0);
2059 : : /* Need parens if it's not a bare function call */
3079 tgl@sss.pgh.pa.us 2060 [ + + ]: 79 : if (looks_like_function(partkey))
2061 : 28 : appendStringInfoString(&buf, str);
2062 : : else
2063 : 51 : appendStringInfo(&buf, "(%s)", str);
2064 : :
3297 rhaas@postgresql.org 2065 : 79 : keycoltype = exprType(partkey);
2066 : 79 : keycolcollation = exprCollation(partkey);
2067 : : }
2068 : :
2069 : : /* Add collation, if not default for column */
2070 : 880 : partcoll = partcollation->values[keyno];
3211 2071 [ + + + + : 880 : if (!attrsOnly && OidIsValid(partcoll) && partcoll != keycolcollation)
+ + ]
3297 2072 : 3 : appendStringInfo(&buf, " COLLATE %s",
2073 : : generate_collation_name((partcoll)));
2074 : :
2075 : : /* Add the operator class name, if not default */
3211 2076 [ + + ]: 880 : if (!attrsOnly)
2077 : 782 : get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2078 : : }
2079 : :
2080 [ + + ]: 804 : if (!attrsOnly)
2081 : 733 : appendStringInfoChar(&buf, ')');
2082 : :
2083 : : /* Clean up */
3297 2084 : 804 : ReleaseSysCache(tuple);
2085 : :
2086 : 804 : return buf.data;
2087 : : }
2088 : :
2089 : : /*
2090 : : * pg_get_partition_constraintdef
2091 : : *
2092 : : * Returns partition constraint expression as a string for the input relation
2093 : : */
2094 : : Datum
3140 2095 : 115 : pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
2096 : : {
3136 bruce@momjian.us 2097 : 115 : Oid relationId = PG_GETARG_OID(0);
2098 : : Expr *constr_expr;
2099 : : int prettyFlags;
2100 : : List *context;
2101 : : char *consrc;
2102 : :
3140 rhaas@postgresql.org 2103 : 115 : constr_expr = get_partition_qual_relid(relationId);
2104 : :
2105 : : /* Quick exit if no partition constraint */
2106 [ + + ]: 115 : if (constr_expr == NULL)
2107 : 12 : PG_RETURN_NULL();
2108 : :
2109 : : /*
2110 : : * Deparse and return the constraint expression.
2111 : : */
2112 : 103 : prettyFlags = PRETTYFLAG_INDENT;
2113 : 103 : context = deparse_context_for(get_relation_name(relationId), relationId);
2114 : 103 : consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2115 : : false, prettyFlags, 0);
2116 : :
2117 : 103 : PG_RETURN_TEXT_P(string_to_text(consrc));
2118 : : }
2119 : :
2120 : : /*
2121 : : * pg_get_partconstrdef_string
2122 : : *
2123 : : * Returns the partition constraint as a C-string for the input relation, with
2124 : : * the given alias. No pretty-printing.
2125 : : */
2126 : : char *
2450 alvherre@alvh.no-ip. 2127 : 55 : pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
2128 : : {
2129 : : Expr *constr_expr;
2130 : : List *context;
2131 : :
2132 : 55 : constr_expr = get_partition_qual_relid(partitionId);
2133 : 55 : context = deparse_context_for(aliasname, partitionId);
2134 : :
2135 : 55 : return deparse_expression((Node *) constr_expr, context, true, false);
2136 : : }
2137 : :
2138 : : /*
2139 : : * pg_get_constraintdef
2140 : : *
2141 : : * Returns the definition for the constraint, ie, everything that needs to
2142 : : * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
2143 : : */
2144 : : Datum
8524 tgl@sss.pgh.pa.us 2145 : 1093 : pg_get_constraintdef(PG_FUNCTION_ARGS)
2146 : : {
8505 bruce@momjian.us 2147 : 1093 : Oid constraintId = PG_GETARG_OID(0);
2148 : : int prettyFlags;
2149 : : char *res;
2150 : :
4700 tgl@sss.pgh.pa.us 2151 : 1093 : prettyFlags = PRETTYFLAG_INDENT;
2152 : :
3431 rhaas@postgresql.org 2153 : 1093 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2154 : :
2155 [ + + ]: 1093 : if (res == NULL)
2156 : 3 : PG_RETURN_NULL();
2157 : :
2158 : 1090 : PG_RETURN_TEXT_P(string_to_text(res));
2159 : : }
2160 : :
2161 : : Datum
8176 tgl@sss.pgh.pa.us 2162 : 2418 : pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
2163 : : {
2164 : 2418 : Oid constraintId = PG_GETARG_OID(0);
2165 : 2418 : bool pretty = PG_GETARG_BOOL(1);
2166 : : int prettyFlags;
2167 : : char *res;
2168 : :
1360 2169 [ + + ]: 2418 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2170 : :
3431 rhaas@postgresql.org 2171 : 2418 : res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2172 : :
2173 [ - + ]: 2418 : if (res == NULL)
3431 rhaas@postgresql.org 2174 :UBC 0 : PG_RETURN_NULL();
2175 : :
3431 rhaas@postgresql.org 2176 :CBC 2418 : PG_RETURN_TEXT_P(string_to_text(res));
2177 : : }
2178 : :
2179 : : /*
2180 : : * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
2181 : : */
2182 : : char *
3680 tgl@sss.pgh.pa.us 2183 : 304 : pg_get_constraintdef_command(Oid constraintId)
2184 : : {
3431 rhaas@postgresql.org 2185 : 304 : return pg_get_constraintdef_worker(constraintId, true, 0, false);
2186 : : }
2187 : :
2188 : : /*
2189 : : * As of 9.4, we now use an MVCC snapshot for this.
2190 : : */
2191 : : static char *
7896 tgl@sss.pgh.pa.us 2192 : 3815 : pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
2193 : : int prettyFlags, bool missing_ok)
2194 : : {
2195 : : HeapTuple tup;
2196 : : Form_pg_constraint conForm;
2197 : : StringInfoData buf;
2198 : : SysScanDesc scandesc;
2199 : : ScanKeyData scankey[1];
4243 bruce@momjian.us 2200 : 3815 : Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());
2522 andres@anarazel.de 2201 : 3815 : Relation relation = table_open(ConstraintRelationId, AccessShareLock);
2202 : :
4273 simon@2ndQuadrant.co 2203 : 3815 : ScanKeyInit(&scankey[0],
2204 : : Anum_pg_constraint_oid,
2205 : : BTEqualStrategyNumber, F_OIDEQ,
2206 : : ObjectIdGetDatum(constraintId));
2207 : :
2208 : 3815 : scandesc = systable_beginscan(relation,
2209 : : ConstraintOidIndexId,
2210 : : true,
2211 : : snapshot,
2212 : : 1,
2213 : : scankey);
2214 : :
2215 : : /*
2216 : : * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2217 : : * via SearchSysCache, which works fine.
2218 : : */
2219 : 3815 : tup = systable_getnext(scandesc);
2220 : :
2221 : 3815 : UnregisterSnapshot(snapshot);
2222 : :
3431 rhaas@postgresql.org 2223 [ + + ]: 3815 : if (!HeapTupleIsValid(tup))
2224 : : {
2225 [ + - ]: 3 : if (missing_ok)
2226 : : {
2227 : 3 : systable_endscan(scandesc);
2522 andres@anarazel.de 2228 : 3 : table_close(relation, AccessShareLock);
3431 rhaas@postgresql.org 2229 : 3 : return NULL;
2230 : : }
3118 tgl@sss.pgh.pa.us 2231 [ # # ]:UBC 0 : elog(ERROR, "could not find tuple for constraint %u", constraintId);
2232 : : }
2233 : :
8524 tgl@sss.pgh.pa.us 2234 :CBC 3812 : conForm = (Form_pg_constraint) GETSTRUCT(tup);
2235 : :
2236 : 3812 : initStringInfo(&buf);
2237 : :
3680 2238 [ + + ]: 3812 : if (fullCommand)
2239 : : {
2968 2240 [ + + ]: 304 : if (OidIsValid(conForm->conrelid))
2241 : : {
2242 : : /*
2243 : : * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2244 : : * constraints, and other types of constraints don't inherit
2245 : : * anyway so it doesn't matter whether we say ONLY or not. Someday
2246 : : * we might need to let callers specify whether to put ONLY in the
2247 : : * command.
2248 : : */
2249 : 297 : appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2250 : : generate_qualified_relation_name(conForm->conrelid),
2251 : 297 : quote_identifier(NameStr(conForm->conname)));
2252 : : }
2253 : : else
2254 : : {
2255 : : /* Must be a domain constraint */
2256 [ - + ]: 7 : Assert(OidIsValid(conForm->contypid));
2257 : 7 : appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2258 : : generate_qualified_type_name(conForm->contypid),
2259 : 7 : quote_identifier(NameStr(conForm->conname)));
2260 : : }
2261 : : }
2262 : :
8524 2263 [ + + + + : 3812 : switch (conForm->contype)
- + - ]
2264 : : {
2265 : 386 : case CONSTRAINT_FOREIGN:
2266 : : {
2267 : : Datum val;
2268 : : bool isnull;
2269 : : const char *string;
2270 : :
2271 : : /* Start off the constraint definition */
4430 rhaas@postgresql.org 2272 : 386 : appendStringInfoString(&buf, "FOREIGN KEY (");
2273 : :
2274 : : /* Fetch and build referencing-column list */
998 dgustafsson@postgres 2275 : 386 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2276 : : Anum_pg_constraint_conkey);
2277 : :
2278 : : /* If it is a temporal foreign key then it uses PERIOD. */
456 peter@eisentraut.org 2279 : 386 : decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
2280 : :
2281 : : /* add foreign relation name */
8505 bruce@momjian.us 2282 : 386 : appendStringInfo(&buf, ") REFERENCES %s(",
2283 : : generate_relation_name(conForm->confrelid,
2284 : : NIL));
2285 : :
2286 : : /* Fetch and build referenced-column list */
998 dgustafsson@postgres 2287 : 386 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2288 : : Anum_pg_constraint_confkey);
2289 : :
456 peter@eisentraut.org 2290 : 386 : decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
2291 : :
4430 rhaas@postgresql.org 2292 : 386 : appendStringInfoChar(&buf, ')');
2293 : :
2294 : : /* Add match type */
8505 bruce@momjian.us 2295 [ + - + - ]: 386 : switch (conForm->confmatchtype)
2296 : : {
2297 : 17 : case FKCONSTR_MATCH_FULL:
2298 : 17 : string = " MATCH FULL";
2299 : 17 : break;
8505 bruce@momjian.us 2300 :UBC 0 : case FKCONSTR_MATCH_PARTIAL:
2301 : 0 : string = " MATCH PARTIAL";
2302 : 0 : break;
4931 tgl@sss.pgh.pa.us 2303 :CBC 369 : case FKCONSTR_MATCH_SIMPLE:
8505 bruce@momjian.us 2304 : 369 : string = "";
2305 : 369 : break;
8505 bruce@momjian.us 2306 :UBC 0 : default:
8179 tgl@sss.pgh.pa.us 2307 [ # # ]: 0 : elog(ERROR, "unrecognized confmatchtype: %d",
2308 : : conForm->confmatchtype);
2309 : : string = ""; /* keep compiler quiet */
2310 : : break;
2311 : : }
7991 neilc@samurai.com 2312 :CBC 386 : appendStringInfoString(&buf, string);
2313 : :
2314 : : /* Add ON UPDATE and ON DELETE clauses, if needed */
8505 bruce@momjian.us 2315 [ + - + + : 386 : switch (conForm->confupdtype)
- - ]
2316 : : {
2317 : 318 : case FKCONSTR_ACTION_NOACTION:
8171 2318 : 318 : string = NULL; /* suppress default */
8505 2319 : 318 : break;
8505 bruce@momjian.us 2320 :UBC 0 : case FKCONSTR_ACTION_RESTRICT:
2321 : 0 : string = "RESTRICT";
2322 : 0 : break;
8505 bruce@momjian.us 2323 :CBC 54 : case FKCONSTR_ACTION_CASCADE:
2324 : 54 : string = "CASCADE";
2325 : 54 : break;
2326 : 14 : case FKCONSTR_ACTION_SETNULL:
2327 : 14 : string = "SET NULL";
2328 : 14 : break;
8505 bruce@momjian.us 2329 :UBC 0 : case FKCONSTR_ACTION_SETDEFAULT:
2330 : 0 : string = "SET DEFAULT";
2331 : 0 : break;
2332 : 0 : default:
8179 tgl@sss.pgh.pa.us 2333 [ # # ]: 0 : elog(ERROR, "unrecognized confupdtype: %d",
2334 : : conForm->confupdtype);
2335 : : string = NULL; /* keep compiler quiet */
2336 : : break;
2337 : : }
8353 tgl@sss.pgh.pa.us 2338 [ + + ]:CBC 386 : if (string)
bruce@momjian.us 2339 : 68 : appendStringInfo(&buf, " ON UPDATE %s", string);
2340 : :
8505 2341 [ + - + + : 386 : switch (conForm->confdeltype)
+ - ]
2342 : : {
2343 : 320 : case FKCONSTR_ACTION_NOACTION:
8171 2344 : 320 : string = NULL; /* suppress default */
8505 2345 : 320 : break;
8505 bruce@momjian.us 2346 :UBC 0 : case FKCONSTR_ACTION_RESTRICT:
2347 : 0 : string = "RESTRICT";
2348 : 0 : break;
8505 bruce@momjian.us 2349 :CBC 54 : case FKCONSTR_ACTION_CASCADE:
2350 : 54 : string = "CASCADE";
2351 : 54 : break;
2352 : 9 : case FKCONSTR_ACTION_SETNULL:
2353 : 9 : string = "SET NULL";
2354 : 9 : break;
2355 : 3 : case FKCONSTR_ACTION_SETDEFAULT:
2356 : 3 : string = "SET DEFAULT";
2357 : 3 : break;
8505 bruce@momjian.us 2358 :UBC 0 : default:
8179 tgl@sss.pgh.pa.us 2359 [ # # ]: 0 : elog(ERROR, "unrecognized confdeltype: %d",
2360 : : conForm->confdeltype);
2361 : : string = NULL; /* keep compiler quiet */
2362 : : break;
2363 : : }
8353 tgl@sss.pgh.pa.us 2364 [ + + ]:CBC 386 : if (string)
bruce@momjian.us 2365 : 66 : appendStringInfo(&buf, " ON DELETE %s", string);
2366 : :
2367 : : /*
2368 : : * Add columns specified to SET NULL or SET DEFAULT if
2369 : : * provided.
2370 : : */
1470 peter@eisentraut.org 2371 : 386 : val = SysCacheGetAttr(CONSTROID, tup,
2372 : : Anum_pg_constraint_confdelsetcols, &isnull);
2373 [ + + ]: 386 : if (!isnull)
2374 : : {
1198 drowley@postgresql.o 2375 : 6 : appendStringInfoString(&buf, " (");
456 peter@eisentraut.org 2376 : 6 : decompile_column_index_array(val, conForm->conrelid, false, &buf);
1198 drowley@postgresql.o 2377 : 6 : appendStringInfoChar(&buf, ')');
2378 : : }
2379 : :
8505 bruce@momjian.us 2380 : 386 : break;
2381 : : }
8343 2382 : 1985 : case CONSTRAINT_PRIMARY:
2383 : : case CONSTRAINT_UNIQUE:
2384 : : {
2385 : : Datum val;
2386 : : Oid indexId;
2387 : : int keyatts;
2388 : : HeapTuple indtup;
2389 : :
2390 : : /* Start off the constraint definition */
2391 [ + + ]: 1985 : if (conForm->contype == CONSTRAINT_PRIMARY)
1413 peter@eisentraut.org 2392 : 1618 : appendStringInfoString(&buf, "PRIMARY KEY ");
2393 : : else
2394 : 367 : appendStringInfoString(&buf, "UNIQUE ");
2395 : :
2396 : 1985 : indexId = conForm->conindid;
2397 : :
2398 : 1985 : indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
2399 [ - + ]: 1985 : if (!HeapTupleIsValid(indtup))
1413 peter@eisentraut.org 2400 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for index %u", indexId);
1413 peter@eisentraut.org 2401 [ + + ]:CBC 1985 : if (conForm->contype == CONSTRAINT_UNIQUE &&
2402 [ - + ]: 367 : ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
1413 peter@eisentraut.org 2403 :UBC 0 : appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2404 : :
1198 drowley@postgresql.o 2405 :CBC 1985 : appendStringInfoChar(&buf, '(');
2406 : :
2407 : : /* Fetch and build target column list */
998 dgustafsson@postgres 2408 : 1985 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2409 : : Anum_pg_constraint_conkey);
2410 : :
456 peter@eisentraut.org 2411 : 1985 : keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
2412 [ + + ]: 1985 : if (conForm->conperiod)
2413 : 191 : appendStringInfoString(&buf, " WITHOUT OVERLAPS");
2414 : :
4430 rhaas@postgresql.org 2415 : 1985 : appendStringInfoChar(&buf, ')');
2416 : :
2417 : : /* Build including column list (from pg_index.indkeys) */
998 dgustafsson@postgres 2418 : 1985 : val = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2419 : : Anum_pg_index_indnatts);
2662 alvherre@alvh.no-ip. 2420 [ + + ]: 1985 : if (DatumGetInt32(val) > keyatts)
2421 : : {
2422 : : Datum cols;
2423 : : Datum *keys;
2424 : : int nKeys;
2425 : : int j;
2426 : :
2811 teodor@sigaev.ru 2427 : 41 : appendStringInfoString(&buf, " INCLUDE (");
2428 : :
998 dgustafsson@postgres 2429 : 41 : cols = SysCacheGetAttrNotNull(INDEXRELID, indtup,
2430 : : Anum_pg_index_indkey);
2431 : :
1265 peter@eisentraut.org 2432 : 41 : deconstruct_array_builtin(DatumGetArrayTypeP(cols), INT2OID,
2433 : : &keys, NULL, &nKeys);
2434 : :
2662 alvherre@alvh.no-ip. 2435 [ + + ]: 123 : for (j = keyatts; j < nKeys; j++)
2436 : : {
2437 : : char *colName;
2438 : :
2439 : 82 : colName = get_attname(conForm->conrelid,
2440 : 82 : DatumGetInt16(keys[j]), false);
2441 [ + + ]: 82 : if (j > keyatts)
2442 : 41 : appendStringInfoString(&buf, ", ");
2443 : 82 : appendStringInfoString(&buf, quote_identifier(colName));
2444 : : }
2445 : :
2811 teodor@sigaev.ru 2446 : 41 : appendStringInfoChar(&buf, ')');
2447 : : }
2662 alvherre@alvh.no-ip. 2448 : 1985 : ReleaseSysCache(indtup);
2449 : :
2450 : : /* XXX why do we only print these bits if fullCommand? */
6640 tgl@sss.pgh.pa.us 2451 [ + + + - ]: 1985 : if (fullCommand && OidIsValid(indexId))
2452 : : {
2453 : 102 : char *options = flatten_reloptions(indexId);
2454 : : Oid tblspc;
2455 : :
7108 bruce@momjian.us 2456 [ - + ]: 102 : if (options)
2457 : : {
7108 bruce@momjian.us 2458 :UBC 0 : appendStringInfo(&buf, " WITH (%s)", options);
2459 : 0 : pfree(options);
2460 : : }
2461 : :
2462 : : /*
2463 : : * Print the tablespace, unless it's the database default.
2464 : : * This is to help ALTER TABLE usage of this facility,
2465 : : * which needs this behavior to recreate exact catalog
2466 : : * state.
2467 : : */
6640 tgl@sss.pgh.pa.us 2468 :CBC 102 : tblspc = get_rel_tablespace(indexId);
2469 [ + + ]: 102 : if (OidIsValid(tblspc))
2470 : 12 : appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
3101 2471 : 12 : quote_identifier(get_tablespace_name(tblspc)));
2472 : : }
2473 : :
8343 bruce@momjian.us 2474 : 1985 : break;
2475 : : }
2476 : 1167 : case CONSTRAINT_CHECK:
2477 : : {
2478 : : Datum val;
2479 : : char *conbin;
2480 : : char *consrc;
2481 : : Node *expr;
2482 : : List *context;
2483 : :
2484 : : /* Fetch constraint expression in parsetree form */
998 dgustafsson@postgres 2485 : 1167 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2486 : : Anum_pg_constraint_conbin);
2487 : :
6476 tgl@sss.pgh.pa.us 2488 : 1167 : conbin = TextDatumGetCString(val);
8211 bruce@momjian.us 2489 : 1167 : expr = stringToNode(conbin);
2490 : :
2491 : : /* Set up deparsing context for Var nodes in constraint */
2492 [ + + ]: 1167 : if (conForm->conrelid != InvalidOid)
2493 : : {
2494 : : /* relation constraint */
5524 tgl@sss.pgh.pa.us 2495 : 1041 : context = deparse_context_for(get_relation_name(conForm->conrelid),
2496 : : conForm->conrelid);
2497 : : }
2498 : : else
2499 : : {
2500 : : /* domain constraint --- can't have Vars */
8110 2501 : 126 : context = NIL;
2502 : : }
2503 : :
8176 2504 : 1167 : consrc = deparse_expression_pretty(expr, context, false, false,
2505 : : prettyFlags, 0);
2506 : :
2507 : : /*
2508 : : * Now emit the constraint definition, adding NO INHERIT if
2509 : : * necessary.
2510 : : *
2511 : : * There are cases where the constraint expression will be
2512 : : * fully parenthesized and we don't need the outer parens ...
2513 : : * but there are other cases where we do need 'em. Be
2514 : : * conservative for now.
2515 : : *
2516 : : * Note that simply checking for leading '(' and trailing ')'
2517 : : * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2518 : : */
4894 alvherre@alvh.no-ip. 2519 : 1167 : appendStringInfo(&buf, "CHECK (%s)%s",
2520 : : consrc,
2521 [ + + ]: 1167 : conForm->connoinherit ? " NO INHERIT" : "");
8343 bruce@momjian.us 2522 : 1167 : break;
2523 : : }
404 alvherre@alvh.no-ip. 2524 : 222 : case CONSTRAINT_NOTNULL:
2525 : : {
2526 [ + + ]: 222 : if (conForm->conrelid)
2527 : : {
2528 : : AttrNumber attnum;
2529 : :
2530 : 166 : attnum = extractNotNullColumn(tup);
2531 : :
2532 : 166 : appendStringInfo(&buf, "NOT NULL %s",
2533 : 166 : quote_identifier(get_attname(conForm->conrelid,
2534 : : attnum, false)));
2535 [ - + ]: 166 : if (((Form_pg_constraint) GETSTRUCT(tup))->connoinherit)
404 alvherre@alvh.no-ip. 2536 :UBC 0 : appendStringInfoString(&buf, " NO INHERIT");
2537 : : }
404 alvherre@alvh.no-ip. 2538 [ + - ]:CBC 56 : else if (conForm->contypid)
2539 : : {
2540 : : /* conkey is null for domain not-null constraints */
2541 : 56 : appendStringInfoString(&buf, "NOT NULL");
2542 : : }
2543 : 222 : break;
2544 : : }
2545 : :
5813 tgl@sss.pgh.pa.us 2546 :UBC 0 : case CONSTRAINT_TRIGGER:
2547 : :
2548 : : /*
2549 : : * There isn't an ALTER TABLE syntax for creating a user-defined
2550 : : * constraint trigger, but it seems better to print something than
2551 : : * throw an error; if we throw error then this function couldn't
2552 : : * safely be applied to all rows of pg_constraint.
2553 : : */
4430 rhaas@postgresql.org 2554 : 0 : appendStringInfoString(&buf, "TRIGGER");
5813 tgl@sss.pgh.pa.us 2555 : 0 : break;
5854 tgl@sss.pgh.pa.us 2556 :CBC 52 : case CONSTRAINT_EXCLUSION:
2557 : : {
5773 bruce@momjian.us 2558 : 52 : Oid indexOid = conForm->conindid;
2559 : : Datum val;
2560 : : Datum *elems;
2561 : : int nElems;
2562 : : int i;
2563 : : Oid *operators;
2564 : :
2565 : : /* Extract operator OIDs from the pg_constraint tuple */
998 dgustafsson@postgres 2566 : 52 : val = SysCacheGetAttrNotNull(CONSTROID, tup,
2567 : : Anum_pg_constraint_conexclop);
2568 : :
1265 peter@eisentraut.org 2569 : 52 : deconstruct_array_builtin(DatumGetArrayTypeP(val), OIDOID,
2570 : : &elems, NULL, &nElems);
2571 : :
5854 tgl@sss.pgh.pa.us 2572 : 52 : operators = (Oid *) palloc(nElems * sizeof(Oid));
2573 [ + + ]: 114 : for (i = 0; i < nElems; i++)
2574 : 62 : operators[i] = DatumGetObjectId(elems[i]);
2575 : :
2576 : : /* pg_get_indexdef_worker does the rest */
2577 : : /* suppress tablespace because pg_dump wants it that way */
2578 : 52 : appendStringInfoString(&buf,
2579 : 52 : pg_get_indexdef_worker(indexOid,
2580 : : 0,
2581 : : operators,
2582 : : false,
2583 : : false,
2584 : : false,
2585 : : false,
2586 : : prettyFlags,
2587 : : false));
2588 : 52 : break;
2589 : : }
8524 tgl@sss.pgh.pa.us 2590 :UBC 0 : default:
8129 peter_e@gmx.net 2591 [ # # ]: 0 : elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2592 : : break;
2593 : : }
2594 : :
5985 tgl@sss.pgh.pa.us 2595 [ + + ]:CBC 3812 : if (conForm->condeferrable)
4430 rhaas@postgresql.org 2596 : 60 : appendStringInfoString(&buf, " DEFERRABLE");
5985 tgl@sss.pgh.pa.us 2597 [ + + ]: 3812 : if (conForm->condeferred)
4430 rhaas@postgresql.org 2598 : 24 : appendStringInfoString(&buf, " INITIALLY DEFERRED");
2599 : :
2600 : : /* Validated status is irrelevant when the constraint is NOT ENFORCED. */
340 peter@eisentraut.org 2601 [ + + ]: 3812 : if (!conForm->conenforced)
2602 : 61 : appendStringInfoString(&buf, " NOT ENFORCED");
2603 [ + + ]: 3751 : else if (!conForm->convalidated)
5312 alvherre@alvh.no-ip. 2604 : 139 : appendStringInfoString(&buf, " NOT VALID");
2605 : :
2606 : : /* Cleanup */
4273 simon@2ndQuadrant.co 2607 : 3812 : systable_endscan(scandesc);
2522 andres@anarazel.de 2608 : 3812 : table_close(relation, AccessShareLock);
2609 : :
7896 tgl@sss.pgh.pa.us 2610 : 3812 : return buf.data;
2611 : : }
2612 : :
2613 : :
2614 : : /*
2615 : : * Convert an int16[] Datum into a comma-separated list of column names
2616 : : * for the indicated relation; append the list to buf. Returns the number
2617 : : * of keys.
2618 : : */
2619 : : static int
8524 2620 : 2763 : decompile_column_index_array(Datum column_index_array, Oid relId,
2621 : : bool withPeriod, StringInfo buf)
2622 : : {
2623 : : Datum *keys;
2624 : : int nKeys;
2625 : : int j;
2626 : :
2627 : : /* Extract data from array of int16 */
1265 peter@eisentraut.org 2628 : 2763 : deconstruct_array_builtin(DatumGetArrayTypeP(column_index_array), INT2OID,
2629 : : &keys, NULL, &nKeys);
2630 : :
8524 tgl@sss.pgh.pa.us 2631 [ + + ]: 6664 : for (j = 0; j < nKeys; j++)
2632 : : {
2633 : : char *colName;
2634 : :
2865 alvherre@alvh.no-ip. 2635 : 3901 : colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2636 : :
8524 tgl@sss.pgh.pa.us 2637 [ + + ]: 3901 : if (j == 0)
7991 neilc@samurai.com 2638 : 2763 : appendStringInfoString(buf, quote_identifier(colName));
2639 : : else
456 peter@eisentraut.org 2640 [ + + ]: 1252 : appendStringInfo(buf, ", %s%s",
2641 [ + + ]: 114 : (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
2642 : : quote_identifier(colName));
2643 : : }
2644 : :
2662 alvherre@alvh.no-ip. 2645 : 2763 : return nKeys;
2646 : : }
2647 : :
2648 : :
2649 : : /* ----------
2650 : : * pg_get_expr - Decompile an expression tree
2651 : : *
2652 : : * Input: an expression tree in nodeToString form, and a relation OID
2653 : : *
2654 : : * Output: reverse-listed expression
2655 : : *
2656 : : * Currently, the expression can only refer to a single relation, namely
2657 : : * the one specified by the second parameter. This is sufficient for
2658 : : * partial indexes, column default expressions, etc. We also support
2659 : : * Var-free expressions, for which the OID can be InvalidOid.
2660 : : *
2661 : : * If the OID is nonzero but not actually valid, don't throw an error,
2662 : : * just return NULL. This is a bit questionable, but it's what we've
2663 : : * done historically, and it can help avoid unwanted failures when
2664 : : * examining catalog entries for just-deleted relations.
2665 : : *
2666 : : * We expect this function to work, or throw a reasonably clean error,
2667 : : * for any node tree that can appear in a catalog pg_node_tree column.
2668 : : * Query trees, such as those appearing in pg_rewrite.ev_action, are
2669 : : * not supported. Nor are expressions in more than one relation, which
2670 : : * can appear in places like pg_rewrite.ev_qual.
2671 : : * ----------
2672 : : */
2673 : : Datum
8920 tgl@sss.pgh.pa.us 2674 : 4626 : pg_get_expr(PG_FUNCTION_ARGS)
2675 : : {
3202 noah@leadboat.com 2676 : 4626 : text *expr = PG_GETARG_TEXT_PP(0);
8171 bruce@momjian.us 2677 : 4626 : Oid relid = PG_GETARG_OID(1);
2678 : : text *result;
2679 : : int prettyFlags;
2680 : :
4700 tgl@sss.pgh.pa.us 2681 : 4626 : prettyFlags = PRETTYFLAG_INDENT;
2682 : :
677 2683 : 4626 : result = pg_get_expr_worker(expr, relid, prettyFlags);
2684 [ + - ]: 4626 : if (result)
2685 : 4626 : PG_RETURN_TEXT_P(result);
2686 : : else
677 tgl@sss.pgh.pa.us 2687 :UBC 0 : PG_RETURN_NULL();
2688 : : }
2689 : :
2690 : : Datum
8176 tgl@sss.pgh.pa.us 2691 :CBC 422 : pg_get_expr_ext(PG_FUNCTION_ARGS)
2692 : : {
3202 noah@leadboat.com 2693 : 422 : text *expr = PG_GETARG_TEXT_PP(0);
8171 bruce@momjian.us 2694 : 422 : Oid relid = PG_GETARG_OID(1);
8176 tgl@sss.pgh.pa.us 2695 : 422 : bool pretty = PG_GETARG_BOOL(2);
2696 : : text *result;
2697 : : int prettyFlags;
2698 : :
1360 2699 [ + - ]: 422 : prettyFlags = GET_PRETTY_FLAGS(pretty);
2700 : :
677 2701 : 422 : result = pg_get_expr_worker(expr, relid, prettyFlags);
2702 [ + - ]: 422 : if (result)
2703 : 422 : PG_RETURN_TEXT_P(result);
2704 : : else
677 tgl@sss.pgh.pa.us 2705 :UBC 0 : PG_RETURN_NULL();
2706 : : }
2707 : :
2708 : : static text *
677 tgl@sss.pgh.pa.us 2709 :CBC 5048 : pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
2710 : : {
2711 : : Node *node;
2712 : : Node *tst;
2713 : : Relids relids;
2714 : : List *context;
2715 : : char *exprstr;
2716 : 5048 : Relation rel = NULL;
2717 : : char *str;
2718 : :
2719 : : /* Convert input pg_node_tree (really TEXT) object to C string */
6476 2720 : 5048 : exprstr = text_to_cstring(expr);
2721 : :
2722 : : /* Convert expression to node tree */
8920 2723 : 5048 : node = (Node *) stringToNode(exprstr);
2724 : :
6049 2725 : 5048 : pfree(exprstr);
2726 : :
2727 : : /*
2728 : : * Throw error if the input is a querytree rather than an expression tree.
2729 : : * While we could support queries here, there seems no very good reason
2730 : : * to. In most such catalog columns, we'll see a List of Query nodes, or
2731 : : * even nested Lists, so drill down to a non-List node before checking.
2732 : : */
1438 2733 : 5048 : tst = node;
2734 [ + - - + ]: 5048 : while (tst && IsA(tst, List))
1438 tgl@sss.pgh.pa.us 2735 :UBC 0 : tst = linitial((List *) tst);
1438 tgl@sss.pgh.pa.us 2736 [ + - - + ]:CBC 5048 : if (tst && IsA(tst, Query))
1438 tgl@sss.pgh.pa.us 2737 [ # # ]:UBC 0 : ereport(ERROR,
2738 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2739 : : errmsg("input is a query, not an expression")));
2740 : :
2741 : : /*
2742 : : * Throw error if the expression contains Vars we won't be able to
2743 : : * deparse.
2744 : : */
1438 tgl@sss.pgh.pa.us 2745 :CBC 5048 : relids = pull_varnos(NULL, node);
2746 [ + + ]: 5048 : if (OidIsValid(relid))
2747 : : {
2748 [ - + ]: 5006 : if (!bms_is_subset(relids, bms_make_singleton(1)))
1438 tgl@sss.pgh.pa.us 2749 [ # # ]:UBC 0 : ereport(ERROR,
2750 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2751 : : errmsg("expression contains variables of more than one relation")));
2752 : : }
2753 : : else
2754 : : {
1438 tgl@sss.pgh.pa.us 2755 [ - + ]:CBC 42 : if (!bms_is_empty(relids))
1438 tgl@sss.pgh.pa.us 2756 [ # # ]:UBC 0 : ereport(ERROR,
2757 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2758 : : errmsg("expression contains variables")));
2759 : : }
2760 : :
2761 : : /*
2762 : : * Prepare deparse context if needed. If we are deparsing with a relid,
2763 : : * we need to transiently open and lock the rel, to make sure it won't go
2764 : : * away underneath us. (set_relation_column_names would lock it anyway,
2765 : : * so this isn't really introducing any new behavior.)
2766 : : */
6049 tgl@sss.pgh.pa.us 2767 [ + + ]:CBC 5048 : if (OidIsValid(relid))
2768 : : {
677 2769 : 5006 : rel = try_relation_open(relid, AccessShareLock);
2770 [ - + ]: 5006 : if (rel == NULL)
677 tgl@sss.pgh.pa.us 2771 :UBC 0 : return NULL;
677 tgl@sss.pgh.pa.us 2772 :CBC 5006 : context = deparse_context_for(RelationGetRelationName(rel), relid);
2773 : : }
2774 : : else
6049 2775 : 42 : context = NIL;
2776 : :
2777 : : /* Deparse */
8176 2778 : 5048 : str = deparse_expression_pretty(node, context, false, false,
2779 : : prettyFlags, 0);
2780 : :
677 2781 [ + + ]: 5048 : if (rel != NULL)
2782 : 5006 : relation_close(rel, AccessShareLock);
2783 : :
6049 2784 : 5048 : return string_to_text(str);
2785 : : }
2786 : :
2787 : :
2788 : : /* ----------
2789 : : * pg_get_userbyid - Get a user name by roleid and
2790 : : * fallback to 'unknown (OID=n)'
2791 : : * ----------
2792 : : */
2793 : : Datum
9318 2794 : 888 : pg_get_userbyid(PG_FUNCTION_ARGS)
2795 : : {
7477 2796 : 888 : Oid roleid = PG_GETARG_OID(0);
2797 : : Name result;
2798 : : HeapTuple roletup;
2799 : : Form_pg_authid role_rec;
2800 : :
2801 : : /*
2802 : : * Allocate space for the result
2803 : : */
9318 2804 : 888 : result = (Name) palloc(NAMEDATALEN);
9537 bruce@momjian.us 2805 : 888 : memset(NameStr(*result), 0, NAMEDATALEN);
2806 : :
2807 : : /*
2808 : : * Get the pg_authid entry and print the result
2809 : : */
5785 rhaas@postgresql.org 2810 : 888 : roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
7477 tgl@sss.pgh.pa.us 2811 [ + - ]: 888 : if (HeapTupleIsValid(roletup))
2812 : : {
2813 : 888 : role_rec = (Form_pg_authid) GETSTRUCT(roletup);
1955 peter@eisentraut.org 2814 : 888 : *result = role_rec->rolname;
7477 tgl@sss.pgh.pa.us 2815 : 888 : ReleaseSysCache(roletup);
2816 : : }
2817 : : else
7477 tgl@sss.pgh.pa.us 2818 :UBC 0 : sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
2819 : :
9318 tgl@sss.pgh.pa.us 2820 :CBC 888 : PG_RETURN_NAME(result);
2821 : : }
2822 : :
2823 : :
2824 : : /*
2825 : : * pg_get_serial_sequence
2826 : : * Get the name of the sequence used by an identity or serial column,
2827 : : * formatted suitably for passing to setval, nextval or currval.
2828 : : * First parameter is not treated as double-quoted, second parameter
2829 : : * is --- see documentation for reason.
2830 : : */
2831 : : Datum
7845 2832 : 6 : pg_get_serial_sequence(PG_FUNCTION_ARGS)
2833 : : {
3202 noah@leadboat.com 2834 : 6 : text *tablename = PG_GETARG_TEXT_PP(0);
6476 tgl@sss.pgh.pa.us 2835 : 6 : text *columnname = PG_GETARG_TEXT_PP(1);
2836 : : RangeVar *tablerv;
2837 : : Oid tableOid;
2838 : : char *column;
2839 : : AttrNumber attnum;
7780 bruce@momjian.us 2840 : 6 : Oid sequenceId = InvalidOid;
2841 : : Relation depRel;
2842 : : ScanKeyData key[3];
2843 : : SysScanDesc scan;
2844 : : HeapTuple tup;
2845 : :
2846 : : /* Look up table name. Can't lock it - we might not have privileges. */
7509 neilc@samurai.com 2847 : 6 : tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
5131 rhaas@postgresql.org 2848 : 6 : tableOid = RangeVarGetRelid(tablerv, NoLock, false);
2849 : :
2850 : : /* Get the number of the column */
6476 tgl@sss.pgh.pa.us 2851 : 6 : column = text_to_cstring(columnname);
2852 : :
7845 2853 : 6 : attnum = get_attnum(tableOid, column);
2854 [ - + ]: 6 : if (attnum == InvalidAttrNumber)
7845 tgl@sss.pgh.pa.us 2855 [ # # ]:UBC 0 : ereport(ERROR,
2856 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
2857 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
2858 : : column, tablerv->relname)));
2859 : :
2860 : : /* Search the dependency table for the dependent sequence */
2522 andres@anarazel.de 2861 :CBC 6 : depRel = table_open(DependRelationId, AccessShareLock);
2862 : :
7845 tgl@sss.pgh.pa.us 2863 : 6 : ScanKeyInit(&key[0],
2864 : : Anum_pg_depend_refclassid,
2865 : : BTEqualStrategyNumber, F_OIDEQ,
2866 : : ObjectIdGetDatum(RelationRelationId));
2867 : 6 : ScanKeyInit(&key[1],
2868 : : Anum_pg_depend_refobjid,
2869 : : BTEqualStrategyNumber, F_OIDEQ,
2870 : : ObjectIdGetDatum(tableOid));
2871 : 6 : ScanKeyInit(&key[2],
2872 : : Anum_pg_depend_refobjsubid,
2873 : : BTEqualStrategyNumber, F_INT4EQ,
2874 : : Int32GetDatum(attnum));
2875 : :
7552 2876 : 6 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
2877 : : NULL, 3, key);
2878 : :
7845 2879 [ + - ]: 15 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
2880 : : {
7780 bruce@momjian.us 2881 : 15 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
2882 : :
2883 : : /*
2884 : : * Look for an auto dependency (serial column) or internal dependency
2885 : : * (identity column) of a sequence on a column. (We need the relkind
2886 : : * test because indexes can also have auto dependencies on columns.)
2887 : : */
7552 tgl@sss.pgh.pa.us 2888 [ + + ]: 15 : if (deprec->classid == RelationRelationId &&
7845 2889 [ + - ]: 6 : deprec->objsubid == 0 &&
3015 peter_e@gmx.net 2890 [ + + ]: 6 : (deprec->deptype == DEPENDENCY_AUTO ||
2891 [ + - + - ]: 9 : deprec->deptype == DEPENDENCY_INTERNAL) &&
6977 tgl@sss.pgh.pa.us 2892 : 6 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
2893 : : {
7845 2894 : 6 : sequenceId = deprec->objid;
2895 : 6 : break;
2896 : : }
2897 : : }
2898 : :
2899 : 6 : systable_endscan(scan);
2522 andres@anarazel.de 2900 : 6 : table_close(depRel, AccessShareLock);
2901 : :
7845 tgl@sss.pgh.pa.us 2902 [ + - ]: 6 : if (OidIsValid(sequenceId))
2903 : : {
2904 : : char *result;
2905 : :
3680 2906 : 6 : result = generate_qualified_relation_name(sequenceId);
2907 : :
7845 2908 : 6 : PG_RETURN_TEXT_P(string_to_text(result));
2909 : : }
2910 : :
7845 tgl@sss.pgh.pa.us 2911 :UBC 0 : PG_RETURN_NULL();
2912 : : }
2913 : :
2914 : :
2915 : : /*
2916 : : * pg_get_functiondef
2917 : : * Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
2918 : : * the specified function.
2919 : : *
2920 : : * Note: if you change the output format of this function, be careful not
2921 : : * to break psql's rules (in \ef and \sf) for identifying the start of the
2922 : : * function body. To wit: the function body starts on a line that begins with
2923 : : * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
2924 : : */
2925 : : Datum
6311 tgl@sss.pgh.pa.us 2926 :CBC 86 : pg_get_functiondef(PG_FUNCTION_ARGS)
2927 : : {
2928 : 86 : Oid funcid = PG_GETARG_OID(0);
2929 : : StringInfoData buf;
2930 : : StringInfoData dq;
2931 : : HeapTuple proctup;
2932 : : Form_pg_proc proc;
2933 : : bool isfunction;
2934 : : Datum tmp;
2935 : : bool isnull;
2936 : : const char *prosrc;
2937 : : const char *name;
2938 : : const char *nsp;
2939 : : float4 procost;
2940 : : int oldlen;
2941 : :
2942 : 86 : initStringInfo(&buf);
2943 : :
2944 : : /* Look up the function */
5785 rhaas@postgresql.org 2945 : 86 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
6311 tgl@sss.pgh.pa.us 2946 [ + + ]: 86 : if (!HeapTupleIsValid(proctup))
3431 rhaas@postgresql.org 2947 : 3 : PG_RETURN_NULL();
2948 : :
6311 tgl@sss.pgh.pa.us 2949 : 83 : proc = (Form_pg_proc) GETSTRUCT(proctup);
2950 : 83 : name = NameStr(proc->proname);
2951 : :
2847 peter_e@gmx.net 2952 [ - + ]: 83 : if (proc->prokind == PROKIND_AGGREGATE)
6311 tgl@sss.pgh.pa.us 2953 [ # # ]:UBC 0 : ereport(ERROR,
2954 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2955 : : errmsg("\"%s\" is an aggregate function", name)));
2956 : :
2847 peter_e@gmx.net 2957 :CBC 83 : isfunction = (proc->prokind != PROKIND_PROCEDURE);
2958 : :
2959 : : /*
2960 : : * We always qualify the function name, to ensure the right function gets
2961 : : * replaced.
2962 : : */
1604 tgl@sss.pgh.pa.us 2963 : 83 : nsp = get_namespace_name_or_temp(proc->pronamespace);
2864 peter_e@gmx.net 2964 [ + + ]: 83 : appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
2965 : : isfunction ? "FUNCTION" : "PROCEDURE",
2966 : : quote_qualified_identifier(nsp, name));
6222 2967 : 83 : (void) print_function_arguments(&buf, proctup, false, true);
2864 2968 : 83 : appendStringInfoString(&buf, ")\n");
2969 [ + + ]: 83 : if (isfunction)
2970 : : {
2971 : 73 : appendStringInfoString(&buf, " RETURNS ");
2972 : 73 : print_function_rettype(&buf, proctup);
2973 : 73 : appendStringInfoChar(&buf, '\n');
2974 : : }
2975 : :
3888 2976 : 83 : print_function_trftypes(&buf, proctup);
2977 : :
2864 2978 : 83 : appendStringInfo(&buf, " LANGUAGE %s\n",
3101 tgl@sss.pgh.pa.us 2979 : 83 : quote_identifier(get_language_name(proc->prolang, false)));
2980 : :
2981 : : /* Emit some miscellaneous options on one line */
6311 2982 : 83 : oldlen = buf.len;
2983 : :
2847 peter_e@gmx.net 2984 [ - + ]: 83 : if (proc->prokind == PROKIND_WINDOW)
6195 tgl@sss.pgh.pa.us 2985 :UBC 0 : appendStringInfoString(&buf, " WINDOW");
6311 tgl@sss.pgh.pa.us 2986 [ + + + - ]:CBC 83 : switch (proc->provolatile)
2987 : : {
2988 : 6 : case PROVOLATILE_IMMUTABLE:
2989 : 6 : appendStringInfoString(&buf, " IMMUTABLE");
2990 : 6 : break;
2991 : 15 : case PROVOLATILE_STABLE:
2992 : 15 : appendStringInfoString(&buf, " STABLE");
2993 : 15 : break;
2994 : 62 : case PROVOLATILE_VOLATILE:
2995 : 62 : break;
2996 : : }
2997 : :
3522 rhaas@postgresql.org 2998 [ + - + - ]: 83 : switch (proc->proparallel)
2999 : : {
3000 : 14 : case PROPARALLEL_SAFE:
3001 : 14 : appendStringInfoString(&buf, " PARALLEL SAFE");
3002 : 14 : break;
3522 rhaas@postgresql.org 3003 :UBC 0 : case PROPARALLEL_RESTRICTED:
3004 : 0 : appendStringInfoString(&buf, " PARALLEL RESTRICTED");
3005 : 0 : break;
3522 rhaas@postgresql.org 3006 :CBC 69 : case PROPARALLEL_UNSAFE:
3007 : 69 : break;
3008 : : }
3009 : :
6311 tgl@sss.pgh.pa.us 3010 [ + + ]: 83 : if (proc->proisstrict)
3011 : 25 : appendStringInfoString(&buf, " STRICT");
3012 [ + + ]: 83 : if (proc->prosecdef)
3013 : 3 : appendStringInfoString(&buf, " SECURITY DEFINER");
3856 3014 [ - + ]: 83 : if (proc->proleakproof)
3856 tgl@sss.pgh.pa.us 3015 :UBC 0 : appendStringInfoString(&buf, " LEAKPROOF");
3016 : :
3017 : : /* This code for the default cost and rows should match functioncmds.c */
6311 tgl@sss.pgh.pa.us 3018 [ + - ]:CBC 83 : if (proc->prolang == INTERNALlanguageId ||
3019 [ + + ]: 83 : proc->prolang == ClanguageId)
3020 : 5 : procost = 1;
3021 : : else
3022 : 78 : procost = 100;
3023 [ + + ]: 83 : if (proc->procost != procost)
3024 : 3 : appendStringInfo(&buf, " COST %g", proc->procost);
3025 : :
3026 [ + + - + ]: 83 : if (proc->prorows > 0 && proc->prorows != 1000)
6311 tgl@sss.pgh.pa.us 3027 :UBC 0 : appendStringInfo(&buf, " ROWS %g", proc->prorows);
3028 : :
2503 tgl@sss.pgh.pa.us 3029 [ - + ]:CBC 83 : if (proc->prosupport)
3030 : : {
3031 : : Oid argtypes[1];
3032 : :
3033 : : /*
3034 : : * We should qualify the support function's name if it wouldn't be
3035 : : * resolved by lookup in the current search path.
3036 : : */
2503 tgl@sss.pgh.pa.us 3037 :UBC 0 : argtypes[0] = INTERNALOID;
3038 : 0 : appendStringInfo(&buf, " SUPPORT %s",
3039 : : generate_function_name(proc->prosupport, 1,
3040 : : NIL, argtypes,
3041 : : false, NULL, false));
3042 : : }
3043 : :
6311 tgl@sss.pgh.pa.us 3044 [ + + ]:CBC 83 : if (oldlen != buf.len)
3045 : 32 : appendStringInfoChar(&buf, '\n');
3046 : :
3047 : : /* Emit any proconfig options, one per line */
3048 : 83 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
3049 [ + + ]: 83 : if (!isnull)
3050 : : {
6033 bruce@momjian.us 3051 : 3 : ArrayType *a = DatumGetArrayTypeP(tmp);
3052 : : int i;
3053 : :
6311 tgl@sss.pgh.pa.us 3054 [ - + ]: 3 : Assert(ARR_ELEMTYPE(a) == TEXTOID);
3055 [ - + ]: 3 : Assert(ARR_NDIM(a) == 1);
3056 [ - + ]: 3 : Assert(ARR_LBOUND(a)[0] == 1);
3057 : :
3058 [ + + ]: 21 : for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3059 : : {
3060 : : Datum d;
3061 : :
3062 : 18 : d = array_ref(a, 1, &i,
3063 : : -1 /* varlenarray */ ,
3064 : : -1 /* TEXT's typlen */ ,
3065 : : false /* TEXT's typbyval */ ,
3066 : : TYPALIGN_INT /* TEXT's typalign */ ,
3067 : : &isnull);
3068 [ + - ]: 18 : if (!isnull)
3069 : : {
3070 : 18 : char *configitem = TextDatumGetCString(d);
3071 : : char *pos;
3072 : :
3073 : 18 : pos = strchr(configitem, '=');
3074 [ - + ]: 18 : if (pos == NULL)
6311 tgl@sss.pgh.pa.us 3075 :UBC 0 : continue;
6311 tgl@sss.pgh.pa.us 3076 :CBC 18 : *pos++ = '\0';
3077 : :
3078 : 18 : appendStringInfo(&buf, " SET %s TO ",
3079 : : quote_identifier(configitem));
3080 : :
3081 : : /*
3082 : : * Variables that are marked GUC_LIST_QUOTE were already fully
3083 : : * quoted by flatten_set_variable_args() before they were put
3084 : : * into the proconfig array. However, because the quoting
3085 : : * rules used there aren't exactly like SQL's, we have to
3086 : : * break the list value apart and then quote the elements as
3087 : : * string literals. (The elements may be double-quoted as-is,
3088 : : * but we can't just feed them to the SQL parser; it would do
3089 : : * the wrong thing with elements that are zero-length or
3090 : : * longer than NAMEDATALEN.) Also, we need a special case for
3091 : : * empty lists.
3092 : : *
3093 : : * Variables that are not so marked should just be emitted as
3094 : : * simple string literals. If the variable is not known to
3095 : : * guc.c, we'll do that; this makes it unsafe to use
3096 : : * GUC_LIST_QUOTE for extension variables.
3097 : : */
2828 3098 [ + + ]: 18 : if (GetConfigOptionFlags(configitem, true) & GUC_LIST_QUOTE)
3099 : : {
3100 : : List *namelist;
3101 : : ListCell *lc;
3102 : :
3103 : : /* Parse string into list of identifiers */
2696 3104 [ - + ]: 9 : if (!SplitGUCList(pos, ',', &namelist))
3105 : : {
3106 : : /* this shouldn't fail really */
2696 tgl@sss.pgh.pa.us 3107 [ # # ]:UBC 0 : elog(ERROR, "invalid list syntax in proconfig item");
3108 : : }
3109 : : /* Special case: represent an empty list as NULL */
43 tgl@sss.pgh.pa.us 3110 [ + + ]:GNC 9 : if (namelist == NIL)
3111 : 3 : appendStringInfoString(&buf, "NULL");
2696 tgl@sss.pgh.pa.us 3112 [ + + + + :CBC 24 : foreach(lc, namelist)
+ + ]
3113 : : {
3114 : 15 : char *curname = (char *) lfirst(lc);
3115 : :
3116 : 15 : simple_quote_literal(&buf, curname);
2347 3117 [ + + ]: 15 : if (lnext(namelist, lc))
2696 3118 : 9 : appendStringInfoString(&buf, ", ");
3119 : : }
3120 : : }
3121 : : else
6311 3122 : 9 : simple_quote_literal(&buf, pos);
3123 : 18 : appendStringInfoChar(&buf, '\n');
3124 : : }
3125 : : }
3126 : : }
3127 : :
3128 : : /* And finally the function definition ... */
1569 3129 : 83 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
1715 peter@eisentraut.org 3130 [ + + + + ]: 83 : if (proc->prolang == SQLlanguageId && !isnull)
3131 : : {
3132 : 57 : print_function_sqlbody(&buf, proctup);
3133 : : }
3134 : : else
3135 : : {
1680 tgl@sss.pgh.pa.us 3136 : 26 : appendStringInfoString(&buf, "AS ");
3137 : :
3138 : 26 : tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
3139 [ + + ]: 26 : if (!isnull)
3140 : : {
3141 : 5 : simple_quote_literal(&buf, TextDatumGetCString(tmp));
3142 : 5 : appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3143 : : }
3144 : :
998 dgustafsson@postgres 3145 : 26 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosrc);
1680 tgl@sss.pgh.pa.us 3146 : 26 : prosrc = TextDatumGetCString(tmp);
3147 : :
3148 : : /*
3149 : : * We always use dollar quoting. Figure out a suitable delimiter.
3150 : : *
3151 : : * Since the user is likely to be editing the function body string, we
3152 : : * shouldn't use a short delimiter that he might easily create a
3153 : : * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3154 : : * if needed.
3155 : : */
3156 : 26 : initStringInfo(&dq);
3157 : 26 : appendStringInfoChar(&dq, '$');
3158 [ + + ]: 26 : appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3159 [ - + ]: 26 : while (strstr(prosrc, dq.data) != NULL)
1680 tgl@sss.pgh.pa.us 3160 :UBC 0 : appendStringInfoChar(&dq, 'x');
1680 tgl@sss.pgh.pa.us 3161 :CBC 26 : appendStringInfoChar(&dq, '$');
3162 : :
3163 : 26 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3164 : 26 : appendStringInfoString(&buf, prosrc);
3165 : 26 : appendBinaryStringInfo(&buf, dq.data, dq.len);
3166 : : }
3167 : :
4430 rhaas@postgresql.org 3168 : 83 : appendStringInfoChar(&buf, '\n');
3169 : :
6311 tgl@sss.pgh.pa.us 3170 : 83 : ReleaseSysCache(proctup);
3171 : :
3172 : 83 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3173 : : }
3174 : :
3175 : : /*
3176 : : * pg_get_function_arguments
3177 : : * Get a nicely-formatted list of arguments for a function.
3178 : : * This is everything that would go between the parentheses in
3179 : : * CREATE FUNCTION.
3180 : : */
3181 : : Datum
6361 3182 : 2320 : pg_get_function_arguments(PG_FUNCTION_ARGS)
3183 : : {
3184 : 2320 : Oid funcid = PG_GETARG_OID(0);
3185 : : StringInfoData buf;
3186 : : HeapTuple proctup;
3187 : :
5785 rhaas@postgresql.org 3188 : 2320 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
6361 tgl@sss.pgh.pa.us 3189 [ + + ]: 2320 : if (!HeapTupleIsValid(proctup))
3428 rhaas@postgresql.org 3190 : 3 : PG_RETURN_NULL();
3191 : :
3192 : 2317 : initStringInfo(&buf);
3193 : :
6222 peter_e@gmx.net 3194 : 2317 : (void) print_function_arguments(&buf, proctup, false, true);
3195 : :
6361 tgl@sss.pgh.pa.us 3196 : 2317 : ReleaseSysCache(proctup);
3197 : :
3198 : 2317 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3199 : : }
3200 : :
3201 : : /*
3202 : : * pg_get_function_identity_arguments
3203 : : * Get a formatted list of arguments for a function.
3204 : : * This is everything that would go between the parentheses in
3205 : : * ALTER FUNCTION, etc. In particular, don't print defaults.
3206 : : */
3207 : : Datum
6222 peter_e@gmx.net 3208 : 2054 : pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
3209 : : {
3210 : 2054 : Oid funcid = PG_GETARG_OID(0);
3211 : : StringInfoData buf;
3212 : : HeapTuple proctup;
3213 : :
5785 rhaas@postgresql.org 3214 : 2054 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
6222 peter_e@gmx.net 3215 [ + + ]: 2054 : if (!HeapTupleIsValid(proctup))
3428 rhaas@postgresql.org 3216 : 3 : PG_RETURN_NULL();
3217 : :
3218 : 2051 : initStringInfo(&buf);
3219 : :
6222 peter_e@gmx.net 3220 : 2051 : (void) print_function_arguments(&buf, proctup, false, false);
3221 : :
3222 : 2051 : ReleaseSysCache(proctup);
3223 : :
3224 : 2051 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3225 : : }
3226 : :
3227 : : /*
3228 : : * pg_get_function_result
3229 : : * Get a nicely-formatted version of the result type of a function.
3230 : : * This is what would appear after RETURNS in CREATE FUNCTION.
3231 : : */
3232 : : Datum
6361 tgl@sss.pgh.pa.us 3233 : 2028 : pg_get_function_result(PG_FUNCTION_ARGS)
3234 : : {
3235 : 2028 : Oid funcid = PG_GETARG_OID(0);
3236 : : StringInfoData buf;
3237 : : HeapTuple proctup;
3238 : :
5785 rhaas@postgresql.org 3239 : 2028 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
6361 tgl@sss.pgh.pa.us 3240 [ + + ]: 2028 : if (!HeapTupleIsValid(proctup))
3428 rhaas@postgresql.org 3241 : 3 : PG_RETURN_NULL();
3242 : :
2847 peter_e@gmx.net 3243 [ + + ]: 2025 : if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
3244 : : {
2939 3245 : 119 : ReleaseSysCache(proctup);
3246 : 119 : PG_RETURN_NULL();
3247 : : }
3248 : :
3428 rhaas@postgresql.org 3249 : 1906 : initStringInfo(&buf);
3250 : :
6311 tgl@sss.pgh.pa.us 3251 : 1906 : print_function_rettype(&buf, proctup);
3252 : :
3253 : 1906 : ReleaseSysCache(proctup);
3254 : :
3255 : 1906 : PG_RETURN_TEXT_P(string_to_text(buf.data));
3256 : : }
3257 : :
3258 : : /*
3259 : : * Guts of pg_get_function_result: append the function's return type
3260 : : * to the specified buffer.
3261 : : */
3262 : : static void
3263 : 1979 : print_function_rettype(StringInfo buf, HeapTuple proctup)
3264 : : {
3265 : 1979 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3266 : 1979 : int ntabargs = 0;
3267 : : StringInfoData rbuf;
3268 : :
3269 : 1979 : initStringInfo(&rbuf);
3270 : :
3271 [ + + ]: 1979 : if (proc->proretset)
3272 : : {
3273 : : /* It might be a table function; try to print the arguments */
3274 : 202 : appendStringInfoString(&rbuf, "TABLE(");
6208 3275 : 202 : ntabargs = print_function_arguments(&rbuf, proctup, true, false);
6361 3276 [ + + ]: 202 : if (ntabargs > 0)
3873 peter_e@gmx.net 3277 : 38 : appendStringInfoChar(&rbuf, ')');
3278 : : else
6311 tgl@sss.pgh.pa.us 3279 : 164 : resetStringInfo(&rbuf);
3280 : : }
3281 : :
6361 3282 [ + + ]: 1979 : if (ntabargs == 0)
3283 : : {
3284 : : /* Not a table function, so do the normal thing */
6311 3285 [ + + ]: 1941 : if (proc->proretset)
3286 : 164 : appendStringInfoString(&rbuf, "SETOF ");
3287 : 1941 : appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3288 : : }
3289 : :
2339 drowley@postgresql.o 3290 : 1979 : appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
6361 tgl@sss.pgh.pa.us 3291 : 1979 : }
3292 : :
3293 : : /*
3294 : : * Common code for pg_get_function_arguments and pg_get_function_result:
3295 : : * append the desired subset of arguments to buf. We print only TABLE
3296 : : * arguments when print_table_args is true, and all the others when it's false.
3297 : : * We print argument defaults only if print_defaults is true.
3298 : : * Function return value is the number of arguments printed.
3299 : : */
3300 : : static int
3301 : 4653 : print_function_arguments(StringInfo buf, HeapTuple proctup,
3302 : : bool print_table_args, bool print_defaults)
3303 : : {
6208 3304 : 4653 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
3305 : : int numargs;
3306 : : Oid *argtypes;
3307 : : char **argnames;
3308 : : char *argmodes;
4377 3309 : 4653 : int insertorderbyat = -1;
3310 : : int argsprinted;
3311 : : int inputargno;
3312 : : int nlackdefaults;
2347 3313 : 4653 : List *argdefaults = NIL;
6208 3314 : 4653 : ListCell *nextargdefault = NULL;
3315 : : int i;
3316 : :
6361 3317 : 4653 : numargs = get_func_arg_info(proctup,
3318 : : &argtypes, &argnames, &argmodes);
3319 : :
6208 3320 : 4653 : nlackdefaults = numargs;
3321 [ + + + + ]: 4653 : if (print_defaults && proc->pronargdefaults > 0)
3322 : : {
3323 : : Datum proargdefaults;
3324 : : bool isnull;
3325 : :
3326 : 19 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3327 : : Anum_pg_proc_proargdefaults,
3328 : : &isnull);
3329 [ + - ]: 19 : if (!isnull)
3330 : : {
3331 : : char *str;
3332 : :
3333 : 19 : str = TextDatumGetCString(proargdefaults);
3221 peter_e@gmx.net 3334 : 19 : argdefaults = castNode(List, stringToNode(str));
6208 tgl@sss.pgh.pa.us 3335 : 19 : pfree(str);
3336 : 19 : nextargdefault = list_head(argdefaults);
3337 : : /* nlackdefaults counts only *input* arguments lacking defaults */
3338 : 19 : nlackdefaults = proc->pronargs - list_length(argdefaults);
3339 : : }
3340 : : }
3341 : :
3342 : : /* Check for special treatment of ordered-set aggregates */
2847 peter_e@gmx.net 3343 [ + + ]: 4653 : if (proc->prokind == PROKIND_AGGREGATE)
3344 : : {
3345 : : HeapTuple aggtup;
3346 : : Form_pg_aggregate agg;
3347 : :
881 michael@paquier.xyz 3348 : 585 : aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(proc->oid));
4377 tgl@sss.pgh.pa.us 3349 [ - + ]: 585 : if (!HeapTupleIsValid(aggtup))
4377 tgl@sss.pgh.pa.us 3350 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for aggregate %u",
3351 : : proc->oid);
4377 tgl@sss.pgh.pa.us 3352 :CBC 585 : agg = (Form_pg_aggregate) GETSTRUCT(aggtup);
3353 [ + + ]: 585 : if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3354 : 26 : insertorderbyat = agg->aggnumdirectargs;
3355 : 585 : ReleaseSysCache(aggtup);
3356 : : }
3357 : :
6361 3358 : 4653 : argsprinted = 0;
6208 3359 : 4653 : inputargno = 0;
6361 3360 [ + + ]: 9368 : for (i = 0; i < numargs; i++)
3361 : : {
6033 bruce@momjian.us 3362 : 4715 : Oid argtype = argtypes[i];
3363 [ + + ]: 4715 : char *argname = argnames ? argnames[i] : NULL;
3364 [ + + ]: 4715 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
3365 : : const char *modename;
3366 : : bool isinput;
3367 : :
6361 tgl@sss.pgh.pa.us 3368 [ + + + + : 4715 : switch (argmode)
+ - ]
3369 : : {
3370 : 3876 : case PROARGMODE_IN:
3371 : :
3372 : : /*
3373 : : * For procedures, explicitly mark all argument modes, so as
3374 : : * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3375 : : */
1651 3376 [ + + ]: 3876 : if (proc->prokind == PROKIND_PROCEDURE)
3377 : 266 : modename = "IN ";
3378 : : else
3379 : 3610 : modename = "";
6208 3380 : 3876 : isinput = true;
6361 3381 : 3876 : break;
3382 : 50 : case PROARGMODE_INOUT:
3383 : 50 : modename = "INOUT ";
6208 3384 : 50 : isinput = true;
6361 3385 : 50 : break;
3386 : 478 : case PROARGMODE_OUT:
3387 : 478 : modename = "OUT ";
6208 3388 : 478 : isinput = false;
6361 3389 : 478 : break;
3390 : 89 : case PROARGMODE_VARIADIC:
3391 : 89 : modename = "VARIADIC ";
6208 3392 : 89 : isinput = true;
6361 3393 : 89 : break;
3394 : 222 : case PROARGMODE_TABLE:
3395 : 222 : modename = "";
6208 3396 : 222 : isinput = false;
6361 3397 : 222 : break;
6361 tgl@sss.pgh.pa.us 3398 :UBC 0 : default:
3399 [ # # ]: 0 : elog(ERROR, "invalid parameter mode '%c'", argmode);
3400 : : modename = NULL; /* keep compiler quiet */
3401 : : isinput = false;
3402 : : break;
3403 : : }
6208 tgl@sss.pgh.pa.us 3404 [ + + ]:CBC 4715 : if (isinput)
3405 : 4015 : inputargno++; /* this is a 1-based counter */
3406 : :
3407 [ + + ]: 4715 : if (print_table_args != (argmode == PROARGMODE_TABLE))
3408 : 382 : continue;
3409 : :
4377 3410 [ + + ]: 4333 : if (argsprinted == insertorderbyat)
3411 : : {
3412 [ + - ]: 26 : if (argsprinted)
3413 : 26 : appendStringInfoChar(buf, ' ');
3414 : 26 : appendStringInfoString(buf, "ORDER BY ");
3415 : : }
3416 [ + + ]: 4307 : else if (argsprinted)
6361 3417 : 1388 : appendStringInfoString(buf, ", ");
3418 : :
3419 : 4333 : appendStringInfoString(buf, modename);
6208 3420 [ + + + + ]: 4333 : if (argname && argname[0])
6035 3421 : 1553 : appendStringInfo(buf, "%s ", quote_identifier(argname));
6361 3422 : 4333 : appendStringInfoString(buf, format_type_be(argtype));
6208 3423 [ + + + + : 4333 : if (print_defaults && isinput && inputargno > nlackdefaults)
+ + ]
3424 : : {
3425 : : Node *expr;
3426 : :
3427 [ - + ]: 29 : Assert(nextargdefault != NULL);
3428 : 29 : expr = (Node *) lfirst(nextargdefault);
2347 3429 : 29 : nextargdefault = lnext(argdefaults, nextargdefault);
3430 : :
6208 3431 : 29 : appendStringInfo(buf, " DEFAULT %s",
3432 : : deparse_expression(expr, NIL, false, false));
3433 : : }
6361 3434 : 4333 : argsprinted++;
3435 : :
3436 : : /* nasty hack: print the last arg twice for variadic ordered-set agg */
4377 3437 [ + + + + ]: 4333 : if (argsprinted == insertorderbyat && i == numargs - 1)
3438 : : {
3439 : 13 : i--;
3440 : : /* aggs shouldn't have defaults anyway, but just to be sure ... */
3441 : 13 : print_defaults = false;
3442 : : }
3443 : : }
3444 : :
6361 3445 : 4653 : return argsprinted;
3446 : : }
3447 : :
3448 : : static bool
4404 peter_e@gmx.net 3449 : 48 : is_input_argument(int nth, const char *argmodes)
3450 : : {
3451 : : return (!argmodes
3452 [ + + ]: 21 : || argmodes[nth] == PROARGMODE_IN
3453 [ + - ]: 9 : || argmodes[nth] == PROARGMODE_INOUT
3454 [ + + - + ]: 69 : || argmodes[nth] == PROARGMODE_VARIADIC);
3455 : : }
3456 : :
3457 : : /*
3458 : : * Append used transformed types to specified buffer
3459 : : */
3460 : : static void
3888 3461 : 83 : print_function_trftypes(StringInfo buf, HeapTuple proctup)
3462 : : {
3463 : : Oid *trftypes;
3464 : : int ntypes;
3465 : :
3466 : 83 : ntypes = get_func_trftypes(proctup, &trftypes);
3467 [ + + ]: 83 : if (ntypes > 0)
3468 : : {
3469 : : int i;
3470 : :
1787 tgl@sss.pgh.pa.us 3471 : 3 : appendStringInfoString(buf, " TRANSFORM ");
3888 peter_e@gmx.net 3472 [ + + ]: 8 : for (i = 0; i < ntypes; i++)
3473 : : {
3474 [ + + ]: 5 : if (i != 0)
3475 : 2 : appendStringInfoString(buf, ", ");
3877 magnus@hagander.net 3476 : 5 : appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3477 : : }
1787 tgl@sss.pgh.pa.us 3478 : 3 : appendStringInfoChar(buf, '\n');
3479 : : }
3888 peter_e@gmx.net 3480 : 83 : }
3481 : :
3482 : : /*
3483 : : * Get textual representation of a function argument's default value. The
3484 : : * second argument of this function is the argument number among all arguments
3485 : : * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
3486 : : * how information_schema.sql uses it.
3487 : : */
3488 : : Datum
4404 3489 : 27 : pg_get_function_arg_default(PG_FUNCTION_ARGS)
3490 : : {
3491 : 27 : Oid funcid = PG_GETARG_OID(0);
3492 : 27 : int32 nth_arg = PG_GETARG_INT32(1);
3493 : : HeapTuple proctup;
3494 : : Form_pg_proc proc;
3495 : : int numargs;
3496 : : Oid *argtypes;
3497 : : char **argnames;
3498 : : char *argmodes;
3499 : : int i;
3500 : : List *argdefaults;
3501 : : Node *node;
3502 : : char *str;
3503 : : int nth_inputarg;
3504 : : Datum proargdefaults;
3505 : : bool isnull;
3506 : : int nth_default;
3507 : :
3508 : 27 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3509 [ + + ]: 27 : if (!HeapTupleIsValid(proctup))
3428 rhaas@postgresql.org 3510 : 6 : PG_RETURN_NULL();
3511 : :
4404 peter_e@gmx.net 3512 : 21 : numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3513 [ + - + - : 21 : if (nth_arg < 1 || nth_arg > numargs || !is_input_argument(nth_arg - 1, argmodes))
+ + ]
3514 : : {
3515 : 6 : ReleaseSysCache(proctup);
3516 : 6 : PG_RETURN_NULL();
3517 : : }
3518 : :
3519 : 15 : nth_inputarg = 0;
3520 [ + + ]: 42 : for (i = 0; i < nth_arg; i++)
3521 [ + + ]: 27 : if (is_input_argument(i, argmodes))
3522 : 24 : nth_inputarg++;
3523 : :
3524 : 15 : proargdefaults = SysCacheGetAttr(PROCOID, proctup,
3525 : : Anum_pg_proc_proargdefaults,
3526 : : &isnull);
3527 [ - + ]: 15 : if (isnull)
3528 : : {
4404 peter_e@gmx.net 3529 :UBC 0 : ReleaseSysCache(proctup);
3530 : 0 : PG_RETURN_NULL();
3531 : : }
3532 : :
4404 peter_e@gmx.net 3533 :CBC 15 : str = TextDatumGetCString(proargdefaults);
3221 3534 : 15 : argdefaults = castNode(List, stringToNode(str));
4404 3535 : 15 : pfree(str);
3536 : :
3537 : 15 : proc = (Form_pg_proc) GETSTRUCT(proctup);
3538 : :
3539 : : /*
3540 : : * Calculate index into proargdefaults: proargdefaults corresponds to the
3541 : : * last N input arguments, where N = pronargdefaults.
3542 : : */
3543 : 15 : nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3544 : :
3545 [ + + - + ]: 15 : if (nth_default < 0 || nth_default >= list_length(argdefaults))
3546 : : {
3547 : 3 : ReleaseSysCache(proctup);
3548 : 3 : PG_RETURN_NULL();
3549 : : }
3550 : 12 : node = list_nth(argdefaults, nth_default);
3551 : 12 : str = deparse_expression(node, NIL, false, false);
3552 : :
3553 : 12 : ReleaseSysCache(proctup);
3554 : :
3555 : 12 : PG_RETURN_TEXT_P(string_to_text(str));
3556 : : }
3557 : :
3558 : : static void
1715 peter@eisentraut.org 3559 : 105 : print_function_sqlbody(StringInfo buf, HeapTuple proctup)
3560 : : {
3561 : : int numargs;
3562 : : Oid *argtypes;
3563 : : char **argnames;
3564 : : char *argmodes;
3565 : 105 : deparse_namespace dpns = {0};
3566 : : Datum tmp;
3567 : : Node *n;
3568 : :
3569 : 105 : dpns.funcname = pstrdup(NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname));
3570 : 105 : numargs = get_func_arg_info(proctup,
3571 : : &argtypes, &argnames, &argmodes);
3572 : 105 : dpns.numargs = numargs;
3573 : 105 : dpns.argnames = argnames;
3574 : :
998 dgustafsson@postgres 3575 : 105 : tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosqlbody);
1715 peter@eisentraut.org 3576 : 105 : n = stringToNode(TextDatumGetCString(tmp));
3577 : :
3578 [ + + ]: 105 : if (IsA(n, List))
3579 : : {
3580 : : List *stmts;
3581 : : ListCell *lc;
3582 : :
3583 : 82 : stmts = linitial(castNode(List, n));
3584 : :
3585 : 82 : appendStringInfoString(buf, "BEGIN ATOMIC\n");
3586 : :
3587 [ + + + + : 159 : foreach(lc, stmts)
+ + ]
3588 : : {
3589 : 77 : Query *query = lfirst_node(Query, lc);
3590 : :
3591 : : /* It seems advisable to get at least AccessShareLock on rels */
1569 tgl@sss.pgh.pa.us 3592 : 77 : AcquireRewriteLocks(query, false, false);
1306 3593 : 77 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3594 : : PRETTYFLAG_INDENT, WRAP_COLUMN_DEFAULT, 1);
1715 peter@eisentraut.org 3595 : 77 : appendStringInfoChar(buf, ';');
3596 : 77 : appendStringInfoChar(buf, '\n');
3597 : : }
3598 : :
3599 : 82 : appendStringInfoString(buf, "END");
3600 : : }
3601 : : else
3602 : : {
1569 tgl@sss.pgh.pa.us 3603 : 23 : Query *query = castNode(Query, n);
3604 : :
3605 : : /* It seems advisable to get at least AccessShareLock on rels */
3606 : 23 : AcquireRewriteLocks(query, false, false);
1306 3607 : 23 : get_query_def(query, buf, list_make1(&dpns), NULL, false,
3608 : : 0, WRAP_COLUMN_DEFAULT, 0);
3609 : : }
1715 peter@eisentraut.org 3610 : 105 : }
3611 : :
3612 : : Datum
3613 : 1766 : pg_get_function_sqlbody(PG_FUNCTION_ARGS)
3614 : : {
3615 : 1766 : Oid funcid = PG_GETARG_OID(0);
3616 : : StringInfoData buf;
3617 : : HeapTuple proctup;
3618 : : bool isnull;
3619 : :
3620 : 1766 : initStringInfo(&buf);
3621 : :
3622 : : /* Look up the function */
3623 : 1766 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
3624 [ - + ]: 1766 : if (!HeapTupleIsValid(proctup))
1715 peter@eisentraut.org 3625 :UBC 0 : PG_RETURN_NULL();
3626 : :
1569 tgl@sss.pgh.pa.us 3627 :CBC 1766 : (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
1715 peter@eisentraut.org 3628 [ + + ]: 1766 : if (isnull)
3629 : : {
3630 : 1718 : ReleaseSysCache(proctup);
3631 : 1718 : PG_RETURN_NULL();
3632 : : }
3633 : :
3634 : 48 : print_function_sqlbody(&buf, proctup);
3635 : :
3636 : 48 : ReleaseSysCache(proctup);
3637 : :
1198 drowley@postgresql.o 3638 : 48 : PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
3639 : : }
3640 : :
3641 : :
3642 : : /*
3643 : : * deparse_expression - General utility for deparsing expressions
3644 : : *
3645 : : * calls deparse_expression_pretty with all prettyPrinting disabled
3646 : : */
3647 : : char *
8176 tgl@sss.pgh.pa.us 3648 : 40747 : deparse_expression(Node *expr, List *dpcontext,
3649 : : bool forceprefix, bool showimplicit)
3650 : : {
8171 bruce@momjian.us 3651 : 40747 : return deparse_expression_pretty(expr, dpcontext, forceprefix,
3652 : : showimplicit, 0, 0);
3653 : : }
3654 : :
3655 : : /* ----------
3656 : : * deparse_expression_pretty - General utility for deparsing expressions
3657 : : *
3658 : : * expr is the node tree to be deparsed. It must be a transformed expression
3659 : : * tree (ie, not the raw output of gram.y).
3660 : : *
3661 : : * dpcontext is a list of deparse_namespace nodes representing the context
3662 : : * for interpreting Vars in the node tree. It can be NIL if no Vars are
3663 : : * expected.
3664 : : *
3665 : : * forceprefix is true to force all Vars to be prefixed with their table names.
3666 : : *
3667 : : * showimplicit is true to force all implicit casts to be shown explicitly.
3668 : : *
3669 : : * Tries to pretty up the output according to prettyFlags and startIndent.
3670 : : *
3671 : : * The result is a palloc'd string.
3672 : : * ----------
3673 : : */
3674 : : static char *
8176 tgl@sss.pgh.pa.us 3675 : 47840 : deparse_expression_pretty(Node *expr, List *dpcontext,
3676 : : bool forceprefix, bool showimplicit,
3677 : : int prettyFlags, int startIndent)
3678 : : {
3679 : : StringInfoData buf;
3680 : : deparse_context context;
3681 : :
9572 3682 : 47840 : initStringInfo(&buf);
3683 : 47840 : context.buf = &buf;
9072 3684 : 47840 : context.namespaces = dpcontext;
475 3685 : 47840 : context.resultDesc = NULL;
3686 : 47840 : context.targetList = NIL;
6198 3687 : 47840 : context.windowClause = NIL;
9072 3688 : 47840 : context.varprefix = forceprefix;
8176 3689 : 47840 : context.prettyFlags = prettyFlags;
4741 3690 : 47840 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
8176 3691 : 47840 : context.indentLevel = startIndent;
475 3692 : 47840 : context.colNamesVisible = true;
3693 : 47840 : context.inGroupBy = false;
3694 : 47840 : context.varInOrderBy = false;
2198 3695 : 47840 : context.appendparents = NULL;
3696 : :
8490 3697 : 47840 : get_rule_expr(expr, &context, showimplicit);
3698 : :
9572 3699 : 47840 : return buf.data;
3700 : : }
3701 : :
3702 : : /* ----------
3703 : : * deparse_context_for - Build deparse context for a single relation
3704 : : *
3705 : : * Given the reference name (alias) and OID of a relation, build deparsing
3706 : : * context for an expression referencing only that relation (as varno 1,
3707 : : * varlevelsup 0). This is sufficient for many uses of deparse_expression.
3708 : : * ----------
3709 : : */
3710 : : List *
8671 3711 : 12450 : deparse_context_for(const char *aliasname, Oid relid)
3712 : : {
3713 : : deparse_namespace *dpns;
3714 : : RangeTblEntry *rte;
3715 : :
7 michael@paquier.xyz 3716 :GNC 12450 : dpns = palloc0_object(deparse_namespace);
3717 : :
3718 : : /* Build a minimal RTE for the rel */
9072 tgl@sss.pgh.pa.us 3719 :CBC 12450 : rte = makeNode(RangeTblEntry);
8681 3720 : 12450 : rte->rtekind = RTE_RELATION;
9072 3721 : 12450 : rte->relid = relid;
5412 3722 : 12450 : rte->relkind = RELKIND_RELATION; /* no need for exactness here */
2635 3723 : 12450 : rte->rellockmode = AccessShareLock;
4835 3724 : 12450 : rte->alias = makeAlias(aliasname, NIL);
3725 : 12450 : rte->eref = rte->alias;
4880 3726 : 12450 : rte->lateral = false;
9072 3727 : 12450 : rte->inh = false;
3728 : 12450 : rte->inFromCl = true;
3729 : :
3730 : : /* Build one-element rtable */
7871 neilc@samurai.com 3731 : 12450 : dpns->rtable = list_make1(rte);
2198 tgl@sss.pgh.pa.us 3732 : 12450 : dpns->subplans = NIL;
6281 3733 : 12450 : dpns->ctes = NIL;
2198 3734 : 12450 : dpns->appendrels = NULL;
4835 3735 : 12450 : set_rtable_names(dpns, NIL, NULL);
4734 3736 : 12450 : set_simple_column_names(dpns);
3737 : :
3738 : : /* Return a one-deep namespace stack */
7871 neilc@samurai.com 3739 : 12450 : return list_make1(dpns);
3740 : : }
3741 : :
3742 : : /*
3743 : : * deparse_context_for_plan_tree - Build deparse context for a Plan tree
3744 : : *
3745 : : * When deparsing an expression in a Plan tree, we use the plan's rangetable
3746 : : * to resolve names of simple Vars. The initialization of column names for
3747 : : * this is rather expensive if the rangetable is large, and it'll be the same
3748 : : * for every expression in the Plan tree; so we do it just once and re-use
3749 : : * the result of this function for each expression. (Note that the result
3750 : : * is not usable until set_deparse_context_plan() is applied to it.)
3751 : : *
3752 : : * In addition to the PlannedStmt, pass the per-RTE alias names
3753 : : * assigned by a previous call to select_rtable_names_for_explain.
3754 : : */
3755 : : List *
2198 tgl@sss.pgh.pa.us 3756 : 12268 : deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
3757 : : {
3758 : : deparse_namespace *dpns;
3759 : :
7 michael@paquier.xyz 3760 :GNC 12268 : dpns = palloc0_object(deparse_namespace);
3761 : :
3762 : : /* Initialize fields that stay the same across the whole plan tree */
2198 tgl@sss.pgh.pa.us 3763 :CBC 12268 : dpns->rtable = pstmt->rtable;
3989 3764 : 12268 : dpns->rtable_names = rtable_names;
2198 3765 : 12268 : dpns->subplans = pstmt->subplans;
3989 3766 : 12268 : dpns->ctes = NIL;
2198 3767 [ + + ]: 12268 : if (pstmt->appendRelations)
3768 : : {
3769 : : /* Set up the array, indexed by child relid */
3770 : 1969 : int ntables = list_length(dpns->rtable);
3771 : : ListCell *lc;
3772 : :
3773 : 1969 : dpns->appendrels = (AppendRelInfo **)
3774 : 1969 : palloc0((ntables + 1) * sizeof(AppendRelInfo *));
3775 [ + - + + : 10892 : foreach(lc, pstmt->appendRelations)
+ + ]
3776 : : {
3777 : 8923 : AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
3778 : 8923 : Index crelid = appinfo->child_relid;
3779 : :
3780 [ + - - + ]: 8923 : Assert(crelid > 0 && crelid <= ntables);
3781 [ - + ]: 8923 : Assert(dpns->appendrels[crelid] == NULL);
3782 : 8923 : dpns->appendrels[crelid] = appinfo;
3783 : : }
3784 : : }
3785 : : else
3786 : 10299 : dpns->appendrels = NULL; /* don't need it */
3787 : :
3788 : : /*
3789 : : * Set up column name aliases, ignoring any join RTEs; they don't matter
3790 : : * because plan trees don't contain any join alias Vars.
3791 : : */
3989 3792 : 12268 : set_simple_column_names(dpns);
3793 : :
3794 : : /* Return a one-deep namespace stack */
3795 : 12268 : return list_make1(dpns);
3796 : : }
3797 : :
3798 : : /*
3799 : : * set_deparse_context_plan - Specify Plan node containing expression
3800 : : *
3801 : : * When deparsing an expression in a Plan tree, we might have to resolve
3802 : : * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
3803 : : * provide the parent Plan node. Then OUTER_VAR and INNER_VAR references
3804 : : * can be resolved by drilling down into the left and right child plans.
3805 : : * Similarly, INDEX_VAR references can be resolved by reference to the
3806 : : * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
3807 : : * ForeignScan and CustomScan nodes. (Note that we don't currently support
3808 : : * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
3809 : : * for those, we can only deparse the indexqualorig fields, which won't
3810 : : * contain INDEX_VAR Vars.)
3811 : : *
3812 : : * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
3813 : : * the most-closely-nested first. This is needed to resolve PARAM_EXEC
3814 : : * Params. Note we assume that all the Plan nodes share the same rtable.
3815 : : *
3816 : : * For a ModifyTable plan, we might also need to resolve references to OLD/NEW
3817 : : * variables in the RETURNING list, so we copy the alias names of the OLD and
3818 : : * NEW rows from the ModifyTable plan node.
3819 : : *
3820 : : * Once this function has been called, deparse_expression() can be called on
3821 : : * subsidiary expression(s) of the specified Plan node. To deparse
3822 : : * expressions of a different Plan node in the same Plan tree, re-call this
3823 : : * function to identify the new parent Plan node.
3824 : : *
3825 : : * The result is the same List passed in; this is a notational convenience.
3826 : : */
3827 : : List *
2198 3828 : 29046 : set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
3829 : : {
3830 : : deparse_namespace *dpns;
3831 : :
3832 : : /* Should always have one-entry namespace list for Plan deparsing */
3989 3833 [ - + ]: 29046 : Assert(list_length(dpcontext) == 1);
3834 : 29046 : dpns = (deparse_namespace *) linitial(dpcontext);
3835 : :
3836 : : /* Set our attention on the specific plan node passed in */
5636 3837 : 29046 : dpns->ancestors = ancestors;
1528 3838 : 29046 : set_deparse_plan(dpns, plan);
3839 : :
3840 : : /* For ModifyTable, set aliases for OLD and NEW in RETURNING */
335 dean.a.rasheed@gmail 3841 [ + + ]: 29046 : if (IsA(plan, ModifyTable))
3842 : : {
3843 : 105 : dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias;
3844 : 105 : dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias;
3845 : : }
3846 : :
3989 tgl@sss.pgh.pa.us 3847 : 29046 : return dpcontext;
3848 : : }
3849 : :
3850 : : /*
3851 : : * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
3852 : : *
3853 : : * Determine the relation aliases we'll use during an EXPLAIN operation.
3854 : : * This is just a frontend to set_rtable_names. We have to expose the aliases
3855 : : * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
3856 : : */
3857 : : List *
4835 3858 : 12268 : select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
3859 : : {
3860 : : deparse_namespace dpns;
3861 : :
3862 : 12268 : memset(&dpns, 0, sizeof(dpns));
3863 : 12268 : dpns.rtable = rtable;
2198 3864 : 12268 : dpns.subplans = NIL;
4835 3865 : 12268 : dpns.ctes = NIL;
2198 3866 : 12268 : dpns.appendrels = NULL;
4835 3867 : 12268 : set_rtable_names(&dpns, NIL, rels_used);
3868 : : /* We needn't bother computing column aliases yet */
3869 : :
3870 : 12268 : return dpns.rtable_names;
3871 : : }
3872 : :
3873 : : /*
3874 : : * set_rtable_names: select RTE aliases to be used in printing a query
3875 : : *
3876 : : * We fill in dpns->rtable_names with a list of names that is one-for-one with
3877 : : * the already-filled dpns->rtable list. Each RTE name is unique among those
3878 : : * in the new namespace plus any ancestor namespaces listed in
3879 : : * parent_namespaces.
3880 : : *
3881 : : * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
3882 : : *
3883 : : * Note that this function is only concerned with relation names, not column
3884 : : * names.
3885 : : */
3886 : : static void
3887 : 27725 : set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
3888 : : Bitmapset *rels_used)
3889 : : {
3890 : : HASHCTL hash_ctl;
3891 : : HTAB *names_hash;
3892 : : NameHashEntry *hentry;
3893 : : bool found;
3894 : : int rtindex;
3895 : : ListCell *lc;
3896 : :
3897 : 27725 : dpns->rtable_names = NIL;
3898 : : /* nothing more to do if empty rtable */
3684 3899 [ + + ]: 27725 : if (dpns->rtable == NIL)
3900 : 286 : return;
3901 : :
3902 : : /*
3903 : : * We use a hash table to hold known names, so that this process is O(N)
3904 : : * not O(N^2) for N names.
3905 : : */
3906 : 27439 : hash_ctl.keysize = NAMEDATALEN;
3907 : 27439 : hash_ctl.entrysize = sizeof(NameHashEntry);
3908 : 27439 : hash_ctl.hcxt = CurrentMemoryContext;
3909 : 27439 : names_hash = hash_create("set_rtable_names names",
3910 : 27439 : list_length(dpns->rtable),
3911 : : &hash_ctl,
3912 : : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
3913 : :
3914 : : /* Preload the hash table with names appearing in parent_namespaces */
3915 [ + + + + : 28307 : foreach(lc, parent_namespaces)
+ + ]
3916 : : {
3917 : 868 : deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
3918 : : ListCell *lc2;
3919 : :
3920 [ + + + + : 3149 : foreach(lc2, olddpns->rtable_names)
+ + ]
3921 : : {
3922 : 2281 : char *oldname = (char *) lfirst(lc2);
3923 : :
3924 [ + + ]: 2281 : if (oldname == NULL)
3925 : 168 : continue;
3926 : 2113 : hentry = (NameHashEntry *) hash_search(names_hash,
3927 : : oldname,
3928 : : HASH_ENTER,
3929 : : &found);
3930 : : /* we do not complain about duplicate names in parent namespaces */
3931 : 2113 : hentry->counter = 0;
3932 : : }
3933 : : }
3934 : :
3935 : : /* Now we can scan the rtable */
3936 : 27439 : rtindex = 1;
4835 3937 [ + - + + : 79132 : foreach(lc, dpns->rtable)
+ + ]
3938 : : {
3939 : 51693 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
3940 : : char *refname;
3941 : :
3942 : : /* Just in case this takes an unreasonable amount of time ... */
3684 3943 [ + + ]: 51693 : CHECK_FOR_INTERRUPTS();
3944 : :
4835 3945 [ + + + + ]: 51693 : if (rels_used && !bms_is_member(rtindex, rels_used))
3946 : : {
3947 : : /* Ignore unreferenced RTE */
3948 : 8966 : refname = NULL;
3949 : : }
3950 [ + + ]: 42727 : else if (rte->alias)
3951 : : {
3952 : : /* If RTE has a user-defined alias, prefer that */
3953 : 27990 : refname = rte->alias->aliasname;
3954 : : }
3955 [ + + ]: 14737 : else if (rte->rtekind == RTE_RELATION)
3956 : : {
3957 : : /* Use the current actual name of the relation */
3958 : 11203 : refname = get_rel_name(rte->relid);
3959 : : }
3960 [ + + ]: 3534 : else if (rte->rtekind == RTE_JOIN)
3961 : : {
3962 : : /* Unnamed join has no refname */
3963 : 903 : refname = NULL;
3964 : : }
3965 : : else
3966 : : {
3967 : : /* Otherwise use whatever the parser assigned */
3968 : 2631 : refname = rte->eref->aliasname;
3969 : : }
3970 : :
3971 : : /*
3972 : : * If the selected name isn't unique, append digits to make it so, and
3973 : : * make a new hash entry for it once we've got a unique name. For a
3974 : : * very long input name, we might have to truncate to stay within
3975 : : * NAMEDATALEN.
3976 : : */
3684 3977 [ + + ]: 51693 : if (refname)
3978 : : {
3979 : 41824 : hentry = (NameHashEntry *) hash_search(names_hash,
3980 : : refname,
3981 : : HASH_ENTER,
3982 : : &found);
3983 [ + + ]: 41824 : if (found)
3984 : : {
3985 : : /* Name already in use, must choose a new one */
3986 : 7628 : int refnamelen = strlen(refname);
3987 : 7628 : char *modname = (char *) palloc(refnamelen + 16);
3988 : : NameHashEntry *hentry2;
3989 : :
3990 : : do
3991 : : {
3992 : 7631 : hentry->counter++;
3993 : : for (;;)
3994 : : {
3995 : 7637 : memcpy(modname, refname, refnamelen);
3996 : 7637 : sprintf(modname + refnamelen, "_%d", hentry->counter);
3997 [ + + ]: 7637 : if (strlen(modname) < NAMEDATALEN)
3998 : 7631 : break;
3999 : : /* drop chars from refname to keep all the digits */
4000 : 6 : refnamelen = pg_mbcliplen(refname, refnamelen,
4001 : : refnamelen - 1);
4002 : : }
4003 : 7631 : hentry2 = (NameHashEntry *) hash_search(names_hash,
4004 : : modname,
4005 : : HASH_ENTER,
4006 : : &found);
4007 [ + + ]: 7631 : } while (found);
4008 : 7628 : hentry2->counter = 0; /* init new hash entry */
4009 : 7628 : refname = modname;
4010 : : }
4011 : : else
4012 : : {
4013 : : /* Name not previously used, need only initialize hentry */
4014 : 34196 : hentry->counter = 0;
4015 : : }
4016 : : }
4017 : :
4835 4018 : 51693 : dpns->rtable_names = lappend(dpns->rtable_names, refname);
4019 : 51693 : rtindex++;
4020 : : }
4021 : :
3684 4022 : 27439 : hash_destroy(names_hash);
4023 : : }
4024 : :
4025 : : /*
4026 : : * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
4027 : : *
4028 : : * For convenience, this is defined to initialize the deparse_namespace struct
4029 : : * from scratch.
4030 : : */
4031 : : static void
4734 4032 : 2931 : set_deparse_for_query(deparse_namespace *dpns, Query *query,
4033 : : List *parent_namespaces)
4034 : : {
4035 : : ListCell *lc;
4036 : : ListCell *lc2;
4037 : :
4038 : : /* Initialize *dpns and fill rtable/ctes links */
4039 : 2931 : memset(dpns, 0, sizeof(deparse_namespace));
4040 : 2931 : dpns->rtable = query->rtable;
2198 4041 : 2931 : dpns->subplans = NIL;
4734 4042 : 2931 : dpns->ctes = query->cteList;
2198 4043 : 2931 : dpns->appendrels = NULL;
335 dean.a.rasheed@gmail 4044 : 2931 : dpns->ret_old_alias = query->returningOldAlias;
4045 : 2931 : dpns->ret_new_alias = query->returningNewAlias;
4046 : :
4047 : : /* Assign a unique relation alias to each RTE */
4734 tgl@sss.pgh.pa.us 4048 : 2931 : set_rtable_names(dpns, parent_namespaces, NULL);
4049 : :
4050 : : /* Initialize dpns->rtable_columns to contain zeroed structs */
4051 : 2931 : dpns->rtable_columns = NIL;
4052 [ + + ]: 8221 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4053 : 5290 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4054 : : palloc0(sizeof(deparse_columns)));
4055 : :
4056 : : /* If it's a utility query, it won't have a jointree */
4598 4057 [ + + ]: 2931 : if (query->jointree)
4058 : : {
4059 : : /* Detect whether global uniqueness of USING names is needed */
4060 : 2923 : dpns->unique_using =
4530 4061 : 2923 : has_dangerous_join_using(dpns, (Node *) query->jointree);
4062 : :
4063 : : /*
4064 : : * Select names for columns merged by USING, via a recursive pass over
4065 : : * the query jointree.
4066 : : */
4248 4067 : 2923 : set_using_names(dpns, (Node *) query->jointree, NIL);
4068 : : }
4069 : :
4070 : : /*
4071 : : * Now assign remaining column aliases for each RTE. We do this in a
4072 : : * linear scan of the rtable, so as to process RTEs whether or not they
4073 : : * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4074 : : * etc). JOIN RTEs must be processed after their children, but this is
4075 : : * okay because they appear later in the rtable list than their children
4076 : : * (cf Asserts in identify_join_columns()).
4077 : : */
4734 4078 [ + + + + : 8221 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
+ + + + +
+ + - +
+ ]
4079 : : {
4080 : 5290 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4081 : 5290 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4082 : :
4083 [ + + ]: 5290 : if (rte->rtekind == RTE_JOIN)
4084 : 761 : set_join_column_names(dpns, rte, colinfo);
4085 : : else
4086 : 4529 : set_relation_column_names(dpns, rte, colinfo);
4087 : : }
4088 : 2931 : }
4089 : :
4090 : : /*
4091 : : * set_simple_column_names: fill in column aliases for non-query situations
4092 : : *
4093 : : * This handles EXPLAIN and cases where we only have relation RTEs. Without
4094 : : * a join tree, we can't do anything smart about join RTEs, but we don't
4095 : : * need to, because EXPLAIN should never see join alias Vars anyway.
4096 : : * If we find a join RTE we'll just skip it, leaving its deparse_columns
4097 : : * struct all-zero. If somehow we try to deparse a join alias Var, we'll
4098 : : * error out cleanly because the struct's num_cols will be zero.
4099 : : */
4100 : : static void
4101 : 24794 : set_simple_column_names(deparse_namespace *dpns)
4102 : : {
4103 : : ListCell *lc;
4104 : : ListCell *lc2;
4105 : :
4106 : : /* Initialize dpns->rtable_columns to contain zeroed structs */
4107 : 24794 : dpns->rtable_columns = NIL;
4108 [ + + ]: 71197 : while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4109 : 46403 : dpns->rtable_columns = lappend(dpns->rtable_columns,
4110 : : palloc0(sizeof(deparse_columns)));
4111 : :
4112 : : /* Assign unique column aliases within each non-join RTE */
4113 [ + - + + : 71197 : forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
+ - + + +
+ + - +
+ ]
4114 : : {
4115 : 46403 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
4116 : 46403 : deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
4117 : :
365 4118 [ + + ]: 46403 : if (rte->rtekind != RTE_JOIN)
4119 : 43405 : set_relation_column_names(dpns, rte, colinfo);
4120 : : }
4734 4121 : 24794 : }
4122 : :
4123 : : /*
4124 : : * has_dangerous_join_using: search jointree for unnamed JOIN USING
4125 : : *
4126 : : * Merged columns of a JOIN USING may act differently from either of the input
4127 : : * columns, either because they are merged with COALESCE (in a FULL JOIN) or
4128 : : * because an implicit coercion of the underlying input column is required.
4129 : : * In such a case the column must be referenced as a column of the JOIN not as
4130 : : * a column of either input. And this is problematic if the join is unnamed
4131 : : * (alias-less): we cannot qualify the column's name with an RTE name, since
4132 : : * there is none. (Forcibly assigning an alias to the join is not a solution,
4133 : : * since that will prevent legal references to tables below the join.)
4134 : : * To ensure that every column in the query is unambiguously referenceable,
4135 : : * we must assign such merged columns names that are globally unique across
4136 : : * the whole query, aliasing other columns out of the way as necessary.
4137 : : *
4138 : : * Because the ensuing re-aliasing is fairly damaging to the readability of
4139 : : * the query, we don't do this unless we have to. So, we must pre-scan
4140 : : * the join tree to see if we have to, before starting set_using_names().
4141 : : */
4142 : : static bool
4530 4143 : 6949 : has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
4144 : : {
4734 4145 [ + + ]: 6949 : if (IsA(jtnode, RangeTblRef))
4146 : : {
4147 : : /* nothing to do here */
4148 : : }
4149 [ + + ]: 3651 : else if (IsA(jtnode, FromExpr))
4150 : : {
4151 : 2923 : FromExpr *f = (FromExpr *) jtnode;
4152 : : ListCell *lc;
4153 : :
4154 [ + + + + : 5529 : foreach(lc, f->fromlist)
+ + ]
4155 : : {
4530 4156 [ + + ]: 2642 : if (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))
4734 4157 : 36 : return true;
4158 : : }
4159 : : }
4160 [ + - ]: 728 : else if (IsA(jtnode, JoinExpr))
4161 : : {
4162 : 728 : JoinExpr *j = (JoinExpr *) jtnode;
4163 : :
4164 : : /* Is it an unnamed JOIN with USING? */
4530 4165 [ + + + + ]: 728 : if (j->alias == NULL && j->usingClause)
4166 : : {
4167 : : /*
4168 : : * Yes, so check each join alias var to see if any of them are not
4169 : : * simple references to underlying columns. If so, we have a
4170 : : * dangerous situation and must pick unique aliases.
4171 : : */
4172 : 143 : RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4173 : :
4174 : : /* We need only examine the merged columns */
2169 4175 [ + + ]: 298 : for (int i = 0; i < jrte->joinmergedcols; i++)
4176 : : {
4177 : 191 : Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4178 : :
4179 [ + + ]: 191 : if (!IsA(aliasvar, Var))
4530 4180 : 36 : return true;
4181 : : }
4182 : : }
4183 : :
4184 : : /* Nope, but inspect children */
4185 [ - + ]: 692 : if (has_dangerous_join_using(dpns, j->larg))
4734 tgl@sss.pgh.pa.us 4186 :UBC 0 : return true;
4530 tgl@sss.pgh.pa.us 4187 [ - + ]:CBC 692 : if (has_dangerous_join_using(dpns, j->rarg))
4734 tgl@sss.pgh.pa.us 4188 :UBC 0 : return true;
4189 : : }
4190 : : else
4191 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
4192 : : (int) nodeTag(jtnode));
4734 tgl@sss.pgh.pa.us 4193 :CBC 6877 : return false;
4194 : : }
4195 : :
4196 : : /*
4197 : : * set_using_names: select column aliases to be used for merged USING columns
4198 : : *
4199 : : * We do this during a recursive descent of the query jointree.
4200 : : * dpns->unique_using must already be set to determine the global strategy.
4201 : : *
4202 : : * Column alias info is saved in the dpns->rtable_columns list, which is
4203 : : * assumed to be filled with pre-zeroed deparse_columns structs.
4204 : : *
4205 : : * parentUsing is a list of all USING aliases assigned in parent joins of
4206 : : * the current jointree node. (The passed-in list must not be modified.)
4207 : : *
4208 : : * Note that we do not use per-deparse_columns hash tables in this function.
4209 : : * The number of names that need to be assigned should be small enough that
4210 : : * we don't need to trouble with that.
4211 : : */
4212 : : static void
4248 4213 : 7108 : set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
4214 : : {
4734 4215 [ + + ]: 7108 : if (IsA(jtnode, RangeTblRef))
4216 : : {
4217 : : /* nothing to do now */
4218 : : }
4219 [ + + ]: 3684 : else if (IsA(jtnode, FromExpr))
4220 : : {
4221 : 2923 : FromExpr *f = (FromExpr *) jtnode;
4222 : : ListCell *lc;
4223 : :
4224 [ + + + + : 5586 : foreach(lc, f->fromlist)
+ + ]
4248 4225 : 2663 : set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4226 : : }
4734 4227 [ + - ]: 761 : else if (IsA(jtnode, JoinExpr))
4228 : : {
4229 : 761 : JoinExpr *j = (JoinExpr *) jtnode;
4230 : 761 : RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4231 : 761 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
4232 : : int *leftattnos;
4233 : : int *rightattnos;
4234 : : deparse_columns *leftcolinfo;
4235 : : deparse_columns *rightcolinfo;
4236 : : int i;
4237 : : ListCell *lc;
4238 : :
4239 : : /* Get info about the shape of the join */
4240 : 761 : identify_join_columns(j, rte, colinfo);
4241 : 761 : leftattnos = colinfo->leftattnos;
4242 : 761 : rightattnos = colinfo->rightattnos;
4243 : :
4244 : : /* Look up the not-yet-filled-in child deparse_columns structs */
4245 : 761 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4246 : 761 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4247 : :
4248 : : /*
4249 : : * If this join is unnamed, then we cannot substitute new aliases at
4250 : : * this level, so any name requirements pushed down to here must be
4251 : : * pushed down again to the children.
4252 : : */
4253 [ + + ]: 761 : if (rte->alias == NULL)
4254 : : {
4255 [ + + ]: 776 : for (i = 0; i < colinfo->num_cols; i++)
4256 : : {
4257 : 69 : char *colname = colinfo->colnames[i];
4258 : :
4259 [ + + ]: 69 : if (colname == NULL)
4260 : 12 : continue;
4261 : :
4262 : : /* Push down to left column, unless it's a system column */
4263 [ + + ]: 57 : if (leftattnos[i] > 0)
4264 : : {
4265 : 51 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4266 : 51 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4267 : : }
4268 : :
4269 : : /* Same on the righthand side */
4270 [ + - ]: 57 : if (rightattnos[i] > 0)
4271 : : {
4272 : 57 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4273 : 57 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4274 : : }
4275 : : }
4276 : : }
4277 : :
4278 : : /*
4279 : : * If there's a USING clause, select the USING column names and push
4280 : : * those names down to the children. We have two strategies:
4281 : : *
4282 : : * If dpns->unique_using is true, we force all USING names to be
4283 : : * unique across the whole query level. In principle we'd only need
4284 : : * the names of dangerous USING columns to be globally unique, but to
4285 : : * safely assign all USING names in a single pass, we have to enforce
4286 : : * the same uniqueness rule for all of them. However, if a USING
4287 : : * column's name has been pushed down from the parent, we should use
4288 : : * it as-is rather than making a uniqueness adjustment. This is
4289 : : * necessary when we're at an unnamed join, and it creates no risk of
4290 : : * ambiguity. Also, if there's a user-written output alias for a
4291 : : * merged column, we prefer to use that rather than the input name;
4292 : : * this simplifies the logic and seems likely to lead to less aliasing
4293 : : * overall.
4294 : : *
4295 : : * If dpns->unique_using is false, we only need USING names to be
4296 : : * unique within their own join RTE. We still need to honor
4297 : : * pushed-down names, though.
4298 : : *
4299 : : * Though significantly different in results, these two strategies are
4300 : : * implemented by the same code, with only the difference of whether
4301 : : * to put assigned names into dpns->using_names.
4302 : : */
4303 [ + + ]: 761 : if (j->usingClause)
4304 : : {
4305 : : /* Copy the input parentUsing list so we don't modify it */
4248 4306 : 212 : parentUsing = list_copy(parentUsing);
4307 : :
4308 : : /* USING names must correspond to the first join output columns */
4734 4309 : 212 : expand_colnames_array_to(colinfo, list_length(j->usingClause));
4310 : 212 : i = 0;
4311 [ + - + + : 502 : foreach(lc, j->usingClause)
+ + ]
4312 : : {
4313 : 290 : char *colname = strVal(lfirst(lc));
4314 : :
4315 : : /* Assert it's a merged column */
4316 [ + - - + ]: 290 : Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4317 : :
4318 : : /* Adopt passed-down name if any, else select unique name */
4319 [ + + ]: 290 : if (colinfo->colnames[i] != NULL)
4320 : 51 : colname = colinfo->colnames[i];
4321 : : else
4322 : : {
4323 : : /* Prefer user-written output alias if any */
4324 [ + + - + ]: 239 : if (rte->alias && i < list_length(rte->alias->colnames))
4734 tgl@sss.pgh.pa.us 4325 :UBC 0 : colname = strVal(list_nth(rte->alias->colnames, i));
4326 : : /* Make it appropriately unique */
4734 tgl@sss.pgh.pa.us 4327 :CBC 239 : colname = make_colname_unique(colname, dpns, colinfo);
4328 [ + + ]: 239 : if (dpns->unique_using)
4329 : 63 : dpns->using_names = lappend(dpns->using_names,
4330 : : colname);
4331 : : /* Save it as output column name, too */
4332 : 239 : colinfo->colnames[i] = colname;
4333 : : }
4334 : :
4335 : : /* Remember selected names for use later */
4336 : 290 : colinfo->usingNames = lappend(colinfo->usingNames, colname);
4248 4337 : 290 : parentUsing = lappend(parentUsing, colname);
4338 : :
4339 : : /* Push down to left column, unless it's a system column */
4734 4340 [ + - ]: 290 : if (leftattnos[i] > 0)
4341 : : {
4342 : 290 : expand_colnames_array_to(leftcolinfo, leftattnos[i]);
4343 : 290 : leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4344 : : }
4345 : :
4346 : : /* Same on the righthand side */
4347 [ + - ]: 290 : if (rightattnos[i] > 0)
4348 : : {
4349 : 290 : expand_colnames_array_to(rightcolinfo, rightattnos[i]);
4350 : 290 : rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4351 : : }
4352 : :
4353 : 290 : i++;
4354 : : }
4355 : : }
4356 : :
4357 : : /* Mark child deparse_columns structs with correct parentUsing info */
4248 4358 : 761 : leftcolinfo->parentUsing = parentUsing;
4359 : 761 : rightcolinfo->parentUsing = parentUsing;
4360 : :
4361 : : /* Now recursively assign USING column names in children */
4362 : 761 : set_using_names(dpns, j->larg, parentUsing);
4363 : 761 : set_using_names(dpns, j->rarg, parentUsing);
4364 : : }
4365 : : else
4734 tgl@sss.pgh.pa.us 4366 [ # # ]:UBC 0 : elog(ERROR, "unrecognized node type: %d",
4367 : : (int) nodeTag(jtnode));
4734 tgl@sss.pgh.pa.us 4368 :CBC 7108 : }
4369 : :
4370 : : /*
4371 : : * set_relation_column_names: select column aliases for a non-join RTE
4372 : : *
4373 : : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4374 : : * If any colnames entries are already filled in, those override local
4375 : : * choices.
4376 : : */
4377 : : static void
4378 : 47934 : set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4379 : : deparse_columns *colinfo)
4380 : : {
4381 : : int ncolumns;
4382 : : char **real_colnames;
4383 : : bool changed_any;
4384 : : int noldcolumns;
4385 : : int i;
4386 : : int j;
4387 : :
4388 : : /*
4389 : : * Construct an array of the current "real" column names of the RTE.
4390 : : * real_colnames[] will be indexed by physical column number, with NULL
4391 : : * entries for dropped columns.
4392 : : */
4393 [ + + ]: 47934 : if (rte->rtekind == RTE_RELATION)
4394 : : {
4395 : : /* Relation --- look to the system catalogs for up-to-date info */
4396 : : Relation rel;
4397 : : TupleDesc tupdesc;
4398 : :
4399 : 40782 : rel = relation_open(rte->relid, AccessShareLock);
4400 : 40782 : tupdesc = RelationGetDescr(rel);
4401 : :
4402 : 40782 : ncolumns = tupdesc->natts;
4403 : 40782 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4404 : :
4405 [ + + ]: 258057 : for (i = 0; i < ncolumns; i++)
4406 : : {
3041 andres@anarazel.de 4407 : 217275 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4408 : :
4409 [ + + ]: 217275 : if (attr->attisdropped)
4734 tgl@sss.pgh.pa.us 4410 : 1592 : real_colnames[i] = NULL;
4411 : : else
3041 andres@anarazel.de 4412 : 215683 : real_colnames[i] = pstrdup(NameStr(attr->attname));
4413 : : }
4734 tgl@sss.pgh.pa.us 4414 : 40782 : relation_close(rel, AccessShareLock);
4415 : : }
4416 : : else
4417 : : {
4418 : : /* Otherwise get the column names from eref or expandRTE() */
4419 : : List *colnames;
4420 : : ListCell *lc;
4421 : :
4422 : : /*
4423 : : * Functions returning composites have the annoying property that some
4424 : : * of the composite type's columns might have been dropped since the
4425 : : * query was parsed. If possible, use expandRTE() to handle that
4426 : : * case, since it has the tedious logic needed to find out about
4427 : : * dropped columns. However, if we're explaining a plan, then we
4428 : : * don't have rte->functions because the planner thinks that won't be
4429 : : * needed later, and that breaks expandRTE(). So in that case we have
4430 : : * to rely on rte->eref, which may lead us to report a dropped
4431 : : * column's old name; that seems close enough for EXPLAIN's purposes.
4432 : : *
4433 : : * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4434 : : * which should be sufficiently up-to-date: no other RTE types can
4435 : : * have columns get dropped from under them after parsing.
4436 : : */
1245 4437 [ + + + + ]: 7152 : if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4438 : : {
4439 : : /* Since we're not creating Vars, rtindex etc. don't matter */
335 dean.a.rasheed@gmail 4440 : 429 : expandRTE(rte, 1, 0, VAR_RETURNING_DEFAULT, -1,
4441 : : true /* include dropped */ , &colnames, NULL);
4442 : : }
4443 : : else
1245 tgl@sss.pgh.pa.us 4444 : 6723 : colnames = rte->eref->colnames;
4445 : :
4446 : 7152 : ncolumns = list_length(colnames);
4734 4447 : 7152 : real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4448 : :
4449 : 7152 : i = 0;
1245 4450 [ + + + + : 23381 : foreach(lc, colnames)
+ + ]
4451 : : {
4452 : : /*
4453 : : * If the column name we find here is an empty string, then it's a
4454 : : * dropped column, so change to NULL.
4455 : : */
4169 4456 : 16229 : char *cname = strVal(lfirst(lc));
4457 : :
4458 [ + + ]: 16229 : if (cname[0] == '\0')
4459 : 27 : cname = NULL;
4460 : 16229 : real_colnames[i] = cname;
4734 4461 : 16229 : i++;
4462 : : }
4463 : : }
4464 : :
4465 : : /*
4466 : : * Ensure colinfo->colnames has a slot for each column. (It could be long
4467 : : * enough already, if we pushed down a name for the last column.) Note:
4468 : : * it's possible that there are now more columns than there were when the
4469 : : * query was parsed, ie colnames could be longer than rte->eref->colnames.
4470 : : * We must assign unique aliases to the new columns too, else there could
4471 : : * be unresolved conflicts when the view/rule is reloaded.
4472 : : */
4473 : 47934 : expand_colnames_array_to(colinfo, ncolumns);
4474 [ - + ]: 47934 : Assert(colinfo->num_cols == ncolumns);
4475 : :
4476 : : /*
4477 : : * Make sufficiently large new_colnames and is_new_col arrays, too.
4478 : : *
4479 : : * Note: because we leave colinfo->num_new_cols zero until after the loop,
4480 : : * colname_is_unique will not consult that array, which is fine because it
4481 : : * would only be duplicate effort.
4482 : : */
4483 : 47934 : colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4484 : 47934 : colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4485 : :
4486 : : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
463 4487 : 47934 : build_colinfo_names_hash(colinfo);
4488 : :
4489 : : /*
4490 : : * Scan the columns, select a unique alias for each one, and store it in
4491 : : * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4492 : : * entries for dropped columns, the latter omits them. Also mark
4493 : : * new_colnames entries as to whether they are new since parse time; this
4494 : : * is the case for entries beyond the length of rte->eref->colnames.
4495 : : */
4734 4496 : 47934 : noldcolumns = list_length(rte->eref->colnames);
4497 : 47934 : changed_any = false;
4498 : 47934 : j = 0;
4499 [ + + ]: 281438 : for (i = 0; i < ncolumns; i++)
4500 : : {
4501 : 233504 : char *real_colname = real_colnames[i];
4502 : 233504 : char *colname = colinfo->colnames[i];
4503 : :
4504 : : /* Skip dropped columns */
4505 [ + + ]: 233504 : if (real_colname == NULL)
4506 : : {
4507 [ - + ]: 1619 : Assert(colname == NULL); /* colnames[i] is already NULL */
4508 : 1619 : continue;
4509 : : }
4510 : :
4511 : : /* If alias already assigned, that's what to use */
4512 [ + + ]: 231885 : if (colname == NULL)
4513 : : {
4514 : : /* If user wrote an alias, prefer that over real column name */
4515 [ + + + + ]: 231356 : if (rte->alias && i < list_length(rte->alias->colnames))
4516 : 22406 : colname = strVal(list_nth(rte->alias->colnames, i));
4517 : : else
4518 : 208950 : colname = real_colname;
4519 : :
4520 : : /* Unique-ify and insert into colinfo */
4521 : 231356 : colname = make_colname_unique(colname, dpns, colinfo);
4522 : :
4523 : 231356 : colinfo->colnames[i] = colname;
463 4524 : 231356 : add_to_names_hash(colinfo, colname);
4525 : : }
4526 : :
4527 : : /* Put names of non-dropped columns in new_colnames[] too */
4734 4528 : 231885 : colinfo->new_colnames[j] = colname;
4529 : : /* And mark them as new or not */
4530 : 231885 : colinfo->is_new_col[j] = (i >= noldcolumns);
4531 : 231885 : j++;
4532 : :
4533 : : /* Remember if any assigned aliases differ from "real" name */
4534 [ + + + + ]: 231885 : if (!changed_any && strcmp(colname, real_colname) != 0)
4535 : 599 : changed_any = true;
4536 : : }
4537 : :
4538 : : /* We're now done needing the colinfo's names_hash */
463 4539 : 47934 : destroy_colinfo_names_hash(colinfo);
4540 : :
4541 : : /*
4542 : : * Set correct length for new_colnames[] array. (Note: if columns have
4543 : : * been added, colinfo->num_cols includes them, which is not really quite
4544 : : * right but is harmless, since any new columns must be at the end where
4545 : : * they won't affect varattnos of pre-existing columns.)
4546 : : */
4734 4547 : 47934 : colinfo->num_new_cols = j;
4548 : :
4549 : : /*
4550 : : * For a relation RTE, we need only print the alias column names if any
4551 : : * are different from the underlying "real" names. For a function RTE,
4552 : : * always emit a complete column alias list; this is to protect against
4553 : : * possible instability of the default column names (eg, from altering
4554 : : * parameter names). For tablefunc RTEs, we never print aliases, because
4555 : : * the column names are part of the clause itself. For other RTE types,
4556 : : * print if we changed anything OR if there were user-written column
4557 : : * aliases (since the latter would be part of the underlying "reality").
4558 : : */
4559 [ + + ]: 47934 : if (rte->rtekind == RTE_RELATION)
4560 : 40782 : colinfo->printaliases = changed_any;
4561 [ + + ]: 7152 : else if (rte->rtekind == RTE_FUNCTION)
4562 : 717 : colinfo->printaliases = true;
3206 alvherre@alvh.no-ip. 4563 [ + + ]: 6435 : else if (rte->rtekind == RTE_TABLEFUNC)
4564 : 88 : colinfo->printaliases = false;
4734 tgl@sss.pgh.pa.us 4565 [ + + + + ]: 6347 : else if (rte->alias && rte->alias->colnames != NIL)
4566 : 369 : colinfo->printaliases = true;
4567 : : else
4568 : 5978 : colinfo->printaliases = changed_any;
4569 : 47934 : }
4570 : :
4571 : : /*
4572 : : * set_join_column_names: select column aliases for a join RTE
4573 : : *
4574 : : * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4575 : : * If any colnames entries are already filled in, those override local
4576 : : * choices. Also, names for USING columns were already chosen by
4577 : : * set_using_names(). We further expect that column alias selection has been
4578 : : * completed for both input RTEs.
4579 : : */
4580 : : static void
4581 : 761 : set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
4582 : : deparse_columns *colinfo)
4583 : : {
4584 : : deparse_columns *leftcolinfo;
4585 : : deparse_columns *rightcolinfo;
4586 : : bool changed_any;
4587 : : int noldcolumns;
4588 : : int nnewcolumns;
4589 : 761 : Bitmapset *leftmerged = NULL;
4590 : 761 : Bitmapset *rightmerged = NULL;
4591 : : int i;
4592 : : int j;
4593 : : int ic;
4594 : : int jc;
4595 : :
4596 : : /* Look up the previously-filled-in child deparse_columns structs */
4597 : 761 : leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
4598 : 761 : rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
4599 : :
4600 : : /*
4601 : : * Ensure colinfo->colnames has a slot for each column. (It could be long
4602 : : * enough already, if we pushed down a name for the last column.) Note:
4603 : : * it's possible that one or both inputs now have more columns than there
4604 : : * were when the query was parsed, but we'll deal with that below. We
4605 : : * only need entries in colnames for pre-existing columns.
4606 : : */
4607 : 761 : noldcolumns = list_length(rte->eref->colnames);
4608 : 761 : expand_colnames_array_to(colinfo, noldcolumns);
4609 [ - + ]: 761 : Assert(colinfo->num_cols == noldcolumns);
4610 : :
4611 : : /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
463 4612 : 761 : build_colinfo_names_hash(colinfo);
4613 : :
4614 : : /*
4615 : : * Scan the join output columns, select an alias for each one, and store
4616 : : * it in colinfo->colnames. If there are USING columns, set_using_names()
4617 : : * already selected their names, so we can start the loop at the first
4618 : : * non-merged column.
4619 : : */
4734 4620 : 761 : changed_any = false;
4621 [ + + ]: 25643 : for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4622 : : {
4623 : 24882 : char *colname = colinfo->colnames[i];
4624 : : char *real_colname;
4625 : :
4626 : : /* Join column must refer to at least one input column */
2169 4627 [ + + - + ]: 24882 : Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4628 : :
4629 : : /* Get the child column name */
4734 4630 [ + + ]: 24882 : if (colinfo->leftattnos[i] > 0)
4631 : 17594 : real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4632 [ + - ]: 7288 : else if (colinfo->rightattnos[i] > 0)
4633 : 7288 : real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4634 : : else
4635 : : {
4636 : : /* We're joining system columns --- use eref name */
4530 tgl@sss.pgh.pa.us 4637 :UBC 0 : real_colname = strVal(list_nth(rte->eref->colnames, i));
4638 : : }
4639 : :
4640 : : /* If child col has been dropped, no need to assign a join colname */
2169 tgl@sss.pgh.pa.us 4641 [ + + ]:CBC 24882 : if (real_colname == NULL)
4642 : : {
4643 : 3 : colinfo->colnames[i] = NULL;
4644 : 3 : continue;
4645 : : }
4646 : :
4647 : : /* In an unnamed join, just report child column names as-is */
4734 4648 [ + + ]: 24879 : if (rte->alias == NULL)
4649 : : {
4650 : 24690 : colinfo->colnames[i] = real_colname;
463 4651 : 24690 : add_to_names_hash(colinfo, real_colname);
4734 4652 : 24690 : continue;
4653 : : }
4654 : :
4655 : : /* If alias already assigned, that's what to use */
4656 [ + - ]: 189 : if (colname == NULL)
4657 : : {
4658 : : /* If user wrote an alias, prefer that over real column name */
4659 [ + - + + ]: 189 : if (rte->alias && i < list_length(rte->alias->colnames))
4660 : 48 : colname = strVal(list_nth(rte->alias->colnames, i));
4661 : : else
4662 : 141 : colname = real_colname;
4663 : :
4664 : : /* Unique-ify and insert into colinfo */
4665 : 189 : colname = make_colname_unique(colname, dpns, colinfo);
4666 : :
4667 : 189 : colinfo->colnames[i] = colname;
463 4668 : 189 : add_to_names_hash(colinfo, colname);
4669 : : }
4670 : :
4671 : : /* Remember if any assigned aliases differ from "real" name */
4734 4672 [ + + + + ]: 189 : if (!changed_any && strcmp(colname, real_colname) != 0)
4673 : 12 : changed_any = true;
4674 : : }
4675 : :
4676 : : /*
4677 : : * Calculate number of columns the join would have if it were re-parsed
4678 : : * now, and create storage for the new_colnames and is_new_col arrays.
4679 : : *
4680 : : * Note: colname_is_unique will be consulting new_colnames[] during the
4681 : : * loops below, so its not-yet-filled entries must be zeroes.
4682 : : */
4683 : 1522 : nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
4684 : 761 : list_length(colinfo->usingNames);
4685 : 761 : colinfo->num_new_cols = nnewcolumns;
4686 : 761 : colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
4687 : 761 : colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
4688 : :
4689 : : /*
4690 : : * Generating the new_colnames array is a bit tricky since any new columns
4691 : : * added since parse time must be inserted in the right places. This code
4692 : : * must match the parser, which will order a join's columns as merged
4693 : : * columns first (in USING-clause order), then non-merged columns from the
4694 : : * left input (in attnum order), then non-merged columns from the right
4695 : : * input (ditto). If one of the inputs is itself a join, its columns will
4696 : : * be ordered according to the same rule, which means newly-added columns
4697 : : * might not be at the end. We can figure out what's what by consulting
4698 : : * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
4699 : : *
4700 : : * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
4701 : : * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
4702 : : * meanings for the current child RTE.
4703 : : */
4704 : :
4705 : : /* Handle merged columns; they are first and can't be new */
4706 : 761 : i = j = 0;
4707 : 761 : while (i < noldcolumns &&
4708 [ + - + - ]: 1051 : colinfo->leftattnos[i] != 0 &&
4709 [ + + ]: 1051 : colinfo->rightattnos[i] != 0)
4710 : : {
4711 : : /* column name is already determined and known unique */
4712 : 290 : colinfo->new_colnames[j] = colinfo->colnames[i];
4713 : 290 : colinfo->is_new_col[j] = false;
4714 : :
4715 : : /* build bitmapsets of child attnums of merged columns */
4716 [ + - ]: 290 : if (colinfo->leftattnos[i] > 0)
4717 : 290 : leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
4718 [ + - ]: 290 : if (colinfo->rightattnos[i] > 0)
4719 : 290 : rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);
4720 : :
4721 : 290 : i++, j++;
4722 : : }
4723 : :
4724 : : /* Handle non-merged left-child columns */
4725 : 761 : ic = 0;
4726 [ + + ]: 18888 : for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
4727 : : {
4728 : 18127 : char *child_colname = leftcolinfo->new_colnames[jc];
4729 : :
4730 [ + + ]: 18127 : if (!leftcolinfo->is_new_col[jc])
4731 : : {
4732 : : /* Advance ic to next non-dropped old column of left child */
4733 [ + - ]: 17923 : while (ic < leftcolinfo->num_cols &&
4734 [ + + ]: 17923 : leftcolinfo->colnames[ic] == NULL)
4735 : 42 : ic++;
4736 [ - + ]: 17881 : Assert(ic < leftcolinfo->num_cols);
4737 : 17881 : ic++;
4738 : : /* If it is a merged column, we already processed it */
4739 [ + + ]: 17881 : if (bms_is_member(ic, leftmerged))
4740 : 290 : continue;
4741 : : /* Else, advance i to the corresponding existing join column */
4742 [ + - ]: 17594 : while (i < colinfo->num_cols &&
4743 [ + + ]: 17594 : colinfo->colnames[i] == NULL)
4744 : 3 : i++;
4745 [ - + ]: 17591 : Assert(i < colinfo->num_cols);
4746 [ - + ]: 17591 : Assert(ic == colinfo->leftattnos[i]);
4747 : : /* Use the already-assigned name of this column */
4748 : 17591 : colinfo->new_colnames[j] = colinfo->colnames[i];
4749 : 17591 : i++;
4750 : : }
4751 : : else
4752 : : {
4753 : : /*
4754 : : * Unique-ify the new child column name and assign, unless we're
4755 : : * in an unnamed join, in which case just copy
4756 : : */
4757 [ + + ]: 246 : if (rte->alias != NULL)
4758 : : {
4759 : 132 : colinfo->new_colnames[j] =
4760 : 66 : make_colname_unique(child_colname, dpns, colinfo);
4761 [ + + ]: 66 : if (!changed_any &&
4762 [ + + ]: 54 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4763 : 6 : changed_any = true;
4764 : : }
4765 : : else
4766 : 180 : colinfo->new_colnames[j] = child_colname;
463 4767 : 246 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
4768 : : }
4769 : :
4734 4770 : 17837 : colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
4771 : 17837 : j++;
4772 : : }
4773 : :
4774 : : /* Handle non-merged right-child columns in exactly the same way */
4775 : 761 : ic = 0;
4776 [ + + ]: 8423 : for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
4777 : : {
4778 : 7662 : char *child_colname = rightcolinfo->new_colnames[jc];
4779 : :
4780 [ + + ]: 7662 : if (!rightcolinfo->is_new_col[jc])
4781 : : {
4782 : : /* Advance ic to next non-dropped old column of right child */
4783 [ + - ]: 7578 : while (ic < rightcolinfo->num_cols &&
4784 [ - + ]: 7578 : rightcolinfo->colnames[ic] == NULL)
4734 tgl@sss.pgh.pa.us 4785 :UBC 0 : ic++;
4734 tgl@sss.pgh.pa.us 4786 [ - + ]:CBC 7578 : Assert(ic < rightcolinfo->num_cols);
4787 : 7578 : ic++;
4788 : : /* If it is a merged column, we already processed it */
4789 [ + + ]: 7578 : if (bms_is_member(ic, rightmerged))
4790 : 290 : continue;
4791 : : /* Else, advance i to the corresponding existing join column */
4792 [ + - ]: 7288 : while (i < colinfo->num_cols &&
4793 [ - + ]: 7288 : colinfo->colnames[i] == NULL)
4734 tgl@sss.pgh.pa.us 4794 :UBC 0 : i++;
4734 tgl@sss.pgh.pa.us 4795 [ - + ]:CBC 7288 : Assert(i < colinfo->num_cols);
4796 [ - + ]: 7288 : Assert(ic == colinfo->rightattnos[i]);
4797 : : /* Use the already-assigned name of this column */
4798 : 7288 : colinfo->new_colnames[j] = colinfo->colnames[i];
4799 : 7288 : i++;
4800 : : }
4801 : : else
4802 : : {
4803 : : /*
4804 : : * Unique-ify the new child column name and assign, unless we're
4805 : : * in an unnamed join, in which case just copy
4806 : : */
4807 [ + + ]: 84 : if (rte->alias != NULL)
4808 : : {
4809 : 24 : colinfo->new_colnames[j] =
4810 : 12 : make_colname_unique(child_colname, dpns, colinfo);
4811 [ + - ]: 12 : if (!changed_any &&
4812 [ + + ]: 12 : strcmp(colinfo->new_colnames[j], child_colname) != 0)
4813 : 6 : changed_any = true;
4814 : : }
4815 : : else
4816 : 72 : colinfo->new_colnames[j] = child_colname;
463 4817 : 84 : add_to_names_hash(colinfo, colinfo->new_colnames[j]);
4818 : : }
4819 : :
4734 4820 : 7372 : colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
4821 : 7372 : j++;
4822 : : }
4823 : :
4824 : : /* Assert we processed the right number of columns */
4825 : : #ifdef USE_ASSERT_CHECKING
4826 [ - + - - ]: 761 : while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
4734 tgl@sss.pgh.pa.us 4827 :UBC 0 : i++;
4734 tgl@sss.pgh.pa.us 4828 [ - + ]:CBC 761 : Assert(i == colinfo->num_cols);
4829 [ - + ]: 761 : Assert(j == nnewcolumns);
4830 : : #endif
4831 : :
4832 : : /* We're now done needing the colinfo's names_hash */
463 4833 : 761 : destroy_colinfo_names_hash(colinfo);
4834 : :
4835 : : /*
4836 : : * For a named join, print column aliases if we changed any from the child
4837 : : * names. Unnamed joins cannot print aliases.
4838 : : */
4734 4839 [ + + ]: 761 : if (rte->alias != NULL)
4840 : 54 : colinfo->printaliases = changed_any;
4841 : : else
4842 : 707 : colinfo->printaliases = false;
4843 : 761 : }
4844 : :
4845 : : /*
4846 : : * colname_is_unique: is colname distinct from already-chosen column names?
4847 : : *
4848 : : * dpns is query-wide info, colinfo is for the column's RTE
4849 : : */
4850 : : static bool
2969 peter_e@gmx.net 4851 : 233046 : colname_is_unique(const char *colname, deparse_namespace *dpns,
4852 : : deparse_columns *colinfo)
4853 : : {
4854 : : int i;
4855 : : ListCell *lc;
4856 : :
4857 : : /*
4858 : : * If we have a hash table, consult that instead of linearly scanning the
4859 : : * colinfo's strings.
4860 : : */
463 tgl@sss.pgh.pa.us 4861 [ + + ]: 233046 : if (colinfo->names_hash)
4862 : : {
4863 [ - + ]: 9001 : if (hash_search(colinfo->names_hash,
4864 : : colname,
4865 : : HASH_FIND,
4866 : : NULL) != NULL)
4734 tgl@sss.pgh.pa.us 4867 :UBC 0 : return false;
4868 : : }
4869 : : else
4870 : : {
4871 : : /* Check against already-assigned column aliases within RTE */
463 tgl@sss.pgh.pa.us 4872 [ + + ]:CBC 3072566 : for (i = 0; i < colinfo->num_cols; i++)
4873 : : {
4874 : 2849669 : char *oldname = colinfo->colnames[i];
4875 : :
4876 [ + + + + ]: 2849669 : if (oldname && strcmp(oldname, colname) == 0)
4877 : 1148 : return false;
4878 : : }
4879 : :
4880 : : /*
4881 : : * If we're building a new_colnames array, check that too (this will
4882 : : * be partially but not completely redundant with the previous checks)
4883 : : */
4884 [ + + ]: 223533 : for (i = 0; i < colinfo->num_new_cols; i++)
4885 : : {
4886 : 648 : char *oldname = colinfo->new_colnames[i];
4887 : :
4888 [ + + + + ]: 648 : if (oldname && strcmp(oldname, colname) == 0)
4889 : 12 : return false;
4890 : : }
4891 : :
4892 : : /*
4893 : : * Also check against names already assigned for parent-join USING
4894 : : * cols
4895 : : */
4896 [ + + + + : 224181 : foreach(lc, colinfo->parentUsing)
+ + ]
4897 : : {
4898 : 1299 : char *oldname = (char *) lfirst(lc);
4899 : :
4900 [ + + ]: 1299 : if (strcmp(oldname, colname) == 0)
4901 : 3 : return false;
4902 : : }
4903 : : }
4904 : :
4905 : : /*
4906 : : * Also check against USING-column names that must be globally unique.
4907 : : * These are not hashed, but there should be few of them.
4908 : : */
4909 [ + + + + : 232303 : foreach(lc, dpns->using_names)
+ + ]
4910 : : {
4248 4911 : 441 : char *oldname = (char *) lfirst(lc);
4912 : :
4913 [ + + ]: 441 : if (strcmp(oldname, colname) == 0)
4914 : 21 : return false;
4915 : : }
4916 : :
4734 4917 : 231862 : return true;
4918 : : }
4919 : :
4920 : : /*
4921 : : * make_colname_unique: modify colname if necessary to make it unique
4922 : : *
4923 : : * dpns is query-wide info, colinfo is for the column's RTE
4924 : : */
4925 : : static char *
4926 : 231862 : make_colname_unique(char *colname, deparse_namespace *dpns,
4927 : : deparse_columns *colinfo)
4928 : : {
4929 : : /*
4930 : : * If the selected name isn't unique, append digits to make it so. For a
4931 : : * very long input name, we might have to truncate to stay within
4932 : : * NAMEDATALEN.
4933 : : */
4934 [ + + ]: 231862 : if (!colname_is_unique(colname, dpns, colinfo))
4935 : : {
3684 4936 : 822 : int colnamelen = strlen(colname);
4937 : 822 : char *modname = (char *) palloc(colnamelen + 16);
4734 4938 : 822 : int i = 0;
4939 : :
4940 : : do
4941 : : {
3684 4942 : 1184 : i++;
4943 : : for (;;)
4944 : : {
4945 : 1184 : memcpy(modname, colname, colnamelen);
4946 : 1184 : sprintf(modname + colnamelen, "_%d", i);
4947 [ + - ]: 1184 : if (strlen(modname) < NAMEDATALEN)
4948 : 1184 : break;
4949 : : /* drop chars from colname to keep all the digits */
3684 tgl@sss.pgh.pa.us 4950 :UBC 0 : colnamelen = pg_mbcliplen(colname, colnamelen,
4951 : : colnamelen - 1);
4952 : : }
4734 tgl@sss.pgh.pa.us 4953 [ + + ]:CBC 1184 : } while (!colname_is_unique(modname, dpns, colinfo));
4954 : 822 : colname = modname;
4955 : : }
4956 : 231862 : return colname;
4957 : : }
4958 : :
4959 : : /*
4960 : : * expand_colnames_array_to: make colinfo->colnames at least n items long
4961 : : *
4962 : : * Any added array entries are initialized to zero.
4963 : : */
4964 : : static void
4965 : 49595 : expand_colnames_array_to(deparse_columns *colinfo, int n)
4966 : : {
4967 [ + + ]: 49595 : if (n > colinfo->num_cols)
4968 : : {
4969 [ + + ]: 48251 : if (colinfo->colnames == NULL)
1131 peter@eisentraut.org 4970 : 47543 : colinfo->colnames = palloc0_array(char *, n);
4971 : : else
4972 : 708 : colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
4734 tgl@sss.pgh.pa.us 4973 : 48251 : colinfo->num_cols = n;
4974 : : }
4975 : 49595 : }
4976 : :
4977 : : /*
4978 : : * build_colinfo_names_hash: optionally construct a hash table for colinfo
4979 : : */
4980 : : static void
463 4981 : 48695 : build_colinfo_names_hash(deparse_columns *colinfo)
4982 : : {
4983 : : HASHCTL hash_ctl;
4984 : : int i;
4985 : : ListCell *lc;
4986 : :
4987 : : /*
4988 : : * Use a hash table only for RTEs with at least 32 columns. (The cutoff
4989 : : * is somewhat arbitrary, but let's choose it so that this code does get
4990 : : * exercised in the regression tests.)
4991 : : */
4992 [ + + ]: 48695 : if (colinfo->num_cols < 32)
4993 : 48019 : return;
4994 : :
4995 : : /*
4996 : : * Set up the hash table. The entries are just strings with no other
4997 : : * payload.
4998 : : */
4999 : 676 : hash_ctl.keysize = NAMEDATALEN;
5000 : 676 : hash_ctl.entrysize = NAMEDATALEN;
5001 : 676 : hash_ctl.hcxt = CurrentMemoryContext;
5002 : 1352 : colinfo->names_hash = hash_create("deparse_columns names",
5003 : 676 : colinfo->num_cols + colinfo->num_new_cols,
5004 : : &hash_ctl,
5005 : : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
5006 : :
5007 : : /*
5008 : : * Preload the hash table with any names already present (these would have
5009 : : * come from set_using_names).
5010 : : */
5011 [ + + ]: 31976 : for (i = 0; i < colinfo->num_cols; i++)
5012 : : {
5013 : 31300 : char *oldname = colinfo->colnames[i];
5014 : :
5015 [ - + ]: 31300 : if (oldname)
463 tgl@sss.pgh.pa.us 5016 :UBC 0 : add_to_names_hash(colinfo, oldname);
5017 : : }
5018 : :
463 tgl@sss.pgh.pa.us 5019 [ - + ]:CBC 676 : for (i = 0; i < colinfo->num_new_cols; i++)
5020 : : {
463 tgl@sss.pgh.pa.us 5021 :UBC 0 : char *oldname = colinfo->new_colnames[i];
5022 : :
5023 [ # # ]: 0 : if (oldname)
5024 : 0 : add_to_names_hash(colinfo, oldname);
5025 : : }
5026 : :
463 tgl@sss.pgh.pa.us 5027 [ - + - - :CBC 676 : foreach(lc, colinfo->parentUsing)
- + ]
5028 : : {
463 tgl@sss.pgh.pa.us 5029 :UBC 0 : char *oldname = (char *) lfirst(lc);
5030 : :
5031 : 0 : add_to_names_hash(colinfo, oldname);
5032 : : }
5033 : : }
5034 : :
5035 : : /*
5036 : : * add_to_names_hash: add a string to the names_hash, if we're using one
5037 : : */
5038 : : static void
463 tgl@sss.pgh.pa.us 5039 :CBC 256565 : add_to_names_hash(deparse_columns *colinfo, const char *name)
5040 : : {
5041 [ + + ]: 256565 : if (colinfo->names_hash)
5042 : 31300 : (void) hash_search(colinfo->names_hash,
5043 : : name,
5044 : : HASH_ENTER,
5045 : : NULL);
5046 : 256565 : }
5047 : :
5048 : : /*
5049 : : * destroy_colinfo_names_hash: destroy hash table when done with it
5050 : : */
5051 : : static void
5052 : 48695 : destroy_colinfo_names_hash(deparse_columns *colinfo)
5053 : : {
5054 [ + + ]: 48695 : if (colinfo->names_hash)
5055 : : {
5056 : 676 : hash_destroy(colinfo->names_hash);
5057 : 676 : colinfo->names_hash = NULL;
5058 : : }
5059 : 48695 : }
5060 : :
5061 : : /*
5062 : : * identify_join_columns: figure out where columns of a join come from
5063 : : *
5064 : : * Fills the join-specific fields of the colinfo struct, except for
5065 : : * usingNames which is filled later.
5066 : : */
5067 : : static void
4734 5068 : 761 : identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
5069 : : deparse_columns *colinfo)
5070 : : {
5071 : : int numjoincols;
5072 : : int jcolno;
5073 : : int rcolno;
5074 : : ListCell *lc;
5075 : :
5076 : : /* Extract left/right child RT indexes */
5077 [ + + ]: 761 : if (IsA(j->larg, RangeTblRef))
5078 : 483 : colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
5079 [ + - ]: 278 : else if (IsA(j->larg, JoinExpr))
5080 : 278 : colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
5081 : : else
4734 tgl@sss.pgh.pa.us 5082 [ # # ]:UBC 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5083 : : (int) nodeTag(j->larg));
4734 tgl@sss.pgh.pa.us 5084 [ + - ]:CBC 761 : if (IsA(j->rarg, RangeTblRef))
5085 : 761 : colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
4734 tgl@sss.pgh.pa.us 5086 [ # # ]:UBC 0 : else if (IsA(j->rarg, JoinExpr))
5087 : 0 : colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
5088 : : else
5089 [ # # ]: 0 : elog(ERROR, "unrecognized node type in jointree: %d",
5090 : : (int) nodeTag(j->rarg));
5091 : :
5092 : : /* Assert children will be processed earlier than join in second pass */
4734 tgl@sss.pgh.pa.us 5093 [ - + ]:CBC 761 : Assert(colinfo->leftrti < j->rtindex);
5094 [ - + ]: 761 : Assert(colinfo->rightrti < j->rtindex);
5095 : :
5096 : : /* Initialize result arrays with zeroes */
5097 : 761 : numjoincols = list_length(jrte->joinaliasvars);
5098 [ - + ]: 761 : Assert(numjoincols == list_length(jrte->eref->colnames));
5099 : 761 : colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
5100 : 761 : colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
5101 : :
5102 : : /*
5103 : : * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
5104 : : * Recall that the column(s) merged due to USING are the first column(s)
5105 : : * of the join output. We need not do anything special while scanning
5106 : : * joinleftcols, but while scanning joinrightcols we must distinguish
5107 : : * merged from unmerged columns.
5108 : : */
2169 5109 : 761 : jcolno = 0;
5110 [ + - + + : 18645 : foreach(lc, jrte->joinleftcols)
+ + ]
5111 : : {
5112 : 17884 : int leftattno = lfirst_int(lc);
5113 : :
5114 : 17884 : colinfo->leftattnos[jcolno++] = leftattno;
5115 : : }
5116 : 761 : rcolno = 0;
5117 [ + - + + : 8339 : foreach(lc, jrte->joinrightcols)
+ + ]
5118 : : {
5119 : 7578 : int rightattno = lfirst_int(lc);
5120 : :
5121 [ + + ]: 7578 : if (rcolno < jrte->joinmergedcols) /* merged column? */
5122 : 290 : colinfo->rightattnos[rcolno] = rightattno;
5123 : : else
5124 : 7288 : colinfo->rightattnos[jcolno++] = rightattno;
5125 : 7578 : rcolno++;
5126 : : }
5127 [ - + ]: 761 : Assert(jcolno == numjoincols);
4734 5128 : 761 : }
5129 : :
5130 : : /*
5131 : : * get_rtable_name: convenience function to get a previously assigned RTE alias
5132 : : *
5133 : : * The RTE must belong to the topmost namespace level in "context".
5134 : : */
5135 : : static char *
4835 5136 : 3347 : get_rtable_name(int rtindex, deparse_context *context)
5137 : : {
5138 : 3347 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
5139 : :
5140 [ + - - + ]: 3347 : Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
5141 : 3347 : return (char *) list_nth(dpns->rtable_names, rtindex - 1);
5142 : : }
5143 : :
5144 : : /*
5145 : : * set_deparse_plan: set up deparse_namespace to parse subexpressions
5146 : : * of a given Plan node
5147 : : *
5148 : : * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
5149 : : * and index_tlist fields. Caller must already have adjusted the ancestors
5150 : : * list if necessary. Note that the rtable, subplans, and ctes fields do
5151 : : * not need to change when shifting attention to different plan nodes in a
5152 : : * single plan tree.
5153 : : */
5154 : : static void
2198 5155 : 74778 : set_deparse_plan(deparse_namespace *dpns, Plan *plan)
5156 : : {
5157 : 74778 : dpns->plan = plan;
5158 : :
5159 : : /*
5160 : : * We special-case Append and MergeAppend to pretend that the first child
5161 : : * plan is the OUTER referent; we have to interpret OUTER Vars in their
5162 : : * tlists according to one of the children, and the first one is the most
5163 : : * natural choice.
5164 : : */
5165 [ + + ]: 74778 : if (IsA(plan, Append))
5166 : 2247 : dpns->outer_plan = linitial(((Append *) plan)->appendplans);
5167 [ + + ]: 72531 : else if (IsA(plan, MergeAppend))
5168 : 276 : dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
5169 : : else
5170 : 72255 : dpns->outer_plan = outerPlan(plan);
5171 : :
5172 [ + + ]: 74778 : if (dpns->outer_plan)
5173 : 36087 : dpns->outer_tlist = dpns->outer_plan->targetlist;
5174 : : else
5181 5175 : 38691 : dpns->outer_tlist = NIL;
5176 : :
5177 : : /*
5178 : : * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
5179 : : * use OUTER because that could someday conflict with the normal meaning.)
5180 : : * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
5181 : : * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
5182 : : * that as INNER referent.
5183 : : *
5184 : : * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
5185 : : * INNER referent. This is the join from the target relation to the data
5186 : : * source, and all INNER_VAR Vars in other parts of the query refer to its
5187 : : * targetlist.
5188 : : *
5189 : : * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the
5190 : : * excluded expression's tlist. (Similar to the SubqueryScan we don't want
5191 : : * to reuse OUTER, it's used for RETURNING in some modify table cases,
5192 : : * although not INSERT .. CONFLICT).
5193 : : */
2198 5194 [ + + ]: 74778 : if (IsA(plan, SubqueryScan))
5195 : 331 : dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5196 [ + + ]: 74447 : else if (IsA(plan, CteScan))
5197 : 276 : dpns->inner_plan = list_nth(dpns->subplans,
5198 : 276 : ((CteScan *) plan)->ctePlanId - 1);
1553 5199 [ + + ]: 74171 : else if (IsA(plan, WorkTableScan))
5200 : 87 : dpns->inner_plan = find_recursive_union(dpns,
5201 : : (WorkTableScan *) plan);
2198 5202 [ + + ]: 74084 : else if (IsA(plan, ModifyTable))
5203 : : {
1360 alvherre@alvh.no-ip. 5204 [ + + ]: 201 : if (((ModifyTable *) plan)->operation == CMD_MERGE)
640 dean.a.rasheed@gmail 5205 : 30 : dpns->inner_plan = outerPlan(plan);
5206 : : else
5207 : 171 : dpns->inner_plan = plan;
5208 : : }
5209 : : else
5210 : 73883 : dpns->inner_plan = innerPlan(plan);
5211 : :
5212 [ + + + + ]: 74778 : if (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)
5213 : 85 : dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
2198 tgl@sss.pgh.pa.us 5214 [ + + ]: 74693 : else if (dpns->inner_plan)
5215 : 13009 : dpns->inner_tlist = dpns->inner_plan->targetlist;
5216 : : else
5181 5217 : 61684 : dpns->inner_tlist = NIL;
5218 : :
5219 : : /* Set up referent for INDEX_VAR Vars, if needed */
2198 5220 [ + + ]: 74778 : if (IsA(plan, IndexOnlyScan))
5221 : 1742 : dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5222 [ + + ]: 73036 : else if (IsA(plan, ForeignScan))
5223 : 1567 : dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5224 [ - + ]: 71469 : else if (IsA(plan, CustomScan))
2198 tgl@sss.pgh.pa.us 5225 :UBC 0 : dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5226 : : else
5181 tgl@sss.pgh.pa.us 5227 :CBC 71469 : dpns->index_tlist = NIL;
5636 5228 : 74778 : }
5229 : :
5230 : : /*
5231 : : * Locate the ancestor plan node that is the RecursiveUnion generating
5232 : : * the WorkTableScan's work table. We can match on wtParam, since that
5233 : : * should be unique within the plan tree.
5234 : : */
5235 : : static Plan *
1553 5236 : 87 : find_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)
5237 : : {
5238 : : ListCell *lc;
5239 : :
5240 [ + - + - : 219 : foreach(lc, dpns->ancestors)
+ - ]
5241 : : {
5242 : 219 : Plan *ancestor = (Plan *) lfirst(lc);
5243 : :
5244 [ + + ]: 219 : if (IsA(ancestor, RecursiveUnion) &&
5245 [ + - ]: 87 : ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5246 : 87 : return ancestor;
5247 : : }
1553 tgl@sss.pgh.pa.us 5248 [ # # ]:UBC 0 : elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5249 : : wtscan->wtParam);
5250 : : return NULL;
5251 : : }
5252 : :
5253 : : /*
5254 : : * push_child_plan: temporarily transfer deparsing attention to a child plan
5255 : : *
5256 : : * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
5257 : : * deparse context in case the referenced expression itself uses
5258 : : * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
5259 : : * affecting levelsup issues (although in a Plan tree there really shouldn't
5260 : : * be any).
5261 : : *
5262 : : * Caller must provide a local deparse_namespace variable to save the
5263 : : * previous state for pop_child_plan.
5264 : : */
5265 : : static void
2198 tgl@sss.pgh.pa.us 5266 :CBC 43421 : push_child_plan(deparse_namespace *dpns, Plan *plan,
5267 : : deparse_namespace *save_dpns)
5268 : : {
5269 : : /* Save state for restoration later */
5636 5270 : 43421 : *save_dpns = *dpns;
5271 : :
5272 : : /* Link current plan node into ancestors list */
2198 5273 : 43421 : dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5274 : :
5275 : : /* Set attention on selected child */
5276 : 43421 : set_deparse_plan(dpns, plan);
9072 5277 : 43421 : }
5278 : :
5279 : : /*
5280 : : * pop_child_plan: undo the effects of push_child_plan
5281 : : */
5282 : : static void
5636 5283 : 43421 : pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5284 : : {
5285 : : List *ancestors;
5286 : :
5287 : : /* Get rid of ancestors list cell added by push_child_plan */
4857 5288 : 43421 : ancestors = list_delete_first(dpns->ancestors);
5289 : :
5290 : : /* Restore fields changed by push_child_plan */
5636 5291 : 43421 : *dpns = *save_dpns;
5292 : :
5293 : : /* Make sure dpns->ancestors is right (may be unnecessary) */
4857 5294 : 43421 : dpns->ancestors = ancestors;
5636 5295 : 43421 : }
5296 : :
5297 : : /*
5298 : : * push_ancestor_plan: temporarily transfer deparsing attention to an
5299 : : * ancestor plan
5300 : : *
5301 : : * When expanding a Param reference, we must adjust the deparse context
5302 : : * to match the plan node that contains the expression being printed;
5303 : : * otherwise we'd fail if that expression itself contains a Param or
5304 : : * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
5305 : : *
5306 : : * The target ancestor is conveniently identified by the ListCell holding it
5307 : : * in dpns->ancestors.
5308 : : *
5309 : : * Caller must provide a local deparse_namespace variable to save the
5310 : : * previous state for pop_ancestor_plan.
5311 : : */
5312 : : static void
5313 : 2311 : push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
5314 : : deparse_namespace *save_dpns)
5315 : : {
2198 5316 : 2311 : Plan *plan = (Plan *) lfirst(ancestor_cell);
5317 : :
5318 : : /* Save state for restoration later */
5636 5319 : 2311 : *save_dpns = *dpns;
5320 : :
5321 : : /* Build a new ancestor list with just this node's ancestors */
2347 5322 : 2311 : dpns->ancestors =
5323 : 2311 : list_copy_tail(dpns->ancestors,
5324 : 2311 : list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5325 : :
5326 : : /* Set attention on selected ancestor */
2198 5327 : 2311 : set_deparse_plan(dpns, plan);
5636 5328 : 2311 : }
5329 : :
5330 : : /*
5331 : : * pop_ancestor_plan: undo the effects of push_ancestor_plan
5332 : : */
5333 : : static void
5334 : 2311 : pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
5335 : : {
5336 : : /* Free the ancestor list made in push_ancestor_plan */
5337 : 2311 : list_free(dpns->ancestors);
5338 : :
5339 : : /* Restore fields changed by push_ancestor_plan */
5340 : 2311 : *dpns = *save_dpns;
5341 : 2311 : }
5342 : :
5343 : :
5344 : : /* ----------
5345 : : * make_ruledef - reconstruct the CREATE RULE command
5346 : : * for a given pg_rewrite tuple
5347 : : * ----------
5348 : : */
5349 : : static void
8176 5350 : 279 : make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5351 : : int prettyFlags)
5352 : : {
5353 : : char *rulename;
5354 : : char ev_type;
5355 : : Oid ev_class;
5356 : : bool is_instead;
5357 : : char *ev_qual;
5358 : : char *ev_action;
5359 : : List *actions;
5360 : : Relation ev_relation;
3068 5361 : 279 : TupleDesc viewResultDesc = NULL;
5362 : : int fno;
5363 : : Datum dat;
5364 : : bool isnull;
5365 : :
5366 : : /*
5367 : : * Get the attribute values from the rules tuple
5368 : : */
8644 5369 : 279 : fno = SPI_fnumber(rulettc, "rulename");
5370 : 279 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5371 [ - + ]: 279 : Assert(!isnull);
5372 : 279 : rulename = NameStr(*(DatumGetName(dat)));
5373 : :
9969 bruce@momjian.us 5374 : 279 : fno = SPI_fnumber(rulettc, "ev_type");
8644 tgl@sss.pgh.pa.us 5375 : 279 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5376 [ - + ]: 279 : Assert(!isnull);
5377 : 279 : ev_type = DatumGetChar(dat);
5378 : :
9969 bruce@momjian.us 5379 : 279 : fno = SPI_fnumber(rulettc, "ev_class");
8644 tgl@sss.pgh.pa.us 5380 : 279 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5381 [ - + ]: 279 : Assert(!isnull);
5382 : 279 : ev_class = DatumGetObjectId(dat);
5383 : :
9969 bruce@momjian.us 5384 : 279 : fno = SPI_fnumber(rulettc, "is_instead");
8644 tgl@sss.pgh.pa.us 5385 : 279 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5386 [ - + ]: 279 : Assert(!isnull);
5387 : 279 : is_instead = DatumGetBool(dat);
5388 : :
9969 bruce@momjian.us 5389 : 279 : fno = SPI_fnumber(rulettc, "ev_qual");
5390 : 279 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
1871 tgl@sss.pgh.pa.us 5391 [ - + ]: 279 : Assert(ev_qual != NULL);
5392 : :
9969 bruce@momjian.us 5393 : 279 : fno = SPI_fnumber(rulettc, "ev_action");
5394 : 279 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
1871 tgl@sss.pgh.pa.us 5395 [ - + ]: 279 : Assert(ev_action != NULL);
5396 : 279 : actions = (List *) stringToNode(ev_action);
5397 [ - + ]: 279 : if (actions == NIL)
1871 tgl@sss.pgh.pa.us 5398 [ # # ]:UBC 0 : elog(ERROR, "invalid empty ev_action list");
5399 : :
2522 andres@anarazel.de 5400 :CBC 279 : ev_relation = table_open(ev_class, AccessShareLock);
5401 : :
5402 : : /*
5403 : : * Build the rules definition text
5404 : : */
8176 tgl@sss.pgh.pa.us 5405 : 279 : appendStringInfo(buf, "CREATE RULE %s AS",
5406 : : quote_identifier(rulename));
5407 : :
5408 [ + - ]: 279 : if (prettyFlags & PRETTYFLAG_INDENT)
8171 bruce@momjian.us 5409 : 279 : appendStringInfoString(buf, "\n ON ");
5410 : : else
8171 bruce@momjian.us 5411 :UBC 0 : appendStringInfoString(buf, " ON ");
5412 : :
5413 : : /* The event the rule is fired for */
9969 bruce@momjian.us 5414 [ + + + + :CBC 279 : switch (ev_type)
- ]
5415 : : {
5416 : 3 : case '1':
4430 rhaas@postgresql.org 5417 : 3 : appendStringInfoString(buf, "SELECT");
3068 tgl@sss.pgh.pa.us 5418 : 3 : viewResultDesc = RelationGetDescr(ev_relation);
9977 bruce@momjian.us 5419 : 3 : break;
5420 : :
9969 5421 : 77 : case '2':
4430 rhaas@postgresql.org 5422 : 77 : appendStringInfoString(buf, "UPDATE");
9977 bruce@momjian.us 5423 : 77 : break;
5424 : :
9969 5425 : 147 : case '3':
4430 rhaas@postgresql.org 5426 : 147 : appendStringInfoString(buf, "INSERT");
9977 bruce@momjian.us 5427 : 147 : break;
5428 : :
9969 5429 : 52 : case '4':
4430 rhaas@postgresql.org 5430 : 52 : appendStringInfoString(buf, "DELETE");
9977 bruce@momjian.us 5431 : 52 : break;
5432 : :
9969 bruce@momjian.us 5433 :UBC 0 : default:
8179 tgl@sss.pgh.pa.us 5434 [ # # ]: 0 : ereport(ERROR,
5435 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5436 : : errmsg("rule \"%s\" has unsupported event type %d",
5437 : : rulename, ev_type)));
5438 : : break;
5439 : : }
5440 : :
5441 : : /* The relation the rule is fired on */
2851 tgl@sss.pgh.pa.us 5442 :CBC 279 : appendStringInfo(buf, " TO %s",
5443 [ + + ]: 279 : (prettyFlags & PRETTYFLAG_SCHEMA) ?
5444 : 57 : generate_relation_name(ev_class, NIL) :
5445 : 222 : generate_qualified_relation_name(ev_class));
5446 : :
5447 : : /* If the rule has an event qualification, add it */
1871 5448 [ + + ]: 279 : if (strcmp(ev_qual, "<>") != 0)
5449 : : {
5450 : : Node *qual;
5451 : : Query *query;
5452 : : deparse_context context;
5453 : : deparse_namespace dpns;
5454 : :
8176 5455 [ + - ]: 61 : if (prettyFlags & PRETTYFLAG_INDENT)
8171 bruce@momjian.us 5456 : 61 : appendStringInfoString(buf, "\n ");
4430 rhaas@postgresql.org 5457 : 61 : appendStringInfoString(buf, " WHERE ");
5458 : :
9969 bruce@momjian.us 5459 : 61 : qual = stringToNode(ev_qual);
5460 : :
5461 : : /*
5462 : : * We need to make a context for recognizing any Vars in the qual
5463 : : * (which can only be references to OLD and NEW). Use the rtable of
5464 : : * the first query in the action list for this purpose.
5465 : : */
7875 neilc@samurai.com 5466 : 61 : query = (Query *) linitial(actions);
5467 : :
5468 : : /*
5469 : : * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5470 : : * into the SELECT, and that's what we need to look at. (Ugly kluge
5471 : : * ... try to fix this when we redesign querytrees.)
5472 : : */
8787 tgl@sss.pgh.pa.us 5473 : 61 : query = getInsertSelectQuery(query, NULL);
5474 : :
5475 : : /* Must acquire locks right away; see notes in get_query_def() */
4304 5476 : 61 : AcquireRewriteLocks(query, false, false);
5477 : :
9572 5478 : 61 : context.buf = buf;
7871 neilc@samurai.com 5479 : 61 : context.namespaces = list_make1(&dpns);
475 tgl@sss.pgh.pa.us 5480 : 61 : context.resultDesc = NULL;
5481 : 61 : context.targetList = NIL;
6198 5482 : 61 : context.windowClause = NIL;
7871 neilc@samurai.com 5483 : 61 : context.varprefix = (list_length(query->rtable) != 1);
8176 tgl@sss.pgh.pa.us 5484 : 61 : context.prettyFlags = prettyFlags;
4741 5485 : 61 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
8176 5486 : 61 : context.indentLevel = PRETTYINDENT_STD;
475 5487 : 61 : context.colNamesVisible = true;
5488 : 61 : context.inGroupBy = false;
5489 : 61 : context.varInOrderBy = false;
2198 5490 : 61 : context.appendparents = NULL;
5491 : :
4734 5492 : 61 : set_deparse_for_query(&dpns, query, NIL);
5493 : :
8490 5494 : 61 : get_rule_expr(qual, &context, false);
5495 : : }
5496 : :
4430 rhaas@postgresql.org 5497 : 279 : appendStringInfoString(buf, " DO ");
5498 : :
5499 : : /* The INSTEAD keyword (if so) */
9969 bruce@momjian.us 5500 [ + + ]: 279 : if (is_instead)
4430 rhaas@postgresql.org 5501 : 165 : appendStringInfoString(buf, "INSTEAD ");
5502 : :
5503 : : /* Finally the rules actions */
7871 neilc@samurai.com 5504 [ + + ]: 279 : if (list_length(actions) > 1)
5505 : : {
5506 : : ListCell *action;
5507 : : Query *query;
5508 : :
4430 rhaas@postgresql.org 5509 : 10 : appendStringInfoChar(buf, '(');
9969 bruce@momjian.us 5510 [ + - + + : 30 : foreach(action, actions)
+ + ]
5511 : : {
5512 : 20 : query = (Query *) lfirst(action);
1306 tgl@sss.pgh.pa.us 5513 : 20 : get_query_def(query, buf, NIL, viewResultDesc, true,
5514 : : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
8176 5515 [ + - ]: 20 : if (prettyFlags)
4430 rhaas@postgresql.org 5516 : 20 : appendStringInfoString(buf, ";\n");
5517 : : else
4430 rhaas@postgresql.org 5518 :UBC 0 : appendStringInfoString(buf, "; ");
5519 : : }
4430 rhaas@postgresql.org 5520 :CBC 10 : appendStringInfoString(buf, ");");
5521 : : }
5522 : : else
5523 : : {
5524 : : Query *query;
5525 : :
7875 neilc@samurai.com 5526 : 269 : query = (Query *) linitial(actions);
1306 tgl@sss.pgh.pa.us 5527 : 269 : get_query_def(query, buf, NIL, viewResultDesc, true,
5528 : : prettyFlags, WRAP_COLUMN_DEFAULT, 0);
4430 rhaas@postgresql.org 5529 : 269 : appendStringInfoChar(buf, ';');
5530 : : }
5531 : :
2522 andres@anarazel.de 5532 : 279 : table_close(ev_relation, AccessShareLock);
9977 bruce@momjian.us 5533 : 279 : }
5534 : :
5535 : :
5536 : : /* ----------
5537 : : * make_viewdef - reconstruct the SELECT part of a
5538 : : * view rewrite rule
5539 : : * ----------
5540 : : */
5541 : : static void
8176 tgl@sss.pgh.pa.us 5542 : 1759 : make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
5543 : : int prettyFlags, int wrapColumn)
5544 : : {
5545 : : Query *query;
5546 : : char ev_type;
5547 : : Oid ev_class;
5548 : : bool is_instead;
5549 : : char *ev_qual;
5550 : : char *ev_action;
5551 : : List *actions;
5552 : : Relation ev_relation;
5553 : : int fno;
5554 : : Datum dat;
5555 : : bool isnull;
5556 : :
5557 : : /*
5558 : : * Get the attribute values from the rules tuple
5559 : : */
9969 bruce@momjian.us 5560 : 1759 : fno = SPI_fnumber(rulettc, "ev_type");
3068 tgl@sss.pgh.pa.us 5561 : 1759 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5562 [ - + ]: 1759 : Assert(!isnull);
5563 : 1759 : ev_type = DatumGetChar(dat);
5564 : :
9969 bruce@momjian.us 5565 : 1759 : fno = SPI_fnumber(rulettc, "ev_class");
3068 tgl@sss.pgh.pa.us 5566 : 1759 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5567 [ - + ]: 1759 : Assert(!isnull);
5568 : 1759 : ev_class = DatumGetObjectId(dat);
5569 : :
9969 bruce@momjian.us 5570 : 1759 : fno = SPI_fnumber(rulettc, "is_instead");
3068 tgl@sss.pgh.pa.us 5571 : 1759 : dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5572 [ - + ]: 1759 : Assert(!isnull);
5573 : 1759 : is_instead = DatumGetBool(dat);
5574 : :
9969 bruce@momjian.us 5575 : 1759 : fno = SPI_fnumber(rulettc, "ev_qual");
5576 : 1759 : ev_qual = SPI_getvalue(ruletup, rulettc, fno);
1871 tgl@sss.pgh.pa.us 5577 [ - + ]: 1759 : Assert(ev_qual != NULL);
5578 : :
9969 bruce@momjian.us 5579 : 1759 : fno = SPI_fnumber(rulettc, "ev_action");
5580 : 1759 : ev_action = SPI_getvalue(ruletup, rulettc, fno);
1871 tgl@sss.pgh.pa.us 5581 [ - + ]: 1759 : Assert(ev_action != NULL);
5582 : 1759 : actions = (List *) stringToNode(ev_action);
5583 : :
7871 neilc@samurai.com 5584 [ - + ]: 1759 : if (list_length(actions) != 1)
5585 : : {
5586 : : /* keep output buffer empty and leave */
9573 tgl@sss.pgh.pa.us 5587 :UBC 0 : return;
5588 : : }
5589 : :
7875 neilc@samurai.com 5590 :CBC 1759 : query = (Query *) linitial(actions);
5591 : :
4486 kgrittn@postgresql.o 5592 [ + - + - ]: 1759 : if (ev_type != '1' || !is_instead ||
8532 tgl@sss.pgh.pa.us 5593 [ + - - + ]: 1759 : strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5594 : : {
5595 : : /* keep output buffer empty and leave */
9573 tgl@sss.pgh.pa.us 5596 :UBC 0 : return;
5597 : : }
5598 : :
2522 andres@anarazel.de 5599 :CBC 1759 : ev_relation = table_open(ev_class, AccessShareLock);
5600 : :
1306 tgl@sss.pgh.pa.us 5601 : 1759 : get_query_def(query, buf, NIL, RelationGetDescr(ev_relation), true,
5602 : : prettyFlags, wrapColumn, 0);
4430 rhaas@postgresql.org 5603 : 1759 : appendStringInfoChar(buf, ';');
5604 : :
2522 andres@anarazel.de 5605 : 1759 : table_close(ev_relation, AccessShareLock);
5606 : : }
5607 : :
5608 : :
5609 : : /* ----------
5610 : : * get_query_def - Parse back one query parsetree
5611 : : *
5612 : : * query: parsetree to be displayed
5613 : : * buf: output text is appended to buf
5614 : : * parentnamespace: list (initially empty) of outer-level deparse_namespace's
5615 : : * resultDesc: if not NULL, the output tuple descriptor for the view
5616 : : * represented by a SELECT query. We use the column names from it
5617 : : * to label SELECT output columns, in preference to names in the query
5618 : : * colNamesVisible: true if the surrounding context cares about the output
5619 : : * column names at all (as, for example, an EXISTS() context does not);
5620 : : * when false, we can suppress dummy column labels such as "?column?"
5621 : : * prettyFlags: bitmask of PRETTYFLAG_XXX options
5622 : : * wrapColumn: maximum line length, or -1 to disable wrapping
5623 : : * startIndent: initial indentation amount
5624 : : * ----------
5625 : : */
5626 : : static void
8532 tgl@sss.pgh.pa.us 5627 : 2852 : get_query_def(Query *query, StringInfo buf, List *parentnamespace,
5628 : : TupleDesc resultDesc, bool colNamesVisible,
5629 : : int prettyFlags, int wrapColumn, int startIndent)
5630 : : {
5631 : : deparse_context context;
5632 : : deparse_namespace dpns;
5633 : : int rtable_size;
5634 : :
5635 : : /* Guard against excessively long or deeply-nested queries */
4249 5636 [ - + ]: 2852 : CHECK_FOR_INTERRUPTS();
5637 : 2852 : check_stack_depth();
5638 : :
463 rguo@postgresql.org 5639 : 5704 : rtable_size = query->hasGroupRTE ?
5640 [ + + ]: 2852 : list_length(query->rtable) - 1 :
5641 : 2742 : list_length(query->rtable);
5642 : :
5643 : : /*
5644 : : * Replace any Vars in the query's targetlist and havingQual that
5645 : : * reference GROUP outputs with the underlying grouping expressions.
5646 : : */
5647 [ + + ]: 2852 : if (query->hasGroupRTE)
5648 : : {
5649 : 110 : query->targetList = (List *)
5650 : 110 : flatten_group_exprs(NULL, query, (Node *) query->targetList);
5651 : 110 : query->havingQual =
5652 : 110 : flatten_group_exprs(NULL, query, query->havingQual);
5653 : : }
5654 : :
5655 : : /*
5656 : : * Before we begin to examine the query, acquire locks on referenced
5657 : : * relations, and fix up deleted columns in JOIN RTEs. This ensures
5658 : : * consistent results. Note we assume it's OK to scribble on the passed
5659 : : * querytree!
5660 : : *
5661 : : * We are only deparsing the query (we are not about to execute it), so we
5662 : : * only need AccessShareLock on the relations it mentions.
5663 : : */
4304 tgl@sss.pgh.pa.us 5664 : 2852 : AcquireRewriteLocks(query, false, false);
5665 : :
9572 5666 : 2852 : context.buf = buf;
7875 neilc@samurai.com 5667 : 2852 : context.namespaces = lcons(&dpns, list_copy(parentnamespace));
475 tgl@sss.pgh.pa.us 5668 : 2852 : context.resultDesc = NULL;
5669 : 2852 : context.targetList = NIL;
6198 5670 : 2852 : context.windowClause = NIL;
9072 5671 [ + + + + ]: 2852 : context.varprefix = (parentnamespace != NIL ||
5672 : : rtable_size != 1);
8176 5673 : 2852 : context.prettyFlags = prettyFlags;
4741 5674 : 2852 : context.wrapColumn = wrapColumn;
8176 5675 : 2852 : context.indentLevel = startIndent;
475 5676 : 2852 : context.colNamesVisible = colNamesVisible;
5677 : 2852 : context.inGroupBy = false;
5678 : 2852 : context.varInOrderBy = false;
2198 5679 : 2852 : context.appendparents = NULL;
5680 : :
4734 5681 : 2852 : set_deparse_for_query(&dpns, query, parentnamespace);
5682 : :
9969 bruce@momjian.us 5683 [ + + + + : 2852 : switch (query->commandType)
+ + + - ]
5684 : : {
9703 5685 : 2532 : case CMD_SELECT:
5686 : : /* We set context.resultDesc only if it's a SELECT */
475 tgl@sss.pgh.pa.us 5687 : 2532 : context.resultDesc = resultDesc;
5688 : 2532 : get_select_query_def(query, &context);
9969 bruce@momjian.us 5689 : 2532 : break;
5690 : :
5691 : 77 : case CMD_UPDATE:
475 tgl@sss.pgh.pa.us 5692 : 77 : get_update_query_def(query, &context);
9969 bruce@momjian.us 5693 : 77 : break;
5694 : :
5695 : 170 : case CMD_INSERT:
475 tgl@sss.pgh.pa.us 5696 : 170 : get_insert_query_def(query, &context);
9969 bruce@momjian.us 5697 : 170 : break;
5698 : :
5699 : 38 : case CMD_DELETE:
475 tgl@sss.pgh.pa.us 5700 : 38 : get_delete_query_def(query, &context);
9969 bruce@momjian.us 5701 : 38 : break;
5702 : :
955 tgl@sss.pgh.pa.us 5703 : 6 : case CMD_MERGE:
475 5704 : 6 : get_merge_query_def(query, &context);
955 5705 : 6 : break;
5706 : :
9969 bruce@momjian.us 5707 : 21 : case CMD_NOTHING:
4430 rhaas@postgresql.org 5708 : 21 : appendStringInfoString(buf, "NOTHING");
9969 bruce@momjian.us 5709 : 21 : break;
5710 : :
9114 tgl@sss.pgh.pa.us 5711 : 8 : case CMD_UTILITY:
5712 : 8 : get_utility_query_def(query, &context);
5713 : 8 : break;
5714 : :
9969 bruce@momjian.us 5715 :UBC 0 : default:
8179 tgl@sss.pgh.pa.us 5716 [ # # ]: 0 : elog(ERROR, "unrecognized query command type: %d",
5717 : : query->commandType);
5718 : : break;
5719 : : }
9977 bruce@momjian.us 5720 :CBC 2852 : }
5721 : :
5722 : : /* ----------
5723 : : * get_values_def - Parse back a VALUES list
5724 : : * ----------
5725 : : */
5726 : : static void
7077 mail@joeconway.com 5727 : 136 : get_values_def(List *values_lists, deparse_context *context)
5728 : : {
5729 : 136 : StringInfo buf = context->buf;
5730 : 136 : bool first_list = true;
5731 : : ListCell *vtl;
5732 : :
5733 : 136 : appendStringInfoString(buf, "VALUES ");
5734 : :
5735 [ + - + + : 389 : foreach(vtl, values_lists)
+ + ]
5736 : : {
5737 : 253 : List *sublist = (List *) lfirst(vtl);
5738 : 253 : bool first_col = true;
5739 : : ListCell *lc;
5740 : :
5741 [ + + ]: 253 : if (first_list)
5742 : 136 : first_list = false;
5743 : : else
5744 : 117 : appendStringInfoString(buf, ", ");
5745 : :
5746 : 253 : appendStringInfoChar(buf, '(');
5747 [ + - + + : 979 : foreach(lc, sublist)
+ + ]
5748 : : {
7014 bruce@momjian.us 5749 : 726 : Node *col = (Node *) lfirst(lc);
5750 : :
7077 mail@joeconway.com 5751 [ + + ]: 726 : if (first_col)
5752 : 253 : first_col = false;
5753 : : else
5754 : 473 : appendStringInfoChar(buf, ',');
5755 : :
5756 : : /*
5757 : : * Print the value. Whole-row Vars need special treatment.
5758 : : */
3423 tgl@sss.pgh.pa.us 5759 : 726 : get_rule_expr_toplevel(col, context, false);
5760 : : }
7077 mail@joeconway.com 5761 : 253 : appendStringInfoChar(buf, ')');
5762 : : }
5763 : 136 : }
5764 : :
5765 : : /* ----------
5766 : : * get_with_clause - Parse back a WITH clause
5767 : : * ----------
5768 : : */
5769 : : static void
6283 tgl@sss.pgh.pa.us 5770 : 2823 : get_with_clause(Query *query, deparse_context *context)
5771 : : {
5772 : 2823 : StringInfo buf = context->buf;
5773 : : const char *sep;
5774 : : ListCell *l;
5775 : :
5776 [ + + ]: 2823 : if (query->cteList == NIL)
5777 : 2775 : return;
5778 : :
5779 [ + - ]: 48 : if (PRETTY_INDENT(context))
5780 : : {
5781 : 48 : context->indentLevel += PRETTYINDENT_STD;
5782 : 48 : appendStringInfoChar(buf, ' ');
5783 : : }
5784 : :
5785 [ + + ]: 48 : if (query->hasRecursive)
5786 : 28 : sep = "WITH RECURSIVE ";
5787 : : else
5788 : 20 : sep = "WITH ";
5789 [ + - + + : 121 : foreach(l, query->cteList)
+ + ]
5790 : : {
5791 : 73 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
5792 : :
5793 : 73 : appendStringInfoString(buf, sep);
5794 : 73 : appendStringInfoString(buf, quote_identifier(cte->ctename));
5795 [ + + ]: 73 : if (cte->aliascolnames)
5796 : : {
5797 : 28 : bool first = true;
5798 : : ListCell *col;
5799 : :
5800 : 28 : appendStringInfoChar(buf, '(');
5801 [ + - + + : 74 : foreach(col, cte->aliascolnames)
+ + ]
5802 : : {
5803 [ + + ]: 46 : if (first)
5804 : 28 : first = false;
5805 : : else
5806 : 18 : appendStringInfoString(buf, ", ");
5807 : 46 : appendStringInfoString(buf,
5808 : 46 : quote_identifier(strVal(lfirst(col))));
5809 : : }
5810 : 28 : appendStringInfoChar(buf, ')');
5811 : : }
2496 5812 : 73 : appendStringInfoString(buf, " AS ");
5813 [ + + - - ]: 73 : switch (cte->ctematerialized)
5814 : : {
5815 : 64 : case CTEMaterializeDefault:
5816 : 64 : break;
5817 : 9 : case CTEMaterializeAlways:
5818 : 9 : appendStringInfoString(buf, "MATERIALIZED ");
5819 : 9 : break;
2496 tgl@sss.pgh.pa.us 5820 :UBC 0 : case CTEMaterializeNever:
5821 : 0 : appendStringInfoString(buf, "NOT MATERIALIZED ");
5822 : 0 : break;
5823 : : }
2496 tgl@sss.pgh.pa.us 5824 :CBC 73 : appendStringInfoChar(buf, '(');
6283 5825 [ + - ]: 73 : if (PRETTY_INDENT(context))
5826 : 73 : appendContextKeyword(context, "", 0, 0, 0);
5827 : 73 : get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
5828 : : true,
5829 : : context->prettyFlags, context->wrapColumn,
5830 : : context->indentLevel);
5831 [ + - ]: 73 : if (PRETTY_INDENT(context))
5832 : 73 : appendContextKeyword(context, "", 0, 0, 0);
5833 : 73 : appendStringInfoChar(buf, ')');
5834 : :
1780 peter@eisentraut.org 5835 [ + + ]: 73 : if (cte->search_clause)
5836 : : {
5837 : 3 : bool first = true;
5838 : : ListCell *lc;
5839 : :
5840 : 3 : appendStringInfo(buf, " SEARCH %s FIRST BY ",
5841 [ - + ]: 3 : cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
5842 : :
5843 [ + - + + : 9 : foreach(lc, cte->search_clause->search_col_list)
+ + ]
5844 : : {
5845 [ + + ]: 6 : if (first)
5846 : 3 : first = false;
5847 : : else
5848 : 3 : appendStringInfoString(buf, ", ");
5849 : 6 : appendStringInfoString(buf,
5850 : 6 : quote_identifier(strVal(lfirst(lc))));
5851 : : }
5852 : :
5853 : 3 : appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
5854 : : }
5855 : :
5856 [ + + ]: 73 : if (cte->cycle_clause)
5857 : : {
5858 : 6 : bool first = true;
5859 : : ListCell *lc;
5860 : :
5861 : 6 : appendStringInfoString(buf, " CYCLE ");
5862 : :
5863 [ + - + + : 18 : foreach(lc, cte->cycle_clause->cycle_col_list)
+ + ]
5864 : : {
5865 [ + + ]: 12 : if (first)
5866 : 6 : first = false;
5867 : : else
5868 : 6 : appendStringInfoString(buf, ", ");
5869 : 12 : appendStringInfoString(buf,
5870 : 12 : quote_identifier(strVal(lfirst(lc))));
5871 : : }
5872 : :
5873 : 6 : appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
5874 : :
5875 : : {
1754 5876 : 6 : Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
5877 : 6 : Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
5878 : :
5879 [ + + + - : 9 : if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
+ - - + ]
5880 [ + - + - ]: 3 : cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
5881 : : {
5882 : 3 : appendStringInfoString(buf, " TO ");
5883 : 3 : get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
5884 : 3 : appendStringInfoString(buf, " DEFAULT ");
5885 : 3 : get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
5886 : : }
5887 : : }
5888 : :
1780 5889 : 6 : appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
5890 : : }
5891 : :
6283 tgl@sss.pgh.pa.us 5892 : 73 : sep = ", ";
5893 : : }
5894 : :
5895 [ + - ]: 48 : if (PRETTY_INDENT(context))
5896 : : {
5897 : 48 : context->indentLevel -= PRETTYINDENT_STD;
5898 : 48 : appendContextKeyword(context, "", 0, 0, 0);
5899 : : }
5900 : : else
6283 tgl@sss.pgh.pa.us 5901 :UBC 0 : appendStringInfoChar(buf, ' ');
5902 : : }
5903 : :
5904 : : /* ----------
5905 : : * get_select_query_def - Parse back a SELECT parsetree
5906 : : * ----------
5907 : : */
5908 : : static void
475 tgl@sss.pgh.pa.us 5909 :CBC 2532 : get_select_query_def(Query *query, deparse_context *context)
5910 : : {
9204 5911 : 2532 : StringInfo buf = context->buf;
5912 : : bool force_colno;
5913 : : ListCell *l;
5914 : :
5915 : : /* Insert the WITH clause if given */
6283 5916 : 2532 : get_with_clause(query, context);
5917 : :
5918 : : /* Subroutines may need to consult the SELECT targetlist and windowClause */
475 5919 : 2532 : context->targetList = query->targetList;
6198 5920 : 2532 : context->windowClause = query->windowClause;
5921 : :
5922 : : /*
5923 : : * If the Query node has a setOperations tree, then it's the top level of
5924 : : * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
5925 : : * fields are interesting in the top query itself.
5926 : : */
9204 5927 [ + + ]: 2532 : if (query->setOperations)
5928 : : {
475 5929 : 82 : get_setop_query(query->setOperations, query, context);
5930 : : /* ORDER BY clauses must be simple in this case */
9012 5931 : 82 : force_colno = true;
5932 : : }
5933 : : else
5934 : : {
475 5935 : 2450 : get_basic_select_query(query, context);
9012 5936 : 2450 : force_colno = false;
5937 : : }
5938 : :
5939 : : /* Add the ORDER BY clause if given */
9204 5940 [ + + ]: 2532 : if (query->sortClause != NIL)
5941 : : {
8171 bruce@momjian.us 5942 : 90 : appendContextKeyword(context, " ORDER BY ",
5943 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6198 tgl@sss.pgh.pa.us 5944 : 90 : get_rule_orderby(query->sortClause, query->targetList,
5945 : : force_colno, context);
5946 : : }
5947 : :
5948 : : /*
5949 : : * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
5950 : : * standard spelling of LIMIT.
5951 : : */
9183 5952 [ + + ]: 2532 : if (query->limitOffset != NULL)
5953 : : {
8176 5954 : 16 : appendContextKeyword(context, " OFFSET ",
5955 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
8490 5956 : 16 : get_rule_expr(query->limitOffset, context, false);
5957 : : }
9183 5958 [ + + ]: 2532 : if (query->limitCount != NULL)
5959 : : {
2080 alvherre@alvh.no-ip. 5960 [ + + ]: 43 : if (query->limitOption == LIMIT_OPTION_WITH_TIES)
5961 : : {
5962 : : /*
5963 : : * The limitCount arg is a c_expr, so it needs parens. Simple
5964 : : * literals and function expressions would not need parens, but
5965 : : * unfortunately it's hard to tell if the expression will be
5966 : : * printed as a simple literal like 123 or as a typecast
5967 : : * expression, like '-123'::int4. The grammar accepts the former
5968 : : * without quoting, but not the latter.
5969 : : */
5970 : 24 : appendContextKeyword(context, " FETCH FIRST ",
5971 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
212 heikki.linnakangas@i 5972 : 24 : appendStringInfoChar(buf, '(');
8490 tgl@sss.pgh.pa.us 5973 : 24 : get_rule_expr(query->limitCount, context, false);
212 heikki.linnakangas@i 5974 : 24 : appendStringInfoChar(buf, ')');
1889 drowley@postgresql.o 5975 : 24 : appendStringInfoString(buf, " ROWS WITH TIES");
5976 : : }
5977 : : else
5978 : : {
2080 alvherre@alvh.no-ip. 5979 : 19 : appendContextKeyword(context, " LIMIT ",
5980 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
5981 [ + + ]: 19 : if (IsA(query->limitCount, Const) &&
5982 [ + - ]: 8 : ((Const *) query->limitCount)->constisnull)
5983 : 8 : appendStringInfoString(buf, "ALL");
5984 : : else
5985 : 11 : get_rule_expr(query->limitCount, context, false);
5986 : : }
5987 : : }
5988 : :
5989 : : /* Add FOR [KEY] UPDATE/SHARE clauses if present */
5894 tgl@sss.pgh.pa.us 5990 [ + + ]: 2532 : if (query->hasForUpdate)
5991 : : {
5992 [ + - + + : 6 : foreach(l, query->rowMarks)
+ + ]
5993 : : {
5994 : 3 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
5995 : :
5996 : : /* don't print implicit clauses */
5997 [ - + ]: 3 : if (rc->pushedDown)
5894 tgl@sss.pgh.pa.us 5998 :UBC 0 : continue;
5999 : :
4711 alvherre@alvh.no-ip. 6000 [ - - - - :CBC 3 : switch (rc->strength)
+ - ]
6001 : : {
3930 tgl@sss.pgh.pa.us 6002 :UBC 0 : case LCS_NONE:
6003 : : /* we intentionally throw an error for LCS_NONE */
6004 [ # # ]: 0 : elog(ERROR, "unrecognized LockClauseStrength %d",
6005 : : (int) rc->strength);
6006 : : break;
4711 alvherre@alvh.no-ip. 6007 : 0 : case LCS_FORKEYSHARE:
6008 : 0 : appendContextKeyword(context, " FOR KEY SHARE",
6009 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6010 : 0 : break;
6011 : 0 : case LCS_FORSHARE:
6012 : 0 : appendContextKeyword(context, " FOR SHARE",
6013 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6014 : 0 : break;
6015 : 0 : case LCS_FORNOKEYUPDATE:
6016 : 0 : appendContextKeyword(context, " FOR NO KEY UPDATE",
6017 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6018 : 0 : break;
4711 alvherre@alvh.no-ip. 6019 :CBC 3 : case LCS_FORUPDATE:
6020 : 3 : appendContextKeyword(context, " FOR UPDATE",
6021 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6022 : 3 : break;
6023 : : }
6024 : :
5894 tgl@sss.pgh.pa.us 6025 : 3 : appendStringInfo(buf, " OF %s",
4835 6026 : 3 : quote_identifier(get_rtable_name(rc->rti,
6027 : : context)));
4089 alvherre@alvh.no-ip. 6028 [ - + ]: 3 : if (rc->waitPolicy == LockWaitError)
4430 rhaas@postgresql.org 6029 :UBC 0 : appendStringInfoString(buf, " NOWAIT");
4089 alvherre@alvh.no-ip. 6030 [ - + ]:CBC 3 : else if (rc->waitPolicy == LockWaitSkip)
4089 alvherre@alvh.no-ip. 6031 :UBC 0 : appendStringInfoString(buf, " SKIP LOCKED");
6032 : : }
6033 : : }
9204 tgl@sss.pgh.pa.us 6034 :CBC 2532 : }
6035 : :
6036 : : /*
6037 : : * Detect whether query looks like SELECT ... FROM VALUES(),
6038 : : * with no need to rename the output columns of the VALUES RTE.
6039 : : * If so, return the VALUES RTE. Otherwise return NULL.
6040 : : */
6041 : : static RangeTblEntry *
2223 6042 : 2450 : get_simple_values_rte(Query *query, TupleDesc resultDesc)
6043 : : {
4868 6044 : 2450 : RangeTblEntry *result = NULL;
6045 : : ListCell *lc;
6046 : :
6047 : : /*
6048 : : * We want to detect a match even if the Query also contains OLD or NEW
6049 : : * rule RTEs. So the idea is to scan the rtable and see if there is only
6050 : : * one inFromCl RTE that is a VALUES RTE.
6051 : : */
6052 [ + + + + : 2636 : foreach(lc, query->rtable)
+ + ]
6053 : : {
6054 : 2227 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
6055 : :
6056 [ + + + - ]: 2227 : if (rte->rtekind == RTE_VALUES && rte->inFromCl)
6057 : : {
6058 [ - + ]: 114 : if (result)
6059 : 2041 : return NULL; /* multiple VALUES (probably not possible) */
6060 : 114 : result = rte;
6061 : : }
6062 [ + + + + ]: 2113 : else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
6063 : 72 : continue; /* ignore rule entries */
6064 : : else
6065 : 2041 : return NULL; /* something else -> not simple VALUES */
6066 : : }
6067 : :
6068 : : /*
6069 : : * We don't need to check the targetlist in any great detail, because
6070 : : * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
6071 : : * appear inside auto-generated sub-queries with very restricted
6072 : : * structure. However, DefineView might have modified the tlist by
6073 : : * injecting new column aliases, or we might have some other column
6074 : : * aliases forced by a resultDesc. We can only simplify if the RTE's
6075 : : * column names match the names that get_target_list() would select.
6076 : : */
3948 6077 [ + + ]: 409 : if (result)
6078 : : {
6079 : : ListCell *lcn;
6080 : : int colno;
6081 : :
6082 [ - + ]: 114 : if (list_length(query->targetList) != list_length(result->eref->colnames))
3948 tgl@sss.pgh.pa.us 6083 :UBC 0 : return NULL; /* this probably cannot happen */
2223 tgl@sss.pgh.pa.us 6084 :CBC 114 : colno = 0;
3948 6085 [ + - + + : 421 : forboth(lc, query->targetList, lcn, result->eref->colnames)
+ - + + +
+ + - +
+ ]
6086 : : {
6087 : 313 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
6088 : 313 : char *cname = strVal(lfirst(lcn));
6089 : : char *colname;
6090 : :
6091 [ - + ]: 313 : if (tle->resjunk)
6092 : 6 : return NULL; /* this probably cannot happen */
6093 : :
6094 : : /* compute name that get_target_list would use for column */
2223 6095 : 313 : colno++;
6096 [ + + + - ]: 313 : if (resultDesc && colno <= resultDesc->natts)
6097 : 15 : colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6098 : : else
6099 : 298 : colname = tle->resname;
6100 : :
6101 : : /* does it match the VALUES RTE? */
6102 [ + - + + ]: 313 : if (colname == NULL || strcmp(colname, cname) != 0)
3948 6103 : 6 : return NULL; /* column name has been changed */
6104 : : }
6105 : : }
6106 : :
4868 6107 : 403 : return result;
6108 : : }
6109 : :
6110 : : static void
475 6111 : 2450 : get_basic_select_query(Query *query, deparse_context *context)
6112 : : {
9572 6113 : 2450 : StringInfo buf = context->buf;
6114 : : RangeTblEntry *values_rte;
6115 : : char *sep;
6116 : : ListCell *l;
6117 : :
8176 6118 [ + + ]: 2450 : if (PRETTY_INDENT(context))
6119 : : {
8171 bruce@momjian.us 6120 : 2427 : context->indentLevel += PRETTYINDENT_STD;
6121 : 2427 : appendStringInfoChar(buf, ' ');
6122 : : }
6123 : :
6124 : : /*
6125 : : * If the query looks like SELECT * FROM (VALUES ...), then print just the
6126 : : * VALUES part. This reverses what transformValuesClause() did at parse
6127 : : * time.
6128 : : */
475 tgl@sss.pgh.pa.us 6129 : 2450 : values_rte = get_simple_values_rte(query, context->resultDesc);
4868 6130 [ + + ]: 2450 : if (values_rte)
6131 : : {
6132 : 108 : get_values_def(values_rte->values_lists, context);
6133 : 108 : return;
6134 : : }
6135 : :
6136 : : /*
6137 : : * Build up the query string - first we say SELECT
6138 : : */
1715 peter@eisentraut.org 6139 [ + + ]: 2342 : if (query->isReturn)
6140 : 26 : appendStringInfoString(buf, "RETURN");
6141 : : else
6142 : 2316 : appendStringInfoString(buf, "SELECT");
6143 : :
6144 : : /* Add the DISTINCT clause if given */
9204 tgl@sss.pgh.pa.us 6145 [ - + ]: 2342 : if (query->distinctClause != NIL)
6146 : : {
6346 tgl@sss.pgh.pa.us 6147 [ # # ]:UBC 0 : if (query->hasDistinctOn)
6148 : : {
4430 rhaas@postgresql.org 6149 : 0 : appendStringInfoString(buf, " DISTINCT ON (");
9204 tgl@sss.pgh.pa.us 6150 : 0 : sep = "";
6151 [ # # # # : 0 : foreach(l, query->distinctClause)
# # ]
6152 : : {
6346 6153 : 0 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6154 : :
7536 neilc@samurai.com 6155 : 0 : appendStringInfoString(buf, sep);
3868 andres@anarazel.de 6156 : 0 : get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6157 : : false, context);
9204 tgl@sss.pgh.pa.us 6158 : 0 : sep = ", ";
6159 : : }
4430 rhaas@postgresql.org 6160 : 0 : appendStringInfoChar(buf, ')');
6161 : : }
6162 : : else
6163 : 0 : appendStringInfoString(buf, " DISTINCT");
6164 : : }
6165 : :
6166 : : /* Then we tell what to select (the targetlist) */
475 tgl@sss.pgh.pa.us 6167 :CBC 2342 : get_target_list(query->targetList, context);
6168 : :
6169 : : /* Add the FROM clause if needed */
7067 6170 : 2342 : get_from_clause(query, " FROM ", context);
6171 : :
6172 : : /* Add the WHERE clause if given */
6173 [ + + ]: 2342 : if (query->jointree->quals != NULL)
6174 : : {
6175 : 742 : appendContextKeyword(context, " WHERE ",
6176 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6177 : 742 : get_rule_expr(query->jointree->quals, context, false);
6178 : : }
6179 : :
6180 : : /* Add the GROUP BY clause if given */
3868 andres@anarazel.de 6181 [ + + - + ]: 2342 : if (query->groupClause != NULL || query->groupingSets != NULL)
6182 : : {
6183 : : bool save_ingroupby;
6184 : :
7067 tgl@sss.pgh.pa.us 6185 : 110 : appendContextKeyword(context, " GROUP BY ",
6186 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
1735 tomas.vondra@postgre 6187 [ - + ]: 110 : if (query->groupDistinct)
1735 tomas.vondra@postgre 6188 :UBC 0 : appendStringInfoString(buf, "DISTINCT ");
6189 : :
475 tgl@sss.pgh.pa.us 6190 :CBC 110 : save_ingroupby = context->inGroupBy;
6191 : 110 : context->inGroupBy = true;
6192 : :
79 tgl@sss.pgh.pa.us 6193 [ + + ]:GNC 110 : if (query->groupByAll)
6194 : 3 : appendStringInfoString(buf, "ALL");
6195 [ + + ]: 107 : else if (query->groupingSets == NIL)
6196 : : {
3868 andres@anarazel.de 6197 :CBC 104 : sep = "";
6198 [ + - + + : 237 : foreach(l, query->groupClause)
+ + ]
6199 : : {
6200 : 133 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6201 : :
6202 : 133 : appendStringInfoString(buf, sep);
6203 : 133 : get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
6204 : : false, context);
6205 : 133 : sep = ", ";
6206 : : }
6207 : : }
6208 : : else
6209 : : {
6210 : 3 : sep = "";
6211 [ + - + + : 6 : foreach(l, query->groupingSets)
+ + ]
6212 : : {
6213 : 3 : GroupingSet *grp = lfirst(l);
6214 : :
6215 : 3 : appendStringInfoString(buf, sep);
6216 : 3 : get_rule_groupingset(grp, query->targetList, true, context);
6217 : 3 : sep = ", ";
6218 : : }
6219 : : }
6220 : :
475 tgl@sss.pgh.pa.us 6221 : 110 : context->inGroupBy = save_ingroupby;
6222 : : }
6223 : :
6224 : : /* Add the HAVING clause if given */
7067 6225 [ + + ]: 2342 : if (query->havingQual != NULL)
6226 : : {
6227 : 5 : appendContextKeyword(context, " HAVING ",
6228 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
6229 : 5 : get_rule_expr(query->havingQual, context, false);
6230 : : }
6231 : :
6232 : : /* Add the WINDOW clause if needed */
6198 6233 [ + + ]: 2342 : if (query->windowClause != NIL)
6234 : 24 : get_rule_windowclause(query, context);
6235 : : }
6236 : :
6237 : : /* ----------
6238 : : * get_target_list - Parse back a SELECT target list
6239 : : *
6240 : : * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.
6241 : : * ----------
6242 : : */
6243 : : static void
475 6244 : 2415 : get_target_list(List *targetList, deparse_context *context)
6245 : : {
7067 6246 : 2415 : StringInfo buf = context->buf;
6247 : : StringInfoData targetbuf;
4741 6248 : 2415 : bool last_was_multiline = false;
6249 : : char *sep;
6250 : : int colno;
6251 : : ListCell *l;
6252 : :
6253 : : /* we use targetbuf to hold each TLE's text temporarily */
6254 : 2415 : initStringInfo(&targetbuf);
6255 : :
9977 bruce@momjian.us 6256 : 2415 : sep = " ";
8532 tgl@sss.pgh.pa.us 6257 : 2415 : colno = 0;
7067 6258 [ + - + + : 12613 : foreach(l, targetList)
+ + ]
6259 : : {
9227 6260 : 10198 : TargetEntry *tle = (TargetEntry *) lfirst(l);
6261 : : char *colname;
6262 : : char *attname;
6263 : :
7560 6264 [ + + ]: 10198 : if (tle->resjunk)
9210 6265 : 17 : continue; /* ignore junk entries */
6266 : :
7536 neilc@samurai.com 6267 : 10181 : appendStringInfoString(buf, sep);
9969 bruce@momjian.us 6268 : 10181 : sep = ", ";
8532 tgl@sss.pgh.pa.us 6269 : 10181 : colno++;
6270 : :
6271 : : /*
6272 : : * Put the new field text into targetbuf so we can decide after we've
6273 : : * got it whether or not it needs to go on a new line.
6274 : : */
4741 6275 : 10181 : resetStringInfo(&targetbuf);
5050 andrew@dunslane.net 6276 : 10181 : context->buf = &targetbuf;
6277 : :
6278 : : /*
6279 : : * We special-case Var nodes rather than using get_rule_expr. This is
6280 : : * needed because get_rule_expr will display a whole-row Var as
6281 : : * "foo.*", which is the preferred notation in most contexts, but at
6282 : : * the top level of a SELECT list it's not right (the parser will
6283 : : * expand that notation into multiple columns, yielding behavior
6284 : : * different from a whole-row Var). We need to call get_variable
6285 : : * directly so that we can tell it to do the right thing, and so that
6286 : : * we can get the attribute name which is the default AS label.
6287 : : */
3868 andres@anarazel.de 6288 [ + - + + ]: 10181 : if (tle->expr && (IsA(tle->expr, Var)))
6289 : : {
4982 tgl@sss.pgh.pa.us 6290 : 7846 : attname = get_variable((Var *) tle->expr, 0, true, context);
6291 : : }
6292 : : else
6293 : : {
7265 6294 : 2335 : get_rule_expr((Node *) tle->expr, context, true);
6295 : :
6296 : : /*
6297 : : * When colNamesVisible is true, we should always show the
6298 : : * assigned column name explicitly. Otherwise, show it only if
6299 : : * it's not FigureColname's fallback.
6300 : : */
475 6301 [ + + ]: 2335 : attname = context->colNamesVisible ? NULL : "?column?";
6302 : : }
6303 : :
6304 : : /*
6305 : : * Figure out what the result column should be called. In the context
6306 : : * of a view, use the view's tuple descriptor (so as to pick up the
6307 : : * effects of any column RENAME that's been done on the view).
6308 : : * Otherwise, just use what we can find in the TLE.
6309 : : */
6310 [ + + + - ]: 10181 : if (context->resultDesc && colno <= context->resultDesc->natts)
6311 : 9275 : colname = NameStr(TupleDescAttr(context->resultDesc,
6312 : : colno - 1)->attname);
6313 : : else
7560 6314 : 906 : colname = tle->resname;
6315 : :
6316 : : /* Show AS unless the column's name is correct as-is */
8164 6317 [ + + ]: 10181 : if (colname) /* resname could be NULL */
6318 : : {
7265 6319 [ + + + + ]: 10155 : if (attname == NULL || strcmp(attname, colname) != 0)
5050 andrew@dunslane.net 6320 : 3308 : appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
6321 : : }
6322 : :
6323 : : /* Restore context's output buffer */
6324 : 10181 : context->buf = buf;
6325 : :
6326 : : /* Consider line-wrapping if enabled */
4741 tgl@sss.pgh.pa.us 6327 [ + + + - ]: 10181 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
6328 : : {
6329 : : int leading_nl_pos;
6330 : :
6331 : : /* Does the new field start with a new line? */
4419 6332 [ + - + + ]: 10158 : if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
6333 : 259 : leading_nl_pos = 0;
6334 : : else
6335 : 9899 : leading_nl_pos = -1;
6336 : :
6337 : : /* If so, we shouldn't add anything */
6338 [ + + ]: 10158 : if (leading_nl_pos >= 0)
6339 : : {
6340 : : /* instead, remove any trailing spaces currently in buf */
6341 : 259 : removeStringInfoSpaces(buf);
6342 : : }
6343 : : else
6344 : : {
6345 : : char *trailing_nl;
6346 : :
6347 : : /* Locate the start of the current line in the output buffer */
6348 : 9899 : trailing_nl = strrchr(buf->data, '\n');
6349 [ + + ]: 9899 : if (trailing_nl == NULL)
6350 : 2965 : trailing_nl = buf->data;
6351 : : else
6352 : 6934 : trailing_nl++;
6353 : :
6354 : : /*
6355 : : * Add a newline, plus some indentation, if the new field is
6356 : : * not the first and either the new field would cause an
6357 : : * overflow or the last field used more than one line.
6358 : : */
6359 [ + + ]: 9899 : if (colno > 1 &&
6360 [ - + - - ]: 7515 : ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
6361 : : last_was_multiline))
6362 : 7515 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
6363 : : PRETTYINDENT_STD, PRETTYINDENT_VAR);
6364 : : }
6365 : :
6366 : : /* Remember this field's multiline status for next iteration */
4741 6367 : 10158 : last_was_multiline =
6368 : 10158 : (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
6369 : : }
6370 : :
6371 : : /* Add the new field */
2339 drowley@postgresql.o 6372 : 10181 : appendBinaryStringInfo(buf, targetbuf.data, targetbuf.len);
6373 : : }
6374 : :
6375 : : /* clean up */
4741 tgl@sss.pgh.pa.us 6376 : 2415 : pfree(targetbuf.data);
9204 6377 : 2415 : }
6378 : :
6379 : : static void
335 dean.a.rasheed@gmail 6380 : 73 : get_returning_clause(Query *query, deparse_context *context)
6381 : : {
6382 : 73 : StringInfo buf = context->buf;
6383 : :
6384 [ + - ]: 73 : if (query->returningList)
6385 : : {
6386 : 73 : bool have_with = false;
6387 : :
6388 : 73 : appendContextKeyword(context, " RETURNING",
6389 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6390 : :
6391 : : /* Add WITH (OLD/NEW) options, if they're not the defaults */
6392 [ + + + + ]: 73 : if (query->returningOldAlias && strcmp(query->returningOldAlias, "old") != 0)
6393 : : {
6394 : 9 : appendStringInfo(buf, " WITH (OLD AS %s",
6395 : 9 : quote_identifier(query->returningOldAlias));
6396 : 9 : have_with = true;
6397 : : }
6398 [ + + + + ]: 73 : if (query->returningNewAlias && strcmp(query->returningNewAlias, "new") != 0)
6399 : : {
6400 [ + + ]: 9 : if (have_with)
6401 : 6 : appendStringInfo(buf, ", NEW AS %s",
6402 : 6 : quote_identifier(query->returningNewAlias));
6403 : : else
6404 : : {
6405 : 3 : appendStringInfo(buf, " WITH (NEW AS %s",
6406 : 3 : quote_identifier(query->returningNewAlias));
6407 : 3 : have_with = true;
6408 : : }
6409 : : }
6410 [ + + ]: 73 : if (have_with)
6411 : 12 : appendStringInfoChar(buf, ')');
6412 : :
6413 : : /* Add the returning expressions themselves */
6414 : 73 : get_target_list(query->returningList, context);
6415 : : }
6416 : 73 : }
6417 : :
6418 : : static void
475 tgl@sss.pgh.pa.us 6419 : 378 : get_setop_query(Node *setOp, Query *query, deparse_context *context)
6420 : : {
9204 6421 : 378 : StringInfo buf = context->buf;
6422 : : bool need_paren;
6423 : :
6424 : : /* Guard against excessively long or deeply-nested queries */
4249 6425 [ - + ]: 378 : CHECK_FOR_INTERRUPTS();
6426 : 378 : check_stack_depth();
6427 : :
9204 6428 [ + + ]: 378 : if (IsA(setOp, RangeTblRef))
6429 : : {
6430 : 230 : RangeTblRef *rtr = (RangeTblRef *) setOp;
6431 : 230 : RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
9036 bruce@momjian.us 6432 : 230 : Query *subquery = rte->subquery;
6433 : :
9204 tgl@sss.pgh.pa.us 6434 [ - + ]: 230 : Assert(subquery != NULL);
6435 : :
6436 : : /*
6437 : : * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.
6438 : : * Also add parens if the leaf query contains its own set operations.
6439 : : * (That shouldn't happen unless one of the other clauses is also
6440 : : * present, see transformSetOperationTree; but let's be safe.)
6441 : : */
6283 6442 : 690 : need_paren = (subquery->cteList ||
6443 [ + - ]: 230 : subquery->sortClause ||
7834 6444 [ + - ]: 230 : subquery->rowMarks ||
6445 [ + - ]: 230 : subquery->limitOffset ||
392 6446 [ + - + - ]: 690 : subquery->limitCount ||
6447 [ - + ]: 230 : subquery->setOperations);
7834 6448 [ - + ]: 230 : if (need_paren)
7834 tgl@sss.pgh.pa.us 6449 :UBC 0 : appendStringInfoChar(buf, '(');
475 tgl@sss.pgh.pa.us 6450 :CBC 230 : get_query_def(subquery, buf, context->namespaces,
6451 : 230 : context->resultDesc, context->colNamesVisible,
6452 : : context->prettyFlags, context->wrapColumn,
6453 : : context->indentLevel);
7834 6454 [ - + ]: 230 : if (need_paren)
7834 tgl@sss.pgh.pa.us 6455 :UBC 0 : appendStringInfoChar(buf, ')');
6456 : : }
9204 tgl@sss.pgh.pa.us 6457 [ + - ]:CBC 148 : else if (IsA(setOp, SetOperationStmt))
6458 : : {
6459 : 148 : SetOperationStmt *op = (SetOperationStmt *) setOp;
6460 : : int subindent;
6461 : : bool save_colnamesvisible;
6462 : :
6463 : : /*
6464 : : * We force parens when nesting two SetOperationStmts, except when the
6465 : : * lefthand input is another setop of the same kind. Syntactically,
6466 : : * we could omit parens in rather more cases, but it seems best to use
6467 : : * parens to flag cases where the setop operator changes. If we use
6468 : : * parens, we also increase the indentation level for the child query.
6469 : : *
6470 : : * There are some cases in which parens are needed around a leaf query
6471 : : * too, but those are more easily handled at the next level down (see
6472 : : * code above).
6473 : : */
4249 6474 [ + + ]: 148 : if (IsA(op->larg, SetOperationStmt))
6475 : : {
6476 : 66 : SetOperationStmt *lop = (SetOperationStmt *) op->larg;
6477 : :
6478 [ + - + - ]: 66 : if (op->op == lop->op && op->all == lop->all)
6479 : 66 : need_paren = false;
6480 : : else
4249 tgl@sss.pgh.pa.us 6481 :UBC 0 : need_paren = true;
6482 : : }
6483 : : else
4249 tgl@sss.pgh.pa.us 6484 :CBC 82 : need_paren = false;
6485 : :
7834 6486 [ - + ]: 148 : if (need_paren)
6487 : : {
7834 tgl@sss.pgh.pa.us 6488 :UBC 0 : appendStringInfoChar(buf, '(');
4249 6489 : 0 : subindent = PRETTYINDENT_STD;
6490 : 0 : appendContextKeyword(context, "", subindent, 0, 0);
6491 : : }
6492 : : else
4249 tgl@sss.pgh.pa.us 6493 :CBC 148 : subindent = 0;
6494 : :
475 6495 : 148 : get_setop_query(op->larg, query, context);
6496 : :
4249 6497 [ - + ]: 148 : if (need_paren)
4249 tgl@sss.pgh.pa.us 6498 :UBC 0 : appendContextKeyword(context, ") ", -subindent, 0, 0);
4249 tgl@sss.pgh.pa.us 6499 [ + - ]:CBC 148 : else if (PRETTY_INDENT(context))
6500 : 148 : appendContextKeyword(context, "", -subindent, 0, 0);
6501 : : else
8171 bruce@momjian.us 6502 :UBC 0 : appendStringInfoChar(buf, ' ');
6503 : :
9204 tgl@sss.pgh.pa.us 6504 [ + - - - ]:CBC 148 : switch (op->op)
6505 : : {
6506 : 148 : case SETOP_UNION:
4249 6507 : 148 : appendStringInfoString(buf, "UNION ");
9204 6508 : 148 : break;
9204 tgl@sss.pgh.pa.us 6509 :UBC 0 : case SETOP_INTERSECT:
4249 6510 : 0 : appendStringInfoString(buf, "INTERSECT ");
9204 6511 : 0 : break;
6512 : 0 : case SETOP_EXCEPT:
4249 6513 : 0 : appendStringInfoString(buf, "EXCEPT ");
9204 6514 : 0 : break;
6515 : 0 : default:
8179 6516 [ # # ]: 0 : elog(ERROR, "unrecognized set op: %d",
6517 : : (int) op->op);
6518 : : }
9204 tgl@sss.pgh.pa.us 6519 [ + + ]:CBC 148 : if (op->all)
4430 rhaas@postgresql.org 6520 : 142 : appendStringInfoString(buf, "ALL ");
6521 : :
6522 : : /* Always parenthesize if RHS is another setop */
4249 tgl@sss.pgh.pa.us 6523 : 148 : need_paren = IsA(op->rarg, SetOperationStmt);
6524 : :
6525 : : /*
6526 : : * The indentation code here is deliberately a bit different from that
6527 : : * for the lefthand input, because we want the line breaks in
6528 : : * different places.
6529 : : */
7834 6530 [ - + ]: 148 : if (need_paren)
6531 : : {
7834 tgl@sss.pgh.pa.us 6532 :UBC 0 : appendStringInfoChar(buf, '(');
4249 6533 : 0 : subindent = PRETTYINDENT_STD;
6534 : : }
6535 : : else
4249 tgl@sss.pgh.pa.us 6536 :CBC 148 : subindent = 0;
6537 : 148 : appendContextKeyword(context, "", subindent, 0, 0);
6538 : :
6539 : : /*
6540 : : * The output column names of the RHS sub-select don't matter.
6541 : : */
475 6542 : 148 : save_colnamesvisible = context->colNamesVisible;
6543 : 148 : context->colNamesVisible = false;
6544 : :
6545 : 148 : get_setop_query(op->rarg, query, context);
6546 : :
6547 : 148 : context->colNamesVisible = save_colnamesvisible;
6548 : :
6283 6549 [ + - ]: 148 : if (PRETTY_INDENT(context))
4249 6550 : 148 : context->indentLevel -= subindent;
6551 [ - + ]: 148 : if (need_paren)
4249 tgl@sss.pgh.pa.us 6552 :UBC 0 : appendContextKeyword(context, ")", 0, 0, 0);
6553 : : }
6554 : : else
6555 : : {
8179 6556 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
6557 : : (int) nodeTag(setOp));
6558 : : }
9204 tgl@sss.pgh.pa.us 6559 :CBC 378 : }
6560 : :
6561 : : /*
6562 : : * Display a sort/group clause.
6563 : : *
6564 : : * Also returns the expression tree, so caller need not find it again.
6565 : : */
6566 : : static Node *
3868 andres@anarazel.de 6567 : 336 : get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
6568 : : deparse_context *context)
6569 : : {
9012 tgl@sss.pgh.pa.us 6570 : 336 : StringInfo buf = context->buf;
6571 : : TargetEntry *tle;
6572 : : Node *expr;
6573 : :
3868 andres@anarazel.de 6574 : 336 : tle = get_sortgroupref_tle(ref, tlist);
8406 tgl@sss.pgh.pa.us 6575 : 336 : expr = (Node *) tle->expr;
6576 : :
6577 : : /*
6578 : : * Use column-number form if requested by caller. Otherwise, if
6579 : : * expression is a constant, force it to be dumped with an explicit cast
6580 : : * as decoration --- this is because a simple integer constant is
6581 : : * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
6582 : : * we dump it without any decoration. Similarly, if it's just a Var,
6583 : : * there is risk of misinterpretation if the column name is reassigned in
6584 : : * the SELECT list, so we may need to force table qualification. And, if
6585 : : * it's anything more complex than a simple Var, then force extra parens
6586 : : * around it, to ensure it can't be misinterpreted as a cube() or rollup()
6587 : : * construct.
6588 : : */
6555 6589 [ + + ]: 336 : if (force_colno)
6590 : : {
7560 6591 [ - + ]: 6 : Assert(!tle->resjunk);
6592 : 6 : appendStringInfo(buf, "%d", tle->resno);
6593 : : }
475 6594 [ + - ]: 330 : else if (!expr)
6595 : : /* do nothing, probably can't happen */ ;
6596 [ - + ]: 330 : else if (IsA(expr, Const))
6555 tgl@sss.pgh.pa.us 6597 :UBC 0 : get_const_expr((Const *) expr, context, 1);
475 tgl@sss.pgh.pa.us 6598 [ + + ]:CBC 330 : else if (IsA(expr, Var))
6599 : : {
6600 : : /* Tell get_variable to check for name conflict */
6601 : 316 : bool save_varinorderby = context->varInOrderBy;
6602 : :
6603 : 316 : context->varInOrderBy = true;
6604 : 316 : (void) get_variable((Var *) expr, 0, false, context);
6605 : 316 : context->varInOrderBy = save_varinorderby;
6606 : : }
6607 : : else
6608 : : {
6609 : : /*
6610 : : * We must force parens for function-like expressions even if
6611 : : * PRETTY_PAREN is off, since those are the ones in danger of
6612 : : * misparsing. For other expressions we need to force them only if
6613 : : * PRETTY_PAREN is on, since otherwise the expression will output them
6614 : : * itself. (We can't skip the parens.)
6615 : : */
3861 bruce@momjian.us 6616 : 28 : bool need_paren = (PRETTY_PAREN(context)
6617 [ + + ]: 14 : || IsA(expr, FuncExpr)
2041 tgl@sss.pgh.pa.us 6618 [ + - ]: 12 : || IsA(expr, Aggref)
994 alvherre@alvh.no-ip. 6619 [ + - ]: 12 : || IsA(expr, WindowFunc)
6620 [ + - - + ]: 28 : || IsA(expr, JsonConstructorExpr));
6621 : :
3868 andres@anarazel.de 6622 [ + + ]: 14 : if (need_paren)
3046 peter_e@gmx.net 6623 : 2 : appendStringInfoChar(context->buf, '(');
8490 tgl@sss.pgh.pa.us 6624 : 14 : get_rule_expr(expr, context, true);
3868 andres@anarazel.de 6625 [ + + ]: 14 : if (need_paren)
3046 peter_e@gmx.net 6626 : 2 : appendStringInfoChar(context->buf, ')');
6627 : : }
6628 : :
8629 tgl@sss.pgh.pa.us 6629 : 336 : return expr;
6630 : : }
6631 : :
6632 : : /*
6633 : : * Display a GroupingSet
6634 : : */
6635 : : static void
3868 andres@anarazel.de 6636 : 9 : get_rule_groupingset(GroupingSet *gset, List *targetlist,
6637 : : bool omit_parens, deparse_context *context)
6638 : : {
6639 : : ListCell *l;
6640 : 9 : StringInfo buf = context->buf;
6641 : 9 : bool omit_child_parens = true;
6642 : 9 : char *sep = "";
6643 : :
6644 [ - + + - : 9 : switch (gset->kind)
- - ]
6645 : : {
3868 andres@anarazel.de 6646 :UBC 0 : case GROUPING_SET_EMPTY:
6647 : 0 : appendStringInfoString(buf, "()");
6648 : 0 : return;
6649 : :
3868 andres@anarazel.de 6650 :CBC 6 : case GROUPING_SET_SIMPLE:
6651 : : {
6652 [ + - + - ]: 6 : if (!omit_parens || list_length(gset->content) != 1)
3046 peter_e@gmx.net 6653 : 6 : appendStringInfoChar(buf, '(');
6654 : :
3868 andres@anarazel.de 6655 [ + - + + : 21 : foreach(l, gset->content)
+ + ]
6656 : : {
3861 bruce@momjian.us 6657 : 15 : Index ref = lfirst_int(l);
6658 : :
3868 andres@anarazel.de 6659 : 15 : appendStringInfoString(buf, sep);
6660 : 15 : get_rule_sortgroupclause(ref, targetlist,
6661 : : false, context);
6662 : 15 : sep = ", ";
6663 : : }
6664 : :
6665 [ + - + - ]: 6 : if (!omit_parens || list_length(gset->content) != 1)
3046 peter_e@gmx.net 6666 : 6 : appendStringInfoChar(buf, ')');
6667 : : }
3868 andres@anarazel.de 6668 : 6 : return;
6669 : :
6670 : 3 : case GROUPING_SET_ROLLUP:
6671 : 3 : appendStringInfoString(buf, "ROLLUP(");
6672 : 3 : break;
3868 andres@anarazel.de 6673 :UBC 0 : case GROUPING_SET_CUBE:
6674 : 0 : appendStringInfoString(buf, "CUBE(");
6675 : 0 : break;
6676 : 0 : case GROUPING_SET_SETS:
6677 : 0 : appendStringInfoString(buf, "GROUPING SETS (");
6678 : 0 : omit_child_parens = false;
6679 : 0 : break;
6680 : : }
6681 : :
3868 andres@anarazel.de 6682 [ + - + + :CBC 9 : foreach(l, gset->content)
+ + ]
6683 : : {
6684 : 6 : appendStringInfoString(buf, sep);
6685 : 6 : get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
6686 : 6 : sep = ", ";
6687 : : }
6688 : :
3046 peter_e@gmx.net 6689 : 3 : appendStringInfoChar(buf, ')');
6690 : : }
6691 : :
6692 : : /*
6693 : : * Display an ORDER BY list.
6694 : : */
6695 : : static void
6198 tgl@sss.pgh.pa.us 6696 : 172 : get_rule_orderby(List *orderList, List *targetList,
6697 : : bool force_colno, deparse_context *context)
6698 : : {
6699 : 172 : StringInfo buf = context->buf;
6700 : : const char *sep;
6701 : : ListCell *l;
6702 : :
6703 : 172 : sep = "";
6704 [ + - + + : 360 : foreach(l, orderList)
+ + ]
6705 : : {
6706 : 188 : SortGroupClause *srt = (SortGroupClause *) lfirst(l);
6707 : : Node *sortexpr;
6708 : : Oid sortcoltype;
6709 : : TypeCacheEntry *typentry;
6710 : :
6711 : 188 : appendStringInfoString(buf, sep);
3868 andres@anarazel.de 6712 : 188 : sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
6713 : : force_colno, context);
6198 tgl@sss.pgh.pa.us 6714 : 188 : sortcoltype = exprType(sortexpr);
6715 : : /* See whether operator is default < or > for datatype */
6716 : 188 : typentry = lookup_type_cache(sortcoltype,
6717 : : TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
6718 [ + + ]: 188 : if (srt->sortop == typentry->lt_opr)
6719 : : {
6720 : : /* ASC is default, so emit nothing for it */
6721 [ - + ]: 174 : if (srt->nulls_first)
4430 rhaas@postgresql.org 6722 :UBC 0 : appendStringInfoString(buf, " NULLS FIRST");
6723 : : }
6198 tgl@sss.pgh.pa.us 6724 [ + + ]:CBC 14 : else if (srt->sortop == typentry->gt_opr)
6725 : : {
4430 rhaas@postgresql.org 6726 : 5 : appendStringInfoString(buf, " DESC");
6727 : : /* DESC defaults to NULLS FIRST */
6198 tgl@sss.pgh.pa.us 6728 [ + + ]: 5 : if (!srt->nulls_first)
4430 rhaas@postgresql.org 6729 : 1 : appendStringInfoString(buf, " NULLS LAST");
6730 : : }
6731 : : else
6732 : : {
6198 tgl@sss.pgh.pa.us 6733 : 9 : appendStringInfo(buf, " USING %s",
6734 : : generate_operator_name(srt->sortop,
6735 : : sortcoltype,
6736 : : sortcoltype));
6737 : : /* be specific to eliminate ambiguity */
6738 [ - + ]: 9 : if (srt->nulls_first)
4430 rhaas@postgresql.org 6739 :UBC 0 : appendStringInfoString(buf, " NULLS FIRST");
6740 : : else
4430 rhaas@postgresql.org 6741 :CBC 9 : appendStringInfoString(buf, " NULLS LAST");
6742 : : }
6198 tgl@sss.pgh.pa.us 6743 : 188 : sep = ", ";
6744 : : }
6745 : 172 : }
6746 : :
6747 : : /*
6748 : : * Display a WINDOW clause.
6749 : : *
6750 : : * Note that the windowClause list might contain only anonymous window
6751 : : * specifications, in which case we should print nothing here.
6752 : : */
6753 : : static void
6754 : 24 : get_rule_windowclause(Query *query, deparse_context *context)
6755 : : {
6756 : 24 : StringInfo buf = context->buf;
6757 : : const char *sep;
6758 : : ListCell *l;
6759 : :
6760 : 24 : sep = NULL;
6761 [ + - + + : 48 : foreach(l, query->windowClause)
+ + ]
6762 : : {
6763 : 24 : WindowClause *wc = (WindowClause *) lfirst(l);
6764 : :
6765 [ + + ]: 24 : if (wc->name == NULL)
6766 : 21 : continue; /* ignore anonymous windows */
6767 : :
6198 tgl@sss.pgh.pa.us 6768 [ + - ]:GBC 3 : if (sep == NULL)
6769 : 3 : appendContextKeyword(context, " WINDOW ",
6770 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
6771 : : else
6198 tgl@sss.pgh.pa.us 6772 :UBC 0 : appendStringInfoString(buf, sep);
6773 : :
6198 tgl@sss.pgh.pa.us 6774 :GBC 3 : appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
6775 : :
6776 : 3 : get_rule_windowspec(wc, query->targetList, context);
6777 : :
6778 : 3 : sep = ", ";
6779 : : }
6198 tgl@sss.pgh.pa.us 6780 :CBC 24 : }
6781 : :
6782 : : /*
6783 : : * Display a window definition
6784 : : */
6785 : : static void
6786 : 24 : get_rule_windowspec(WindowClause *wc, List *targetList,
6787 : : deparse_context *context)
6788 : : {
6789 : 24 : StringInfo buf = context->buf;
6790 : 24 : bool needspace = false;
6791 : : const char *sep;
6792 : : ListCell *l;
6793 : :
6794 : 24 : appendStringInfoChar(buf, '(');
6795 [ - + ]: 24 : if (wc->refname)
6796 : : {
6198 tgl@sss.pgh.pa.us 6797 :UBC 0 : appendStringInfoString(buf, quote_identifier(wc->refname));
6798 : 0 : needspace = true;
6799 : : }
6800 : : /* partition clauses are always inherited, so only print if no refname */
6198 tgl@sss.pgh.pa.us 6801 [ - + - - ]:CBC 24 : if (wc->partitionClause && !wc->refname)
6802 : : {
6198 tgl@sss.pgh.pa.us 6803 [ # # ]:UBC 0 : if (needspace)
6804 : 0 : appendStringInfoChar(buf, ' ');
6805 : 0 : appendStringInfoString(buf, "PARTITION BY ");
6806 : 0 : sep = "";
6807 [ # # # # : 0 : foreach(l, wc->partitionClause)
# # ]
6808 : : {
6809 : 0 : SortGroupClause *grp = (SortGroupClause *) lfirst(l);
6810 : :
6811 : 0 : appendStringInfoString(buf, sep);
3868 andres@anarazel.de 6812 : 0 : get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
6813 : : false, context);
6198 tgl@sss.pgh.pa.us 6814 : 0 : sep = ", ";
6815 : : }
6816 : 0 : needspace = true;
6817 : : }
6818 : : /* print ordering clause only if not inherited */
6198 tgl@sss.pgh.pa.us 6819 [ + - + - ]:CBC 24 : if (wc->orderClause && !wc->copiedOrder)
6820 : : {
6821 [ - + ]: 24 : if (needspace)
6198 tgl@sss.pgh.pa.us 6822 :UBC 0 : appendStringInfoChar(buf, ' ');
6198 tgl@sss.pgh.pa.us 6823 :CBC 24 : appendStringInfoString(buf, "ORDER BY ");
6824 : 24 : get_rule_orderby(wc->orderClause, targetList, false, context);
6825 : 24 : needspace = true;
6826 : : }
6827 : : /* framing clause is never inherited, so print unless it's default */
6195 6828 [ + + ]: 24 : if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
6829 : : {
6830 [ + - ]: 21 : if (needspace)
6831 : 21 : appendStringInfoChar(buf, ' ');
281 6832 : 21 : get_window_frame_options(wc->frameOptions,
6833 : : wc->startOffset, wc->endOffset,
6834 : : context);
6835 : : }
6836 : 24 : appendStringInfoChar(buf, ')');
6837 : 24 : }
6838 : :
6839 : : /*
6840 : : * Append the description of a window's framing options to context->buf
6841 : : */
6842 : : static void
6843 : 119 : get_window_frame_options(int frameOptions,
6844 : : Node *startOffset, Node *endOffset,
6845 : : deparse_context *context)
6846 : : {
6847 : 119 : StringInfo buf = context->buf;
6848 : :
6849 [ + - ]: 119 : if (frameOptions & FRAMEOPTION_NONDEFAULT)
6850 : : {
6851 [ + + ]: 119 : if (frameOptions & FRAMEOPTION_RANGE)
6195 6852 : 10 : appendStringInfoString(buf, "RANGE ");
281 6853 [ + + ]: 109 : else if (frameOptions & FRAMEOPTION_ROWS)
6195 6854 : 103 : appendStringInfoString(buf, "ROWS ");
281 6855 [ + - ]: 6 : else if (frameOptions & FRAMEOPTION_GROUPS)
2870 6856 : 6 : appendStringInfoString(buf, "GROUPS ");
6857 : : else
6195 tgl@sss.pgh.pa.us 6858 :UBC 0 : Assert(false);
281 tgl@sss.pgh.pa.us 6859 [ + + ]:CBC 119 : if (frameOptions & FRAMEOPTION_BETWEEN)
6195 6860 : 46 : appendStringInfoString(buf, "BETWEEN ");
281 6861 [ + + ]: 119 : if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
6195 6862 : 76 : appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
281 6863 [ + + ]: 43 : else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
6195 6864 : 13 : appendStringInfoString(buf, "CURRENT ROW ");
281 6865 [ + - ]: 30 : else if (frameOptions & FRAMEOPTION_START_OFFSET)
6866 : : {
6867 : 30 : get_rule_expr(startOffset, context, false);
6868 [ + - ]: 30 : if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
5787 6869 : 30 : appendStringInfoString(buf, " PRECEDING ");
281 tgl@sss.pgh.pa.us 6870 [ # # ]:UBC 0 : else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
5787 6871 : 0 : appendStringInfoString(buf, " FOLLOWING ");
6872 : : else
6873 : 0 : Assert(false);
6874 : : }
6875 : : else
6195 6876 : 0 : Assert(false);
281 tgl@sss.pgh.pa.us 6877 [ + + ]:CBC 119 : if (frameOptions & FRAMEOPTION_BETWEEN)
6878 : : {
6195 6879 : 46 : appendStringInfoString(buf, "AND ");
281 6880 [ + + ]: 46 : if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
6195 6881 : 10 : appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
281 6882 [ + + ]: 36 : else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
6195 6883 : 3 : appendStringInfoString(buf, "CURRENT ROW ");
281 6884 [ + - ]: 33 : else if (frameOptions & FRAMEOPTION_END_OFFSET)
6885 : : {
6886 : 33 : get_rule_expr(endOffset, context, false);
6887 [ - + ]: 33 : if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
5787 tgl@sss.pgh.pa.us 6888 :UBC 0 : appendStringInfoString(buf, " PRECEDING ");
281 tgl@sss.pgh.pa.us 6889 [ + - ]:CBC 33 : else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
5787 6890 : 33 : appendStringInfoString(buf, " FOLLOWING ");
6891 : : else
5787 tgl@sss.pgh.pa.us 6892 :UBC 0 : Assert(false);
6893 : : }
6894 : : else
6195 6895 : 0 : Assert(false);
6896 : : }
281 tgl@sss.pgh.pa.us 6897 [ + + ]:CBC 119 : if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
2870 6898 : 3 : appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
281 6899 [ + + ]: 116 : else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
2870 6900 : 3 : appendStringInfoString(buf, "EXCLUDE GROUP ");
281 6901 [ + + ]: 113 : else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
2870 6902 : 3 : appendStringInfoString(buf, "EXCLUDE TIES ");
6903 : : /* we will now have a trailing space; remove it */
281 6904 : 119 : buf->data[--(buf->len)] = '\0';
6905 : : }
6906 : 119 : }
6907 : :
6908 : : /*
6909 : : * Return the description of a window's framing options as a palloc'd string
6910 : : */
6911 : : char *
6912 : 98 : get_window_frame_options_for_explain(int frameOptions,
6913 : : Node *startOffset, Node *endOffset,
6914 : : List *dpcontext, bool forceprefix)
6915 : : {
6916 : : StringInfoData buf;
6917 : : deparse_context context;
6918 : :
6919 : 98 : initStringInfo(&buf);
6920 : 98 : context.buf = &buf;
6921 : 98 : context.namespaces = dpcontext;
6922 : 98 : context.resultDesc = NULL;
6923 : 98 : context.targetList = NIL;
6924 : 98 : context.windowClause = NIL;
6925 : 98 : context.varprefix = forceprefix;
6926 : 98 : context.prettyFlags = 0;
6927 : 98 : context.wrapColumn = WRAP_COLUMN_DEFAULT;
6928 : 98 : context.indentLevel = 0;
6929 : 98 : context.colNamesVisible = true;
6930 : 98 : context.inGroupBy = false;
6931 : 98 : context.varInOrderBy = false;
6932 : 98 : context.appendparents = NULL;
6933 : :
6934 : 98 : get_window_frame_options(frameOptions, startOffset, endOffset, &context);
6935 : :
6936 : 98 : return buf.data;
6937 : : }
6938 : :
6939 : : /* ----------
6940 : : * get_insert_query_def - Parse back an INSERT parsetree
6941 : : * ----------
6942 : : */
6943 : : static void
475 6944 : 170 : get_insert_query_def(Query *query, deparse_context *context)
6945 : : {
9572 6946 : 170 : StringInfo buf = context->buf;
9204 6947 : 170 : RangeTblEntry *select_rte = NULL;
7077 mail@joeconway.com 6948 : 170 : RangeTblEntry *values_rte = NULL;
6949 : : RangeTblEntry *rte;
6950 : : char *sep;
6951 : : ListCell *l;
6952 : : List *strippedexprs;
6953 : :
6954 : : /* Insert the WITH clause if given */
5542 tgl@sss.pgh.pa.us 6955 : 170 : get_with_clause(query, context);
6956 : :
6957 : : /*
6958 : : * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
6959 : : * single RTE for the SELECT or VALUES. Plain VALUES has neither.
6960 : : */
9969 bruce@momjian.us 6961 [ + - + + : 661 : foreach(l, query->rtable)
+ + ]
6962 : : {
6963 : 491 : rte = (RangeTblEntry *) lfirst(l);
6964 : :
7077 mail@joeconway.com 6965 [ + + ]: 491 : if (rte->rtekind == RTE_SUBQUERY)
6966 : : {
6967 [ - + ]: 25 : if (select_rte)
7077 mail@joeconway.com 6968 [ # # ]:UBC 0 : elog(ERROR, "too many subquery RTEs in INSERT");
7077 mail@joeconway.com 6969 :CBC 25 : select_rte = rte;
6970 : : }
6971 : :
6972 [ + + ]: 491 : if (rte->rtekind == RTE_VALUES)
6973 : : {
6974 [ - + ]: 22 : if (values_rte)
7077 mail@joeconway.com 6975 [ # # ]:UBC 0 : elog(ERROR, "too many values RTEs in INSERT");
7077 mail@joeconway.com 6976 :CBC 22 : values_rte = rte;
6977 : : }
6978 : : }
6979 [ + + - + ]: 170 : if (select_rte && values_rte)
7077 mail@joeconway.com 6980 [ # # ]:UBC 0 : elog(ERROR, "both subquery and values RTEs in INSERT");
6981 : :
6982 : : /*
6983 : : * Start the query with INSERT INTO relname
6984 : : */
9544 tgl@sss.pgh.pa.us 6985 :CBC 170 : rte = rt_fetch(query->resultRelation, query->rtable);
8629 6986 [ - + ]: 170 : Assert(rte->rtekind == RTE_RELATION);
6987 : :
8176 6988 [ + - ]: 170 : if (PRETTY_INDENT(context))
6989 : : {
8171 bruce@momjian.us 6990 : 170 : context->indentLevel += PRETTYINDENT_STD;
6991 : 170 : appendStringInfoChar(buf, ' ');
6992 : : }
1034 tgl@sss.pgh.pa.us 6993 : 170 : appendStringInfo(buf, "INSERT INTO %s",
6994 : : generate_relation_name(rte->relid, NIL));
6995 : :
6996 : : /* Print the relation alias, if needed; INSERT requires explicit AS */
6997 : 170 : get_rte_alias(rte, query->resultRelation, true, context);
6998 : :
6999 : : /* always want a space here */
7000 : 170 : appendStringInfoChar(buf, ' ');
7001 : :
7002 : : /*
7003 : : * Add the insert-column-names list. Any indirection decoration needed on
7004 : : * the column names can be inferred from the top targetlist.
7005 : : */
7861 7006 : 170 : strippedexprs = NIL;
7007 : 170 : sep = "";
4807 7008 [ + - ]: 170 : if (query->targetList)
7009 : 170 : appendStringInfoChar(buf, '(');
9969 bruce@momjian.us 7010 [ + - + + : 621 : foreach(l, query->targetList)
+ + ]
7011 : : {
9227 tgl@sss.pgh.pa.us 7012 : 451 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7013 : :
7560 7014 [ - + ]: 451 : if (tle->resjunk)
9210 tgl@sss.pgh.pa.us 7015 :UBC 0 : continue; /* ignore junk entries */
7016 : :
7536 neilc@samurai.com 7017 :CBC 451 : appendStringInfoString(buf, sep);
9969 bruce@momjian.us 7018 : 451 : sep = ", ";
7019 : :
7020 : : /*
7021 : : * Put out name of target column; look in the catalogs, not at
7022 : : * tle->resname, since resname will fail to track RENAME.
7023 : : */
7991 neilc@samurai.com 7024 : 451 : appendStringInfoString(buf,
2865 alvherre@alvh.no-ip. 7025 : 451 : quote_identifier(get_attname(rte->relid,
7026 : 451 : tle->resno,
7027 : : false)));
7028 : :
7029 : : /*
7030 : : * Print any indirection needed (subfields or subscripts), and strip
7031 : : * off the top-level nodes representing the indirection assignments.
7032 : : * Add the stripped expressions to strippedexprs. (If it's a
7033 : : * single-VALUES statement, the stripped expressions are the VALUES to
7034 : : * print below. Otherwise they're just Vars and not really
7035 : : * interesting.)
7036 : : */
3423 tgl@sss.pgh.pa.us 7037 : 451 : strippedexprs = lappend(strippedexprs,
7038 : 451 : processIndirection((Node *) tle->expr,
7039 : : context));
7040 : : }
4807 7041 [ + - ]: 170 : if (query->targetList)
4430 rhaas@postgresql.org 7042 : 170 : appendStringInfoString(buf, ") ");
7043 : :
3177 peter_e@gmx.net 7044 [ - + ]: 170 : if (query->override)
7045 : : {
3177 peter_e@gmx.net 7046 [ # # ]:UBC 0 : if (query->override == OVERRIDING_SYSTEM_VALUE)
7047 : 0 : appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
7048 [ # # ]: 0 : else if (query->override == OVERRIDING_USER_VALUE)
7049 : 0 : appendStringInfoString(buf, "OVERRIDING USER VALUE ");
7050 : : }
7051 : :
7077 mail@joeconway.com 7052 [ + + ]:CBC 170 : if (select_rte)
7053 : : {
7054 : : /* Add the SELECT */
1491 tgl@sss.pgh.pa.us 7055 : 25 : get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
7056 : : false,
7057 : : context->prettyFlags, context->wrapColumn,
7058 : : context->indentLevel);
7059 : : }
7077 mail@joeconway.com 7060 [ + + ]: 145 : else if (values_rte)
7061 : : {
7062 : : /* Add the multi-VALUES expression lists */
7063 : 22 : get_values_def(values_rte->values_lists, context);
7064 : : }
4807 tgl@sss.pgh.pa.us 7065 [ + - ]: 123 : else if (strippedexprs)
7066 : : {
7067 : : /* Add the single-VALUES expression list */
8176 7068 : 123 : appendContextKeyword(context, "VALUES (",
7069 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
1434 7070 : 123 : get_rule_list_toplevel(strippedexprs, context, false);
9432 7071 : 123 : appendStringInfoChar(buf, ')');
7072 : : }
7073 : : else
7074 : : {
7075 : : /* No expressions, so it must be DEFAULT VALUES */
4430 rhaas@postgresql.org 7076 :UBC 0 : appendStringInfoString(buf, "DEFAULT VALUES");
7077 : : }
7078 : :
7079 : : /* Add ON CONFLICT if present */
3876 andres@anarazel.de 7080 [ + + ]:CBC 170 : if (query->onConflict)
7081 : : {
7082 : 15 : OnConflictExpr *confl = query->onConflict;
7083 : :
3821 heikki.linnakangas@i 7084 : 15 : appendStringInfoString(buf, " ON CONFLICT");
7085 : :
3865 andres@anarazel.de 7086 [ + + ]: 15 : if (confl->arbiterElems)
7087 : : {
7088 : : /* Add the single-VALUES expression list */
7089 : 12 : appendStringInfoChar(buf, '(');
7090 : 12 : get_rule_expr((Node *) confl->arbiterElems, context, false);
7091 : 12 : appendStringInfoChar(buf, ')');
7092 : :
7093 : : /* Add a WHERE clause (for partial indexes) if given */
7094 [ + + ]: 12 : if (confl->arbiterWhere != NULL)
7095 : : {
7096 : : bool save_varprefix;
7097 : :
7098 : : /*
7099 : : * Force non-prefixing of Vars, since parser assumes that they
7100 : : * belong to target relation. WHERE clause does not use
7101 : : * InferenceElem, so this is separately required.
7102 : : */
3601 tgl@sss.pgh.pa.us 7103 : 6 : save_varprefix = context->varprefix;
7104 : 6 : context->varprefix = false;
7105 : :
3865 andres@anarazel.de 7106 : 6 : appendContextKeyword(context, " WHERE ",
7107 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7108 : 6 : get_rule_expr(confl->arbiterWhere, context, false);
7109 : :
3601 tgl@sss.pgh.pa.us 7110 : 6 : context->varprefix = save_varprefix;
7111 : : }
7112 : : }
3507 7113 [ - + ]: 3 : else if (OidIsValid(confl->constraint))
7114 : : {
3861 bruce@momjian.us 7115 :UBC 0 : char *constraint = get_constraint_name(confl->constraint);
7116 : :
3507 tgl@sss.pgh.pa.us 7117 [ # # ]: 0 : if (!constraint)
7118 [ # # ]: 0 : elog(ERROR, "cache lookup failed for constraint %u",
7119 : : confl->constraint);
3865 andres@anarazel.de 7120 : 0 : appendStringInfo(buf, " ON CONSTRAINT %s",
7121 : : quote_identifier(constraint));
7122 : : }
7123 : :
3876 andres@anarazel.de 7124 [ + + ]:CBC 15 : if (confl->action == ONCONFLICT_NOTHING)
7125 : : {
3865 7126 : 9 : appendStringInfoString(buf, " DO NOTHING");
7127 : : }
7128 : : else
7129 : : {
7130 : 6 : appendStringInfoString(buf, " DO UPDATE SET ");
7131 : : /* Deparse targetlist */
3876 7132 : 6 : get_update_query_targetlist_def(query, confl->onConflictSet,
7133 : : context, rte);
7134 : :
7135 : : /* Add a WHERE clause if given */
7136 [ + - ]: 6 : if (confl->onConflictWhere != NULL)
7137 : : {
7138 : 6 : appendContextKeyword(context, " WHERE ",
7139 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7140 : 6 : get_rule_expr(confl->onConflictWhere, context, false);
7141 : : }
7142 : : }
7143 : : }
7144 : :
7145 : : /* Add RETURNING if present */
7067 tgl@sss.pgh.pa.us 7146 [ + + ]: 170 : if (query->returningList)
335 dean.a.rasheed@gmail 7147 : 39 : get_returning_clause(query, context);
9977 bruce@momjian.us 7148 : 170 : }
7149 : :
7150 : :
7151 : : /* ----------
7152 : : * get_update_query_def - Parse back an UPDATE parsetree
7153 : : * ----------
7154 : : */
7155 : : static void
475 tgl@sss.pgh.pa.us 7156 : 77 : get_update_query_def(Query *query, deparse_context *context)
7157 : : {
7780 bruce@momjian.us 7158 : 77 : StringInfo buf = context->buf;
7159 : : RangeTblEntry *rte;
7160 : :
7161 : : /* Insert the WITH clause if given */
5542 tgl@sss.pgh.pa.us 7162 : 77 : get_with_clause(query, context);
7163 : :
7164 : : /*
7165 : : * Start the query with UPDATE relname SET
7166 : : */
9544 7167 : 77 : rte = rt_fetch(query->resultRelation, query->rtable);
8629 7168 [ - + ]: 77 : Assert(rte->rtekind == RTE_RELATION);
8176 7169 [ + - ]: 77 : if (PRETTY_INDENT(context))
7170 : : {
8171 bruce@momjian.us 7171 : 77 : appendStringInfoChar(buf, ' ');
7172 : 77 : context->indentLevel += PRETTYINDENT_STD;
7173 : : }
6437 tgl@sss.pgh.pa.us 7174 : 154 : appendStringInfo(buf, "UPDATE %s%s",
9321 7175 [ + - ]: 77 : only_marker(rte),
7176 : : generate_relation_name(rte->relid, NIL));
7177 : :
7178 : : /* Print the relation alias, if needed */
1034 7179 : 77 : get_rte_alias(rte, query->resultRelation, false, context);
7180 : :
6437 7181 : 77 : appendStringInfoString(buf, " SET ");
7182 : :
7183 : : /* Deparse targetlist */
3876 andres@anarazel.de 7184 : 77 : get_update_query_targetlist_def(query, query->targetList, context, rte);
7185 : :
7186 : : /* Add the FROM clause if needed */
7187 : 77 : get_from_clause(query, " FROM ", context);
7188 : :
7189 : : /* Add a WHERE clause if given */
7190 [ + + ]: 77 : if (query->jointree->quals != NULL)
7191 : : {
7192 : 57 : appendContextKeyword(context, " WHERE ",
7193 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
7194 : 57 : get_rule_expr(query->jointree->quals, context, false);
7195 : : }
7196 : :
7197 : : /* Add RETURNING if present */
7198 [ + + ]: 77 : if (query->returningList)
335 dean.a.rasheed@gmail 7199 : 23 : get_returning_clause(query, context);
3876 andres@anarazel.de 7200 : 77 : }
7201 : :
7202 : :
7203 : : /* ----------
7204 : : * get_update_query_targetlist_def - Parse back an UPDATE targetlist
7205 : : * ----------
7206 : : */
7207 : : static void
7208 : 95 : get_update_query_targetlist_def(Query *query, List *targetList,
7209 : : deparse_context *context, RangeTblEntry *rte)
7210 : : {
7211 : 95 : StringInfo buf = context->buf;
7212 : : ListCell *l;
7213 : : ListCell *next_ma_cell;
7214 : : int remaining_ma_columns;
7215 : : const char *sep;
7216 : : SubLink *cur_ma_sublink;
7217 : : List *ma_sublinks;
7218 : :
7219 : : /*
7220 : : * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
7221 : : * into a list. We expect them to appear, in ID order, in resjunk tlist
7222 : : * entries.
7223 : : */
4200 tgl@sss.pgh.pa.us 7224 : 95 : ma_sublinks = NIL;
7225 [ + + ]: 95 : if (query->hasSubLinks) /* else there can't be any */
7226 : : {
3876 andres@anarazel.de 7227 [ + - + + : 21 : foreach(l, targetList)
+ + ]
7228 : : {
4200 tgl@sss.pgh.pa.us 7229 : 15 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7230 : :
7231 [ + + + - ]: 15 : if (tle->resjunk && IsA(tle->expr, SubLink))
7232 : : {
7233 : 3 : SubLink *sl = (SubLink *) tle->expr;
7234 : :
7235 [ + - ]: 3 : if (sl->subLinkType == MULTIEXPR_SUBLINK)
7236 : : {
7237 : 3 : ma_sublinks = lappend(ma_sublinks, sl);
7238 [ - + ]: 3 : Assert(sl->subLinkId == list_length(ma_sublinks));
7239 : : }
7240 : : }
7241 : : }
7242 : : }
7243 : 95 : next_ma_cell = list_head(ma_sublinks);
7244 : 95 : cur_ma_sublink = NULL;
7245 : 95 : remaining_ma_columns = 0;
7246 : :
7247 : : /* Add the comma separated list of 'attname = value' */
9969 bruce@momjian.us 7248 : 95 : sep = "";
3876 andres@anarazel.de 7249 [ + - + + : 244 : foreach(l, targetList)
+ + ]
7250 : : {
9210 tgl@sss.pgh.pa.us 7251 : 149 : TargetEntry *tle = (TargetEntry *) lfirst(l);
7252 : : Node *expr;
7253 : :
7560 7254 [ + + ]: 149 : if (tle->resjunk)
9210 7255 : 3 : continue; /* ignore junk entries */
7256 : :
7257 : : /* Emit separator (OK whether we're in multiassignment or not) */
7536 neilc@samurai.com 7258 : 146 : appendStringInfoString(buf, sep);
9969 bruce@momjian.us 7259 : 146 : sep = ", ";
7260 : :
7261 : : /*
7262 : : * Check to see if we're starting a multiassignment group: if so,
7263 : : * output a left paren.
7264 : : */
4200 tgl@sss.pgh.pa.us 7265 [ + + + - ]: 146 : if (next_ma_cell != NULL && cur_ma_sublink == NULL)
7266 : : {
7267 : : /*
7268 : : * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
7269 : : * Param. That could be buried under FieldStores and
7270 : : * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
7271 : : * and underneath those there could be an implicit type coercion.
7272 : : * Because we would ignore implicit type coercions anyway, we
7273 : : * don't need to be as careful as processIndirection() is about
7274 : : * descending past implicit CoerceToDomains.
7275 : : */
7276 : 3 : expr = (Node *) tle->expr;
7277 [ + - ]: 6 : while (expr)
7278 : : {
7279 [ - + ]: 6 : if (IsA(expr, FieldStore))
7280 : : {
4200 tgl@sss.pgh.pa.us 7281 :UBC 0 : FieldStore *fstore = (FieldStore *) expr;
7282 : :
7283 : 0 : expr = (Node *) linitial(fstore->newvals);
7284 : : }
2511 alvherre@alvh.no-ip. 7285 [ + + ]:CBC 6 : else if (IsA(expr, SubscriptingRef))
7286 : : {
7287 : 3 : SubscriptingRef *sbsref = (SubscriptingRef *) expr;
7288 : :
7289 [ - + ]: 3 : if (sbsref->refassgnexpr == NULL)
4200 tgl@sss.pgh.pa.us 7290 :UBC 0 : break;
7291 : :
2511 alvherre@alvh.no-ip. 7292 :CBC 3 : expr = (Node *) sbsref->refassgnexpr;
7293 : : }
3080 tgl@sss.pgh.pa.us 7294 [ - + ]: 3 : else if (IsA(expr, CoerceToDomain))
7295 : : {
3080 tgl@sss.pgh.pa.us 7296 :UBC 0 : CoerceToDomain *cdomain = (CoerceToDomain *) expr;
7297 : :
7298 [ # # ]: 0 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
7299 : 0 : break;
7300 : 0 : expr = (Node *) cdomain->arg;
7301 : : }
7302 : : else
4200 tgl@sss.pgh.pa.us 7303 :CBC 3 : break;
7304 : : }
7305 : 3 : expr = strip_implicit_coercions(expr);
7306 : :
7307 [ + - + - ]: 3 : if (expr && IsA(expr, Param) &&
7308 [ + - ]: 3 : ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
7309 : : {
7310 : 3 : cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
2347 7311 : 3 : next_ma_cell = lnext(ma_sublinks, next_ma_cell);
2148 alvherre@alvh.no-ip. 7312 : 3 : remaining_ma_columns = count_nonjunk_tlist_entries(((Query *) cur_ma_sublink->subselect)->targetList);
4200 tgl@sss.pgh.pa.us 7313 [ - + ]: 3 : Assert(((Param *) expr)->paramid ==
7314 : : ((cur_ma_sublink->subLinkId << 16) | 1));
7315 : 3 : appendStringInfoChar(buf, '(');
7316 : : }
7317 : : }
7318 : :
7319 : : /*
7320 : : * Put out name of target column; look in the catalogs, not at
7321 : : * tle->resname, since resname will fail to track RENAME.
7322 : : */
7861 7323 : 146 : appendStringInfoString(buf,
2865 alvherre@alvh.no-ip. 7324 : 146 : quote_identifier(get_attname(rte->relid,
7325 : 146 : tle->resno,
7326 : : false)));
7327 : :
7328 : : /*
7329 : : * Print any indirection needed (subfields or subscripts), and strip
7330 : : * off the top-level nodes representing the indirection assignments.
7331 : : */
3423 tgl@sss.pgh.pa.us 7332 : 146 : expr = processIndirection((Node *) tle->expr, context);
7333 : :
7334 : : /*
7335 : : * If we're in a multiassignment, skip printing anything more, unless
7336 : : * this is the last column; in which case, what we print should be the
7337 : : * sublink, not the Param.
7338 : : */
4200 7339 [ + + ]: 146 : if (cur_ma_sublink != NULL)
7340 : : {
7341 [ + + ]: 9 : if (--remaining_ma_columns > 0)
7342 : 6 : continue; /* not the last column of multiassignment */
7343 : 3 : appendStringInfoChar(buf, ')');
7344 : 3 : expr = (Node *) cur_ma_sublink;
7345 : 3 : cur_ma_sublink = NULL;
7346 : : }
7347 : :
4430 rhaas@postgresql.org 7348 : 140 : appendStringInfoString(buf, " = ");
7349 : :
7861 tgl@sss.pgh.pa.us 7350 : 140 : get_rule_expr(expr, context, false);
7351 : : }
9977 bruce@momjian.us 7352 : 95 : }
7353 : :
7354 : :
7355 : : /* ----------
7356 : : * get_delete_query_def - Parse back a DELETE parsetree
7357 : : * ----------
7358 : : */
7359 : : static void
475 tgl@sss.pgh.pa.us 7360 : 38 : get_delete_query_def(Query *query, deparse_context *context)
7361 : : {
9572 7362 : 38 : StringInfo buf = context->buf;
7363 : : RangeTblEntry *rte;
7364 : :
7365 : : /* Insert the WITH clause if given */
5542 7366 : 38 : get_with_clause(query, context);
7367 : :
7368 : : /*
7369 : : * Start the query with DELETE FROM relname
7370 : : */
9544 7371 : 38 : rte = rt_fetch(query->resultRelation, query->rtable);
8629 7372 [ - + ]: 38 : Assert(rte->rtekind == RTE_RELATION);
8176 7373 [ + - ]: 38 : if (PRETTY_INDENT(context))
7374 : : {
8171 bruce@momjian.us 7375 : 38 : appendStringInfoChar(buf, ' ');
6437 tgl@sss.pgh.pa.us 7376 : 38 : context->indentLevel += PRETTYINDENT_STD;
7377 : : }
9544 7378 : 76 : appendStringInfo(buf, "DELETE FROM %s%s",
9321 7379 [ + - ]: 38 : only_marker(rte),
7380 : : generate_relation_name(rte->relid, NIL));
7381 : :
7382 : : /* Print the relation alias, if needed */
1034 7383 : 38 : get_rte_alias(rte, query->resultRelation, false, context);
7384 : :
7385 : : /* Add the USING clause if given */
7443 7386 : 38 : get_from_clause(query, " USING ", context);
7387 : :
7388 : : /* Add a WHERE clause if given */
9210 7389 [ + - ]: 38 : if (query->jointree->quals != NULL)
7390 : : {
8176 7391 : 38 : appendContextKeyword(context, " WHERE ",
7392 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
8490 7393 : 38 : get_rule_expr(query->jointree->quals, context, false);
7394 : : }
7395 : :
7396 : : /* Add RETURNING if present */
7067 7397 [ + + ]: 38 : if (query->returningList)
335 dean.a.rasheed@gmail 7398 : 8 : get_returning_clause(query, context);
9977 bruce@momjian.us 7399 : 38 : }
7400 : :
7401 : :
7402 : : /* ----------
7403 : : * get_merge_query_def - Parse back a MERGE parsetree
7404 : : * ----------
7405 : : */
7406 : : static void
475 tgl@sss.pgh.pa.us 7407 : 6 : get_merge_query_def(Query *query, deparse_context *context)
7408 : : {
955 7409 : 6 : StringInfo buf = context->buf;
7410 : : RangeTblEntry *rte;
7411 : : ListCell *lc;
7412 : : bool haveNotMatchedBySource;
7413 : :
7414 : : /* Insert the WITH clause if given */
7415 : 6 : get_with_clause(query, context);
7416 : :
7417 : : /*
7418 : : * Start the query with MERGE INTO relname
7419 : : */
7420 : 6 : rte = rt_fetch(query->resultRelation, query->rtable);
7421 [ - + ]: 6 : Assert(rte->rtekind == RTE_RELATION);
7422 [ + - ]: 6 : if (PRETTY_INDENT(context))
7423 : : {
7424 : 6 : appendStringInfoChar(buf, ' ');
7425 : 6 : context->indentLevel += PRETTYINDENT_STD;
7426 : : }
7427 : 12 : appendStringInfo(buf, "MERGE INTO %s%s",
7428 [ + - ]: 6 : only_marker(rte),
7429 : : generate_relation_name(rte->relid, NIL));
7430 : :
7431 : : /* Print the relation alias, if needed */
7432 : 6 : get_rte_alias(rte, query->resultRelation, false, context);
7433 : :
7434 : : /* Print the source relation and join clause */
7435 : 6 : get_from_clause(query, " USING ", context);
7436 : 6 : appendContextKeyword(context, " ON ",
7437 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
627 dean.a.rasheed@gmail 7438 : 6 : get_rule_expr(query->mergeJoinCondition, context, false);
7439 : :
7440 : : /*
7441 : : * Test for any NOT MATCHED BY SOURCE actions. If there are none, then
7442 : : * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
7443 : : * SQL standard. Otherwise, we have a non-SQL-standard query, so output
7444 : : * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
7445 : : * more explicit.
7446 : : */
7447 : 6 : haveNotMatchedBySource = false;
7448 [ + - + + : 42 : foreach(lc, query->mergeActionList)
+ + ]
7449 : : {
7450 : 39 : MergeAction *action = lfirst_node(MergeAction, lc);
7451 : :
7452 [ + + ]: 39 : if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
7453 : : {
7454 : 3 : haveNotMatchedBySource = true;
7455 : 3 : break;
7456 : : }
7457 : : }
7458 : :
7459 : : /* Print each merge action */
955 tgl@sss.pgh.pa.us 7460 [ + - + + : 45 : foreach(lc, query->mergeActionList)
+ + ]
7461 : : {
7462 : 39 : MergeAction *action = lfirst_node(MergeAction, lc);
7463 : :
7464 : 39 : appendContextKeyword(context, " WHEN ",
7465 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
627 dean.a.rasheed@gmail 7466 [ + + + - ]: 39 : switch (action->matchKind)
7467 : : {
7468 : 18 : case MERGE_WHEN_MATCHED:
616 drowley@postgresql.o 7469 : 18 : appendStringInfoString(buf, "MATCHED");
627 dean.a.rasheed@gmail 7470 : 18 : break;
7471 : 3 : case MERGE_WHEN_NOT_MATCHED_BY_SOURCE:
616 drowley@postgresql.o 7472 : 3 : appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
627 dean.a.rasheed@gmail 7473 : 3 : break;
7474 : 18 : case MERGE_WHEN_NOT_MATCHED_BY_TARGET:
7475 [ + + ]: 18 : if (haveNotMatchedBySource)
616 drowley@postgresql.o 7476 : 3 : appendStringInfoString(buf, "NOT MATCHED BY TARGET");
7477 : : else
7478 : 15 : appendStringInfoString(buf, "NOT MATCHED");
627 dean.a.rasheed@gmail 7479 : 18 : break;
627 dean.a.rasheed@gmail 7480 :UBC 0 : default:
7481 [ # # ]: 0 : elog(ERROR, "unrecognized matchKind: %d",
7482 : : (int) action->matchKind);
7483 : : }
7484 : :
955 tgl@sss.pgh.pa.us 7485 [ + + ]:CBC 39 : if (action->qual)
7486 : : {
7487 : 24 : appendContextKeyword(context, " AND ",
7488 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7489 : 24 : get_rule_expr(action->qual, context, false);
7490 : : }
7491 : 39 : appendContextKeyword(context, " THEN ",
7492 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
7493 : :
7494 [ + + ]: 39 : if (action->commandType == CMD_INSERT)
7495 : : {
7496 : : /* This generally matches get_insert_query_def() */
7497 : 18 : List *strippedexprs = NIL;
7498 : 18 : const char *sep = "";
7499 : : ListCell *lc2;
7500 : :
7501 : 18 : appendStringInfoString(buf, "INSERT");
7502 : :
7503 [ + + ]: 18 : if (action->targetList)
7504 : 15 : appendStringInfoString(buf, " (");
7505 [ + + + + : 51 : foreach(lc2, action->targetList)
+ + ]
7506 : : {
7507 : 33 : TargetEntry *tle = (TargetEntry *) lfirst(lc2);
7508 : :
7509 [ - + ]: 33 : Assert(!tle->resjunk);
7510 : :
7511 : 33 : appendStringInfoString(buf, sep);
7512 : 33 : sep = ", ";
7513 : :
7514 : 33 : appendStringInfoString(buf,
7515 : 33 : quote_identifier(get_attname(rte->relid,
7516 : 33 : tle->resno,
7517 : : false)));
7518 : 33 : strippedexprs = lappend(strippedexprs,
7519 : 33 : processIndirection((Node *) tle->expr,
7520 : : context));
7521 : : }
7522 [ + + ]: 18 : if (action->targetList)
7523 : 15 : appendStringInfoChar(buf, ')');
7524 : :
7525 [ + + ]: 18 : if (action->override)
7526 : : {
7527 [ - + ]: 3 : if (action->override == OVERRIDING_SYSTEM_VALUE)
955 tgl@sss.pgh.pa.us 7528 :UBC 0 : appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
955 tgl@sss.pgh.pa.us 7529 [ + - ]:CBC 3 : else if (action->override == OVERRIDING_USER_VALUE)
7530 : 3 : appendStringInfoString(buf, " OVERRIDING USER VALUE");
7531 : : }
7532 : :
7533 [ + + ]: 18 : if (strippedexprs)
7534 : : {
7535 : 15 : appendContextKeyword(context, " VALUES (",
7536 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);
7537 : 15 : get_rule_list_toplevel(strippedexprs, context, false);
7538 : 15 : appendStringInfoChar(buf, ')');
7539 : : }
7540 : : else
7541 : 3 : appendStringInfoString(buf, " DEFAULT VALUES");
7542 : : }
7543 [ + + ]: 21 : else if (action->commandType == CMD_UPDATE)
7544 : : {
7545 : 12 : appendStringInfoString(buf, "UPDATE SET ");
7546 : 12 : get_update_query_targetlist_def(query, action->targetList,
7547 : : context, rte);
7548 : : }
7549 [ + + ]: 9 : else if (action->commandType == CMD_DELETE)
7550 : 6 : appendStringInfoString(buf, "DELETE");
7551 [ + - ]: 3 : else if (action->commandType == CMD_NOTHING)
7552 : 3 : appendStringInfoString(buf, "DO NOTHING");
7553 : : }
7554 : :
7555 : : /* Add RETURNING if present */
640 dean.a.rasheed@gmail 7556 [ + + ]: 6 : if (query->returningList)
335 7557 : 3 : get_returning_clause(query, context);
955 tgl@sss.pgh.pa.us 7558 : 6 : }
7559 : :
7560 : :
7561 : : /* ----------
7562 : : * get_utility_query_def - Parse back a UTILITY parsetree
7563 : : * ----------
7564 : : */
7565 : : static void
9114 7566 : 8 : get_utility_query_def(Query *query, deparse_context *context)
7567 : : {
7568 : 8 : StringInfo buf = context->buf;
7569 : :
7570 [ + - + - ]: 8 : if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7571 : 8 : {
7572 : 8 : NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7573 : :
8176 7574 : 8 : appendContextKeyword(context, "",
7575 : : 0, PRETTYINDENT_STD, 1);
8671 7576 : 8 : appendStringInfo(buf, "NOTIFY %s",
6316 7577 : 8 : quote_identifier(stmt->conditionname));
5783 7578 [ - + ]: 8 : if (stmt->payload)
7579 : : {
5783 tgl@sss.pgh.pa.us 7580 :UBC 0 : appendStringInfoString(buf, ", ");
7581 : 0 : simple_quote_literal(buf, stmt->payload);
7582 : : }
7583 : : }
7584 : : else
7585 : : {
7586 : : /* Currently only NOTIFY utility commands can appear in rules */
8179 7587 [ # # ]: 0 : elog(ERROR, "unexpected utility statement type");
7588 : : }
9114 tgl@sss.pgh.pa.us 7589 :CBC 8 : }
7590 : :
7591 : : /*
7592 : : * Display a Var appropriately.
7593 : : *
7594 : : * In some cases (currently only when recursing into an unnamed join)
7595 : : * the Var's varlevelsup has to be interpreted with respect to a context
7596 : : * above the current one; levelsup indicates the offset.
7597 : : *
7598 : : * If istoplevel is true, the Var is at the top level of a SELECT's
7599 : : * targetlist, which means we need special treatment of whole-row Vars.
7600 : : * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
7601 : : * dirty hack to prevent "tab.*" from being expanded into multiple columns.
7602 : : * (The parser will strip the useless coercion, so no inefficiency is added in
7603 : : * dump and reload.) We used to print just "tab" in such cases, but that is
7604 : : * ambiguous and will yield the wrong result if "tab" is also a plain column
7605 : : * name in the query.
7606 : : *
7607 : : * Returns the attname of the Var, or NULL if the Var has no attname (because
7608 : : * it is a whole-row Var or a subplan output reference).
7609 : : */
7610 : : static char *
4982 7611 : 94795 : get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
7612 : : {
6872 7613 : 94795 : StringInfo buf = context->buf;
7614 : : RangeTblEntry *rte;
7615 : : AttrNumber attnum;
7616 : : int netlevelsup;
7617 : : deparse_namespace *dpns;
7618 : : int varno;
7619 : : AttrNumber varattno;
7620 : : deparse_columns *colinfo;
7621 : : char *refname;
7622 : : char *attname;
7623 : : bool need_prefix;
7624 : :
7625 : : /* Find appropriate nesting depth */
7643 7626 : 94795 : netlevelsup = var->varlevelsup + levelsup;
7627 [ - + ]: 94795 : if (netlevelsup >= list_length(context->namespaces))
7643 tgl@sss.pgh.pa.us 7628 [ # # ]:UBC 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
7629 : : var->varlevelsup, levelsup);
7875 tgl@sss.pgh.pa.us 7630 :CBC 94795 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7631 : : netlevelsup);
7632 : :
7633 : : /*
7634 : : * If we have a syntactic referent for the Var, and we're working from a
7635 : : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
7636 : : * on the semantic referent. (Forcing use of the semantic referent when
7637 : : * printing plan trees is a design choice that's perhaps more motivated by
7638 : : * backwards compatibility than anything else. But it does have the
7639 : : * advantage of making plans more explicit.)
7640 : : */
2169 7641 [ + + + + ]: 94795 : if (var->varnosyn > 0 && dpns->plan == NULL)
7642 : : {
7643 : 19373 : varno = var->varnosyn;
7644 : 19373 : varattno = var->varattnosyn;
7645 : : }
7646 : : else
7647 : : {
7648 : 75422 : varno = var->varno;
7649 : 75422 : varattno = var->varattno;
7650 : : }
7651 : :
7652 : : /*
7653 : : * Try to find the relevant RTE in this rtable. In a plan tree, it's
7654 : : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
7655 : : * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
7656 : : * find the aliases previously assigned for this RTE.
7657 : : */
7658 [ + + + - ]: 94795 : if (varno >= 1 && varno <= list_length(dpns->rtable))
7659 : : {
7660 : : /*
7661 : : * We might have been asked to map child Vars to some parent relation.
7662 : : */
2198 7663 [ + + + + ]: 69210 : if (context->appendparents && dpns->appendrels)
7664 : : {
1554 7665 : 1939 : int pvarno = varno;
2198 7666 : 1939 : AttrNumber pvarattno = varattno;
7667 : 1939 : AppendRelInfo *appinfo = dpns->appendrels[pvarno];
7668 : 1939 : bool found = false;
7669 : :
7670 : : /* Only map up to inheritance parents, not UNION ALL appendrels */
7671 [ + + ]: 3915 : while (appinfo &&
7672 : 2151 : rt_fetch(appinfo->parent_relid,
7673 [ + + ]: 2151 : dpns->rtable)->rtekind == RTE_RELATION)
7674 : : {
7675 : 1976 : found = false;
7676 [ + + ]: 1976 : if (pvarattno > 0) /* system columns stay as-is */
7677 : : {
7678 [ - + ]: 1837 : if (pvarattno > appinfo->num_child_cols)
2198 tgl@sss.pgh.pa.us 7679 :UBC 0 : break; /* safety check */
2198 tgl@sss.pgh.pa.us 7680 :CBC 1837 : pvarattno = appinfo->parent_colnos[pvarattno - 1];
7681 [ - + ]: 1837 : if (pvarattno == 0)
2198 tgl@sss.pgh.pa.us 7682 :UBC 0 : break; /* Var is local to child */
7683 : : }
7684 : :
2198 tgl@sss.pgh.pa.us 7685 :CBC 1976 : pvarno = appinfo->parent_relid;
7686 : 1976 : found = true;
7687 : :
7688 : : /* If the parent is itself a child, continue up. */
7689 [ + - - + ]: 1976 : Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
7690 : 1976 : appinfo = dpns->appendrels[pvarno];
7691 : : }
7692 : :
7693 : : /*
7694 : : * If we found an ancestral rel, and that rel is included in
7695 : : * appendparents, print that column not the original one.
7696 : : */
7697 [ + + + + ]: 1939 : if (found && bms_is_member(pvarno, context->appendparents))
7698 : : {
7699 : 1581 : varno = pvarno;
7700 : 1581 : varattno = pvarattno;
7701 : : }
7702 : : }
7703 : :
7704 : 69210 : rte = rt_fetch(varno, dpns->rtable);
7705 : :
7706 : : /* might be returning old/new column value */
335 dean.a.rasheed@gmail 7707 [ + + ]: 69210 : if (var->varreturningtype == VAR_RETURNING_OLD)
7708 : 208 : refname = dpns->ret_old_alias;
7709 [ + + ]: 69002 : else if (var->varreturningtype == VAR_RETURNING_NEW)
7710 : 207 : refname = dpns->ret_new_alias;
7711 : : else
7712 : 68795 : refname = (char *) list_nth(dpns->rtable_names, varno - 1);
7713 : :
2198 tgl@sss.pgh.pa.us 7714 : 69210 : colinfo = deparse_columns_fetch(varno, dpns);
7715 : 69210 : attnum = varattno;
7716 : : }
7717 : : else
7718 : : {
7719 : 25585 : resolve_special_varno((Node *) var, context,
7720 : : get_special_variable, NULL);
3521 rhaas@postgresql.org 7721 : 25585 : return NULL;
7722 : : }
7723 : :
7724 : : /*
7725 : : * The planner will sometimes emit Vars referencing resjunk elements of a
7726 : : * subquery's target list (this is currently only possible if it chooses
7727 : : * to generate a "physical tlist" for a SubqueryScan or CteScan node).
7728 : : * Although we prefer to print subquery-referencing Vars using the
7729 : : * subquery's alias, that's not possible for resjunk items since they have
7730 : : * no alias. So in that case, drill down to the subplan and print the
7731 : : * contents of the referenced tlist item. This works because in a plan
7732 : : * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
7733 : : * we'll have set dpns->inner_plan to reference the child plan node.
7734 : : */
5640 tgl@sss.pgh.pa.us 7735 [ + + + + : 71366 : if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
+ + ]
7736 : 2156 : attnum > list_length(rte->eref->colnames) &&
2198 7737 [ + - ]: 1 : dpns->inner_plan)
7738 : : {
7739 : : TargetEntry *tle;
7740 : : deparse_namespace save_dpns;
7741 : :
7742 : 1 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
5640 7743 [ - + ]: 1 : if (!tle)
3456 tgl@sss.pgh.pa.us 7744 [ # # ]:UBC 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
7745 : : attnum, rte->eref->aliasname);
7746 : :
5640 tgl@sss.pgh.pa.us 7747 [ - + ]:CBC 1 : Assert(netlevelsup == 0);
2198 7748 : 1 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7749 : :
7750 : : /*
7751 : : * Force parentheses because our caller probably assumed a Var is a
7752 : : * simple expression.
7753 : : */
5640 7754 [ - + ]: 1 : if (!IsA(tle->expr, Var))
5640 tgl@sss.pgh.pa.us 7755 :UBC 0 : appendStringInfoChar(buf, '(');
5640 tgl@sss.pgh.pa.us 7756 :CBC 1 : get_rule_expr((Node *) tle->expr, context, true);
7757 [ - + ]: 1 : if (!IsA(tle->expr, Var))
5640 tgl@sss.pgh.pa.us 7758 :UBC 0 : appendStringInfoChar(buf, ')');
7759 : :
5636 tgl@sss.pgh.pa.us 7760 :CBC 1 : pop_child_plan(dpns, &save_dpns);
5640 7761 : 1 : return NULL;
7762 : : }
7763 : :
7764 : : /*
7765 : : * If it's an unnamed join, look at the expansion of the alias variable.
7766 : : * If it's a simple reference to one of the input vars, then recursively
7767 : : * print the name of that var instead. When it's not a simple reference,
7768 : : * we have to just print the unqualified join column name. (This can only
7769 : : * happen with "dangerous" merged columns in a JOIN USING; we took pains
7770 : : * previously to make the unqualified column name unique in such cases.)
7771 : : *
7772 : : * This wouldn't work in decompiling plan trees, because we don't store
7773 : : * joinaliasvars lists after planning; but a plan tree should never
7774 : : * contain a join alias variable.
7775 : : */
4835 7776 [ + + + + ]: 69209 : if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
7777 : : {
7778 [ - + ]: 48 : if (rte->joinaliasvars == NIL)
4835 tgl@sss.pgh.pa.us 7779 [ # # ]:UBC 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
4835 tgl@sss.pgh.pa.us 7780 [ + - ]:CBC 48 : if (attnum > 0)
7781 : : {
7782 : : Var *aliasvar;
7783 : :
7784 : 48 : aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
7785 : : /* we intentionally don't strip implicit coercions here */
4530 7786 [ + - - + ]: 48 : if (aliasvar && IsA(aliasvar, Var))
7787 : : {
4835 tgl@sss.pgh.pa.us 7788 :UBC 0 : return get_variable(aliasvar, var->varlevelsup + levelsup,
7789 : : istoplevel, context);
7790 : : }
7791 : : }
7792 : :
7793 : : /*
7794 : : * Unnamed join has no refname. (Note: since it's unnamed, there is
7795 : : * no way the user could have referenced it to create a whole-row Var
7796 : : * for it. So we don't have to cover that case below.)
7797 : : */
4835 tgl@sss.pgh.pa.us 7798 [ - + ]:CBC 48 : Assert(refname == NULL);
7799 : : }
7800 : :
7193 7801 [ + + ]: 69209 : if (attnum == InvalidAttrNumber)
6872 7802 : 523 : attname = NULL;
4734 7803 [ + + ]: 68686 : else if (attnum > 0)
7804 : : {
7805 : : /* Get column name to use from the colinfo struct */
3456 7806 [ - + ]: 67699 : if (attnum > colinfo->num_cols)
3456 tgl@sss.pgh.pa.us 7807 [ # # ]:UBC 0 : elog(ERROR, "invalid attnum %d for relation \"%s\"",
7808 : : attnum, rte->eref->aliasname);
4734 tgl@sss.pgh.pa.us 7809 :CBC 67699 : attname = colinfo->colnames[attnum - 1];
7810 : :
7811 : : /*
7812 : : * If we find a Var referencing a dropped column, it seems better to
7813 : : * print something (anything) than to fail. In general this should
7814 : : * not happen, but it used to be possible for some cases involving
7815 : : * functions returning named composite types, and perhaps there are
7816 : : * still bugs out there.
7817 : : */
1245 7818 [ + + ]: 67699 : if (attname == NULL)
7819 : 3 : attname = "?dropped?column?";
7820 : : }
7821 : : else
7822 : : {
7823 : : /* System column - name is fixed, get it from the catalog */
6872 7824 : 987 : attname = get_rte_attribute_name(rte, attnum);
7825 : : }
7826 : :
335 dean.a.rasheed@gmail 7827 [ + + + + ]: 101921 : need_prefix = (context->varprefix || attname == NULL ||
7828 [ + + ]: 32712 : var->varreturningtype != VAR_RETURNING_DEFAULT);
7829 : :
7830 : : /*
7831 : : * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
7832 : : * clause, we may need to add a table-name prefix to prevent
7833 : : * findTargetlistEntrySQL92 from misinterpreting the name as an
7834 : : * output-column name. To avoid cluttering the output with unnecessary
7835 : : * prefixes, do so only if there is a name match to a SELECT tlist item
7836 : : * that is different from the Var.
7837 : : */
475 tgl@sss.pgh.pa.us 7838 [ + + + + : 69209 : if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
+ + ]
7839 : : {
7840 : 126 : int colno = 0;
7841 : :
7842 [ + + + + : 488 : foreach_node(TargetEntry, tle, context->targetList)
+ + ]
7843 : : {
7844 : : char *colname;
7845 : :
7846 [ - + ]: 242 : if (tle->resjunk)
475 tgl@sss.pgh.pa.us 7847 :UBC 0 : continue; /* ignore junk entries */
475 tgl@sss.pgh.pa.us 7848 :CBC 242 : colno++;
7849 : :
7850 : : /* This must match colname-choosing logic in get_target_list() */
7851 [ + - + - ]: 242 : if (context->resultDesc && colno <= context->resultDesc->natts)
7852 : 242 : colname = NameStr(TupleDescAttr(context->resultDesc,
7853 : : colno - 1)->attname);
7854 : : else
475 tgl@sss.pgh.pa.us 7855 :UBC 0 : colname = tle->resname;
7856 : :
475 tgl@sss.pgh.pa.us 7857 [ + - + + ]:CBC 242 : if (colname && strcmp(colname, attname) == 0 &&
7858 [ + + ]: 87 : !equal(var, tle->expr))
7859 : : {
7860 : 6 : need_prefix = true;
7861 : 6 : break;
7862 : : }
7863 : : }
7864 : : }
7865 : :
7866 [ + + + + ]: 69209 : if (refname && need_prefix)
7867 : : {
5886 7868 : 36469 : appendStringInfoString(buf, quote_identifier(refname));
4982 7869 : 36469 : appendStringInfoChar(buf, '.');
7870 : : }
6872 7871 [ + + ]: 69209 : if (attname)
7872 : 68686 : appendStringInfoString(buf, quote_identifier(attname));
7873 : : else
7874 : : {
7875 : 523 : appendStringInfoChar(buf, '*');
4982 7876 [ + + ]: 523 : if (istoplevel)
7877 : 42 : appendStringInfo(buf, "::%s",
7878 : : format_type_with_typemod(var->vartype,
7879 : : var->vartypmod));
7880 : : }
7881 : :
6872 7882 : 69209 : return attname;
7883 : : }
7884 : :
7885 : : /*
7886 : : * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
7887 : : * routine is actually a callback for resolve_special_varno, which handles
7888 : : * finding the correct TargetEntry. We get the expression contained in that
7889 : : * TargetEntry and just need to deparse it, a job we can throw back on
7890 : : * get_rule_expr.
7891 : : */
7892 : : static void
2198 7893 : 25585 : get_special_variable(Node *node, deparse_context *context, void *callback_arg)
7894 : : {
3521 rhaas@postgresql.org 7895 : 25585 : StringInfo buf = context->buf;
7896 : :
7897 : : /*
7898 : : * For a non-Var referent, force parentheses because our caller probably
7899 : : * assumed a Var is a simple expression.
7900 : : */
7901 [ + + ]: 25585 : if (!IsA(node, Var))
7902 : 2558 : appendStringInfoChar(buf, '(');
7903 : 25585 : get_rule_expr(node, context, true);
7904 [ + + ]: 25585 : if (!IsA(node, Var))
7905 : 2558 : appendStringInfoChar(buf, ')');
7906 : 25585 : }
7907 : :
7908 : : /*
7909 : : * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
7910 : : * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
7911 : : * invoke the callback provided.
7912 : : */
7913 : : static void
2198 tgl@sss.pgh.pa.us 7914 : 72111 : resolve_special_varno(Node *node, deparse_context *context,
7915 : : rsv_callback callback, void *callback_arg)
7916 : : {
7917 : : Var *var;
7918 : : deparse_namespace *dpns;
7919 : :
7920 : : /* This function is recursive, so let's be paranoid. */
7921 : 72111 : check_stack_depth();
7922 : :
7923 : : /* If it's not a Var, invoke the callback. */
3521 rhaas@postgresql.org 7924 [ + + ]: 72111 : if (!IsA(node, Var))
7925 : : {
2198 tgl@sss.pgh.pa.us 7926 : 2946 : (*callback) (node, context, callback_arg);
3521 rhaas@postgresql.org 7927 : 2946 : return;
7928 : : }
7929 : :
7930 : : /* Find appropriate nesting depth */
7931 : 69165 : var = (Var *) node;
7932 : 69165 : dpns = (deparse_namespace *) list_nth(context->namespaces,
7933 : 69165 : var->varlevelsup);
7934 : :
7935 : : /*
7936 : : * If varno is special, recurse. (Don't worry about varnosyn; if we're
7937 : : * here, we already decided not to use that.)
7938 : : */
7939 [ + + + - ]: 69165 : if (var->varno == OUTER_VAR && dpns->outer_tlist)
7940 : : {
7941 : : TargetEntry *tle;
7942 : : deparse_namespace save_dpns;
7943 : : Bitmapset *save_appendparents;
7944 : :
7945 : 34871 : tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
7946 [ - + ]: 34871 : if (!tle)
3521 rhaas@postgresql.org 7947 [ # # ]:UBC 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
7948 : :
7949 : : /*
7950 : : * If we're descending to the first child of an Append or MergeAppend,
7951 : : * update appendparents. This will affect deparsing of all Vars
7952 : : * appearing within the eventually-resolved subexpression.
7953 : : */
2198 tgl@sss.pgh.pa.us 7954 :CBC 34871 : save_appendparents = context->appendparents;
7955 : :
7956 [ + + ]: 34871 : if (IsA(dpns->plan, Append))
7957 : 2262 : context->appendparents = bms_union(context->appendparents,
7958 : 2262 : ((Append *) dpns->plan)->apprelids);
7959 [ + + ]: 32609 : else if (IsA(dpns->plan, MergeAppend))
7960 : 316 : context->appendparents = bms_union(context->appendparents,
7961 : 316 : ((MergeAppend *) dpns->plan)->apprelids);
7962 : :
7963 : 34871 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
7964 : 34871 : resolve_special_varno((Node *) tle->expr, context,
7965 : : callback, callback_arg);
3521 rhaas@postgresql.org 7966 : 34871 : pop_child_plan(dpns, &save_dpns);
2198 tgl@sss.pgh.pa.us 7967 : 34871 : context->appendparents = save_appendparents;
3521 rhaas@postgresql.org 7968 : 34871 : return;
7969 : : }
7970 [ + + + - ]: 34294 : else if (var->varno == INNER_VAR && dpns->inner_tlist)
7971 : : {
7972 : : TargetEntry *tle;
7973 : : deparse_namespace save_dpns;
7974 : :
7975 : 8483 : tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
7976 [ - + ]: 8483 : if (!tle)
3521 rhaas@postgresql.org 7977 [ # # ]:UBC 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
7978 : :
2198 tgl@sss.pgh.pa.us 7979 :CBC 8483 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7980 : 8483 : resolve_special_varno((Node *) tle->expr, context,
7981 : : callback, callback_arg);
3521 rhaas@postgresql.org 7982 : 8483 : pop_child_plan(dpns, &save_dpns);
7983 : 8483 : return;
7984 : : }
7985 [ + + + - ]: 25811 : else if (var->varno == INDEX_VAR && dpns->index_tlist)
7986 : : {
7987 : : TargetEntry *tle;
7988 : :
7989 : 2784 : tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
7990 [ - + ]: 2784 : if (!tle)
3521 rhaas@postgresql.org 7991 [ # # ]:UBC 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
7992 : :
2198 tgl@sss.pgh.pa.us 7993 :CBC 2784 : resolve_special_varno((Node *) tle->expr, context,
7994 : : callback, callback_arg);
3521 rhaas@postgresql.org 7995 : 2784 : return;
7996 : : }
7997 [ + - - + ]: 23027 : else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
3521 rhaas@postgresql.org 7998 [ # # ]:UBC 0 : elog(ERROR, "bogus varno: %d", var->varno);
7999 : :
8000 : : /* Not special. Just invoke the callback. */
2198 tgl@sss.pgh.pa.us 8001 :CBC 23027 : (*callback) (node, context, callback_arg);
8002 : : }
8003 : :
8004 : : /*
8005 : : * Get the name of a field of an expression of composite type. The
8006 : : * expression is usually a Var, but we handle other cases too.
8007 : : *
8008 : : * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
8009 : : *
8010 : : * This is fairly straightforward when the expression has a named composite
8011 : : * type; we need only look up the type in the catalogs. However, the type
8012 : : * could also be RECORD. Since no actual table or view column is allowed to
8013 : : * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
8014 : : * or to a subquery output. We drill down to find the ultimate defining
8015 : : * expression and attempt to infer the field name from it. We ereport if we
8016 : : * can't determine the name.
8017 : : *
8018 : : * Similarly, a PARAM of type RECORD has to refer to some expression of
8019 : : * a determinable composite type.
8020 : : */
8021 : : static const char *
7505 8022 : 670 : get_name_for_var_field(Var *var, int fieldno,
8023 : : int levelsup, deparse_context *context)
8024 : : {
8025 : : RangeTblEntry *rte;
8026 : : AttrNumber attnum;
8027 : : int netlevelsup;
8028 : : deparse_namespace *dpns;
8029 : : int varno;
8030 : : AttrNumber varattno;
8031 : : TupleDesc tupleDesc;
8032 : : Node *expr;
8033 : :
8034 : : /*
8035 : : * If it's a RowExpr that was expanded from a whole-row Var, use the
8036 : : * column names attached to it. (We could let get_expr_result_tupdesc()
8037 : : * handle this, but it's much cheaper to just pull out the name we need.)
8038 : : */
6281 8039 [ + + ]: 670 : if (IsA(var, RowExpr))
8040 : : {
6033 bruce@momjian.us 8041 : 18 : RowExpr *r = (RowExpr *) var;
8042 : :
6281 tgl@sss.pgh.pa.us 8043 [ + - + - ]: 18 : if (fieldno > 0 && fieldno <= list_length(r->colnames))
8044 : 18 : return strVal(list_nth(r->colnames, fieldno - 1));
8045 : : }
8046 : :
8047 : : /*
8048 : : * If it's a Param of type RECORD, try to find what the Param refers to.
8049 : : */
5215 8050 [ + + ]: 652 : if (IsA(var, Param))
8051 : : {
8052 : 9 : Param *param = (Param *) var;
8053 : : ListCell *ancestor_cell;
8054 : :
8055 : 9 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8056 [ + - ]: 9 : if (expr)
8057 : : {
8058 : : /* Found a match, so recurse to decipher the field name */
8059 : : deparse_namespace save_dpns;
8060 : : const char *result;
8061 : :
8062 : 9 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
8063 : 9 : result = get_name_for_var_field((Var *) expr, fieldno,
8064 : : 0, context);
8065 : 9 : pop_ancestor_plan(dpns, &save_dpns);
8066 : 9 : return result;
8067 : : }
8068 : : }
8069 : :
8070 : : /*
8071 : : * If it's a Var of type RECORD, we have to find what the Var refers to;
8072 : : * if not, we can use get_expr_result_tupdesc().
8073 : : */
6872 8074 [ + + ]: 643 : if (!IsA(var, Var) ||
8075 [ + + ]: 603 : var->vartype != RECORDOID)
8076 : : {
2974 8077 : 520 : tupleDesc = get_expr_result_tupdesc((Node *) var, false);
8078 : : /* Got the tupdesc, so we can extract the field name */
6872 8079 [ + - - + ]: 520 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
3041 andres@anarazel.de 8080 : 520 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
8081 : : }
8082 : :
8083 : : /* Find appropriate nesting depth */
6872 tgl@sss.pgh.pa.us 8084 : 123 : netlevelsup = var->varlevelsup + levelsup;
8085 [ - + ]: 123 : if (netlevelsup >= list_length(context->namespaces))
6872 tgl@sss.pgh.pa.us 8086 [ # # ]:UBC 0 : elog(ERROR, "bogus varlevelsup: %d offset %d",
8087 : : var->varlevelsup, levelsup);
6872 tgl@sss.pgh.pa.us 8088 :CBC 123 : dpns = (deparse_namespace *) list_nth(context->namespaces,
8089 : : netlevelsup);
8090 : :
8091 : : /*
8092 : : * If we have a syntactic referent for the Var, and we're working from a
8093 : : * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8094 : : * on the semantic referent. (See comments in get_variable().)
8095 : : */
2169 8096 [ + + + + ]: 123 : if (var->varnosyn > 0 && dpns->plan == NULL)
8097 : : {
8098 : 48 : varno = var->varnosyn;
8099 : 48 : varattno = var->varattnosyn;
8100 : : }
8101 : : else
8102 : : {
8103 : 75 : varno = var->varno;
8104 : 75 : varattno = var->varattno;
8105 : : }
8106 : :
8107 : : /*
8108 : : * Try to find the relevant RTE in this rtable. In a plan tree, it's
8109 : : * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8110 : : * down into the subplans, or INDEX_VAR, which is resolved similarly.
8111 : : *
8112 : : * Note: unlike get_variable and resolve_special_varno, we need not worry
8113 : : * about inheritance mapping: a child Var should have the same datatype as
8114 : : * its parent, and here we're really only interested in the Var's type.
8115 : : */
8116 [ + + + - ]: 123 : if (varno >= 1 && varno <= list_length(dpns->rtable))
8117 : : {
8118 : 84 : rte = rt_fetch(varno, dpns->rtable);
8119 : 84 : attnum = varattno;
8120 : : }
8121 [ + + + - ]: 39 : else if (varno == OUTER_VAR && dpns->outer_tlist)
8122 : : {
8123 : : TargetEntry *tle;
8124 : : deparse_namespace save_dpns;
8125 : : const char *result;
8126 : :
8127 : 30 : tle = get_tle_by_resno(dpns->outer_tlist, varattno);
6872 8128 [ - + ]: 30 : if (!tle)
2169 tgl@sss.pgh.pa.us 8129 [ # # ]:UBC 0 : elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
8130 : :
6872 tgl@sss.pgh.pa.us 8131 [ - + ]:CBC 30 : Assert(netlevelsup == 0);
2198 8132 : 30 : push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8133 : :
6872 8134 : 30 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8135 : : levelsup, context);
8136 : :
5636 8137 : 30 : pop_child_plan(dpns, &save_dpns);
6872 8138 : 30 : return result;
8139 : : }
2169 8140 [ + - + - ]: 9 : else if (varno == INNER_VAR && dpns->inner_tlist)
8141 : : {
8142 : : TargetEntry *tle;
8143 : : deparse_namespace save_dpns;
8144 : : const char *result;
8145 : :
8146 : 9 : tle = get_tle_by_resno(dpns->inner_tlist, varattno);
6872 8147 [ - + ]: 9 : if (!tle)
2169 tgl@sss.pgh.pa.us 8148 [ # # ]:UBC 0 : elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
8149 : :
6872 tgl@sss.pgh.pa.us 8150 [ - + ]:CBC 9 : Assert(netlevelsup == 0);
2198 8151 : 9 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8152 : :
6872 8153 : 9 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8154 : : levelsup, context);
8155 : :
5636 8156 : 9 : pop_child_plan(dpns, &save_dpns);
6872 8157 : 9 : return result;
8158 : : }
2169 tgl@sss.pgh.pa.us 8159 [ # # # # ]:UBC 0 : else if (varno == INDEX_VAR && dpns->index_tlist)
8160 : : {
8161 : : TargetEntry *tle;
8162 : : const char *result;
8163 : :
8164 : 0 : tle = get_tle_by_resno(dpns->index_tlist, varattno);
5181 8165 [ # # ]: 0 : if (!tle)
2169 8166 [ # # ]: 0 : elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
8167 : :
5181 8168 [ # # ]: 0 : Assert(netlevelsup == 0);
8169 : :
8170 : 0 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8171 : : levelsup, context);
8172 : :
8173 : 0 : return result;
8174 : : }
8175 : : else
8176 : : {
2169 8177 [ # # ]: 0 : elog(ERROR, "bogus varno: %d", varno);
8178 : : return NULL; /* keep compiler quiet */
8179 : : }
8180 : :
7505 tgl@sss.pgh.pa.us 8181 [ + + ]:CBC 84 : if (attnum == InvalidAttrNumber)
8182 : : {
8183 : : /* Var is whole-row reference to RTE, so select the right field */
8184 : 12 : return get_rte_attribute_name(rte, fieldno);
8185 : : }
8186 : :
8187 : : /*
8188 : : * This part has essentially the same logic as the parser's
8189 : : * expandRecordVariable() function, but we are dealing with a different
8190 : : * representation of the input context, and we only need one field name
8191 : : * not a TupleDesc. Also, we need special cases for finding subquery and
8192 : : * CTE subplans when deparsing Plan trees.
8193 : : */
8194 : 72 : expr = (Node *) var; /* default if we can't drill down */
8195 : :
8196 [ - + - - : 72 : switch (rte->rtekind)
+ - - ]
8197 : : {
7505 tgl@sss.pgh.pa.us 8198 :UBC 0 : case RTE_RELATION:
8199 : : case RTE_VALUES:
8200 : : case RTE_NAMEDTUPLESTORE:
8201 : : case RTE_RESULT:
8202 : :
8203 : : /*
8204 : : * This case should not occur: a column of a table, values list,
8205 : : * or ENR shouldn't have type RECORD. Fall through and fail (most
8206 : : * likely) at the bottom.
8207 : : */
8208 : 0 : break;
7505 tgl@sss.pgh.pa.us 8209 :CBC 36 : case RTE_SUBQUERY:
8210 : : /* Subselect-in-FROM: examine sub-select's output expr */
8211 : : {
6872 8212 [ + + ]: 36 : if (rte->subquery)
8213 : : {
8214 : 21 : TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
8215 : : attnum);
8216 : :
8217 [ + - - + ]: 21 : if (ste == NULL || ste->resjunk)
6872 tgl@sss.pgh.pa.us 8218 [ # # ]:UBC 0 : elog(ERROR, "subquery %s does not have attribute %d",
8219 : : rte->eref->aliasname, attnum);
6872 tgl@sss.pgh.pa.us 8220 :CBC 21 : expr = (Node *) ste->expr;
8221 [ + + ]: 21 : if (IsA(expr, Var))
8222 : : {
8223 : : /*
8224 : : * Recurse into the sub-select to see what its Var
8225 : : * refers to. We have to build an additional level of
8226 : : * namespace to keep in step with varlevelsup in the
8227 : : * subselect; furthermore, the subquery RTE might be
8228 : : * from an outer query level, in which case the
8229 : : * namespace for the subselect must have that outer
8230 : : * level as parent namespace.
8231 : : */
824 8232 : 9 : List *save_nslist = context->namespaces;
8233 : : List *parent_namespaces;
8234 : : deparse_namespace mydpns;
8235 : : const char *result;
8236 : :
8237 : 9 : parent_namespaces = list_copy_tail(context->namespaces,
8238 : : netlevelsup);
8239 : :
4734 8240 : 9 : set_deparse_for_query(&mydpns, rte->subquery,
8241 : : parent_namespaces);
8242 : :
824 8243 : 9 : context->namespaces = lcons(&mydpns, parent_namespaces);
8244 : :
6872 8245 : 9 : result = get_name_for_var_field((Var *) expr, fieldno,
8246 : : 0, context);
8247 : :
824 8248 : 9 : context->namespaces = save_nslist;
8249 : :
6872 8250 : 9 : return result;
8251 : : }
8252 : : /* else fall through to inspect the expression */
8253 : : }
8254 : : else
8255 : : {
8256 : : /*
8257 : : * We're deparsing a Plan tree so we don't have complete
8258 : : * RTE entries (in particular, rte->subquery is NULL). But
8259 : : * the only place we'd normally see a Var directly
8260 : : * referencing a SUBQUERY RTE is in a SubqueryScan plan
8261 : : * node, and we can look into the child plan's tlist
8262 : : * instead. An exception occurs if the subquery was
8263 : : * proven empty and optimized away: then we'd find such a
8264 : : * Var in a childless Result node, and there's nothing in
8265 : : * the plan tree that would let us figure out what it had
8266 : : * originally referenced. In that case, fall back on
8267 : : * printing "fN", analogously to the default column names
8268 : : * for RowExprs.
8269 : : */
8270 : : TargetEntry *tle;
8271 : : deparse_namespace save_dpns;
8272 : : const char *result;
8273 : :
2198 8274 [ + + ]: 15 : if (!dpns->inner_plan)
8275 : : {
495 8276 : 6 : char *dummy_name = palloc(32);
8277 : :
493 8278 [ + - - + ]: 6 : Assert(dpns->plan && IsA(dpns->plan, Result));
495 8279 : 6 : snprintf(dummy_name, 32, "f%d", fieldno);
8280 : 6 : return dummy_name;
8281 : : }
493 8282 [ + - - + ]: 9 : Assert(dpns->plan && IsA(dpns->plan, SubqueryScan));
8283 : :
5181 8284 : 9 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
6872 8285 [ - + ]: 9 : if (!tle)
6872 tgl@sss.pgh.pa.us 8286 [ # # ]:UBC 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8287 : : attnum);
6872 tgl@sss.pgh.pa.us 8288 [ - + ]:CBC 9 : Assert(netlevelsup == 0);
2198 8289 : 9 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8290 : :
6872 8291 : 9 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8292 : : levelsup, context);
8293 : :
5636 8294 : 9 : pop_child_plan(dpns, &save_dpns);
7505 8295 : 9 : return result;
8296 : : }
8297 : : }
8298 : 12 : break;
7505 tgl@sss.pgh.pa.us 8299 :UBC 0 : case RTE_JOIN:
8300 : : /* Join RTE --- recursively inspect the alias variable */
6782 8301 [ # # ]: 0 : if (rte->joinaliasvars == NIL)
8302 [ # # ]: 0 : elog(ERROR, "cannot decompile join alias var in plan tree");
7505 8303 [ # # # # ]: 0 : Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
8304 : 0 : expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
4530 8305 [ # # ]: 0 : Assert(expr != NULL);
8306 : : /* we intentionally don't strip implicit coercions here */
7505 8307 [ # # ]: 0 : if (IsA(expr, Var))
8308 : 0 : return get_name_for_var_field((Var *) expr, fieldno,
8309 : 0 : var->varlevelsup + levelsup,
8310 : : context);
8311 : : /* else fall through to inspect the expression */
8312 : 0 : break;
8313 : 0 : case RTE_FUNCTION:
8314 : : case RTE_TABLEFUNC:
8315 : :
8316 : : /*
8317 : : * We couldn't get here unless a function is declared with one of
8318 : : * its result columns as RECORD, which is not allowed.
8319 : : */
8320 : 0 : break;
6283 tgl@sss.pgh.pa.us 8321 :CBC 36 : case RTE_CTE:
8322 : : /* CTE reference: examine subquery's output expr */
8323 : : {
6281 8324 : 36 : CommonTableExpr *cte = NULL;
8325 : : Index ctelevelsup;
8326 : : ListCell *lc;
8327 : :
8328 : : /*
8329 : : * Try to find the referenced CTE using the namespace stack.
8330 : : */
8331 : 36 : ctelevelsup = rte->ctelevelsup + netlevelsup;
8332 [ + + ]: 36 : if (ctelevelsup >= list_length(context->namespaces))
8333 : 6 : lc = NULL;
8334 : : else
8335 : : {
8336 : : deparse_namespace *ctedpns;
8337 : :
8338 : : ctedpns = (deparse_namespace *)
8339 : 30 : list_nth(context->namespaces, ctelevelsup);
8340 [ + + + - : 33 : foreach(lc, ctedpns->ctes)
+ + ]
8341 : : {
8342 : 18 : cte = (CommonTableExpr *) lfirst(lc);
8343 [ + + ]: 18 : if (strcmp(cte->ctename, rte->ctename) == 0)
8344 : 15 : break;
8345 : : }
8346 : : }
8347 [ + + ]: 36 : if (lc != NULL)
8348 : : {
8349 : 15 : Query *ctequery = (Query *) cte->ctequery;
5365 bruce@momjian.us 8350 [ - + + - ]: 15 : TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
8351 : : attnum);
8352 : :
6281 tgl@sss.pgh.pa.us 8353 [ + - - + ]: 15 : if (ste == NULL || ste->resjunk)
824 tgl@sss.pgh.pa.us 8354 [ # # ]:UBC 0 : elog(ERROR, "CTE %s does not have attribute %d",
8355 : : rte->eref->aliasname, attnum);
6281 tgl@sss.pgh.pa.us 8356 :CBC 15 : expr = (Node *) ste->expr;
8357 [ + + ]: 15 : if (IsA(expr, Var))
8358 : : {
8359 : : /*
8360 : : * Recurse into the CTE to see what its Var refers to.
8361 : : * We have to build an additional level of namespace
8362 : : * to keep in step with varlevelsup in the CTE;
8363 : : * furthermore it could be an outer CTE (compare
8364 : : * SUBQUERY case above).
8365 : : */
8366 : 9 : List *save_nslist = context->namespaces;
8367 : : List *parent_namespaces;
8368 : : deparse_namespace mydpns;
8369 : : const char *result;
8370 : :
824 8371 : 9 : parent_namespaces = list_copy_tail(context->namespaces,
8372 : : ctelevelsup);
8373 : :
4734 8374 : 9 : set_deparse_for_query(&mydpns, ctequery,
8375 : : parent_namespaces);
8376 : :
824 8377 : 9 : context->namespaces = lcons(&mydpns, parent_namespaces);
8378 : :
6281 8379 : 9 : result = get_name_for_var_field((Var *) expr, fieldno,
8380 : : 0, context);
8381 : :
8382 : 9 : context->namespaces = save_nslist;
8383 : :
8384 : 9 : return result;
8385 : : }
8386 : : /* else fall through to inspect the expression */
8387 : : }
8388 : : else
8389 : : {
8390 : : /*
8391 : : * We're deparsing a Plan tree so we don't have a CTE
8392 : : * list. But the only places we'd normally see a Var
8393 : : * directly referencing a CTE RTE are in CteScan or
8394 : : * WorkTableScan plan nodes. For those cases,
8395 : : * set_deparse_plan arranged for dpns->inner_plan to be
8396 : : * the plan node that emits the CTE or RecursiveUnion
8397 : : * result, and we can look at its tlist instead. As
8398 : : * above, this can fail if the CTE has been proven empty,
8399 : : * in which case fall back to "fN".
8400 : : */
8401 : : TargetEntry *tle;
8402 : : deparse_namespace save_dpns;
8403 : : const char *result;
8404 : :
2198 8405 [ + + ]: 21 : if (!dpns->inner_plan)
8406 : : {
495 8407 : 3 : char *dummy_name = palloc(32);
8408 : :
493 8409 [ + - - + ]: 3 : Assert(dpns->plan && IsA(dpns->plan, Result));
495 8410 : 3 : snprintf(dummy_name, 32, "f%d", fieldno);
8411 : 3 : return dummy_name;
8412 : : }
493 8413 [ + - + + : 18 : Assert(dpns->plan && (IsA(dpns->plan, CteScan) ||
- + ]
8414 : : IsA(dpns->plan, WorkTableScan)));
8415 : :
5181 8416 : 18 : tle = get_tle_by_resno(dpns->inner_tlist, attnum);
6281 8417 [ - + ]: 18 : if (!tle)
6281 tgl@sss.pgh.pa.us 8418 [ # # ]:UBC 0 : elog(ERROR, "bogus varattno for subquery var: %d",
8419 : : attnum);
6281 tgl@sss.pgh.pa.us 8420 [ - + ]:CBC 18 : Assert(netlevelsup == 0);
2198 8421 : 18 : push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8422 : :
6281 8423 : 18 : result = get_name_for_var_field((Var *) tle->expr, fieldno,
8424 : : levelsup, context);
8425 : :
5636 8426 : 18 : pop_child_plan(dpns, &save_dpns);
6281 8427 : 18 : return result;
8428 : : }
8429 : : }
6283 8430 : 6 : break;
463 rguo@postgresql.org 8431 :UBC 0 : case RTE_GROUP:
8432 : :
8433 : : /*
8434 : : * We couldn't get here: any Vars that reference the RTE_GROUP RTE
8435 : : * should have been replaced with the underlying grouping
8436 : : * expressions.
8437 : : */
8438 : 0 : break;
8439 : : }
8440 : :
8441 : : /*
8442 : : * We now have an expression we can't expand any more, so see if
8443 : : * get_expr_result_tupdesc() can do anything with it.
8444 : : */
2974 tgl@sss.pgh.pa.us 8445 :CBC 18 : tupleDesc = get_expr_result_tupdesc(expr, false);
8446 : : /* Got the tupdesc, so we can extract the field name */
7505 8447 [ + - - + ]: 18 : Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
3041 andres@anarazel.de 8448 : 18 : return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
8449 : : }
8450 : :
8451 : : /*
8452 : : * Try to find the referenced expression for a PARAM_EXEC Param that might
8453 : : * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
8454 : : *
8455 : : * If successful, return the expression and set *dpns_p and *ancestor_cell_p
8456 : : * appropriately for calling push_ancestor_plan(). If no referent can be
8457 : : * found, return NULL.
8458 : : */
8459 : : static Node *
5215 tgl@sss.pgh.pa.us 8460 : 3605 : find_param_referent(Param *param, deparse_context *context,
8461 : : deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
8462 : : {
8463 : : /* Initialize output parameters to prevent compiler warnings */
8464 : 3605 : *dpns_p = NULL;
8465 : 3605 : *ancestor_cell_p = NULL;
8466 : :
8467 : : /*
8468 : : * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
8469 : : * SubPlan argument. This will necessarily be in some ancestor of the
8470 : : * current expression's Plan node.
8471 : : */
5636 8472 [ + + ]: 3605 : if (param->paramkind == PARAM_EXEC)
8473 : : {
8474 : : deparse_namespace *dpns;
8475 : : Plan *child_plan;
8476 : : ListCell *lc;
8477 : :
8478 : 3159 : dpns = (deparse_namespace *) linitial(context->namespaces);
2198 8479 : 3159 : child_plan = dpns->plan;
8480 : :
5636 8481 [ + + + + : 5579 : foreach(lc, dpns->ancestors)
+ + ]
8482 : : {
2198 8483 : 4731 : Node *ancestor = (Node *) lfirst(lc);
8484 : : ListCell *lc2;
8485 : :
8486 : : /*
8487 : : * NestLoops transmit params to their inner child only.
8488 : : */
8489 [ + + ]: 4731 : if (IsA(ancestor, NestLoop) &&
1127 8490 [ + + ]: 2161 : child_plan == innerPlan(ancestor))
8491 : : {
2198 8492 : 2069 : NestLoop *nl = (NestLoop *) ancestor;
8493 : :
5636 8494 [ + + + + : 2562 : foreach(lc2, nl->nestParams)
+ + ]
8495 : : {
5365 bruce@momjian.us 8496 : 2473 : NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
8497 : :
5636 tgl@sss.pgh.pa.us 8498 [ + + ]: 2473 : if (nlp->paramno == param->paramid)
8499 : : {
8500 : : /* Found a match, so return it */
5215 8501 : 1980 : *dpns_p = dpns;
8502 : 1980 : *ancestor_cell_p = lc;
8503 : 1980 : return (Node *) nlp->paramval;
8504 : : }
8505 : : }
8506 : : }
8507 : :
8508 : : /*
8509 : : * If ancestor is a SubPlan, check the arguments it provides.
8510 : : */
2198 8511 [ + + ]: 2751 : if (IsA(ancestor, SubPlan))
5636 8512 : 177 : {
2198 8513 : 508 : SubPlan *subplan = (SubPlan *) ancestor;
8514 : : ListCell *lc3;
8515 : : ListCell *lc4;
8516 : :
5636 8517 [ + + + + : 676 : forboth(lc3, subplan->parParam, lc4, subplan->args)
+ + + + +
+ + - +
+ ]
8518 : : {
5365 bruce@momjian.us 8519 : 499 : int paramid = lfirst_int(lc3);
8520 : 499 : Node *arg = (Node *) lfirst(lc4);
8521 : :
5636 tgl@sss.pgh.pa.us 8522 [ + + ]: 499 : if (paramid == param->paramid)
8523 : : {
8524 : : /*
8525 : : * Found a match, so return it. But, since Vars in
8526 : : * the arg are to be evaluated in the surrounding
8527 : : * context, we have to point to the next ancestor item
8528 : : * that is *not* a SubPlan.
8529 : : */
8530 : : ListCell *rest;
8531 : :
2198 8532 [ + - + - : 331 : for_each_cell(rest, dpns->ancestors,
+ - ]
8533 : : lnext(dpns->ancestors, lc))
8534 : : {
8535 : 331 : Node *ancestor2 = (Node *) lfirst(rest);
8536 : :
8537 [ + - ]: 331 : if (!IsA(ancestor2, SubPlan))
8538 : : {
8539 : 331 : *dpns_p = dpns;
8540 : 331 : *ancestor_cell_p = rest;
8541 : 331 : return arg;
8542 : : }
8543 : : }
2198 tgl@sss.pgh.pa.us 8544 [ # # ]:UBC 0 : elog(ERROR, "SubPlan cannot be outermost ancestor");
8545 : : }
8546 : : }
8547 : :
8548 : : /* SubPlan isn't a kind of Plan, so skip the rest */
2198 tgl@sss.pgh.pa.us 8549 :CBC 177 : continue;
8550 : : }
8551 : :
8552 : : /*
8553 : : * We need not consider the ancestor's initPlan list, since
8554 : : * initplans never have any parParams.
8555 : : */
8556 : :
8557 : : /* No luck, crawl up to next ancestor */
8558 : 2243 : child_plan = (Plan *) ancestor;
8559 : : }
8560 : : }
8561 : :
8562 : : /* No referent found */
5215 8563 : 1294 : return NULL;
8564 : : }
8565 : :
8566 : : /*
8567 : : * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.
8568 : : *
8569 : : * If successful, return the generating subplan/initplan and set *column_p
8570 : : * to the subplan's 0-based output column number.
8571 : : * Otherwise, return NULL.
8572 : : */
8573 : : static SubPlan *
638 8574 : 1294 : find_param_generator(Param *param, deparse_context *context, int *column_p)
8575 : : {
8576 : : /* Initialize output parameter to prevent compiler warnings */
8577 : 1294 : *column_p = 0;
8578 : :
8579 : : /*
8580 : : * If it's a PARAM_EXEC parameter, search the current plan node as well as
8581 : : * ancestor nodes looking for a subplan or initplan that emits the value
8582 : : * for the Param. It could appear in the setParams of an initplan or
8583 : : * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
8584 : : */
8585 [ + + ]: 1294 : if (param->paramkind == PARAM_EXEC)
8586 : : {
8587 : : SubPlan *result;
8588 : : deparse_namespace *dpns;
8589 : : ListCell *lc;
8590 : :
8591 : 848 : dpns = (deparse_namespace *) linitial(context->namespaces);
8592 : :
8593 : : /* First check the innermost plan node's initplans */
8594 : 848 : result = find_param_generator_initplan(param, dpns->plan, column_p);
8595 [ + + ]: 848 : if (result)
8596 : 264 : return result;
8597 : :
8598 : : /*
8599 : : * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
8600 : : * which can be referenced by Params elsewhere in the targetlist.
8601 : : * (Such Params should always be in the same targetlist, so there's no
8602 : : * need to do this work at upper plan nodes.)
8603 : : */
8604 [ + + + + : 2984 : foreach_node(TargetEntry, tle, dpns->plan->targetlist)
+ + ]
8605 : : {
8606 [ + - + + ]: 1868 : if (tle->expr && IsA(tle->expr, SubPlan))
8607 : : {
8608 : 50 : SubPlan *subplan = (SubPlan *) tle->expr;
8609 : :
8610 [ + + ]: 50 : if (subplan->subLinkType == MULTIEXPR_SUBLINK)
8611 : : {
8612 [ + - + - : 39 : foreach_int(paramid, subplan->setParam)
+ - ]
8613 : : {
8614 [ + + ]: 39 : if (paramid == param->paramid)
8615 : : {
8616 : : /* Found a match, so return it. */
8617 : 26 : *column_p = foreach_current_index(paramid);
8618 : 26 : return subplan;
8619 : : }
8620 : : }
8621 : : }
8622 : : }
8623 : : }
8624 : :
8625 : : /* No luck, so check the ancestor nodes */
8626 [ + - + - : 735 : foreach(lc, dpns->ancestors)
+ - ]
8627 : : {
8628 : 735 : Node *ancestor = (Node *) lfirst(lc);
8629 : :
8630 : : /*
8631 : : * If ancestor is a SubPlan, check the paramIds it provides.
8632 : : */
8633 [ + + ]: 735 : if (IsA(ancestor, SubPlan))
638 tgl@sss.pgh.pa.us 8634 :UBC 0 : {
638 tgl@sss.pgh.pa.us 8635 :CBC 105 : SubPlan *subplan = (SubPlan *) ancestor;
8636 : :
8637 [ + - + - : 118 : foreach_int(paramid, subplan->paramIds)
+ - ]
8638 : : {
8639 [ + + ]: 118 : if (paramid == param->paramid)
8640 : : {
8641 : : /* Found a match, so return it. */
8642 : 105 : *column_p = foreach_current_index(paramid);
8643 : 105 : return subplan;
8644 : : }
8645 : : }
8646 : :
8647 : : /* SubPlan isn't a kind of Plan, so skip the rest */
638 tgl@sss.pgh.pa.us 8648 :UBC 0 : continue;
8649 : : }
8650 : :
8651 : : /*
8652 : : * Otherwise, it's some kind of Plan node, so check its initplans.
8653 : : */
638 tgl@sss.pgh.pa.us 8654 :CBC 630 : result = find_param_generator_initplan(param, (Plan *) ancestor,
8655 : : column_p);
8656 [ + + ]: 630 : if (result)
8657 : 453 : return result;
8658 : :
8659 : : /* No luck, crawl up to next ancestor */
8660 : : }
8661 : : }
8662 : :
8663 : : /* No generator found */
8664 : 446 : return NULL;
8665 : : }
8666 : :
8667 : : /*
8668 : : * Subroutine for find_param_generator: search one Plan node's initplans
8669 : : */
8670 : : static SubPlan *
8671 : 1478 : find_param_generator_initplan(Param *param, Plan *plan, int *column_p)
8672 : : {
8673 [ + + + - : 2311 : foreach_node(SubPlan, subplan, plan->initPlan)
+ + ]
8674 : : {
8675 [ + - + + : 936 : foreach_int(paramid, subplan->setParam)
+ + ]
8676 : : {
8677 [ + + ]: 792 : if (paramid == param->paramid)
8678 : : {
8679 : : /* Found a match, so return it. */
8680 : 717 : *column_p = foreach_current_index(paramid);
8681 : 717 : return subplan;
8682 : : }
8683 : : }
8684 : : }
8685 : 761 : return NULL;
8686 : : }
8687 : :
8688 : : /*
8689 : : * Display a Param appropriately.
8690 : : */
8691 : : static void
5215 8692 : 3596 : get_parameter(Param *param, deparse_context *context)
8693 : : {
8694 : : Node *expr;
8695 : : deparse_namespace *dpns;
8696 : : ListCell *ancestor_cell;
8697 : : SubPlan *subplan;
8698 : : int column;
8699 : :
8700 : : /*
8701 : : * If it's a PARAM_EXEC parameter, try to locate the expression from which
8702 : : * the parameter was computed. This stanza handles only cases in which
8703 : : * the Param represents an input to the subplan we are currently in.
8704 : : */
8705 : 3596 : expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8706 [ + + ]: 3596 : if (expr)
8707 : : {
8708 : : /* Found a match, so print it */
8709 : : deparse_namespace save_dpns;
8710 : : bool save_varprefix;
8711 : : bool need_paren;
8712 : :
8713 : : /* Switch attention to the ancestor plan node */
8714 : 2302 : push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
8715 : :
8716 : : /*
8717 : : * Force prefixing of Vars, since they won't belong to the relation
8718 : : * being scanned in the original plan node.
8719 : : */
8720 : 2302 : save_varprefix = context->varprefix;
8721 : 2302 : context->varprefix = true;
8722 : :
8723 : : /*
8724 : : * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
8725 : : * upper-level Param, which wouldn't need extra parentheses.
8726 : : * Otherwise, insert parens to ensure the expression looks atomic.
8727 : : */
8728 [ + + ]: 2314 : need_paren = !(IsA(expr, Var) ||
8729 [ + + ]: 12 : IsA(expr, Aggref) ||
1367 8730 [ + + ]: 9 : IsA(expr, GroupingFunc) ||
5215 8731 [ - + ]: 6 : IsA(expr, Param));
8732 [ - + ]: 2302 : if (need_paren)
5215 tgl@sss.pgh.pa.us 8733 :UBC 0 : appendStringInfoChar(context->buf, '(');
8734 : :
5215 tgl@sss.pgh.pa.us 8735 :CBC 2302 : get_rule_expr(expr, context, false);
8736 : :
8737 [ - + ]: 2302 : if (need_paren)
5215 tgl@sss.pgh.pa.us 8738 :UBC 0 : appendStringInfoChar(context->buf, ')');
8739 : :
5215 tgl@sss.pgh.pa.us 8740 :CBC 2302 : context->varprefix = save_varprefix;
8741 : :
8742 : 2302 : pop_ancestor_plan(dpns, &save_dpns);
8743 : :
8744 : 2302 : return;
8745 : : }
8746 : :
8747 : : /*
8748 : : * Alternatively, maybe it's a subplan output, which we print as a
8749 : : * reference to the subplan. (We could drill down into the subplan and
8750 : : * print the relevant targetlist expression, but that has been deemed too
8751 : : * confusing since it would violate normal SQL scope rules. Also, we're
8752 : : * relying on this reference to show that the testexpr containing the
8753 : : * Param has anything to do with that subplan at all.)
8754 : : */
638 8755 : 1294 : subplan = find_param_generator(param, context, &column);
8756 [ + + ]: 1294 : if (subplan)
8757 : : {
8758 : : const char *nameprefix;
8759 : :
71 rhaas@postgresql.org 8760 [ + + ]:GNC 848 : if (subplan->isInitPlan)
8761 : 717 : nameprefix = "InitPlan ";
8762 : : else
8763 : 131 : nameprefix = "SubPlan ";
8764 : :
8765 : 848 : appendStringInfo(context->buf, "(%s%s%s).col%d",
638 tgl@sss.pgh.pa.us 8766 [ + + ]:CBC 848 : subplan->useHashTable ? "hashed " : "",
8767 : : nameprefix,
8768 : : subplan->plan_name, column + 1);
8769 : :
8770 : 848 : return;
8771 : : }
8772 : :
8773 : : /*
8774 : : * If it's an external parameter, see if the outermost namespace provides
8775 : : * function argument names.
8776 : : */
1491 8777 [ + - + - ]: 446 : if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
8778 : : {
8779 : 446 : dpns = llast(context->namespaces);
8780 [ + + ]: 446 : if (dpns->argnames &&
8781 [ + - ]: 34 : param->paramid > 0 &&
8782 [ + - ]: 34 : param->paramid <= dpns->numargs)
8783 : : {
1715 peter@eisentraut.org 8784 : 34 : char *argname = dpns->argnames[param->paramid - 1];
8785 : :
8786 [ + - ]: 34 : if (argname)
8787 : : {
8788 : 34 : bool should_qualify = false;
8789 : : ListCell *lc;
8790 : :
8791 : : /*
8792 : : * Qualify the parameter name if there are any other deparse
8793 : : * namespaces with range tables. This avoids qualifying in
8794 : : * trivial cases like "RETURN a + b", but makes it safe in all
8795 : : * other cases.
8796 : : */
8797 [ + - + + : 78 : foreach(lc, context->namespaces)
+ + ]
8798 : : {
1169 drowley@postgresql.o 8799 : 59 : deparse_namespace *depns = lfirst(lc);
8800 : :
8801 [ + + ]: 59 : if (depns->rtable_names != NIL)
8802 : : {
1715 peter@eisentraut.org 8803 : 15 : should_qualify = true;
8804 : 15 : break;
8805 : : }
8806 : : }
8807 [ + + ]: 34 : if (should_qualify)
8808 : : {
8809 : 15 : appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
8810 : 15 : appendStringInfoChar(context->buf, '.');
8811 : : }
8812 : :
8813 : 34 : appendStringInfoString(context->buf, quote_identifier(argname));
8814 : 34 : return;
8815 : : }
8816 : : }
8817 : : }
8818 : :
8819 : : /*
8820 : : * Not PARAM_EXEC, or couldn't find referent: just print $N.
8821 : : *
8822 : : * It's a bug if we get here for anything except PARAM_EXTERN Params, but
8823 : : * in production builds printing $N seems more useful than failing.
8824 : : */
638 tgl@sss.pgh.pa.us 8825 [ - + ]: 412 : Assert(param->paramkind == PARAM_EXTERN);
8826 : :
5215 8827 : 412 : appendStringInfo(context->buf, "$%d", param->paramid);
8828 : : }
8829 : :
8830 : : /*
8831 : : * get_simple_binary_op_name
8832 : : *
8833 : : * helper function for isSimpleNode
8834 : : * will return single char binary operator name, or NULL if it's not
8835 : : */
8836 : : static const char *
8167 bruce@momjian.us 8837 : 75 : get_simple_binary_op_name(OpExpr *expr)
8838 : : {
8176 tgl@sss.pgh.pa.us 8839 : 75 : List *args = expr->args;
8840 : :
7871 neilc@samurai.com 8841 [ + - ]: 75 : if (list_length(args) == 2)
8842 : : {
8843 : : /* binary operator */
7875 8844 : 75 : Node *arg1 = (Node *) linitial(args);
8176 tgl@sss.pgh.pa.us 8845 : 75 : Node *arg2 = (Node *) lsecond(args);
8846 : : const char *op;
8847 : :
8848 : 75 : op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
8849 [ + - ]: 75 : if (strlen(op) == 1)
8171 bruce@momjian.us 8850 : 75 : return op;
8851 : : }
8176 tgl@sss.pgh.pa.us 8852 :UBC 0 : return NULL;
8853 : : }
8854 : :
8855 : :
8856 : : /*
8857 : : * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
8858 : : *
8859 : : * true : simple in the context of parent node's type
8860 : : * false : not simple
8861 : : */
8862 : : static bool
8176 tgl@sss.pgh.pa.us 8863 :CBC 2938 : isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
8864 : : {
8171 bruce@momjian.us 8865 [ - + ]: 2938 : if (!node)
8176 tgl@sss.pgh.pa.us 8866 :UBC 0 : return false;
8867 : :
8171 bruce@momjian.us 8868 [ + + + + :CBC 2938 : switch (nodeTag(node))
- - + - -
- - + + +
- + ]
8869 : : {
8176 tgl@sss.pgh.pa.us 8870 : 2465 : case T_Var:
8871 : : case T_Const:
8872 : : case T_Param:
8873 : : case T_CoerceToDomainValue:
8874 : : case T_SetToDefault:
8875 : : case T_CurrentOfExpr:
8876 : : /* single words: always simple */
8877 : 2465 : return true;
8878 : :
2511 alvherre@alvh.no-ip. 8879 : 250 : case T_SubscriptingRef:
8880 : : case T_ArrayExpr:
8881 : : case T_RowExpr:
8882 : : case T_CoalesceExpr:
8883 : : case T_MinMaxExpr:
8884 : : case T_SQLValueFunction:
8885 : : case T_XmlExpr:
8886 : : case T_NextValueExpr:
8887 : : case T_NullIfExpr:
8888 : : case T_Aggref:
8889 : : case T_GroupingFunc:
8890 : : case T_WindowFunc:
8891 : : case T_MergeSupportFunc:
8892 : : case T_FuncExpr:
8893 : : case T_JsonConstructorExpr:
8894 : : case T_JsonExpr:
8895 : : /* function-like: name(..) or name[..] */
8176 tgl@sss.pgh.pa.us 8896 : 250 : return true;
8897 : :
8898 : : /* CASE keywords act as parentheses */
8176 tgl@sss.pgh.pa.us 8899 :GBC 4 : case T_CaseExpr:
8900 : 4 : return true;
8901 : :
8176 tgl@sss.pgh.pa.us 8902 :CBC 33 : case T_FieldSelect:
8903 : :
8904 : : /*
8905 : : * appears simple since . has top precedence, unless parent is
8906 : : * T_FieldSelect itself!
8907 : : */
1561 michael@paquier.xyz 8908 : 33 : return !IsA(parentNode, FieldSelect);
8909 : :
7861 tgl@sss.pgh.pa.us 8910 :UBC 0 : case T_FieldStore:
8911 : :
8912 : : /*
8913 : : * treat like FieldSelect (probably doesn't matter)
8914 : : */
1561 michael@paquier.xyz 8915 : 0 : return !IsA(parentNode, FieldStore);
8916 : :
8176 tgl@sss.pgh.pa.us 8917 : 0 : case T_CoerceToDomain:
8918 : : /* maybe simple, check args */
8171 bruce@momjian.us 8919 : 0 : return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
8920 : : node, prettyFlags);
8176 tgl@sss.pgh.pa.us 8921 :CBC 13 : case T_RelabelType:
8171 bruce@momjian.us 8922 : 13 : return isSimpleNode((Node *) ((RelabelType *) node)->arg,
8923 : : node, prettyFlags);
6770 tgl@sss.pgh.pa.us 8924 :UBC 0 : case T_CoerceViaIO:
8925 : 0 : return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
8926 : : node, prettyFlags);
6840 8927 : 0 : case T_ArrayCoerceExpr:
8928 : 0 : return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
8929 : : node, prettyFlags);
7676 8930 : 0 : case T_ConvertRowtypeExpr:
8931 : 0 : return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
8932 : : node, prettyFlags);
335 dean.a.rasheed@gmail 8933 : 0 : case T_ReturningExpr:
8934 : 0 : return isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr,
8935 : : node, prettyFlags);
8936 : :
8176 tgl@sss.pgh.pa.us 8937 :CBC 151 : case T_OpExpr:
8938 : : {
8939 : : /* depends on parent node type; needs further checking */
8171 bruce@momjian.us 8940 [ + - + + ]: 151 : if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
8941 : : {
8942 : : const char *op;
8943 : : const char *parentOp;
8944 : : bool is_lopriop;
8945 : : bool is_hipriop;
8946 : : bool is_lopriparent;
8947 : : bool is_hipriparent;
8948 : :
8949 : 39 : op = get_simple_binary_op_name((OpExpr *) node);
8950 [ - + ]: 39 : if (!op)
8171 bruce@momjian.us 8951 :UBC 0 : return false;
8952 : :
8953 : : /* We know only the basic operators + - and * / % */
8171 bruce@momjian.us 8954 :CBC 39 : is_lopriop = (strchr("+-", *op) != NULL);
8955 : 39 : is_hipriop = (strchr("*/%", *op) != NULL);
8956 [ + + + + ]: 39 : if (!(is_lopriop || is_hipriop))
8957 : 3 : return false;
8958 : :
8959 : 36 : parentOp = get_simple_binary_op_name((OpExpr *) parentNode);
8960 [ - + ]: 36 : if (!parentOp)
8171 bruce@momjian.us 8961 :UBC 0 : return false;
8962 : :
8171 bruce@momjian.us 8963 :CBC 36 : is_lopriparent = (strchr("+-", *parentOp) != NULL);
8964 : 36 : is_hipriparent = (strchr("*/%", *parentOp) != NULL);
8965 [ + + - + ]: 36 : if (!(is_lopriparent || is_hipriparent))
8171 bruce@momjian.us 8966 :UBC 0 : return false;
8967 : :
8171 bruce@momjian.us 8968 [ + + + - ]:CBC 36 : if (is_hipriop && is_lopriparent)
8969 : 6 : return true; /* op binds tighter than parent */
8970 : :
8971 [ + - + + ]: 30 : if (is_lopriop && is_hipriparent)
8972 : 24 : return false;
8973 : :
8974 : : /*
8975 : : * Operators are same priority --- can skip parens only if
8976 : : * we have (a - b) - c, not a - (b - c).
8977 : : */
7875 neilc@samurai.com 8978 [ + + ]: 6 : if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
8171 bruce@momjian.us 8979 : 3 : return true;
8980 : :
8981 : 3 : return false;
8982 : : }
8983 : : /* else do the same stuff as for T_SubLink et al. */
8984 : : }
8985 : : /* FALLTHROUGH */
8986 : :
8987 : : case T_SubLink:
8988 : : case T_NullTest:
8989 : : case T_BooleanTest:
8990 : : case T_DistinctExpr:
8991 : : case T_JsonIsPredicate:
8176 tgl@sss.pgh.pa.us 8992 [ + + + ]: 121 : switch (nodeTag(parentNode))
8993 : : {
8994 : 30 : case T_FuncExpr:
8995 : : {
8996 : : /* special handling for casts and COERCE_SQL_SYNTAX */
8171 bruce@momjian.us 8997 : 30 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
8998 : :
8999 [ + + + + ]: 30 : if (type == COERCE_EXPLICIT_CAST ||
1112 tgl@sss.pgh.pa.us 9000 [ + - ]: 3 : type == COERCE_IMPLICIT_CAST ||
9001 : : type == COERCE_SQL_SYNTAX)
8171 bruce@momjian.us 9002 : 30 : return false;
8171 bruce@momjian.us 9003 :UBC 0 : return true; /* own parentheses */
9004 : : }
3101 tgl@sss.pgh.pa.us 9005 :CBC 76 : case T_BoolExpr: /* lower precedence */
9006 : : case T_SubscriptingRef: /* other separators */
9007 : : case T_ArrayExpr: /* other separators */
9008 : : case T_RowExpr: /* other separators */
9009 : : case T_CoalesceExpr: /* own parentheses */
9010 : : case T_MinMaxExpr: /* own parentheses */
9011 : : case T_XmlExpr: /* own parentheses */
9012 : : case T_NullIfExpr: /* other separators */
9013 : : case T_Aggref: /* own parentheses */
9014 : : case T_GroupingFunc: /* own parentheses */
9015 : : case T_WindowFunc: /* own parentheses */
9016 : : case T_CaseExpr: /* other separators */
8176 9017 : 76 : return true;
9018 : 15 : default:
9019 : 15 : return false;
9020 : : }
9021 : :
9022 : 9 : case T_BoolExpr:
9023 [ + - - - ]: 9 : switch (nodeTag(parentNode))
9024 : : {
9025 : 9 : case T_BoolExpr:
9026 [ + - ]: 9 : if (prettyFlags & PRETTYFLAG_PAREN)
9027 : : {
9028 : : BoolExprType type;
9029 : : BoolExprType parentType;
9030 : :
8171 bruce@momjian.us 9031 : 9 : type = ((BoolExpr *) node)->boolop;
9032 : 9 : parentType = ((BoolExpr *) parentNode)->boolop;
8176 tgl@sss.pgh.pa.us 9033 [ + + - ]: 9 : switch (type)
9034 : : {
9035 : 6 : case NOT_EXPR:
9036 : : case AND_EXPR:
9037 [ + + + - ]: 6 : if (parentType == AND_EXPR || parentType == OR_EXPR)
9038 : 6 : return true;
8176 tgl@sss.pgh.pa.us 9039 :UBC 0 : break;
8176 tgl@sss.pgh.pa.us 9040 :CBC 3 : case OR_EXPR:
9041 [ - + ]: 3 : if (parentType == OR_EXPR)
8176 tgl@sss.pgh.pa.us 9042 :UBC 0 : return true;
8176 tgl@sss.pgh.pa.us 9043 :CBC 3 : break;
9044 : : }
9045 : : }
9046 : 3 : return false;
8176 tgl@sss.pgh.pa.us 9047 :UBC 0 : case T_FuncExpr:
9048 : : {
9049 : : /* special handling for casts and COERCE_SQL_SYNTAX */
8171 bruce@momjian.us 9050 : 0 : CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9051 : :
9052 [ # # # # ]: 0 : if (type == COERCE_EXPLICIT_CAST ||
1112 tgl@sss.pgh.pa.us 9053 [ # # ]: 0 : type == COERCE_IMPLICIT_CAST ||
9054 : : type == COERCE_SQL_SYNTAX)
8171 bruce@momjian.us 9055 : 0 : return false;
9056 : 0 : return true; /* own parentheses */
9057 : : }
2511 alvherre@alvh.no-ip. 9058 : 0 : case T_SubscriptingRef: /* other separators */
9059 : : case T_ArrayExpr: /* other separators */
9060 : : case T_RowExpr: /* other separators */
9061 : : case T_CoalesceExpr: /* own parentheses */
9062 : : case T_MinMaxExpr: /* own parentheses */
9063 : : case T_XmlExpr: /* own parentheses */
9064 : : case T_NullIfExpr: /* other separators */
9065 : : case T_Aggref: /* own parentheses */
9066 : : case T_GroupingFunc: /* own parentheses */
9067 : : case T_WindowFunc: /* own parentheses */
9068 : : case T_CaseExpr: /* other separators */
9069 : : case T_JsonExpr: /* own parentheses */
8176 tgl@sss.pgh.pa.us 9070 : 0 : return true;
9071 : 0 : default:
9072 : 0 : return false;
9073 : : }
9074 : :
994 alvherre@alvh.no-ip. 9075 : 0 : case T_JsonValueExpr:
9076 : : /* maybe simple, check args */
9077 : 0 : return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
9078 : : node, prettyFlags);
9079 : :
8176 tgl@sss.pgh.pa.us 9080 :CBC 4 : default:
9081 : 4 : break;
9082 : : }
9083 : : /* those we don't know: in dubio complexo */
8171 bruce@momjian.us 9084 : 4 : return false;
9085 : : }
9086 : :
9087 : :
9088 : : /*
9089 : : * appendContextKeyword - append a keyword to buffer
9090 : : *
9091 : : * If prettyPrint is enabled, perform a line break, and adjust indentation.
9092 : : * Otherwise, just append the keyword.
9093 : : */
9094 : : static void
8176 tgl@sss.pgh.pa.us 9095 : 15172 : appendContextKeyword(deparse_context *context, const char *str,
9096 : : int indentBefore, int indentAfter, int indentPlus)
9097 : : {
4419 9098 : 15172 : StringInfo buf = context->buf;
9099 : :
8171 bruce@momjian.us 9100 [ + + ]: 15172 : if (PRETTY_INDENT(context))
9101 : : {
9102 : : int indentAmount;
9103 : :
8176 tgl@sss.pgh.pa.us 9104 : 14714 : context->indentLevel += indentBefore;
9105 : :
9106 : : /* remove any trailing spaces currently in the buffer ... */
4419 9107 : 14714 : removeStringInfoSpaces(buf);
9108 : : /* ... then add a newline and some spaces */
9109 : 14714 : appendStringInfoChar(buf, '\n');
9110 : :
4249 9111 [ + - ]: 14714 : if (context->indentLevel < PRETTYINDENT_LIMIT)
9112 : 14714 : indentAmount = Max(context->indentLevel, 0) + indentPlus;
9113 : : else
9114 : : {
9115 : : /*
9116 : : * If we're indented more than PRETTYINDENT_LIMIT characters, try
9117 : : * to conserve horizontal space by reducing the per-level
9118 : : * indentation. For best results the scale factor here should
9119 : : * divide all the indent amounts that get added to indentLevel
9120 : : * (PRETTYINDENT_STD, etc). It's important that the indentation
9121 : : * not grow unboundedly, else deeply-nested trees use O(N^2)
9122 : : * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
9123 : : */
4249 tgl@sss.pgh.pa.us 9124 :UBC 0 : indentAmount = PRETTYINDENT_LIMIT +
9125 : 0 : (context->indentLevel - PRETTYINDENT_LIMIT) /
9126 : : (PRETTYINDENT_STD / 2);
9127 : 0 : indentAmount %= PRETTYINDENT_LIMIT;
9128 : : /* scale/wrap logic affects indentLevel, but not indentPlus */
9129 : 0 : indentAmount += indentPlus;
9130 : : }
4249 tgl@sss.pgh.pa.us 9131 :CBC 14714 : appendStringInfoSpaces(buf, indentAmount);
9132 : :
4419 9133 : 14714 : appendStringInfoString(buf, str);
9134 : :
8176 9135 : 14714 : context->indentLevel += indentAfter;
9136 [ - + ]: 14714 : if (context->indentLevel < 0)
8176 tgl@sss.pgh.pa.us 9137 :UBC 0 : context->indentLevel = 0;
9138 : : }
9139 : : else
4419 tgl@sss.pgh.pa.us 9140 :CBC 458 : appendStringInfoString(buf, str);
8176 9141 : 15172 : }
9142 : :
9143 : : /*
9144 : : * removeStringInfoSpaces - delete trailing spaces from a buffer.
9145 : : *
9146 : : * Possibly this should move to stringinfo.c at some point.
9147 : : */
9148 : : static void
4419 9149 : 14973 : removeStringInfoSpaces(StringInfo str)
9150 : : {
9151 [ + + + + ]: 23439 : while (str->len > 0 && str->data[str->len - 1] == ' ')
9152 : 8466 : str->data[--(str->len)] = '\0';
9153 : 14973 : }
9154 : :
9155 : :
9156 : : /*
9157 : : * get_rule_expr_paren - deparse expr using get_rule_expr,
9158 : : * embracing the string with parentheses if necessary for prettyPrint.
9159 : : *
9160 : : * Never embrace if prettyFlags=0, because it's done in the calling node.
9161 : : *
9162 : : * Any node that does *not* embrace its argument node by sql syntax (with
9163 : : * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
9164 : : * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
9165 : : * added.
9166 : : */
9167 : : static void
8171 bruce@momjian.us 9168 : 82540 : get_rule_expr_paren(Node *node, deparse_context *context,
9169 : : bool showimplicit, Node *parentNode)
9170 : : {
9171 : : bool need_paren;
9172 : :
8176 tgl@sss.pgh.pa.us 9173 [ + + ]: 85465 : need_paren = PRETTY_PAREN(context) &&
9174 [ + + ]: 2925 : !isSimpleNode(node, parentNode, context->prettyFlags);
9175 : :
9176 [ + + ]: 82540 : if (need_paren)
8171 bruce@momjian.us 9177 : 82 : appendStringInfoChar(context->buf, '(');
9178 : :
8176 tgl@sss.pgh.pa.us 9179 : 82540 : get_rule_expr(node, context, showimplicit);
9180 : :
9181 [ + + ]: 82540 : if (need_paren)
8171 bruce@momjian.us 9182 : 82 : appendStringInfoChar(context->buf, ')');
8176 tgl@sss.pgh.pa.us 9183 : 82540 : }
9184 : :
9185 : : static void
636 amitlan@postgresql.o 9186 : 42 : get_json_behavior(JsonBehavior *behavior, deparse_context *context,
9187 : : const char *on)
9188 : : {
9189 : : /*
9190 : : * The order of array elements must correspond to the order of
9191 : : * JsonBehaviorType members.
9192 : : */
9193 : 42 : const char *behavior_names[] =
9194 : : {
9195 : : " NULL",
9196 : : " ERROR",
9197 : : " EMPTY",
9198 : : " TRUE",
9199 : : " FALSE",
9200 : : " UNKNOWN",
9201 : : " EMPTY ARRAY",
9202 : : " EMPTY OBJECT",
9203 : : " DEFAULT "
9204 : : };
9205 : :
9206 [ + - - + ]: 42 : if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
636 amitlan@postgresql.o 9207 [ # # ]:UBC 0 : elog(ERROR, "invalid json behavior type: %d", behavior->btype);
9208 : :
636 amitlan@postgresql.o 9209 :CBC 42 : appendStringInfoString(context->buf, behavior_names[behavior->btype]);
9210 : :
9211 [ + + ]: 42 : if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
9212 : 9 : get_rule_expr(behavior->expr, context, false);
9213 : :
9214 : 42 : appendStringInfo(context->buf, " ON %s", on);
9215 : 42 : }
9216 : :
9217 : : /*
9218 : : * get_json_expr_options
9219 : : *
9220 : : * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and
9221 : : * JSON_TABLE columns.
9222 : : */
9223 : : static void
9224 : 228 : get_json_expr_options(JsonExpr *jsexpr, deparse_context *context,
9225 : : JsonBehaviorType default_behavior)
9226 : : {
9227 [ + + ]: 228 : if (jsexpr->op == JSON_QUERY_OP)
9228 : : {
9229 [ + + ]: 105 : if (jsexpr->wrapper == JSW_CONDITIONAL)
616 drowley@postgresql.o 9230 : 6 : appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
636 amitlan@postgresql.o 9231 [ + + ]: 99 : else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
616 drowley@postgresql.o 9232 : 15 : appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
9233 : : /* The default */
618 amitlan@postgresql.o 9234 [ + + + - ]: 84 : else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
616 drowley@postgresql.o 9235 : 84 : appendStringInfoString(context->buf, " WITHOUT WRAPPER");
9236 : :
636 amitlan@postgresql.o 9237 [ + + ]: 105 : if (jsexpr->omit_quotes)
616 drowley@postgresql.o 9238 : 21 : appendStringInfoString(context->buf, " OMIT QUOTES");
9239 : : /* The default */
9240 : : else
9241 : 84 : appendStringInfoString(context->buf, " KEEP QUOTES");
9242 : : }
9243 : :
636 amitlan@postgresql.o 9244 [ + + + + ]: 228 : if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
9245 : 15 : get_json_behavior(jsexpr->on_empty, context, "EMPTY");
9246 : :
9247 [ + - + + ]: 228 : if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
9248 : 24 : get_json_behavior(jsexpr->on_error, context, "ERROR");
9249 : 228 : }
9250 : :
9251 : : /* ----------
9252 : : * get_rule_expr - Parse back an expression
9253 : : *
9254 : : * Note: showimplicit determines whether we display any implicit cast that
9255 : : * is present at the top of the expression tree. It is a passed argument,
9256 : : * not a field of the context struct, because we change the value as we
9257 : : * recurse down into the expression. In general we suppress implicit casts
9258 : : * when the result type is known with certainty (eg, the arguments of an
9259 : : * OR must be boolean). We display implicit casts for arguments of functions
9260 : : * and operators, since this is needed to be certain that the same function
9261 : : * or operator will be chosen when the expression is re-parsed.
9262 : : * ----------
9263 : : */
9264 : : static void
8490 tgl@sss.pgh.pa.us 9265 : 178576 : get_rule_expr(Node *node, deparse_context *context,
9266 : : bool showimplicit)
9267 : : {
9572 9268 : 178576 : StringInfo buf = context->buf;
9269 : :
9969 bruce@momjian.us 9270 [ + + ]: 178576 : if (node == NULL)
9573 tgl@sss.pgh.pa.us 9271 : 45 : return;
9272 : :
9273 : : /* Guard against excessively long or deeply-nested queries */
4249 9274 [ - + ]: 178531 : CHECK_FOR_INTERRUPTS();
9275 : 178531 : check_stack_depth();
9276 : :
9277 : : /*
9278 : : * Each level of get_rule_expr must emit an indivisible term
9279 : : * (parenthesized if necessary) to ensure result is reparsed into the same
9280 : : * expression tree. The only exception is that when the input is a List,
9281 : : * we emit the component items comma-separated with no surrounding
9282 : : * decoration; this is convenient for most callers.
9283 : : */
9969 bruce@momjian.us 9284 [ + + + + : 178531 : switch (nodeTag(node))
+ + + + +
+ + + + +
+ + + - +
+ + + + +
+ + - + +
+ + + + +
+ + + + +
+ - + + +
+ + + + +
+ - ]
9285 : : {
9608 tgl@sss.pgh.pa.us 9286 : 86026 : case T_Var:
4982 9287 : 86026 : (void) get_variable((Var *) node, 0, false, context);
9969 bruce@momjian.us 9288 : 86026 : break;
9289 : :
8406 tgl@sss.pgh.pa.us 9290 : 31388 : case T_Const:
6555 9291 : 31388 : get_const_expr((Const *) node, context, 0);
8406 9292 : 31388 : break;
9293 : :
9294 : 3596 : case T_Param:
5636 9295 : 3596 : get_parameter((Param *) node, context);
9969 bruce@momjian.us 9296 : 3596 : break;
9297 : :
9608 tgl@sss.pgh.pa.us 9298 : 1985 : case T_Aggref:
3521 rhaas@postgresql.org 9299 : 1985 : get_agg_expr((Aggref *) node, context, (Aggref *) node);
9608 tgl@sss.pgh.pa.us 9300 : 1985 : break;
9301 : :
3868 andres@anarazel.de 9302 : 56 : case T_GroupingFunc:
9303 : : {
9304 : 56 : GroupingFunc *gexpr = (GroupingFunc *) node;
9305 : :
9306 : 56 : appendStringInfoString(buf, "GROUPING(");
9307 : 56 : get_rule_expr((Node *) gexpr->args, context, true);
9308 : 56 : appendStringInfoChar(buf, ')');
9309 : : }
9310 : 56 : break;
9311 : :
6198 tgl@sss.pgh.pa.us 9312 : 162 : case T_WindowFunc:
9313 : 162 : get_windowfunc_expr((WindowFunc *) node, context);
9314 : 162 : break;
9315 : :
640 dean.a.rasheed@gmail 9316 : 3 : case T_MergeSupportFunc:
9317 : 3 : appendStringInfoString(buf, "MERGE_ACTION()");
9318 : 3 : break;
9319 : :
2511 alvherre@alvh.no-ip. 9320 : 164 : case T_SubscriptingRef:
9321 : : {
9322 : 164 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
9323 : : bool need_parens;
9324 : :
9325 : : /*
9326 : : * If the argument is a CaseTestExpr, we must be inside a
9327 : : * FieldStore, ie, we are assigning to an element of an array
9328 : : * within a composite column. Since we already punted on
9329 : : * displaying the FieldStore's target information, just punt
9330 : : * here too, and display only the assignment source
9331 : : * expression.
9332 : : */
9333 [ - + ]: 164 : if (IsA(sbsref->refexpr, CaseTestExpr))
9334 : : {
2511 alvherre@alvh.no-ip. 9335 [ # # ]:UBC 0 : Assert(sbsref->refassgnexpr);
9336 : 0 : get_rule_expr((Node *) sbsref->refassgnexpr,
9337 : : context, showimplicit);
5781 tgl@sss.pgh.pa.us 9338 : 0 : break;
9339 : : }
9340 : :
9341 : : /*
9342 : : * Parenthesize the argument unless it's a simple Var or a
9343 : : * FieldSelect. (In particular, if it's another
9344 : : * SubscriptingRef, we *must* parenthesize to avoid
9345 : : * confusion.)
9346 : : */
2511 alvherre@alvh.no-ip. 9347 [ + + ]:CBC 241 : need_parens = !IsA(sbsref->refexpr, Var) &&
9348 [ + + ]: 77 : !IsA(sbsref->refexpr, FieldSelect);
8289 tgl@sss.pgh.pa.us 9349 [ + + ]: 164 : if (need_parens)
9350 : 47 : appendStringInfoChar(buf, '(');
2511 alvherre@alvh.no-ip. 9351 : 164 : get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
8289 tgl@sss.pgh.pa.us 9352 [ + + ]: 164 : if (need_parens)
9353 : 47 : appendStringInfoChar(buf, ')');
9354 : :
9355 : : /*
9356 : : * If there's a refassgnexpr, we want to print the node in the
9357 : : * format "container[subscripts] := refassgnexpr". This is
9358 : : * not legal SQL, so decompilation of INSERT or UPDATE
9359 : : * statements should always use processIndirection as part of
9360 : : * the statement-level syntax. We should only see this when
9361 : : * EXPLAIN tries to print the targetlist of a plan resulting
9362 : : * from such a statement.
9363 : : */
2511 alvherre@alvh.no-ip. 9364 [ + + ]: 164 : if (sbsref->refassgnexpr)
9365 : : {
9366 : : Node *refassgnexpr;
9367 : :
9368 : : /*
9369 : : * Use processIndirection to print this node's subscripts
9370 : : * as well as any additional field selections or
9371 : : * subscripting in immediate descendants. It returns the
9372 : : * RHS expr that is actually being "assigned".
9373 : : */
3423 tgl@sss.pgh.pa.us 9374 : 6 : refassgnexpr = processIndirection(node, context);
5781 9375 : 6 : appendStringInfoString(buf, " := ");
9376 : 6 : get_rule_expr(refassgnexpr, context, showimplicit);
9377 : : }
9378 : : else
9379 : : {
9380 : : /* Just an ordinary container fetch, so print subscripts */
2511 alvherre@alvh.no-ip. 9381 : 158 : printSubscripts(sbsref, context);
9382 : : }
9383 : : }
8406 tgl@sss.pgh.pa.us 9384 : 164 : break;
9385 : :
9386 : 6362 : case T_FuncExpr:
9387 : 6362 : get_func_expr((FuncExpr *) node, context, showimplicit);
9388 : 6362 : break;
9389 : :
5914 9390 : 15 : case T_NamedArgExpr:
9391 : : {
9392 : 15 : NamedArgExpr *na = (NamedArgExpr *) node;
9393 : :
3883 rhaas@postgresql.org 9394 : 15 : appendStringInfo(buf, "%s => ", quote_identifier(na->name));
5914 tgl@sss.pgh.pa.us 9395 : 15 : get_rule_expr((Node *) na->arg, context, showimplicit);
9396 : : }
9397 : 15 : break;
9398 : :
8406 9399 : 30922 : case T_OpExpr:
9400 : 30922 : get_oper_expr((OpExpr *) node, context);
9401 : 30922 : break;
9402 : :
9403 : 9 : case T_DistinctExpr:
9404 : : {
9405 : 9 : DistinctExpr *expr = (DistinctExpr *) node;
9406 : 9 : List *args = expr->args;
7875 neilc@samurai.com 9407 : 9 : Node *arg1 = (Node *) linitial(args);
8207 tgl@sss.pgh.pa.us 9408 : 9 : Node *arg2 = (Node *) lsecond(args);
9409 : :
8176 9410 [ + + ]: 9 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 9411 : 6 : appendStringInfoChar(buf, '(');
8176 tgl@sss.pgh.pa.us 9412 : 9 : get_rule_expr_paren(arg1, context, true, node);
4430 rhaas@postgresql.org 9413 : 9 : appendStringInfoString(buf, " IS DISTINCT FROM ");
8176 tgl@sss.pgh.pa.us 9414 : 9 : get_rule_expr_paren(arg2, context, true, node);
9415 [ + + ]: 9 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 9416 : 6 : appendStringInfoChar(buf, ')');
9417 : : }
8207 tgl@sss.pgh.pa.us 9418 : 9 : break;
9419 : :
5387 9420 : 80 : case T_NullIfExpr:
9421 : : {
9422 : 80 : NullIfExpr *nullifexpr = (NullIfExpr *) node;
9423 : :
4430 rhaas@postgresql.org 9424 : 80 : appendStringInfoString(buf, "NULLIF(");
5387 tgl@sss.pgh.pa.us 9425 : 80 : get_rule_expr((Node *) nullifexpr->args, context, true);
9426 : 80 : appendStringInfoChar(buf, ')');
9427 : : }
9428 : 80 : break;
9429 : :
8207 9430 : 1516 : case T_ScalarArrayOpExpr:
9431 : : {
9432 : 1516 : ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
9433 : 1516 : List *args = expr->args;
7875 neilc@samurai.com 9434 : 1516 : Node *arg1 = (Node *) linitial(args);
8207 tgl@sss.pgh.pa.us 9435 : 1516 : Node *arg2 = (Node *) lsecond(args);
9436 : :
8176 9437 [ + + ]: 1516 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 9438 : 1509 : appendStringInfoChar(buf, '(');
8176 tgl@sss.pgh.pa.us 9439 : 1516 : get_rule_expr_paren(arg1, context, true, node);
8207 9440 : 1516 : appendStringInfo(buf, " %s %s (",
9441 : : generate_operator_name(expr->opno,
9442 : : exprType(arg1),
9443 : : get_base_element_type(exprType(arg2))),
9444 [ + + ]: 1516 : expr->useOr ? "ANY" : "ALL");
8176 9445 : 1516 : get_rule_expr_paren(arg2, context, true, node);
9446 : :
9447 : : /*
9448 : : * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
9449 : : * a bare sub-SELECT. Since we're here, the sub-SELECT must
9450 : : * be meant as a scalar sub-SELECT yielding an array value to
9451 : : * be used in ScalarArrayOpExpr; but the grammar will
9452 : : * preferentially interpret such a construct as an ANY/ALL
9453 : : * SubLink. To prevent misparsing the output that way, insert
9454 : : * a dummy coercion (which will be stripped by parse analysis,
9455 : : * so no inefficiency is added in dump and reload). This is
9456 : : * indeed most likely what the user wrote to get the construct
9457 : : * accepted in the first place.
9458 : : */
3527 9459 [ + + ]: 1516 : if (IsA(arg2, SubLink) &&
9460 [ + - ]: 3 : ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
9461 : 3 : appendStringInfo(buf, "::%s",
9462 : : format_type_with_typemod(exprType(arg2),
9463 : : exprTypmod(arg2)));
8176 9464 : 1516 : appendStringInfoChar(buf, ')');
9465 [ + + ]: 1516 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 9466 : 1509 : appendStringInfoChar(buf, ')');
9467 : : }
9969 9468 : 1516 : break;
9469 : :
8406 tgl@sss.pgh.pa.us 9470 : 5630 : case T_BoolExpr:
9471 : : {
9472 : 5630 : BoolExpr *expr = (BoolExpr *) node;
7875 neilc@samurai.com 9473 : 5630 : Node *first_arg = linitial(expr->args);
9474 : : ListCell *arg;
9475 : :
8406 tgl@sss.pgh.pa.us 9476 [ + + + - ]: 5630 : switch (expr->boolop)
9477 : : {
9478 : 4493 : case AND_EXPR:
8176 9479 [ + + ]: 4493 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 9480 : 4462 : appendStringInfoChar(buf, '(');
7875 neilc@samurai.com 9481 : 4493 : get_rule_expr_paren(first_arg, context,
9482 : : false, node);
1906 tgl@sss.pgh.pa.us 9483 [ + - + + : 10246 : for_each_from(arg, expr->args, 1)
+ + ]
9484 : : {
4430 rhaas@postgresql.org 9485 : 5753 : appendStringInfoString(buf, " AND ");
7875 neilc@samurai.com 9486 : 5753 : get_rule_expr_paren((Node *) lfirst(arg), context,
9487 : : false, node);
9488 : : }
8176 tgl@sss.pgh.pa.us 9489 [ + + ]: 4493 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 9490 : 4462 : appendStringInfoChar(buf, ')');
8406 tgl@sss.pgh.pa.us 9491 : 4493 : break;
9492 : :
9493 : 951 : case OR_EXPR:
8176 9494 [ + + ]: 951 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 9495 : 944 : appendStringInfoChar(buf, '(');
7875 neilc@samurai.com 9496 : 951 : get_rule_expr_paren(first_arg, context,
9497 : : false, node);
1906 tgl@sss.pgh.pa.us 9498 [ + - + + : 2260 : for_each_from(arg, expr->args, 1)
+ + ]
9499 : : {
4430 rhaas@postgresql.org 9500 : 1309 : appendStringInfoString(buf, " OR ");
7875 neilc@samurai.com 9501 : 1309 : get_rule_expr_paren((Node *) lfirst(arg), context,
9502 : : false, node);
9503 : : }
8176 tgl@sss.pgh.pa.us 9504 [ + + ]: 951 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 9505 : 944 : appendStringInfoChar(buf, ')');
8406 tgl@sss.pgh.pa.us 9506 : 951 : break;
9507 : :
9508 : 186 : case NOT_EXPR:
8176 9509 [ + + ]: 186 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 9510 : 180 : appendStringInfoChar(buf, '(');
4430 rhaas@postgresql.org 9511 : 186 : appendStringInfoString(buf, "NOT ");
7875 neilc@samurai.com 9512 : 186 : get_rule_expr_paren(first_arg, context,
9513 : : false, node);
8176 tgl@sss.pgh.pa.us 9514 [ + + ]: 186 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 9515 : 180 : appendStringInfoChar(buf, ')');
8406 tgl@sss.pgh.pa.us 9516 : 186 : break;
9517 : :
8406 tgl@sss.pgh.pa.us 9518 :UBC 0 : default:
8179 9519 [ # # ]: 0 : elog(ERROR, "unrecognized boolop: %d",
9520 : : (int) expr->boolop);
9521 : : }
9522 : : }
8406 tgl@sss.pgh.pa.us 9523 :CBC 5630 : break;
9524 : :
9525 : 230 : case T_SubLink:
9526 : 230 : get_sublink_expr((SubLink *) node, context);
9527 : 230 : break;
9528 : :
8404 9529 : 364 : case T_SubPlan:
9530 : : {
6033 bruce@momjian.us 9531 : 364 : SubPlan *subplan = (SubPlan *) node;
9532 : :
9533 : : /*
9534 : : * We cannot see an already-planned subplan in rule deparsing,
9535 : : * only while EXPLAINing a query plan. We don't try to
9536 : : * reconstruct the original SQL, just reference the subplan
9537 : : * that appears elsewhere in EXPLAIN's result. It does seem
9538 : : * useful to show the subLinkType and testexpr (if any), and
9539 : : * we also note whether the subplan will be hashed.
9540 : : */
638 tgl@sss.pgh.pa.us 9541 [ + + + + : 364 : switch (subplan->subLinkType)
+ + + -
- ]
9542 : : {
9543 : 51 : case EXISTS_SUBLINK:
9544 : 51 : appendStringInfoString(buf, "EXISTS(");
9545 [ - + ]: 51 : Assert(subplan->testexpr == NULL);
9546 : 51 : break;
9547 : 3 : case ALL_SUBLINK:
9548 : 3 : appendStringInfoString(buf, "(ALL ");
9549 [ - + ]: 3 : Assert(subplan->testexpr != NULL);
9550 : 3 : break;
9551 : 83 : case ANY_SUBLINK:
9552 : 83 : appendStringInfoString(buf, "(ANY ");
9553 [ - + ]: 83 : Assert(subplan->testexpr != NULL);
9554 : 83 : break;
9555 : 3 : case ROWCOMPARE_SUBLINK:
9556 : : /* Parenthesizing the testexpr seems sufficient */
9557 : 3 : appendStringInfoChar(buf, '(');
9558 [ - + ]: 3 : Assert(subplan->testexpr != NULL);
9559 : 3 : break;
9560 : 205 : case EXPR_SUBLINK:
9561 : : /* No need to decorate these subplan references */
9562 : 205 : appendStringInfoChar(buf, '(');
9563 [ - + ]: 205 : Assert(subplan->testexpr == NULL);
9564 : 205 : break;
9565 : 13 : case MULTIEXPR_SUBLINK:
9566 : : /* MULTIEXPR isn't executed in the normal way */
9567 : 13 : appendStringInfoString(buf, "(rescan ");
9568 [ - + ]: 13 : Assert(subplan->testexpr == NULL);
9569 : 13 : break;
9570 : 6 : case ARRAY_SUBLINK:
9571 : 6 : appendStringInfoString(buf, "ARRAY(");
9572 [ - + ]: 6 : Assert(subplan->testexpr == NULL);
9573 : 6 : break;
638 tgl@sss.pgh.pa.us 9574 :UBC 0 : case CTE_SUBLINK:
9575 : : /* This case is unreachable within expressions */
9576 : 0 : appendStringInfoString(buf, "CTE(");
9577 [ # # ]: 0 : Assert(subplan->testexpr == NULL);
9578 : 0 : break;
9579 : : }
9580 : :
638 tgl@sss.pgh.pa.us 9581 [ + + ]:CBC 364 : if (subplan->testexpr != NULL)
9582 : : {
9583 : : deparse_namespace *dpns;
9584 : :
9585 : : /*
9586 : : * Push SubPlan into ancestors list while deparsing
9587 : : * testexpr, so that we can handle PARAM_EXEC references
9588 : : * to the SubPlan's paramIds. (This makes it look like
9589 : : * the SubPlan is an "ancestor" of the current plan node,
9590 : : * which is a little weird, but it does no harm.) In this
9591 : : * path, we don't need to mention the SubPlan explicitly,
9592 : : * because the referencing Params will show its existence.
9593 : : */
9594 : 89 : dpns = (deparse_namespace *) linitial(context->namespaces);
9595 : 89 : dpns->ancestors = lcons(subplan, dpns->ancestors);
9596 : :
9597 : 89 : get_rule_expr(subplan->testexpr, context, showimplicit);
9598 : 89 : appendStringInfoChar(buf, ')');
9599 : :
9600 : 89 : dpns->ancestors = list_delete_first(dpns->ancestors);
9601 : : }
9602 : : else
9603 : : {
9604 : : const char *nameprefix;
9605 : :
9606 : : /* No referencing Params, so show the SubPlan's name */
71 rhaas@postgresql.org 9607 [ - + ]:GNC 275 : if (subplan->isInitPlan)
71 rhaas@postgresql.org 9608 :UNC 0 : nameprefix = "InitPlan ";
9609 : : else
71 rhaas@postgresql.org 9610 :GNC 275 : nameprefix = "SubPlan ";
638 tgl@sss.pgh.pa.us 9611 [ - + ]:CBC 275 : if (subplan->useHashTable)
71 rhaas@postgresql.org 9612 :UNC 0 : appendStringInfo(buf, "hashed %s%s)",
9613 : : nameprefix, subplan->plan_name);
9614 : : else
71 rhaas@postgresql.org 9615 :GNC 275 : appendStringInfo(buf, "%s%s)",
9616 : : nameprefix, subplan->plan_name);
9617 : : }
9618 : : }
8406 tgl@sss.pgh.pa.us 9619 :CBC 364 : break;
9620 : :
6326 tgl@sss.pgh.pa.us 9621 :UBC 0 : case T_AlternativeSubPlan:
9622 : : {
6100 9623 : 0 : AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
9624 : : ListCell *lc;
9625 : :
9626 : : /*
9627 : : * This case cannot be reached in normal usage, since no
9628 : : * AlternativeSubPlan can appear either in parsetrees or
9629 : : * finished plan trees. We keep it just in case somebody
9630 : : * wants to use this code to print planner data structures.
9631 : : */
4430 rhaas@postgresql.org 9632 : 0 : appendStringInfoString(buf, "(alternatives: ");
6100 tgl@sss.pgh.pa.us 9633 [ # # # # : 0 : foreach(lc, asplan->subplans)
# # ]
9634 : : {
3173 9635 : 0 : SubPlan *splan = lfirst_node(SubPlan, lc);
9636 : : const char *nameprefix;
9637 : :
71 rhaas@postgresql.org 9638 [ # # ]:UNC 0 : if (splan->isInitPlan)
9639 : 0 : nameprefix = "InitPlan ";
9640 : : else
9641 : 0 : nameprefix = "SubPlan ";
6100 tgl@sss.pgh.pa.us 9642 [ # # ]:UBC 0 : if (splan->useHashTable)
71 rhaas@postgresql.org 9643 :UNC 0 : appendStringInfo(buf, "hashed %s%s", nameprefix,
9644 : : splan->plan_name);
9645 : : else
9646 : 0 : appendStringInfo(buf, "%s%s", nameprefix,
9647 : : splan->plan_name);
2347 tgl@sss.pgh.pa.us 9648 [ # # ]:UBC 0 : if (lnext(asplan->subplans, lc))
4430 rhaas@postgresql.org 9649 : 0 : appendStringInfoString(buf, " or ");
9650 : : }
9651 : 0 : appendStringInfoChar(buf, ')');
9652 : : }
6326 tgl@sss.pgh.pa.us 9653 : 0 : break;
9654 : :
9262 tgl@sss.pgh.pa.us 9655 :CBC 577 : case T_FieldSelect:
9656 : : {
9657 : 577 : FieldSelect *fselect = (FieldSelect *) node;
7505 9658 : 577 : Node *arg = (Node *) fselect->arg;
9659 : 577 : int fno = fselect->fieldnum;
9660 : : const char *fieldname;
9661 : : bool need_parens;
9662 : :
9663 : : /*
9664 : : * Parenthesize the argument unless it's an SubscriptingRef or
9665 : : * another FieldSelect. Note in particular that it would be
9666 : : * WRONG to not parenthesize a Var argument; simplicity is not
9667 : : * the issue here, having the right number of names is.
9668 : : */
2511 alvherre@alvh.no-ip. 9669 [ + + ]: 1136 : need_parens = !IsA(arg, SubscriptingRef) &&
9670 [ + - ]: 559 : !IsA(arg, FieldSelect);
7861 tgl@sss.pgh.pa.us 9671 [ + + ]: 577 : if (need_parens)
9672 : 559 : appendStringInfoChar(buf, '(');
7505 9673 : 577 : get_rule_expr(arg, context, true);
7861 9674 [ + + ]: 577 : if (need_parens)
9675 : 559 : appendStringInfoChar(buf, ')');
9676 : :
9677 : : /*
9678 : : * Get and print the field name.
9679 : : */
6872 9680 : 577 : fieldname = get_name_for_var_field((Var *) arg, fno,
9681 : : 0, context);
8176 9682 : 577 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
9683 : : }
9262 9684 : 577 : break;
9685 : :
7861 9686 : 3 : case T_FieldStore:
9687 : : {
5781 9688 : 3 : FieldStore *fstore = (FieldStore *) node;
9689 : : bool need_parens;
9690 : :
9691 : : /*
9692 : : * There is no good way to represent a FieldStore as real SQL,
9693 : : * so decompilation of INSERT or UPDATE statements should
9694 : : * always use processIndirection as part of the
9695 : : * statement-level syntax. We should only get here when
9696 : : * EXPLAIN tries to print the targetlist of a plan resulting
9697 : : * from such a statement. The plan case is even harder than
9698 : : * ordinary rules would be, because the planner tries to
9699 : : * collapse multiple assignments to the same field or subfield
9700 : : * into one FieldStore; so we can see a list of target fields
9701 : : * not just one, and the arguments could be FieldStores
9702 : : * themselves. We don't bother to try to print the target
9703 : : * field names; we just print the source arguments, with a
9704 : : * ROW() around them if there's more than one. This isn't
9705 : : * terribly complete, but it's probably good enough for
9706 : : * EXPLAIN's purposes; especially since anything more would be
9707 : : * either hopelessly confusing or an even poorer
9708 : : * representation of what the plan is actually doing.
9709 : : */
9710 : 3 : need_parens = (list_length(fstore->newvals) != 1);
9711 [ + - ]: 3 : if (need_parens)
9712 : 3 : appendStringInfoString(buf, "ROW(");
9713 : 3 : get_rule_expr((Node *) fstore->newvals, context, showimplicit);
9714 [ + - ]: 3 : if (need_parens)
9715 : 3 : appendStringInfoChar(buf, ')');
9716 : : }
7861 9717 : 3 : break;
9718 : :
9432 9719 : 1378 : case T_RelabelType:
9720 : : {
9721 : 1378 : RelabelType *relabel = (RelabelType *) node;
8171 bruce@momjian.us 9722 : 1378 : Node *arg = (Node *) relabel->arg;
9723 : :
8490 tgl@sss.pgh.pa.us 9724 [ + + ]: 1378 : if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
9725 [ + + ]: 1278 : !showimplicit)
9726 : : {
9727 : : /* don't show the implicit cast */
7854 9728 : 37 : get_rule_expr_paren(arg, context, false, node);
9729 : : }
9730 : : else
9731 : : {
6850 9732 : 1341 : get_coercion_expr(arg, context,
9733 : : relabel->resulttype,
9734 : : relabel->resulttypmod,
9735 : : node);
9736 : : }
9737 : : }
9432 9738 : 1378 : break;
9739 : :
6770 9740 : 334 : case T_CoerceViaIO:
9741 : : {
9742 : 334 : CoerceViaIO *iocoerce = (CoerceViaIO *) node;
9743 : 334 : Node *arg = (Node *) iocoerce->arg;
9744 : :
9745 [ + + ]: 334 : if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
9746 [ + - ]: 12 : !showimplicit)
9747 : : {
9748 : : /* don't show the implicit cast */
9749 : 12 : get_rule_expr_paren(arg, context, false, node);
9750 : : }
9751 : : else
9752 : : {
9753 : 322 : get_coercion_expr(arg, context,
9754 : : iocoerce->resulttype,
9755 : : -1,
9756 : : node);
9757 : : }
9758 : : }
9759 : 334 : break;
9760 : :
6840 9761 : 26 : case T_ArrayCoerceExpr:
9762 : : {
9763 : 26 : ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
9764 : 26 : Node *arg = (Node *) acoerce->arg;
9765 : :
9766 [ + - ]: 26 : if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
9767 [ - + ]: 26 : !showimplicit)
9768 : : {
9769 : : /* don't show the implicit cast */
6840 tgl@sss.pgh.pa.us 9770 :UBC 0 : get_rule_expr_paren(arg, context, false, node);
9771 : : }
9772 : : else
9773 : : {
6840 tgl@sss.pgh.pa.us 9774 :CBC 26 : get_coercion_expr(arg, context,
9775 : : acoerce->resulttype,
9776 : : acoerce->resulttypmod,
9777 : : node);
9778 : : }
9779 : : }
9780 : 26 : break;
9781 : :
7676 9782 : 44 : case T_ConvertRowtypeExpr:
9783 : : {
9784 : 44 : ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
9785 : 44 : Node *arg = (Node *) convert->arg;
9786 : :
9787 [ + + ]: 44 : if (convert->convertformat == COERCE_IMPLICIT_CAST &&
9788 [ + + ]: 41 : !showimplicit)
9789 : : {
9790 : : /* don't show the implicit cast */
9791 : 12 : get_rule_expr_paren(arg, context, false, node);
9792 : : }
9793 : : else
9794 : : {
6850 9795 : 32 : get_coercion_expr(arg, context,
9796 : : convert->resulttype, -1,
9797 : : node);
9798 : : }
9799 : : }
7676 9800 : 44 : break;
9801 : :
5395 9802 : 45 : case T_CollateExpr:
9803 : : {
9804 : 45 : CollateExpr *collate = (CollateExpr *) node;
9805 : 45 : Node *arg = (Node *) collate->arg;
9806 : :
9807 [ + + ]: 45 : if (!PRETTY_PAREN(context))
9808 : 42 : appendStringInfoChar(buf, '(');
9809 : 45 : get_rule_expr_paren(arg, context, showimplicit, node);
9810 : 45 : appendStringInfo(buf, " COLLATE %s",
9811 : : generate_collation_name(collate->collOid));
9812 [ + + ]: 45 : if (!PRETTY_PAREN(context))
9813 : 42 : appendStringInfoChar(buf, ')');
9814 : : }
9815 : 45 : break;
9816 : :
9608 9817 : 325 : case T_CaseExpr:
9818 : : {
9819 : 325 : CaseExpr *caseexpr = (CaseExpr *) node;
9820 : : ListCell *temp;
9821 : :
8176 9822 : 325 : appendContextKeyword(context, "CASE",
9823 : : 0, PRETTYINDENT_VAR, 0);
7945 9824 [ + + ]: 325 : if (caseexpr->arg)
9825 : : {
9826 : 114 : appendStringInfoChar(buf, ' ');
9827 : 114 : get_rule_expr((Node *) caseexpr->arg, context, true);
9828 : : }
9608 9829 [ + - + + : 1415 : foreach(temp, caseexpr->args)
+ + ]
9830 : : {
9831 : 1090 : CaseWhen *when = (CaseWhen *) lfirst(temp);
7312 9832 : 1090 : Node *w = (Node *) when->expr;
9833 : :
7945 9834 [ + + ]: 1090 : if (caseexpr->arg)
9835 : : {
9836 : : /*
9837 : : * The parser should have produced WHEN clauses of the
9838 : : * form "CaseTestExpr = RHS", possibly with an
9839 : : * implicit coercion inserted above the CaseTestExpr.
9840 : : * For accurate decompilation of rules it's essential
9841 : : * that we show just the RHS. However in an
9842 : : * expression that's been through the optimizer, the
9843 : : * WHEN clause could be almost anything (since the
9844 : : * equality operator could have been expanded into an
9845 : : * inline function). If we don't recognize the form
9846 : : * of the WHEN clause, just punt and display it as-is.
9847 : : */
7312 9848 [ + - ]: 447 : if (IsA(w, OpExpr))
9849 : : {
6139 9850 : 447 : List *args = ((OpExpr *) w)->args;
9851 : :
5319 9852 [ + - ]: 447 : if (list_length(args) == 2 &&
9853 [ + - ]: 447 : IsA(strip_implicit_coercions(linitial(args)),
9854 : : CaseTestExpr))
9855 : 447 : w = (Node *) lsecond(args);
9856 : : }
9857 : : }
9858 : :
9859 [ + + ]: 1090 : if (!PRETTY_INDENT(context))
9860 : 59 : appendStringInfoChar(buf, ' ');
9861 : 1090 : appendContextKeyword(context, "WHEN ",
9862 : : 0, 0, 0);
9863 : 1090 : get_rule_expr(w, context, false);
4430 rhaas@postgresql.org 9864 : 1090 : appendStringInfoString(buf, " THEN ");
8406 tgl@sss.pgh.pa.us 9865 : 1090 : get_rule_expr((Node *) when->result, context, true);
9866 : : }
8176 9867 [ + + ]: 325 : if (!PRETTY_INDENT(context))
8171 bruce@momjian.us 9868 : 54 : appendStringInfoChar(buf, ' ');
8176 tgl@sss.pgh.pa.us 9869 : 325 : appendContextKeyword(context, "ELSE ",
9870 : : 0, 0, 0);
8406 9871 : 325 : get_rule_expr((Node *) caseexpr->defresult, context, true);
8176 9872 [ + + ]: 325 : if (!PRETTY_INDENT(context))
8171 bruce@momjian.us 9873 : 54 : appendStringInfoChar(buf, ' ');
8176 tgl@sss.pgh.pa.us 9874 : 325 : appendContextKeyword(context, "END",
9875 : : -PRETTYINDENT_VAR, 0, 0);
9876 : : }
9969 bruce@momjian.us 9877 : 325 : break;
9878 : :
5319 tgl@sss.pgh.pa.us 9879 :UBC 0 : case T_CaseTestExpr:
9880 : : {
9881 : : /*
9882 : : * Normally we should never get here, since for expressions
9883 : : * that can contain this node type we attempt to avoid
9884 : : * recursing to it. But in an optimized expression we might
9885 : : * be unable to avoid that (see comments for CaseExpr). If we
9886 : : * do see one, print it as CASE_TEST_EXPR.
9887 : : */
4430 rhaas@postgresql.org 9888 : 0 : appendStringInfoString(buf, "CASE_TEST_EXPR");
9889 : : }
5319 tgl@sss.pgh.pa.us 9890 : 0 : break;
9891 : :
8289 tgl@sss.pgh.pa.us 9892 :CBC 281 : case T_ArrayExpr:
9893 : : {
8171 bruce@momjian.us 9894 : 281 : ArrayExpr *arrayexpr = (ArrayExpr *) node;
9895 : :
4430 rhaas@postgresql.org 9896 : 281 : appendStringInfoString(buf, "ARRAY[");
7741 tgl@sss.pgh.pa.us 9897 : 281 : get_rule_expr((Node *) arrayexpr->elements, context, true);
9898 : 281 : appendStringInfoChar(buf, ']');
9899 : :
9900 : : /*
9901 : : * If the array isn't empty, we assume its elements are
9902 : : * coerced to the desired type. If it's empty, though, we
9903 : : * need an explicit coercion to the array type.
9904 : : */
6207 9905 [ + + ]: 281 : if (arrayexpr->elements == NIL)
9906 : 3 : appendStringInfo(buf, "::%s",
9907 : : format_type_with_typemod(arrayexpr->array_typeid, -1));
9908 : : }
8289 9909 : 281 : break;
9910 : :
7891 9911 : 96 : case T_RowExpr:
9912 : : {
7780 bruce@momjian.us 9913 : 96 : RowExpr *rowexpr = (RowExpr *) node;
7792 tgl@sss.pgh.pa.us 9914 : 96 : TupleDesc tupdesc = NULL;
9915 : : ListCell *arg;
9916 : : int i;
9917 : : char *sep;
9918 : :
9919 : : /*
9920 : : * If it's a named type and not RECORD, we may have to skip
9921 : : * dropped columns and/or claim there are NULLs for added
9922 : : * columns.
9923 : : */
9924 [ + + ]: 96 : if (rowexpr->row_typeid != RECORDOID)
9925 : : {
9926 : 27 : tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
9927 [ - + ]: 27 : Assert(list_length(rowexpr->args) <= tupdesc->natts);
9928 : : }
9929 : :
9930 : : /*
9931 : : * SQL99 allows "ROW" to be omitted when there is more than
9932 : : * one column, but for simplicity we always print it.
9933 : : */
4430 rhaas@postgresql.org 9934 : 96 : appendStringInfoString(buf, "ROW(");
7891 tgl@sss.pgh.pa.us 9935 : 96 : sep = "";
7792 9936 : 96 : i = 0;
7891 9937 [ + - + + : 285 : foreach(arg, rowexpr->args)
+ + ]
9938 : : {
9939 : 189 : Node *e = (Node *) lfirst(arg);
9940 : :
7792 9941 [ + + ]: 189 : if (tupdesc == NULL ||
56 drowley@postgresql.o 9942 [ + - ]:GNC 60 : !TupleDescCompactAttr(tupdesc, i)->attisdropped)
9943 : : {
7536 neilc@samurai.com 9944 :CBC 189 : appendStringInfoString(buf, sep);
9945 : : /* Whole-row Vars need special treatment here */
3685 tgl@sss.pgh.pa.us 9946 : 189 : get_rule_expr_toplevel(e, context, true);
7792 9947 : 189 : sep = ", ";
9948 : : }
9949 : 189 : i++;
9950 : : }
9951 [ + + ]: 96 : if (tupdesc != NULL)
9952 : : {
9953 [ - + ]: 27 : while (i < tupdesc->natts)
9954 : : {
56 drowley@postgresql.o 9955 [ # # ]:UNC 0 : if (!TupleDescCompactAttr(tupdesc, i)->attisdropped)
9956 : : {
7536 neilc@samurai.com 9957 :UBC 0 : appendStringInfoString(buf, sep);
4430 rhaas@postgresql.org 9958 : 0 : appendStringInfoString(buf, "NULL");
7792 tgl@sss.pgh.pa.us 9959 : 0 : sep = ", ";
9960 : : }
9961 : 0 : i++;
9962 : : }
9963 : :
7124 tgl@sss.pgh.pa.us 9964 [ + - ]:CBC 27 : ReleaseTupleDesc(tupdesc);
9965 : : }
4430 rhaas@postgresql.org 9966 : 96 : appendStringInfoChar(buf, ')');
7891 tgl@sss.pgh.pa.us 9967 [ + + ]: 96 : if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
9968 : 18 : appendStringInfo(buf, "::%s",
9969 : : format_type_with_typemod(rowexpr->row_typeid, -1));
9970 : : }
9971 : 96 : break;
9972 : :
7294 9973 : 57 : case T_RowCompareExpr:
9974 : : {
9975 : 57 : RowCompareExpr *rcexpr = (RowCompareExpr *) node;
9976 : :
9977 : : /*
9978 : : * SQL99 allows "ROW" to be omitted when there is more than
9979 : : * one column, but for simplicity we always print it. Within
9980 : : * a ROW expression, whole-row Vars need special treatment, so
9981 : : * use get_rule_list_toplevel.
9982 : : */
4430 rhaas@postgresql.org 9983 : 57 : appendStringInfoString(buf, "(ROW(");
1434 tgl@sss.pgh.pa.us 9984 : 57 : get_rule_list_toplevel(rcexpr->largs, context, true);
9985 : :
9986 : : /*
9987 : : * We assume that the name of the first-column operator will
9988 : : * do for all the rest too. This is definitely open to
9989 : : * failure, eg if some but not all operators were renamed
9990 : : * since the construct was parsed, but there seems no way to
9991 : : * be perfect.
9992 : : */
7294 9993 : 57 : appendStringInfo(buf, ") %s ROW(",
3101 9994 : 57 : generate_operator_name(linitial_oid(rcexpr->opnos),
9995 : 57 : exprType(linitial(rcexpr->largs)),
9996 : 57 : exprType(linitial(rcexpr->rargs))));
1434 9997 : 57 : get_rule_list_toplevel(rcexpr->rargs, context, true);
4430 rhaas@postgresql.org 9998 : 57 : appendStringInfoString(buf, "))");
9999 : : }
7294 tgl@sss.pgh.pa.us 10000 : 57 : break;
10001 : :
8340 10002 : 600 : case T_CoalesceExpr:
10003 : : {
10004 : 600 : CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
10005 : :
4430 rhaas@postgresql.org 10006 : 600 : appendStringInfoString(buf, "COALESCE(");
7741 tgl@sss.pgh.pa.us 10007 : 600 : get_rule_expr((Node *) coalesceexpr->args, context, true);
10008 : 600 : appendStringInfoChar(buf, ')');
10009 : : }
8340 10010 : 600 : break;
10011 : :
7479 10012 : 18 : case T_MinMaxExpr:
10013 : : {
10014 : 18 : MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
10015 : :
10016 [ + + - ]: 18 : switch (minmaxexpr->op)
10017 : : {
10018 : 3 : case IS_GREATEST:
4430 rhaas@postgresql.org 10019 : 3 : appendStringInfoString(buf, "GREATEST(");
7479 tgl@sss.pgh.pa.us 10020 : 3 : break;
10021 : 15 : case IS_LEAST:
4430 rhaas@postgresql.org 10022 : 15 : appendStringInfoString(buf, "LEAST(");
7479 tgl@sss.pgh.pa.us 10023 : 15 : break;
10024 : : }
10025 : 18 : get_rule_expr((Node *) minmaxexpr->args, context, true);
10026 : 18 : appendStringInfoChar(buf, ')');
10027 : : }
10028 : 18 : break;
10029 : :
945 michael@paquier.xyz 10030 : 359 : case T_SQLValueFunction:
10031 : : {
10032 : 359 : SQLValueFunction *svf = (SQLValueFunction *) node;
10033 : :
10034 : : /*
10035 : : * Note: this code knows that typmod for time, timestamp, and
10036 : : * timestamptz just prints as integer.
10037 : : */
10038 [ + + + + : 359 : switch (svf->op)
+ + + + +
+ + + + +
+ - ]
10039 : : {
10040 : 52 : case SVFOP_CURRENT_DATE:
10041 : 52 : appendStringInfoString(buf, "CURRENT_DATE");
10042 : 52 : break;
10043 : 6 : case SVFOP_CURRENT_TIME:
10044 : 6 : appendStringInfoString(buf, "CURRENT_TIME");
10045 : 6 : break;
10046 : 6 : case SVFOP_CURRENT_TIME_N:
10047 : 6 : appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
10048 : 6 : break;
10049 : 6 : case SVFOP_CURRENT_TIMESTAMP:
10050 : 6 : appendStringInfoString(buf, "CURRENT_TIMESTAMP");
10051 : 6 : break;
10052 : 64 : case SVFOP_CURRENT_TIMESTAMP_N:
10053 : 64 : appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
10054 : : svf->typmod);
10055 : 64 : break;
10056 : 6 : case SVFOP_LOCALTIME:
10057 : 6 : appendStringInfoString(buf, "LOCALTIME");
10058 : 6 : break;
10059 : 6 : case SVFOP_LOCALTIME_N:
10060 : 6 : appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
10061 : 6 : break;
10062 : 15 : case SVFOP_LOCALTIMESTAMP:
10063 : 15 : appendStringInfoString(buf, "LOCALTIMESTAMP");
10064 : 15 : break;
10065 : 9 : case SVFOP_LOCALTIMESTAMP_N:
10066 : 9 : appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
10067 : : svf->typmod);
10068 : 9 : break;
10069 : 6 : case SVFOP_CURRENT_ROLE:
10070 : 6 : appendStringInfoString(buf, "CURRENT_ROLE");
10071 : 6 : break;
10072 : 148 : case SVFOP_CURRENT_USER:
10073 : 148 : appendStringInfoString(buf, "CURRENT_USER");
10074 : 148 : break;
10075 : 6 : case SVFOP_USER:
10076 : 6 : appendStringInfoString(buf, "USER");
10077 : 6 : break;
10078 : 17 : case SVFOP_SESSION_USER:
10079 : 17 : appendStringInfoString(buf, "SESSION_USER");
10080 : 17 : break;
10081 : 6 : case SVFOP_CURRENT_CATALOG:
10082 : 6 : appendStringInfoString(buf, "CURRENT_CATALOG");
10083 : 6 : break;
10084 : 6 : case SVFOP_CURRENT_SCHEMA:
10085 : 6 : appendStringInfoString(buf, "CURRENT_SCHEMA");
10086 : 6 : break;
10087 : : }
10088 : : }
10089 : 359 : break;
10090 : :
6933 tgl@sss.pgh.pa.us 10091 : 88 : case T_XmlExpr:
10092 : : {
6607 bruce@momjian.us 10093 : 88 : XmlExpr *xexpr = (XmlExpr *) node;
10094 : 88 : bool needcomma = false;
10095 : : ListCell *arg;
10096 : : ListCell *narg;
10097 : : Const *con;
10098 : :
6933 tgl@sss.pgh.pa.us 10099 [ + + + + : 88 : switch (xexpr->op)
+ + + -
- ]
10100 : : {
10101 : 8 : case IS_XMLCONCAT:
10102 : 8 : appendStringInfoString(buf, "XMLCONCAT(");
10103 : 8 : break;
10104 : 16 : case IS_XMLELEMENT:
10105 : 16 : appendStringInfoString(buf, "XMLELEMENT(");
10106 : 16 : break;
10107 : 8 : case IS_XMLFOREST:
10108 : 8 : appendStringInfoString(buf, "XMLFOREST(");
10109 : 8 : break;
10110 : 8 : case IS_XMLPARSE:
10111 : 8 : appendStringInfoString(buf, "XMLPARSE(");
10112 : 8 : break;
10113 : 8 : case IS_XMLPI:
10114 : 8 : appendStringInfoString(buf, "XMLPI(");
10115 : 8 : break;
10116 : 8 : case IS_XMLROOT:
10117 : 8 : appendStringInfoString(buf, "XMLROOT(");
10118 : 8 : break;
6892 peter_e@gmx.net 10119 : 32 : case IS_XMLSERIALIZE:
10120 : 32 : appendStringInfoString(buf, "XMLSERIALIZE(");
10121 : 32 : break;
6912 peter_e@gmx.net 10122 :UBC 0 : case IS_DOCUMENT:
10123 : 0 : break;
10124 : : }
6892 peter_e@gmx.net 10125 [ + + + + ]:CBC 88 : if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
10126 : : {
10127 [ + + ]: 40 : if (xexpr->xmloption == XMLOPTION_DOCUMENT)
10128 : 16 : appendStringInfoString(buf, "DOCUMENT ");
10129 : : else
10130 : 24 : appendStringInfoString(buf, "CONTENT ");
10131 : : }
6933 tgl@sss.pgh.pa.us 10132 [ + + ]: 88 : if (xexpr->name)
10133 : : {
10134 : 24 : appendStringInfo(buf, "NAME %s",
6928 peter_e@gmx.net 10135 : 24 : quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));
6933 tgl@sss.pgh.pa.us 10136 : 24 : needcomma = true;
10137 : : }
10138 [ + + ]: 88 : if (xexpr->named_args)
10139 : : {
10140 [ + + ]: 16 : if (xexpr->op != IS_XMLFOREST)
10141 : : {
10142 [ + - ]: 8 : if (needcomma)
10143 : 8 : appendStringInfoString(buf, ", ");
10144 : 8 : appendStringInfoString(buf, "XMLATTRIBUTES(");
10145 : 8 : needcomma = false;
10146 : : }
10147 [ + - + + : 56 : forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
+ - + + +
+ + - +
+ ]
10148 : : {
6607 bruce@momjian.us 10149 : 40 : Node *e = (Node *) lfirst(arg);
10150 : 40 : char *argname = strVal(lfirst(narg));
10151 : :
6933 tgl@sss.pgh.pa.us 10152 [ + + ]: 40 : if (needcomma)
10153 : 24 : appendStringInfoString(buf, ", ");
15 peter@eisentraut.org 10154 :GNC 40 : get_rule_expr(e, context, true);
6933 tgl@sss.pgh.pa.us 10155 :CBC 40 : appendStringInfo(buf, " AS %s",
6928 peter_e@gmx.net 10156 : 40 : quote_identifier(map_xml_name_to_sql_identifier(argname)));
6933 tgl@sss.pgh.pa.us 10157 : 40 : needcomma = true;
10158 : : }
10159 [ + + ]: 16 : if (xexpr->op != IS_XMLFOREST)
10160 : 8 : appendStringInfoChar(buf, ')');
10161 : : }
10162 [ + + ]: 88 : if (xexpr->args)
10163 : : {
10164 [ + + ]: 80 : if (needcomma)
10165 : 24 : appendStringInfoString(buf, ", ");
10166 [ + + + - : 80 : switch (xexpr->op)
- ]
10167 : : {
10168 : 64 : case IS_XMLCONCAT:
10169 : : case IS_XMLELEMENT:
10170 : : case IS_XMLFOREST:
10171 : : case IS_XMLPI:
10172 : : case IS_XMLSERIALIZE:
10173 : : /* no extra decoration needed */
10174 : 64 : get_rule_expr((Node *) xexpr->args, context, true);
10175 : 64 : break;
10176 : 8 : case IS_XMLPARSE:
6892 peter_e@gmx.net 10177 [ - + ]: 8 : Assert(list_length(xexpr->args) == 2);
10178 : :
6933 tgl@sss.pgh.pa.us 10179 : 8 : get_rule_expr((Node *) linitial(xexpr->args),
10180 : : context, true);
10181 : :
3173 10182 : 8 : con = lsecond_node(Const, xexpr->args);
6933 10183 [ - + ]: 8 : Assert(!con->constisnull);
10184 [ - + ]: 8 : if (DatumGetBool(con->constvalue))
6933 tgl@sss.pgh.pa.us 10185 :UBC 0 : appendStringInfoString(buf,
10186 : : " PRESERVE WHITESPACE");
10187 : : else
6933 tgl@sss.pgh.pa.us 10188 :CBC 8 : appendStringInfoString(buf,
10189 : : " STRIP WHITESPACE");
10190 : 8 : break;
10191 : 8 : case IS_XMLROOT:
10192 [ - + ]: 8 : Assert(list_length(xexpr->args) == 3);
10193 : :
10194 : 8 : get_rule_expr((Node *) linitial(xexpr->args),
10195 : : context, true);
10196 : :
10197 : 8 : appendStringInfoString(buf, ", VERSION ");
10198 : 8 : con = (Const *) lsecond(xexpr->args);
10199 [ + - ]: 8 : if (IsA(con, Const) &&
10200 [ + - ]: 8 : con->constisnull)
10201 : 8 : appendStringInfoString(buf, "NO VALUE");
10202 : : else
6933 tgl@sss.pgh.pa.us 10203 :UBC 0 : get_rule_expr((Node *) con, context, false);
10204 : :
3173 tgl@sss.pgh.pa.us 10205 :CBC 8 : con = lthird_node(Const, xexpr->args);
6933 10206 [ + - ]: 8 : if (con->constisnull)
10207 : : /* suppress STANDALONE NO VALUE */ ;
10208 : : else
10209 : : {
6892 peter_e@gmx.net 10210 [ + - - - ]: 8 : switch (DatumGetInt32(con->constvalue))
10211 : : {
10212 : 8 : case XML_STANDALONE_YES:
10213 : 8 : appendStringInfoString(buf,
10214 : : ", STANDALONE YES");
10215 : 8 : break;
6892 peter_e@gmx.net 10216 :UBC 0 : case XML_STANDALONE_NO:
10217 : 0 : appendStringInfoString(buf,
10218 : : ", STANDALONE NO");
10219 : 0 : break;
10220 : 0 : case XML_STANDALONE_NO_VALUE:
10221 : 0 : appendStringInfoString(buf,
10222 : : ", STANDALONE NO VALUE");
10223 : 0 : break;
10224 : 0 : default:
10225 : 0 : break;
10226 : : }
10227 : : }
6933 tgl@sss.pgh.pa.us 10228 :CBC 8 : break;
6912 peter_e@gmx.net 10229 :UBC 0 : case IS_DOCUMENT:
10230 : 0 : get_rule_expr_paren((Node *) xexpr->args, context, false, node);
10231 : 0 : break;
10232 : : }
10233 : : }
6892 peter_e@gmx.net 10234 [ + + ]:CBC 88 : if (xexpr->op == IS_XMLSERIALIZE)
10235 : : {
5387 tgl@sss.pgh.pa.us 10236 : 32 : appendStringInfo(buf, " AS %s",
10237 : : format_type_with_typemod(xexpr->type,
10238 : : xexpr->typmod));
299 michael@paquier.xyz 10239 [ + + ]: 32 : if (xexpr->indent)
10240 : 8 : appendStringInfoString(buf, " INDENT");
10241 : : else
10242 : 24 : appendStringInfoString(buf, " NO INDENT");
10243 : : }
10244 : :
6912 peter_e@gmx.net 10245 [ - + ]: 88 : if (xexpr->op == IS_DOCUMENT)
6912 peter_e@gmx.net 10246 :UBC 0 : appendStringInfoString(buf, " IS DOCUMENT");
10247 : : else
6912 peter_e@gmx.net 10248 :CBC 88 : appendStringInfoChar(buf, ')');
10249 : : }
6933 tgl@sss.pgh.pa.us 10250 : 88 : break;
10251 : :
8947 10252 : 1329 : case T_NullTest:
10253 : : {
8819 bruce@momjian.us 10254 : 1329 : NullTest *ntest = (NullTest *) node;
10255 : :
8176 tgl@sss.pgh.pa.us 10256 [ + + ]: 1329 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 10257 : 1301 : appendStringInfoChar(buf, '(');
8176 tgl@sss.pgh.pa.us 10258 : 1329 : get_rule_expr_paren((Node *) ntest->arg, context, true, node);
10259 : :
10260 : : /*
10261 : : * For scalar inputs, we prefer to print as IS [NOT] NULL,
10262 : : * which is shorter and traditional. If it's a rowtype input
10263 : : * but we're applying a scalar test, must print IS [NOT]
10264 : : * DISTINCT FROM NULL to be semantically correct.
10265 : : */
3429 10266 [ + + ]: 1329 : if (ntest->argisrow ||
10267 [ + + ]: 1298 : !type_is_rowtype(exprType((Node *) ntest->arg)))
10268 : : {
10269 [ + + - ]: 1320 : switch (ntest->nulltesttype)
10270 : : {
10271 : 412 : case IS_NULL:
10272 : 412 : appendStringInfoString(buf, " IS NULL");
10273 : 412 : break;
10274 : 908 : case IS_NOT_NULL:
10275 : 908 : appendStringInfoString(buf, " IS NOT NULL");
10276 : 908 : break;
3429 tgl@sss.pgh.pa.us 10277 :UBC 0 : default:
10278 [ # # ]: 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10279 : : (int) ntest->nulltesttype);
10280 : : }
10281 : : }
10282 : : else
10283 : : {
3429 tgl@sss.pgh.pa.us 10284 [ + + - ]:CBC 9 : switch (ntest->nulltesttype)
10285 : : {
10286 : 3 : case IS_NULL:
10287 : 3 : appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
10288 : 3 : break;
10289 : 6 : case IS_NOT_NULL:
10290 : 6 : appendStringInfoString(buf, " IS DISTINCT FROM NULL");
10291 : 6 : break;
3429 tgl@sss.pgh.pa.us 10292 :UBC 0 : default:
10293 [ # # ]: 0 : elog(ERROR, "unrecognized nulltesttype: %d",
10294 : : (int) ntest->nulltesttype);
10295 : : }
10296 : : }
8176 tgl@sss.pgh.pa.us 10297 [ + + ]:CBC 1329 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 10298 : 1301 : appendStringInfoChar(buf, ')');
10299 : : }
8947 tgl@sss.pgh.pa.us 10300 : 1329 : break;
10301 : :
10302 : 153 : case T_BooleanTest:
10303 : : {
8819 bruce@momjian.us 10304 : 153 : BooleanTest *btest = (BooleanTest *) node;
10305 : :
8176 tgl@sss.pgh.pa.us 10306 [ + - ]: 153 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 10307 : 153 : appendStringInfoChar(buf, '(');
8176 tgl@sss.pgh.pa.us 10308 : 153 : get_rule_expr_paren((Node *) btest->arg, context, false, node);
8819 bruce@momjian.us 10309 [ + + - + : 153 : switch (btest->booltesttype)
+ + - ]
10310 : : {
10311 : 18 : case IS_TRUE:
4430 rhaas@postgresql.org 10312 : 18 : appendStringInfoString(buf, " IS TRUE");
8947 tgl@sss.pgh.pa.us 10313 : 18 : break;
8819 bruce@momjian.us 10314 : 69 : case IS_NOT_TRUE:
4430 rhaas@postgresql.org 10315 : 69 : appendStringInfoString(buf, " IS NOT TRUE");
8947 tgl@sss.pgh.pa.us 10316 : 69 : break;
8819 bruce@momjian.us 10317 :UBC 0 : case IS_FALSE:
4430 rhaas@postgresql.org 10318 : 0 : appendStringInfoString(buf, " IS FALSE");
8947 tgl@sss.pgh.pa.us 10319 : 0 : break;
8819 bruce@momjian.us 10320 :CBC 27 : case IS_NOT_FALSE:
4430 rhaas@postgresql.org 10321 : 27 : appendStringInfoString(buf, " IS NOT FALSE");
8947 tgl@sss.pgh.pa.us 10322 : 27 : break;
8819 bruce@momjian.us 10323 : 12 : case IS_UNKNOWN:
4430 rhaas@postgresql.org 10324 : 12 : appendStringInfoString(buf, " IS UNKNOWN");
8947 tgl@sss.pgh.pa.us 10325 : 12 : break;
8819 bruce@momjian.us 10326 : 27 : case IS_NOT_UNKNOWN:
4430 rhaas@postgresql.org 10327 : 27 : appendStringInfoString(buf, " IS NOT UNKNOWN");
8947 tgl@sss.pgh.pa.us 10328 : 27 : break;
8819 bruce@momjian.us 10329 :UBC 0 : default:
8179 tgl@sss.pgh.pa.us 10330 [ # # ]: 0 : elog(ERROR, "unrecognized booltesttype: %d",
10331 : : (int) btest->booltesttype);
10332 : : }
8176 tgl@sss.pgh.pa.us 10333 [ + - ]:CBC 153 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 10334 : 153 : appendStringInfoChar(buf, ')');
10335 : : }
8947 tgl@sss.pgh.pa.us 10336 : 153 : break;
10337 : :
8353 10338 : 58 : case T_CoerceToDomain:
10339 : : {
10340 : 58 : CoerceToDomain *ctest = (CoerceToDomain *) node;
8171 bruce@momjian.us 10341 : 58 : Node *arg = (Node *) ctest->arg;
10342 : :
8353 tgl@sss.pgh.pa.us 10343 [ + + ]: 58 : if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
10344 [ + + ]: 24 : !showimplicit)
10345 : : {
10346 : : /* don't show the implicit cast */
10347 : 16 : get_rule_expr(arg, context, false);
10348 : : }
10349 : : else
10350 : : {
6850 10351 : 42 : get_coercion_expr(arg, context,
10352 : : ctest->resulttype,
10353 : : ctest->resulttypmod,
10354 : : node);
10355 : : }
10356 : : }
8509 10357 : 58 : break;
10358 : :
8353 10359 : 219 : case T_CoerceToDomainValue:
4430 rhaas@postgresql.org 10360 : 219 : appendStringInfoString(buf, "VALUE");
8681 tgl@sss.pgh.pa.us 10361 : 219 : break;
10362 : :
8203 10363 : 38 : case T_SetToDefault:
4430 rhaas@postgresql.org 10364 : 38 : appendStringInfoString(buf, "DEFAULT");
8203 tgl@sss.pgh.pa.us 10365 : 38 : break;
10366 : :
6764 10367 : 12 : case T_CurrentOfExpr:
10368 : : {
10369 : 12 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
10370 : :
10371 [ + - ]: 12 : if (cexpr->cursor_name)
10372 : 12 : appendStringInfo(buf, "CURRENT OF %s",
10373 : 12 : quote_identifier(cexpr->cursor_name));
10374 : : else
6764 tgl@sss.pgh.pa.us 10375 :UBC 0 : appendStringInfo(buf, "CURRENT OF $%d",
10376 : : cexpr->cursor_param);
10377 : : }
6764 tgl@sss.pgh.pa.us 10378 :CBC 12 : break;
10379 : :
3078 tgl@sss.pgh.pa.us 10380 :UBC 0 : case T_NextValueExpr:
10381 : : {
10382 : 0 : NextValueExpr *nvexpr = (NextValueExpr *) node;
10383 : :
10384 : : /*
10385 : : * This isn't exactly nextval(), but that seems close enough
10386 : : * for EXPLAIN's purposes.
10387 : : */
10388 : 0 : appendStringInfoString(buf, "nextval(");
10389 : 0 : simple_quote_literal(buf,
10390 : 0 : generate_relation_name(nvexpr->seqid,
10391 : : NIL));
10392 : 0 : appendStringInfoChar(buf, ')');
10393 : : }
10394 : 0 : break;
10395 : :
3865 andres@anarazel.de 10396 :CBC 12 : case T_InferenceElem:
10397 : : {
3861 bruce@momjian.us 10398 : 12 : InferenceElem *iexpr = (InferenceElem *) node;
10399 : : bool save_varprefix;
10400 : : bool need_parens;
10401 : :
10402 : : /*
10403 : : * InferenceElem can only refer to target relation, so a
10404 : : * prefix is not useful, and indeed would cause parse errors.
10405 : : */
3601 tgl@sss.pgh.pa.us 10406 : 12 : save_varprefix = context->varprefix;
3865 andres@anarazel.de 10407 : 12 : context->varprefix = false;
10408 : :
10409 : : /*
10410 : : * Parenthesize the element unless it's a simple Var or a bare
10411 : : * function call. Follows pg_get_indexdef_worker().
10412 : : */
10413 : 12 : need_parens = !IsA(iexpr->expr, Var);
10414 [ - + ]: 12 : if (IsA(iexpr->expr, FuncExpr) &&
3865 andres@anarazel.de 10415 [ # # ]:UBC 0 : ((FuncExpr *) iexpr->expr)->funcformat ==
10416 : : COERCE_EXPLICIT_CALL)
10417 : 0 : need_parens = false;
10418 : :
3865 andres@anarazel.de 10419 [ - + ]:CBC 12 : if (need_parens)
3865 andres@anarazel.de 10420 :UBC 0 : appendStringInfoChar(buf, '(');
3865 andres@anarazel.de 10421 :CBC 12 : get_rule_expr((Node *) iexpr->expr,
10422 : : context, false);
10423 [ - + ]: 12 : if (need_parens)
3865 andres@anarazel.de 10424 :UBC 0 : appendStringInfoChar(buf, ')');
10425 : :
3601 tgl@sss.pgh.pa.us 10426 :CBC 12 : context->varprefix = save_varprefix;
10427 : :
3865 andres@anarazel.de 10428 [ + + ]: 12 : if (iexpr->infercollid)
10429 : 6 : appendStringInfo(buf, " COLLATE %s",
10430 : : generate_collation_name(iexpr->infercollid));
10431 : :
10432 : : /* Add the operator class name, if not default */
10433 [ + + ]: 12 : if (iexpr->inferopclass)
10434 : : {
3861 bruce@momjian.us 10435 : 6 : Oid inferopclass = iexpr->inferopclass;
10436 : 6 : Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
10437 : :
3865 andres@anarazel.de 10438 : 6 : get_opclass_name(inferopclass, inferopcinputtype, buf);
10439 : : }
10440 : : }
10441 : 12 : break;
10442 : :
335 dean.a.rasheed@gmail 10443 : 6 : case T_ReturningExpr:
10444 : : {
10445 : 6 : ReturningExpr *retExpr = (ReturningExpr *) node;
10446 : :
10447 : : /*
10448 : : * We cannot see a ReturningExpr in rule deparsing, only while
10449 : : * EXPLAINing a query plan (ReturningExpr nodes are only ever
10450 : : * adding during query rewriting). Just display the expression
10451 : : * returned (an expanded view column).
10452 : : */
10453 : 6 : get_rule_expr((Node *) retExpr->retexpr, context, showimplicit);
10454 : : }
10455 : 6 : break;
10456 : :
3297 rhaas@postgresql.org 10457 : 2356 : case T_PartitionBoundSpec:
10458 : : {
10459 : 2356 : PartitionBoundSpec *spec = (PartitionBoundSpec *) node;
10460 : : ListCell *cell;
10461 : : char *sep;
10462 : :
3022 10463 [ + + ]: 2356 : if (spec->is_default)
10464 : : {
10465 : 132 : appendStringInfoString(buf, "DEFAULT");
10466 : 132 : break;
10467 : : }
10468 : :
3297 10469 [ + + + - ]: 2224 : switch (spec->strategy)
10470 : : {
2960 10471 : 153 : case PARTITION_STRATEGY_HASH:
10472 [ + - - + ]: 153 : Assert(spec->modulus > 0 && spec->remainder >= 0);
10473 [ - + ]: 153 : Assert(spec->modulus > spec->remainder);
10474 : :
10475 : 153 : appendStringInfoString(buf, "FOR VALUES");
10476 : 153 : appendStringInfo(buf, " WITH (modulus %d, remainder %d)",
10477 : : spec->modulus, spec->remainder);
10478 : 153 : break;
10479 : :
3297 10480 : 726 : case PARTITION_STRATEGY_LIST:
10481 [ - + ]: 726 : Assert(spec->listdatums != NIL);
10482 : :
3125 tgl@sss.pgh.pa.us 10483 : 726 : appendStringInfoString(buf, "FOR VALUES IN (");
3297 rhaas@postgresql.org 10484 : 726 : sep = "";
3249 10485 [ + - + + : 2013 : foreach(cell, spec->listdatums)
+ + ]
10486 : : {
1612 peter@eisentraut.org 10487 : 1287 : Const *val = lfirst_node(Const, cell);
10488 : :
3297 rhaas@postgresql.org 10489 : 1287 : appendStringInfoString(buf, sep);
10490 : 1287 : get_const_expr(val, context, -1);
10491 : 1287 : sep = ", ";
10492 : : }
10493 : :
3046 peter_e@gmx.net 10494 : 726 : appendStringInfoChar(buf, ')');
3297 rhaas@postgresql.org 10495 : 726 : break;
10496 : :
10497 : 1345 : case PARTITION_STRATEGY_RANGE:
10498 [ + - + - : 1345 : Assert(spec->lowerdatums != NIL &&
- + ]
10499 : : spec->upperdatums != NIL &&
10500 : : list_length(spec->lowerdatums) ==
10501 : : list_length(spec->upperdatums));
10502 : :
3051 10503 : 1345 : appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
10504 : : get_range_partbound_string(spec->lowerdatums),
10505 : : get_range_partbound_string(spec->upperdatums));
3297 10506 : 1345 : break;
10507 : :
3297 rhaas@postgresql.org 10508 :UBC 0 : default:
10509 [ # # ]: 0 : elog(ERROR, "unrecognized partition strategy: %d",
10510 : : (int) spec->strategy);
10511 : : break;
10512 : : }
10513 : : }
3297 rhaas@postgresql.org 10514 :CBC 2224 : break;
10515 : :
994 alvherre@alvh.no-ip. 10516 : 75 : case T_JsonValueExpr:
10517 : : {
10518 : 75 : JsonValueExpr *jve = (JsonValueExpr *) node;
10519 : :
10520 : 75 : get_rule_expr((Node *) jve->raw_expr, context, false);
10521 : 75 : get_json_format(jve->format, context->buf);
10522 : : }
10523 : 75 : break;
10524 : :
10525 : 93 : case T_JsonConstructorExpr:
10526 : 93 : get_json_constructor((JsonConstructorExpr *) node, context, false);
10527 : 93 : break;
10528 : :
992 10529 : 30 : case T_JsonIsPredicate:
10530 : : {
10531 : 30 : JsonIsPredicate *pred = (JsonIsPredicate *) node;
10532 : :
10533 [ + + ]: 30 : if (!PRETTY_PAREN(context))
10534 : 15 : appendStringInfoChar(context->buf, '(');
10535 : :
10536 : 30 : get_rule_expr_paren(pred->expr, context, true, node);
10537 : :
10538 : 30 : appendStringInfoString(context->buf, " IS JSON");
10539 : :
10540 : : /* TODO: handle FORMAT clause */
10541 : :
10542 [ + + + + ]: 30 : switch (pred->item_type)
10543 : : {
10544 : 6 : case JS_TYPE_SCALAR:
10545 : 6 : appendStringInfoString(context->buf, " SCALAR");
10546 : 6 : break;
10547 : 6 : case JS_TYPE_ARRAY:
10548 : 6 : appendStringInfoString(context->buf, " ARRAY");
10549 : 6 : break;
10550 : 6 : case JS_TYPE_OBJECT:
10551 : 6 : appendStringInfoString(context->buf, " OBJECT");
10552 : 6 : break;
10553 : 12 : default:
10554 : 12 : break;
10555 : : }
10556 : :
10557 [ + + ]: 30 : if (pred->unique_keys)
10558 : 6 : appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
10559 : :
10560 [ + + ]: 30 : if (!PRETTY_PAREN(context))
10561 : 15 : appendStringInfoChar(context->buf, ')');
10562 : : }
10563 : 30 : break;
10564 : :
636 amitlan@postgresql.o 10565 : 30 : case T_JsonExpr:
10566 : : {
10567 : 30 : JsonExpr *jexpr = (JsonExpr *) node;
10568 : :
10569 [ + + + - ]: 30 : switch (jexpr->op)
10570 : : {
10571 : 6 : case JSON_EXISTS_OP:
10572 : 6 : appendStringInfoString(buf, "JSON_EXISTS(");
10573 : 6 : break;
10574 : 18 : case JSON_QUERY_OP:
10575 : 18 : appendStringInfoString(buf, "JSON_QUERY(");
10576 : 18 : break;
10577 : 6 : case JSON_VALUE_OP:
10578 : 6 : appendStringInfoString(buf, "JSON_VALUE(");
10579 : 6 : break;
636 amitlan@postgresql.o 10580 :UBC 0 : default:
10581 [ # # ]: 0 : elog(ERROR, "unrecognized JsonExpr op: %d",
10582 : : (int) jexpr->op);
10583 : : }
10584 : :
636 amitlan@postgresql.o 10585 :CBC 30 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
10586 : :
10587 : 30 : appendStringInfoString(buf, ", ");
10588 : :
10589 : 30 : get_json_path_spec(jexpr->path_spec, context, showimplicit);
10590 : :
10591 [ + + ]: 30 : if (jexpr->passing_values)
10592 : : {
10593 : : ListCell *lc1,
10594 : : *lc2;
10595 : 6 : bool needcomma = false;
10596 : :
10597 : 6 : appendStringInfoString(buf, " PASSING ");
10598 : :
10599 [ + - + + : 24 : forboth(lc1, jexpr->passing_names,
+ - + + +
+ + - +
+ ]
10600 : : lc2, jexpr->passing_values)
10601 : : {
10602 [ + + ]: 18 : if (needcomma)
10603 : 12 : appendStringInfoString(buf, ", ");
10604 : 18 : needcomma = true;
10605 : :
10606 : 18 : get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
10607 : 18 : appendStringInfo(buf, " AS %s",
339 dean.a.rasheed@gmail 10608 : 18 : quote_identifier(lfirst_node(String, lc1)->sval));
10609 : : }
10610 : : }
10611 : :
636 amitlan@postgresql.o 10612 [ + + ]: 30 : if (jexpr->op != JSON_EXISTS_OP ||
10613 [ - + ]: 6 : jexpr->returning->typid != BOOLOID)
10614 : 24 : get_json_returning(jexpr->returning, context->buf,
10615 : 24 : jexpr->op == JSON_QUERY_OP);
10616 : :
10617 : 30 : get_json_expr_options(jexpr, context,
10618 [ + + ]: 30 : jexpr->op != JSON_EXISTS_OP ?
10619 : : JSON_BEHAVIOR_NULL :
10620 : : JSON_BEHAVIOR_FALSE);
10621 : :
616 drowley@postgresql.o 10622 : 30 : appendStringInfoChar(buf, ')');
10623 : : }
636 amitlan@postgresql.o 10624 : 30 : break;
10625 : :
7741 tgl@sss.pgh.pa.us 10626 : 1345 : case T_List:
10627 : : {
10628 : : char *sep;
10629 : : ListCell *l;
10630 : :
10631 : 1345 : sep = "";
10632 [ + - + + : 3799 : foreach(l, (List *) node)
+ + ]
10633 : : {
7536 neilc@samurai.com 10634 : 2454 : appendStringInfoString(buf, sep);
7741 tgl@sss.pgh.pa.us 10635 : 2454 : get_rule_expr((Node *) lfirst(l), context, showimplicit);
10636 : 2454 : sep = ", ";
10637 : : }
10638 : : }
10639 : 1345 : break;
10640 : :
3206 alvherre@alvh.no-ip. 10641 : 36 : case T_TableFunc:
10642 : 36 : get_tablefunc((TableFunc *) node, context, showimplicit);
10643 : 36 : break;
10644 : :
9969 bruce@momjian.us 10645 :UBC 0 : default:
8179 tgl@sss.pgh.pa.us 10646 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
10647 : : break;
10648 : : }
10649 : : }
10650 : :
10651 : : /*
10652 : : * get_rule_expr_toplevel - Parse back a toplevel expression
10653 : : *
10654 : : * Same as get_rule_expr(), except that if the expr is just a Var, we pass
10655 : : * istoplevel = true not false to get_variable(). This causes whole-row Vars
10656 : : * to get printed with decoration that will prevent expansion of "*".
10657 : : * We need to use this in contexts such as ROW() and VALUES(), where the
10658 : : * parser would expand "foo.*" appearing at top level. (In principle we'd
10659 : : * use this in get_target_list() too, but that has additional worries about
10660 : : * whether to print AS, so it needs to invoke get_variable() directly anyway.)
10661 : : */
10662 : : static void
3685 tgl@sss.pgh.pa.us 10663 :CBC 1522 : get_rule_expr_toplevel(Node *node, deparse_context *context,
10664 : : bool showimplicit)
10665 : : {
10666 [ + - + + ]: 1522 : if (node && IsA(node, Var))
10667 : 607 : (void) get_variable((Var *) node, 0, true, context);
10668 : : else
10669 : 915 : get_rule_expr(node, context, showimplicit);
10670 : 1522 : }
10671 : :
10672 : : /*
10673 : : * get_rule_list_toplevel - Parse back a list of toplevel expressions
10674 : : *
10675 : : * Apply get_rule_expr_toplevel() to each element of a List.
10676 : : *
10677 : : * This adds commas between the expressions, but caller is responsible
10678 : : * for printing surrounding decoration.
10679 : : */
10680 : : static void
1434 10681 : 252 : get_rule_list_toplevel(List *lst, deparse_context *context,
10682 : : bool showimplicit)
10683 : : {
10684 : : const char *sep;
10685 : : ListCell *lc;
10686 : :
10687 : 252 : sep = "";
10688 [ + - + + : 859 : foreach(lc, lst)
+ + ]
10689 : : {
10690 : 607 : Node *e = (Node *) lfirst(lc);
10691 : :
10692 : 607 : appendStringInfoString(context->buf, sep);
10693 : 607 : get_rule_expr_toplevel(e, context, showimplicit);
10694 : 607 : sep = ", ";
10695 : : }
10696 : 252 : }
10697 : :
10698 : : /*
10699 : : * get_rule_expr_funccall - Parse back a function-call expression
10700 : : *
10701 : : * Same as get_rule_expr(), except that we guarantee that the output will
10702 : : * look like a function call, or like one of the things the grammar treats as
10703 : : * equivalent to a function call (see the func_expr_windowless production).
10704 : : * This is needed in places where the grammar uses func_expr_windowless and
10705 : : * you can't substitute a parenthesized a_expr. If what we have isn't going
10706 : : * to look like a function call, wrap it in a dummy CAST() expression, which
10707 : : * will satisfy the grammar --- and, indeed, is likely what the user wrote to
10708 : : * produce such a thing.
10709 : : */
10710 : : static void
3079 10711 : 438 : get_rule_expr_funccall(Node *node, deparse_context *context,
10712 : : bool showimplicit)
10713 : : {
10714 [ + + ]: 438 : if (looks_like_function(node))
10715 : 432 : get_rule_expr(node, context, showimplicit);
10716 : : else
10717 : : {
10718 : 6 : StringInfo buf = context->buf;
10719 : :
10720 : 6 : appendStringInfoString(buf, "CAST(");
10721 : : /* no point in showing any top-level implicit cast */
10722 : 6 : get_rule_expr(node, context, false);
10723 : 6 : appendStringInfo(buf, " AS %s)",
10724 : : format_type_with_typemod(exprType(node),
10725 : : exprTypmod(node)));
10726 : : }
10727 : 438 : }
10728 : :
10729 : : /*
10730 : : * Helper function to identify node types that satisfy func_expr_windowless.
10731 : : * If in doubt, "false" is always a safe answer.
10732 : : */
10733 : : static bool
10734 : 1033 : looks_like_function(Node *node)
10735 : : {
10736 [ - + ]: 1033 : if (node == NULL)
3079 tgl@sss.pgh.pa.us 10737 :UBC 0 : return false; /* probably shouldn't happen */
3079 tgl@sss.pgh.pa.us 10738 [ + + + ]:CBC 1033 : switch (nodeTag(node))
10739 : : {
10740 : 452 : case T_FuncExpr:
10741 : : /* OK, unless it's going to deparse as a cast */
1869 10742 [ + + ]: 461 : return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
10743 [ + + ]: 9 : ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
3079 10744 : 54 : case T_NullIfExpr:
10745 : : case T_CoalesceExpr:
10746 : : case T_MinMaxExpr:
10747 : : case T_SQLValueFunction:
10748 : : case T_XmlExpr:
10749 : : case T_JsonExpr:
10750 : : /* these are all accepted by func_expr_common_subexpr */
10751 : 54 : return true;
10752 : 527 : default:
10753 : 527 : break;
10754 : : }
10755 : 527 : return false;
10756 : : }
10757 : :
10758 : :
10759 : : /*
10760 : : * get_oper_expr - Parse back an OpExpr node
10761 : : */
10762 : : static void
8167 bruce@momjian.us 10763 : 30922 : get_oper_expr(OpExpr *expr, deparse_context *context)
10764 : : {
8629 tgl@sss.pgh.pa.us 10765 : 30922 : StringInfo buf = context->buf;
8406 10766 : 30922 : Oid opno = expr->opno;
8629 10767 : 30922 : List *args = expr->args;
10768 : :
8176 10769 [ + + ]: 30922 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 10770 : 29716 : appendStringInfoChar(buf, '(');
7871 neilc@samurai.com 10771 [ + + ]: 30922 : if (list_length(args) == 2)
10772 : : {
10773 : : /* binary operator */
7875 10774 : 30907 : Node *arg1 = (Node *) linitial(args);
8505 bruce@momjian.us 10775 : 30907 : Node *arg2 = (Node *) lsecond(args);
10776 : :
8171 10777 : 30907 : get_rule_expr_paren(arg1, context, true, (Node *) expr);
8629 tgl@sss.pgh.pa.us 10778 : 30907 : appendStringInfo(buf, " %s ",
10779 : : generate_operator_name(opno,
10780 : : exprType(arg1),
10781 : : exprType(arg2)));
8171 bruce@momjian.us 10782 : 30907 : get_rule_expr_paren(arg2, context, true, (Node *) expr);
10783 : : }
10784 : : else
10785 : : {
10786 : : /* prefix operator */
7875 neilc@samurai.com 10787 : 15 : Node *arg = (Node *) linitial(args);
10788 : :
1917 tgl@sss.pgh.pa.us 10789 : 15 : appendStringInfo(buf, "%s ",
10790 : : generate_operator_name(opno,
10791 : : InvalidOid,
10792 : : exprType(arg)));
10793 : 15 : get_rule_expr_paren(arg, context, true, (Node *) expr);
10794 : : }
8176 10795 [ + + ]: 30922 : if (!PRETTY_PAREN(context))
8171 bruce@momjian.us 10796 : 29716 : appendStringInfoChar(buf, ')');
8629 tgl@sss.pgh.pa.us 10797 : 30922 : }
10798 : :
10799 : : /*
10800 : : * get_func_expr - Parse back a FuncExpr node
10801 : : */
10802 : : static void
8167 bruce@momjian.us 10803 : 6362 : get_func_expr(FuncExpr *expr, deparse_context *context,
10804 : : bool showimplicit)
10805 : : {
9572 tgl@sss.pgh.pa.us 10806 : 6362 : StringInfo buf = context->buf;
8406 10807 : 6362 : Oid funcoid = expr->funcid;
10808 : : Oid argtypes[FUNC_MAX_ARGS];
10809 : : int nargs;
10810 : : List *argnames;
10811 : : bool use_variadic;
10812 : : ListCell *l;
10813 : :
10814 : : /*
10815 : : * If the function call came from an implicit coercion, then just show the
10816 : : * first argument --- unless caller wants to see implicit coercions.
10817 : : */
10818 [ + + + + ]: 6362 : if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
10819 : : {
7875 neilc@samurai.com 10820 : 669 : get_rule_expr_paren((Node *) linitial(expr->args), context,
10821 : : false, (Node *) expr);
8491 tgl@sss.pgh.pa.us 10822 : 1648 : return;
10823 : : }
10824 : :
10825 : : /*
10826 : : * If the function call came from a cast, then show the first argument
10827 : : * plus an explicit cast operation.
10828 : : */
8406 10829 [ + + ]: 5693 : if (expr->funcformat == COERCE_EXPLICIT_CAST ||
10830 [ + + ]: 5347 : expr->funcformat == COERCE_IMPLICIT_CAST)
10831 : : {
7875 neilc@samurai.com 10832 : 892 : Node *arg = linitial(expr->args);
8406 tgl@sss.pgh.pa.us 10833 : 892 : Oid rettype = expr->funcresulttype;
10834 : : int32 coercedTypmod;
10835 : :
10836 : : /* Get the typmod if this is a length-coercion function */
8491 10837 : 892 : (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
10838 : :
6850 10839 : 892 : get_coercion_expr(arg, context,
10840 : : rettype, coercedTypmod,
10841 : : (Node *) expr);
10842 : :
9426 10843 : 892 : return;
10844 : : }
10845 : :
10846 : : /*
10847 : : * If the function was called using one of the SQL spec's random special
10848 : : * syntaxes, try to reproduce that. If we don't recognize the function,
10849 : : * fall through.
10850 : : */
1869 10851 [ + + ]: 4801 : if (expr->funcformat == COERCE_SQL_SYNTAX)
10852 : : {
10853 [ + + ]: 90 : if (get_func_sql_syntax(expr, context))
10854 : 87 : return;
10855 : : }
10856 : :
10857 : : /*
10858 : : * Normal function: display as proname(args). First we need to extract
10859 : : * the argument datatypes.
10860 : : */
6198 10861 [ - + ]: 4714 : if (list_length(expr->args) > FUNC_MAX_ARGS)
6198 tgl@sss.pgh.pa.us 10862 [ # # ]:UBC 0 : ereport(ERROR,
10863 : : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
10864 : : errmsg("too many arguments")));
8629 tgl@sss.pgh.pa.us 10865 :CBC 4714 : nargs = 0;
5914 10866 : 4714 : argnames = NIL;
8629 10867 [ + + + + : 9751 : foreach(l, expr->args)
+ + ]
10868 : : {
5773 bruce@momjian.us 10869 : 5037 : Node *arg = (Node *) lfirst(l);
10870 : :
5914 tgl@sss.pgh.pa.us 10871 [ + + ]: 5037 : if (IsA(arg, NamedArgExpr))
10872 : 15 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
10873 : 5037 : argtypes[nargs] = exprType(arg);
8629 10874 : 5037 : nargs++;
10875 : : }
10876 : :
10877 : 4714 : appendStringInfo(buf, "%s(",
10878 : : generate_function_name(funcoid, nargs,
10879 : : argnames, argtypes,
4713 10880 : 4714 : expr->funcvariadic,
10881 : : &use_variadic,
475 10882 : 4714 : context->inGroupBy));
6363 10883 : 4714 : nargs = 0;
10884 [ + + + + : 9751 : foreach(l, expr->args)
+ + ]
10885 : : {
10886 [ + + ]: 5037 : if (nargs++ > 0)
10887 : 918 : appendStringInfoString(buf, ", ");
2347 10888 [ + + + - ]: 5037 : if (use_variadic && lnext(expr->args, l) == NULL)
6363 10889 : 6 : appendStringInfoString(buf, "VARIADIC ");
10890 : 5037 : get_rule_expr((Node *) lfirst(l), context, true);
10891 : : }
9432 10892 : 4714 : appendStringInfoChar(buf, ')');
10893 : : }
10894 : :
10895 : : /*
10896 : : * get_agg_expr - Parse back an Aggref node
10897 : : */
10898 : : static void
1203 andrew@dunslane.net 10899 : 2373 : get_agg_expr(Aggref *aggref, deparse_context *context,
10900 : : Aggref *original_aggref)
10901 : : {
994 alvherre@alvh.no-ip. 10902 : 2373 : get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
10903 : : false);
10904 : 2373 : }
10905 : :
10906 : : /*
10907 : : * get_agg_expr_helper - subroutine for get_agg_expr and
10908 : : * get_json_agg_constructor
10909 : : */
10910 : : static void
10911 : 2400 : get_agg_expr_helper(Aggref *aggref, deparse_context *context,
10912 : : Aggref *original_aggref, const char *funcname,
10913 : : const char *options, bool is_json_objectagg)
10914 : : {
8651 tgl@sss.pgh.pa.us 10915 : 2400 : StringInfo buf = context->buf;
10916 : : Oid argtypes[FUNC_MAX_ARGS];
10917 : : int nargs;
994 alvherre@alvh.no-ip. 10918 : 2400 : bool use_variadic = false;
10919 : :
10920 : : /*
10921 : : * For a combining aggregate, we look up and deparse the corresponding
10922 : : * partial aggregate instead. This is necessary because our input
10923 : : * argument list has been replaced; the new argument list always has just
10924 : : * one element, which will point to a partial Aggref that supplies us with
10925 : : * transition states to combine.
10926 : : */
3461 tgl@sss.pgh.pa.us 10927 [ + + ]: 2400 : if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
10928 : : {
10929 : : TargetEntry *tle;
10930 : :
3521 rhaas@postgresql.org 10931 [ - + ]: 388 : Assert(list_length(aggref->args) == 1);
2198 tgl@sss.pgh.pa.us 10932 : 388 : tle = linitial_node(TargetEntry, aggref->args);
10933 : 388 : resolve_special_varno((Node *) tle->expr, context,
10934 : : get_agg_combine_expr, original_aggref);
3521 rhaas@postgresql.org 10935 : 388 : return;
10936 : : }
10937 : :
10938 : : /*
10939 : : * Mark as PARTIAL, if appropriate. We look to the original aggref so as
10940 : : * to avoid printing this when recursing from the code just above.
10941 : : */
3461 tgl@sss.pgh.pa.us 10942 [ + + ]: 2012 : if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))
3521 rhaas@postgresql.org 10943 : 862 : appendStringInfoString(buf, "PARTIAL ");
10944 : :
10945 : : /* Extract the argument types as seen by the parser */
4377 tgl@sss.pgh.pa.us 10946 : 2012 : nargs = get_aggregate_argtypes(aggref, argtypes);
10947 : :
994 alvherre@alvh.no-ip. 10948 [ + + ]: 2012 : if (!funcname)
10949 : 1985 : funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
10950 : 1985 : argtypes, aggref->aggvariadic,
10951 : : &use_variadic,
475 tgl@sss.pgh.pa.us 10952 : 1985 : context->inGroupBy);
10953 : :
10954 : : /* Print the aggregate name, schema-qualified if needed */
994 alvherre@alvh.no-ip. 10955 : 2012 : appendStringInfo(buf, "%s(%s", funcname,
5846 tgl@sss.pgh.pa.us 10956 [ + + ]: 2012 : (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
10957 : :
4377 10958 [ + + ]: 2012 : if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
10959 : : {
10960 : : /*
10961 : : * Ordered-set aggregates do not use "*" syntax. Also, we needn't
10962 : : * worry about inserting VARIADIC. So we can just dump the direct
10963 : : * args as-is.
10964 : : */
10965 [ - + ]: 14 : Assert(!aggref->aggvariadic);
10966 : 14 : get_rule_expr((Node *) aggref->aggdirectargs, context, true);
10967 [ - + ]: 14 : Assert(aggref->aggorder != NIL);
10968 : 14 : appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
10969 : 14 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
10970 : : }
10971 : : else
10972 : : {
10973 : : /* aggstar can be set only in zero-argument aggregates */
10974 [ + + ]: 1998 : if (aggref->aggstar)
10975 : 588 : appendStringInfoChar(buf, '*');
10976 : : else
10977 : : {
10978 : : ListCell *l;
10979 : : int i;
10980 : :
10981 : 1410 : i = 0;
10982 [ + - + + : 2914 : foreach(l, aggref->args)
+ + ]
10983 : : {
10984 : 1504 : TargetEntry *tle = (TargetEntry *) lfirst(l);
10985 : 1504 : Node *arg = (Node *) tle->expr;
10986 : :
10987 [ - + ]: 1504 : Assert(!IsA(arg, NamedArgExpr));
10988 [ + + ]: 1504 : if (tle->resjunk)
10989 : 25 : continue;
10990 [ + + ]: 1479 : if (i++ > 0)
10991 : : {
994 alvherre@alvh.no-ip. 10992 [ + + ]: 69 : if (is_json_objectagg)
10993 : : {
10994 : : /*
10995 : : * the ABSENT ON NULL and WITH UNIQUE args are printed
10996 : : * separately, so ignore them here
10997 : : */
10998 [ - + ]: 15 : if (i > 2)
994 alvherre@alvh.no-ip. 10999 :UBC 0 : break;
11000 : :
994 alvherre@alvh.no-ip. 11001 :CBC 15 : appendStringInfoString(buf, " : ");
11002 : : }
11003 : : else
11004 : 54 : appendStringInfoString(buf, ", ");
11005 : : }
4377 tgl@sss.pgh.pa.us 11006 [ + + + - ]: 1479 : if (use_variadic && i == nargs)
11007 : 4 : appendStringInfoString(buf, "VARIADIC ");
11008 : 1479 : get_rule_expr(arg, context, true);
11009 : : }
11010 : : }
11011 : :
11012 [ + + ]: 1998 : if (aggref->aggorder != NIL)
11013 : : {
11014 : 44 : appendStringInfoString(buf, " ORDER BY ");
11015 : 44 : get_rule_orderby(aggref->aggorder, aggref->args, false, context);
11016 : : }
11017 : : }
11018 : :
994 alvherre@alvh.no-ip. 11019 [ + + ]: 2012 : if (options)
11020 : 27 : appendStringInfoString(buf, options);
11021 : :
4537 noah@leadboat.com 11022 [ + + ]: 2012 : if (aggref->aggfilter != NULL)
11023 : : {
11024 : 23 : appendStringInfoString(buf, ") FILTER (WHERE ");
11025 : 23 : get_rule_expr((Node *) aggref->aggfilter, context, false);
11026 : : }
11027 : :
8651 tgl@sss.pgh.pa.us 11028 : 2012 : appendStringInfoChar(buf, ')');
11029 : : }
11030 : :
11031 : : /*
11032 : : * This is a helper function for get_agg_expr(). It's used when we deparse
11033 : : * a combining Aggref; resolve_special_varno locates the corresponding partial
11034 : : * Aggref and then calls this.
11035 : : */
11036 : : static void
2198 11037 : 388 : get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
11038 : : {
11039 : : Aggref *aggref;
11040 : 388 : Aggref *original_aggref = callback_arg;
11041 : :
3521 rhaas@postgresql.org 11042 [ - + ]: 388 : if (!IsA(node, Aggref))
3521 rhaas@postgresql.org 11043 [ # # ]:UBC 0 : elog(ERROR, "combining Aggref does not point to an Aggref");
11044 : :
3521 rhaas@postgresql.org 11045 :CBC 388 : aggref = (Aggref *) node;
11046 : 388 : get_agg_expr(aggref, context, original_aggref);
11047 : 388 : }
11048 : :
11049 : : /*
11050 : : * get_windowfunc_expr - Parse back a WindowFunc node
11051 : : */
11052 : : static void
1203 andrew@dunslane.net 11053 : 162 : get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
11054 : : {
994 alvherre@alvh.no-ip. 11055 : 162 : get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
11056 : 162 : }
11057 : :
11058 : :
11059 : : /*
11060 : : * get_windowfunc_expr_helper - subroutine for get_windowfunc_expr and
11061 : : * get_json_agg_constructor
11062 : : */
11063 : : static void
11064 : 168 : get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
11065 : : const char *funcname, const char *options,
11066 : : bool is_json_objectagg)
11067 : : {
6198 tgl@sss.pgh.pa.us 11068 : 168 : StringInfo buf = context->buf;
11069 : : Oid argtypes[FUNC_MAX_ARGS];
11070 : : int nargs;
11071 : : List *argnames;
11072 : : ListCell *l;
11073 : :
11074 [ - + ]: 168 : if (list_length(wfunc->args) > FUNC_MAX_ARGS)
6198 tgl@sss.pgh.pa.us 11075 [ # # ]:UBC 0 : ereport(ERROR,
11076 : : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
11077 : : errmsg("too many arguments")));
6198 tgl@sss.pgh.pa.us 11078 :CBC 168 : nargs = 0;
4424 11079 : 168 : argnames = NIL;
6198 11080 [ + + + + : 285 : foreach(l, wfunc->args)
+ + ]
11081 : : {
5773 bruce@momjian.us 11082 : 117 : Node *arg = (Node *) lfirst(l);
11083 : :
4424 tgl@sss.pgh.pa.us 11084 [ - + ]: 117 : if (IsA(arg, NamedArgExpr))
4424 tgl@sss.pgh.pa.us 11085 :UBC 0 : argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
5914 tgl@sss.pgh.pa.us 11086 :CBC 117 : argtypes[nargs] = exprType(arg);
6198 11087 : 117 : nargs++;
11088 : : }
11089 : :
994 alvherre@alvh.no-ip. 11090 [ + + ]: 168 : if (!funcname)
11091 : 162 : funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
11092 : : argtypes, false, NULL,
475 tgl@sss.pgh.pa.us 11093 : 162 : context->inGroupBy);
11094 : :
994 alvherre@alvh.no-ip. 11095 : 168 : appendStringInfo(buf, "%s(", funcname);
11096 : :
11097 : : /* winstar can be set only in zero-argument aggregates */
6198 tgl@sss.pgh.pa.us 11098 [ + + ]: 168 : if (wfunc->winstar)
11099 : 12 : appendStringInfoChar(buf, '*');
11100 : : else
11101 : : {
994 alvherre@alvh.no-ip. 11102 [ + + ]: 156 : if (is_json_objectagg)
11103 : : {
11104 : 3 : get_rule_expr((Node *) linitial(wfunc->args), context, false);
11105 : 3 : appendStringInfoString(buf, " : ");
11106 : 3 : get_rule_expr((Node *) lsecond(wfunc->args), context, false);
11107 : : }
11108 : : else
11109 : 153 : get_rule_expr((Node *) wfunc->args, context, true);
11110 : : }
11111 : :
11112 [ + + ]: 168 : if (options)
11113 : 6 : appendStringInfoString(buf, options);
11114 : :
4537 noah@leadboat.com 11115 [ - + ]: 168 : if (wfunc->aggfilter != NULL)
11116 : : {
4537 noah@leadboat.com 11117 :UBC 0 : appendStringInfoString(buf, ") FILTER (WHERE ");
11118 : 0 : get_rule_expr((Node *) wfunc->aggfilter, context, false);
11119 : : }
11120 : :
75 ishii@postgresql.org 11121 :GNC 168 : appendStringInfoString(buf, ") ");
11122 : :
11123 [ + + ]: 168 : if (wfunc->ignore_nulls == PARSER_IGNORE_NULLS)
11124 : 3 : appendStringInfoString(buf, "IGNORE NULLS ");
11125 : :
11126 : 168 : appendStringInfoString(buf, "OVER ");
11127 : :
281 tgl@sss.pgh.pa.us 11128 [ + + ]:CBC 168 : if (context->windowClause)
11129 : : {
11130 : : /* Query-decompilation case: search the windowClause list */
11131 [ + - + - : 30 : foreach(l, context->windowClause)
+ - ]
11132 : : {
11133 : 30 : WindowClause *wc = (WindowClause *) lfirst(l);
11134 : :
11135 [ + - ]: 30 : if (wc->winref == wfunc->winref)
11136 : : {
11137 [ + + ]: 30 : if (wc->name)
281 tgl@sss.pgh.pa.us 11138 :GBC 9 : appendStringInfoString(buf, quote_identifier(wc->name));
11139 : : else
281 tgl@sss.pgh.pa.us 11140 :CBC 21 : get_rule_windowspec(wc, context->targetList, context);
11141 : 30 : break;
11142 : : }
11143 : : }
11144 [ - + ]: 30 : if (l == NULL)
6198 tgl@sss.pgh.pa.us 11145 [ # # ]:UBC 0 : elog(ERROR, "could not find window clause for winref %u",
11146 : : wfunc->winref);
11147 : : }
11148 : : else
11149 : : {
11150 : : /*
11151 : : * In EXPLAIN, search the namespace stack for a matching WindowAgg
11152 : : * node (probably it's always the first entry), and print winname.
11153 : : */
281 tgl@sss.pgh.pa.us 11154 [ + - + - :CBC 138 : foreach(l, context->namespaces)
+ - ]
11155 : : {
11156 : 138 : deparse_namespace *dpns = (deparse_namespace *) lfirst(l);
11157 : :
11158 [ + - + - ]: 138 : if (dpns->plan && IsA(dpns->plan, WindowAgg))
11159 : : {
11160 : 138 : WindowAgg *wagg = (WindowAgg *) dpns->plan;
11161 : :
11162 [ + - ]: 138 : if (wagg->winref == wfunc->winref)
11163 : : {
11164 : 138 : appendStringInfoString(buf, quote_identifier(wagg->winname));
11165 : 138 : break;
11166 : : }
11167 : : }
11168 : : }
11169 [ - + ]: 138 : if (l == NULL)
281 tgl@sss.pgh.pa.us 11170 [ # # ]:UBC 0 : elog(ERROR, "could not find window clause for winref %u",
11171 : : wfunc->winref);
11172 : : }
6198 tgl@sss.pgh.pa.us 11173 :CBC 168 : }
11174 : :
11175 : : /*
11176 : : * get_func_sql_syntax - Parse back a SQL-syntax function call
11177 : : *
11178 : : * Returns true if we successfully deparsed, false if we did not
11179 : : * recognize the function.
11180 : : */
11181 : : static bool
1869 11182 : 90 : get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
11183 : : {
11184 : 90 : StringInfo buf = context->buf;
11185 : 90 : Oid funcoid = expr->funcid;
11186 : :
11187 [ + + + + : 90 : switch (funcoid)
+ + + + +
+ + + + +
+ - + ]
11188 : : {
11189 : 12 : case F_TIMEZONE_INTERVAL_TIMESTAMP:
11190 : : case F_TIMEZONE_INTERVAL_TIMESTAMPTZ:
11191 : : case F_TIMEZONE_INTERVAL_TIMETZ:
11192 : : case F_TIMEZONE_TEXT_TIMESTAMP:
11193 : : case F_TIMEZONE_TEXT_TIMESTAMPTZ:
11194 : : case F_TIMEZONE_TEXT_TIMETZ:
11195 : : /* AT TIME ZONE ... note reversed argument order */
11196 : 12 : appendStringInfoChar(buf, '(');
1112 11197 : 12 : get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
11198 : : (Node *) expr);
1869 11199 : 12 : appendStringInfoString(buf, " AT TIME ZONE ");
1112 11200 : 12 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11201 : : (Node *) expr);
1869 11202 : 12 : appendStringInfoChar(buf, ')');
11203 : 12 : return true;
11204 : :
796 michael@paquier.xyz 11205 : 9 : case F_TIMEZONE_TIMESTAMP:
11206 : : case F_TIMEZONE_TIMESTAMPTZ:
11207 : : case F_TIMEZONE_TIMETZ:
11208 : : /* AT LOCAL */
11209 : 9 : appendStringInfoChar(buf, '(');
11210 : 9 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11211 : : (Node *) expr);
11212 : 9 : appendStringInfoString(buf, " AT LOCAL)");
11213 : 9 : return true;
11214 : :
1869 tgl@sss.pgh.pa.us 11215 : 3 : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL:
11216 : : case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ:
11217 : : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL:
11218 : : case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ:
11219 : : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_INTERVAL:
11220 : : case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_TIMESTAMP:
11221 : : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_INTERVAL:
11222 : : case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_TIMESTAMP:
11223 : : case F_OVERLAPS_TIMETZ_TIMETZ_TIMETZ_TIMETZ:
11224 : : case F_OVERLAPS_TIME_INTERVAL_TIME_INTERVAL:
11225 : : case F_OVERLAPS_TIME_INTERVAL_TIME_TIME:
11226 : : case F_OVERLAPS_TIME_TIME_TIME_INTERVAL:
11227 : : case F_OVERLAPS_TIME_TIME_TIME_TIME:
11228 : : /* (x1, x2) OVERLAPS (y1, y2) */
11229 : 3 : appendStringInfoString(buf, "((");
11230 : 3 : get_rule_expr((Node *) linitial(expr->args), context, false);
11231 : 3 : appendStringInfoString(buf, ", ");
11232 : 3 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11233 : 3 : appendStringInfoString(buf, ") OVERLAPS (");
11234 : 3 : get_rule_expr((Node *) lthird(expr->args), context, false);
11235 : 3 : appendStringInfoString(buf, ", ");
11236 : 3 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11237 : 3 : appendStringInfoString(buf, "))");
11238 : 3 : return true;
11239 : :
1716 peter@eisentraut.org 11240 : 9 : case F_EXTRACT_TEXT_DATE:
11241 : : case F_EXTRACT_TEXT_TIME:
11242 : : case F_EXTRACT_TEXT_TIMETZ:
11243 : : case F_EXTRACT_TEXT_TIMESTAMP:
11244 : : case F_EXTRACT_TEXT_TIMESTAMPTZ:
11245 : : case F_EXTRACT_TEXT_INTERVAL:
11246 : : /* EXTRACT (x FROM y) */
11247 : 9 : appendStringInfoString(buf, "EXTRACT(");
11248 : : {
11249 : 9 : Const *con = (Const *) linitial(expr->args);
11250 : :
11251 [ + - + - : 9 : Assert(IsA(con, Const) &&
- + ]
11252 : : con->consttype == TEXTOID &&
11253 : : !con->constisnull);
11254 : 9 : appendStringInfoString(buf, TextDatumGetCString(con->constvalue));
11255 : : }
11256 : 9 : appendStringInfoString(buf, " FROM ");
11257 : 9 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11258 : 9 : appendStringInfoChar(buf, ')');
11259 : 9 : return true;
11260 : :
1869 tgl@sss.pgh.pa.us 11261 : 6 : case F_IS_NORMALIZED:
11262 : : /* IS xxx NORMALIZED */
806 drowley@postgresql.o 11263 : 6 : appendStringInfoChar(buf, '(');
1112 tgl@sss.pgh.pa.us 11264 : 6 : get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11265 : : (Node *) expr);
11266 : 6 : appendStringInfoString(buf, " IS");
1869 11267 [ + + ]: 6 : if (list_length(expr->args) == 2)
11268 : : {
11269 : 3 : Const *con = (Const *) lsecond(expr->args);
11270 : :
11271 [ + - + - : 3 : Assert(IsA(con, Const) &&
- + ]
11272 : : con->consttype == TEXTOID &&
11273 : : !con->constisnull);
11274 : 3 : appendStringInfo(buf, " %s",
11275 : 3 : TextDatumGetCString(con->constvalue));
11276 : : }
11277 : 6 : appendStringInfoString(buf, " NORMALIZED)");
11278 : 6 : return true;
11279 : :
11280 : 3 : case F_PG_COLLATION_FOR:
11281 : : /* COLLATION FOR */
11282 : 3 : appendStringInfoString(buf, "COLLATION FOR (");
11283 : 3 : get_rule_expr((Node *) linitial(expr->args), context, false);
11284 : 3 : appendStringInfoChar(buf, ')');
11285 : 3 : return true;
11286 : :
11287 : 6 : case F_NORMALIZE:
11288 : : /* NORMALIZE() */
11289 : 6 : appendStringInfoString(buf, "NORMALIZE(");
11290 : 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11291 [ + + ]: 6 : if (list_length(expr->args) == 2)
11292 : : {
11293 : 3 : Const *con = (Const *) lsecond(expr->args);
11294 : :
11295 [ + - + - : 3 : Assert(IsA(con, Const) &&
- + ]
11296 : : con->consttype == TEXTOID &&
11297 : : !con->constisnull);
11298 : 3 : appendStringInfo(buf, ", %s",
11299 : 3 : TextDatumGetCString(con->constvalue));
11300 : : }
11301 : 6 : appendStringInfoChar(buf, ')');
11302 : 6 : return true;
11303 : :
11304 : 6 : case F_OVERLAY_BIT_BIT_INT4:
11305 : : case F_OVERLAY_BIT_BIT_INT4_INT4:
11306 : : case F_OVERLAY_BYTEA_BYTEA_INT4:
11307 : : case F_OVERLAY_BYTEA_BYTEA_INT4_INT4:
11308 : : case F_OVERLAY_TEXT_TEXT_INT4:
11309 : : case F_OVERLAY_TEXT_TEXT_INT4_INT4:
11310 : : /* OVERLAY() */
11311 : 6 : appendStringInfoString(buf, "OVERLAY(");
11312 : 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11313 : 6 : appendStringInfoString(buf, " PLACING ");
11314 : 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11315 : 6 : appendStringInfoString(buf, " FROM ");
11316 : 6 : get_rule_expr((Node *) lthird(expr->args), context, false);
11317 [ + + ]: 6 : if (list_length(expr->args) == 4)
11318 : : {
11319 : 3 : appendStringInfoString(buf, " FOR ");
11320 : 3 : get_rule_expr((Node *) lfourth(expr->args), context, false);
11321 : : }
11322 : 6 : appendStringInfoChar(buf, ')');
11323 : 6 : return true;
11324 : :
11325 : 3 : case F_POSITION_BIT_BIT:
11326 : : case F_POSITION_BYTEA_BYTEA:
11327 : : case F_POSITION_TEXT_TEXT:
11328 : : /* POSITION() ... extra parens since args are b_expr not a_expr */
11329 : 3 : appendStringInfoString(buf, "POSITION((");
11330 : 3 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11331 : 3 : appendStringInfoString(buf, ") IN (");
11332 : 3 : get_rule_expr((Node *) linitial(expr->args), context, false);
11333 : 3 : appendStringInfoString(buf, "))");
11334 : 3 : return true;
11335 : :
11336 : 3 : case F_SUBSTRING_BIT_INT4:
11337 : : case F_SUBSTRING_BIT_INT4_INT4:
11338 : : case F_SUBSTRING_BYTEA_INT4:
11339 : : case F_SUBSTRING_BYTEA_INT4_INT4:
11340 : : case F_SUBSTRING_TEXT_INT4:
11341 : : case F_SUBSTRING_TEXT_INT4_INT4:
11342 : : /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
11343 : 3 : appendStringInfoString(buf, "SUBSTRING(");
11344 : 3 : get_rule_expr((Node *) linitial(expr->args), context, false);
11345 : 3 : appendStringInfoString(buf, " FROM ");
11346 : 3 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11347 [ + - ]: 3 : if (list_length(expr->args) == 3)
11348 : : {
11349 : 3 : appendStringInfoString(buf, " FOR ");
11350 : 3 : get_rule_expr((Node *) lthird(expr->args), context, false);
11351 : : }
11352 : 3 : appendStringInfoChar(buf, ')');
11353 : 3 : return true;
11354 : :
11355 : 3 : case F_SUBSTRING_TEXT_TEXT_TEXT:
11356 : : /* SUBSTRING SIMILAR/ESCAPE */
11357 : 3 : appendStringInfoString(buf, "SUBSTRING(");
11358 : 3 : get_rule_expr((Node *) linitial(expr->args), context, false);
11359 : 3 : appendStringInfoString(buf, " SIMILAR ");
11360 : 3 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11361 : 3 : appendStringInfoString(buf, " ESCAPE ");
11362 : 3 : get_rule_expr((Node *) lthird(expr->args), context, false);
11363 : 3 : appendStringInfoChar(buf, ')');
11364 : 3 : return true;
11365 : :
11366 : 6 : case F_BTRIM_BYTEA_BYTEA:
11367 : : case F_BTRIM_TEXT:
11368 : : case F_BTRIM_TEXT_TEXT:
11369 : : /* TRIM() */
11370 : 6 : appendStringInfoString(buf, "TRIM(BOTH");
11371 [ + - ]: 6 : if (list_length(expr->args) == 2)
11372 : : {
11373 : 6 : appendStringInfoChar(buf, ' ');
11374 : 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11375 : : }
11376 : 6 : appendStringInfoString(buf, " FROM ");
11377 : 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11378 : 6 : appendStringInfoChar(buf, ')');
11379 : 6 : return true;
11380 : :
1794 11381 : 6 : case F_LTRIM_BYTEA_BYTEA:
11382 : : case F_LTRIM_TEXT:
11383 : : case F_LTRIM_TEXT_TEXT:
11384 : : /* TRIM() */
1869 11385 : 6 : appendStringInfoString(buf, "TRIM(LEADING");
11386 [ + - ]: 6 : if (list_length(expr->args) == 2)
11387 : : {
11388 : 6 : appendStringInfoChar(buf, ' ');
11389 : 6 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11390 : : }
11391 : 6 : appendStringInfoString(buf, " FROM ");
11392 : 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11393 : 6 : appendStringInfoChar(buf, ')');
11394 : 6 : return true;
11395 : :
1794 11396 : 6 : case F_RTRIM_BYTEA_BYTEA:
11397 : : case F_RTRIM_TEXT:
11398 : : case F_RTRIM_TEXT_TEXT:
11399 : : /* TRIM() */
1869 11400 : 6 : appendStringInfoString(buf, "TRIM(TRAILING");
11401 [ + + ]: 6 : if (list_length(expr->args) == 2)
11402 : : {
11403 : 3 : appendStringInfoChar(buf, ' ');
11404 : 3 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11405 : : }
11406 : 6 : appendStringInfoString(buf, " FROM ");
11407 : 6 : get_rule_expr((Node *) linitial(expr->args), context, false);
11408 : 6 : appendStringInfoChar(buf, ')');
11409 : 6 : return true;
11410 : :
1175 michael@paquier.xyz 11411 : 6 : case F_SYSTEM_USER:
11412 : 6 : appendStringInfoString(buf, "SYSTEM_USER");
11413 : 6 : return true;
11414 : :
1869 tgl@sss.pgh.pa.us 11415 :UBC 0 : case F_XMLEXISTS:
11416 : : /* XMLEXISTS ... extra parens because args are c_expr */
11417 : 0 : appendStringInfoString(buf, "XMLEXISTS((");
11418 : 0 : get_rule_expr((Node *) linitial(expr->args), context, false);
11419 : 0 : appendStringInfoString(buf, ") PASSING (");
11420 : 0 : get_rule_expr((Node *) lsecond(expr->args), context, false);
11421 : 0 : appendStringInfoString(buf, "))");
11422 : 0 : return true;
11423 : : }
1869 tgl@sss.pgh.pa.us 11424 :CBC 3 : return false;
11425 : : }
11426 : :
11427 : : /* ----------
11428 : : * get_coercion_expr
11429 : : *
11430 : : * Make a string representation of a value coerced to a specific type
11431 : : * ----------
11432 : : */
11433 : : static void
6850 11434 : 2655 : get_coercion_expr(Node *arg, deparse_context *context,
11435 : : Oid resulttype, int32 resulttypmod,
11436 : : Node *parentNode)
11437 : : {
11438 : 2655 : StringInfo buf = context->buf;
11439 : :
11440 : : /*
11441 : : * Since parse_coerce.c doesn't immediately collapse application of
11442 : : * length-coercion functions to constants, what we'll typically see in
11443 : : * such cases is a Const with typmod -1 and a length-coercion function
11444 : : * right above it. Avoid generating redundant output. However, beware of
11445 : : * suppressing casts when the user actually wrote something like
11446 : : * 'foo'::text::char(3).
11447 : : *
11448 : : * Note: it might seem that we are missing the possibility of needing to
11449 : : * print a COLLATE clause for such a Const. However, a Const could only
11450 : : * have nondefault collation in a post-constant-folding tree, in which the
11451 : : * length coercion would have been folded too. See also the special
11452 : : * handling of CollateExpr in coerce_to_target_type(): any collation
11453 : : * marking will be above the coercion node, not below it.
11454 : : */
11455 [ + - + + ]: 2655 : if (arg && IsA(arg, Const) &&
11456 [ + + ]: 325 : ((Const *) arg)->consttype == resulttype &&
11457 [ + - ]: 12 : ((Const *) arg)->consttypmod == -1)
11458 : : {
11459 : : /* Show the constant without normal ::typename decoration */
6555 11460 : 12 : get_const_expr((Const *) arg, context, -1);
11461 : : }
11462 : : else
11463 : : {
6850 11464 [ + + ]: 2643 : if (!PRETTY_PAREN(context))
11465 : 2439 : appendStringInfoChar(buf, '(');
11466 : 2643 : get_rule_expr_paren(arg, context, false, parentNode);
11467 [ + + ]: 2643 : if (!PRETTY_PAREN(context))
11468 : 2439 : appendStringInfoChar(buf, ')');
11469 : : }
11470 : :
11471 : : /*
11472 : : * Never emit resulttype(arg) functional notation. A pg_proc entry could
11473 : : * take precedence, and a resulttype in pg_temp would require schema
11474 : : * qualification that format_type_with_typemod() would usually omit. We've
11475 : : * standardized on arg::resulttype, but CAST(arg AS resulttype) notation
11476 : : * would work fine.
11477 : : */
11478 : 2655 : appendStringInfo(buf, "::%s",
11479 : : format_type_with_typemod(resulttype, resulttypmod));
11480 : 2655 : }
11481 : :
11482 : : /* ----------
11483 : : * get_const_expr
11484 : : *
11485 : : * Make a string representation of a Const
11486 : : *
11487 : : * showtype can be -1 to never show "::typename" decoration, or +1 to always
11488 : : * show it, or 0 to show it only if the constant wouldn't be assumed to be
11489 : : * the right type by default.
11490 : : *
11491 : : * If the Const's collation isn't default for its type, show that too.
11492 : : * We mustn't do this when showtype is -1 (since that means the caller will
11493 : : * print "::typename", and we can't put a COLLATE clause in between). It's
11494 : : * caller's responsibility that collation isn't missed in such cases.
11495 : : * ----------
11496 : : */
11497 : : static void
6555 11498 : 35938 : get_const_expr(Const *constval, deparse_context *context, int showtype)
11499 : : {
9572 11500 : 35938 : StringInfo buf = context->buf;
11501 : : Oid typoutput;
11502 : : bool typIsVarlena;
11503 : : char *extval;
3915 11504 : 35938 : bool needlabel = false;
11505 : :
9490 11506 [ + + ]: 35938 : if (constval->constisnull)
11507 : : {
11508 : : /*
11509 : : * Always label the type of a NULL constant to prevent misdecisions
11510 : : * about type when reparsing.
11511 : : */
4430 rhaas@postgresql.org 11512 : 609 : appendStringInfoString(buf, "NULL");
6555 tgl@sss.pgh.pa.us 11513 [ + + ]: 609 : if (showtype >= 0)
11514 : : {
6850 11515 : 582 : appendStringInfo(buf, "::%s",
11516 : : format_type_with_typemod(constval->consttype,
11517 : : constval->consttypmod));
5395 11518 : 582 : get_const_collation(constval, context);
11519 : : }
9490 11520 : 5132 : return;
11521 : : }
11522 : :
7864 11523 : 35329 : getTypeOutputInfo(constval->consttype,
11524 : : &typoutput, &typIsVarlena);
11525 : :
7197 11526 : 35329 : extval = OidOutputFunctionCall(typoutput, constval->constvalue);
11527 : :
9571 11528 [ + + + + ]: 35329 : switch (constval->consttype)
11529 : : {
11530 : 20188 : case INT4OID:
11531 : :
11532 : : /*
11533 : : * INT4 can be printed without any decoration, unless it is
11534 : : * negative; in that case print it as '-nnn'::integer to ensure
11535 : : * that the output will re-parse as a constant, not as a constant
11536 : : * plus operator. In most cases we could get away with printing
11537 : : * (-nnn) instead, because of the way that gram.y handles negative
11538 : : * literals; but that doesn't work for INT_MIN, and it doesn't
11539 : : * seem that much prettier anyway.
11540 : : */
3915 11541 [ + + ]: 20188 : if (extval[0] != '-')
11542 : 19936 : appendStringInfoString(buf, extval);
11543 : : else
11544 : : {
11545 : 252 : appendStringInfo(buf, "'%s'", extval);
3101 11546 : 252 : needlabel = true; /* we must attach a cast */
11547 : : }
3915 11548 : 20188 : break;
11549 : :
8522 peter_e@gmx.net 11550 : 551 : case NUMERICOID:
11551 : :
11552 : : /*
11553 : : * NUMERIC can be printed without quotes if it looks like a float
11554 : : * constant (not an integer, and not Infinity or NaN) and doesn't
11555 : : * have a leading sign (for the same reason as for INT4).
11556 : : */
3915 tgl@sss.pgh.pa.us 11557 [ + - ]: 551 : if (isdigit((unsigned char) extval[0]) &&
11558 [ + + ]: 551 : strcspn(extval, "eE.") != strlen(extval))
11559 : : {
11560 : 196 : appendStringInfoString(buf, extval);
11561 : : }
11562 : : else
11563 : : {
11564 : 355 : appendStringInfo(buf, "'%s'", extval);
3101 11565 : 355 : needlabel = true; /* we must attach a cast */
11566 : : }
8505 bruce@momjian.us 11567 : 551 : break;
11568 : :
8522 peter_e@gmx.net 11569 : 837 : case BOOLOID:
8505 bruce@momjian.us 11570 [ + + ]: 837 : if (strcmp(extval, "t") == 0)
4430 rhaas@postgresql.org 11571 : 373 : appendStringInfoString(buf, "true");
11572 : : else
11573 : 464 : appendStringInfoString(buf, "false");
8522 peter_e@gmx.net 11574 : 837 : break;
11575 : :
11576 : 13753 : default:
6311 tgl@sss.pgh.pa.us 11577 : 13753 : simple_quote_literal(buf, extval);
9571 11578 : 13753 : break;
11579 : : }
11580 : :
9573 11581 : 35329 : pfree(extval);
11582 : :
6555 11583 [ + + ]: 35329 : if (showtype < 0)
6850 11584 : 4523 : return;
11585 : :
11586 : : /*
11587 : : * For showtype == 0, append ::typename unless the constant will be
11588 : : * implicitly typed as the right type when it is read in.
11589 : : *
11590 : : * XXX this code has to be kept in sync with the behavior of the parser,
11591 : : * especially make_const.
11592 : : */
9571 11593 [ + + + + ]: 30806 : switch (constval->consttype)
11594 : : {
8522 peter_e@gmx.net 11595 : 871 : case BOOLOID:
11596 : : case UNKNOWNOID:
11597 : : /* These types can be left unlabeled */
8491 tgl@sss.pgh.pa.us 11598 : 871 : needlabel = false;
11599 : 871 : break;
3915 11600 : 17791 : case INT4OID:
11601 : : /* We determined above whether a label is needed */
11602 : 17791 : break;
8491 11603 : 551 : case NUMERICOID:
11604 : :
11605 : : /*
11606 : : * Float-looking constants will be typed as numeric, which we
11607 : : * checked above; but if there's a nondefault typmod we need to
11608 : : * show it.
11609 : : */
3915 11610 : 551 : needlabel |= (constval->consttypmod >= 0);
9571 11611 : 551 : break;
11612 : 11593 : default:
8491 11613 : 11593 : needlabel = true;
9571 11614 : 11593 : break;
11615 : : }
6555 11616 [ + + - + ]: 30806 : if (needlabel || showtype > 0)
8491 11617 : 12193 : appendStringInfo(buf, "::%s",
11618 : : format_type_with_typemod(constval->consttype,
11619 : : constval->consttypmod));
11620 : :
5395 11621 : 30806 : get_const_collation(constval, context);
11622 : : }
11623 : :
11624 : : /*
11625 : : * helper for get_const_expr: append COLLATE if needed
11626 : : */
11627 : : static void
11628 : 31388 : get_const_collation(Const *constval, deparse_context *context)
11629 : : {
11630 : 31388 : StringInfo buf = context->buf;
11631 : :
11632 [ + + ]: 31388 : if (OidIsValid(constval->constcollid))
11633 : : {
5365 bruce@momjian.us 11634 : 4580 : Oid typcollation = get_typcollation(constval->consttype);
11635 : :
5395 tgl@sss.pgh.pa.us 11636 [ + + ]: 4580 : if (constval->constcollid != typcollation)
11637 : : {
11638 : 37 : appendStringInfo(buf, " COLLATE %s",
11639 : : generate_collation_name(constval->constcollid));
11640 : : }
11641 : : }
9938 bruce@momjian.us 11642 : 31388 : }
11643 : :
11644 : : /*
11645 : : * get_json_path_spec - Parse back a JSON path specification
11646 : : */
11647 : : static void
636 amitlan@postgresql.o 11648 : 228 : get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
11649 : : {
11650 [ + - ]: 228 : if (IsA(path_spec, Const))
11651 : 228 : get_const_expr((Const *) path_spec, context, -1);
11652 : : else
636 amitlan@postgresql.o 11653 :UBC 0 : get_rule_expr(path_spec, context, showimplicit);
636 amitlan@postgresql.o 11654 :CBC 228 : }
11655 : :
11656 : : /*
11657 : : * get_json_format - Parse back a JsonFormat node
11658 : : */
11659 : : static void
994 alvherre@alvh.no-ip. 11660 : 93 : get_json_format(JsonFormat *format, StringInfo buf)
11661 : : {
11662 [ + + ]: 93 : if (format->format_type == JS_FORMAT_DEFAULT)
11663 : 54 : return;
11664 : :
11665 : 39 : appendStringInfoString(buf,
11666 [ - + ]: 39 : format->format_type == JS_FORMAT_JSONB ?
11667 : : " FORMAT JSONB" : " FORMAT JSON");
11668 : :
11669 [ + + ]: 39 : if (format->encoding != JS_ENC_DEFAULT)
11670 : : {
11671 : : const char *encoding;
11672 : :
11673 : 3 : encoding =
11674 [ + - ]: 6 : format->encoding == JS_ENC_UTF16 ? "UTF16" :
11675 [ - + ]: 3 : format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
11676 : :
11677 : 3 : appendStringInfo(buf, " ENCODING %s", encoding);
11678 : : }
11679 : : }
11680 : :
11681 : : /*
11682 : : * get_json_returning - Parse back a JsonReturning structure
11683 : : */
11684 : : static void
11685 : 90 : get_json_returning(JsonReturning *returning, StringInfo buf,
11686 : : bool json_format_by_default)
11687 : : {
11688 [ - + ]: 90 : if (!OidIsValid(returning->typid))
994 alvherre@alvh.no-ip. 11689 :UBC 0 : return;
11690 : :
994 alvherre@alvh.no-ip. 11691 :CBC 90 : appendStringInfo(buf, " RETURNING %s",
11692 : : format_type_with_typemod(returning->typid,
11693 : : returning->typmod));
11694 : :
11695 [ + + + + ]: 174 : if (!json_format_by_default ||
11696 : 84 : returning->format->format_type !=
11697 [ + + ]: 84 : (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
11698 : 18 : get_json_format(returning->format, buf);
11699 : : }
11700 : :
11701 : : /*
11702 : : * get_json_constructor - Parse back a JsonConstructorExpr node
11703 : : */
11704 : : static void
11705 : 93 : get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
11706 : : bool showimplicit)
11707 : : {
11708 : 93 : StringInfo buf = context->buf;
11709 : : const char *funcname;
11710 : : bool is_json_object;
11711 : : int curridx;
11712 : : ListCell *lc;
11713 : :
11714 [ + + ]: 93 : if (ctor->type == JSCTOR_JSON_OBJECTAGG)
11715 : : {
11716 : 18 : get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
11717 : 18 : return;
11718 : : }
11719 [ + + ]: 75 : else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
11720 : : {
11721 : 15 : get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
11722 : 15 : return;
11723 : : }
11724 : :
11725 [ + + + + : 60 : switch (ctor->type)
+ - ]
11726 : : {
11727 : 15 : case JSCTOR_JSON_OBJECT:
11728 : 15 : funcname = "JSON_OBJECT";
11729 : 15 : break;
11730 : 12 : case JSCTOR_JSON_ARRAY:
11731 : 12 : funcname = "JSON_ARRAY";
11732 : 12 : break;
881 amitlan@postgresql.o 11733 : 21 : case JSCTOR_JSON_PARSE:
11734 : 21 : funcname = "JSON";
11735 : 21 : break;
11736 : 6 : case JSCTOR_JSON_SCALAR:
11737 : 6 : funcname = "JSON_SCALAR";
11738 : 6 : break;
11739 : 6 : case JSCTOR_JSON_SERIALIZE:
11740 : 6 : funcname = "JSON_SERIALIZE";
11741 : 6 : break;
994 alvherre@alvh.no-ip. 11742 :UBC 0 : default:
993 11743 [ # # ]: 0 : elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
11744 : : }
11745 : :
994 alvherre@alvh.no-ip. 11746 :CBC 60 : appendStringInfo(buf, "%s(", funcname);
11747 : :
11748 : 60 : is_json_object = ctor->type == JSCTOR_JSON_OBJECT;
11749 [ + - + + : 159 : foreach(lc, ctor->args)
+ + ]
11750 : : {
11751 : 99 : curridx = foreach_current_index(lc);
11752 [ + + ]: 99 : if (curridx > 0)
11753 : : {
11754 : : const char *sep;
11755 : :
11756 [ + + + + ]: 39 : sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
11757 : 39 : appendStringInfoString(buf, sep);
11758 : : }
11759 : :
11760 : 99 : get_rule_expr((Node *) lfirst(lc), context, true);
11761 : : }
11762 : :
11763 : 60 : get_json_constructor_options(ctor, buf);
806 drowley@postgresql.o 11764 : 60 : appendStringInfoChar(buf, ')');
11765 : : }
11766 : :
11767 : : /*
11768 : : * Append options, if any, to the JSON constructor being deparsed
11769 : : */
11770 : : static void
994 alvherre@alvh.no-ip. 11771 : 93 : get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
11772 : : {
11773 [ + + ]: 93 : if (ctor->absent_on_null)
11774 : : {
11775 [ + - ]: 18 : if (ctor->type == JSCTOR_JSON_OBJECT ||
11776 [ - + ]: 18 : ctor->type == JSCTOR_JSON_OBJECTAGG)
994 alvherre@alvh.no-ip. 11777 :UBC 0 : appendStringInfoString(buf, " ABSENT ON NULL");
11778 : : }
11779 : : else
11780 : : {
994 alvherre@alvh.no-ip. 11781 [ + - ]:CBC 75 : if (ctor->type == JSCTOR_JSON_ARRAY ||
11782 [ + + ]: 75 : ctor->type == JSCTOR_JSON_ARRAYAGG)
11783 : 9 : appendStringInfoString(buf, " NULL ON NULL");
11784 : : }
11785 : :
11786 [ + + ]: 93 : if (ctor->unique)
11787 : 12 : appendStringInfoString(buf, " WITH UNIQUE KEYS");
11788 : :
11789 : : /*
11790 : : * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
11791 : : * support one.
11792 : : */
881 amitlan@postgresql.o 11793 [ + + + + ]: 93 : if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
11794 : 66 : get_json_returning(ctor->returning, buf, true);
994 alvherre@alvh.no-ip. 11795 : 93 : }
11796 : :
11797 : : /*
11798 : : * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
11799 : : */
11800 : : static void
11801 : 33 : get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
11802 : : const char *funcname, bool is_json_objectagg)
11803 : : {
11804 : : StringInfoData options;
11805 : :
11806 : 33 : initStringInfo(&options);
11807 : 33 : get_json_constructor_options(ctor, &options);
11808 : :
11809 [ + + ]: 33 : if (IsA(ctor->func, Aggref))
11810 : 27 : get_agg_expr_helper((Aggref *) ctor->func, context,
11811 : 27 : (Aggref *) ctor->func,
11812 : 27 : funcname, options.data, is_json_objectagg);
11813 [ + - ]: 6 : else if (IsA(ctor->func, WindowFunc))
11814 : 6 : get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
11815 : 6 : funcname, options.data,
11816 : : is_json_objectagg);
11817 : : else
994 alvherre@alvh.no-ip. 11818 [ # # ]:UBC 0 : elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
11819 : : nodeTag(ctor->func));
994 alvherre@alvh.no-ip. 11820 :CBC 33 : }
11821 : :
11822 : : /*
11823 : : * simple_quote_literal - Format a string as a SQL literal, append to buf
11824 : : */
11825 : : static void
6311 tgl@sss.pgh.pa.us 11826 : 14189 : simple_quote_literal(StringInfo buf, const char *val)
11827 : : {
11828 : : const char *valptr;
11829 : :
11830 : : /*
11831 : : * We form the string literal according to the prevailing setting of
11832 : : * standard_conforming_strings; we never use E''. User is responsible for
11833 : : * making sure result is used correctly.
11834 : : */
11835 : 14189 : appendStringInfoChar(buf, '\'');
11836 [ + + ]: 144542 : for (valptr = val; *valptr; valptr++)
11837 : : {
11838 : 130353 : char ch = *valptr;
11839 : :
11840 [ + + + + : 130353 : if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
- + ]
11841 : 153 : appendStringInfoChar(buf, ch);
11842 : 130353 : appendStringInfoChar(buf, ch);
11843 : : }
11844 : 14189 : appendStringInfoChar(buf, '\'');
11845 : 14189 : }
11846 : :
11847 : :
11848 : : /* ----------
11849 : : * get_sublink_expr - Parse back a sublink
11850 : : * ----------
11851 : : */
11852 : : static void
8406 11853 : 230 : get_sublink_expr(SubLink *sublink, deparse_context *context)
11854 : : {
9572 11855 : 230 : StringInfo buf = context->buf;
9938 bruce@momjian.us 11856 : 230 : Query *query = (Query *) (sublink->subselect);
7294 tgl@sss.pgh.pa.us 11857 : 230 : char *opname = NULL;
11858 : : bool need_paren;
11859 : :
8289 11860 [ + + ]: 230 : if (sublink->subLinkType == ARRAY_SUBLINK)
4430 rhaas@postgresql.org 11861 : 12 : appendStringInfoString(buf, "ARRAY(");
11862 : : else
8289 tgl@sss.pgh.pa.us 11863 : 218 : appendStringInfoChar(buf, '(');
11864 : :
11865 : : /*
11866 : : * Note that we print the name of only the first operator, when there are
11867 : : * multiple combining operators. This is an approximation that could go
11868 : : * wrong in various scenarios (operators in different schemas, renamed
11869 : : * operators, etc) but there is not a whole lot we can do about it, since
11870 : : * the syntax allows only one operator to be shown.
11871 : : */
7294 11872 [ + + ]: 230 : if (sublink->testexpr)
11873 : : {
11874 [ + + ]: 9 : if (IsA(sublink->testexpr, OpExpr))
11875 : : {
11876 : : /* single combining operator */
7014 bruce@momjian.us 11877 : 3 : OpExpr *opexpr = (OpExpr *) sublink->testexpr;
11878 : :
7294 tgl@sss.pgh.pa.us 11879 : 3 : get_rule_expr(linitial(opexpr->args), context, true);
11880 : 3 : opname = generate_operator_name(opexpr->opno,
11881 : 3 : exprType(linitial(opexpr->args)),
11882 : 3 : exprType(lsecond(opexpr->args)));
11883 : : }
11884 [ + + ]: 6 : else if (IsA(sublink->testexpr, BoolExpr))
11885 : : {
11886 : : /* multiple combining operators, = or <> cases */
11887 : : char *sep;
11888 : : ListCell *l;
11889 : :
9432 11890 : 3 : appendStringInfoChar(buf, '(');
7294 11891 : 3 : sep = "";
11892 [ + - + + : 9 : foreach(l, ((BoolExpr *) sublink->testexpr)->args)
+ + ]
11893 : : {
3173 11894 : 6 : OpExpr *opexpr = lfirst_node(OpExpr, l);
11895 : :
7294 11896 : 6 : appendStringInfoString(buf, sep);
11897 : 6 : get_rule_expr(linitial(opexpr->args), context, true);
11898 [ + + ]: 6 : if (!opname)
11899 : 3 : opname = generate_operator_name(opexpr->opno,
3101 11900 : 3 : exprType(linitial(opexpr->args)),
11901 : 3 : exprType(lsecond(opexpr->args)));
7294 11902 : 6 : sep = ", ";
11903 : : }
7741 11904 : 3 : appendStringInfoChar(buf, ')');
11905 : : }
7294 11906 [ + - ]: 3 : else if (IsA(sublink->testexpr, RowCompareExpr))
11907 : : {
11908 : : /* multiple combining operators, < <= > >= cases */
11909 : 3 : RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
11910 : :
11911 : 3 : appendStringInfoChar(buf, '(');
11912 : 3 : get_rule_expr((Node *) rcexpr->largs, context, true);
11913 : 3 : opname = generate_operator_name(linitial_oid(rcexpr->opnos),
11914 : 3 : exprType(linitial(rcexpr->largs)),
3101 11915 : 3 : exprType(linitial(rcexpr->rargs)));
7294 11916 : 3 : appendStringInfoChar(buf, ')');
11917 : : }
11918 : : else
7294 tgl@sss.pgh.pa.us 11919 [ # # ]:UBC 0 : elog(ERROR, "unrecognized testexpr type: %d",
11920 : : (int) nodeTag(sublink->testexpr));
11921 : : }
11922 : :
9529 tgl@sss.pgh.pa.us 11923 :CBC 230 : need_paren = true;
11924 : :
9703 bruce@momjian.us 11925 [ + + + - : 230 : switch (sublink->subLinkType)
+ - ]
11926 : : {
9938 11927 : 88 : case EXISTS_SUBLINK:
4430 rhaas@postgresql.org 11928 : 88 : appendStringInfoString(buf, "EXISTS ");
9938 bruce@momjian.us 11929 : 88 : break;
11930 : :
11931 : 6 : case ANY_SUBLINK:
3101 tgl@sss.pgh.pa.us 11932 [ + + ]: 6 : if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
4430 rhaas@postgresql.org 11933 : 3 : appendStringInfoString(buf, " IN ");
11934 : : else
7294 tgl@sss.pgh.pa.us 11935 : 3 : appendStringInfo(buf, " %s ANY ", opname);
9938 bruce@momjian.us 11936 : 6 : break;
11937 : :
11938 : 3 : case ALL_SUBLINK:
7294 tgl@sss.pgh.pa.us 11939 : 3 : appendStringInfo(buf, " %s ALL ", opname);
9938 bruce@momjian.us 11940 : 3 : break;
11941 : :
7294 tgl@sss.pgh.pa.us 11942 :UBC 0 : case ROWCOMPARE_SUBLINK:
11943 : 0 : appendStringInfo(buf, " %s ", opname);
9938 bruce@momjian.us 11944 : 0 : break;
11945 : :
9529 tgl@sss.pgh.pa.us 11946 :CBC 133 : case EXPR_SUBLINK:
11947 : : case MULTIEXPR_SUBLINK:
11948 : : case ARRAY_SUBLINK:
11949 : 133 : need_paren = false;
11950 : 133 : break;
11951 : :
6283 tgl@sss.pgh.pa.us 11952 :UBC 0 : case CTE_SUBLINK: /* shouldn't occur in a SubLink */
11953 : : default:
8179 11954 [ # # ]: 0 : elog(ERROR, "unrecognized sublink type: %d",
11955 : : (int) sublink->subLinkType);
11956 : : break;
11957 : : }
11958 : :
9529 tgl@sss.pgh.pa.us 11959 [ + + ]:CBC 230 : if (need_paren)
9432 11960 : 97 : appendStringInfoChar(buf, '(');
11961 : :
1306 11962 : 230 : get_query_def(query, buf, context->namespaces, NULL, false,
11963 : : context->prettyFlags, context->wrapColumn,
11964 : : context->indentLevel);
11965 : :
9529 11966 [ + + ]: 230 : if (need_paren)
4430 rhaas@postgresql.org 11967 : 97 : appendStringInfoString(buf, "))");
11968 : : else
9432 tgl@sss.pgh.pa.us 11969 : 133 : appendStringInfoChar(buf, ')');
9977 bruce@momjian.us 11970 : 230 : }
11971 : :
11972 : :
11973 : : /* ----------
11974 : : * get_xmltable - Parse back a XMLTABLE function
11975 : : * ----------
11976 : : */
11977 : : static void
622 amitlan@postgresql.o 11978 : 31 : get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
11979 : : {
3206 alvherre@alvh.no-ip. 11980 : 31 : StringInfo buf = context->buf;
11981 : :
11982 : 31 : appendStringInfoString(buf, "XMLTABLE(");
11983 : :
11984 [ + + ]: 31 : if (tf->ns_uris != NIL)
11985 : : {
11986 : : ListCell *lc1,
11987 : : *lc2;
11988 : 8 : bool first = true;
11989 : :
11990 : 8 : appendStringInfoString(buf, "XMLNAMESPACES (");
11991 [ + - + + : 16 : forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
+ - + + +
+ + - +
+ ]
11992 : : {
11993 : 8 : Node *expr = (Node *) lfirst(lc1);
1560 peter@eisentraut.org 11994 : 8 : String *ns_node = lfirst_node(String, lc2);
11995 : :
3206 alvherre@alvh.no-ip. 11996 [ - + ]: 8 : if (!first)
3206 alvherre@alvh.no-ip. 11997 :UBC 0 : appendStringInfoString(buf, ", ");
11998 : : else
3206 alvherre@alvh.no-ip. 11999 :CBC 8 : first = false;
12000 : :
2648 tgl@sss.pgh.pa.us 12001 [ + - ]: 8 : if (ns_node != NULL)
12002 : : {
3206 alvherre@alvh.no-ip. 12003 : 8 : get_rule_expr(expr, context, showimplicit);
339 dean.a.rasheed@gmail 12004 : 8 : appendStringInfo(buf, " AS %s",
12005 : 8 : quote_identifier(strVal(ns_node)));
12006 : : }
12007 : : else
12008 : : {
3206 alvherre@alvh.no-ip. 12009 :UBC 0 : appendStringInfoString(buf, "DEFAULT ");
12010 : 0 : get_rule_expr(expr, context, showimplicit);
12011 : : }
12012 : : }
3206 alvherre@alvh.no-ip. 12013 :CBC 8 : appendStringInfoString(buf, "), ");
12014 : : }
12015 : :
12016 : 31 : appendStringInfoChar(buf, '(');
12017 : 31 : get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
12018 : 31 : appendStringInfoString(buf, ") PASSING (");
12019 : 31 : get_rule_expr((Node *) tf->docexpr, context, showimplicit);
12020 : 31 : appendStringInfoChar(buf, ')');
12021 : :
12022 [ + - ]: 31 : if (tf->colexprs != NIL)
12023 : : {
12024 : : ListCell *l1;
12025 : : ListCell *l2;
12026 : : ListCell *l3;
12027 : : ListCell *l4;
12028 : : ListCell *l5;
12029 : 31 : int colnum = 0;
12030 : :
12031 : 31 : appendStringInfoString(buf, " COLUMNS ");
2484 tgl@sss.pgh.pa.us 12032 [ + - + + : 187 : forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
+ - + + +
- + + + -
+ + + - +
+ + + + -
+ - + - +
- + + ]
12033 : : l4, tf->colexprs, l5, tf->coldefexprs)
12034 : : {
3206 alvherre@alvh.no-ip. 12035 : 156 : char *colname = strVal(lfirst(l1));
2484 tgl@sss.pgh.pa.us 12036 : 156 : Oid typid = lfirst_oid(l2);
12037 : 156 : int32 typmod = lfirst_int(l3);
12038 : 156 : Node *colexpr = (Node *) lfirst(l4);
12039 : 156 : Node *coldefexpr = (Node *) lfirst(l5);
12040 : 156 : bool ordinality = (tf->ordinalitycol == colnum);
3206 alvherre@alvh.no-ip. 12041 : 156 : bool notnull = bms_is_member(colnum, tf->notnulls);
12042 : :
12043 [ + + ]: 156 : if (colnum > 0)
12044 : 125 : appendStringInfoString(buf, ", ");
12045 : 156 : colnum++;
12046 : :
12047 [ + + ]: 295 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
12048 : : ordinality ? "FOR ORDINALITY" :
12049 : 139 : format_type_with_typemod(typid, typmod));
12050 [ + + ]: 156 : if (ordinality)
12051 : 17 : continue;
12052 : :
12053 [ + + ]: 139 : if (coldefexpr != NULL)
12054 : : {
12055 : 17 : appendStringInfoString(buf, " DEFAULT (");
12056 : 17 : get_rule_expr((Node *) coldefexpr, context, showimplicit);
12057 : 17 : appendStringInfoChar(buf, ')');
12058 : : }
12059 [ + + ]: 139 : if (colexpr != NULL)
12060 : : {
12061 : 127 : appendStringInfoString(buf, " PATH (");
12062 : 127 : get_rule_expr((Node *) colexpr, context, showimplicit);
12063 : 127 : appendStringInfoChar(buf, ')');
12064 : : }
12065 [ + + ]: 139 : if (notnull)
12066 : 17 : appendStringInfoString(buf, " NOT NULL");
12067 : : }
12068 : : }
12069 : :
12070 : 31 : appendStringInfoChar(buf, ')');
12071 : 31 : }
12072 : :
12073 : : /*
12074 : : * get_json_table_nested_columns - Parse back nested JSON_TABLE columns
12075 : : */
12076 : : static void
618 amitlan@postgresql.o 12077 : 51 : get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
12078 : : deparse_context *context, bool showimplicit,
12079 : : bool needcomma)
12080 : : {
12081 [ + + ]: 51 : if (IsA(plan, JsonTablePathScan))
12082 : : {
12083 : 36 : JsonTablePathScan *scan = castNode(JsonTablePathScan, plan);
12084 : :
12085 [ + + ]: 36 : if (needcomma)
12086 : 24 : appendStringInfoChar(context->buf, ',');
12087 : :
12088 : 36 : appendStringInfoChar(context->buf, ' ');
12089 : 36 : appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
12090 : 36 : get_const_expr(scan->path->value, context, -1);
12091 : 36 : appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
12092 : 36 : get_json_table_columns(tf, scan, context, showimplicit);
12093 : : }
12094 [ + - ]: 15 : else if (IsA(plan, JsonTableSiblingJoin))
12095 : : {
12096 : 15 : JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
12097 : :
12098 : 15 : get_json_table_nested_columns(tf, join->lplan, context, showimplicit,
12099 : : needcomma);
12100 : 15 : get_json_table_nested_columns(tf, join->rplan, context, showimplicit,
12101 : : true);
12102 : : }
12103 : 51 : }
12104 : :
12105 : : /*
12106 : : * get_json_table_columns - Parse back JSON_TABLE columns
12107 : : */
12108 : : static void
12109 : 90 : get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
12110 : : deparse_context *context,
12111 : : bool showimplicit)
12112 : : {
622 12113 : 90 : StringInfo buf = context->buf;
12114 : : ListCell *lc_colname;
12115 : : ListCell *lc_coltype;
12116 : : ListCell *lc_coltypmod;
12117 : : ListCell *lc_colvalexpr;
12118 : 90 : int colnum = 0;
12119 : :
12120 : 90 : appendStringInfoChar(buf, ' ');
12121 : 90 : appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12122 : :
12123 [ + + ]: 90 : if (PRETTY_INDENT(context))
12124 : 69 : context->indentLevel += PRETTYINDENT_VAR;
12125 : :
12126 [ + - + + : 429 : forfour(lc_colname, tf->colnames,
+ - + + +
- + + + -
+ + + + +
- + - + -
+ + ]
12127 : : lc_coltype, tf->coltypes,
12128 : : lc_coltypmod, tf->coltypmods,
12129 : : lc_colvalexpr, tf->colvalexprs)
12130 : : {
12131 : 363 : char *colname = strVal(lfirst(lc_colname));
12132 : : JsonExpr *colexpr;
12133 : : Oid typid;
12134 : : int32 typmod;
12135 : : bool ordinality;
12136 : : JsonBehaviorType default_behavior;
12137 : :
12138 : 363 : typid = lfirst_oid(lc_coltype);
12139 : 363 : typmod = lfirst_int(lc_coltypmod);
12140 : 363 : colexpr = castNode(JsonExpr, lfirst(lc_colvalexpr));
12141 : :
12142 : : /* Skip columns that don't belong to this scan. */
618 12143 [ + + + + ]: 363 : if (scan->colMin < 0 || colnum < scan->colMin)
12144 : : {
12145 : 132 : colnum++;
12146 : 132 : continue;
12147 : : }
12148 [ + + ]: 231 : if (colnum > scan->colMax)
12149 : 24 : break;
12150 : :
12151 [ + + ]: 207 : if (colnum > scan->colMin)
622 12152 : 129 : appendStringInfoString(buf, ", ");
12153 : :
12154 : 207 : colnum++;
12155 : :
12156 : 207 : ordinality = !colexpr;
12157 : :
12158 : 207 : appendContextKeyword(context, "", 0, 0, 0);
12159 : :
12160 [ + + ]: 405 : appendStringInfo(buf, "%s %s", quote_identifier(colname),
12161 : : ordinality ? "FOR ORDINALITY" :
12162 : 198 : format_type_with_typemod(typid, typmod));
12163 [ + + ]: 207 : if (ordinality)
12164 : 9 : continue;
12165 : :
12166 : : /*
12167 : : * Set default_behavior to guide get_json_expr_options() on whether to
12168 : : * emit the ON ERROR / EMPTY clauses.
12169 : : */
12170 [ + + ]: 198 : if (colexpr->op == JSON_EXISTS_OP)
12171 : : {
12172 : 18 : appendStringInfoString(buf, " EXISTS");
12173 : 18 : default_behavior = JSON_BEHAVIOR_FALSE;
12174 : : }
12175 : : else
12176 : : {
12177 [ + + ]: 180 : if (colexpr->op == JSON_QUERY_OP)
12178 : : {
12179 : : char typcategory;
12180 : : bool typispreferred;
12181 : :
12182 : 87 : get_type_category_preferred(typid, &typcategory, &typispreferred);
12183 : :
12184 [ + + ]: 87 : if (typcategory == TYPCATEGORY_STRING)
12185 : 18 : appendStringInfoString(buf,
12186 [ - + ]: 18 : colexpr->format->format_type == JS_FORMAT_JSONB ?
12187 : : " FORMAT JSONB" : " FORMAT JSON");
12188 : : }
12189 : :
12190 : 180 : default_behavior = JSON_BEHAVIOR_NULL;
12191 : : }
12192 : :
12193 : 198 : appendStringInfoString(buf, " PATH ");
12194 : :
12195 : 198 : get_json_path_spec(colexpr->path_spec, context, showimplicit);
12196 : :
12197 : 198 : get_json_expr_options(colexpr, context, default_behavior);
12198 : : }
12199 : :
618 12200 [ + + ]: 90 : if (scan->child)
12201 : 21 : get_json_table_nested_columns(tf, scan->child, context, showimplicit,
12202 : 21 : scan->colMin >= 0);
12203 : :
622 12204 [ + + ]: 90 : if (PRETTY_INDENT(context))
12205 : 69 : context->indentLevel -= PRETTYINDENT_VAR;
12206 : :
12207 : 90 : appendContextKeyword(context, ")", 0, 0, 0);
12208 : 90 : }
12209 : :
12210 : : /* ----------
12211 : : * get_json_table - Parse back a JSON_TABLE function
12212 : : * ----------
12213 : : */
12214 : : static void
12215 : 54 : get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
12216 : : {
12217 : 54 : StringInfo buf = context->buf;
12218 : 54 : JsonExpr *jexpr = castNode(JsonExpr, tf->docexpr);
12219 : 54 : JsonTablePathScan *root = castNode(JsonTablePathScan, tf->plan);
12220 : :
12221 : 54 : appendStringInfoString(buf, "JSON_TABLE(");
12222 : :
12223 [ + + ]: 54 : if (PRETTY_INDENT(context))
12224 : 33 : context->indentLevel += PRETTYINDENT_VAR;
12225 : :
12226 : 54 : appendContextKeyword(context, "", 0, 0, 0);
12227 : :
12228 : 54 : get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12229 : :
12230 : 54 : appendStringInfoString(buf, ", ");
12231 : :
12232 : 54 : get_const_expr(root->path->value, context, -1);
12233 : :
12234 : 54 : appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12235 : :
12236 [ + + ]: 54 : if (jexpr->passing_values)
12237 : : {
12238 : : ListCell *lc1,
12239 : : *lc2;
12240 : 42 : bool needcomma = false;
12241 : :
12242 : 42 : appendStringInfoChar(buf, ' ');
12243 : 42 : appendContextKeyword(context, "PASSING ", 0, 0, 0);
12244 : :
12245 [ + + ]: 42 : if (PRETTY_INDENT(context))
12246 : 21 : context->indentLevel += PRETTYINDENT_VAR;
12247 : :
12248 [ + - + + : 126 : forboth(lc1, jexpr->passing_names,
+ - + + +
+ + - +
+ ]
12249 : : lc2, jexpr->passing_values)
12250 : : {
12251 [ + + ]: 84 : if (needcomma)
12252 : 42 : appendStringInfoString(buf, ", ");
12253 : 84 : needcomma = true;
12254 : :
12255 : 84 : appendContextKeyword(context, "", 0, 0, 0);
12256 : :
12257 : 84 : get_rule_expr((Node *) lfirst(lc2), context, false);
12258 : 84 : appendStringInfo(buf, " AS %s",
12259 : 84 : quote_identifier((lfirst_node(String, lc1))->sval)
12260 : : );
12261 : : }
12262 : :
12263 [ + + ]: 42 : if (PRETTY_INDENT(context))
12264 : 21 : context->indentLevel -= PRETTYINDENT_VAR;
12265 : : }
12266 : :
618 12267 : 54 : get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12268 : : showimplicit);
12269 : :
467 12270 [ + + ]: 54 : if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
622 12271 : 3 : get_json_behavior(jexpr->on_error, context, "ERROR");
12272 : :
12273 [ + + ]: 54 : if (PRETTY_INDENT(context))
12274 : 33 : context->indentLevel -= PRETTYINDENT_VAR;
12275 : :
12276 : 54 : appendContextKeyword(context, ")", 0, 0, 0);
12277 : 54 : }
12278 : :
12279 : : /* ----------
12280 : : * get_tablefunc - Parse back a table function
12281 : : * ----------
12282 : : */
12283 : : static void
12284 : 85 : get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
12285 : : {
12286 : : /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12287 : :
12288 [ + + ]: 85 : if (tf->functype == TFT_XMLTABLE)
12289 : 31 : get_xmltable(tf, context, showimplicit);
12290 [ + - ]: 54 : else if (tf->functype == TFT_JSON_TABLE)
12291 : 54 : get_json_table(tf, context, showimplicit);
12292 : 85 : }
12293 : :
12294 : : /* ----------
12295 : : * get_from_clause - Parse back a FROM clause
12296 : : *
12297 : : * "prefix" is the keyword that denotes the start of the list of FROM
12298 : : * elements. It is FROM when used to parse back SELECT and UPDATE, but
12299 : : * is USING when parsing back DELETE.
12300 : : * ----------
12301 : : */
12302 : : static void
7559 neilc@samurai.com 12303 : 2463 : get_from_clause(Query *query, const char *prefix, deparse_context *context)
12304 : : {
9227 tgl@sss.pgh.pa.us 12305 : 2463 : StringInfo buf = context->buf;
8176 12306 : 2463 : bool first = true;
12307 : : ListCell *l;
12308 : :
12309 : : /*
12310 : : * We use the query's jointree as a guide to what to print. However, we
12311 : : * must ignore auto-added RTEs that are marked not inFromCl. (These can
12312 : : * only appear at the top level of the jointree, so it's sufficient to
12313 : : * check here.) This check also ensures we ignore the rule pseudo-RTEs
12314 : : * for NEW and OLD.
12315 : : */
9210 12316 [ + + + + : 4901 : foreach(l, query->jointree->fromlist)
+ + ]
12317 : : {
9036 bruce@momjian.us 12318 : 2438 : Node *jtnode = (Node *) lfirst(l);
12319 : :
9227 tgl@sss.pgh.pa.us 12320 [ + + ]: 2438 : if (IsA(jtnode, RangeTblRef))
12321 : : {
12322 : 1955 : int varno = ((RangeTblRef *) jtnode)->rtindex;
12323 : 1955 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12324 : :
12325 [ + + ]: 1955 : if (!rte->inFromCl)
12326 : 200 : continue;
12327 : : }
12328 : :
8176 12329 [ + + ]: 2238 : if (first)
12330 : : {
7559 neilc@samurai.com 12331 : 2053 : appendContextKeyword(context, prefix,
12332 : : -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
8176 tgl@sss.pgh.pa.us 12333 : 2053 : first = false;
12334 : :
5050 andrew@dunslane.net 12335 : 2053 : get_from_clause_item(jtnode, query, context);
12336 : : }
12337 : : else
12338 : : {
12339 : : StringInfoData itembuf;
12340 : :
8171 bruce@momjian.us 12341 : 185 : appendStringInfoString(buf, ", ");
12342 : :
12343 : : /*
12344 : : * Put the new FROM item's text into itembuf so we can decide
12345 : : * after we've got it whether or not it needs to go on a new line.
12346 : : */
4741 tgl@sss.pgh.pa.us 12347 : 185 : initStringInfo(&itembuf);
12348 : 185 : context->buf = &itembuf;
12349 : :
5050 andrew@dunslane.net 12350 : 185 : get_from_clause_item(jtnode, query, context);
12351 : :
12352 : : /* Restore context's output buffer */
12353 : 185 : context->buf = buf;
12354 : :
12355 : : /* Consider line-wrapping if enabled */
4741 tgl@sss.pgh.pa.us 12356 [ + - + - ]: 185 : if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
12357 : : {
12358 : : /* Does the new item start with a new line? */
4419 12359 [ + - - + ]: 185 : if (itembuf.len > 0 && itembuf.data[0] == '\n')
12360 : : {
12361 : : /* If so, we shouldn't add anything */
12362 : : /* instead, remove any trailing spaces currently in buf */
4419 tgl@sss.pgh.pa.us 12363 :UBC 0 : removeStringInfoSpaces(buf);
12364 : : }
12365 : : else
12366 : : {
12367 : : char *trailing_nl;
12368 : :
12369 : : /* Locate the start of the current line in the buffer */
4419 tgl@sss.pgh.pa.us 12370 :CBC 185 : trailing_nl = strrchr(buf->data, '\n');
12371 [ - + ]: 185 : if (trailing_nl == NULL)
4419 tgl@sss.pgh.pa.us 12372 :UBC 0 : trailing_nl = buf->data;
12373 : : else
4419 tgl@sss.pgh.pa.us 12374 :CBC 185 : trailing_nl++;
12375 : :
12376 : : /*
12377 : : * Add a newline, plus some indentation, if the new item
12378 : : * would cause an overflow.
12379 : : */
12380 [ + - ]: 185 : if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
12381 : 185 : appendContextKeyword(context, "", -PRETTYINDENT_STD,
12382 : : PRETTYINDENT_STD,
12383 : : PRETTYINDENT_VAR);
12384 : : }
12385 : : }
12386 : :
12387 : : /* Add the new item */
2339 drowley@postgresql.o 12388 : 185 : appendBinaryStringInfo(buf, itembuf.data, itembuf.len);
12389 : :
12390 : : /* clean up */
4741 tgl@sss.pgh.pa.us 12391 : 185 : pfree(itembuf.data);
12392 : : }
12393 : : }
9227 12394 : 2463 : }
12395 : :
12396 : : static void
12397 : 3760 : get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
12398 : : {
12399 : 3760 : StringInfo buf = context->buf;
4734 12400 : 3760 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
12401 : :
9227 12402 [ + + ]: 3760 : if (IsA(jtnode, RangeTblRef))
12403 : : {
12404 : 2999 : int varno = ((RangeTblRef *) jtnode)->rtindex;
12405 : 2999 : RangeTblEntry *rte = rt_fetch(varno, query->rtable);
4734 12406 : 2999 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
4409 12407 : 2999 : RangeTblFunction *rtfunc1 = NULL;
12408 : :
4880 12409 [ + + ]: 2999 : if (rte->lateral)
12410 : 62 : appendStringInfoString(buf, "LATERAL ");
12411 : :
12412 : : /* Print the FROM item proper */
8671 12413 [ + + + + : 2999 : switch (rte->rtekind)
+ + - ]
12414 : : {
12415 : 2277 : case RTE_RELATION:
12416 : : /* Normal relation RTE */
12417 : 4554 : appendStringInfo(buf, "%s%s",
12418 [ + + ]: 2277 : only_marker(rte),
12419 : : generate_relation_name(rte->relid,
12420 : : context->namespaces));
12421 : 2277 : break;
12422 : 146 : case RTE_SUBQUERY:
12423 : : /* Subquery RTE */
12424 : 146 : appendStringInfoChar(buf, '(');
8171 bruce@momjian.us 12425 : 146 : get_query_def(rte->subquery, buf, context->namespaces, NULL,
12426 : : true,
12427 : : context->prettyFlags, context->wrapColumn,
12428 : : context->indentLevel);
8671 tgl@sss.pgh.pa.us 12429 : 146 : appendStringInfoChar(buf, ')');
12430 : 146 : break;
8620 12431 : 429 : case RTE_FUNCTION:
12432 : : /* Function RTE */
4409 12433 : 429 : rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
12434 : :
12435 : : /*
12436 : : * Omit ROWS FROM() syntax for just one function, unless it
12437 : : * has both a coldeflist and WITH ORDINALITY. If it has both,
12438 : : * we must use ROWS FROM() syntax to avoid ambiguity about
12439 : : * whether the coldeflist includes the ordinality column.
12440 : : */
12441 [ + + ]: 429 : if (list_length(rte->functions) == 1 &&
12442 [ - + - - ]: 414 : (rtfunc1->funccolnames == NIL || !rte->funcordinality))
12443 : : {
3079 12444 : 414 : get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
12445 : : /* we'll print the coldeflist below, if it has one */
12446 : : }
12447 : : else
12448 : : {
12449 : : bool all_unnest;
12450 : : ListCell *lc;
12451 : :
12452 : : /*
12453 : : * If all the function calls in the list are to unnest,
12454 : : * and none need a coldeflist, then collapse the list back
12455 : : * down to UNNEST(args). (If we had more than one
12456 : : * built-in unnest function, this would get more
12457 : : * difficult.)
12458 : : *
12459 : : * XXX This is pretty ugly, since it makes not-terribly-
12460 : : * future-proof assumptions about what the parser would do
12461 : : * with the output; but the alternative is to emit our
12462 : : * nonstandard ROWS FROM() notation for what might have
12463 : : * been a perfectly spec-compliant multi-argument
12464 : : * UNNEST().
12465 : : */
4409 12466 : 15 : all_unnest = true;
12467 [ + - + + : 39 : foreach(lc, rte->functions)
+ + ]
12468 : : {
12469 : 33 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12470 : :
12471 [ + - ]: 33 : if (!IsA(rtfunc->funcexpr, FuncExpr) ||
1871 12472 [ + + ]: 33 : ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
4409 12473 [ - + ]: 24 : rtfunc->funccolnames != NIL)
12474 : : {
12475 : 9 : all_unnest = false;
12476 : 9 : break;
12477 : : }
12478 : : }
12479 : :
12480 [ + + ]: 15 : if (all_unnest)
12481 : : {
12482 : 6 : List *allargs = NIL;
12483 : :
12484 [ + - + + : 24 : foreach(lc, rte->functions)
+ + ]
12485 : : {
12486 : 18 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12487 : 18 : List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
12488 : :
2319 12489 : 18 : allargs = list_concat(allargs, args);
12490 : : }
12491 : :
4409 12492 : 6 : appendStringInfoString(buf, "UNNEST(");
12493 : 6 : get_rule_expr((Node *) allargs, context, true);
12494 : 6 : appendStringInfoChar(buf, ')');
12495 : : }
12496 : : else
12497 : : {
12498 : 9 : int funcno = 0;
12499 : :
4390 noah@leadboat.com 12500 : 9 : appendStringInfoString(buf, "ROWS FROM(");
4409 tgl@sss.pgh.pa.us 12501 [ + - + + : 33 : foreach(lc, rte->functions)
+ + ]
12502 : : {
12503 : 24 : RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
12504 : :
12505 [ + + ]: 24 : if (funcno > 0)
12506 : 15 : appendStringInfoString(buf, ", ");
3079 12507 : 24 : get_rule_expr_funccall(rtfunc->funcexpr, context, true);
4409 12508 [ + + ]: 24 : if (rtfunc->funccolnames != NIL)
12509 : : {
12510 : : /* Reconstruct the column definition list */
12511 : 3 : appendStringInfoString(buf, " AS ");
12512 : 3 : get_from_clause_coldeflist(rtfunc,
12513 : : NULL,
12514 : : context);
12515 : : }
12516 : 24 : funcno++;
12517 : : }
12518 : 9 : appendStringInfoChar(buf, ')');
12519 : : }
12520 : : /* prevent printing duplicate coldeflist below */
12521 : 15 : rtfunc1 = NULL;
12522 : : }
4524 stark@mit.edu 12523 [ + + ]: 429 : if (rte->funcordinality)
12524 : 9 : appendStringInfoString(buf, " WITH ORDINALITY");
8620 tgl@sss.pgh.pa.us 12525 : 429 : break;
3206 alvherre@alvh.no-ip. 12526 : 49 : case RTE_TABLEFUNC:
12527 : 49 : get_tablefunc(rte->tablefunc, context, true);
12528 : 49 : break;
7077 mail@joeconway.com 12529 : 6 : case RTE_VALUES:
12530 : : /* Values list RTE */
3948 tgl@sss.pgh.pa.us 12531 : 6 : appendStringInfoChar(buf, '(');
7077 mail@joeconway.com 12532 : 6 : get_values_def(rte->values_lists, context);
3948 tgl@sss.pgh.pa.us 12533 : 6 : appendStringInfoChar(buf, ')');
7077 mail@joeconway.com 12534 : 6 : break;
6283 tgl@sss.pgh.pa.us 12535 : 92 : case RTE_CTE:
12536 : 92 : appendStringInfoString(buf, quote_identifier(rte->ctename));
12537 : 92 : break;
8671 tgl@sss.pgh.pa.us 12538 :UBC 0 : default:
8179 12539 [ # # ]: 0 : elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
12540 : : break;
12541 : : }
12542 : :
12543 : : /* Print the relation alias, if needed */
1034 tgl@sss.pgh.pa.us 12544 :CBC 2999 : get_rte_alias(rte, varno, false, context);
12545 : :
12546 : : /* Print the column definitions or aliases, if needed */
4409 12547 [ + + - + ]: 2999 : if (rtfunc1 && rtfunc1->funccolnames != NIL)
12548 : : {
12549 : : /* Reconstruct the columndef list, which is also the aliases */
4409 tgl@sss.pgh.pa.us 12550 :UBC 0 : get_from_clause_coldeflist(rtfunc1, colinfo, context);
12551 : : }
12552 : : else
12553 : : {
12554 : : /* Else print column aliases as needed */
4734 tgl@sss.pgh.pa.us 12555 :CBC 2999 : get_column_alias_list(colinfo, context);
12556 : : }
12557 : :
12558 : : /* Tablesample clause must go after any alias */
3798 12559 [ + + + + ]: 2999 : if (rte->rtekind == RTE_RELATION && rte->tablesample)
12560 : 16 : get_tablesample_def(rte->tablesample, context);
12561 : : }
9227 12562 [ + - ]: 761 : else if (IsA(jtnode, JoinExpr))
12563 : : {
12564 : 761 : JoinExpr *j = (JoinExpr *) jtnode;
4734 12565 : 761 : deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
12566 : : bool need_paren_on_right;
12567 : :
8176 12568 : 1747 : need_paren_on_right = PRETTY_PAREN(context) &&
7479 12569 [ + + - + ]: 761 : !IsA(j->rarg, RangeTblRef) &&
2041 tgl@sss.pgh.pa.us 12570 [ # # # # ]:UBC 0 : !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
12571 : :
8176 tgl@sss.pgh.pa.us 12572 [ + + + + ]:CBC 761 : if (!PRETTY_PAREN(context) || j->alias != NULL)
8171 bruce@momjian.us 12573 : 590 : appendStringInfoChar(buf, '(');
12574 : :
9227 tgl@sss.pgh.pa.us 12575 : 761 : get_from_clause_item(j->larg, query, context);
12576 : :
4734 12577 [ + + + - : 761 : switch (j->jointype)
- ]
12578 : : {
12579 : 418 : case JOIN_INNER:
12580 [ + + ]: 418 : if (j->quals)
12581 : 397 : appendContextKeyword(context, " JOIN ",
12582 : : -PRETTYINDENT_STD,
12583 : : PRETTYINDENT_STD,
12584 : : PRETTYINDENT_JOIN);
12585 : : else
12586 : 21 : appendContextKeyword(context, " CROSS JOIN ",
12587 : : -PRETTYINDENT_STD,
12588 : : PRETTYINDENT_STD,
12589 : : PRETTYINDENT_JOIN);
12590 : 418 : break;
12591 : 292 : case JOIN_LEFT:
12592 : 292 : appendContextKeyword(context, " LEFT JOIN ",
12593 : : -PRETTYINDENT_STD,
12594 : : PRETTYINDENT_STD,
12595 : : PRETTYINDENT_JOIN);
12596 : 292 : break;
12597 : 51 : case JOIN_FULL:
12598 : 51 : appendContextKeyword(context, " FULL JOIN ",
12599 : : -PRETTYINDENT_STD,
12600 : : PRETTYINDENT_STD,
12601 : : PRETTYINDENT_JOIN);
12602 : 51 : break;
4734 tgl@sss.pgh.pa.us 12603 :UBC 0 : case JOIN_RIGHT:
12604 : 0 : appendContextKeyword(context, " RIGHT JOIN ",
12605 : : -PRETTYINDENT_STD,
12606 : : PRETTYINDENT_STD,
12607 : : PRETTYINDENT_JOIN);
12608 : 0 : break;
12609 : 0 : default:
12610 [ # # ]: 0 : elog(ERROR, "unrecognized join type: %d",
12611 : : (int) j->jointype);
12612 : : }
12613 : :
8176 tgl@sss.pgh.pa.us 12614 [ - + ]:CBC 761 : if (need_paren_on_right)
8171 bruce@momjian.us 12615 :UBC 0 : appendStringInfoChar(buf, '(');
9227 tgl@sss.pgh.pa.us 12616 :CBC 761 : get_from_clause_item(j->rarg, query, context);
8176 12617 [ - + ]: 761 : if (need_paren_on_right)
8171 bruce@momjian.us 12618 :UBC 0 : appendStringInfoChar(buf, ')');
12619 : :
4734 tgl@sss.pgh.pa.us 12620 [ + + ]:CBC 761 : if (j->usingClause)
12621 : : {
12622 : : ListCell *lc;
12623 : 212 : bool first = true;
12624 : :
4430 rhaas@postgresql.org 12625 : 212 : appendStringInfoString(buf, " USING (");
12626 : : /* Use the assigned names, not what's in usingClause */
4734 tgl@sss.pgh.pa.us 12627 [ + - + + : 502 : foreach(lc, colinfo->usingNames)
+ + ]
12628 : : {
12629 : 290 : char *colname = (char *) lfirst(lc);
12630 : :
12631 [ + + ]: 290 : if (first)
12632 : 212 : first = false;
12633 : : else
4430 rhaas@postgresql.org 12634 : 78 : appendStringInfoString(buf, ", ");
4734 tgl@sss.pgh.pa.us 12635 : 290 : appendStringInfoString(buf, quote_identifier(colname));
12636 : : }
12637 : 212 : appendStringInfoChar(buf, ')');
12638 : :
1722 peter@eisentraut.org 12639 [ + + ]: 212 : if (j->join_using_alias)
12640 : 6 : appendStringInfo(buf, " AS %s",
12641 : 6 : quote_identifier(j->join_using_alias->aliasname));
12642 : : }
4734 tgl@sss.pgh.pa.us 12643 [ + + ]: 549 : else if (j->quals)
12644 : : {
4430 rhaas@postgresql.org 12645 : 525 : appendStringInfoString(buf, " ON ");
4734 tgl@sss.pgh.pa.us 12646 [ + + ]: 525 : if (!PRETTY_PAREN(context))
12647 : 516 : appendStringInfoChar(buf, '(');
12648 : 525 : get_rule_expr(j->quals, context, false);
12649 [ + + ]: 525 : if (!PRETTY_PAREN(context))
12650 : 516 : appendStringInfoChar(buf, ')');
12651 : : }
3072 12652 [ + + ]: 24 : else if (j->jointype != JOIN_INNER)
12653 : : {
12654 : : /* If we didn't say CROSS JOIN above, we must provide an ON */
12655 : 3 : appendStringInfoString(buf, " ON TRUE");
12656 : : }
12657 : :
8176 12658 [ + + + + ]: 761 : if (!PRETTY_PAREN(context) || j->alias != NULL)
8171 bruce@momjian.us 12659 : 590 : appendStringInfoChar(buf, ')');
12660 : :
12661 : : /* Yes, it's correct to put alias after the right paren ... */
9227 tgl@sss.pgh.pa.us 12662 [ + + ]: 761 : if (j->alias != NULL)
12663 : : {
12664 : : /*
12665 : : * Note that it's correct to emit an alias clause if and only if
12666 : : * there was one originally. Otherwise we'd be converting a named
12667 : : * join to unnamed or vice versa, which creates semantic
12668 : : * subtleties we don't want. However, we might print a different
12669 : : * alias name than was there originally.
12670 : : */
12671 : 54 : appendStringInfo(buf, " %s",
2380 12672 : 54 : quote_identifier(get_rtable_name(j->rtindex,
12673 : : context)));
4734 12674 : 54 : get_column_alias_list(colinfo, context);
12675 : : }
12676 : : }
12677 : : else
8179 tgl@sss.pgh.pa.us 12678 [ # # ]:UBC 0 : elog(ERROR, "unrecognized node type: %d",
12679 : : (int) nodeTag(jtnode));
9227 tgl@sss.pgh.pa.us 12680 :CBC 3760 : }
12681 : :
12682 : : /*
12683 : : * get_rte_alias - print the relation's alias, if needed
12684 : : *
12685 : : * If printed, the alias is preceded by a space, or by " AS " if use_as is true.
12686 : : */
12687 : : static void
1034 12688 : 3290 : get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
12689 : : deparse_context *context)
12690 : : {
12691 : 3290 : deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
12692 : 3290 : char *refname = get_rtable_name(varno, context);
12693 : 3290 : deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
12694 : 3290 : bool printalias = false;
12695 : :
12696 [ + + ]: 3290 : if (rte->alias != NULL)
12697 : : {
12698 : : /* Always print alias if user provided one */
12699 : 1548 : printalias = true;
12700 : : }
12701 [ + + ]: 1742 : else if (colinfo->printaliases)
12702 : : {
12703 : : /* Always print alias if we need to print column aliases */
12704 : 165 : printalias = true;
12705 : : }
12706 [ + + ]: 1577 : else if (rte->rtekind == RTE_RELATION)
12707 : : {
12708 : : /*
12709 : : * No need to print alias if it's same as relation name (this would
12710 : : * normally be the case, but not if set_rtable_names had to resolve a
12711 : : * conflict).
12712 : : */
12713 [ + + ]: 1441 : if (strcmp(refname, get_relation_name(rte->relid)) != 0)
12714 : 40 : printalias = true;
12715 : : }
12716 [ - + ]: 136 : else if (rte->rtekind == RTE_FUNCTION)
12717 : : {
12718 : : /*
12719 : : * For a function RTE, always print alias. This covers possible
12720 : : * renaming of the function and/or instability of the FigureColname
12721 : : * rules for things that aren't simple functions. Note we'd need to
12722 : : * force it anyway for the columndef list case.
12723 : : */
1034 tgl@sss.pgh.pa.us 12724 :UBC 0 : printalias = true;
12725 : : }
1034 tgl@sss.pgh.pa.us 12726 [ + + ]:CBC 136 : else if (rte->rtekind == RTE_SUBQUERY ||
12727 [ + + ]: 124 : rte->rtekind == RTE_VALUES)
12728 : : {
12729 : : /*
12730 : : * For a subquery, always print alias. This makes the output
12731 : : * SQL-spec-compliant, even though we allow such aliases to be omitted
12732 : : * on input.
12733 : : */
12734 : 18 : printalias = true;
12735 : : }
12736 [ + + ]: 118 : else if (rte->rtekind == RTE_CTE)
12737 : : {
12738 : : /*
12739 : : * No need to print alias if it's same as CTE name (this would
12740 : : * normally be the case, but not if set_rtable_names had to resolve a
12741 : : * conflict).
12742 : : */
12743 [ + + ]: 72 : if (strcmp(refname, rte->ctename) != 0)
12744 : 11 : printalias = true;
12745 : : }
12746 : :
12747 [ + + ]: 3290 : if (printalias)
12748 [ + + ]: 1782 : appendStringInfo(context->buf, "%s%s",
12749 : : use_as ? " AS " : " ",
12750 : : quote_identifier(refname));
12751 : 3290 : }
12752 : :
12753 : : /*
12754 : : * get_column_alias_list - print column alias list for an RTE
12755 : : *
12756 : : * Caller must already have printed the relation's alias name.
12757 : : */
12758 : : static void
4734 12759 : 3053 : get_column_alias_list(deparse_columns *colinfo, deparse_context *context)
12760 : : {
7790 12761 : 3053 : StringInfo buf = context->buf;
12762 : : int i;
12763 : 3053 : bool first = true;
12764 : :
12765 : : /* Don't print aliases if not needed */
4734 12766 [ + + ]: 3053 : if (!colinfo->printaliases)
12767 : 2444 : return;
12768 : :
12769 [ + + ]: 4896 : for (i = 0; i < colinfo->num_new_cols; i++)
12770 : : {
12771 : 4287 : char *colname = colinfo->new_colnames[i];
12772 : :
7790 12773 [ + + ]: 4287 : if (first)
12774 : : {
12775 : 609 : appendStringInfoChar(buf, '(');
12776 : 609 : first = false;
12777 : : }
12778 : : else
4430 rhaas@postgresql.org 12779 : 3678 : appendStringInfoString(buf, ", ");
4734 tgl@sss.pgh.pa.us 12780 : 4287 : appendStringInfoString(buf, quote_identifier(colname));
12781 : : }
7790 12782 [ + - ]: 609 : if (!first)
12783 : 609 : appendStringInfoChar(buf, ')');
12784 : : }
12785 : :
12786 : : /*
12787 : : * get_from_clause_coldeflist - reproduce FROM clause coldeflist
12788 : : *
12789 : : * When printing a top-level coldeflist (which is syntactically also the
12790 : : * relation's column alias list), use column names from colinfo. But when
12791 : : * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
12792 : : * original coldeflist's names, which are available in rtfunc->funccolnames.
12793 : : * Pass NULL for colinfo to select the latter behavior.
12794 : : *
12795 : : * The coldeflist is appended immediately (no space) to buf. Caller is
12796 : : * responsible for ensuring that an alias or AS is present before it.
12797 : : */
12798 : : static void
4409 12799 : 3 : get_from_clause_coldeflist(RangeTblFunction *rtfunc,
12800 : : deparse_columns *colinfo,
12801 : : deparse_context *context)
12802 : : {
8511 12803 : 3 : StringInfo buf = context->buf;
12804 : : ListCell *l1;
12805 : : ListCell *l2;
12806 : : ListCell *l3;
12807 : : ListCell *l4;
12808 : : int i;
12809 : :
12810 : 3 : appendStringInfoChar(buf, '(');
12811 : :
4734 12812 : 3 : i = 0;
2484 12813 [ + - + + : 12 : forfour(l1, rtfunc->funccoltypes,
+ - + + +
- + + + -
+ + + + +
- + - + -
+ + ]
12814 : : l2, rtfunc->funccoltypmods,
12815 : : l3, rtfunc->funccolcollations,
12816 : : l4, rtfunc->funccolnames)
12817 : : {
4734 12818 : 9 : Oid atttypid = lfirst_oid(l1);
12819 : 9 : int32 atttypmod = lfirst_int(l2);
12820 : 9 : Oid attcollation = lfirst_oid(l3);
12821 : : char *attname;
12822 : :
4409 12823 [ - + ]: 9 : if (colinfo)
4409 tgl@sss.pgh.pa.us 12824 :UBC 0 : attname = colinfo->colnames[i];
12825 : : else
4409 tgl@sss.pgh.pa.us 12826 :CBC 9 : attname = strVal(lfirst(l4));
12827 : :
4734 12828 [ - + ]: 9 : Assert(attname); /* shouldn't be any dropped columns here */
12829 : :
8511 12830 [ + + ]: 9 : if (i > 0)
4430 rhaas@postgresql.org 12831 : 6 : appendStringInfoString(buf, ", ");
8511 tgl@sss.pgh.pa.us 12832 : 9 : appendStringInfo(buf, "%s %s",
12833 : : quote_identifier(attname),
12834 : : format_type_with_typemod(atttypid, atttypmod));
5353 12835 [ + + - + ]: 12 : if (OidIsValid(attcollation) &&
12836 : 3 : attcollation != get_typcollation(atttypid))
5382 tgl@sss.pgh.pa.us 12837 :UBC 0 : appendStringInfo(buf, " COLLATE %s",
12838 : : generate_collation_name(attcollation));
12839 : :
8511 tgl@sss.pgh.pa.us 12840 :CBC 9 : i++;
12841 : : }
12842 : :
12843 : 3 : appendStringInfoChar(buf, ')');
12844 : 3 : }
12845 : :
12846 : : /*
12847 : : * get_tablesample_def - print a TableSampleClause
12848 : : */
12849 : : static void
3798 12850 : 16 : get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
12851 : : {
12852 : 16 : StringInfo buf = context->buf;
12853 : : Oid argtypes[1];
12854 : : int nargs;
12855 : : ListCell *l;
12856 : :
12857 : : /*
12858 : : * We should qualify the handler's function name if it wouldn't be
12859 : : * resolved by lookup in the current search path.
12860 : : */
12861 : 16 : argtypes[0] = INTERNALOID;
12862 : 16 : appendStringInfo(buf, " TABLESAMPLE %s (",
12863 : : generate_function_name(tablesample->tsmhandler, 1,
12864 : : NIL, argtypes,
12865 : : false, NULL, false));
12866 : :
12867 : 16 : nargs = 0;
12868 [ + - + + : 32 : foreach(l, tablesample->args)
+ + ]
12869 : : {
12870 [ - + ]: 16 : if (nargs++ > 0)
3798 tgl@sss.pgh.pa.us 12871 :UBC 0 : appendStringInfoString(buf, ", ");
3798 tgl@sss.pgh.pa.us 12872 :CBC 16 : get_rule_expr((Node *) lfirst(l), context, false);
12873 : : }
12874 : 16 : appendStringInfoChar(buf, ')');
12875 : :
12876 [ + + ]: 16 : if (tablesample->repeatable != NULL)
12877 : : {
12878 : 8 : appendStringInfoString(buf, " REPEATABLE (");
12879 : 8 : get_rule_expr((Node *) tablesample->repeatable, context, false);
12880 : 8 : appendStringInfoChar(buf, ')');
12881 : : }
12882 : 16 : }
12883 : :
12884 : : /*
12885 : : * get_opclass_name - fetch name of an index operator class
12886 : : *
12887 : : * The opclass name is appended (after a space) to buf.
12888 : : *
12889 : : * Output is suppressed if the opclass is the default for the given
12890 : : * actual_datatype. (If you don't want this behavior, just pass
12891 : : * InvalidOid for actual_datatype.)
12892 : : */
12893 : : static void
8840 12894 : 6387 : get_opclass_name(Oid opclass, Oid actual_datatype,
12895 : : StringInfo buf)
12896 : : {
12897 : : HeapTuple ht_opc;
12898 : : Form_pg_opclass opcrec;
12899 : : char *opcname;
12900 : : char *nspname;
12901 : :
5785 rhaas@postgresql.org 12902 : 6387 : ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
8843 tgl@sss.pgh.pa.us 12903 [ - + ]: 6387 : if (!HeapTupleIsValid(ht_opc))
8843 tgl@sss.pgh.pa.us 12904 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for opclass %u", opclass);
8843 tgl@sss.pgh.pa.us 12905 :CBC 6387 : opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
12906 : :
6934 12907 [ + + + + ]: 12754 : if (!OidIsValid(actual_datatype) ||
12908 : 6367 : GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
12909 : : {
12910 : : /* Okay, we need the opclass name. Do we need to qualify it? */
8629 12911 : 279 : opcname = NameStr(opcrec->opcname);
6934 12912 [ + - ]: 279 : if (OpclassIsVisible(opclass))
8629 12913 : 279 : appendStringInfo(buf, " %s", quote_identifier(opcname));
12914 : : else
12915 : : {
1604 tgl@sss.pgh.pa.us 12916 :UBC 0 : nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
8629 12917 : 0 : appendStringInfo(buf, " %s.%s",
12918 : : quote_identifier(nspname),
12919 : : quote_identifier(opcname));
12920 : : }
12921 : : }
8843 tgl@sss.pgh.pa.us 12922 :CBC 6387 : ReleaseSysCache(ht_opc);
12923 : 6387 : }
12924 : :
12925 : : /*
12926 : : * generate_opclass_name
12927 : : * Compute the name to display for an opclass specified by OID
12928 : : *
12929 : : * The result includes all necessary quoting and schema-prefixing.
12930 : : */
12931 : : char *
2088 akorotkov@postgresql 12932 : 3 : generate_opclass_name(Oid opclass)
12933 : : {
12934 : : StringInfoData buf;
12935 : :
12936 : 3 : initStringInfo(&buf);
12937 : 3 : get_opclass_name(opclass, InvalidOid, &buf);
12938 : :
2043 tgl@sss.pgh.pa.us 12939 : 3 : return &buf.data[1]; /* get_opclass_name() prepends space */
12940 : : }
12941 : :
12942 : : /*
12943 : : * processIndirection - take care of array and subfield assignment
12944 : : *
12945 : : * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
12946 : : * appear in the input, printing them as decoration for the base column
12947 : : * name (which we assume the caller just printed). We might also need to
12948 : : * strip CoerceToDomain nodes, but only ones that appear above assignment
12949 : : * nodes.
12950 : : *
12951 : : * Returns the subexpression that's to be assigned.
12952 : : */
12953 : : static Node *
3423 12954 : 636 : processIndirection(Node *node, deparse_context *context)
12955 : : {
7861 12956 : 636 : StringInfo buf = context->buf;
3080 12957 : 636 : CoerceToDomain *cdomain = NULL;
12958 : :
12959 : : for (;;)
12960 : : {
7861 12961 [ - + ]: 789 : if (node == NULL)
7861 tgl@sss.pgh.pa.us 12962 :UBC 0 : break;
7861 tgl@sss.pgh.pa.us 12963 [ + + ]:CBC 789 : if (IsA(node, FieldStore))
12964 : : {
12965 : 54 : FieldStore *fstore = (FieldStore *) node;
12966 : : Oid typrelid;
12967 : : char *fieldname;
12968 : :
12969 : : /* lookup tuple type */
12970 : 54 : typrelid = get_typ_typrelid(fstore->resulttype);
12971 [ - + ]: 54 : if (!OidIsValid(typrelid))
7861 tgl@sss.pgh.pa.us 12972 [ # # ]:UBC 0 : elog(ERROR, "argument type %s of FieldStore is not a tuple type",
12973 : : format_type_be(fstore->resulttype));
12974 : :
12975 : : /*
12976 : : * Print the field name. There should only be one target field in
12977 : : * stored rules. There could be more than that in executable
12978 : : * target lists, but this function cannot be used for that case.
12979 : : */
5781 tgl@sss.pgh.pa.us 12980 [ - + ]:CBC 54 : Assert(list_length(fstore->fieldnums) == 1);
2865 alvherre@alvh.no-ip. 12981 : 54 : fieldname = get_attname(typrelid,
12982 : 54 : linitial_int(fstore->fieldnums), false);
3423 tgl@sss.pgh.pa.us 12983 : 54 : appendStringInfo(buf, ".%s", quote_identifier(fieldname));
12984 : :
12985 : : /*
12986 : : * We ignore arg since it should be an uninteresting reference to
12987 : : * the target column or subcolumn.
12988 : : */
7861 12989 : 54 : node = (Node *) linitial(fstore->newvals);
12990 : : }
2511 alvherre@alvh.no-ip. 12991 [ + + ]: 735 : else if (IsA(node, SubscriptingRef))
12992 : : {
12993 : 69 : SubscriptingRef *sbsref = (SubscriptingRef *) node;
12994 : :
12995 [ - + ]: 69 : if (sbsref->refassgnexpr == NULL)
7861 tgl@sss.pgh.pa.us 12996 :UBC 0 : break;
12997 : :
2511 alvherre@alvh.no-ip. 12998 :CBC 69 : printSubscripts(sbsref, context);
12999 : :
13000 : : /*
13001 : : * We ignore refexpr since it should be an uninteresting reference
13002 : : * to the target column or subcolumn.
13003 : : */
13004 : 69 : node = (Node *) sbsref->refassgnexpr;
13005 : : }
3080 tgl@sss.pgh.pa.us 13006 [ + + ]: 666 : else if (IsA(node, CoerceToDomain))
13007 : : {
13008 : 30 : cdomain = (CoerceToDomain *) node;
13009 : : /* If it's an explicit domain coercion, we're done */
13010 [ - + ]: 30 : if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
3080 tgl@sss.pgh.pa.us 13011 :UBC 0 : break;
13012 : : /* Tentatively descend past the CoerceToDomain */
3080 tgl@sss.pgh.pa.us 13013 :CBC 30 : node = (Node *) cdomain->arg;
13014 : : }
13015 : : else
7861 13016 : 636 : break;
13017 : : }
13018 : :
13019 : : /*
13020 : : * If we descended past a CoerceToDomain whose argument turned out not to
13021 : : * be a FieldStore or array assignment, back up to the CoerceToDomain.
13022 : : * (This is not enough to be fully correct if there are nested implicit
13023 : : * CoerceToDomains, but such cases shouldn't ever occur.)
13024 : : */
3080 13025 [ + + - + ]: 636 : if (cdomain && node == (Node *) cdomain->arg)
3080 tgl@sss.pgh.pa.us 13026 :UBC 0 : node = (Node *) cdomain;
13027 : :
7861 tgl@sss.pgh.pa.us 13028 :CBC 636 : return node;
13029 : : }
13030 : :
13031 : : static void
2511 alvherre@alvh.no-ip. 13032 : 227 : printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
13033 : : {
7861 tgl@sss.pgh.pa.us 13034 : 227 : StringInfo buf = context->buf;
13035 : : ListCell *lowlist_item;
13036 : : ListCell *uplist_item;
13037 : :
2511 alvherre@alvh.no-ip. 13038 : 227 : lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
13039 [ + - + + : 454 : foreach(uplist_item, sbsref->refupperindexpr)
+ + ]
13040 : : {
7861 tgl@sss.pgh.pa.us 13041 : 227 : appendStringInfoChar(buf, '[');
13042 [ - + ]: 227 : if (lowlist_item)
13043 : : {
13044 : : /* If subexpression is NULL, get_rule_expr prints nothing */
7861 tgl@sss.pgh.pa.us 13045 :UBC 0 : get_rule_expr((Node *) lfirst(lowlist_item), context, false);
13046 : 0 : appendStringInfoChar(buf, ':');
2347 13047 : 0 : lowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item);
13048 : : }
13049 : : /* If subexpression is NULL, get_rule_expr prints nothing */
7861 tgl@sss.pgh.pa.us 13050 :CBC 227 : get_rule_expr((Node *) lfirst(uplist_item), context, false);
13051 : 227 : appendStringInfoChar(buf, ']');
13052 : : }
9258 13053 : 227 : }
13054 : :
13055 : : /*
13056 : : * quote_identifier - Quote an identifier only if needed
13057 : : *
13058 : : * When quotes are needed, we palloc the required space; slightly
13059 : : * space-wasteful but well worth it for notational simplicity.
13060 : : */
13061 : : const char *
8637 13062 : 1284707 : quote_identifier(const char *ident)
13063 : : {
13064 : : /*
13065 : : * Can avoid quoting if ident starts with a lowercase letter or underscore
13066 : : * and contains only lowercase letters, digits, and underscores, *and* is
13067 : : * not any SQL keyword. Otherwise, supply quotes.
13068 : : */
8615 13069 : 1284707 : int nquotes = 0;
13070 : : bool safe;
13071 : : const char *ptr;
13072 : : char *result;
13073 : : char *optr;
13074 : :
13075 : : /*
13076 : : * would like to use <ctype.h> macros here, but they might yield unwanted
13077 : : * locale-specific results...
13078 : : */
8634 13079 [ + + - + : 1284707 : safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
+ + ]
13080 : :
8615 13081 [ + + ]: 11146845 : for (ptr = ident; *ptr; ptr++)
13082 : : {
13083 : 9862138 : char ch = *ptr;
13084 : :
13085 [ + + + - : 9862138 : if ((ch >= 'a' && ch <= 'z') ||
+ + ]
13086 [ + + + + ]: 1204437 : (ch >= '0' && ch <= '9') ||
13087 : : (ch == '_'))
13088 : : {
13089 : : /* okay */
13090 : : }
13091 : : else
13092 : : {
13093 : 32864 : safe = false;
13094 [ + + ]: 32864 : if (ch == '"')
13095 : 82 : nquotes++;
13096 : : }
13097 : : }
13098 : :
5627 rhaas@postgresql.org 13099 [ + + ]: 1284707 : if (quote_all_identifiers)
13100 : 6622 : safe = false;
13101 : :
9508 tgl@sss.pgh.pa.us 13102 [ + + ]: 1284707 : if (safe)
13103 : : {
13104 : : /*
13105 : : * Check for keyword. We quote keywords except for unreserved ones.
13106 : : * (In some cases we could avoid quoting a col_name or type_func_name
13107 : : * keyword, but it seems much harder than it's worth to tell that.)
13108 : : *
13109 : : * Note: ScanKeywordLookup() does case-insensitive comparison, but
13110 : : * that's fine, since we already know we have all-lower-case.
13111 : : */
2537 13112 : 1264751 : int kwnum = ScanKeywordLookup(ident, &ScanKeywords);
13113 : :
13114 [ + + + + ]: 1264751 : if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
9508 13115 : 1756 : safe = false;
13116 : : }
13117 : :
9571 13118 [ + + ]: 1284707 : if (safe)
13119 : 1262995 : return ident; /* no change needed */
13120 : :
8615 13121 : 21712 : result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13122 : :
13123 : 21712 : optr = result;
13124 : 21712 : *optr++ = '"';
13125 [ + + ]: 130696 : for (ptr = ident; *ptr; ptr++)
13126 : : {
13127 : 108984 : char ch = *ptr;
13128 : :
13129 [ + + ]: 108984 : if (ch == '"')
13130 : 82 : *optr++ = '"';
13131 : 108984 : *optr++ = ch;
13132 : : }
13133 : 21712 : *optr++ = '"';
13134 : 21712 : *optr = '\0';
13135 : :
9571 13136 : 21712 : return result;
13137 : : }
13138 : :
13139 : : /*
13140 : : * quote_qualified_identifier - Quote a possibly-qualified identifier
13141 : : *
13142 : : * Return a name of the form qualifier.ident, or just ident if qualifier
13143 : : * is NULL, quoting each component if necessary. The result is palloc'd.
13144 : : */
13145 : : char *
5998 peter_e@gmx.net 13146 : 637794 : quote_qualified_identifier(const char *qualifier,
13147 : : const char *ident)
13148 : : {
13149 : : StringInfoData buf;
13150 : :
8637 tgl@sss.pgh.pa.us 13151 : 637794 : initStringInfo(&buf);
5998 peter_e@gmx.net 13152 [ + + ]: 637794 : if (qualifier)
13153 : 223236 : appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
7991 neilc@samurai.com 13154 : 637794 : appendStringInfoString(&buf, quote_identifier(ident));
8637 tgl@sss.pgh.pa.us 13155 : 637794 : return buf.data;
13156 : : }
13157 : :
13158 : : /*
13159 : : * get_relation_name
13160 : : * Get the unqualified name of a relation specified by OID
13161 : : *
13162 : : * This differs from the underlying get_rel_name() function in that it will
13163 : : * throw error instead of silently returning NULL if the OID is bad.
13164 : : */
13165 : : static char *
5524 13166 : 8325 : get_relation_name(Oid relid)
13167 : : {
13168 : 8325 : char *relname = get_rel_name(relid);
13169 : :
13170 [ - + ]: 8325 : if (!relname)
5524 tgl@sss.pgh.pa.us 13171 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
5524 tgl@sss.pgh.pa.us 13172 :CBC 8325 : return relname;
13173 : : }
13174 : :
13175 : : /*
13176 : : * generate_relation_name
13177 : : * Compute the name to display for a relation specified by OID
13178 : : *
13179 : : * The result includes all necessary quoting and schema-prefixing.
13180 : : *
13181 : : * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
13182 : : * We will forcibly qualify the relation name if it equals any CTE name
13183 : : * visible in the namespace list.
13184 : : */
13185 : : static char *
6281 13186 : 4031 : generate_relation_name(Oid relid, List *namespaces)
13187 : : {
13188 : : HeapTuple tp;
13189 : : Form_pg_class reltup;
13190 : : bool need_qual;
13191 : : ListCell *nslist;
13192 : : char *relname;
13193 : : char *nspname;
13194 : : char *result;
13195 : :
5785 rhaas@postgresql.org 13196 : 4031 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
8629 tgl@sss.pgh.pa.us 13197 [ - + ]: 4031 : if (!HeapTupleIsValid(tp))
8179 tgl@sss.pgh.pa.us 13198 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
8629 tgl@sss.pgh.pa.us 13199 :CBC 4031 : reltup = (Form_pg_class) GETSTRUCT(tp);
6281 13200 : 4031 : relname = NameStr(reltup->relname);
13201 : :
13202 : : /* Check for conflicting CTE name */
13203 : 4031 : need_qual = false;
13204 [ + + + + : 6921 : foreach(nslist, namespaces)
+ + ]
13205 : : {
13206 : 2890 : deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
13207 : : ListCell *ctlist;
13208 : :
13209 [ + + + + : 2956 : foreach(ctlist, dpns->ctes)
+ + ]
13210 : : {
13211 : 66 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);
13212 : :
13213 [ - + ]: 66 : if (strcmp(cte->ctename, relname) == 0)
13214 : : {
6281 tgl@sss.pgh.pa.us 13215 :UBC 0 : need_qual = true;
13216 : 0 : break;
13217 : : }
13218 : : }
6281 tgl@sss.pgh.pa.us 13219 [ - + ]:CBC 2890 : if (need_qual)
6281 tgl@sss.pgh.pa.us 13220 :UBC 0 : break;
13221 : : }
13222 : :
13223 : : /* Otherwise, qualify the name if not visible in search path */
6281 tgl@sss.pgh.pa.us 13224 [ + - ]:CBC 4031 : if (!need_qual)
13225 : 4031 : need_qual = !RelationIsVisible(relid);
13226 : :
13227 [ + + ]: 4031 : if (need_qual)
1604 13228 : 1149 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
13229 : : else
6281 13230 : 2882 : nspname = NULL;
13231 : :
13232 : 4031 : result = quote_qualified_identifier(nspname, relname);
13233 : :
8629 13234 : 4031 : ReleaseSysCache(tp);
13235 : :
13236 : 4031 : return result;
13237 : : }
13238 : :
13239 : : /*
13240 : : * generate_qualified_relation_name
13241 : : * Compute the name to display for a relation specified by OID
13242 : : *
13243 : : * As above, but unconditionally schema-qualify the name.
13244 : : */
13245 : : static char *
3680 13246 : 4096 : generate_qualified_relation_name(Oid relid)
13247 : : {
13248 : : HeapTuple tp;
13249 : : Form_pg_class reltup;
13250 : : char *relname;
13251 : : char *nspname;
13252 : : char *result;
13253 : :
13254 : 4096 : tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13255 [ - + ]: 4096 : if (!HeapTupleIsValid(tp))
3680 tgl@sss.pgh.pa.us 13256 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
3680 tgl@sss.pgh.pa.us 13257 :CBC 4096 : reltup = (Form_pg_class) GETSTRUCT(tp);
13258 : 4096 : relname = NameStr(reltup->relname);
13259 : :
1604 13260 : 4096 : nspname = get_namespace_name_or_temp(reltup->relnamespace);
3680 13261 [ - + ]: 4096 : if (!nspname)
3680 tgl@sss.pgh.pa.us 13262 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for namespace %u",
13263 : : reltup->relnamespace);
13264 : :
3680 tgl@sss.pgh.pa.us 13265 :CBC 4096 : result = quote_qualified_identifier(nspname, relname);
13266 : :
13267 : 4096 : ReleaseSysCache(tp);
13268 : :
13269 : 4096 : return result;
13270 : : }
13271 : :
13272 : : /*
13273 : : * generate_function_name
13274 : : * Compute the name to display for a function specified by OID,
13275 : : * given that it is being called with the specified actual arg names and
13276 : : * types. (Those matter because of ambiguous-function resolution rules.)
13277 : : *
13278 : : * If we're dealing with a potentially variadic function (in practice, this
13279 : : * means a FuncExpr or Aggref, not some other way of calling a function), then
13280 : : * has_variadic must specify whether variadic arguments have been merged,
13281 : : * and *use_variadic_p will be set to indicate whether to print VARIADIC in
13282 : : * the output. For non-FuncExpr cases, has_variadic should be false and
13283 : : * use_variadic_p can be NULL.
13284 : : *
13285 : : * inGroupBy must be true if we're deparsing a GROUP BY clause.
13286 : : *
13287 : : * The result includes all necessary quoting and schema-prefixing.
13288 : : */
13289 : : static char *
4713 13290 : 7569 : generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
13291 : : bool has_variadic, bool *use_variadic_p,
13292 : : bool inGroupBy)
13293 : : {
13294 : : char *result;
13295 : : HeapTuple proctup;
13296 : : Form_pg_proc procform;
13297 : : char *proname;
13298 : : bool use_variadic;
13299 : : char *nspname;
13300 : : FuncDetailCode p_result;
13301 : : int fgc_flags;
13302 : : Oid p_funcid;
13303 : : Oid p_rettype;
13304 : : bool p_retset;
13305 : : int p_nvargs;
13306 : : Oid p_vatype;
13307 : : Oid *p_true_typeids;
3868 andres@anarazel.de 13308 : 7569 : bool force_qualify = false;
13309 : :
5785 rhaas@postgresql.org 13310 : 7569 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
8629 tgl@sss.pgh.pa.us 13311 [ - + ]: 7569 : if (!HeapTupleIsValid(proctup))
8179 tgl@sss.pgh.pa.us 13312 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
8629 tgl@sss.pgh.pa.us 13313 :CBC 7569 : procform = (Form_pg_proc) GETSTRUCT(proctup);
13314 : 7569 : proname = NameStr(procform->proname);
13315 : :
13316 : : /*
13317 : : * Due to parser hacks to avoid needing to reserve CUBE, we need to force
13318 : : * qualification of some function names within GROUP BY.
13319 : : */
475 13320 [ - + ]: 7569 : if (inGroupBy)
13321 : : {
3868 andres@anarazel.de 13322 [ # # # # ]:UBC 0 : if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
13323 : 0 : force_qualify = true;
13324 : : }
13325 : :
13326 : : /*
13327 : : * Determine whether VARIADIC should be printed. We must do this first
13328 : : * since it affects the lookup rules in func_get_detail().
13329 : : *
13330 : : * We always print VARIADIC if the function has a merged variadic-array
13331 : : * argument. Note that this is always the case for functions taking a
13332 : : * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
13333 : : * and printed the array elements as separate arguments, the call could
13334 : : * match a newer non-VARIADIC function.
13335 : : */
4713 tgl@sss.pgh.pa.us 13336 [ + + ]:CBC 7569 : if (use_variadic_p)
13337 : : {
13338 : : /* Parser should not have set funcvariadic unless fn is variadic */
4276 13339 [ + + - + ]: 6699 : Assert(!has_variadic || OidIsValid(procform->provariadic));
13340 : 6699 : use_variadic = has_variadic;
4713 13341 : 6699 : *use_variadic_p = use_variadic;
13342 : : }
13343 : : else
13344 : : {
4276 13345 [ - + ]: 870 : Assert(!has_variadic);
4713 13346 : 870 : use_variadic = false;
13347 : : }
13348 : :
13349 : : /*
13350 : : * The idea here is to schema-qualify only if the parser would fail to
13351 : : * resolve the correct function given the unqualified func name with the
13352 : : * specified argtypes and VARIADIC flag. But if we already decided to
13353 : : * force qualification, then we can skip the lookup and pretend we didn't
13354 : : * find it.
13355 : : */
3868 andres@anarazel.de 13356 [ + - ]: 7569 : if (!force_qualify)
13357 : 7569 : p_result = func_get_detail(list_make1(makeString(proname)),
13358 : : NIL, argnames, nargs, argtypes,
1651 tgl@sss.pgh.pa.us 13359 : 7569 : !use_variadic, true, false,
13360 : : &fgc_flags,
13361 : : &p_funcid, &p_rettype,
13362 : : &p_retset, &p_nvargs, &p_vatype,
3868 andres@anarazel.de 13363 : 7569 : &p_true_typeids, NULL);
13364 : : else
13365 : : {
3868 andres@anarazel.de 13366 :UBC 0 : p_result = FUNCDETAIL_NOTFOUND;
13367 : 0 : p_funcid = InvalidOid;
13368 : : }
13369 : :
6198 tgl@sss.pgh.pa.us 13370 [ + + + + ]:CBC 7569 : if ((p_result == FUNCDETAIL_NORMAL ||
13371 [ + + ]: 634 : p_result == FUNCDETAIL_AGGREGATE ||
13372 : 6998 : p_result == FUNCDETAIL_WINDOWFUNC) &&
8202 13373 [ + - ]: 6998 : p_funcid == funcid)
8629 13374 : 6998 : nspname = NULL;
13375 : : else
1604 13376 : 571 : nspname = get_namespace_name_or_temp(procform->pronamespace);
13377 : :
8629 13378 : 7569 : result = quote_qualified_identifier(nspname, proname);
13379 : :
13380 : 7569 : ReleaseSysCache(proctup);
13381 : :
13382 : 7569 : return result;
13383 : : }
13384 : :
13385 : : /*
13386 : : * generate_operator_name
13387 : : * Compute the name to display for an operator specified by OID,
13388 : : * given that it is being called with the specified actual arg types.
13389 : : * (Arg types matter because of ambiguous-operator resolution rules.
13390 : : * Pass InvalidOid for unused arg of a unary operator.)
13391 : : *
13392 : : * The result includes all necessary quoting and schema-prefixing,
13393 : : * plus the OPERATOR() decoration needed to use a qualified operator name
13394 : : * in an expression.
13395 : : */
13396 : : static char *
13397 : 32650 : generate_operator_name(Oid operid, Oid arg1, Oid arg2)
13398 : : {
13399 : : StringInfoData buf;
13400 : : HeapTuple opertup;
13401 : : Form_pg_operator operform;
13402 : : char *oprname;
13403 : : char *nspname;
13404 : : Operator p_result;
13405 : :
13406 : 32650 : initStringInfo(&buf);
13407 : :
5785 rhaas@postgresql.org 13408 : 32650 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
8629 tgl@sss.pgh.pa.us 13409 [ - + ]: 32650 : if (!HeapTupleIsValid(opertup))
8179 tgl@sss.pgh.pa.us 13410 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u", operid);
8629 tgl@sss.pgh.pa.us 13411 :CBC 32650 : operform = (Form_pg_operator) GETSTRUCT(opertup);
13412 : 32650 : oprname = NameStr(operform->oprname);
13413 : :
13414 : : /*
13415 : : * The idea here is to schema-qualify only if the parser would fail to
13416 : : * resolve the correct operator given the unqualified op name with the
13417 : : * specified argtypes.
13418 : : */
13419 [ + + - ]: 32650 : switch (operform->oprkind)
13420 : : {
13421 : 32635 : case 'b':
7218 13422 : 32635 : p_result = oper(NULL, list_make1(makeString(oprname)), arg1, arg2,
13423 : : true, -1);
8629 13424 : 32635 : break;
13425 : 15 : case 'l':
7218 13426 : 15 : p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2,
13427 : : true, -1);
8629 13428 : 15 : break;
8629 tgl@sss.pgh.pa.us 13429 :UBC 0 : default:
8179 13430 [ # # ]: 0 : elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
13431 : : p_result = NULL; /* keep compiler quiet */
13432 : : break;
13433 : : }
13434 : :
8629 tgl@sss.pgh.pa.us 13435 [ + + + - ]:CBC 32650 : if (p_result != NULL && oprid(p_result) == operid)
13436 : 32645 : nspname = NULL;
13437 : : else
13438 : : {
1604 13439 : 5 : nspname = get_namespace_name_or_temp(operform->oprnamespace);
8629 13440 : 5 : appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
13441 : : }
13442 : :
7991 neilc@samurai.com 13443 : 32650 : appendStringInfoString(&buf, oprname);
13444 : :
8629 tgl@sss.pgh.pa.us 13445 [ + + ]: 32650 : if (nspname)
13446 : 5 : appendStringInfoChar(&buf, ')');
13447 : :
13448 [ + + ]: 32650 : if (p_result != NULL)
13449 : 32645 : ReleaseSysCache(p_result);
13450 : :
13451 : 32650 : ReleaseSysCache(opertup);
13452 : :
13453 : 32650 : return buf.data;
13454 : : }
13455 : :
13456 : : /*
13457 : : * generate_operator_clause --- generate a binary-operator WHERE clause
13458 : : *
13459 : : * This is used for internally-generated-and-executed SQL queries, where
13460 : : * precision is essential and readability is secondary. The basic
13461 : : * requirement is to append "leftop op rightop" to buf, where leftop and
13462 : : * rightop are given as strings and are assumed to yield types leftoptype
13463 : : * and rightoptype; the operator is identified by OID. The complexity
13464 : : * comes from needing to be sure that the parser will select the desired
13465 : : * operator when the query is parsed. We always name the operator using
13466 : : * OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
13467 : : * We have to emit casts too, if either input isn't already the input type
13468 : : * of the operator; else we are at the mercy of the parser's heuristics for
13469 : : * ambiguous-operator resolution. The caller must ensure that leftop and
13470 : : * rightop are suitable arguments for a cast operation; it's best to insert
13471 : : * parentheses if they aren't just variables or parameters.
13472 : : */
13473 : : void
2830 13474 : 3334 : generate_operator_clause(StringInfo buf,
13475 : : const char *leftop, Oid leftoptype,
13476 : : Oid opoid,
13477 : : const char *rightop, Oid rightoptype)
13478 : : {
13479 : : HeapTuple opertup;
13480 : : Form_pg_operator operform;
13481 : : char *oprname;
13482 : : char *nspname;
13483 : :
13484 : 3334 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
13485 [ - + ]: 3334 : if (!HeapTupleIsValid(opertup))
2830 tgl@sss.pgh.pa.us 13486 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u", opoid);
2830 tgl@sss.pgh.pa.us 13487 :CBC 3334 : operform = (Form_pg_operator) GETSTRUCT(opertup);
13488 [ - + ]: 3334 : Assert(operform->oprkind == 'b');
13489 : 3334 : oprname = NameStr(operform->oprname);
13490 : :
13491 : 3334 : nspname = get_namespace_name(operform->oprnamespace);
13492 : :
13493 : 3334 : appendStringInfoString(buf, leftop);
13494 [ + + ]: 3334 : if (leftoptype != operform->oprleft)
13495 : 599 : add_cast_to(buf, operform->oprleft);
13496 : 3334 : appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
13497 : 3334 : appendStringInfoString(buf, oprname);
13498 : 3334 : appendStringInfo(buf, ") %s", rightop);
13499 [ + + ]: 3334 : if (rightoptype != operform->oprright)
13500 : 486 : add_cast_to(buf, operform->oprright);
13501 : :
13502 : 3334 : ReleaseSysCache(opertup);
13503 : 3334 : }
13504 : :
13505 : : /*
13506 : : * Add a cast specification to buf. We spell out the type name the hard way,
13507 : : * intentionally not using format_type_be(). This is to avoid corner cases
13508 : : * for CHARACTER, BIT, and perhaps other types, where specifying the type
13509 : : * using SQL-standard syntax results in undesirable data truncation. By
13510 : : * doing it this way we can be certain that the cast will have default (-1)
13511 : : * target typmod.
13512 : : */
13513 : : static void
13514 : 1085 : add_cast_to(StringInfo buf, Oid typid)
13515 : : {
13516 : : HeapTuple typetup;
13517 : : Form_pg_type typform;
13518 : : char *typname;
13519 : : char *nspname;
13520 : :
13521 : 1085 : typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
13522 [ - + ]: 1085 : if (!HeapTupleIsValid(typetup))
2830 tgl@sss.pgh.pa.us 13523 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", typid);
2830 tgl@sss.pgh.pa.us 13524 :CBC 1085 : typform = (Form_pg_type) GETSTRUCT(typetup);
13525 : :
13526 : 1085 : typname = NameStr(typform->typname);
1604 13527 : 1085 : nspname = get_namespace_name_or_temp(typform->typnamespace);
13528 : :
2830 13529 : 1085 : appendStringInfo(buf, "::%s.%s",
13530 : : quote_identifier(nspname), quote_identifier(typname));
13531 : :
13532 : 1085 : ReleaseSysCache(typetup);
13533 : 1085 : }
13534 : :
13535 : : /*
13536 : : * generate_qualified_type_name
13537 : : * Compute the name to display for a type specified by OID
13538 : : *
13539 : : * This is different from format_type_be() in that we unconditionally
13540 : : * schema-qualify the name. That also means no special syntax for
13541 : : * SQL-standard type names ... although in current usage, this should
13542 : : * only get used for domains, so such cases wouldn't occur anyway.
13543 : : */
13544 : : static char *
2968 13545 : 7 : generate_qualified_type_name(Oid typid)
13546 : : {
13547 : : HeapTuple tp;
13548 : : Form_pg_type typtup;
13549 : : char *typname;
13550 : : char *nspname;
13551 : : char *result;
13552 : :
13553 : 7 : tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
13554 [ - + ]: 7 : if (!HeapTupleIsValid(tp))
2968 tgl@sss.pgh.pa.us 13555 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", typid);
2968 tgl@sss.pgh.pa.us 13556 :CBC 7 : typtup = (Form_pg_type) GETSTRUCT(tp);
13557 : 7 : typname = NameStr(typtup->typname);
13558 : :
1604 13559 : 7 : nspname = get_namespace_name_or_temp(typtup->typnamespace);
2968 13560 [ - + ]: 7 : if (!nspname)
2968 tgl@sss.pgh.pa.us 13561 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for namespace %u",
13562 : : typtup->typnamespace);
13563 : :
2968 tgl@sss.pgh.pa.us 13564 :CBC 7 : result = quote_qualified_identifier(nspname, typname);
13565 : :
13566 : 7 : ReleaseSysCache(tp);
13567 : :
13568 : 7 : return result;
13569 : : }
13570 : :
13571 : : /*
13572 : : * generate_collation_name
13573 : : * Compute the name to display for a collation specified by OID
13574 : : *
13575 : : * The result includes all necessary quoting and schema-prefixing.
13576 : : */
13577 : : char *
5426 peter_e@gmx.net 13578 : 147 : generate_collation_name(Oid collid)
13579 : : {
13580 : : HeapTuple tp;
13581 : : Form_pg_collation colltup;
13582 : : char *collname;
13583 : : char *nspname;
13584 : : char *result;
13585 : :
13586 : 147 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
13587 [ - + ]: 147 : if (!HeapTupleIsValid(tp))
5426 peter_e@gmx.net 13588 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
5426 peter_e@gmx.net 13589 :CBC 147 : colltup = (Form_pg_collation) GETSTRUCT(tp);
13590 : 147 : collname = NameStr(colltup->collname);
13591 : :
13592 [ - + ]: 147 : if (!CollationIsVisible(collid))
1604 tgl@sss.pgh.pa.us 13593 :UBC 0 : nspname = get_namespace_name_or_temp(colltup->collnamespace);
13594 : : else
5426 peter_e@gmx.net 13595 :CBC 147 : nspname = NULL;
13596 : :
13597 : 147 : result = quote_qualified_identifier(nspname, collname);
13598 : :
13599 : 147 : ReleaseSysCache(tp);
13600 : :
13601 : 147 : return result;
13602 : : }
13603 : :
13604 : : /*
13605 : : * Given a C string, produce a TEXT datum.
13606 : : *
13607 : : * We assume that the input was palloc'd and may be freed.
13608 : : */
13609 : : static text *
7896 tgl@sss.pgh.pa.us 13610 : 22689 : string_to_text(char *str)
13611 : : {
13612 : : text *result;
13613 : :
6476 13614 : 22689 : result = cstring_to_text(str);
7896 13615 : 22689 : pfree(str);
13616 : 22689 : return result;
13617 : : }
13618 : :
13619 : : /*
13620 : : * Generate a C string representing a relation options from text[] datum.
13621 : : */
13622 : : static void
2088 akorotkov@postgresql 13623 : 122 : get_reloptions(StringInfo buf, Datum reloptions)
13624 : : {
13625 : : Datum *options;
13626 : : int noptions;
13627 : : int i;
13628 : :
1265 peter@eisentraut.org 13629 : 122 : deconstruct_array_builtin(DatumGetArrayTypeP(reloptions), TEXTOID,
13630 : : &options, NULL, &noptions);
13631 : :
2088 akorotkov@postgresql 13632 [ + + ]: 254 : for (i = 0; i < noptions; i++)
13633 : : {
13634 : 132 : char *option = TextDatumGetCString(options[i]);
13635 : : char *name;
13636 : : char *separator;
13637 : : char *value;
13638 : :
13639 : : /*
13640 : : * Each array element should have the form name=value. If the "=" is
13641 : : * missing for some reason, treat it like an empty value.
13642 : : */
13643 : 132 : name = option;
13644 : 132 : separator = strchr(option, '=');
13645 [ + - ]: 132 : if (separator)
13646 : : {
13647 : 132 : *separator = '\0';
13648 : 132 : value = separator + 1;
13649 : : }
13650 : : else
2088 akorotkov@postgresql 13651 :UBC 0 : value = "";
13652 : :
2088 akorotkov@postgresql 13653 [ + + ]:CBC 132 : if (i > 0)
13654 : 10 : appendStringInfoString(buf, ", ");
13655 : 132 : appendStringInfo(buf, "%s=", quote_identifier(name));
13656 : :
13657 : : /*
13658 : : * In general we need to quote the value; but to avoid unnecessary
13659 : : * clutter, do not quote if it is an identifier that would not need
13660 : : * quoting. (We could also allow numbers, but that is a bit trickier
13661 : : * than it looks --- for example, are leading zeroes significant? We
13662 : : * don't want to assume very much here about what custom reloptions
13663 : : * might mean.)
13664 : : */
13665 [ + + ]: 132 : if (quote_identifier(value) == value)
13666 : 4 : appendStringInfoString(buf, value);
13667 : : else
13668 : 128 : simple_quote_literal(buf, value);
13669 : :
13670 : 132 : pfree(option);
13671 : : }
13672 : 122 : }
13673 : :
13674 : : /*
13675 : : * Generate a C string representing a relation's reloptions, or NULL if none.
13676 : : */
13677 : : static char *
7108 bruce@momjian.us 13678 : 3898 : flatten_reloptions(Oid relid)
13679 : : {
13680 : 3898 : char *result = NULL;
13681 : : HeapTuple tuple;
13682 : : Datum reloptions;
13683 : : bool isnull;
13684 : :
5785 rhaas@postgresql.org 13685 : 3898 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
7107 tgl@sss.pgh.pa.us 13686 [ - + ]: 3898 : if (!HeapTupleIsValid(tuple))
7107 tgl@sss.pgh.pa.us 13687 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
13688 : :
7107 tgl@sss.pgh.pa.us 13689 :CBC 3898 : reloptions = SysCacheGetAttr(RELOID, tuple,
13690 : : Anum_pg_class_reloptions, &isnull);
13691 [ + + ]: 3898 : if (!isnull)
13692 : : {
13693 : : StringInfoData buf;
13694 : :
3638 13695 : 105 : initStringInfo(&buf);
2088 akorotkov@postgresql 13696 : 105 : get_reloptions(&buf, reloptions);
13697 : :
3638 tgl@sss.pgh.pa.us 13698 : 105 : result = buf.data;
13699 : : }
13700 : :
7107 13701 : 3898 : ReleaseSysCache(tuple);
13702 : :
7108 bruce@momjian.us 13703 : 3898 : return result;
13704 : : }
13705 : :
13706 : : /*
13707 : : * get_range_partbound_string
13708 : : * A C string representation of one range partition bound
13709 : : */
13710 : : char *
3051 rhaas@postgresql.org 13711 : 2708 : get_range_partbound_string(List *bound_datums)
13712 : : {
13713 : : deparse_context context;
13714 : : StringInfoData buf;
13715 : : ListCell *cell;
13716 : : char *sep;
13717 : :
41 drowley@postgresql.o 13718 :GNC 2708 : initStringInfo(&buf);
3051 rhaas@postgresql.org 13719 :CBC 2708 : memset(&context, 0, sizeof(deparse_context));
41 drowley@postgresql.o 13720 :GNC 2708 : context.buf = &buf;
13721 : :
13722 : 2708 : appendStringInfoChar(&buf, '(');
3051 rhaas@postgresql.org 13723 :CBC 2708 : sep = "";
13724 [ + - + + : 5812 : foreach(cell, bound_datums)
+ + ]
13725 : : {
13726 : : PartitionRangeDatum *datum =
943 tgl@sss.pgh.pa.us 13727 : 3104 : lfirst_node(PartitionRangeDatum, cell);
13728 : :
41 drowley@postgresql.o 13729 :GNC 3104 : appendStringInfoString(&buf, sep);
3051 rhaas@postgresql.org 13730 [ + + ]:CBC 3104 : if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
41 drowley@postgresql.o 13731 :GNC 111 : appendStringInfoString(&buf, "MINVALUE");
3051 rhaas@postgresql.org 13732 [ + + ]:CBC 2993 : else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
41 drowley@postgresql.o 13733 :GNC 60 : appendStringInfoString(&buf, "MAXVALUE");
13734 : : else
13735 : : {
3051 rhaas@postgresql.org 13736 :CBC 2933 : Const *val = castNode(Const, datum->value);
13737 : :
13738 : 2933 : get_const_expr(val, &context, -1);
13739 : : }
13740 : 3104 : sep = ", ";
13741 : : }
41 drowley@postgresql.o 13742 :GNC 2708 : appendStringInfoChar(&buf, ')');
13743 : :
13744 : 2708 : return buf.data;
13745 : : }
|