PostgreSQL Source Code git master
Loading...
Searching...
No Matches
ruleutils.c File Reference
#include "postgres.h"
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "access/table.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_language.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_propgraph_element.h"
#include "catalog/pg_propgraph_element_label.h"
#include "catalog/pg_propgraph_label.h"
#include "catalog/pg_propgraph_label_property.h"
#include "catalog/pg_propgraph_property.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
#include "common/keywords.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/pathnodes.h"
#include "optimizer/optimizer.h"
#include "parser/parse_agg.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "parser/parser.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/hsearch.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
#include "utils/rel.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
#include "utils/varlena.h"
#include "utils/xml.h"
Include dependency graph for ruleutils.c:

Go to the source code of this file.

Data Structures

struct  deparse_context
 
struct  deparse_namespace
 
struct  deparse_columns
 
struct  NameHashEntry
 
struct  oid_str_pair
 

Macros

#define PRETTYINDENT_STD   8
 
#define PRETTYINDENT_JOIN   4
 
#define PRETTYINDENT_VAR   4
 
#define PRETTYINDENT_LIMIT   40 /* wrap limit */
 
#define PRETTYFLAG_PAREN   0x0001
 
#define PRETTYFLAG_INDENT   0x0002
 
#define PRETTYFLAG_SCHEMA   0x0004
 
#define GET_PRETTY_FLAGS(pretty)
 
#define WRAP_COLUMN_DEFAULT   0
 
#define PRETTY_PAREN(context)   ((context)->prettyFlags & PRETTYFLAG_PAREN)
 
#define PRETTY_INDENT(context)   ((context)->prettyFlags & PRETTYFLAG_INDENT)
 
#define PRETTY_SCHEMA(context)   ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
 
#define deparse_columns_fetch(rangetable_index, dpns)    ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
 
#define only_marker(rte)   ((rte)->inh ? "" : "ONLY ")
 

Typedefs

typedef void(* rsv_callback) (Node *node, deparse_context *context, void *callback_arg)
 

Functions

static chardeparse_expression_pretty (Node *expr, List *dpcontext, bool forceprefix, bool showimplicit, int prettyFlags, int startIndent)
 
static charpg_get_viewdef_worker (Oid viewoid, int prettyFlags, int wrapColumn)
 
static charpg_get_triggerdef_worker (Oid trigid, bool pretty)
 
static int decompile_column_index_array (Datum column_index_array, Oid relId, bool withPeriod, StringInfo buf)
 
static charpg_get_ruledef_worker (Oid ruleoid, int prettyFlags)
 
static charpg_get_indexdef_worker (Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool keysOnly, bool showTblSpc, bool inherits, int prettyFlags, bool missing_ok)
 
static void make_propgraphdef_elements (StringInfo buf, Oid pgrelid, char pgekind)
 
static void make_propgraphdef_labels (StringInfo buf, Oid elid, const char *elalias, Oid elrelid)
 
static void make_propgraphdef_properties (StringInfo buf, Oid ellabelid, Oid elrelid)
 
static charpg_get_statisticsobj_worker (Oid statextid, bool columns_only, bool missing_ok)
 
static charpg_get_partkeydef_worker (Oid relid, int prettyFlags, bool attrsOnly, bool missing_ok)
 
static charpg_get_constraintdef_worker (Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok)
 
static textpg_get_expr_worker (text *expr, Oid relid, int prettyFlags)
 
static int print_function_arguments (StringInfo buf, HeapTuple proctup, bool print_table_args, bool print_defaults)
 
static void print_function_rettype (StringInfo buf, HeapTuple proctup)
 
static void print_function_trftypes (StringInfo buf, HeapTuple proctup)
 
static void print_function_sqlbody (StringInfo buf, HeapTuple proctup)
 
static void set_rtable_names (deparse_namespace *dpns, List *parent_namespaces, Bitmapset *rels_used)
 
static void set_deparse_for_query (deparse_namespace *dpns, Query *query, List *parent_namespaces)
 
static void set_simple_column_names (deparse_namespace *dpns)
 
static bool has_dangerous_join_using (deparse_namespace *dpns, Node *jtnode)
 
static void set_using_names (deparse_namespace *dpns, Node *jtnode, List *parentUsing)
 
static void set_relation_column_names (deparse_namespace *dpns, RangeTblEntry *rte, deparse_columns *colinfo)
 
static void set_join_column_names (deparse_namespace *dpns, RangeTblEntry *rte, deparse_columns *colinfo)
 
static bool colname_is_unique (const char *colname, deparse_namespace *dpns, deparse_columns *colinfo)
 
static charmake_colname_unique (char *colname, deparse_namespace *dpns, deparse_columns *colinfo)
 
static void expand_colnames_array_to (deparse_columns *colinfo, int n)
 
static void build_colinfo_names_hash (deparse_columns *colinfo)
 
static void add_to_names_hash (deparse_columns *colinfo, const char *name)
 
static void destroy_colinfo_names_hash (deparse_columns *colinfo)
 
static void identify_join_columns (JoinExpr *j, RangeTblEntry *jrte, deparse_columns *colinfo)
 
static charget_rtable_name (int rtindex, deparse_context *context)
 
static void set_deparse_plan (deparse_namespace *dpns, Plan *plan)
 
static Planfind_recursive_union (deparse_namespace *dpns, WorkTableScan *wtscan)
 
static void push_child_plan (deparse_namespace *dpns, Plan *plan, deparse_namespace *save_dpns)
 
static void pop_child_plan (deparse_namespace *dpns, deparse_namespace *save_dpns)
 
static void push_ancestor_plan (deparse_namespace *dpns, ListCell *ancestor_cell, deparse_namespace *save_dpns)
 
static void pop_ancestor_plan (deparse_namespace *dpns, deparse_namespace *save_dpns)
 
static void make_ruledef (StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags)
 
static void make_viewdef (StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags, int wrapColumn)
 
static void get_query_def (Query *query, StringInfo buf, List *parentnamespace, TupleDesc resultDesc, bool colNamesVisible, int prettyFlags, int wrapColumn, int startIndent)
 
static void get_values_def (List *values_lists, deparse_context *context)
 
static void get_with_clause (Query *query, deparse_context *context)
 
static void get_select_query_def (Query *query, deparse_context *context)
 
static void get_insert_query_def (Query *query, deparse_context *context)
 
static void get_update_query_def (Query *query, deparse_context *context)
 
static void get_update_query_targetlist_def (Query *query, List *targetList, deparse_context *context, RangeTblEntry *rte)
 
static void get_delete_query_def (Query *query, deparse_context *context)
 
static void get_merge_query_def (Query *query, deparse_context *context)
 
static void get_utility_query_def (Query *query, deparse_context *context)
 
static charget_lock_clause_strength (LockClauseStrength strength)
 
static void get_basic_select_query (Query *query, deparse_context *context)
 
static void get_target_list (List *targetList, deparse_context *context)
 
static void get_returning_clause (Query *query, deparse_context *context)
 
static void get_setop_query (Node *setOp, Query *query, deparse_context *context)
 
static Nodeget_rule_sortgroupclause (Index ref, List *tlist, bool force_colno, deparse_context *context)
 
static void get_rule_groupingset (GroupingSet *gset, List *targetlist, bool omit_parens, deparse_context *context)
 
static void get_rule_orderby (List *orderList, List *targetList, bool force_colno, deparse_context *context)
 
static void get_rule_windowclause (Query *query, deparse_context *context)
 
static void get_rule_windowspec (WindowClause *wc, List *targetList, deparse_context *context)
 
static void get_window_frame_options (int frameOptions, Node *startOffset, Node *endOffset, deparse_context *context)
 
static charget_variable (Var *var, int levelsup, bool istoplevel, deparse_context *context)
 
static void get_special_variable (Node *node, deparse_context *context, void *callback_arg)
 
static void resolve_special_varno (Node *node, deparse_context *context, rsv_callback callback, void *callback_arg)
 
static Nodefind_param_referent (Param *param, deparse_context *context, deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
 
static SubPlanfind_param_generator (Param *param, deparse_context *context, int *column_p)
 
static SubPlanfind_param_generator_initplan (Param *param, Plan *plan, int *column_p)
 
static void get_parameter (Param *param, deparse_context *context)
 
static const charget_simple_binary_op_name (OpExpr *expr)
 
static bool isSimpleNode (Node *node, Node *parentNode, int prettyFlags)
 
static void appendContextKeyword (deparse_context *context, const char *str, int indentBefore, int indentAfter, int indentPlus)
 
static void removeStringInfoSpaces (StringInfo str)
 
static void get_rule_expr (Node *node, deparse_context *context, bool showimplicit)
 
static void get_rule_expr_toplevel (Node *node, deparse_context *context, bool showimplicit)
 
static void get_rule_list_toplevel (List *lst, deparse_context *context, bool showimplicit)
 
static void get_rule_expr_funccall (Node *node, deparse_context *context, bool showimplicit)
 
static bool looks_like_function (Node *node)
 
static void get_oper_expr (OpExpr *expr, deparse_context *context)
 
static void get_func_expr (FuncExpr *expr, deparse_context *context, bool showimplicit)
 
static void get_agg_expr (Aggref *aggref, deparse_context *context, Aggref *original_aggref)
 
static void get_agg_expr_helper (Aggref *aggref, deparse_context *context, Aggref *original_aggref, const char *funcname, const char *options, bool is_json_objectagg)
 
static void get_agg_combine_expr (Node *node, deparse_context *context, void *callback_arg)
 
static void get_windowfunc_expr (WindowFunc *wfunc, deparse_context *context)
 
static void get_windowfunc_expr_helper (WindowFunc *wfunc, deparse_context *context, const char *funcname, const char *options, bool is_json_objectagg)
 
static bool get_func_sql_syntax (FuncExpr *expr, deparse_context *context)
 
static void get_coercion_expr (Node *arg, deparse_context *context, Oid resulttype, int32 resulttypmod, Node *parentNode)
 
static void get_const_expr (Const *constval, deparse_context *context, int showtype)
 
static void get_const_collation (Const *constval, deparse_context *context)
 
static void get_json_format (JsonFormat *format, StringInfo buf)
 
static void get_json_returning (JsonReturning *returning, StringInfo buf, bool json_format_by_default)
 
static void get_json_constructor (JsonConstructorExpr *ctor, deparse_context *context, bool showimplicit)
 
static void get_json_constructor_options (JsonConstructorExpr *ctor, StringInfo buf)
 
static void get_json_agg_constructor (JsonConstructorExpr *ctor, deparse_context *context, const char *funcname, bool is_json_objectagg)
 
static void simple_quote_literal (StringInfo buf, const char *val)
 
static void get_sublink_expr (SubLink *sublink, deparse_context *context)
 
static void get_tablefunc (TableFunc *tf, deparse_context *context, bool showimplicit)
 
static void get_from_clause (Query *query, const char *prefix, deparse_context *context)
 
static void get_from_clause_item (Node *jtnode, Query *query, deparse_context *context)
 
static void get_rte_alias (RangeTblEntry *rte, int varno, bool use_as, deparse_context *context)
 
static void get_column_alias_list (deparse_columns *colinfo, deparse_context *context)
 
static void get_for_portion_of (ForPortionOfExpr *forPortionOf, deparse_context *context)
 
static void get_from_clause_coldeflist (RangeTblFunction *rtfunc, deparse_columns *colinfo, deparse_context *context)
 
static void get_tablesample_def (TableSampleClause *tablesample, deparse_context *context)
 
static void get_opclass_name (Oid opclass, Oid actual_datatype, StringInfo buf)
 
static NodeprocessIndirection (Node *node, deparse_context *context)
 
static void printSubscripts (SubscriptingRef *sbsref, deparse_context *context)
 
static charget_relation_name (Oid relid)
 
static chargenerate_relation_name (Oid relid, List *namespaces)
 
static chargenerate_qualified_relation_name (Oid relid)
 
static chargenerate_function_name (Oid funcid, int nargs, List *argnames, Oid *argtypes, bool has_variadic, bool *use_variadic_p, bool inGroupBy)
 
static chargenerate_operator_name (Oid operid, Oid arg1, Oid arg2)
 
static void add_cast_to (StringInfo buf, Oid typid)
 
static chargenerate_qualified_type_name (Oid typid)
 
static textstring_to_text (char *str)
 
static charflatten_reloptions (Oid relid)
 
void get_reloptions (StringInfo buf, Datum reloptions)
 
static void get_json_path_spec (Node *path_spec, deparse_context *context, bool showimplicit)
 
static void get_json_table_columns (TableFunc *tf, JsonTablePathScan *scan, deparse_context *context, bool showimplicit)
 
static void get_json_table_nested_columns (TableFunc *tf, JsonTablePlan *plan, deparse_context *context, bool showimplicit, bool needcomma)
 
Datum pg_get_ruledef (PG_FUNCTION_ARGS)
 
Datum pg_get_ruledef_ext (PG_FUNCTION_ARGS)
 
Datum pg_get_viewdef (PG_FUNCTION_ARGS)
 
Datum pg_get_viewdef_ext (PG_FUNCTION_ARGS)
 
Datum pg_get_viewdef_wrap (PG_FUNCTION_ARGS)
 
Datum pg_get_viewdef_name (PG_FUNCTION_ARGS)
 
Datum pg_get_viewdef_name_ext (PG_FUNCTION_ARGS)
 
Datum pg_get_triggerdef (PG_FUNCTION_ARGS)
 
Datum pg_get_triggerdef_ext (PG_FUNCTION_ARGS)
 
Datum pg_get_indexdef (PG_FUNCTION_ARGS)
 
Datum pg_get_indexdef_ext (PG_FUNCTION_ARGS)
 
charpg_get_indexdef_string (Oid indexrelid)
 
charpg_get_indexdef_columns (Oid indexrelid, bool pretty)
 
charpg_get_indexdef_columns_extended (Oid indexrelid, uint16 flags)
 
charpg_get_querydef (Query *query, bool pretty)
 
Datum pg_get_propgraphdef (PG_FUNCTION_ARGS)
 
static int list_oid_str_pair_cmp_by_str (const ListCell *p1, const ListCell *p2)
 
static int propdata_by_name_cmp (const ListCell *a, const ListCell *b)
 
Datum pg_get_statisticsobjdef (PG_FUNCTION_ARGS)
 
charpg_get_statisticsobjdef_string (Oid statextid)
 
Datum pg_get_statisticsobjdef_columns (PG_FUNCTION_ARGS)
 
Datum pg_get_statisticsobjdef_expressions (PG_FUNCTION_ARGS)
 
Datum pg_get_partkeydef (PG_FUNCTION_ARGS)
 
charpg_get_partkeydef_columns (Oid relid, bool pretty)
 
Datum pg_get_partition_constraintdef (PG_FUNCTION_ARGS)
 
charpg_get_partconstrdef_string (Oid partitionId, char *aliasname)
 
Datum pg_get_constraintdef (PG_FUNCTION_ARGS)
 
Datum pg_get_constraintdef_ext (PG_FUNCTION_ARGS)
 
charpg_get_constraintdef_command (Oid constraintId)
 
Datum pg_get_expr (PG_FUNCTION_ARGS)
 
Datum pg_get_expr_ext (PG_FUNCTION_ARGS)
 
Datum pg_get_userbyid (PG_FUNCTION_ARGS)
 
Datum pg_get_serial_sequence (PG_FUNCTION_ARGS)
 
Datum pg_get_functiondef (PG_FUNCTION_ARGS)
 
Datum pg_get_function_arguments (PG_FUNCTION_ARGS)
 
Datum pg_get_function_identity_arguments (PG_FUNCTION_ARGS)
 
Datum pg_get_function_result (PG_FUNCTION_ARGS)
 
static bool is_input_argument (int nth, const char *argmodes)
 
Datum pg_get_function_arg_default (PG_FUNCTION_ARGS)
 
Datum pg_get_function_sqlbody (PG_FUNCTION_ARGS)
 
chardeparse_expression (Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
 
Listdeparse_context_for (const char *aliasname, Oid relid)
 
Listdeparse_context_for_plan_tree (PlannedStmt *pstmt, List *rtable_names)
 
Listset_deparse_context_plan (List *dpcontext, Plan *plan, List *ancestors)
 
Listselect_rtable_names_for_explain (List *rtable, Bitmapset *rels_used)
 
static RangeTblEntryget_simple_values_rte (Query *query, TupleDesc resultDesc)
 
charget_window_frame_options_for_explain (int frameOptions, Node *startOffset, Node *endOffset, List *dpcontext, bool forceprefix)
 
static void get_graph_label_expr (Node *label_expr, deparse_context *context)
 
static void get_path_pattern_expr_def (List *path_pattern_expr, deparse_context *context)
 
static void get_graph_pattern_def (GraphPattern *graph_pattern, deparse_context *context)
 
static const charget_name_for_var_field (Var *var, int fieldno, int levelsup, deparse_context *context)
 
static void get_rule_expr_paren (Node *node, deparse_context *context, bool showimplicit, Node *parentNode)
 
static void get_json_behavior (JsonBehavior *behavior, deparse_context *context, const char *on)
 
static void get_json_expr_options (JsonExpr *jsexpr, deparse_context *context, JsonBehaviorType default_behavior)
 
static void get_xmltable (TableFunc *tf, deparse_context *context, bool showimplicit)
 
static void get_json_table (TableFunc *tf, deparse_context *context, bool showimplicit)
 
chargenerate_opclass_name (Oid opclass)
 
const charquote_identifier (const char *ident)
 
charquote_qualified_identifier (const char *qualifier, const char *ident)
 
void generate_operator_clause (StringInfo buf, const char *leftop, Oid leftoptype, Oid opoid, const char *rightop, Oid rightoptype)
 
chargenerate_collation_name (Oid collid)
 
charget_range_partbound_string (List *bound_datums)
 

Variables

static SPIPlanPtr plan_getrulebyoid = NULL
 
static const char *const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1"
 
static SPIPlanPtr plan_getviewrule = NULL
 
static const char *const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2"
 
bool quote_all_identifiers = false
 

Macro Definition Documentation

◆ deparse_columns_fetch

#define deparse_columns_fetch (   rangetable_index,
  dpns 
)     ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))

Definition at line 317 of file ruleutils.c.

323{
324 char name[NAMEDATALEN]; /* Hash key --- must be first */
325 int counter; /* Largest addition used so far for name */
327
328/* Callback signature for resolve_special_varno() */
329typedef void (*rsv_callback) (Node *node, deparse_context *context,
330 void *callback_arg);
331
332
333/* ----------
334 * Global data
335 * ----------
336 */
338static const char *const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
340static const char *const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
341
342/* GUC parameters */
343bool quote_all_identifiers = false;
344
345
346/* ----------
347 * Local functions
348 *
349 * Most of these functions used to use fixed-size buffers to build their
350 * results. Now, they take an (already initialized) StringInfo object
351 * as a parameter, and append their text output to its contents.
352 * ----------
353 */
354static char *deparse_expression_pretty(Node *expr, List *dpcontext,
355 bool forceprefix, bool showimplicit,
356 int prettyFlags, int startIndent);
357static char *pg_get_viewdef_worker(Oid viewoid,
358 int prettyFlags, int wrapColumn);
359static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
362static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
363static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
364 const Oid *excludeOps,
365 bool attrsOnly, bool keysOnly,
366 bool showTblSpc, bool inherits,
367 int prettyFlags, bool missing_ok);
372 bool missing_ok);
373static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
374 bool attrsOnly, bool missing_ok);
376 int prettyFlags, bool missing_ok);
377static text *pg_get_expr_worker(text *expr, Oid relid, int prettyFlags);
389static void set_using_names(deparse_namespace *dpns, Node *jtnode,
390 List *parentUsing);
396static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
398static char *make_colname_unique(char *colname, deparse_namespace *dpns,
402static void add_to_names_hash(deparse_columns *colinfo, const char *name);
406static char *get_rtable_name(int rtindex, deparse_context *context);
419 int prettyFlags);
421 int prettyFlags, int wrapColumn);
423 TupleDesc resultDesc, bool colNamesVisible,
424 int prettyFlags, int wrapColumn, int startIndent);
425static void get_values_def(List *values_lists, deparse_context *context);
426static void get_with_clause(Query *query, deparse_context *context);
427static void get_select_query_def(Query *query, deparse_context *context);
428static void get_insert_query_def(Query *query, deparse_context *context);
429static void get_update_query_def(Query *query, deparse_context *context);
430static void get_update_query_targetlist_def(Query *query, List *targetList,
431 deparse_context *context,
433static void get_delete_query_def(Query *query, deparse_context *context);
434static void get_merge_query_def(Query *query, deparse_context *context);
435static void get_utility_query_def(Query *query, deparse_context *context);
436static char *get_lock_clause_strength(LockClauseStrength strength);
437static void get_basic_select_query(Query *query, deparse_context *context);
438static void get_target_list(List *targetList, deparse_context *context);
439static void get_returning_clause(Query *query, deparse_context *context);
440static void get_setop_query(Node *setOp, Query *query,
441 deparse_context *context);
443 bool force_colno,
444 deparse_context *context);
445static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
446 bool omit_parens, deparse_context *context);
447static void get_rule_orderby(List *orderList, List *targetList,
448 bool force_colno, deparse_context *context);
449static void get_rule_windowclause(Query *query, deparse_context *context);
450static void get_rule_windowspec(WindowClause *wc, List *targetList,
451 deparse_context *context);
452static void get_window_frame_options(int frameOptions,
453 Node *startOffset, Node *endOffset,
454 deparse_context *context);
455static char *get_variable(Var *var, int levelsup, bool istoplevel,
456 deparse_context *context);
457static void get_special_variable(Node *node, deparse_context *context,
458 void *callback_arg);
459static void resolve_special_varno(Node *node, deparse_context *context,
460 rsv_callback callback, void *callback_arg);
461static Node *find_param_referent(Param *param, deparse_context *context,
463static SubPlan *find_param_generator(Param *param, deparse_context *context,
464 int *column_p);
466 int *column_p);
467static void get_parameter(Param *param, deparse_context *context);
468static const char *get_simple_binary_op_name(OpExpr *expr);
469static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
470static void appendContextKeyword(deparse_context *context, const char *str,
471 int indentBefore, int indentAfter, int indentPlus);
473static void get_rule_expr(Node *node, deparse_context *context,
474 bool showimplicit);
475static void get_rule_expr_toplevel(Node *node, deparse_context *context,
476 bool showimplicit);
477static void get_rule_list_toplevel(List *lst, deparse_context *context,
478 bool showimplicit);
479static void get_rule_expr_funccall(Node *node, deparse_context *context,
480 bool showimplicit);
481static bool looks_like_function(Node *node);
482static void get_oper_expr(OpExpr *expr, deparse_context *context);
483static void get_func_expr(FuncExpr *expr, deparse_context *context,
484 bool showimplicit);
485static void get_agg_expr(Aggref *aggref, deparse_context *context,
487static void get_agg_expr_helper(Aggref *aggref, deparse_context *context,
488 Aggref *original_aggref, const char *funcname,
489 const char *options, bool is_json_objectagg);
490static void get_agg_combine_expr(Node *node, deparse_context *context,
491 void *callback_arg);
492static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
493static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
494 const char *funcname, const char *options,
495 bool is_json_objectagg);
496static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
497static void get_coercion_expr(Node *arg, deparse_context *context,
498 Oid resulttype, int32 resulttypmod,
500static void get_const_expr(Const *constval, deparse_context *context,
501 int showtype);
502static void get_const_collation(Const *constval, deparse_context *context);
504static void get_json_returning(JsonReturning *returning, StringInfo buf,
507 deparse_context *context, bool showimplicit);
511 deparse_context *context,
512 const char *funcname,
513 bool is_json_objectagg);
514static void simple_quote_literal(StringInfo buf, const char *val);
515static void get_sublink_expr(SubLink *sublink, deparse_context *context);
516static void get_tablefunc(TableFunc *tf, deparse_context *context,
517 bool showimplicit);
518static void get_from_clause(Query *query, const char *prefix,
519 deparse_context *context);
520static void get_from_clause_item(Node *jtnode, Query *query,
521 deparse_context *context);
522static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
523 deparse_context *context);
525 deparse_context *context);
526static void get_for_portion_of(ForPortionOfExpr *forPortionOf,
527 deparse_context *context);
530 deparse_context *context);
531static void get_tablesample_def(TableSampleClause *tablesample,
532 deparse_context *context);
533static void get_opclass_name(Oid opclass, Oid actual_datatype,
535static Node *processIndirection(Node *node, deparse_context *context);
536static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
537static char *get_relation_name(Oid relid);
538static char *generate_relation_name(Oid relid, List *namespaces);
539static char *generate_qualified_relation_name(Oid relid);
540static char *generate_function_name(Oid funcid, int nargs,
541 List *argnames, Oid *argtypes,
542 bool has_variadic, bool *use_variadic_p,
543 bool inGroupBy);
545static void add_cast_to(StringInfo buf, Oid typid);
546static char *generate_qualified_type_name(Oid typid);
547static text *string_to_text(char *str);
548static char *flatten_reloptions(Oid relid);
549void get_reloptions(StringInfo buf, Datum reloptions);
550static void get_json_path_spec(Node *path_spec, deparse_context *context,
551 bool showimplicit);
553 deparse_context *context,
554 bool showimplicit);
556 deparse_context *context,
557 bool showimplicit,
558 bool needcomma);
559
560#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
561
562
563/* ----------
564 * pg_get_ruledef - Do it all and return a text
565 * that could be used as a statement
566 * to recreate the rule
567 * ----------
568 */
569Datum
571{
573 int prettyFlags;
574 char *res;
575
576 prettyFlags = PRETTYFLAG_INDENT;
577
578 res = pg_get_ruledef_worker(ruleoid, prettyFlags);
579
580 if (res == NULL)
582
584}
585
586
587Datum
589{
591 bool pretty = PG_GETARG_BOOL(1);
592 int prettyFlags;
593 char *res;
594
595 prettyFlags = GET_PRETTY_FLAGS(pretty);
596
597 res = pg_get_ruledef_worker(ruleoid, prettyFlags);
598
599 if (res == NULL)
601
603}
604
605
606static char *
607pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
608{
609 Datum args[1];
610 char nulls[1];
611 int spirc;
615
616 /*
617 * Do this first so that string is alloc'd in outer context not SPI's.
618 */
620
621 /*
622 * Connect to SPI manager
623 */
624 SPI_connect();
625
626 /*
627 * On the first call prepare the plan to lookup pg_rewrite. We read
628 * pg_rewrite over the SPI manager instead of using the syscache to be
629 * checked for read access on pg_rewrite.
630 */
631 if (plan_getrulebyoid == NULL)
632 {
633 Oid argtypes[1];
635
636 argtypes[0] = OIDOID;
637 plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
638 if (plan == NULL)
639 elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
642 }
643
644 /*
645 * Get the pg_rewrite tuple for this rule
646 */
648 nulls[0] = ' ';
649 spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
650 if (spirc != SPI_OK_SELECT)
651 elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
652 if (SPI_processed != 1)
653 {
654 /*
655 * There is no tuple data available here, just keep the output buffer
656 * empty.
657 */
658 }
659 else
660 {
661 /*
662 * Get the rule's definition and put it into executor's memory
663 */
666 make_ruledef(&buf, ruletup, rulettc, prettyFlags);
667 }
668
669 /*
670 * Disconnect from SPI manager
671 */
672 if (SPI_finish() != SPI_OK_FINISH)
673 elog(ERROR, "SPI_finish failed");
674
675 if (buf.len == 0)
676 return NULL;
677
678 return buf.data;
679}
680
681
682/* ----------
683 * pg_get_viewdef - Mainly the same thing, but we
684 * only return the SELECT part of a view
685 * ----------
686 */
687Datum
689{
690 /* By OID */
691 Oid viewoid = PG_GETARG_OID(0);
692 int prettyFlags;
693 char *res;
694
695 prettyFlags = PRETTYFLAG_INDENT;
696
697 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
698
699 if (res == NULL)
701
703}
704
705
706Datum
708{
709 /* By OID */
710 Oid viewoid = PG_GETARG_OID(0);
711 bool pretty = PG_GETARG_BOOL(1);
712 int prettyFlags;
713 char *res;
714
715 prettyFlags = GET_PRETTY_FLAGS(pretty);
716
717 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
718
719 if (res == NULL)
721
723}
724
725Datum
727{
728 /* By OID */
729 Oid viewoid = PG_GETARG_OID(0);
730 int wrap = PG_GETARG_INT32(1);
731 int prettyFlags;
732 char *res;
733
734 /* calling this implies we want pretty printing */
735 prettyFlags = GET_PRETTY_FLAGS(true);
736
737 res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
738
739 if (res == NULL)
741
743}
744
745Datum
747{
748 /* By qualified name */
750 int prettyFlags;
752 Oid viewoid;
753 char *res;
754
755 prettyFlags = PRETTYFLAG_INDENT;
756
757 /* Look up view name. Can't lock it - we might not have privileges. */
759 viewoid = RangeVarGetRelid(viewrel, NoLock, false);
760
761 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
762
763 if (res == NULL)
765
767}
768
769
770Datum
772{
773 /* By qualified name */
775 bool pretty = PG_GETARG_BOOL(1);
776 int prettyFlags;
778 Oid viewoid;
779 char *res;
780
781 prettyFlags = GET_PRETTY_FLAGS(pretty);
782
783 /* Look up view name. Can't lock it - we might not have privileges. */
785 viewoid = RangeVarGetRelid(viewrel, NoLock, false);
786
787 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
788
789 if (res == NULL)
791
793}
794
795/*
796 * Common code for by-OID and by-name variants of pg_get_viewdef
797 */
798static char *
799pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
800{
801 Datum args[2];
802 char nulls[2];
803 int spirc;
807
808 /*
809 * Do this first so that string is alloc'd in outer context not SPI's.
810 */
812
813 /*
814 * Connect to SPI manager
815 */
816 SPI_connect();
817
818 /*
819 * On the first call prepare the plan to lookup pg_rewrite. We read
820 * pg_rewrite over the SPI manager instead of using the syscache to be
821 * checked for read access on pg_rewrite.
822 */
823 if (plan_getviewrule == NULL)
824 {
825 Oid argtypes[2];
827
828 argtypes[0] = OIDOID;
829 argtypes[1] = NAMEOID;
830 plan = SPI_prepare(query_getviewrule, 2, argtypes);
831 if (plan == NULL)
832 elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
835 }
836
837 /*
838 * Get the pg_rewrite tuple for the view's SELECT rule
839 */
840 args[0] = ObjectIdGetDatum(viewoid);
842 nulls[0] = ' ';
843 nulls[1] = ' ';
844 spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
845 if (spirc != SPI_OK_SELECT)
846 elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
847 if (SPI_processed != 1)
848 {
849 /*
850 * There is no tuple data available here, just keep the output buffer
851 * empty.
852 */
853 }
854 else
855 {
856 /*
857 * Get the rule's definition and put it into executor's memory
858 */
861 make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
862 }
863
864 /*
865 * Disconnect from SPI manager
866 */
867 if (SPI_finish() != SPI_OK_FINISH)
868 elog(ERROR, "SPI_finish failed");
869
870 if (buf.len == 0)
871 return NULL;
872
873 return buf.data;
874}
875
876/* ----------
877 * pg_get_triggerdef - Get the definition of a trigger
878 * ----------
879 */
880Datum
882{
884 char *res;
885
886 res = pg_get_triggerdef_worker(trigid, false);
887
888 if (res == NULL)
890
892}
893
894Datum
896{
898 bool pretty = PG_GETARG_BOOL(1);
899 char *res;
900
902
903 if (res == NULL)
905
907}
908
909static char *
911{
916 ScanKeyData skey[1];
918 int findx = 0;
919 char *tgname;
920 char *tgoldtable;
921 char *tgnewtable;
922 Datum value;
923 bool isnull;
924
925 /*
926 * Fetch the pg_trigger tuple by the Oid of the trigger
927 */
929
930 ScanKeyInit(&skey[0],
934
936 NULL, 1, skey);
937
939
941 {
944 return NULL;
945 }
946
948
949 /*
950 * Start the trigger definition. Note that the trigger's name should never
951 * be schema-qualified, but the trigger rel's name may be.
952 */
954
955 tgname = NameStr(trigrec->tgname);
956 appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
957 OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
958 quote_identifier(tgname));
959
960 if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
961 appendStringInfoString(&buf, "BEFORE");
962 else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
963 appendStringInfoString(&buf, "AFTER");
964 else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
965 appendStringInfoString(&buf, "INSTEAD OF");
966 else
967 elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
968
969 if (TRIGGER_FOR_INSERT(trigrec->tgtype))
970 {
971 appendStringInfoString(&buf, " INSERT");
972 findx++;
973 }
974 if (TRIGGER_FOR_DELETE(trigrec->tgtype))
975 {
976 if (findx > 0)
977 appendStringInfoString(&buf, " OR DELETE");
978 else
979 appendStringInfoString(&buf, " DELETE");
980 findx++;
981 }
982 if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
983 {
984 if (findx > 0)
985 appendStringInfoString(&buf, " OR UPDATE");
986 else
987 appendStringInfoString(&buf, " UPDATE");
988 findx++;
989 /* tgattr is first var-width field, so OK to access directly */
990 if (trigrec->tgattr.dim1 > 0)
991 {
992 int i;
993
994 appendStringInfoString(&buf, " OF ");
995 for (i = 0; i < trigrec->tgattr.dim1; i++)
996 {
997 char *attname;
998
999 if (i > 0)
1001 attname = get_attname(trigrec->tgrelid,
1002 trigrec->tgattr.values[i], false);
1004 }
1005 }
1006 }
1007 if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
1008 {
1009 if (findx > 0)
1010 appendStringInfoString(&buf, " OR TRUNCATE");
1011 else
1012 appendStringInfoString(&buf, " TRUNCATE");
1013 findx++;
1014 }
1015
1016 /*
1017 * In non-pretty mode, always schema-qualify the target table name for
1018 * safety. In pretty mode, schema-qualify only if not visible.
1019 */
1020 appendStringInfo(&buf, " ON %s ",
1021 pretty ?
1024
1025 if (OidIsValid(trigrec->tgconstraint))
1026 {
1027 if (OidIsValid(trigrec->tgconstrrelid))
1028 appendStringInfo(&buf, "FROM %s ",
1029 generate_relation_name(trigrec->tgconstrrelid, NIL));
1030 if (!trigrec->tgdeferrable)
1031 appendStringInfoString(&buf, "NOT ");
1032 appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
1033 if (trigrec->tginitdeferred)
1034 appendStringInfoString(&buf, "DEFERRED ");
1035 else
1036 appendStringInfoString(&buf, "IMMEDIATE ");
1037 }
1038
1040 tgrel->rd_att, &isnull);
1041 if (!isnull)
1042 tgoldtable = NameStr(*DatumGetName(value));
1043 else
1044 tgoldtable = NULL;
1046 tgrel->rd_att, &isnull);
1047 if (!isnull)
1048 tgnewtable = NameStr(*DatumGetName(value));
1049 else
1050 tgnewtable = NULL;
1051 if (tgoldtable != NULL || tgnewtable != NULL)
1052 {
1053 appendStringInfoString(&buf, "REFERENCING ");
1054 if (tgoldtable != NULL)
1055 appendStringInfo(&buf, "OLD TABLE AS %s ",
1056 quote_identifier(tgoldtable));
1057 if (tgnewtable != NULL)
1058 appendStringInfo(&buf, "NEW TABLE AS %s ",
1059 quote_identifier(tgnewtable));
1060 }
1061
1062 if (TRIGGER_FOR_ROW(trigrec->tgtype))
1063 appendStringInfoString(&buf, "FOR EACH ROW ");
1064 else
1065 appendStringInfoString(&buf, "FOR EACH STATEMENT ");
1066
1067 /* If the trigger has a WHEN qualification, add that */
1069 tgrel->rd_att, &isnull);
1070 if (!isnull)
1071 {
1072 Node *qual;
1073 char relkind;
1074 deparse_context context;
1078
1079 appendStringInfoString(&buf, "WHEN (");
1080
1082
1083 relkind = get_rel_relkind(trigrec->tgrelid);
1084
1085 /* Build minimal OLD and NEW RTEs for the rel */
1087 oldrte->rtekind = RTE_RELATION;
1088 oldrte->relid = trigrec->tgrelid;
1089 oldrte->relkind = relkind;
1090 oldrte->rellockmode = AccessShareLock;
1091 oldrte->alias = makeAlias("old", NIL);
1092 oldrte->eref = oldrte->alias;
1093 oldrte->lateral = false;
1094 oldrte->inh = false;
1095 oldrte->inFromCl = true;
1096
1098 newrte->rtekind = RTE_RELATION;
1099 newrte->relid = trigrec->tgrelid;
1100 newrte->relkind = relkind;
1101 newrte->rellockmode = AccessShareLock;
1102 newrte->alias = makeAlias("new", NIL);
1103 newrte->eref = newrte->alias;
1104 newrte->lateral = false;
1105 newrte->inh = false;
1106 newrte->inFromCl = true;
1107
1108 /* Build two-element rtable */
1109 memset(&dpns, 0, sizeof(dpns));
1110 dpns.rtable = list_make2(oldrte, newrte);
1111 dpns.subplans = NIL;
1112 dpns.ctes = NIL;
1113 dpns.appendrels = NULL;
1116
1117 /* Set up context with one-deep namespace stack */
1118 context.buf = &buf;
1119 context.namespaces = list_make1(&dpns);
1120 context.resultDesc = NULL;
1121 context.targetList = NIL;
1122 context.windowClause = NIL;
1123 context.varprefix = true;
1126 context.indentLevel = PRETTYINDENT_STD;
1127 context.colNamesVisible = true;
1128 context.inGroupBy = false;
1129 context.varInOrderBy = false;
1130 context.appendparents = NULL;
1131
1132 get_rule_expr(qual, &context, false);
1133
1135 }
1136
1137 appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
1139 NIL, NULL,
1140 false, NULL, false));
1141
1142 if (trigrec->tgnargs > 0)
1143 {
1144 char *p;
1145 int i;
1146
1148 tgrel->rd_att, &isnull);
1149 if (isnull)
1150 elog(ERROR, "tgargs is null for trigger %u", trigid);
1151 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
1152 for (i = 0; i < trigrec->tgnargs; i++)
1153 {
1154 if (i > 0)
1157 /* advance p to next string embedded in tgargs */
1158 while (*p)
1159 p++;
1160 p++;
1161 }
1162 }
1163
1164 /* We deliberately do not put semi-colon at end */
1166
1167 /* Clean up */
1169
1171
1172 return buf.data;
1173}
1174
1175/* ----------
1176 * pg_get_indexdef - Get the definition of an index
1177 *
1178 * In the extended version, there is a colno argument as well as pretty bool.
1179 * if colno == 0, we want a complete index definition.
1180 * if colno > 0, we only want the Nth index key's variable or expression.
1181 *
1182 * Note that the SQL-function versions of this omit any info about the
1183 * index tablespace; this is intentional because pg_dump wants it that way.
1184 * However pg_get_indexdef_string() includes the index tablespace.
1185 * ----------
1186 */
1187Datum
1189{
1190 Oid indexrelid = PG_GETARG_OID(0);
1191 int prettyFlags;
1192 char *res;
1193
1194 prettyFlags = PRETTYFLAG_INDENT;
1195
1196 res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1197 false, false,
1198 false, false,
1199 prettyFlags, true);
1200
1201 if (res == NULL)
1203
1205}
1206
1207Datum
1209{
1210 Oid indexrelid = PG_GETARG_OID(0);
1211 int32 colno = PG_GETARG_INT32(1);
1212 bool pretty = PG_GETARG_BOOL(2);
1213 int prettyFlags;
1214 char *res;
1215
1216 prettyFlags = GET_PRETTY_FLAGS(pretty);
1217
1218 res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1219 colno != 0, false,
1220 false, false,
1221 prettyFlags, true);
1222
1223 if (res == NULL)
1225
1227}
1228
1229/*
1230 * Internal version for use by ALTER TABLE.
1231 * Includes a tablespace clause in the result.
1232 * Returns a palloc'd C string; no pretty-printing.
1233 */
1234char *
1235pg_get_indexdef_string(Oid indexrelid)
1236{
1237 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1238 false, false,
1239 true, true,
1240 0, false);
1241}
1242
1243/* Internal version that just reports the key-column definitions */
1244char *
1245pg_get_indexdef_columns(Oid indexrelid, bool pretty)
1246{
1247 int prettyFlags;
1248
1249 prettyFlags = GET_PRETTY_FLAGS(pretty);
1250
1251 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1252 true, true,
1253 false, false,
1254 prettyFlags, false);
1255}
1256
1257/* Internal version, extensible with flags to control its behavior */
1258char *
1260{
1261 bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1262 bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1263 int prettyFlags;
1264
1265 prettyFlags = GET_PRETTY_FLAGS(pretty);
1266
1267 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1268 true, keys_only,
1269 false, false,
1270 prettyFlags, false);
1271}
1272
1273/*
1274 * Internal workhorse to decompile an index definition.
1275 *
1276 * This is now used for exclusion constraints as well: if excludeOps is not
1277 * NULL then it points to an array of exclusion operator OIDs.
1278 */
1279static char *
1280pg_get_indexdef_worker(Oid indexrelid, int colno,
1281 const Oid *excludeOps,
1282 bool attrsOnly, bool keysOnly,
1283 bool showTblSpc, bool inherits,
1284 int prettyFlags, bool missing_ok)
1285{
1286 /* might want a separate isConstraint parameter later */
1287 bool isConstraint = (excludeOps != NULL);
1295 List *indexprs;
1297 List *context;
1298 Oid indrelid;
1299 int keyno;
1307 char *str;
1308 char *sep;
1309
1310 /*
1311 * Fetch the pg_index tuple by the Oid of the index
1312 */
1315 {
1316 if (missing_ok)
1317 return NULL;
1318 elog(ERROR, "cache lookup failed for index %u", indexrelid);
1319 }
1321
1322 indrelid = idxrec->indrelid;
1323 Assert(indexrelid == idxrec->indexrelid);
1324
1325 /* Must get indcollation, indclass, and indoption the hard way */
1329
1333
1337
1338 /*
1339 * Fetch the pg_class tuple of the index relation
1340 */
1343 elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1345
1346 /*
1347 * Fetch the pg_am tuple of the index' access method
1348 */
1350 if (!HeapTupleIsValid(ht_am))
1351 elog(ERROR, "cache lookup failed for access method %u",
1352 idxrelrec->relam);
1354
1355 /* Fetch the index AM's API struct */
1356 amroutine = GetIndexAmRoutine(amrec->amhandler);
1357
1358 /*
1359 * Get the index expressions, if any. (NOTE: we do not use the relcache
1360 * versions of the expressions and predicate, because we want to display
1361 * non-const-folded expressions.)
1362 */
1364 {
1366 char *exprsString;
1367
1373 }
1374 else
1375 indexprs = NIL;
1376
1378
1380
1381 /*
1382 * Start the index definition. Note that the index's name should never be
1383 * schema-qualified, but the indexed rel's name may be.
1384 */
1386
1387 if (!attrsOnly)
1388 {
1389 if (!isConstraint)
1390 appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
1391 idxrec->indisunique ? "UNIQUE " : "",
1394 && !inherits ? "ONLY " : "",
1395 (prettyFlags & PRETTYFLAG_SCHEMA) ?
1398 quote_identifier(NameStr(amrec->amname)));
1399 else /* currently, must be EXCLUDE constraint */
1400 appendStringInfo(&buf, "EXCLUDE USING %s (",
1401 quote_identifier(NameStr(amrec->amname)));
1402 }
1403
1404 /*
1405 * Report the indexed attributes
1406 */
1407 sep = "";
1408 for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1409 {
1410 AttrNumber attnum = idxrec->indkey.values[keyno];
1413
1414 /*
1415 * Ignore non-key attributes if told to.
1416 */
1417 if (keysOnly && keyno >= idxrec->indnkeyatts)
1418 break;
1419
1420 /* Otherwise, print INCLUDE to divide key and non-key attrs. */
1421 if (!colno && keyno == idxrec->indnkeyatts)
1422 {
1423 appendStringInfoString(&buf, ") INCLUDE (");
1424 sep = "";
1425 }
1426
1427 if (!colno)
1429 sep = ", ";
1430
1431 if (attnum != 0)
1432 {
1433 /* Simple index column */
1434 char *attname;
1436
1438 if (!colno || colno == keyno + 1)
1443 }
1444 else
1445 {
1446 /* expressional index */
1447 Node *indexkey;
1448
1449 if (indexpr_item == NULL)
1450 elog(ERROR, "too few entries in indexprs list");
1453 /* Deparse */
1454 str = deparse_expression_pretty(indexkey, context, false, false,
1455 prettyFlags, 0);
1456 if (!colno || colno == keyno + 1)
1457 {
1458 /* Need parens if it's not a bare function call */
1461 else
1462 appendStringInfo(&buf, "(%s)", str);
1463 }
1466 }
1467
1468 /* Print additional decoration for (selected) key columns */
1470 (!colno || colno == keyno + 1))
1471 {
1472 int16 opt = indoption->values[keyno];
1473 Oid indcoll = indcollation->values[keyno];
1474 Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1475 bool has_options = attoptions != (Datum) 0;
1476
1477 /* Add collation, if not default for column */
1479 appendStringInfo(&buf, " COLLATE %s",
1481
1482 /* Add the operator class name, if not default */
1483 get_opclass_name(indclass->values[keyno],
1485
1486 if (has_options)
1487 {
1489 get_reloptions(&buf, attoptions);
1491 }
1492
1493 /* Add options if relevant */
1494 if (amroutine->amcanorder)
1495 {
1496 /* if it supports sort ordering, report DESC and NULLS opts */
1497 if (opt & INDOPTION_DESC)
1498 {
1499 appendStringInfoString(&buf, " DESC");
1500 /* NULLS FIRST is the default in this case */
1501 if (!(opt & INDOPTION_NULLS_FIRST))
1502 appendStringInfoString(&buf, " NULLS LAST");
1503 }
1504 else
1505 {
1506 if (opt & INDOPTION_NULLS_FIRST)
1507 appendStringInfoString(&buf, " NULLS FIRST");
1508 }
1509 }
1510
1511 /* Add the exclusion operator if relevant */
1512 if (excludeOps != NULL)
1513 appendStringInfo(&buf, " WITH %s",
1515 keycoltype,
1516 keycoltype));
1517 }
1518 }
1519
1520 if (!attrsOnly)
1521 {
1523
1524 if (idxrec->indnullsnotdistinct)
1525 appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1526
1527 /*
1528 * If it has options, append "WITH (options)"
1529 */
1530 str = flatten_reloptions(indexrelid);
1531 if (str)
1532 {
1533 appendStringInfo(&buf, " WITH (%s)", str);
1534 pfree(str);
1535 }
1536
1537 /*
1538 * Print tablespace, but only if requested
1539 */
1540 if (showTblSpc)
1541 {
1542 Oid tblspc;
1543
1544 tblspc = get_rel_tablespace(indexrelid);
1545 if (OidIsValid(tblspc))
1546 {
1547 if (isConstraint)
1548 appendStringInfoString(&buf, " USING INDEX");
1549 appendStringInfo(&buf, " TABLESPACE %s",
1551 }
1552 }
1553
1554 /*
1555 * If it's a partial index, decompile and append the predicate
1556 */
1558 {
1559 Node *node;
1561 char *predString;
1562
1563 /* Convert text string to node tree */
1567 node = (Node *) stringToNode(predString);
1569
1570 /* Deparse */
1571 str = deparse_expression_pretty(node, context, false, false,
1572 prettyFlags, 0);
1573 if (isConstraint)
1574 appendStringInfo(&buf, " WHERE (%s)", str);
1575 else
1576 appendStringInfo(&buf, " WHERE %s", str);
1577 }
1578 }
1579
1580 /* Clean up */
1584
1585 return buf.data;
1586}
1587
1588/* ----------
1589 * pg_get_querydef
1590 *
1591 * Public entry point to deparse one query parsetree.
1592 * The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
1593 *
1594 * The result is a palloc'd C string.
1595 * ----------
1596 */
1597char *
1598pg_get_querydef(Query *query, bool pretty)
1599{
1601 int prettyFlags;
1602
1603 prettyFlags = GET_PRETTY_FLAGS(pretty);
1604
1606
1607 get_query_def(query, &buf, NIL, NULL, true,
1608 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1609
1610 return buf.data;
1611}
1612
1613/*
1614 * pg_get_propgraphdef - get the definition of a property graph
1615 */
1616Datum
1618{
1623 char *name;
1624 char *nsp;
1625
1627
1631
1633 name = NameStr(classform->relname);
1634
1635 if (classform->relkind != RELKIND_PROPGRAPH)
1636 ereport(ERROR,
1638 errmsg("\"%s\" is not a property graph", name)));
1639
1640 nsp = get_namespace_name(classform->relnamespace);
1641
1642 appendStringInfo(&buf, "CREATE PROPERTY GRAPH %s",
1644
1646
1649
1651}
1652
1653/*
1654 * Generates a VERTEX TABLES (...) or EDGE TABLES (...) clause. Pass in the
1655 * property graph relation OID and the element kind (vertex or edge). Result
1656 * is appended to buf.
1657 */
1658static void
1660{
1663 SysScanDesc scan;
1664 bool first;
1665 HeapTuple tup;
1666
1668
1669 ScanKeyInit(&scankey[0],
1673
1675
1676 first = true;
1677 while ((tup = systable_getnext(scan)))
1678 {
1680 char *relname;
1681 Datum datum;
1682 bool isnull;
1683
1684 if (pgeform->pgekind != pgekind)
1685 continue;
1686
1687 if (first)
1688 {
1689 appendStringInfo(buf, "\n %s TABLES (\n", pgekind == PGEKIND_VERTEX ? "VERTEX" : "EDGE");
1690 first = false;
1691 }
1692 else
1694
1695 relname = get_rel_name(pgeform->pgerelid);
1696 if (relname && strcmp(relname, NameStr(pgeform->pgealias)) == 0)
1697 appendStringInfo(buf, " %s",
1698 generate_relation_name(pgeform->pgerelid, NIL));
1699 else
1700 appendStringInfo(buf, " %s AS %s",
1702 quote_identifier(NameStr(pgeform->pgealias)));
1703
1705 if (!isnull)
1706 {
1707 appendStringInfoString(buf, " KEY (");
1708 decompile_column_index_array(datum, pgeform->pgerelid, false, buf);
1710 }
1711 else
1712 elog(ERROR, "null pgekey for element %u", pgeform->oid);
1713
1714 if (pgekind == PGEKIND_EDGE)
1715 {
1716 Datum srckey;
1717 Datum srcref;
1718 Datum destkey;
1719 Datum destref;
1722
1724 srckey = isnull ? 0 : datum;
1726 srcref = isnull ? 0 : datum;
1728 destkey = isnull ? 0 : datum;
1730 destref = isnull ? 0 : datum;
1731
1732 appendStringInfoString(buf, " SOURCE");
1734 if (!tup2)
1735 elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgesrcvertexid);
1737 if (srckey)
1738 {
1739 appendStringInfoString(buf, " KEY (");
1740 decompile_column_index_array(srckey, pgeform->pgerelid, false, buf);
1741 appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1742 decompile_column_index_array(srcref, pgeform2->pgerelid, false, buf);
1744 }
1745 else
1748
1749 appendStringInfoString(buf, " DESTINATION");
1751 if (!tup2)
1752 elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgedestvertexid);
1754 if (destkey)
1755 {
1756 appendStringInfoString(buf, " KEY (");
1757 decompile_column_index_array(destkey, pgeform->pgerelid, false, buf);
1758 appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1759 decompile_column_index_array(destref, pgeform2->pgerelid, false, buf);
1761 }
1762 else
1765 }
1766
1767 make_propgraphdef_labels(buf, pgeform->oid, NameStr(pgeform->pgealias), pgeform->pgerelid);
1768 }
1769 if (!first)
1770 appendStringInfoString(buf, "\n )");
1771
1772 systable_endscan(scan);
1774}
1775
1776struct oid_str_pair
1777{
1778 Oid oid;
1779 char *str;
1780};
1781
1782static int
1784{
1785 struct oid_str_pair *v1 = lfirst(p1);
1786 struct oid_str_pair *v2 = lfirst(p2);
1787
1788 return strcmp(v1->str, v2->str);
1789}
1790
1791/*
1792 * Generates label and properties list. Pass in the element OID, the element
1793 * alias, and the graph relation OID. Result is appended to buf.
1794 */
1795static void
1797{
1800 SysScanDesc scan;
1801 int count;
1802 HeapTuple tup;
1803 List *label_list = NIL;
1804
1806
1807 ScanKeyInit(&scankey[0],
1811
1812 /*
1813 * We want to output the labels in a deterministic order. So we first
1814 * read all the data, then sort, then print it.
1815 */
1817
1818 while ((tup = systable_getnext(scan)))
1819 {
1821 struct oid_str_pair *osp;
1822
1823 osp = palloc_object(struct oid_str_pair);
1824 osp->oid = pgelform->oid;
1825 osp->str = get_propgraph_label_name(pgelform->pgellabelid);
1826
1828 }
1829
1830 systable_endscan(scan);
1832
1833 count = list_length(label_list);
1834
1835 /* Each element has at least one label. */
1836 Assert(count > 0);
1837
1838 /*
1839 * It is enough for the comparison function to compare just labels, since
1840 * all the labels of an element table should have distinct names.
1841 */
1843
1845 {
1846 if (strcmp(osp->str, elalias) == 0)
1847 {
1848 /* If the default label is the only label, don't print anything. */
1849 if (count != 1)
1850 appendStringInfoString(buf, " DEFAULT LABEL");
1851 }
1852 else
1853 appendStringInfo(buf, " LABEL %s", quote_identifier(osp->str));
1854
1856 }
1857}
1858
1859/*
1860 * Helper function for make_propgraphdef_properties(): Sort (propname, expr)
1861 * pairs by name.
1862 */
1863static int
1865{
1866 List *la = lfirst_node(List, a);
1867 List *lb = lfirst_node(List, b);
1868 char *pna = strVal(linitial(la));
1869 char *pnb = strVal(linitial(lb));
1870
1871 return strcmp(pna, pnb);
1872}
1873
1874/*
1875 * Generates element table properties clause (PROPERTIES (...) or NO
1876 * PROPERTIES). Pass in label OID and element table OID. Result is appended
1877 * to buf.
1878 */
1879static void
1881{
1884 SysScanDesc scan;
1885 HeapTuple tup;
1886 List *outlist = NIL;
1887
1889
1890 ScanKeyInit(&scankey[0],
1894
1895 /*
1896 * We want to output the properties in a deterministic order. So we first
1897 * read all the data, then sort, then print it.
1898 */
1900
1901 while ((tup = systable_getnext(scan)))
1902 {
1905 bool isnull;
1906 char *tmp;
1907 Node *expr;
1908 char *propname;
1909
1911 Assert(!isnull);
1913 expr = stringToNode(tmp);
1914 pfree(tmp);
1915
1917
1918 outlist = lappend(outlist, list_make2(makeString(propname), expr));
1919 }
1920
1921 systable_endscan(scan);
1923
1925
1926 if (outlist)
1927 {
1928 List *context;
1929 ListCell *lc;
1930 bool first = true;
1931
1933
1934 appendStringInfoString(buf, " PROPERTIES (");
1935
1936 foreach(lc, outlist)
1937 {
1939 char *propname = strVal(linitial(data));
1940 Node *expr = lsecond(data);
1941
1942 if (first)
1943 first = false;
1944 else
1946
1947 if (IsA(expr, Var) && strcmp(propname, get_attname(elrelid, castNode(Var, expr)->varattno, false)) == 0)
1949 else
1950 appendStringInfo(buf, "%s AS %s",
1951 deparse_expression_pretty(expr, context, false, false, 0, 0),
1953 }
1954
1956 }
1957 else
1958 appendStringInfoString(buf, " NO PROPERTIES");
1959}
1960
1961/*
1962 * pg_get_statisticsobjdef
1963 * Get the definition of an extended statistics object
1964 */
1965Datum
1967{
1969 char *res;
1970
1971 res = pg_get_statisticsobj_worker(statextid, false, true);
1972
1973 if (res == NULL)
1975
1977}
1978
1979/*
1980 * Internal version for use by ALTER TABLE.
1981 * Returns a palloc'd C string; no pretty-printing.
1982 */
1983char *
1985{
1986 return pg_get_statisticsobj_worker(statextid, false, false);
1987}
1988
1989/*
1990 * pg_get_statisticsobjdef_columns
1991 * Get columns and expressions for an extended statistics object
1992 */
1993Datum
1995{
1997 char *res;
1998
1999 res = pg_get_statisticsobj_worker(statextid, true, true);
2000
2001 if (res == NULL)
2003
2005}
2006
2007/*
2008 * Internal workhorse to decompile an extended statistics object.
2009 */
2010static char *
2012{
2016 int colno;
2017 char *nsp;
2018 ArrayType *arr;
2019 char *enabled;
2020 Datum datum;
2021 bool ndistinct_enabled;
2023 bool mcv_enabled;
2024 int i;
2025 List *context;
2026 ListCell *lc;
2027 List *exprs = NIL;
2028 bool has_exprs;
2029 int ncolumns;
2030
2032
2034 {
2035 if (missing_ok)
2036 return NULL;
2037 elog(ERROR, "cache lookup failed for statistics object %u", statextid);
2038 }
2039
2040 /* has the statistics expressions? */
2042
2044
2045 /*
2046 * Get the statistics expressions, if any. (NOTE: we do not use the
2047 * relcache versions of the expressions, because we want to display
2048 * non-const-folded expressions.)
2049 */
2050 if (has_exprs)
2051 {
2053 char *exprsString;
2054
2058 exprs = (List *) stringToNode(exprsString);
2060 }
2061 else
2062 exprs = NIL;
2063
2064 /* count the number of columns (attributes and expressions) */
2065 ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
2066
2068
2069 if (!columns_only)
2070 {
2071 nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
2072 appendStringInfo(&buf, "CREATE STATISTICS %s",
2074 NameStr(statextrec->stxname)));
2075
2076 /*
2077 * Decode the stxkind column so that we know which stats types to
2078 * print.
2079 */
2082 arr = DatumGetArrayTypeP(datum);
2083 if (ARR_NDIM(arr) != 1 ||
2084 ARR_HASNULL(arr) ||
2085 ARR_ELEMTYPE(arr) != CHAROID)
2086 elog(ERROR, "stxkind is not a 1-D char array");
2087 enabled = (char *) ARR_DATA_PTR(arr);
2088
2089 ndistinct_enabled = false;
2090 dependencies_enabled = false;
2091 mcv_enabled = false;
2092
2093 for (i = 0; i < ARR_DIMS(arr)[0]; i++)
2094 {
2095 if (enabled[i] == STATS_EXT_NDISTINCT)
2096 ndistinct_enabled = true;
2097 else if (enabled[i] == STATS_EXT_DEPENDENCIES)
2098 dependencies_enabled = true;
2099 else if (enabled[i] == STATS_EXT_MCV)
2100 mcv_enabled = true;
2101
2102 /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
2103 }
2104
2105 /*
2106 * If any option is disabled, then we'll need to append the types
2107 * clause to show which options are enabled. We omit the types clause
2108 * on purpose when all options are enabled, so a pg_dump/pg_restore
2109 * will create all statistics types on a newer postgres version, if
2110 * the statistics had all options enabled on the original version.
2111 *
2112 * But if the statistics is defined on just a single column, it has to
2113 * be an expression statistics. In that case we don't need to specify
2114 * kinds.
2115 */
2117 (ncolumns > 1))
2118 {
2119 bool gotone = false;
2120
2122
2124 {
2125 appendStringInfoString(&buf, "ndistinct");
2126 gotone = true;
2127 }
2128
2130 {
2131 appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
2132 gotone = true;
2133 }
2134
2135 if (mcv_enabled)
2136 appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
2137
2139 }
2140
2141 appendStringInfoString(&buf, " ON ");
2142 }
2143
2144 /* decode simple column references */
2145 for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
2146 {
2147 AttrNumber attnum = statextrec->stxkeys.values[colno];
2148 char *attname;
2149
2150 if (colno > 0)
2152
2153 attname = get_attname(statextrec->stxrelid, attnum, false);
2154
2156 }
2157
2159 statextrec->stxrelid);
2160
2161 foreach(lc, exprs)
2162 {
2163 Node *expr = (Node *) lfirst(lc);
2164 char *str;
2165 int prettyFlags = PRETTYFLAG_PAREN;
2166
2167 str = deparse_expression_pretty(expr, context, false, false,
2168 prettyFlags, 0);
2169
2170 if (colno > 0)
2172
2173 /* Need parens if it's not a bare function call */
2174 if (looks_like_function(expr))
2176 else
2177 appendStringInfo(&buf, "(%s)", str);
2178
2179 colno++;
2180 }
2181
2182 if (!columns_only)
2183 appendStringInfo(&buf, " FROM %s",
2185
2187
2188 return buf.data;
2189}
2190
2191/*
2192 * Generate text array of expressions for statistics object.
2193 */
2194Datum
2196{
2200 Datum datum;
2201 List *context;
2202 ListCell *lc;
2203 List *exprs = NIL;
2204 bool has_exprs;
2205 char *tmp;
2206 ArrayBuildState *astate = NULL;
2207
2209
2212
2213 /* Does the stats object have expressions? */
2215
2216 /* no expressions? we're done */
2217 if (!has_exprs)
2218 {
2221 }
2222
2224
2225 /*
2226 * Get the statistics expressions, and deparse them into text values.
2227 */
2230 tmp = TextDatumGetCString(datum);
2231 exprs = (List *) stringToNode(tmp);
2232 pfree(tmp);
2233
2235 statextrec->stxrelid);
2236
2237 foreach(lc, exprs)
2238 {
2239 Node *expr = (Node *) lfirst(lc);
2240 char *str;
2241 int prettyFlags = PRETTYFLAG_INDENT;
2242
2243 str = deparse_expression_pretty(expr, context, false, false,
2244 prettyFlags, 0);
2245
2246 astate = accumArrayResult(astate,
2248 false,
2249 TEXTOID,
2251 }
2252
2254
2256}
2257
2258/*
2259 * pg_get_partkeydef
2260 *
2261 * Returns the partition key specification, ie, the following:
2262 *
2263 * { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
2264 */
2265Datum
2267{
2268 Oid relid = PG_GETARG_OID(0);
2269 char *res;
2270
2271 res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
2272
2273 if (res == NULL)
2275
2277}
2278
2279/* Internal version that just reports the column definitions */
2280char *
2282{
2283 int prettyFlags;
2284
2285 prettyFlags = GET_PRETTY_FLAGS(pretty);
2286
2287 return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
2288}
2289
2290/*
2291 * Internal workhorse to decompile a partition key definition.
2292 */
2293static char *
2294pg_get_partkeydef_worker(Oid relid, int prettyFlags,
2295 bool attrsOnly, bool missing_ok)
2296{
2298 HeapTuple tuple;
2300 oidvector *partcollation;
2301 List *partexprs;
2303 List *context;
2304 Datum datum;
2306 int keyno;
2307 char *str;
2308 char *sep;
2309
2311 if (!HeapTupleIsValid(tuple))
2312 {
2313 if (missing_ok)
2314 return NULL;
2315 elog(ERROR, "cache lookup failed for partition key of %u", relid);
2316 }
2317
2319
2320 Assert(form->partrelid == relid);
2321
2322 /* Must get partclass and partcollation the hard way */
2323 datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2325 partclass = (oidvector *) DatumGetPointer(datum);
2326
2327 datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2329 partcollation = (oidvector *) DatumGetPointer(datum);
2330
2331
2332 /*
2333 * Get the expressions, if any. (NOTE: we do not use the relcache
2334 * versions of the expressions, because we want to display
2335 * non-const-folded expressions.)
2336 */
2338 {
2340 char *exprsString;
2341
2345 partexprs = (List *) stringToNode(exprsString);
2346
2347 if (!IsA(partexprs, List))
2348 elog(ERROR, "unexpected node type found in partexprs: %d",
2349 (int) nodeTag(partexprs));
2350
2352 }
2353 else
2354 partexprs = NIL;
2355
2356 partexpr_item = list_head(partexprs);
2357 context = deparse_context_for(get_relation_name(relid), relid);
2358
2360
2361 switch (form->partstrat)
2362 {
2364 if (!attrsOnly)
2365 appendStringInfoString(&buf, "HASH");
2366 break;
2368 if (!attrsOnly)
2369 appendStringInfoString(&buf, "LIST");
2370 break;
2372 if (!attrsOnly)
2373 appendStringInfoString(&buf, "RANGE");
2374 break;
2375 default:
2376 elog(ERROR, "unexpected partition strategy: %d",
2377 (int) form->partstrat);
2378 }
2379
2380 if (!attrsOnly)
2382 sep = "";
2383 for (keyno = 0; keyno < form->partnatts; keyno++)
2384 {
2385 AttrNumber attnum = form->partattrs.values[keyno];
2388 Oid partcoll;
2389
2391 sep = ", ";
2392 if (attnum != 0)
2393 {
2394 /* Simple attribute reference */
2395 char *attname;
2397
2398 attname = get_attname(relid, attnum, false);
2403 }
2404 else
2405 {
2406 /* Expression */
2407 Node *partkey;
2408
2409 if (partexpr_item == NULL)
2410 elog(ERROR, "too few entries in partexprs list");
2412 partexpr_item = lnext(partexprs, partexpr_item);
2413
2414 /* Deparse */
2415 str = deparse_expression_pretty(partkey, context, false, false,
2416 prettyFlags, 0);
2417 /* Need parens if it's not a bare function call */
2420 else
2421 appendStringInfo(&buf, "(%s)", str);
2422
2425 }
2426
2427 /* Add collation, if not default for column */
2428 partcoll = partcollation->values[keyno];
2430 appendStringInfo(&buf, " COLLATE %s",
2432
2433 /* Add the operator class name, if not default */
2434 if (!attrsOnly)
2435 get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2436 }
2437
2438 if (!attrsOnly)
2440
2441 /* Clean up */
2442 ReleaseSysCache(tuple);
2443
2444 return buf.data;
2445}
2446
2447/*
2448 * pg_get_partition_constraintdef
2449 *
2450 * Returns partition constraint expression as a string for the input relation
2451 */
2452Datum
2454{
2457 int prettyFlags;
2458 List *context;
2459 char *consrc;
2460
2462
2463 /* Quick exit if no partition constraint */
2464 if (constr_expr == NULL)
2466
2467 /*
2468 * Deparse and return the constraint expression.
2469 */
2470 prettyFlags = PRETTYFLAG_INDENT;
2472 consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2473 false, prettyFlags, 0);
2474
2476}
2477
2478/*
2479 * pg_get_partconstrdef_string
2480 *
2481 * Returns the partition constraint as a C-string for the input relation, with
2482 * the given alias. No pretty-printing.
2483 */
2484char *
2486{
2488 List *context;
2489
2491 context = deparse_context_for(aliasname, partitionId);
2492
2493 return deparse_expression((Node *) constr_expr, context, true, false);
2494}
2495
2496/*
2497 * pg_get_constraintdef
2498 *
2499 * Returns the definition for the constraint, ie, everything that needs to
2500 * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
2501 */
2502Datum
2504{
2506 int prettyFlags;
2507 char *res;
2508
2509 prettyFlags = PRETTYFLAG_INDENT;
2510
2511 res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2512
2513 if (res == NULL)
2515
2517}
2518
2519Datum
2521{
2523 bool pretty = PG_GETARG_BOOL(1);
2524 int prettyFlags;
2525 char *res;
2526
2527 prettyFlags = GET_PRETTY_FLAGS(pretty);
2528
2529 res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2530
2531 if (res == NULL)
2533
2535}
2536
2537/*
2538 * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
2539 */
2540char *
2542{
2543 return pg_get_constraintdef_worker(constraintId, true, 0, false);
2544}
2545
2546/*
2547 * As of 9.4, we now use an MVCC snapshot for this.
2548 */
2549static char *
2551 int prettyFlags, bool missing_ok)
2552{
2553 HeapTuple tup;
2556 SysScanDesc scandesc;
2560
2561 ScanKeyInit(&scankey[0],
2565
2566 scandesc = systable_beginscan(relation,
2568 true,
2569 snapshot,
2570 1,
2571 scankey);
2572
2573 /*
2574 * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2575 * via SearchSysCache, which works fine.
2576 */
2577 tup = systable_getnext(scandesc);
2578
2579 UnregisterSnapshot(snapshot);
2580
2581 if (!HeapTupleIsValid(tup))
2582 {
2583 if (missing_ok)
2584 {
2585 systable_endscan(scandesc);
2586 table_close(relation, AccessShareLock);
2587 return NULL;
2588 }
2589 elog(ERROR, "could not find tuple for constraint %u", constraintId);
2590 }
2591
2593
2595
2596 if (fullCommand)
2597 {
2598 if (OidIsValid(conForm->conrelid))
2599 {
2600 /*
2601 * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2602 * constraints, and other types of constraints don't inherit
2603 * anyway so it doesn't matter whether we say ONLY or not. Someday
2604 * we might need to let callers specify whether to put ONLY in the
2605 * command.
2606 */
2607 appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2609 quote_identifier(NameStr(conForm->conname)));
2610 }
2611 else
2612 {
2613 /* Must be a domain constraint */
2614 Assert(OidIsValid(conForm->contypid));
2615 appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2617 quote_identifier(NameStr(conForm->conname)));
2618 }
2619 }
2620
2621 switch (conForm->contype)
2622 {
2623 case CONSTRAINT_FOREIGN:
2624 {
2625 Datum val;
2626 bool isnull;
2627 const char *string;
2628
2629 /* Start off the constraint definition */
2630 appendStringInfoString(&buf, "FOREIGN KEY (");
2631
2632 /* Fetch and build referencing-column list */
2635
2636 /* If it is a temporal foreign key then it uses PERIOD. */
2637 decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
2638
2639 /* add foreign relation name */
2640 appendStringInfo(&buf, ") REFERENCES %s(",
2642 NIL));
2643
2644 /* Fetch and build referenced-column list */
2647
2648 decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
2649
2651
2652 /* Add match type */
2653 switch (conForm->confmatchtype)
2654 {
2656 string = " MATCH FULL";
2657 break;
2659 string = " MATCH PARTIAL";
2660 break;
2662 string = "";
2663 break;
2664 default:
2665 elog(ERROR, "unrecognized confmatchtype: %d",
2666 conForm->confmatchtype);
2667 string = ""; /* keep compiler quiet */
2668 break;
2669 }
2670 appendStringInfoString(&buf, string);
2671
2672 /* Add ON UPDATE and ON DELETE clauses, if needed */
2673 switch (conForm->confupdtype)
2674 {
2676 string = NULL; /* suppress default */
2677 break;
2679 string = "RESTRICT";
2680 break;
2682 string = "CASCADE";
2683 break;
2685 string = "SET NULL";
2686 break;
2688 string = "SET DEFAULT";
2689 break;
2690 default:
2691 elog(ERROR, "unrecognized confupdtype: %d",
2692 conForm->confupdtype);
2693 string = NULL; /* keep compiler quiet */
2694 break;
2695 }
2696 if (string)
2697 appendStringInfo(&buf, " ON UPDATE %s", string);
2698
2699 switch (conForm->confdeltype)
2700 {
2702 string = NULL; /* suppress default */
2703 break;
2705 string = "RESTRICT";
2706 break;
2708 string = "CASCADE";
2709 break;
2711 string = "SET NULL";
2712 break;
2714 string = "SET DEFAULT";
2715 break;
2716 default:
2717 elog(ERROR, "unrecognized confdeltype: %d",
2718 conForm->confdeltype);
2719 string = NULL; /* keep compiler quiet */
2720 break;
2721 }
2722 if (string)
2723 appendStringInfo(&buf, " ON DELETE %s", string);
2724
2725 /*
2726 * Add columns specified to SET NULL or SET DEFAULT if
2727 * provided.
2728 */
2731 if (!isnull)
2732 {
2734 decompile_column_index_array(val, conForm->conrelid, false, &buf);
2736 }
2737
2738 break;
2739 }
2740 case CONSTRAINT_PRIMARY:
2741 case CONSTRAINT_UNIQUE:
2742 {
2743 Datum val;
2744 Oid indexId;
2745 int keyatts;
2747
2748 /* Start off the constraint definition */
2749 if (conForm->contype == CONSTRAINT_PRIMARY)
2750 appendStringInfoString(&buf, "PRIMARY KEY ");
2751 else
2752 appendStringInfoString(&buf, "UNIQUE ");
2753
2754 indexId = conForm->conindid;
2755
2758 elog(ERROR, "cache lookup failed for index %u", indexId);
2759 if (conForm->contype == CONSTRAINT_UNIQUE &&
2760 ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
2761 appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2762
2764
2765 /* Fetch and build target column list */
2768
2769 keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
2770 if (conForm->conperiod)
2771 appendStringInfoString(&buf, " WITHOUT OVERLAPS");
2772
2774
2775 /* Build including column list (from pg_index.indkeys) */
2778 if (DatumGetInt32(val) > keyatts)
2779 {
2780 Datum cols;
2781 Datum *keys;
2782 int nKeys;
2783 int j;
2784
2785 appendStringInfoString(&buf, " INCLUDE (");
2786
2789
2791 &keys, NULL, &nKeys);
2792
2793 for (j = keyatts; j < nKeys; j++)
2794 {
2795 char *colName;
2796
2797 colName = get_attname(conForm->conrelid,
2798 DatumGetInt16(keys[j]), false);
2799 if (j > keyatts)
2802 }
2803
2805 }
2807
2808 /* XXX why do we only print these bits if fullCommand? */
2810 {
2812 Oid tblspc;
2813
2814 if (options)
2815 {
2816 appendStringInfo(&buf, " WITH (%s)", options);
2817 pfree(options);
2818 }
2819
2820 /*
2821 * Print the tablespace, unless it's the database default.
2822 * This is to help ALTER TABLE usage of this facility,
2823 * which needs this behavior to recreate exact catalog
2824 * state.
2825 */
2827 if (OidIsValid(tblspc))
2828 appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2830 }
2831
2832 break;
2833 }
2834 case CONSTRAINT_CHECK:
2835 {
2836 Datum val;
2837 char *conbin;
2838 char *consrc;
2839 Node *expr;
2840 List *context;
2841
2842 /* Fetch constraint expression in parsetree form */
2845
2847 expr = stringToNode(conbin);
2848
2849 /* Set up deparsing context for Var nodes in constraint */
2850 if (conForm->conrelid != InvalidOid)
2851 {
2852 /* relation constraint */
2853 context = deparse_context_for(get_relation_name(conForm->conrelid),
2854 conForm->conrelid);
2855 }
2856 else
2857 {
2858 /* domain constraint --- can't have Vars */
2859 context = NIL;
2860 }
2861
2862 consrc = deparse_expression_pretty(expr, context, false, false,
2863 prettyFlags, 0);
2864
2865 /*
2866 * Now emit the constraint definition, adding NO INHERIT if
2867 * necessary.
2868 *
2869 * There are cases where the constraint expression will be
2870 * fully parenthesized and we don't need the outer parens ...
2871 * but there are other cases where we do need 'em. Be
2872 * conservative for now.
2873 *
2874 * Note that simply checking for leading '(' and trailing ')'
2875 * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2876 */
2877 appendStringInfo(&buf, "CHECK (%s)%s",
2878 consrc,
2879 conForm->connoinherit ? " NO INHERIT" : "");
2880 break;
2881 }
2882 case CONSTRAINT_NOTNULL:
2883 {
2884 if (conForm->conrelid)
2885 {
2887
2889
2890 appendStringInfo(&buf, "NOT NULL %s",
2892 attnum, false)));
2894 appendStringInfoString(&buf, " NO INHERIT");
2895 }
2896 else if (conForm->contypid)
2897 {
2898 /* conkey is null for domain not-null constraints */
2899 appendStringInfoString(&buf, "NOT NULL");
2900 }
2901 break;
2902 }
2903
2904 case CONSTRAINT_TRIGGER:
2905
2906 /*
2907 * There isn't an ALTER TABLE syntax for creating a user-defined
2908 * constraint trigger, but it seems better to print something than
2909 * throw an error; if we throw error then this function couldn't
2910 * safely be applied to all rows of pg_constraint.
2911 */
2912 appendStringInfoString(&buf, "TRIGGER");
2913 break;
2915 {
2916 Oid indexOid = conForm->conindid;
2917 Datum val;
2918 Datum *elems;
2919 int nElems;
2920 int i;
2921 Oid *operators;
2922
2923 /* Extract operator OIDs from the pg_constraint tuple */
2926
2928 &elems, NULL, &nElems);
2929
2930 operators = (Oid *) palloc(nElems * sizeof(Oid));
2931 for (i = 0; i < nElems; i++)
2932 operators[i] = DatumGetObjectId(elems[i]);
2933
2934 /* pg_get_indexdef_worker does the rest */
2935 /* suppress tablespace because pg_dump wants it that way */
2937 pg_get_indexdef_worker(indexOid,
2938 0,
2939 operators,
2940 false,
2941 false,
2942 false,
2943 false,
2944 prettyFlags,
2945 false));
2946 break;
2947 }
2948 default:
2949 elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2950 break;
2951 }
2952
2953 if (conForm->condeferrable)
2954 appendStringInfoString(&buf, " DEFERRABLE");
2955 if (conForm->condeferred)
2956 appendStringInfoString(&buf, " INITIALLY DEFERRED");
2957
2958 /* Validated status is irrelevant when the constraint is NOT ENFORCED. */
2959 if (!conForm->conenforced)
2960 appendStringInfoString(&buf, " NOT ENFORCED");
2961 else if (!conForm->convalidated)
2962 appendStringInfoString(&buf, " NOT VALID");
2963
2964 /* Cleanup */
2965 systable_endscan(scandesc);
2966 table_close(relation, AccessShareLock);
2967
2968 return buf.data;
2969}
2970
2971
2972/*
2973 * Convert an int16[] Datum into a comma-separated list of column names
2974 * for the indicated relation; append the list to buf. Returns the number
2975 * of keys.
2976 */
2977static int
2980{
2981 Datum *keys;
2982 int nKeys;
2983 int j;
2984
2985 /* Extract data from array of int16 */
2987 &keys, NULL, &nKeys);
2988
2989 for (j = 0; j < nKeys; j++)
2990 {
2991 char *colName;
2992
2993 colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2994
2995 if (j == 0)
2997 else
2998 appendStringInfo(buf, ", %s%s",
2999 (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
3001 }
3002
3003 return nKeys;
3004}
3005
3006
3007/* ----------
3008 * pg_get_expr - Decompile an expression tree
3009 *
3010 * Input: an expression tree in nodeToString form, and a relation OID
3011 *
3012 * Output: reverse-listed expression
3013 *
3014 * Currently, the expression can only refer to a single relation, namely
3015 * the one specified by the second parameter. This is sufficient for
3016 * partial indexes, column default expressions, etc. We also support
3017 * Var-free expressions, for which the OID can be InvalidOid.
3018 *
3019 * If the OID is nonzero but not actually valid, don't throw an error,
3020 * just return NULL. This is a bit questionable, but it's what we've
3021 * done historically, and it can help avoid unwanted failures when
3022 * examining catalog entries for just-deleted relations.
3023 *
3024 * We expect this function to work, or throw a reasonably clean error,
3025 * for any node tree that can appear in a catalog pg_node_tree column.
3026 * Query trees, such as those appearing in pg_rewrite.ev_action, are
3027 * not supported. Nor are expressions in more than one relation, which
3028 * can appear in places like pg_rewrite.ev_qual.
3029 * ----------
3030 */
3031Datum
3033{
3034 text *expr = PG_GETARG_TEXT_PP(0);
3035 Oid relid = PG_GETARG_OID(1);
3036 text *result;
3037 int prettyFlags;
3038
3039 prettyFlags = PRETTYFLAG_INDENT;
3040
3041 result = pg_get_expr_worker(expr, relid, prettyFlags);
3042 if (result)
3044 else
3046}
3047
3048Datum
3050{
3051 text *expr = PG_GETARG_TEXT_PP(0);
3052 Oid relid = PG_GETARG_OID(1);
3053 bool pretty = PG_GETARG_BOOL(2);
3054 text *result;
3055 int prettyFlags;
3056
3057 prettyFlags = GET_PRETTY_FLAGS(pretty);
3058
3059 result = pg_get_expr_worker(expr, relid, prettyFlags);
3060 if (result)
3062 else
3064}
3065
3066static text *
3067pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
3068{
3069 Node *node;
3070 Node *tst;
3071 Relids relids;
3072 List *context;
3073 char *exprstr;
3074 Relation rel = NULL;
3075 char *str;
3076
3077 /* Convert input pg_node_tree (really TEXT) object to C string */
3078 exprstr = text_to_cstring(expr);
3079
3080 /* Convert expression to node tree */
3081 node = (Node *) stringToNode(exprstr);
3082
3083 pfree(exprstr);
3084
3085 /*
3086 * Throw error if the input is a querytree rather than an expression tree.
3087 * While we could support queries here, there seems no very good reason
3088 * to. In most such catalog columns, we'll see a List of Query nodes, or
3089 * even nested Lists, so drill down to a non-List node before checking.
3090 */
3091 tst = node;
3092 while (tst && IsA(tst, List))
3093 tst = linitial((List *) tst);
3094 if (tst && IsA(tst, Query))
3095 ereport(ERROR,
3097 errmsg("input is a query, not an expression")));
3098
3099 /*
3100 * Throw error if the expression contains Vars we won't be able to
3101 * deparse.
3102 */
3103 relids = pull_varnos(NULL, node);
3104 if (OidIsValid(relid))
3105 {
3106 if (!bms_is_subset(relids, bms_make_singleton(1)))
3107 ereport(ERROR,
3109 errmsg("expression contains variables of more than one relation")));
3110 }
3111 else
3112 {
3113 if (!bms_is_empty(relids))
3114 ereport(ERROR,
3116 errmsg("expression contains variables")));
3117 }
3118
3119 /*
3120 * Prepare deparse context if needed. If we are deparsing with a relid,
3121 * we need to transiently open and lock the rel, to make sure it won't go
3122 * away underneath us. (set_relation_column_names would lock it anyway,
3123 * so this isn't really introducing any new behavior.)
3124 */
3125 if (OidIsValid(relid))
3126 {
3127 rel = try_relation_open(relid, AccessShareLock);
3128 if (rel == NULL)
3129 return NULL;
3130 context = deparse_context_for(RelationGetRelationName(rel), relid);
3131 }
3132 else
3133 context = NIL;
3134
3135 /* Deparse */
3136 str = deparse_expression_pretty(node, context, false, false,
3137 prettyFlags, 0);
3138
3139 if (rel != NULL)
3141
3142 return string_to_text(str);
3143}
3144
3145
3146/* ----------
3147 * pg_get_userbyid - Get a user name by roleid and
3148 * fallback to 'unknown (OID=n)'
3149 * ----------
3150 */
3151Datum
3153{
3154 Oid roleid = PG_GETARG_OID(0);
3155 Name result;
3158
3159 /*
3160 * Allocate space for the result
3161 */
3164
3165 /*
3166 * Get the pg_authid entry and print the result
3167 */
3170 {
3172 *result = role_rec->rolname;
3174 }
3175 else
3176 sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
3177
3179}
3180
3181
3182/*
3183 * pg_get_serial_sequence
3184 * Get the name of the sequence used by an identity or serial column,
3185 * formatted suitably for passing to setval, nextval or currval.
3186 * First parameter is not treated as double-quoted, second parameter
3187 * is --- see documentation for reason.
3188 */
3189Datum
3191{
3192 text *tablename = PG_GETARG_TEXT_PP(0);
3195 Oid tableOid;
3196 char *column;
3200 ScanKeyData key[3];
3201 SysScanDesc scan;
3202 HeapTuple tup;
3203
3204 /* Look up table name. Can't lock it - we might not have privileges. */
3206 tableOid = RangeVarGetRelid(tablerv, NoLock, false);
3207
3208 /* Get the number of the column */
3210
3211 attnum = get_attnum(tableOid, column);
3213 ereport(ERROR,
3215 errmsg("column \"%s\" of relation \"%s\" does not exist",
3216 column, tablerv->relname)));
3217
3218 /* Search the dependency table for the dependent sequence */
3220
3221 ScanKeyInit(&key[0],
3225 ScanKeyInit(&key[1],
3228 ObjectIdGetDatum(tableOid));
3229 ScanKeyInit(&key[2],
3233
3235 NULL, 3, key);
3236
3237 while (HeapTupleIsValid(tup = systable_getnext(scan)))
3238 {
3240
3241 /*
3242 * Look for an auto dependency (serial column) or internal dependency
3243 * (identity column) of a sequence on a column. (We need the relkind
3244 * test because indexes can also have auto dependencies on columns.)
3245 */
3246 if (deprec->classid == RelationRelationId &&
3247 deprec->objsubid == 0 &&
3248 (deprec->deptype == DEPENDENCY_AUTO ||
3249 deprec->deptype == DEPENDENCY_INTERNAL) &&
3251 {
3252 sequenceId = deprec->objid;
3253 break;
3254 }
3255 }
3256
3257 systable_endscan(scan);
3259
3261 {
3262 char *result;
3263
3265
3267 }
3268
3270}
3271
3272
3273/*
3274 * pg_get_functiondef
3275 * Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
3276 * the specified function.
3277 *
3278 * Note: if you change the output format of this function, be careful not
3279 * to break psql's rules (in \ef and \sf) for identifying the start of the
3280 * function body. To wit: the function body starts on a line that begins with
3281 * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
3282 */
3283Datum
3285{
3286 Oid funcid = PG_GETARG_OID(0);
3290 Form_pg_proc proc;
3291 bool isfunction;
3292 Datum tmp;
3293 bool isnull;
3294 const char *prosrc;
3295 const char *name;
3296 const char *nsp;
3298 int oldlen;
3299
3301
3302 /* Look up the function */
3306
3307 proc = (Form_pg_proc) GETSTRUCT(proctup);
3308 name = NameStr(proc->proname);
3309
3310 if (proc->prokind == PROKIND_AGGREGATE)
3311 ereport(ERROR,
3313 errmsg("\"%s\" is an aggregate function", name)));
3314
3315 isfunction = (proc->prokind != PROKIND_PROCEDURE);
3316
3317 /*
3318 * We always qualify the function name, to ensure the right function gets
3319 * replaced.
3320 */
3321 nsp = get_namespace_name_or_temp(proc->pronamespace);
3322 appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
3323 isfunction ? "FUNCTION" : "PROCEDURE",
3325 (void) print_function_arguments(&buf, proctup, false, true);
3326 appendStringInfoString(&buf, ")\n");
3327 if (isfunction)
3328 {
3329 appendStringInfoString(&buf, " RETURNS ");
3331 appendStringInfoChar(&buf, '\n');
3332 }
3333
3335
3336 appendStringInfo(&buf, " LANGUAGE %s\n",
3337 quote_identifier(get_language_name(proc->prolang, false)));
3338
3339 /* Emit some miscellaneous options on one line */
3340 oldlen = buf.len;
3341
3342 if (proc->prokind == PROKIND_WINDOW)
3343 appendStringInfoString(&buf, " WINDOW");
3344 switch (proc->provolatile)
3345 {
3347 appendStringInfoString(&buf, " IMMUTABLE");
3348 break;
3349 case PROVOLATILE_STABLE:
3350 appendStringInfoString(&buf, " STABLE");
3351 break;
3353 break;
3354 }
3355
3356 switch (proc->proparallel)
3357 {
3358 case PROPARALLEL_SAFE:
3359 appendStringInfoString(&buf, " PARALLEL SAFE");
3360 break;
3362 appendStringInfoString(&buf, " PARALLEL RESTRICTED");
3363 break;
3364 case PROPARALLEL_UNSAFE:
3365 break;
3366 }
3367
3368 if (proc->proisstrict)
3369 appendStringInfoString(&buf, " STRICT");
3370 if (proc->prosecdef)
3371 appendStringInfoString(&buf, " SECURITY DEFINER");
3372 if (proc->proleakproof)
3373 appendStringInfoString(&buf, " LEAKPROOF");
3374
3375 /* This code for the default cost and rows should match functioncmds.c */
3376 if (proc->prolang == INTERNALlanguageId ||
3377 proc->prolang == ClanguageId)
3378 procost = 1;
3379 else
3380 procost = 100;
3381 if (proc->procost != procost)
3382 appendStringInfo(&buf, " COST %g", proc->procost);
3383
3384 if (proc->prorows > 0 && proc->prorows != 1000)
3385 appendStringInfo(&buf, " ROWS %g", proc->prorows);
3386
3387 if (proc->prosupport)
3388 {
3389 Oid argtypes[1];
3390
3391 /*
3392 * We should qualify the support function's name if it wouldn't be
3393 * resolved by lookup in the current search path.
3394 */
3395 argtypes[0] = INTERNALOID;
3396 appendStringInfo(&buf, " SUPPORT %s",
3397 generate_function_name(proc->prosupport, 1,
3398 NIL, argtypes,
3399 false, NULL, false));
3400 }
3401
3402 if (oldlen != buf.len)
3403 appendStringInfoChar(&buf, '\n');
3404
3405 /* Emit any proconfig options, one per line */
3407 if (!isnull)
3408 {
3410 int i;
3411
3413 Assert(ARR_NDIM(a) == 1);
3414 Assert(ARR_LBOUND(a)[0] == 1);
3415
3416 for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3417 {
3418 Datum d;
3419
3420 d = array_ref(a, 1, &i,
3421 -1 /* varlenarray */ ,
3422 -1 /* TEXT's typlen */ ,
3423 false /* TEXT's typbyval */ ,
3424 TYPALIGN_INT /* TEXT's typalign */ ,
3425 &isnull);
3426 if (!isnull)
3427 {
3429 char *pos;
3430
3431 pos = strchr(configitem, '=');
3432 if (pos == NULL)
3433 continue;
3434 *pos++ = '\0';
3435
3436 appendStringInfo(&buf, " SET %s TO ",
3438
3439 /*
3440 * Variables that are marked GUC_LIST_QUOTE were already fully
3441 * quoted by flatten_set_variable_args() before they were put
3442 * into the proconfig array. However, because the quoting
3443 * rules used there aren't exactly like SQL's, we have to
3444 * break the list value apart and then quote the elements as
3445 * string literals. (The elements may be double-quoted as-is,
3446 * but we can't just feed them to the SQL parser; it would do
3447 * the wrong thing with elements that are zero-length or
3448 * longer than NAMEDATALEN.) Also, we need a special case for
3449 * empty lists.
3450 *
3451 * Variables that are not so marked should just be emitted as
3452 * simple string literals. If the variable is not known to
3453 * guc.c, we'll do that; this makes it unsafe to use
3454 * GUC_LIST_QUOTE for extension variables.
3455 */
3457 {
3458 List *namelist;
3459 ListCell *lc;
3460
3461 /* Parse string into list of identifiers */
3462 if (!SplitGUCList(pos, ',', &namelist))
3463 {
3464 /* this shouldn't fail really */
3465 elog(ERROR, "invalid list syntax in proconfig item");
3466 }
3467 /* Special case: represent an empty list as NULL */
3468 if (namelist == NIL)
3469 appendStringInfoString(&buf, "NULL");
3470 foreach(lc, namelist)
3471 {
3472 char *curname = (char *) lfirst(lc);
3473
3475 if (lnext(namelist, lc))
3477 }
3478 }
3479 else
3481 appendStringInfoChar(&buf, '\n');
3482 }
3483 }
3484 }
3485
3486 /* And finally the function definition ... */
3488 if (proc->prolang == SQLlanguageId && !isnull)
3489 {
3491 }
3492 else
3493 {
3494 appendStringInfoString(&buf, "AS ");
3495
3497 if (!isnull)
3498 {
3500 appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3501 }
3502
3504 prosrc = TextDatumGetCString(tmp);
3505
3506 /*
3507 * We always use dollar quoting. Figure out a suitable delimiter.
3508 *
3509 * Since the user is likely to be editing the function body string, we
3510 * shouldn't use a short delimiter that he might easily create a
3511 * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3512 * if needed.
3513 */
3515 appendStringInfoChar(&dq, '$');
3516 appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3517 while (strstr(prosrc, dq.data) != NULL)
3518 appendStringInfoChar(&dq, 'x');
3519 appendStringInfoChar(&dq, '$');
3520
3521 appendBinaryStringInfo(&buf, dq.data, dq.len);
3522 appendStringInfoString(&buf, prosrc);
3523 appendBinaryStringInfo(&buf, dq.data, dq.len);
3524 }
3525
3526 appendStringInfoChar(&buf, '\n');
3527
3529
3531}
3532
3533/*
3534 * pg_get_function_arguments
3535 * Get a nicely-formatted list of arguments for a function.
3536 * This is everything that would go between the parentheses in
3537 * CREATE FUNCTION.
3538 */
3539Datum
3541{
3542 Oid funcid = PG_GETARG_OID(0);
3545
3549
3551
3552 (void) print_function_arguments(&buf, proctup, false, true);
3553
3555
3557}
3558
3559/*
3560 * pg_get_function_identity_arguments
3561 * Get a formatted list of arguments for a function.
3562 * This is everything that would go between the parentheses in
3563 * ALTER FUNCTION, etc. In particular, don't print defaults.
3564 */
3565Datum
3567{
3568 Oid funcid = PG_GETARG_OID(0);
3571
3575
3577
3578 (void) print_function_arguments(&buf, proctup, false, false);
3579
3581
3583}
3584
3585/*
3586 * pg_get_function_result
3587 * Get a nicely-formatted version of the result type of a function.
3588 * This is what would appear after RETURNS in CREATE FUNCTION.
3589 */
3590Datum
3592{
3593 Oid funcid = PG_GETARG_OID(0);
3596
3600
3601 if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
3602 {
3605 }
3606
3608
3610
3612
3614}
3615
3616/*
3617 * Guts of pg_get_function_result: append the function's return type
3618 * to the specified buffer.
3619 */
3620static void
3622{
3624 int ntabargs = 0;
3626
3628
3629 if (proc->proretset)
3630 {
3631 /* It might be a table function; try to print the arguments */
3632 appendStringInfoString(&rbuf, "TABLE(");
3634 if (ntabargs > 0)
3636 else
3638 }
3639
3640 if (ntabargs == 0)
3641 {
3642 /* Not a table function, so do the normal thing */
3643 if (proc->proretset)
3644 appendStringInfoString(&rbuf, "SETOF ");
3645 appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3646 }
3647
3648 appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
3649}
3650
3651/*
3652 * Common code for pg_get_function_arguments and pg_get_function_result:
3653 * append the desired subset of arguments to buf. We print only TABLE
3654 * arguments when print_table_args is true, and all the others when it's false.
3655 * We print argument defaults only if print_defaults is true.
3656 * Function return value is the number of arguments printed.
3657 */
3658static int
3661{
3663 int numargs;
3664 Oid *argtypes;
3665 char **argnames;
3666 char *argmodes;
3667 int insertorderbyat = -1;
3668 int argsprinted;
3669 int inputargno;
3670 int nlackdefaults;
3671 List *argdefaults = NIL;
3673 int i;
3674
3675 numargs = get_func_arg_info(proctup,
3676 &argtypes, &argnames, &argmodes);
3677
3678 nlackdefaults = numargs;
3679 if (print_defaults && proc->pronargdefaults > 0)
3680 {
3682 bool isnull;
3683
3686 &isnull);
3687 if (!isnull)
3688 {
3689 char *str;
3690
3693 pfree(str);
3695 /* nlackdefaults counts only *input* arguments lacking defaults */
3696 nlackdefaults = proc->pronargs - list_length(argdefaults);
3697 }
3698 }
3699
3700 /* Check for special treatment of ordered-set aggregates */
3701 if (proc->prokind == PROKIND_AGGREGATE)
3702 {
3705
3708 elog(ERROR, "cache lookup failed for aggregate %u",
3709 proc->oid);
3711 if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3712 insertorderbyat = agg->aggnumdirectargs;
3714 }
3715
3716 argsprinted = 0;
3717 inputargno = 0;
3718 for (i = 0; i < numargs; i++)
3719 {
3720 Oid argtype = argtypes[i];
3721 char *argname = argnames ? argnames[i] : NULL;
3723 const char *modename;
3724 bool isinput;
3725
3726 switch (argmode)
3727 {
3728 case PROARGMODE_IN:
3729
3730 /*
3731 * For procedures, explicitly mark all argument modes, so as
3732 * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3733 */
3734 if (proc->prokind == PROKIND_PROCEDURE)
3735 modename = "IN ";
3736 else
3737 modename = "";
3738 isinput = true;
3739 break;
3740 case PROARGMODE_INOUT:
3741 modename = "INOUT ";
3742 isinput = true;
3743 break;
3744 case PROARGMODE_OUT:
3745 modename = "OUT ";
3746 isinput = false;
3747 break;
3749 modename = "VARIADIC ";
3750 isinput = true;
3751 break;
3752 case PROARGMODE_TABLE:
3753 modename = "";
3754 isinput = false;
3755 break;
3756 default:
3757 elog(ERROR, "invalid parameter mode '%c'", argmode);
3758 modename = NULL; /* keep compiler quiet */
3759 isinput = false;
3760 break;
3761 }
3762 if (isinput)
3763 inputargno++; /* this is a 1-based counter */
3764
3766 continue;
3767
3769 {
3770 if (argsprinted)
3772 appendStringInfoString(buf, "ORDER BY ");
3773 }
3774 else if (argsprinted)
3776
3778 if (argname && argname[0])
3779 appendStringInfo(buf, "%s ", quote_identifier(argname));
3782 {
3783 Node *expr;
3784
3786 expr = (Node *) lfirst(nextargdefault);
3788
3789 appendStringInfo(buf, " DEFAULT %s",
3790 deparse_expression(expr, NIL, false, false));
3791 }
3792 argsprinted++;
3793
3794 /* nasty hack: print the last arg twice for variadic ordered-set agg */
3795 if (argsprinted == insertorderbyat && i == numargs - 1)
3796 {
3797 i--;
3798 /* aggs shouldn't have defaults anyway, but just to be sure ... */
3799 print_defaults = false;
3800 }
3801 }
3802
3803 return argsprinted;
3804}
3805
3806static bool
3807is_input_argument(int nth, const char *argmodes)
3808{
3809 return (!argmodes
3813}
3814
3815/*
3816 * Append used transformed types to specified buffer
3817 */
3818static void
3820{
3821 Oid *trftypes;
3822 int ntypes;
3823
3824 ntypes = get_func_trftypes(proctup, &trftypes);
3825 if (ntypes > 0)
3826 {
3827 int i;
3828
3829 appendStringInfoString(buf, " TRANSFORM ");
3830 for (i = 0; i < ntypes; i++)
3831 {
3832 if (i != 0)
3834 appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3835 }
3837 }
3838}
3839
3840/*
3841 * Get textual representation of a function argument's default value. The
3842 * second argument of this function is the argument number among all arguments
3843 * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
3844 * how information_schema.sql uses it.
3845 */
3846Datum
3848{
3849 Oid funcid = PG_GETARG_OID(0);
3852 Form_pg_proc proc;
3853 int numargs;
3854 Oid *argtypes;
3855 char **argnames;
3856 char *argmodes;
3857 int i;
3859 Node *node;
3860 char *str;
3861 int nth_inputarg;
3863 bool isnull;
3864 int nth_default;
3865
3869
3870 numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3872 {
3875 }
3876
3877 nth_inputarg = 0;
3878 for (i = 0; i < nth_arg; i++)
3880 nth_inputarg++;
3881
3884 &isnull);
3885 if (isnull)
3886 {
3889 }
3890
3893 pfree(str);
3894
3895 proc = (Form_pg_proc) GETSTRUCT(proctup);
3896
3897 /*
3898 * Calculate index into proargdefaults: proargdefaults corresponds to the
3899 * last N input arguments, where N = pronargdefaults.
3900 */
3901 nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3902
3904 {
3907 }
3909 str = deparse_expression(node, NIL, false, false);
3910
3912
3914}
3915
3916static void
3918{
3919 int numargs;
3920 Oid *argtypes;
3921 char **argnames;
3922 char *argmodes;
3923 deparse_namespace dpns = {0};
3924 Datum tmp;
3925 Node *n;
3926
3928 numargs = get_func_arg_info(proctup,
3929 &argtypes, &argnames, &argmodes);
3930 dpns.numargs = numargs;
3931 dpns.argnames = argnames;
3932
3935
3936 if (IsA(n, List))
3937 {
3938 List *stmts;
3939 ListCell *lc;
3940
3941 stmts = linitial(castNode(List, n));
3942
3943 appendStringInfoString(buf, "BEGIN ATOMIC\n");
3944
3945 foreach(lc, stmts)
3946 {
3947 Query *query = lfirst_node(Query, lc);
3948
3949 /* It seems advisable to get at least AccessShareLock on rels */
3950 AcquireRewriteLocks(query, false, false);
3951 get_query_def(query, buf, list_make1(&dpns), NULL, false,
3955 }
3956
3958 }
3959 else
3960 {
3961 Query *query = castNode(Query, n);
3962
3963 /* It seems advisable to get at least AccessShareLock on rels */
3964 AcquireRewriteLocks(query, false, false);
3965 get_query_def(query, buf, list_make1(&dpns), NULL, false,
3966 0, WRAP_COLUMN_DEFAULT, 0);
3967 }
3968}
3969
3970Datum
3972{
3973 Oid funcid = PG_GETARG_OID(0);
3976 bool isnull;
3977
3979
3980 /* Look up the function */
3984
3986 if (isnull)
3987 {
3990 }
3991
3993
3995
3997}
3998
3999
4000/*
4001 * deparse_expression - General utility for deparsing expressions
4002 *
4003 * calls deparse_expression_pretty with all prettyPrinting disabled
4004 */
4005char *
4007 bool forceprefix, bool showimplicit)
4008{
4010 showimplicit, 0, 0);
4011}
4012
4013/* ----------
4014 * deparse_expression_pretty - General utility for deparsing expressions
4015 *
4016 * expr is the node tree to be deparsed. It must be a transformed expression
4017 * tree (ie, not the raw output of gram.y).
4018 *
4019 * dpcontext is a list of deparse_namespace nodes representing the context
4020 * for interpreting Vars in the node tree. It can be NIL if no Vars are
4021 * expected.
4022 *
4023 * forceprefix is true to force all Vars to be prefixed with their table names.
4024 *
4025 * showimplicit is true to force all implicit casts to be shown explicitly.
4026 *
4027 * Tries to pretty up the output according to prettyFlags and startIndent.
4028 *
4029 * The result is a palloc'd string.
4030 * ----------
4031 */
4032static char *
4034 bool forceprefix, bool showimplicit,
4035 int prettyFlags, int startIndent)
4036{
4038 deparse_context context;
4039
4041 context.buf = &buf;
4042 context.namespaces = dpcontext;
4043 context.resultDesc = NULL;
4044 context.targetList = NIL;
4045 context.windowClause = NIL;
4046 context.varprefix = forceprefix;
4047 context.prettyFlags = prettyFlags;
4049 context.indentLevel = startIndent;
4050 context.colNamesVisible = true;
4051 context.inGroupBy = false;
4052 context.varInOrderBy = false;
4053 context.appendparents = NULL;
4054
4055 get_rule_expr(expr, &context, showimplicit);
4056
4057 return buf.data;
4058}
4059
4060/* ----------
4061 * deparse_context_for - Build deparse context for a single relation
4062 *
4063 * Given the reference name (alias) and OID of a relation, build deparsing
4064 * context for an expression referencing only that relation (as varno 1,
4065 * varlevelsup 0). This is sufficient for many uses of deparse_expression.
4066 * ----------
4067 */
4068List *
4069deparse_context_for(const char *aliasname, Oid relid)
4070{
4073
4075
4076 /* Build a minimal RTE for the rel */
4078 rte->rtekind = RTE_RELATION;
4079 rte->relid = relid;
4080 rte->relkind = RELKIND_RELATION; /* no need for exactness here */
4081 rte->rellockmode = AccessShareLock;
4082 rte->alias = makeAlias(aliasname, NIL);
4083 rte->eref = rte->alias;
4084 rte->lateral = false;
4085 rte->inh = false;
4086 rte->inFromCl = true;
4087
4088 /* Build one-element rtable */
4089 dpns->rtable = list_make1(rte);
4090 dpns->subplans = NIL;
4091 dpns->ctes = NIL;
4092 dpns->appendrels = NULL;
4095
4096 /* Return a one-deep namespace stack */
4097 return list_make1(dpns);
4098}
4099
4100/*
4101 * deparse_context_for_plan_tree - Build deparse context for a Plan tree
4102 *
4103 * When deparsing an expression in a Plan tree, we use the plan's rangetable
4104 * to resolve names of simple Vars. The initialization of column names for
4105 * this is rather expensive if the rangetable is large, and it'll be the same
4106 * for every expression in the Plan tree; so we do it just once and re-use
4107 * the result of this function for each expression. (Note that the result
4108 * is not usable until set_deparse_context_plan() is applied to it.)
4109 *
4110 * In addition to the PlannedStmt, pass the per-RTE alias names
4111 * assigned by a previous call to select_rtable_names_for_explain.
4112 */
4113List *
4114deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
4115{
4117
4119
4120 /* Initialize fields that stay the same across the whole plan tree */
4121 dpns->rtable = pstmt->rtable;
4122 dpns->rtable_names = rtable_names;
4123 dpns->subplans = pstmt->subplans;
4124 dpns->ctes = NIL;
4125 if (pstmt->appendRelations)
4126 {
4127 /* Set up the array, indexed by child relid */
4128 int ntables = list_length(dpns->rtable);
4129 ListCell *lc;
4130
4131 dpns->appendrels = (AppendRelInfo **)
4132 palloc0((ntables + 1) * sizeof(AppendRelInfo *));
4133 foreach(lc, pstmt->appendRelations)
4134 {
4136 Index crelid = appinfo->child_relid;
4137
4138 Assert(crelid > 0 && crelid <= ntables);
4139 Assert(dpns->appendrels[crelid] == NULL);
4140 dpns->appendrels[crelid] = appinfo;
4141 }
4142 }
4143 else
4144 dpns->appendrels = NULL; /* don't need it */
4145
4146 /*
4147 * Set up column name aliases, ignoring any join RTEs; they don't matter
4148 * because plan trees don't contain any join alias Vars.
4149 */
4151
4152 /* Return a one-deep namespace stack */
4153 return list_make1(dpns);
4154}
4155
4156/*
4157 * set_deparse_context_plan - Specify Plan node containing expression
4158 *
4159 * When deparsing an expression in a Plan tree, we might have to resolve
4160 * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
4161 * provide the parent Plan node. Then OUTER_VAR and INNER_VAR references
4162 * can be resolved by drilling down into the left and right child plans.
4163 * Similarly, INDEX_VAR references can be resolved by reference to the
4164 * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
4165 * ForeignScan and CustomScan nodes. (Note that we don't currently support
4166 * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
4167 * for those, we can only deparse the indexqualorig fields, which won't
4168 * contain INDEX_VAR Vars.)
4169 *
4170 * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
4171 * the most-closely-nested first. This is needed to resolve PARAM_EXEC
4172 * Params. Note we assume that all the Plan nodes share the same rtable.
4173 *
4174 * For a ModifyTable plan, we might also need to resolve references to OLD/NEW
4175 * variables in the RETURNING list, so we copy the alias names of the OLD and
4176 * NEW rows from the ModifyTable plan node.
4177 *
4178 * Once this function has been called, deparse_expression() can be called on
4179 * subsidiary expression(s) of the specified Plan node. To deparse
4180 * expressions of a different Plan node in the same Plan tree, re-call this
4181 * function to identify the new parent Plan node.
4182 *
4183 * The result is the same List passed in; this is a notational convenience.
4184 */
4185List *
4187{
4189
4190 /* Should always have one-entry namespace list for Plan deparsing */
4193
4194 /* Set our attention on the specific plan node passed in */
4195 dpns->ancestors = ancestors;
4197
4198 /* For ModifyTable, set aliases for OLD and NEW in RETURNING */
4199 if (IsA(plan, ModifyTable))
4200 {
4201 dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias;
4202 dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias;
4203 }
4204
4205 return dpcontext;
4206}
4207
4208/*
4209 * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
4210 *
4211 * Determine the relation aliases we'll use during an EXPLAIN operation.
4212 * This is just a frontend to set_rtable_names. We have to expose the aliases
4213 * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
4214 */
4215List *
4217{
4219
4220 memset(&dpns, 0, sizeof(dpns));
4221 dpns.rtable = rtable;
4222 dpns.subplans = NIL;
4223 dpns.ctes = NIL;
4224 dpns.appendrels = NULL;
4226 /* We needn't bother computing column aliases yet */
4227
4228 return dpns.rtable_names;
4229}
4230
4231/*
4232 * set_rtable_names: select RTE aliases to be used in printing a query
4233 *
4234 * We fill in dpns->rtable_names with a list of names that is one-for-one with
4235 * the already-filled dpns->rtable list. Each RTE name is unique among those
4236 * in the new namespace plus any ancestor namespaces listed in
4237 * parent_namespaces.
4238 *
4239 * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
4240 *
4241 * Note that this function is only concerned with relation names, not column
4242 * names.
4243 */
4244static void
4247{
4249 HTAB *names_hash;
4251 bool found;
4252 int rtindex;
4253 ListCell *lc;
4254
4255 dpns->rtable_names = NIL;
4256 /* nothing more to do if empty rtable */
4257 if (dpns->rtable == NIL)
4258 return;
4259
4260 /*
4261 * We use a hash table to hold known names, so that this process is O(N)
4262 * not O(N^2) for N names.
4263 */
4264 hash_ctl.keysize = NAMEDATALEN;
4265 hash_ctl.entrysize = sizeof(NameHashEntry);
4267 names_hash = hash_create("set_rtable_names names",
4268 list_length(dpns->rtable),
4269 &hash_ctl,
4271
4272 /* Preload the hash table with names appearing in parent_namespaces */
4273 foreach(lc, parent_namespaces)
4274 {
4276 ListCell *lc2;
4277
4278 foreach(lc2, olddpns->rtable_names)
4279 {
4280 char *oldname = (char *) lfirst(lc2);
4281
4282 if (oldname == NULL)
4283 continue;
4284 hentry = (NameHashEntry *) hash_search(names_hash,
4285 oldname,
4286 HASH_ENTER,
4287 &found);
4288 /* we do not complain about duplicate names in parent namespaces */
4289 hentry->counter = 0;
4290 }
4291 }
4292
4293 /* Now we can scan the rtable */
4294 rtindex = 1;
4295 foreach(lc, dpns->rtable)
4296 {
4298 char *refname;
4299
4300 /* Just in case this takes an unreasonable amount of time ... */
4302
4303 if (rels_used && !bms_is_member(rtindex, rels_used))
4304 {
4305 /* Ignore unreferenced RTE */
4306 refname = NULL;
4307 }
4308 else if (rte->alias)
4309 {
4310 /* If RTE has a user-defined alias, prefer that */
4311 refname = rte->alias->aliasname;
4312 }
4313 else if (rte->rtekind == RTE_RELATION)
4314 {
4315 /* Use the current actual name of the relation */
4316 refname = get_rel_name(rte->relid);
4317 }
4318 else if (rte->rtekind == RTE_JOIN)
4319 {
4320 /* Unnamed join has no refname */
4321 refname = NULL;
4322 }
4323 else
4324 {
4325 /* Otherwise use whatever the parser assigned */
4326 refname = rte->eref->aliasname;
4327 }
4328
4329 /*
4330 * If the selected name isn't unique, append digits to make it so, and
4331 * make a new hash entry for it once we've got a unique name. For a
4332 * very long input name, we might have to truncate to stay within
4333 * NAMEDATALEN.
4334 */
4335 if (refname)
4336 {
4337 hentry = (NameHashEntry *) hash_search(names_hash,
4338 refname,
4339 HASH_ENTER,
4340 &found);
4341 if (found)
4342 {
4343 /* Name already in use, must choose a new one */
4344 int refnamelen = strlen(refname);
4345 char *modname = (char *) palloc(refnamelen + 16);
4347
4348 do
4349 {
4350 hentry->counter++;
4351 for (;;)
4352 {
4353 memcpy(modname, refname, refnamelen);
4354 sprintf(modname + refnamelen, "_%d", hentry->counter);
4355 if (strlen(modname) < NAMEDATALEN)
4356 break;
4357 /* drop chars from refname to keep all the digits */
4359 refnamelen - 1);
4360 }
4361 hentry2 = (NameHashEntry *) hash_search(names_hash,
4362 modname,
4363 HASH_ENTER,
4364 &found);
4365 } while (found);
4366 hentry2->counter = 0; /* init new hash entry */
4367 refname = modname;
4368 }
4369 else
4370 {
4371 /* Name not previously used, need only initialize hentry */
4372 hentry->counter = 0;
4373 }
4374 }
4375
4376 dpns->rtable_names = lappend(dpns->rtable_names, refname);
4377 rtindex++;
4378 }
4379
4380 hash_destroy(names_hash);
4381}
4382
4383/*
4384 * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
4385 *
4386 * For convenience, this is defined to initialize the deparse_namespace struct
4387 * from scratch.
4388 */
4389static void
4392{
4393 ListCell *lc;
4394 ListCell *lc2;
4395
4396 /* Initialize *dpns and fill rtable/ctes links */
4397 memset(dpns, 0, sizeof(deparse_namespace));
4398 dpns->rtable = query->rtable;
4399 dpns->subplans = NIL;
4400 dpns->ctes = query->cteList;
4401 dpns->appendrels = NULL;
4402 dpns->ret_old_alias = query->returningOldAlias;
4403 dpns->ret_new_alias = query->returningNewAlias;
4404
4405 /* Assign a unique relation alias to each RTE */
4407
4408 /* Initialize dpns->rtable_columns to contain zeroed structs */
4409 dpns->rtable_columns = NIL;
4410 while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4411 dpns->rtable_columns = lappend(dpns->rtable_columns,
4412 palloc0(sizeof(deparse_columns)));
4413
4414 /* If it's a utility query, it won't have a jointree */
4415 if (query->jointree)
4416 {
4417 /* Detect whether global uniqueness of USING names is needed */
4418 dpns->unique_using =
4420
4421 /*
4422 * Select names for columns merged by USING, via a recursive pass over
4423 * the query jointree.
4424 */
4425 set_using_names(dpns, (Node *) query->jointree, NIL);
4426 }
4427
4428 /*
4429 * Now assign remaining column aliases for each RTE. We do this in a
4430 * linear scan of the rtable, so as to process RTEs whether or not they
4431 * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4432 * etc). JOIN RTEs must be processed after their children, but this is
4433 * okay because they appear later in the rtable list than their children
4434 * (cf Asserts in identify_join_columns()).
4435 */
4436 forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4437 {
4440
4441 if (rte->rtekind == RTE_JOIN)
4443 else
4445 }
4446}
4447
4448/*
4449 * set_simple_column_names: fill in column aliases for non-query situations
4450 *
4451 * This handles EXPLAIN and cases where we only have relation RTEs. Without
4452 * a join tree, we can't do anything smart about join RTEs, but we don't
4453 * need to, because EXPLAIN should never see join alias Vars anyway.
4454 * If we find a join RTE we'll just skip it, leaving its deparse_columns
4455 * struct all-zero. If somehow we try to deparse a join alias Var, we'll
4456 * error out cleanly because the struct's num_cols will be zero.
4457 */
4458static void
4460{
4461 ListCell *lc;
4462 ListCell *lc2;
4463
4464 /* Initialize dpns->rtable_columns to contain zeroed structs */
4465 dpns->rtable_columns = NIL;
4466 while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4467 dpns->rtable_columns = lappend(dpns->rtable_columns,
4468 palloc0(sizeof(deparse_columns)));
4469
4470 /* Assign unique column aliases within each non-join RTE */
4471 forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4472 {
4475
4476 if (rte->rtekind != RTE_JOIN)
4478 }
4479}
4480
4481/*
4482 * has_dangerous_join_using: search jointree for unnamed JOIN USING
4483 *
4484 * Merged columns of a JOIN USING may act differently from either of the input
4485 * columns, either because they are merged with COALESCE (in a FULL JOIN) or
4486 * because an implicit coercion of the underlying input column is required.
4487 * In such a case the column must be referenced as a column of the JOIN not as
4488 * a column of either input. And this is problematic if the join is unnamed
4489 * (alias-less): we cannot qualify the column's name with an RTE name, since
4490 * there is none. (Forcibly assigning an alias to the join is not a solution,
4491 * since that will prevent legal references to tables below the join.)
4492 * To ensure that every column in the query is unambiguously referenceable,
4493 * we must assign such merged columns names that are globally unique across
4494 * the whole query, aliasing other columns out of the way as necessary.
4495 *
4496 * Because the ensuing re-aliasing is fairly damaging to the readability of
4497 * the query, we don't do this unless we have to. So, we must pre-scan
4498 * the join tree to see if we have to, before starting set_using_names().
4499 */
4500static bool
4502{
4503 if (IsA(jtnode, RangeTblRef))
4504 {
4505 /* nothing to do here */
4506 }
4507 else if (IsA(jtnode, FromExpr))
4508 {
4509 FromExpr *f = (FromExpr *) jtnode;
4510 ListCell *lc;
4511
4512 foreach(lc, f->fromlist)
4513 {
4515 return true;
4516 }
4517 }
4518 else if (IsA(jtnode, JoinExpr))
4519 {
4520 JoinExpr *j = (JoinExpr *) jtnode;
4521
4522 /* Is it an unnamed JOIN with USING? */
4523 if (j->alias == NULL && j->usingClause)
4524 {
4525 /*
4526 * Yes, so check each join alias var to see if any of them are not
4527 * simple references to underlying columns. If so, we have a
4528 * dangerous situation and must pick unique aliases.
4529 */
4530 RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4531
4532 /* We need only examine the merged columns */
4533 for (int i = 0; i < jrte->joinmergedcols; i++)
4534 {
4535 Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4536
4537 if (!IsA(aliasvar, Var))
4538 return true;
4539 }
4540 }
4541
4542 /* Nope, but inspect children */
4543 if (has_dangerous_join_using(dpns, j->larg))
4544 return true;
4545 if (has_dangerous_join_using(dpns, j->rarg))
4546 return true;
4547 }
4548 else
4549 elog(ERROR, "unrecognized node type: %d",
4550 (int) nodeTag(jtnode));
4551 return false;
4552}
4553
4554/*
4555 * set_using_names: select column aliases to be used for merged USING columns
4556 *
4557 * We do this during a recursive descent of the query jointree.
4558 * dpns->unique_using must already be set to determine the global strategy.
4559 *
4560 * Column alias info is saved in the dpns->rtable_columns list, which is
4561 * assumed to be filled with pre-zeroed deparse_columns structs.
4562 *
4563 * parentUsing is a list of all USING aliases assigned in parent joins of
4564 * the current jointree node. (The passed-in list must not be modified.)
4565 *
4566 * Note that we do not use per-deparse_columns hash tables in this function.
4567 * The number of names that need to be assigned should be small enough that
4568 * we don't need to trouble with that.
4569 */
4570static void
4571set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
4572{
4573 if (IsA(jtnode, RangeTblRef))
4574 {
4575 /* nothing to do now */
4576 }
4577 else if (IsA(jtnode, FromExpr))
4578 {
4579 FromExpr *f = (FromExpr *) jtnode;
4580 ListCell *lc;
4581
4582 foreach(lc, f->fromlist)
4583 set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4584 }
4585 else if (IsA(jtnode, JoinExpr))
4586 {
4587 JoinExpr *j = (JoinExpr *) jtnode;
4588 RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4590 int *leftattnos;
4591 int *rightattnos;
4594 int i;
4595 ListCell *lc;
4596
4597 /* Get info about the shape of the join */
4599 leftattnos = colinfo->leftattnos;
4600 rightattnos = colinfo->rightattnos;
4601
4602 /* Look up the not-yet-filled-in child deparse_columns structs */
4605
4606 /*
4607 * If this join is unnamed, then we cannot substitute new aliases at
4608 * this level, so any name requirements pushed down to here must be
4609 * pushed down again to the children.
4610 */
4611 if (rte->alias == NULL)
4612 {
4613 for (i = 0; i < colinfo->num_cols; i++)
4614 {
4615 char *colname = colinfo->colnames[i];
4616
4617 if (colname == NULL)
4618 continue;
4619
4620 /* Push down to left column, unless it's a system column */
4621 if (leftattnos[i] > 0)
4622 {
4624 leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4625 }
4626
4627 /* Same on the righthand side */
4628 if (rightattnos[i] > 0)
4629 {
4631 rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4632 }
4633 }
4634 }
4635
4636 /*
4637 * If there's a USING clause, select the USING column names and push
4638 * those names down to the children. We have two strategies:
4639 *
4640 * If dpns->unique_using is true, we force all USING names to be
4641 * unique across the whole query level. In principle we'd only need
4642 * the names of dangerous USING columns to be globally unique, but to
4643 * safely assign all USING names in a single pass, we have to enforce
4644 * the same uniqueness rule for all of them. However, if a USING
4645 * column's name has been pushed down from the parent, we should use
4646 * it as-is rather than making a uniqueness adjustment. This is
4647 * necessary when we're at an unnamed join, and it creates no risk of
4648 * ambiguity. Also, if there's a user-written output alias for a
4649 * merged column, we prefer to use that rather than the input name;
4650 * this simplifies the logic and seems likely to lead to less aliasing
4651 * overall.
4652 *
4653 * If dpns->unique_using is false, we only need USING names to be
4654 * unique within their own join RTE. We still need to honor
4655 * pushed-down names, though.
4656 *
4657 * Though significantly different in results, these two strategies are
4658 * implemented by the same code, with only the difference of whether
4659 * to put assigned names into dpns->using_names.
4660 */
4661 if (j->usingClause)
4662 {
4663 /* Copy the input parentUsing list so we don't modify it */
4664 parentUsing = list_copy(parentUsing);
4665
4666 /* USING names must correspond to the first join output columns */
4668 i = 0;
4669 foreach(lc, j->usingClause)
4670 {
4671 char *colname = strVal(lfirst(lc));
4672
4673 /* Assert it's a merged column */
4674 Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4675
4676 /* Adopt passed-down name if any, else select unique name */
4677 if (colinfo->colnames[i] != NULL)
4678 colname = colinfo->colnames[i];
4679 else
4680 {
4681 /* Prefer user-written output alias if any */
4682 if (rte->alias && i < list_length(rte->alias->colnames))
4683 colname = strVal(list_nth(rte->alias->colnames, i));
4684 /* Make it appropriately unique */
4685 colname = make_colname_unique(colname, dpns, colinfo);
4686 if (dpns->unique_using)
4687 dpns->using_names = lappend(dpns->using_names,
4688 colname);
4689 /* Save it as output column name, too */
4690 colinfo->colnames[i] = colname;
4691 }
4692
4693 /* Remember selected names for use later */
4694 colinfo->usingNames = lappend(colinfo->usingNames, colname);
4695 parentUsing = lappend(parentUsing, colname);
4696
4697 /* Push down to left column, unless it's a system column */
4698 if (leftattnos[i] > 0)
4699 {
4701 leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4702 }
4703
4704 /* Same on the righthand side */
4705 if (rightattnos[i] > 0)
4706 {
4708 rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4709 }
4710
4711 i++;
4712 }
4713 }
4714
4715 /* Mark child deparse_columns structs with correct parentUsing info */
4716 leftcolinfo->parentUsing = parentUsing;
4717 rightcolinfo->parentUsing = parentUsing;
4718
4719 /* Now recursively assign USING column names in children */
4720 set_using_names(dpns, j->larg, parentUsing);
4721 set_using_names(dpns, j->rarg, parentUsing);
4722 }
4723 else
4724 elog(ERROR, "unrecognized node type: %d",
4725 (int) nodeTag(jtnode));
4726}
4727
4728/*
4729 * set_relation_column_names: select column aliases for a non-join RTE
4730 *
4731 * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4732 * If any colnames entries are already filled in, those override local
4733 * choices.
4734 */
4735static void
4738{
4739 int ncolumns;
4740 char **real_colnames;
4741 bool changed_any;
4742 int noldcolumns;
4743 int i;
4744 int j;
4745
4746 /*
4747 * Construct an array of the current "real" column names of the RTE.
4748 * real_colnames[] will be indexed by physical column number, with NULL
4749 * entries for dropped columns.
4750 */
4751 if (rte->rtekind == RTE_RELATION)
4752 {
4753 /* Relation --- look to the system catalogs for up-to-date info */
4754 Relation rel;
4755 TupleDesc tupdesc;
4756
4757 rel = relation_open(rte->relid, AccessShareLock);
4758 tupdesc = RelationGetDescr(rel);
4759
4760 ncolumns = tupdesc->natts;
4761 real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4762
4763 for (i = 0; i < ncolumns; i++)
4764 {
4765 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4766
4767 if (attr->attisdropped)
4768 real_colnames[i] = NULL;
4769 else
4770 real_colnames[i] = pstrdup(NameStr(attr->attname));
4771 }
4773 }
4774 else
4775 {
4776 /* Otherwise get the column names from eref or expandRTE() */
4777 List *colnames;
4778 ListCell *lc;
4779
4780 /*
4781 * Functions returning composites have the annoying property that some
4782 * of the composite type's columns might have been dropped since the
4783 * query was parsed. If possible, use expandRTE() to handle that
4784 * case, since it has the tedious logic needed to find out about
4785 * dropped columns. However, if we're explaining a plan, then we
4786 * don't have rte->functions because the planner thinks that won't be
4787 * needed later, and that breaks expandRTE(). So in that case we have
4788 * to rely on rte->eref, which may lead us to report a dropped
4789 * column's old name; that seems close enough for EXPLAIN's purposes.
4790 *
4791 * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4792 * which should be sufficiently up-to-date: no other RTE types can
4793 * have columns get dropped from under them after parsing.
4794 */
4795 if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4796 {
4797 /* Since we're not creating Vars, rtindex etc. don't matter */
4799 true /* include dropped */ , &colnames, NULL);
4800 }
4801 else
4802 colnames = rte->eref->colnames;
4803
4804 ncolumns = list_length(colnames);
4805 real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4806
4807 i = 0;
4808 foreach(lc, colnames)
4809 {
4810 /*
4811 * If the column name we find here is an empty string, then it's a
4812 * dropped column, so change to NULL.
4813 */
4814 char *cname = strVal(lfirst(lc));
4815
4816 if (cname[0] == '\0')
4817 cname = NULL;
4819 i++;
4820 }
4821 }
4822
4823 /*
4824 * Ensure colinfo->colnames has a slot for each column. (It could be long
4825 * enough already, if we pushed down a name for the last column.) Note:
4826 * it's possible that there are now more columns than there were when the
4827 * query was parsed, ie colnames could be longer than rte->eref->colnames.
4828 * We must assign unique aliases to the new columns too, else there could
4829 * be unresolved conflicts when the view/rule is reloaded.
4830 */
4832 Assert(colinfo->num_cols == ncolumns);
4833
4834 /*
4835 * Make sufficiently large new_colnames and is_new_col arrays, too.
4836 *
4837 * Note: because we leave colinfo->num_new_cols zero until after the loop,
4838 * colname_is_unique will not consult that array, which is fine because it
4839 * would only be duplicate effort.
4840 */
4841 colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4842 colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4843
4844 /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4846
4847 /*
4848 * Scan the columns, select a unique alias for each one, and store it in
4849 * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4850 * entries for dropped columns, the latter omits them. Also mark
4851 * new_colnames entries as to whether they are new since parse time; this
4852 * is the case for entries beyond the length of rte->eref->colnames.
4853 */
4854 noldcolumns = list_length(rte->eref->colnames);
4855 changed_any = false;
4856 j = 0;
4857 for (i = 0; i < ncolumns; i++)
4858 {
4859 char *real_colname = real_colnames[i];
4860 char *colname = colinfo->colnames[i];
4861
4862 /* Skip dropped columns */
4863 if (real_colname == NULL)
4864 {
4865 Assert(colname == NULL); /* colnames[i] is already NULL */
4866 continue;
4867 }
4868
4869 /* If alias already assigned, that's what to use */
4870 if (colname == NULL)
4871 {
4872 /* If user wrote an alias, prefer that over real column name */
4873 if (rte->alias && i < list_length(rte->alias->colnames))
4874 colname = strVal(list_nth(rte->alias->colnames, i));
4875 else
4876 colname = real_colname;
4877
4878 /* Unique-ify and insert into colinfo */
4879 colname = make_colname_unique(colname, dpns, colinfo);
4880
4881 colinfo->colnames[i] = colname;
4882 add_to_names_hash(colinfo, colname);
4883 }
4884
4885 /* Put names of non-dropped columns in new_colnames[] too */
4886 colinfo->new_colnames[j] = colname;
4887 /* And mark them as new or not */
4888 colinfo->is_new_col[j] = (i >= noldcolumns);
4889 j++;
4890
4891 /* Remember if any assigned aliases differ from "real" name */
4892 if (!changed_any && strcmp(colname, real_colname) != 0)
4893 changed_any = true;
4894 }
4895
4896 /* We're now done needing the colinfo's names_hash */
4898
4899 /*
4900 * Set correct length for new_colnames[] array. (Note: if columns have
4901 * been added, colinfo->num_cols includes them, which is not really quite
4902 * right but is harmless, since any new columns must be at the end where
4903 * they won't affect varattnos of pre-existing columns.)
4904 */
4905 colinfo->num_new_cols = j;
4906
4907 /*
4908 * For a relation RTE, we need only print the alias column names if any
4909 * are different from the underlying "real" names. For a function RTE,
4910 * always emit a complete column alias list; this is to protect against
4911 * possible instability of the default column names (eg, from altering
4912 * parameter names). For tablefunc RTEs, we never print aliases, because
4913 * the column names are part of the clause itself. For other RTE types,
4914 * print if we changed anything OR if there were user-written column
4915 * aliases (since the latter would be part of the underlying "reality").
4916 */
4917 if (rte->rtekind == RTE_RELATION)
4918 colinfo->printaliases = changed_any;
4919 else if (rte->rtekind == RTE_FUNCTION)
4920 colinfo->printaliases = true;
4921 else if (rte->rtekind == RTE_TABLEFUNC)
4922 colinfo->printaliases = false;
4923 else if (rte->alias && rte->alias->colnames != NIL)
4924 colinfo->printaliases = true;
4925 else
4926 colinfo->printaliases = changed_any;
4927}
4928
4929/*
4930 * set_join_column_names: select column aliases for a join RTE
4931 *
4932 * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4933 * If any colnames entries are already filled in, those override local
4934 * choices. Also, names for USING columns were already chosen by
4935 * set_using_names(). We further expect that column alias selection has been
4936 * completed for both input RTEs.
4937 */
4938static void
4941{
4944 bool changed_any;
4945 int noldcolumns;
4946 int nnewcolumns;
4949 int i;
4950 int j;
4951 int ic;
4952 int jc;
4953
4954 /* Look up the previously-filled-in child deparse_columns structs */
4957
4958 /*
4959 * Ensure colinfo->colnames has a slot for each column. (It could be long
4960 * enough already, if we pushed down a name for the last column.) Note:
4961 * it's possible that one or both inputs now have more columns than there
4962 * were when the query was parsed, but we'll deal with that below. We
4963 * only need entries in colnames for pre-existing columns.
4964 */
4965 noldcolumns = list_length(rte->eref->colnames);
4967 Assert(colinfo->num_cols == noldcolumns);
4968
4969 /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4971
4972 /*
4973 * Scan the join output columns, select an alias for each one, and store
4974 * it in colinfo->colnames. If there are USING columns, set_using_names()
4975 * already selected their names, so we can start the loop at the first
4976 * non-merged column.
4977 */
4978 changed_any = false;
4979 for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4980 {
4981 char *colname = colinfo->colnames[i];
4982 char *real_colname;
4983
4984 /* Join column must refer to at least one input column */
4985 Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4986
4987 /* Get the child column name */
4988 if (colinfo->leftattnos[i] > 0)
4989 real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4990 else if (colinfo->rightattnos[i] > 0)
4991 real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4992 else
4993 {
4994 /* We're joining system columns --- use eref name */
4995 real_colname = strVal(list_nth(rte->eref->colnames, i));
4996 }
4997
4998 /* If child col has been dropped, no need to assign a join colname */
4999 if (real_colname == NULL)
5000 {
5001 colinfo->colnames[i] = NULL;
5002 continue;
5003 }
5004
5005 /* In an unnamed join, just report child column names as-is */
5006 if (rte->alias == NULL)
5007 {
5008 colinfo->colnames[i] = real_colname;
5010 continue;
5011 }
5012
5013 /* If alias already assigned, that's what to use */
5014 if (colname == NULL)
5015 {
5016 /* If user wrote an alias, prefer that over real column name */
5017 if (rte->alias && i < list_length(rte->alias->colnames))
5018 colname = strVal(list_nth(rte->alias->colnames, i));
5019 else
5020 colname = real_colname;
5021
5022 /* Unique-ify and insert into colinfo */
5023 colname = make_colname_unique(colname, dpns, colinfo);
5024
5025 colinfo->colnames[i] = colname;
5026 add_to_names_hash(colinfo, colname);
5027 }
5028
5029 /* Remember if any assigned aliases differ from "real" name */
5030 if (!changed_any && strcmp(colname, real_colname) != 0)
5031 changed_any = true;
5032 }
5033
5034 /*
5035 * Calculate number of columns the join would have if it were re-parsed
5036 * now, and create storage for the new_colnames and is_new_col arrays.
5037 *
5038 * Note: colname_is_unique will be consulting new_colnames[] during the
5039 * loops below, so its not-yet-filled entries must be zeroes.
5040 */
5041 nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
5042 list_length(colinfo->usingNames);
5043 colinfo->num_new_cols = nnewcolumns;
5044 colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
5045 colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
5046
5047 /*
5048 * Generating the new_colnames array is a bit tricky since any new columns
5049 * added since parse time must be inserted in the right places. This code
5050 * must match the parser, which will order a join's columns as merged
5051 * columns first (in USING-clause order), then non-merged columns from the
5052 * left input (in attnum order), then non-merged columns from the right
5053 * input (ditto). If one of the inputs is itself a join, its columns will
5054 * be ordered according to the same rule, which means newly-added columns
5055 * might not be at the end. We can figure out what's what by consulting
5056 * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
5057 *
5058 * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
5059 * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
5060 * meanings for the current child RTE.
5061 */
5062
5063 /* Handle merged columns; they are first and can't be new */
5064 i = j = 0;
5065 while (i < noldcolumns &&
5066 colinfo->leftattnos[i] != 0 &&
5067 colinfo->rightattnos[i] != 0)
5068 {
5069 /* column name is already determined and known unique */
5070 colinfo->new_colnames[j] = colinfo->colnames[i];
5071 colinfo->is_new_col[j] = false;
5072
5073 /* build bitmapsets of child attnums of merged columns */
5074 if (colinfo->leftattnos[i] > 0)
5075 leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
5076 if (colinfo->rightattnos[i] > 0)
5078
5079 i++, j++;
5080 }
5081
5082 /* Handle non-merged left-child columns */
5083 ic = 0;
5084 for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
5085 {
5086 char *child_colname = leftcolinfo->new_colnames[jc];
5087
5088 if (!leftcolinfo->is_new_col[jc])
5089 {
5090 /* Advance ic to next non-dropped old column of left child */
5091 while (ic < leftcolinfo->num_cols &&
5092 leftcolinfo->colnames[ic] == NULL)
5093 ic++;
5094 Assert(ic < leftcolinfo->num_cols);
5095 ic++;
5096 /* If it is a merged column, we already processed it */
5098 continue;
5099 /* Else, advance i to the corresponding existing join column */
5100 while (i < colinfo->num_cols &&
5101 colinfo->colnames[i] == NULL)
5102 i++;
5103 Assert(i < colinfo->num_cols);
5104 Assert(ic == colinfo->leftattnos[i]);
5105 /* Use the already-assigned name of this column */
5106 colinfo->new_colnames[j] = colinfo->colnames[i];
5107 i++;
5108 }
5109 else
5110 {
5111 /*
5112 * Unique-ify the new child column name and assign, unless we're
5113 * in an unnamed join, in which case just copy
5114 */
5115 if (rte->alias != NULL)
5116 {
5117 colinfo->new_colnames[j] =
5119 if (!changed_any &&
5120 strcmp(colinfo->new_colnames[j], child_colname) != 0)
5121 changed_any = true;
5122 }
5123 else
5124 colinfo->new_colnames[j] = child_colname;
5125 add_to_names_hash(colinfo, colinfo->new_colnames[j]);
5126 }
5127
5128 colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
5129 j++;
5130 }
5131
5132 /* Handle non-merged right-child columns in exactly the same way */
5133 ic = 0;
5134 for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
5135 {
5136 char *child_colname = rightcolinfo->new_colnames[jc];
5137
5138 if (!rightcolinfo->is_new_col[jc])
5139 {
5140 /* Advance ic to next non-dropped old column of right child */
5141 while (ic < rightcolinfo->num_cols &&
5142 rightcolinfo->colnames[ic] == NULL)
5143 ic++;
5144 Assert(ic < rightcolinfo->num_cols);
5145 ic++;
5146 /* If it is a merged column, we already processed it */
5148 continue;
5149 /* Else, advance i to the corresponding existing join column */
5150 while (i < colinfo->num_cols &&
5151 colinfo->colnames[i] == NULL)
5152 i++;
5153 Assert(i < colinfo->num_cols);
5154 Assert(ic == colinfo->rightattnos[i]);
5155 /* Use the already-assigned name of this column */
5156 colinfo->new_colnames[j] = colinfo->colnames[i];
5157 i++;
5158 }
5159 else
5160 {
5161 /*
5162 * Unique-ify the new child column name and assign, unless we're
5163 * in an unnamed join, in which case just copy
5164 */
5165 if (rte->alias != NULL)
5166 {
5167 colinfo->new_colnames[j] =
5169 if (!changed_any &&
5170 strcmp(colinfo->new_colnames[j], child_colname) != 0)
5171 changed_any = true;
5172 }
5173 else
5174 colinfo->new_colnames[j] = child_colname;
5175 add_to_names_hash(colinfo, colinfo->new_colnames[j]);
5176 }
5177
5178 colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
5179 j++;
5180 }
5181
5182 /* Assert we processed the right number of columns */
5183#ifdef USE_ASSERT_CHECKING
5184 while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
5185 i++;
5186 Assert(i == colinfo->num_cols);
5187 Assert(j == nnewcolumns);
5188#endif
5189
5190 /* We're now done needing the colinfo's names_hash */
5192
5193 /*
5194 * For a named join, print column aliases if we changed any from the child
5195 * names. Unnamed joins cannot print aliases.
5196 */
5197 if (rte->alias != NULL)
5198 colinfo->printaliases = changed_any;
5199 else
5200 colinfo->printaliases = false;
5201}
5202
5203/*
5204 * colname_is_unique: is colname distinct from already-chosen column names?
5205 *
5206 * dpns is query-wide info, colinfo is for the column's RTE
5207 */
5208static bool
5209colname_is_unique(const char *colname, deparse_namespace *dpns,
5211{
5212 int i;
5213 ListCell *lc;
5214
5215 /*
5216 * If we have a hash table, consult that instead of linearly scanning the
5217 * colinfo's strings.
5218 */
5219 if (colinfo->names_hash)
5220 {
5221 if (hash_search(colinfo->names_hash,
5222 colname,
5223 HASH_FIND,
5224 NULL) != NULL)
5225 return false;
5226 }
5227 else
5228 {
5229 /* Check against already-assigned column aliases within RTE */
5230 for (i = 0; i < colinfo->num_cols; i++)
5231 {
5232 char *oldname = colinfo->colnames[i];
5233
5234 if (oldname && strcmp(oldname, colname) == 0)
5235 return false;
5236 }
5237
5238 /*
5239 * If we're building a new_colnames array, check that too (this will
5240 * be partially but not completely redundant with the previous checks)
5241 */
5242 for (i = 0; i < colinfo->num_new_cols; i++)
5243 {
5244 char *oldname = colinfo->new_colnames[i];
5245
5246 if (oldname && strcmp(oldname, colname) == 0)
5247 return false;
5248 }
5249
5250 /*
5251 * Also check against names already assigned for parent-join USING
5252 * cols
5253 */
5254 foreach(lc, colinfo->parentUsing)
5255 {
5256 char *oldname = (char *) lfirst(lc);
5257
5258 if (strcmp(oldname, colname) == 0)
5259 return false;
5260 }
5261 }
5262
5263 /*
5264 * Also check against USING-column names that must be globally unique.
5265 * These are not hashed, but there should be few of them.
5266 */
5267 foreach(lc, dpns->using_names)
5268 {
5269 char *oldname = (char *) lfirst(lc);
5270
5271 if (strcmp(oldname, colname) == 0)
5272 return false;
5273 }
5274
5275 return true;
5276}
5277
5278/*
5279 * make_colname_unique: modify colname if necessary to make it unique
5280 *
5281 * dpns is query-wide info, colinfo is for the column's RTE
5282 */
5283static char *
5286{
5287 /*
5288 * If the selected name isn't unique, append digits to make it so. For a
5289 * very long input name, we might have to truncate to stay within
5290 * NAMEDATALEN.
5291 */
5292 if (!colname_is_unique(colname, dpns, colinfo))
5293 {
5294 int colnamelen = strlen(colname);
5295 char *modname = (char *) palloc(colnamelen + 16);
5296 int i = 0;
5297
5298 do
5299 {
5300 i++;
5301 for (;;)
5302 {
5303 memcpy(modname, colname, colnamelen);
5304 sprintf(modname + colnamelen, "_%d", i);
5305 if (strlen(modname) < NAMEDATALEN)
5306 break;
5307 /* drop chars from colname to keep all the digits */
5309 colnamelen - 1);
5310 }
5311 } while (!colname_is_unique(modname, dpns, colinfo));
5312 colname = modname;
5313 }
5314 return colname;
5315}
5316
5317/*
5318 * expand_colnames_array_to: make colinfo->colnames at least n items long
5319 *
5320 * Any added array entries are initialized to zero.
5321 */
5322static void
5324{
5325 if (n > colinfo->num_cols)
5326 {
5327 if (colinfo->colnames == NULL)
5328 colinfo->colnames = palloc0_array(char *, n);
5329 else
5330 colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
5331 colinfo->num_cols = n;
5332 }
5333}
5334
5335/*
5336 * build_colinfo_names_hash: optionally construct a hash table for colinfo
5337 */
5338static void
5340{
5342 int i;
5343 ListCell *lc;
5344
5345 /*
5346 * Use a hash table only for RTEs with at least 32 columns. (The cutoff
5347 * is somewhat arbitrary, but let's choose it so that this code does get
5348 * exercised in the regression tests.)
5349 */
5350 if (colinfo->num_cols < 32)
5351 return;
5352
5353 /*
5354 * Set up the hash table. The entries are just strings with no other
5355 * payload.
5356 */
5357 hash_ctl.keysize = NAMEDATALEN;
5358 hash_ctl.entrysize = NAMEDATALEN;
5360 colinfo->names_hash = hash_create("deparse_columns names",
5361 colinfo->num_cols + colinfo->num_new_cols,
5362 &hash_ctl,
5364
5365 /*
5366 * Preload the hash table with any names already present (these would have
5367 * come from set_using_names).
5368 */
5369 for (i = 0; i < colinfo->num_cols; i++)
5370 {
5371 char *oldname = colinfo->colnames[i];
5372
5373 if (oldname)
5375 }
5376
5377 for (i = 0; i < colinfo->num_new_cols; i++)
5378 {
5379 char *oldname = colinfo->new_colnames[i];
5380
5381 if (oldname)
5383 }
5384
5385 foreach(lc, colinfo->parentUsing)
5386 {
5387 char *oldname = (char *) lfirst(lc);
5388
5390 }
5391}
5392
5393/*
5394 * add_to_names_hash: add a string to the names_hash, if we're using one
5395 */
5396static void
5398{
5399 if (colinfo->names_hash)
5400 (void) hash_search(colinfo->names_hash,
5401 name,
5402 HASH_ENTER,
5403 NULL);
5404}
5405
5406/*
5407 * destroy_colinfo_names_hash: destroy hash table when done with it
5408 */
5409static void
5411{
5412 if (colinfo->names_hash)
5413 {
5414 hash_destroy(colinfo->names_hash);
5415 colinfo->names_hash = NULL;
5416 }
5417}
5418
5419/*
5420 * identify_join_columns: figure out where columns of a join come from
5421 *
5422 * Fills the join-specific fields of the colinfo struct, except for
5423 * usingNames which is filled later.
5424 */
5425static void
5428{
5429 int numjoincols;
5430 int jcolno;
5431 int rcolno;
5432 ListCell *lc;
5433
5434 /* Extract left/right child RT indexes */
5435 if (IsA(j->larg, RangeTblRef))
5436 colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
5437 else if (IsA(j->larg, JoinExpr))
5438 colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
5439 else
5440 elog(ERROR, "unrecognized node type in jointree: %d",
5441 (int) nodeTag(j->larg));
5442 if (IsA(j->rarg, RangeTblRef))
5443 colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
5444 else if (IsA(j->rarg, JoinExpr))
5445 colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
5446 else
5447 elog(ERROR, "unrecognized node type in jointree: %d",
5448 (int) nodeTag(j->rarg));
5449
5450 /* Assert children will be processed earlier than join in second pass */
5451 Assert(colinfo->leftrti < j->rtindex);
5452 Assert(colinfo->rightrti < j->rtindex);
5453
5454 /* Initialize result arrays with zeroes */
5455 numjoincols = list_length(jrte->joinaliasvars);
5456 Assert(numjoincols == list_length(jrte->eref->colnames));
5457 colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
5458 colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
5459
5460 /*
5461 * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
5462 * Recall that the column(s) merged due to USING are the first column(s)
5463 * of the join output. We need not do anything special while scanning
5464 * joinleftcols, but while scanning joinrightcols we must distinguish
5465 * merged from unmerged columns.
5466 */
5467 jcolno = 0;
5468 foreach(lc, jrte->joinleftcols)
5469 {
5470 int leftattno = lfirst_int(lc);
5471
5472 colinfo->leftattnos[jcolno++] = leftattno;
5473 }
5474 rcolno = 0;
5475 foreach(lc, jrte->joinrightcols)
5476 {
5477 int rightattno = lfirst_int(lc);
5478
5479 if (rcolno < jrte->joinmergedcols) /* merged column? */
5480 colinfo->rightattnos[rcolno] = rightattno;
5481 else
5482 colinfo->rightattnos[jcolno++] = rightattno;
5483 rcolno++;
5484 }
5486}
5487
5488/*
5489 * get_rtable_name: convenience function to get a previously assigned RTE alias
5490 *
5491 * The RTE must belong to the topmost namespace level in "context".
5492 */
5493static char *
5494get_rtable_name(int rtindex, deparse_context *context)
5495{
5497
5498 Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
5499 return (char *) list_nth(dpns->rtable_names, rtindex - 1);
5500}
5501
5502/*
5503 * set_deparse_plan: set up deparse_namespace to parse subexpressions
5504 * of a given Plan node
5505 *
5506 * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
5507 * and index_tlist fields. Caller must already have adjusted the ancestors
5508 * list if necessary. Note that the rtable, subplans, and ctes fields do
5509 * not need to change when shifting attention to different plan nodes in a
5510 * single plan tree.
5511 */
5512static void
5514{
5515 dpns->plan = plan;
5516
5517 /*
5518 * We special-case Append and MergeAppend to pretend that the first child
5519 * plan is the OUTER referent; we have to interpret OUTER Vars in their
5520 * tlists according to one of the children, and the first one is the most
5521 * natural choice.
5522 */
5523 if (IsA(plan, Append))
5524 dpns->outer_plan = linitial(((Append *) plan)->appendplans);
5525 else if (IsA(plan, MergeAppend))
5526 dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
5527 else
5528 dpns->outer_plan = outerPlan(plan);
5529
5530 if (dpns->outer_plan)
5531 dpns->outer_tlist = dpns->outer_plan->targetlist;
5532 else
5533 dpns->outer_tlist = NIL;
5534
5535 /*
5536 * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
5537 * use OUTER because that could someday conflict with the normal meaning.)
5538 * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
5539 * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
5540 * that as INNER referent.
5541 *
5542 * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
5543 * INNER referent. This is the join from the target relation to the data
5544 * source, and all INNER_VAR Vars in other parts of the query refer to its
5545 * targetlist.
5546 *
5547 * For ON CONFLICT DO SELECT/UPDATE we just need the inner tlist to point
5548 * to the excluded expression's tlist. (Similar to the SubqueryScan we
5549 * don't want to reuse OUTER, it's used for RETURNING in some modify table
5550 * cases, although not INSERT .. CONFLICT).
5551 */
5552 if (IsA(plan, SubqueryScan))
5553 dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5554 else if (IsA(plan, CteScan))
5555 dpns->inner_plan = list_nth(dpns->subplans,
5556 ((CteScan *) plan)->ctePlanId - 1);
5557 else if (IsA(plan, WorkTableScan))
5558 dpns->inner_plan = find_recursive_union(dpns,
5559 (WorkTableScan *) plan);
5560 else if (IsA(plan, ModifyTable))
5561 {
5562 if (((ModifyTable *) plan)->operation == CMD_MERGE)
5563 dpns->inner_plan = outerPlan(plan);
5564 else
5565 dpns->inner_plan = plan;
5566 }
5567 else
5568 dpns->inner_plan = innerPlan(plan);
5569
5571 dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
5572 else if (dpns->inner_plan)
5573 dpns->inner_tlist = dpns->inner_plan->targetlist;
5574 else
5575 dpns->inner_tlist = NIL;
5576
5577 /* Set up referent for INDEX_VAR Vars, if needed */
5578 if (IsA(plan, IndexOnlyScan))
5579 dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5580 else if (IsA(plan, ForeignScan))
5581 dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5582 else if (IsA(plan, CustomScan))
5583 dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5584 else
5585 dpns->index_tlist = NIL;
5586}
5587
5588/*
5589 * Locate the ancestor plan node that is the RecursiveUnion generating
5590 * the WorkTableScan's work table. We can match on wtParam, since that
5591 * should be unique within the plan tree.
5592 */
5593static Plan *
5595{
5596 ListCell *lc;
5597
5598 foreach(lc, dpns->ancestors)
5599 {
5600 Plan *ancestor = (Plan *) lfirst(lc);
5601
5602 if (IsA(ancestor, RecursiveUnion) &&
5603 ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5604 return ancestor;
5605 }
5606 elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5607 wtscan->wtParam);
5608 return NULL;
5609}
5610
5611/*
5612 * push_child_plan: temporarily transfer deparsing attention to a child plan
5613 *
5614 * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
5615 * deparse context in case the referenced expression itself uses
5616 * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
5617 * affecting levelsup issues (although in a Plan tree there really shouldn't
5618 * be any).
5619 *
5620 * Caller must provide a local deparse_namespace variable to save the
5621 * previous state for pop_child_plan.
5622 */
5623static void
5626{
5627 /* Save state for restoration later */
5628 *save_dpns = *dpns;
5629
5630 /* Link current plan node into ancestors list */
5631 dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5632
5633 /* Set attention on selected child */
5635}
5636
5637/*
5638 * pop_child_plan: undo the effects of push_child_plan
5639 */
5640static void
5642{
5643 List *ancestors;
5644
5645 /* Get rid of ancestors list cell added by push_child_plan */
5646 ancestors = list_delete_first(dpns->ancestors);
5647
5648 /* Restore fields changed by push_child_plan */
5649 *dpns = *save_dpns;
5650
5651 /* Make sure dpns->ancestors is right (may be unnecessary) */
5652 dpns->ancestors = ancestors;
5653}
5654
5655/*
5656 * push_ancestor_plan: temporarily transfer deparsing attention to an
5657 * ancestor plan
5658 *
5659 * When expanding a Param reference, we must adjust the deparse context
5660 * to match the plan node that contains the expression being printed;
5661 * otherwise we'd fail if that expression itself contains a Param or
5662 * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
5663 *
5664 * The target ancestor is conveniently identified by the ListCell holding it
5665 * in dpns->ancestors.
5666 *
5667 * Caller must provide a local deparse_namespace variable to save the
5668 * previous state for pop_ancestor_plan.
5669 */
5670static void
5673{
5675
5676 /* Save state for restoration later */
5677 *save_dpns = *dpns;
5678
5679 /* Build a new ancestor list with just this node's ancestors */
5680 dpns->ancestors =
5681 list_copy_tail(dpns->ancestors,
5682 list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5683
5684 /* Set attention on selected ancestor */
5686}
5687
5688/*
5689 * pop_ancestor_plan: undo the effects of push_ancestor_plan
5690 */
5691static void
5693{
5694 /* Free the ancestor list made in push_ancestor_plan */
5695 list_free(dpns->ancestors);
5696
5697 /* Restore fields changed by push_ancestor_plan */
5698 *dpns = *save_dpns;
5699}
5700
5701
5702/* ----------
5703 * make_ruledef - reconstruct the CREATE RULE command
5704 * for a given pg_rewrite tuple
5705 * ----------
5706 */
5707static void
5709 int prettyFlags)
5710{
5711 char *rulename;
5712 char ev_type;
5713 Oid ev_class;
5714 bool is_instead;
5715 char *ev_qual;
5716 char *ev_action;
5717 List *actions;
5720 int fno;
5721 Datum dat;
5722 bool isnull;
5723
5724 /*
5725 * Get the attribute values from the rules tuple
5726 */
5727 fno = SPI_fnumber(rulettc, "rulename");
5728 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5729 Assert(!isnull);
5730 rulename = NameStr(*(DatumGetName(dat)));
5731
5732 fno = SPI_fnumber(rulettc, "ev_type");
5733 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5734 Assert(!isnull);
5735 ev_type = DatumGetChar(dat);
5736
5737 fno = SPI_fnumber(rulettc, "ev_class");
5738 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5739 Assert(!isnull);
5741
5742 fno = SPI_fnumber(rulettc, "is_instead");
5743 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5744 Assert(!isnull);
5745 is_instead = DatumGetBool(dat);
5746
5747 fno = SPI_fnumber(rulettc, "ev_qual");
5749 Assert(ev_qual != NULL);
5750
5751 fno = SPI_fnumber(rulettc, "ev_action");
5753 Assert(ev_action != NULL);
5754 actions = (List *) stringToNode(ev_action);
5755 if (actions == NIL)
5756 elog(ERROR, "invalid empty ev_action list");
5757
5759
5760 /*
5761 * Build the rules definition text
5762 */
5763 appendStringInfo(buf, "CREATE RULE %s AS",
5764 quote_identifier(rulename));
5765
5766 if (prettyFlags & PRETTYFLAG_INDENT)
5767 appendStringInfoString(buf, "\n ON ");
5768 else
5769 appendStringInfoString(buf, " ON ");
5770
5771 /* The event the rule is fired for */
5772 switch (ev_type)
5773 {
5774 case '1':
5775 appendStringInfoString(buf, "SELECT");
5777 break;
5778
5779 case '2':
5780 appendStringInfoString(buf, "UPDATE");
5781 break;
5782
5783 case '3':
5784 appendStringInfoString(buf, "INSERT");
5785 break;
5786
5787 case '4':
5788 appendStringInfoString(buf, "DELETE");
5789 break;
5790
5791 default:
5792 ereport(ERROR,
5794 errmsg("rule \"%s\" has unsupported event type %d",
5795 rulename, ev_type)));
5796 break;
5797 }
5798
5799 /* The relation the rule is fired on */
5800 appendStringInfo(buf, " TO %s",
5801 (prettyFlags & PRETTYFLAG_SCHEMA) ?
5804
5805 /* If the rule has an event qualification, add it */
5806 if (strcmp(ev_qual, "<>") != 0)
5807 {
5808 Node *qual;
5809 Query *query;
5810 deparse_context context;
5812
5813 if (prettyFlags & PRETTYFLAG_INDENT)
5815 appendStringInfoString(buf, " WHERE ");
5816
5817 qual = stringToNode(ev_qual);
5818
5819 /*
5820 * We need to make a context for recognizing any Vars in the qual
5821 * (which can only be references to OLD and NEW). Use the rtable of
5822 * the first query in the action list for this purpose.
5823 */
5824 query = (Query *) linitial(actions);
5825
5826 /*
5827 * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5828 * into the SELECT, and that's what we need to look at. (Ugly kluge
5829 * ... try to fix this when we redesign querytrees.)
5830 */
5831 query = getInsertSelectQuery(query, NULL);
5832
5833 /* Must acquire locks right away; see notes in get_query_def() */
5834 AcquireRewriteLocks(query, false, false);
5835
5836 context.buf = buf;
5837 context.namespaces = list_make1(&dpns);
5838 context.resultDesc = NULL;
5839 context.targetList = NIL;
5840 context.windowClause = NIL;
5841 context.varprefix = (list_length(query->rtable) != 1);
5842 context.prettyFlags = prettyFlags;
5844 context.indentLevel = PRETTYINDENT_STD;
5845 context.colNamesVisible = true;
5846 context.inGroupBy = false;
5847 context.varInOrderBy = false;
5848 context.appendparents = NULL;
5849
5850 set_deparse_for_query(&dpns, query, NIL);
5851
5852 get_rule_expr(qual, &context, false);
5853 }
5854
5855 appendStringInfoString(buf, " DO ");
5856
5857 /* The INSTEAD keyword (if so) */
5858 if (is_instead)
5859 appendStringInfoString(buf, "INSTEAD ");
5860
5861 /* Finally the rules actions */
5862 if (list_length(actions) > 1)
5863 {
5865 Query *query;
5866
5868 foreach(action, actions)
5869 {
5870 query = (Query *) lfirst(action);
5871 get_query_def(query, buf, NIL, viewResultDesc, true,
5872 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5873 if (prettyFlags)
5875 else
5877 }
5879 }
5880 else
5881 {
5882 Query *query;
5883
5884 query = (Query *) linitial(actions);
5885 get_query_def(query, buf, NIL, viewResultDesc, true,
5886 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5888 }
5889
5891}
5892
5893
5894/* ----------
5895 * make_viewdef - reconstruct the SELECT part of a
5896 * view rewrite rule
5897 * ----------
5898 */
5899static void
5901 int prettyFlags, int wrapColumn)
5902{
5903 Query *query;
5904 char ev_type;
5905 Oid ev_class;
5906 bool is_instead;
5907 char *ev_qual;
5908 char *ev_action;
5909 List *actions;
5911 int fno;
5912 Datum dat;
5913 bool isnull;
5914
5915 /*
5916 * Get the attribute values from the rules tuple
5917 */
5918 fno = SPI_fnumber(rulettc, "ev_type");
5919 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5920 Assert(!isnull);
5921 ev_type = DatumGetChar(dat);
5922
5923 fno = SPI_fnumber(rulettc, "ev_class");
5924 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5925 Assert(!isnull);
5927
5928 fno = SPI_fnumber(rulettc, "is_instead");
5929 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5930 Assert(!isnull);
5931 is_instead = DatumGetBool(dat);
5932
5933 fno = SPI_fnumber(rulettc, "ev_qual");
5935 Assert(ev_qual != NULL);
5936
5937 fno = SPI_fnumber(rulettc, "ev_action");
5939 Assert(ev_action != NULL);
5940 actions = (List *) stringToNode(ev_action);
5941
5942 if (list_length(actions) != 1)
5943 {
5944 /* keep output buffer empty and leave */
5945 return;
5946 }
5947
5948 query = (Query *) linitial(actions);
5949
5950 if (ev_type != '1' || !is_instead ||
5951 strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5952 {
5953 /* keep output buffer empty and leave */
5954 return;
5955 }
5956
5958
5960 prettyFlags, wrapColumn, 0);
5962
5964}
5965
5966
5967/* ----------
5968 * get_query_def - Parse back one query parsetree
5969 *
5970 * query: parsetree to be displayed
5971 * buf: output text is appended to buf
5972 * parentnamespace: list (initially empty) of outer-level deparse_namespace's
5973 * resultDesc: if not NULL, the output tuple descriptor for the view
5974 * represented by a SELECT query. We use the column names from it
5975 * to label SELECT output columns, in preference to names in the query
5976 * colNamesVisible: true if the surrounding context cares about the output
5977 * column names at all (as, for example, an EXISTS() context does not);
5978 * when false, we can suppress dummy column labels such as "?column?"
5979 * prettyFlags: bitmask of PRETTYFLAG_XXX options
5980 * wrapColumn: maximum line length, or -1 to disable wrapping
5981 * startIndent: initial indentation amount
5982 * ----------
5983 */
5984static void
5986 TupleDesc resultDesc, bool colNamesVisible,
5987 int prettyFlags, int wrapColumn, int startIndent)
5988{
5989 deparse_context context;
5991 int rtable_size;
5992
5993 /* Guard against excessively long or deeply-nested queries */
5996
5997 rtable_size = query->hasGroupRTE ?
5998 list_length(query->rtable) - 1 :
5999 list_length(query->rtable);
6000
6001 /*
6002 * Replace any Vars in the query's targetlist and havingQual that
6003 * reference GROUP outputs with the underlying grouping expressions.
6004 *
6005 * We can safely pass NULL for the root here. Preserving varnullingrels
6006 * makes no difference to the deparsed source text.
6007 */
6008 if (query->hasGroupRTE)
6009 {
6010 query->targetList = (List *)
6011 flatten_group_exprs(NULL, query, (Node *) query->targetList);
6012 query->havingQual =
6013 flatten_group_exprs(NULL, query, query->havingQual);
6014 }
6015
6016 /*
6017 * Before we begin to examine the query, acquire locks on referenced
6018 * relations, and fix up deleted columns in JOIN RTEs. This ensures
6019 * consistent results. Note we assume it's OK to scribble on the passed
6020 * querytree!
6021 *
6022 * We are only deparsing the query (we are not about to execute it), so we
6023 * only need AccessShareLock on the relations it mentions.
6024 */
6025 AcquireRewriteLocks(query, false, false);
6026
6027 context.buf = buf;
6029 context.resultDesc = NULL;
6030 context.targetList = NIL;
6031 context.windowClause = NIL;
6032 context.varprefix = (parentnamespace != NIL ||
6033 rtable_size != 1);
6034 context.prettyFlags = prettyFlags;
6035 context.wrapColumn = wrapColumn;
6036 context.indentLevel = startIndent;
6037 context.colNamesVisible = colNamesVisible;
6038 context.inGroupBy = false;
6039 context.varInOrderBy = false;
6040 context.appendparents = NULL;
6041
6043
6044 switch (query->commandType)
6045 {
6046 case CMD_SELECT:
6047 /* We set context.resultDesc only if it's a SELECT */
6048 context.resultDesc = resultDesc;
6049 get_select_query_def(query, &context);
6050 break;
6051
6052 case CMD_UPDATE:
6053 get_update_query_def(query, &context);
6054 break;
6055
6056 case CMD_INSERT:
6057 get_insert_query_def(query, &context);
6058 break;
6059
6060 case CMD_DELETE:
6061 get_delete_query_def(query, &context);
6062 break;
6063
6064 case CMD_MERGE:
6065 get_merge_query_def(query, &context);
6066 break;
6067
6068 case CMD_NOTHING:
6069 appendStringInfoString(buf, "NOTHING");
6070 break;
6071
6072 case CMD_UTILITY:
6073 get_utility_query_def(query, &context);
6074 break;
6075
6076 default:
6077 elog(ERROR, "unrecognized query command type: %d",
6078 query->commandType);
6079 break;
6080 }
6081}
6082
6083/* ----------
6084 * get_values_def - Parse back a VALUES list
6085 * ----------
6086 */
6087static void
6088get_values_def(List *values_lists, deparse_context *context)
6089{
6090 StringInfo buf = context->buf;
6091 bool first_list = true;
6092 ListCell *vtl;
6093
6094 appendStringInfoString(buf, "VALUES ");
6095
6096 foreach(vtl, values_lists)
6097 {
6098 List *sublist = (List *) lfirst(vtl);
6099 bool first_col = true;
6100 ListCell *lc;
6101
6102 if (first_list)
6103 first_list = false;
6104 else
6106
6108 foreach(lc, sublist)
6109 {
6110 Node *col = (Node *) lfirst(lc);
6111
6112 if (first_col)
6113 first_col = false;
6114 else
6116
6117 /*
6118 * Print the value. Whole-row Vars need special treatment.
6119 */
6120 get_rule_expr_toplevel(col, context, false);
6121 }
6123 }
6124}
6125
6126/* ----------
6127 * get_with_clause - Parse back a WITH clause
6128 * ----------
6129 */
6130static void
6131get_with_clause(Query *query, deparse_context *context)
6132{
6133 StringInfo buf = context->buf;
6134 const char *sep;
6135 ListCell *l;
6136
6137 if (query->cteList == NIL)
6138 return;
6139
6140 if (PRETTY_INDENT(context))
6141 {
6142 context->indentLevel += PRETTYINDENT_STD;
6144 }
6145
6146 if (query->hasRecursive)
6147 sep = "WITH RECURSIVE ";
6148 else
6149 sep = "WITH ";
6150 foreach(l, query->cteList)
6151 {
6153
6156 if (cte->aliascolnames)
6157 {
6158 bool first = true;
6159 ListCell *col;
6160
6162 foreach(col, cte->aliascolnames)
6163 {
6164 if (first)
6165 first = false;
6166 else
6170 }
6172 }
6173 appendStringInfoString(buf, " AS ");
6174 switch (cte->ctematerialized)
6175 {
6177 break;
6179 appendStringInfoString(buf, "MATERIALIZED ");
6180 break;
6182 appendStringInfoString(buf, "NOT MATERIALIZED ");
6183 break;
6184 }
6186 if (PRETTY_INDENT(context))
6187 appendContextKeyword(context, "", 0, 0, 0);
6188 get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
6189 true,
6190 context->prettyFlags, context->wrapColumn,
6191 context->indentLevel);
6192 if (PRETTY_INDENT(context))
6193 appendContextKeyword(context, "", 0, 0, 0);
6195
6196 if (cte->search_clause)
6197 {
6198 bool first = true;
6199 ListCell *lc;
6200
6201 appendStringInfo(buf, " SEARCH %s FIRST BY ",
6202 cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
6203
6204 foreach(lc, cte->search_clause->search_col_list)
6205 {
6206 if (first)
6207 first = false;
6208 else
6212 }
6213
6214 appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
6215 }
6216
6217 if (cte->cycle_clause)
6218 {
6219 bool first = true;
6220 ListCell *lc;
6221
6222 appendStringInfoString(buf, " CYCLE ");
6223
6224 foreach(lc, cte->cycle_clause->cycle_col_list)
6225 {
6226 if (first)
6227 first = false;
6228 else
6232 }
6233
6234 appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
6235
6236 {
6237 Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
6238 Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
6239
6240 if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
6241 cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
6242 {
6243 appendStringInfoString(buf, " TO ");
6244 get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
6245 appendStringInfoString(buf, " DEFAULT ");
6246 get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
6247 }
6248 }
6249
6250 appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
6251 }
6252
6253 sep = ", ";
6254 }
6255
6256 if (PRETTY_INDENT(context))
6257 {
6258 context->indentLevel -= PRETTYINDENT_STD;
6259 appendContextKeyword(context, "", 0, 0, 0);
6260 }
6261 else
6263}
6264
6265/* ----------
6266 * get_select_query_def - Parse back a SELECT parsetree
6267 * ----------
6268 */
6269static void
6271{
6272 StringInfo buf = context->buf;
6273 bool force_colno;
6274 ListCell *l;
6275
6276 /* Insert the WITH clause if given */
6277 get_with_clause(query, context);
6278
6279 /* Subroutines may need to consult the SELECT targetlist and windowClause */
6280 context->targetList = query->targetList;
6281 context->windowClause = query->windowClause;
6282
6283 /*
6284 * If the Query node has a setOperations tree, then it's the top level of
6285 * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
6286 * fields are interesting in the top query itself.
6287 */
6288 if (query->setOperations)
6289 {
6290 get_setop_query(query->setOperations, query, context);
6291 /* ORDER BY clauses must be simple in this case */
6292 force_colno = true;
6293 }
6294 else
6295 {
6296 get_basic_select_query(query, context);
6297 force_colno = false;
6298 }
6299
6300 /* Add the ORDER BY clause if given */
6301 if (query->sortClause != NIL)
6302 {
6303 appendContextKeyword(context, " ORDER BY ",
6305 get_rule_orderby(query->sortClause, query->targetList,
6306 force_colno, context);
6307 }
6308
6309 /*
6310 * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
6311 * standard spelling of LIMIT.
6312 */
6313 if (query->limitOffset != NULL)
6314 {
6315 appendContextKeyword(context, " OFFSET ",
6317 get_rule_expr(query->limitOffset, context, false);
6318 }
6319 if (query->limitCount != NULL)
6320 {
6321 if (query->limitOption == LIMIT_OPTION_WITH_TIES)
6322 {
6323 /*
6324 * The limitCount arg is a c_expr, so it needs parens. Simple
6325 * literals and function expressions would not need parens, but
6326 * unfortunately it's hard to tell if the expression will be
6327 * printed as a simple literal like 123 or as a typecast
6328 * expression, like '-123'::int4. The grammar accepts the former
6329 * without quoting, but not the latter.
6330 */
6331 appendContextKeyword(context, " FETCH FIRST ",
6334 get_rule_expr(query->limitCount, context, false);
6336 appendStringInfoString(buf, " ROWS WITH TIES");
6337 }
6338 else
6339 {
6340 appendContextKeyword(context, " LIMIT ",
6342 if (IsA(query->limitCount, Const) &&
6343 ((Const *) query->limitCount)->constisnull)
6345 else
6346 get_rule_expr(query->limitCount, context, false);
6347 }
6348 }
6349
6350 /* Add FOR [KEY] UPDATE/SHARE clauses if present */
6351 if (query->hasForUpdate)
6352 {
6353 foreach(l, query->rowMarks)
6354 {
6355 RowMarkClause *rc = (RowMarkClause *) lfirst(l);
6356
6357 /* don't print implicit clauses */
6358 if (rc->pushedDown)
6359 continue;
6360
6361 appendContextKeyword(context,
6364
6365 appendStringInfo(buf, " OF %s",
6367 context)));
6368 if (rc->waitPolicy == LockWaitError)
6369 appendStringInfoString(buf, " NOWAIT");
6370 else if (rc->waitPolicy == LockWaitSkip)
6371 appendStringInfoString(buf, " SKIP LOCKED");
6372 }
6373 }
6374}
6375
6376static char *
6378{
6379 switch (strength)
6380 {
6381 case LCS_NONE:
6382 /* we intentionally throw an error for LCS_NONE */
6383 elog(ERROR, "unrecognized LockClauseStrength %d",
6384 (int) strength);
6385 break;
6386 case LCS_FORKEYSHARE:
6387 return " FOR KEY SHARE";
6388 case LCS_FORSHARE:
6389 return " FOR SHARE";
6390 case LCS_FORNOKEYUPDATE:
6391 return " FOR NO KEY UPDATE";
6392 case LCS_FORUPDATE:
6393 return " FOR UPDATE";
6394 }
6395 return NULL; /* keep compiler quiet */
6396}
6397
6398/*
6399 * Detect whether query looks like SELECT ... FROM VALUES(),
6400 * with no need to rename the output columns of the VALUES RTE.
6401 * If so, return the VALUES RTE. Otherwise return NULL.
6402 */
6403static RangeTblEntry *
6404get_simple_values_rte(Query *query, TupleDesc resultDesc)
6405{
6407 ListCell *lc;
6408
6409 /*
6410 * We want to detect a match even if the Query also contains OLD or NEW
6411 * rule RTEs. So the idea is to scan the rtable and see if there is only
6412 * one inFromCl RTE that is a VALUES RTE.
6413 */
6414 foreach(lc, query->rtable)
6415 {
6417
6418 if (rte->rtekind == RTE_VALUES && rte->inFromCl)
6419 {
6420 if (result)
6421 return NULL; /* multiple VALUES (probably not possible) */
6422 result = rte;
6423 }
6424 else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
6425 continue; /* ignore rule entries */
6426 else
6427 return NULL; /* something else -> not simple VALUES */
6428 }
6429
6430 /*
6431 * We don't need to check the targetlist in any great detail, because
6432 * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
6433 * appear inside auto-generated sub-queries with very restricted
6434 * structure. However, DefineView might have modified the tlist by
6435 * injecting new column aliases, or we might have some other column
6436 * aliases forced by a resultDesc. We can only simplify if the RTE's
6437 * column names match the names that get_target_list() would select.
6438 */
6439 if (result)
6440 {
6441 ListCell *lcn;
6442 int colno;
6443
6444 if (list_length(query->targetList) != list_length(result->eref->colnames))
6445 return NULL; /* this probably cannot happen */
6446 colno = 0;
6447 forboth(lc, query->targetList, lcn, result->eref->colnames)
6448 {
6450 char *cname = strVal(lfirst(lcn));
6451 char *colname;
6452
6453 if (tle->resjunk)
6454 return NULL; /* this probably cannot happen */
6455
6456 /* compute name that get_target_list would use for column */
6457 colno++;
6458 if (resultDesc && colno <= resultDesc->natts)
6459 colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6460 else
6461 colname = tle->resname;
6462
6463 /* does it match the VALUES RTE? */
6464 if (colname == NULL || strcmp(colname, cname) != 0)
6465 return NULL; /* column name has been changed */
6466 }
6467 }
6468
6469 return result;
6470}
6471
6472static void
6474{
6475 StringInfo buf = context->buf;
6477 char *sep;
6478 ListCell *l;
6479
6480 if (PRETTY_INDENT(context))
6481 {
6482 context->indentLevel += PRETTYINDENT_STD;
6484 }
6485
6486 /*
6487 * If the query looks like SELECT * FROM (VALUES ...), then print just the
6488 * VALUES part. This reverses what transformValuesClause() did at parse
6489 * time.
6490 */
6491 values_rte = get_simple_values_rte(query, context->resultDesc);
6492 if (values_rte)
6493 {
6494 get_values_def(values_rte->values_lists, context);
6495 return;
6496 }
6497
6498 /*
6499 * Build up the query string - first we say SELECT
6500 */
6501 if (query->isReturn)
6502 appendStringInfoString(buf, "RETURN");
6503 else
6504 appendStringInfoString(buf, "SELECT");
6505
6506 /* Add the DISTINCT clause if given */
6507 if (query->distinctClause != NIL)
6508 {
6509 if (query->hasDistinctOn)
6510 {
6511 appendStringInfoString(buf, " DISTINCT ON (");
6512 sep = "";
6513 foreach(l, query->distinctClause)
6514 {
6516
6518 get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6519 false, context);
6520 sep = ", ";
6521 }
6523 }
6524 else
6525 appendStringInfoString(buf, " DISTINCT");
6526 }
6527
6528 /* Then we tell what to select (the targetlist) */
6529 get_target_list(query->targetList, context);
6530
6531 /* Add the FROM clause if needed */
6532 get_from_clause(query, " FROM ", context);
6533
6534 /* Add the WHERE clause if given */
6535 if (query->jointree->quals != NULL)
6536 {
6537 appendContextKeyword(context, " WHERE ",
6539 get_rule_expr(query->jointree->quals, context, false);
6540 }
6541
6542 /* Add the GROUP BY clause if given */
6543 if (query->groupClause != NULL || query->groupingSets != NULL)
6544 {
6545 bool save_ingroupby;
6546
6547 appendContextKeyword(context, " GROUP BY ",
6549 if (query->groupDistinct)
6550 appendStringInfoString(buf, "DISTINCT ");
6551
6552 save_ingroupby = context->inGroupBy;
6553 context->inGroupBy = true;
6554
6555 if (query->groupByAll)
6557 else if (query->groupingSets == NIL)
6558 {
6559 sep = "";
6560 foreach(l, query->groupClause)
6561 {
6563
6565 get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
6566 false, context);
6567 sep = ", ";
6568 }
6569 }
6570 else
6571 {
6572 sep = "";
6573 foreach(l, query->groupingSets)
6574 {
6575 GroupingSet *grp = lfirst(l);
6576
6578 get_rule_groupingset(grp, query->targetList, true, context);
6579 sep = ", ";
6580 }
6581 }
6582
6583 context->inGroupBy = save_ingroupby;
6584 }
6585
6586 /* Add the HAVING clause if given */
6587 if (query->havingQual != NULL)
6588 {
6589 appendContextKeyword(context, " HAVING ",
6591 get_rule_expr(query->havingQual, context, false);
6592 }
6593
6594 /* Add the WINDOW clause if needed */
6595 if (query->windowClause != NIL)
6596 get_rule_windowclause(query, context);
6597}
6598
6599/* ----------
6600 * get_target_list - Parse back a SELECT target list
6601 *
6602 * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.
6603 * ----------
6604 */
6605static void
6606get_target_list(List *targetList, deparse_context *context)
6607{
6608 StringInfo buf = context->buf;
6610 bool last_was_multiline = false;
6611 char *sep;
6612 int colno;
6613 ListCell *l;
6614
6615 /* we use targetbuf to hold each TLE's text temporarily */
6617
6618 sep = " ";
6619 colno = 0;
6620 foreach(l, targetList)
6621 {
6623 char *colname;
6624 char *attname;
6625
6626 if (tle->resjunk)
6627 continue; /* ignore junk entries */
6628
6630 sep = ", ";
6631 colno++;
6632
6633 /*
6634 * Put the new field text into targetbuf so we can decide after we've
6635 * got it whether or not it needs to go on a new line.
6636 */
6638 context->buf = &targetbuf;
6639
6640 /*
6641 * We special-case Var nodes rather than using get_rule_expr. This is
6642 * needed because get_rule_expr will display a whole-row Var as
6643 * "foo.*", which is the preferred notation in most contexts, but at
6644 * the top level of a SELECT list it's not right (the parser will
6645 * expand that notation into multiple columns, yielding behavior
6646 * different from a whole-row Var). We need to call get_variable
6647 * directly so that we can tell it to do the right thing, and so that
6648 * we can get the attribute name which is the default AS label.
6649 */
6650 if (tle->expr && (IsA(tle->expr, Var)))
6651 {
6652 attname = get_variable((Var *) tle->expr, 0, true, context);
6653 }
6654 else
6655 {
6656 get_rule_expr((Node *) tle->expr, context, true);
6657
6658 /*
6659 * When colNamesVisible is true, we should always show the
6660 * assigned column name explicitly. Otherwise, show it only if
6661 * it's not FigureColname's fallback.
6662 */
6663 attname = context->colNamesVisible ? NULL : "?column?";
6664 }
6665
6666 /*
6667 * Figure out what the result column should be called. In the context
6668 * of a view, use the view's tuple descriptor (so as to pick up the
6669 * effects of any column RENAME that's been done on the view).
6670 * Otherwise, just use what we can find in the TLE.
6671 */
6672 if (context->resultDesc && colno <= context->resultDesc->natts)
6673 colname = NameStr(TupleDescAttr(context->resultDesc,
6674 colno - 1)->attname);
6675 else
6676 colname = tle->resname;
6677
6678 /* Show AS unless the column's name is correct as-is */
6679 if (colname) /* resname could be NULL */
6680 {
6681 if (attname == NULL || strcmp(attname, colname) != 0)
6682 appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
6683 }
6684
6685 /* Restore context's output buffer */
6686 context->buf = buf;
6687
6688 /* Consider line-wrapping if enabled */
6689 if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
6690 {
6691 int leading_nl_pos;
6692
6693 /* Does the new field start with a new line? */
6694 if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
6695 leading_nl_pos = 0;
6696 else
6697 leading_nl_pos = -1;
6698
6699 /* If so, we shouldn't add anything */
6700 if (leading_nl_pos >= 0)
6701 {
6702 /* instead, remove any trailing spaces currently in buf */
6704 }
6705 else
6706 {
6707 char *trailing_nl;
6708
6709 /* Locate the start of the current line in the output buffer */
6710 trailing_nl = strrchr(buf->data, '\n');
6711 if (trailing_nl == NULL)
6712 trailing_nl = buf->data;
6713 else
6714 trailing_nl++;
6715
6716 /*
6717 * Add a newline, plus some indentation, if the new field is
6718 * not the first and either the new field would cause an
6719 * overflow or the last field used more than one line.
6720 */
6721 if (colno > 1 &&
6722 ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
6726 }
6727
6728 /* Remember this field's multiline status for next iteration */
6730 (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
6731 }
6732
6733 /* Add the new field */
6735 }
6736
6737 /* clean up */
6738 pfree(targetbuf.data);
6739}
6740
6741static void
6743{
6744 StringInfo buf = context->buf;
6745
6746 if (query->returningList)
6747 {
6748 bool have_with = false;
6749
6750 appendContextKeyword(context, " RETURNING",
6752
6753 /* Add WITH (OLD/NEW) options, if they're not the defaults */
6754 if (query->returningOldAlias && strcmp(query->returningOldAlias, "old") != 0)
6755 {
6756 appendStringInfo(buf, " WITH (OLD AS %s",
6757 quote_identifier(query->returningOldAlias));
6758 have_with = true;
6759 }
6760 if (query->returningNewAlias && strcmp(query->returningNewAlias, "new") != 0)
6761 {
6762 if (have_with)
6763 appendStringInfo(buf, ", NEW AS %s",
6764 quote_identifier(query->returningNewAlias));
6765 else
6766 {
6767 appendStringInfo(buf, " WITH (NEW AS %s",
6768 quote_identifier(query->returningNewAlias));
6769 have_with = true;
6770 }
6771 }
6772 if (have_with)
6774
6775 /* Add the returning expressions themselves */
6776 get_target_list(query->returningList, context);
6777 }
6778}
6779
6780static void
6782{
6783 StringInfo buf = context->buf;
6784 bool need_paren;
6785
6786 /* Guard against excessively long or deeply-nested queries */
6789
6790 if (IsA(setOp, RangeTblRef))
6791 {
6793 RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
6794 Query *subquery = rte->subquery;
6795
6796 Assert(subquery != NULL);
6797
6798 /*
6799 * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.
6800 * Also add parens if the leaf query contains its own set operations.
6801 * (That shouldn't happen unless one of the other clauses is also
6802 * present, see transformSetOperationTree; but let's be safe.)
6803 */
6804 need_paren = (subquery->cteList ||
6805 subquery->sortClause ||
6806 subquery->rowMarks ||
6807 subquery->limitOffset ||
6808 subquery->limitCount ||
6809 subquery->setOperations);
6810 if (need_paren)
6812 get_query_def(subquery, buf, context->namespaces,
6813 context->resultDesc, context->colNamesVisible,
6814 context->prettyFlags, context->wrapColumn,
6815 context->indentLevel);
6816 if (need_paren)
6818 }
6819 else if (IsA(setOp, SetOperationStmt))
6820 {
6822 int subindent;
6824
6825 /*
6826 * We force parens when nesting two SetOperationStmts, except when the
6827 * lefthand input is another setop of the same kind. Syntactically,
6828 * we could omit parens in rather more cases, but it seems best to use
6829 * parens to flag cases where the setop operator changes. If we use
6830 * parens, we also increase the indentation level for the child query.
6831 *
6832 * There are some cases in which parens are needed around a leaf query
6833 * too, but those are more easily handled at the next level down (see
6834 * code above).
6835 */
6836 if (IsA(op->larg, SetOperationStmt))
6837 {
6839
6840 if (op->op == lop->op && op->all == lop->all)
6841 need_paren = false;
6842 else
6843 need_paren = true;
6844 }
6845 else
6846 need_paren = false;
6847
6848 if (need_paren)
6849 {
6852 appendContextKeyword(context, "", subindent, 0, 0);
6853 }
6854 else
6855 subindent = 0;
6856
6857 get_setop_query(op->larg, query, context);
6858
6859 if (need_paren)
6860 appendContextKeyword(context, ") ", -subindent, 0, 0);
6861 else if (PRETTY_INDENT(context))
6862 appendContextKeyword(context, "", -subindent, 0, 0);
6863 else
6865
6866 switch (op->op)
6867 {
6868 case SETOP_UNION:
6869 appendStringInfoString(buf, "UNION ");
6870 break;
6871 case SETOP_INTERSECT:
6872 appendStringInfoString(buf, "INTERSECT ");
6873 break;
6874 case SETOP_EXCEPT:
6875 appendStringInfoString(buf, "EXCEPT ");
6876 break;
6877 default:
6878 elog(ERROR, "unrecognized set op: %d",
6879 (int) op->op);
6880 }
6881 if (op->all)
6882 appendStringInfoString(buf, "ALL ");
6883
6884 /* Always parenthesize if RHS is another setop */
6886
6887 /*
6888 * The indentation code here is deliberately a bit different from that
6889 * for the lefthand input, because we want the line breaks in
6890 * different places.
6891 */
6892 if (need_paren)
6893 {
6896 }
6897 else
6898 subindent = 0;
6899 appendContextKeyword(context, "", subindent, 0, 0);
6900
6901 /*
6902 * The output column names of the RHS sub-select don't matter.
6903 */
6905 context->colNamesVisible = false;
6906
6907 get_setop_query(op->rarg, query, context);
6908
6910
6911 if (PRETTY_INDENT(context))
6912 context->indentLevel -= subindent;
6913 if (need_paren)
6914 appendContextKeyword(context, ")", 0, 0, 0);
6915 }
6916 else
6917 {
6918 elog(ERROR, "unrecognized node type: %d",
6919 (int) nodeTag(setOp));
6920 }
6921}
6922
6923/*
6924 * Display a sort/group clause.
6925 *
6926 * Also returns the expression tree, so caller need not find it again.
6927 */
6928static Node *
6930 deparse_context *context)
6931{
6932 StringInfo buf = context->buf;
6934 Node *expr;
6935
6936 tle = get_sortgroupref_tle(ref, tlist);
6937 expr = (Node *) tle->expr;
6938
6939 /*
6940 * Use column-number form if requested by caller. Otherwise, if
6941 * expression is a constant, force it to be dumped with an explicit cast
6942 * as decoration --- this is because a simple integer constant is
6943 * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
6944 * we dump it without any decoration. Similarly, if it's just a Var,
6945 * there is risk of misinterpretation if the column name is reassigned in
6946 * the SELECT list, so we may need to force table qualification. And, if
6947 * it's anything more complex than a simple Var, then force extra parens
6948 * around it, to ensure it can't be misinterpreted as a cube() or rollup()
6949 * construct.
6950 */
6951 if (force_colno)
6952 {
6953 Assert(!tle->resjunk);
6954 appendStringInfo(buf, "%d", tle->resno);
6955 }
6956 else if (!expr)
6957 /* do nothing, probably can't happen */ ;
6958 else if (IsA(expr, Const))
6959 get_const_expr((Const *) expr, context, 1);
6960 else if (IsA(expr, Var))
6961 {
6962 /* Tell get_variable to check for name conflict */
6963 bool save_varinorderby = context->varInOrderBy;
6964
6965 context->varInOrderBy = true;
6966 (void) get_variable((Var *) expr, 0, false, context);
6968 }
6969 else
6970 {
6971 /*
6972 * We must force parens for function-like expressions even if
6973 * PRETTY_PAREN is off, since those are the ones in danger of
6974 * misparsing. For other expressions we need to force them only if
6975 * PRETTY_PAREN is on, since otherwise the expression will output them
6976 * itself. (We can't skip the parens.)
6977 */
6978 bool need_paren = (PRETTY_PAREN(context)
6979 || IsA(expr, FuncExpr)
6980 || IsA(expr, Aggref)
6981 || IsA(expr, WindowFunc)
6982 || IsA(expr, JsonConstructorExpr));
6983
6984 if (need_paren)
6985 appendStringInfoChar(context->buf, '(');
6986 get_rule_expr(expr, context, true);
6987 if (need_paren)
6988 appendStringInfoChar(context->buf, ')');
6989 }
6990
6991 return expr;
6992}
6993
6994/*
6995 * Display a GroupingSet
6996 */
6997static void
6999 bool omit_parens, deparse_context *context)
7000{
7001 ListCell *l;
7002 StringInfo buf = context->buf;
7003 bool omit_child_parens = true;
7004 char *sep = "";
7005
7006 switch (gset->kind)
7007 {
7008 case GROUPING_SET_EMPTY:
7010 return;
7011
7013 {
7014 if (!omit_parens || list_length(gset->content) != 1)
7016
7017 foreach(l, gset->content)
7018 {
7019 Index ref = lfirst_int(l);
7020
7022 get_rule_sortgroupclause(ref, targetlist,
7023 false, context);
7024 sep = ", ";
7025 }
7026
7027 if (!omit_parens || list_length(gset->content) != 1)
7029 }
7030 return;
7031
7033 appendStringInfoString(buf, "ROLLUP(");
7034 break;
7035 case GROUPING_SET_CUBE:
7036 appendStringInfoString(buf, "CUBE(");
7037 break;
7038 case GROUPING_SET_SETS:
7039 appendStringInfoString(buf, "GROUPING SETS (");
7040 omit_child_parens = false;
7041 break;
7042 }
7043
7044 foreach(l, gset->content)
7045 {
7047 get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
7048 sep = ", ";
7049 }
7050
7052}
7053
7054/*
7055 * Display an ORDER BY list.
7056 */
7057static void
7058get_rule_orderby(List *orderList, List *targetList,
7059 bool force_colno, deparse_context *context)
7060{
7061 StringInfo buf = context->buf;
7062 const char *sep;
7063 ListCell *l;
7064
7065 sep = "";
7066 foreach(l, orderList)
7067 {
7069 Node *sortexpr;
7071 TypeCacheEntry *typentry;
7072
7074 sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
7075 force_colno, context);
7077 /* See whether operator is default < or > for datatype */
7078 typentry = lookup_type_cache(sortcoltype,
7080 if (srt->sortop == typentry->lt_opr)
7081 {
7082 /* ASC is default, so emit nothing for it */
7083 if (srt->nulls_first)
7084 appendStringInfoString(buf, " NULLS FIRST");
7085 }
7086 else if (srt->sortop == typentry->gt_opr)
7087 {
7088 appendStringInfoString(buf, " DESC");
7089 /* DESC defaults to NULLS FIRST */
7090 if (!srt->nulls_first)
7091 appendStringInfoString(buf, " NULLS LAST");
7092 }
7093 else
7094 {
7095 appendStringInfo(buf, " USING %s",
7098 sortcoltype));
7099 /* be specific to eliminate ambiguity */
7100 if (srt->nulls_first)
7101 appendStringInfoString(buf, " NULLS FIRST");
7102 else
7103 appendStringInfoString(buf, " NULLS LAST");
7104 }
7105 sep = ", ";
7106 }
7107}
7108
7109/*
7110 * Display a WINDOW clause.
7111 *
7112 * Note that the windowClause list might contain only anonymous window
7113 * specifications, in which case we should print nothing here.
7114 */
7115static void
7117{
7118 StringInfo buf = context->buf;
7119 const char *sep;
7120 ListCell *l;
7121
7122 sep = NULL;
7123 foreach(l, query->windowClause)
7124 {
7125 WindowClause *wc = (WindowClause *) lfirst(l);
7126
7127 if (wc->name == NULL)
7128 continue; /* ignore anonymous windows */
7129
7130 if (sep == NULL)
7131 appendContextKeyword(context, " WINDOW ",
7133 else
7135
7136 appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
7137
7138 get_rule_windowspec(wc, query->targetList, context);
7139
7140 sep = ", ";
7141 }
7142}
7143
7144/*
7145 * Display a window definition
7146 */
7147static void
7148get_rule_windowspec(WindowClause *wc, List *targetList,
7149 deparse_context *context)
7150{
7151 StringInfo buf = context->buf;
7152 bool needspace = false;
7153 const char *sep;
7154 ListCell *l;
7155
7157 if (wc->refname)
7158 {
7160 needspace = true;
7161 }
7162 /* partition clauses are always inherited, so only print if no refname */
7163 if (wc->partitionClause && !wc->refname)
7164 {
7165 if (needspace)
7167 appendStringInfoString(buf, "PARTITION BY ");
7168 sep = "";
7169 foreach(l, wc->partitionClause)
7170 {
7172
7174 get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
7175 false, context);
7176 sep = ", ";
7177 }
7178 needspace = true;
7179 }
7180 /* print ordering clause only if not inherited */
7181 if (wc->orderClause && !wc->copiedOrder)
7182 {
7183 if (needspace)
7185 appendStringInfoString(buf, "ORDER BY ");
7186 get_rule_orderby(wc->orderClause, targetList, false, context);
7187 needspace = true;
7188 }
7189 /* framing clause is never inherited, so print unless it's default */
7191 {
7192 if (needspace)
7195 wc->startOffset, wc->endOffset,
7196 context);
7197 }
7199}
7200
7201/*
7202 * Append the description of a window's framing options to context->buf
7203 */
7204static void
7205get_window_frame_options(int frameOptions,
7206 Node *startOffset, Node *endOffset,
7207 deparse_context *context)
7208{
7209 StringInfo buf = context->buf;
7210
7211 if (frameOptions & FRAMEOPTION_NONDEFAULT)
7212 {
7213 if (frameOptions & FRAMEOPTION_RANGE)
7214 appendStringInfoString(buf, "RANGE ");
7215 else if (frameOptions & FRAMEOPTION_ROWS)
7216 appendStringInfoString(buf, "ROWS ");
7217 else if (frameOptions & FRAMEOPTION_GROUPS)
7218 appendStringInfoString(buf, "GROUPS ");
7219 else
7220 Assert(false);
7221 if (frameOptions & FRAMEOPTION_BETWEEN)
7222 appendStringInfoString(buf, "BETWEEN ");
7223 if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
7224 appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
7225 else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
7226 appendStringInfoString(buf, "CURRENT ROW ");
7227 else if (frameOptions & FRAMEOPTION_START_OFFSET)
7228 {
7229 get_rule_expr(startOffset, context, false);
7230 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
7231 appendStringInfoString(buf, " PRECEDING ");
7232 else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
7233 appendStringInfoString(buf, " FOLLOWING ");
7234 else
7235 Assert(false);
7236 }
7237 else
7238 Assert(false);
7239 if (frameOptions & FRAMEOPTION_BETWEEN)
7240 {
7241 appendStringInfoString(buf, "AND ");
7242 if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
7243 appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
7244 else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
7245 appendStringInfoString(buf, "CURRENT ROW ");
7246 else if (frameOptions & FRAMEOPTION_END_OFFSET)
7247 {
7248 get_rule_expr(endOffset, context, false);
7249 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
7250 appendStringInfoString(buf, " PRECEDING ");
7251 else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
7252 appendStringInfoString(buf, " FOLLOWING ");
7253 else
7254 Assert(false);
7255 }
7256 else
7257 Assert(false);
7258 }
7259 if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
7260 appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
7261 else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
7262 appendStringInfoString(buf, "EXCLUDE GROUP ");
7263 else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
7264 appendStringInfoString(buf, "EXCLUDE TIES ");
7265 /* we will now have a trailing space; remove it */
7266 buf->data[--(buf->len)] = '\0';
7267 }
7268}
7269
7270/*
7271 * Return the description of a window's framing options as a palloc'd string
7272 */
7273char *
7275 Node *startOffset, Node *endOffset,
7276 List *dpcontext, bool forceprefix)
7277{
7279 deparse_context context;
7280
7282 context.buf = &buf;
7283 context.namespaces = dpcontext;
7284 context.resultDesc = NULL;
7285 context.targetList = NIL;
7286 context.windowClause = NIL;
7287 context.varprefix = forceprefix;
7288 context.prettyFlags = 0;
7290 context.indentLevel = 0;
7291 context.colNamesVisible = true;
7292 context.inGroupBy = false;
7293 context.varInOrderBy = false;
7294 context.appendparents = NULL;
7295
7296 get_window_frame_options(frameOptions, startOffset, endOffset, &context);
7297
7298 return buf.data;
7299}
7300
7301/* ----------
7302 * get_insert_query_def - Parse back an INSERT parsetree
7303 * ----------
7304 */
7305static void
7307{
7308 StringInfo buf = context->buf;
7312 char *sep;
7313 ListCell *l;
7315
7316 /* Insert the WITH clause if given */
7317 get_with_clause(query, context);
7318
7319 /*
7320 * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
7321 * single RTE for the SELECT or VALUES. Plain VALUES has neither.
7322 */
7323 foreach(l, query->rtable)
7324 {
7325 rte = (RangeTblEntry *) lfirst(l);
7326
7327 if (rte->rtekind == RTE_SUBQUERY)
7328 {
7329 if (select_rte)
7330 elog(ERROR, "too many subquery RTEs in INSERT");
7331 select_rte = rte;
7332 }
7333
7334 if (rte->rtekind == RTE_VALUES)
7335 {
7336 if (values_rte)
7337 elog(ERROR, "too many values RTEs in INSERT");
7338 values_rte = rte;
7339 }
7340 }
7341 if (select_rte && values_rte)
7342 elog(ERROR, "both subquery and values RTEs in INSERT");
7343
7344 /*
7345 * Start the query with INSERT INTO relname
7346 */
7347 rte = rt_fetch(query->resultRelation, query->rtable);
7348 Assert(rte->rtekind == RTE_RELATION);
7349
7350 if (PRETTY_INDENT(context))
7351 {
7352 context->indentLevel += PRETTYINDENT_STD;
7354 }
7355 appendStringInfo(buf, "INSERT INTO %s",
7356 generate_relation_name(rte->relid, NIL));
7357
7358 /* Print the relation alias, if needed; INSERT requires explicit AS */
7359 get_rte_alias(rte, query->resultRelation, true, context);
7360
7361 /* always want a space here */
7363
7364 /*
7365 * Add the insert-column-names list. Any indirection decoration needed on
7366 * the column names can be inferred from the top targetlist.
7367 */
7369 sep = "";
7370 if (query->targetList)
7372 foreach(l, query->targetList)
7373 {
7375
7376 if (tle->resjunk)
7377 continue; /* ignore junk entries */
7378
7380 sep = ", ";
7381
7382 /*
7383 * Put out name of target column; look in the catalogs, not at
7384 * tle->resname, since resname will fail to track RENAME.
7385 */
7388 tle->resno,
7389 false)));
7390
7391 /*
7392 * Print any indirection needed (subfields or subscripts), and strip
7393 * off the top-level nodes representing the indirection assignments.
7394 * Add the stripped expressions to strippedexprs. (If it's a
7395 * single-VALUES statement, the stripped expressions are the VALUES to
7396 * print below. Otherwise they're just Vars and not really
7397 * interesting.)
7398 */
7400 processIndirection((Node *) tle->expr,
7401 context));
7402 }
7403 if (query->targetList)
7405
7406 if (query->override)
7407 {
7408 if (query->override == OVERRIDING_SYSTEM_VALUE)
7409 appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
7410 else if (query->override == OVERRIDING_USER_VALUE)
7411 appendStringInfoString(buf, "OVERRIDING USER VALUE ");
7412 }
7413
7414 if (select_rte)
7415 {
7416 /* Add the SELECT */
7417 get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
7418 false,
7419 context->prettyFlags, context->wrapColumn,
7420 context->indentLevel);
7421 }
7422 else if (values_rte)
7423 {
7424 /* Add the multi-VALUES expression lists */
7425 get_values_def(values_rte->values_lists, context);
7426 }
7427 else if (strippedexprs)
7428 {
7429 /* Add the single-VALUES expression list */
7430 appendContextKeyword(context, "VALUES (",
7432 get_rule_list_toplevel(strippedexprs, context, false);
7434 }
7435 else
7436 {
7437 /* No expressions, so it must be DEFAULT VALUES */
7438 appendStringInfoString(buf, "DEFAULT VALUES");
7439 }
7440
7441 /* Add ON CONFLICT if present */
7442 if (query->onConflict)
7443 {
7445
7446 appendStringInfoString(buf, " ON CONFLICT");
7447
7448 if (confl->arbiterElems)
7449 {
7450 /* Add the single-VALUES expression list */
7452 get_rule_expr((Node *) confl->arbiterElems, context, false);
7454
7455 /* Add a WHERE clause (for partial indexes) if given */
7456 if (confl->arbiterWhere != NULL)
7457 {
7458 bool save_varprefix;
7459
7460 /*
7461 * Force non-prefixing of Vars, since parser assumes that they
7462 * belong to target relation. WHERE clause does not use
7463 * InferenceElem, so this is separately required.
7464 */
7465 save_varprefix = context->varprefix;
7466 context->varprefix = false;
7467
7468 appendContextKeyword(context, " WHERE ",
7470 get_rule_expr(confl->arbiterWhere, context, false);
7471
7472 context->varprefix = save_varprefix;
7473 }
7474 }
7475 else if (OidIsValid(confl->constraint))
7476 {
7477 char *constraint = get_constraint_name(confl->constraint);
7478
7479 if (!constraint)
7480 elog(ERROR, "cache lookup failed for constraint %u",
7481 confl->constraint);
7482 appendStringInfo(buf, " ON CONSTRAINT %s",
7483 quote_identifier(constraint));
7484 }
7485
7486 if (confl->action == ONCONFLICT_NOTHING)
7487 {
7488 appendStringInfoString(buf, " DO NOTHING");
7489 }
7490 else if (confl->action == ONCONFLICT_UPDATE)
7491 {
7492 appendStringInfoString(buf, " DO UPDATE SET ");
7493 /* Deparse targetlist */
7494 get_update_query_targetlist_def(query, confl->onConflictSet,
7495 context, rte);
7496
7497 /* Add a WHERE clause if given */
7498 if (confl->onConflictWhere != NULL)
7499 {
7500 appendContextKeyword(context, " WHERE ",
7502 get_rule_expr(confl->onConflictWhere, context, false);
7503 }
7504 }
7505 else
7506 {
7507 Assert(confl->action == ONCONFLICT_SELECT);
7508 appendStringInfoString(buf, " DO SELECT");
7509
7510 /* Add FOR [KEY] UPDATE/SHARE clause if present */
7511 if (confl->lockStrength != LCS_NONE)
7513
7514 /* Add a WHERE clause if given */
7515 if (confl->onConflictWhere != NULL)
7516 {
7517 appendContextKeyword(context, " WHERE ",
7519 get_rule_expr(confl->onConflictWhere, context, false);
7520 }
7521 }
7522 }
7523
7524 /* Add RETURNING if present */
7525 if (query->returningList)
7526 get_returning_clause(query, context);
7527}
7528
7529
7530/* ----------
7531 * get_update_query_def - Parse back an UPDATE parsetree
7532 * ----------
7533 */
7534static void
7536{
7537 StringInfo buf = context->buf;
7539
7540 /* Insert the WITH clause if given */
7541 get_with_clause(query, context);
7542
7543 /*
7544 * Start the query with UPDATE relname SET
7545 */
7546 rte = rt_fetch(query->resultRelation, query->rtable);
7547 Assert(rte->rtekind == RTE_RELATION);
7548 if (PRETTY_INDENT(context))
7549 {
7551 context->indentLevel += PRETTYINDENT_STD;
7552 }
7553 appendStringInfo(buf, "UPDATE %s%s",
7555 generate_relation_name(rte->relid, NIL));
7556
7557 /* Print the FOR PORTION OF, if needed */
7558 get_for_portion_of(query->forPortionOf, context);
7559
7560 /* Print the relation alias, if needed */
7561 get_rte_alias(rte, query->resultRelation, false, context);
7562
7563 appendStringInfoString(buf, " SET ");
7564
7565 /* Deparse targetlist */
7566 get_update_query_targetlist_def(query, query->targetList, context, rte);
7567
7568 /* Add the FROM clause if needed */
7569 get_from_clause(query, " FROM ", context);
7570
7571 /* Add a WHERE clause if given */
7572 if (query->jointree->quals != NULL)
7573 {
7574 appendContextKeyword(context, " WHERE ",
7576 get_rule_expr(query->jointree->quals, context, false);
7577 }
7578
7579 /* Add RETURNING if present */
7580 if (query->returningList)
7581 get_returning_clause(query, context);
7582}
7583
7584
7585/* ----------
7586 * get_update_query_targetlist_def - Parse back an UPDATE targetlist
7587 * ----------
7588 */
7589static void
7590get_update_query_targetlist_def(Query *query, List *targetList,
7592{
7593 StringInfo buf = context->buf;
7594 ListCell *l;
7597 const char *sep;
7600
7601 /*
7602 * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
7603 * into a list. We expect them to appear, in ID order, in resjunk tlist
7604 * entries.
7605 */
7606 ma_sublinks = NIL;
7607 if (query->hasSubLinks) /* else there can't be any */
7608 {
7609 foreach(l, targetList)
7610 {
7612
7613 if (tle->resjunk && IsA(tle->expr, SubLink))
7614 {
7615 SubLink *sl = (SubLink *) tle->expr;
7616
7617 if (sl->subLinkType == MULTIEXPR_SUBLINK)
7618 {
7620 Assert(sl->subLinkId == list_length(ma_sublinks));
7621 }
7622 }
7623 }
7624 }
7628
7629 /* Add the comma separated list of 'attname = value' */
7630 sep = "";
7631 foreach(l, targetList)
7632 {
7634 Node *expr;
7635
7636 if (tle->resjunk)
7637 continue; /* ignore junk entries */
7638
7639 /* Emit separator (OK whether we're in multiassignment or not) */
7641 sep = ", ";
7642
7643 /*
7644 * Check to see if we're starting a multiassignment group: if so,
7645 * output a left paren.
7646 */
7647 if (next_ma_cell != NULL && cur_ma_sublink == NULL)
7648 {
7649 /*
7650 * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
7651 * Param. That could be buried under FieldStores and
7652 * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
7653 * and underneath those there could be an implicit type coercion.
7654 * Because we would ignore implicit type coercions anyway, we
7655 * don't need to be as careful as processIndirection() is about
7656 * descending past implicit CoerceToDomains.
7657 */
7658 expr = (Node *) tle->expr;
7659 while (expr)
7660 {
7661 if (IsA(expr, FieldStore))
7662 {
7663 FieldStore *fstore = (FieldStore *) expr;
7664
7665 expr = (Node *) linitial(fstore->newvals);
7666 }
7667 else if (IsA(expr, SubscriptingRef))
7668 {
7669 SubscriptingRef *sbsref = (SubscriptingRef *) expr;
7670
7671 if (sbsref->refassgnexpr == NULL)
7672 break;
7673
7674 expr = (Node *) sbsref->refassgnexpr;
7675 }
7676 else if (IsA(expr, CoerceToDomain))
7677 {
7679
7680 if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
7681 break;
7682 expr = (Node *) cdomain->arg;
7683 }
7684 else
7685 break;
7686 }
7687 expr = strip_implicit_coercions(expr);
7688
7689 if (expr && IsA(expr, Param) &&
7690 ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
7691 {
7695 Assert(((Param *) expr)->paramid ==
7696 ((cur_ma_sublink->subLinkId << 16) | 1));
7698 }
7699 }
7700
7701 /*
7702 * Put out name of target column; look in the catalogs, not at
7703 * tle->resname, since resname will fail to track RENAME.
7704 */
7707 tle->resno,
7708 false)));
7709
7710 /*
7711 * Print any indirection needed (subfields or subscripts), and strip
7712 * off the top-level nodes representing the indirection assignments.
7713 */
7714 expr = processIndirection((Node *) tle->expr, context);
7715
7716 /*
7717 * If we're in a multiassignment, skip printing anything more, unless
7718 * this is the last column; in which case, what we print should be the
7719 * sublink, not the Param.
7720 */
7721 if (cur_ma_sublink != NULL)
7722 {
7723 if (--remaining_ma_columns > 0)
7724 continue; /* not the last column of multiassignment */
7726 expr = (Node *) cur_ma_sublink;
7728 }
7729
7731
7732 get_rule_expr(expr, context, false);
7733 }
7734}
7735
7736
7737/* ----------
7738 * get_delete_query_def - Parse back a DELETE parsetree
7739 * ----------
7740 */
7741static void
7743{
7744 StringInfo buf = context->buf;
7746
7747 /* Insert the WITH clause if given */
7748 get_with_clause(query, context);
7749
7750 /*
7751 * Start the query with DELETE FROM relname
7752 */
7753 rte = rt_fetch(query->resultRelation, query->rtable);
7754 Assert(rte->rtekind == RTE_RELATION);
7755 if (PRETTY_INDENT(context))
7756 {
7758 context->indentLevel += PRETTYINDENT_STD;
7759 }
7760 appendStringInfo(buf, "DELETE FROM %s%s",
7762 generate_relation_name(rte->relid, NIL));
7763
7764 /* Print the FOR PORTION OF, if needed */
7765 get_for_portion_of(query->forPortionOf, context);
7766
7767 /* Print the relation alias, if needed */
7768 get_rte_alias(rte, query->resultRelation, false, context);
7769
7770 /* Add the USING clause if given */
7771 get_from_clause(query, " USING ", context);
7772
7773 /* Add a WHERE clause if given */
7774 if (query->jointree->quals != NULL)
7775 {
7776 appendContextKeyword(context, " WHERE ",
7778 get_rule_expr(query->jointree->quals, context, false);
7779 }
7780
7781 /* Add RETURNING if present */
7782 if (query->returningList)
7783 get_returning_clause(query, context);
7784}
7785
7786
7787/* ----------
7788 * get_merge_query_def - Parse back a MERGE parsetree
7789 * ----------
7790 */
7791static void
7793{
7794 StringInfo buf = context->buf;
7796 ListCell *lc;
7798
7799 /* Insert the WITH clause if given */
7800 get_with_clause(query, context);
7801
7802 /*
7803 * Start the query with MERGE INTO relname
7804 */
7805 rte = rt_fetch(query->resultRelation, query->rtable);
7806 Assert(rte->rtekind == RTE_RELATION);
7807 if (PRETTY_INDENT(context))
7808 {
7810 context->indentLevel += PRETTYINDENT_STD;
7811 }
7812 appendStringInfo(buf, "MERGE INTO %s%s",
7814 generate_relation_name(rte->relid, NIL));
7815
7816 /* Print the relation alias, if needed */
7817 get_rte_alias(rte, query->resultRelation, false, context);
7818
7819 /* Print the source relation and join clause */
7820 get_from_clause(query, " USING ", context);
7821 appendContextKeyword(context, " ON ",
7823 get_rule_expr(query->mergeJoinCondition, context, false);
7824
7825 /*
7826 * Test for any NOT MATCHED BY SOURCE actions. If there are none, then
7827 * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
7828 * SQL standard. Otherwise, we have a non-SQL-standard query, so output
7829 * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
7830 * more explicit.
7831 */
7832 haveNotMatchedBySource = false;
7833 foreach(lc, query->mergeActionList)
7834 {
7836
7837 if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
7838 {
7840 break;
7841 }
7842 }
7843
7844 /* Print each merge action */
7845 foreach(lc, query->mergeActionList)
7846 {
7848
7849 appendContextKeyword(context, " WHEN ",
7851 switch (action->matchKind)
7852 {
7853 case MERGE_WHEN_MATCHED:
7854 appendStringInfoString(buf, "MATCHED");
7855 break;
7857 appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
7858 break;
7861 appendStringInfoString(buf, "NOT MATCHED BY TARGET");
7862 else
7863 appendStringInfoString(buf, "NOT MATCHED");
7864 break;
7865 default:
7866 elog(ERROR, "unrecognized matchKind: %d",
7867 (int) action->matchKind);
7868 }
7869
7870 if (action->qual)
7871 {
7872 appendContextKeyword(context, " AND ",
7874 get_rule_expr(action->qual, context, false);
7875 }
7876 appendContextKeyword(context, " THEN ",
7878
7879 if (action->commandType == CMD_INSERT)
7880 {
7881 /* This generally matches get_insert_query_def() */
7883 const char *sep = "";
7884 ListCell *lc2;
7885
7886 appendStringInfoString(buf, "INSERT");
7887
7888 if (action->targetList)
7890 foreach(lc2, action->targetList)
7891 {
7893
7894 Assert(!tle->resjunk);
7895
7897 sep = ", ";
7898
7901 tle->resno,
7902 false)));
7904 processIndirection((Node *) tle->expr,
7905 context));
7906 }
7907 if (action->targetList)
7909
7910 if (action->override)
7911 {
7912 if (action->override == OVERRIDING_SYSTEM_VALUE)
7913 appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
7914 else if (action->override == OVERRIDING_USER_VALUE)
7915 appendStringInfoString(buf, " OVERRIDING USER VALUE");
7916 }
7917
7918 if (strippedexprs)
7919 {
7920 appendContextKeyword(context, " VALUES (",
7922 get_rule_list_toplevel(strippedexprs, context, false);
7924 }
7925 else
7926 appendStringInfoString(buf, " DEFAULT VALUES");
7927 }
7928 else if (action->commandType == CMD_UPDATE)
7929 {
7930 appendStringInfoString(buf, "UPDATE SET ");
7931 get_update_query_targetlist_def(query, action->targetList,
7932 context, rte);
7933 }
7934 else if (action->commandType == CMD_DELETE)
7935 appendStringInfoString(buf, "DELETE");
7936 else if (action->commandType == CMD_NOTHING)
7937 appendStringInfoString(buf, "DO NOTHING");
7938 }
7939
7940 /* Add RETURNING if present */
7941 if (query->returningList)
7942 get_returning_clause(query, context);
7943}
7944
7945
7946/* ----------
7947 * get_utility_query_def - Parse back a UTILITY parsetree
7948 * ----------
7949 */
7950static void
7952{
7953 StringInfo buf = context->buf;
7954
7955 if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7956 {
7957 NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7958
7959 appendContextKeyword(context, "",
7960 0, PRETTYINDENT_STD, 1);
7961 appendStringInfo(buf, "NOTIFY %s",
7962 quote_identifier(stmt->conditionname));
7963 if (stmt->payload)
7964 {
7966 simple_quote_literal(buf, stmt->payload);
7967 }
7968 }
7969 else
7970 {
7971 /* Currently only NOTIFY utility commands can appear in rules */
7972 elog(ERROR, "unexpected utility statement type");
7973 }
7974}
7975
7976
7977/*
7978 * Parse back a graph label expression
7979 */
7980static void
7982{
7983 StringInfo buf = context->buf;
7984
7986
7987 switch (nodeTag(label_expr))
7988 {
7989 case T_GraphLabelRef:
7990 {
7992
7994 break;
7995 }
7996
7997 case T_BoolExpr:
7998 {
8000 ListCell *lc;
8001 bool first = true;
8002
8003 Assert(be->boolop == OR_EXPR);
8004
8005 foreach(lc, be->args)
8006 {
8007 if (!first)
8008 {
8009 if (be->boolop == OR_EXPR)
8011 }
8012 else
8013 first = false;
8014 get_graph_label_expr(lfirst(lc), context);
8015 }
8016
8017 break;
8018 }
8019
8020 default:
8021 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(label_expr));
8022 break;
8023 }
8024}
8025
8026/*
8027 * Parse back a path pattern expression
8028 */
8029static void
8031{
8032 StringInfo buf = context->buf;
8033 ListCell *lc;
8034
8035 foreach(lc, path_pattern_expr)
8036 {
8038 const char *sep = "";
8039
8040 switch (gep->kind)
8041 {
8042 case VERTEX_PATTERN:
8044 break;
8045 case EDGE_PATTERN_LEFT:
8047 break;
8048 case EDGE_PATTERN_RIGHT:
8049 case EDGE_PATTERN_ANY:
8051 break;
8052 case PAREN_EXPR:
8054 break;
8055 }
8056
8057 if (gep->variable)
8058 {
8060 sep = " ";
8061 }
8062
8063 if (gep->labelexpr)
8064 {
8067 get_graph_label_expr(gep->labelexpr, context);
8068 sep = " ";
8069 }
8070
8071 if (gep->subexpr)
8072 {
8074 get_path_pattern_expr_def(gep->subexpr, context);
8075 sep = " ";
8076 }
8077
8078 if (gep->whereClause)
8079 {
8081 appendStringInfoString(buf, "WHERE ");
8082 get_rule_expr(gep->whereClause, context, false);
8083 }
8084
8085 switch (gep->kind)
8086 {
8087 case VERTEX_PATTERN:
8089 break;
8090 case EDGE_PATTERN_LEFT:
8091 case EDGE_PATTERN_ANY:
8093 break;
8094 case EDGE_PATTERN_RIGHT:
8096 break;
8097 case PAREN_EXPR:
8099 break;
8100 }
8101
8102 if (gep->quantifier)
8103 {
8104 int lower = linitial_int(gep->quantifier);
8105 int upper = lsecond_int(gep->quantifier);
8106
8107 appendStringInfo(buf, "{%d,%d}", lower, upper);
8108 }
8109 }
8110}
8111
8112/*
8113 * Parse back a graph pattern
8114 */
8115static void
8116get_graph_pattern_def(GraphPattern *graph_pattern, deparse_context *context)
8117{
8118 StringInfo buf = context->buf;
8119 ListCell *lc;
8120 bool first = true;
8121
8122 foreach(lc, graph_pattern->path_pattern_list)
8123 {
8125
8126 if (!first)
8128 else
8129 first = false;
8130
8132 }
8133
8134 if (graph_pattern->whereClause)
8135 {
8136 appendStringInfoString(buf, "WHERE ");
8137 get_rule_expr(graph_pattern->whereClause, context, false);
8138 }
8139}
8140
8141/*
8142 * Display a Var appropriately.
8143 *
8144 * In some cases (currently only when recursing into an unnamed join)
8145 * the Var's varlevelsup has to be interpreted with respect to a context
8146 * above the current one; levelsup indicates the offset.
8147 *
8148 * If istoplevel is true, the Var is at the top level of a SELECT's
8149 * targetlist, which means we need special treatment of whole-row Vars.
8150 * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
8151 * dirty hack to prevent "tab.*" from being expanded into multiple columns.
8152 * (The parser will strip the useless coercion, so no inefficiency is added in
8153 * dump and reload.) We used to print just "tab" in such cases, but that is
8154 * ambiguous and will yield the wrong result if "tab" is also a plain column
8155 * name in the query.
8156 *
8157 * Returns the attname of the Var, or NULL if the Var has no attname (because
8158 * it is a whole-row Var or a subplan output reference).
8159 */
8160static char *
8161get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
8162{
8163 StringInfo buf = context->buf;
8166 int netlevelsup;
8168 int varno;
8169 AttrNumber varattno;
8171 char *refname;
8172 char *attname;
8173 bool need_prefix;
8174
8175 /* Find appropriate nesting depth */
8176 netlevelsup = var->varlevelsup + levelsup;
8177 if (netlevelsup >= list_length(context->namespaces))
8178 elog(ERROR, "bogus varlevelsup: %d offset %d",
8179 var->varlevelsup, levelsup);
8181 netlevelsup);
8182
8183 /*
8184 * If we have a syntactic referent for the Var, and we're working from a
8185 * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8186 * on the semantic referent. (Forcing use of the semantic referent when
8187 * printing plan trees is a design choice that's perhaps more motivated by
8188 * backwards compatibility than anything else. But it does have the
8189 * advantage of making plans more explicit.)
8190 */
8191 if (var->varnosyn > 0 && dpns->plan == NULL)
8192 {
8193 varno = var->varnosyn;
8194 varattno = var->varattnosyn;
8195 }
8196 else
8197 {
8198 varno = var->varno;
8199 varattno = var->varattno;
8200 }
8201
8202 /*
8203 * Try to find the relevant RTE in this rtable. In a plan tree, it's
8204 * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8205 * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
8206 * find the aliases previously assigned for this RTE.
8207 */
8208 if (varno >= 1 && varno <= list_length(dpns->rtable))
8209 {
8210 /*
8211 * We might have been asked to map child Vars to some parent relation.
8212 */
8213 if (context->appendparents && dpns->appendrels)
8214 {
8215 int pvarno = varno;
8216 AttrNumber pvarattno = varattno;
8217 AppendRelInfo *appinfo = dpns->appendrels[pvarno];
8218 bool found = false;
8219
8220 /* Only map up to inheritance parents, not UNION ALL appendrels */
8221 while (appinfo &&
8222 rt_fetch(appinfo->parent_relid,
8223 dpns->rtable)->rtekind == RTE_RELATION)
8224 {
8225 found = false;
8226 if (pvarattno > 0) /* system columns stay as-is */
8227 {
8228 if (pvarattno > appinfo->num_child_cols)
8229 break; /* safety check */
8230 pvarattno = appinfo->parent_colnos[pvarattno - 1];
8231 if (pvarattno == 0)
8232 break; /* Var is local to child */
8233 }
8234
8236 found = true;
8237
8238 /* If the parent is itself a child, continue up. */
8239 Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
8240 appinfo = dpns->appendrels[pvarno];
8241 }
8242
8243 /*
8244 * If we found an ancestral rel, and that rel is included in
8245 * appendparents, print that column not the original one.
8246 */
8247 if (found && bms_is_member(pvarno, context->appendparents))
8248 {
8249 varno = pvarno;
8250 varattno = pvarattno;
8251 }
8252 }
8253
8254 rte = rt_fetch(varno, dpns->rtable);
8255
8256 /* might be returning old/new column value */
8258 refname = dpns->ret_old_alias;
8259 else if (var->varreturningtype == VAR_RETURNING_NEW)
8260 refname = dpns->ret_new_alias;
8261 else
8262 refname = (char *) list_nth(dpns->rtable_names, varno - 1);
8263
8265 attnum = varattno;
8266 }
8267 else
8268 {
8269 resolve_special_varno((Node *) var, context,
8271 return NULL;
8272 }
8273
8274 /*
8275 * The planner will sometimes emit Vars referencing resjunk elements of a
8276 * subquery's target list (this is currently only possible if it chooses
8277 * to generate a "physical tlist" for a SubqueryScan or CteScan node).
8278 * Although we prefer to print subquery-referencing Vars using the
8279 * subquery's alias, that's not possible for resjunk items since they have
8280 * no alias. So in that case, drill down to the subplan and print the
8281 * contents of the referenced tlist item. This works because in a plan
8282 * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
8283 * we'll have set dpns->inner_plan to reference the child plan node.
8284 */
8285 if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
8286 attnum > list_length(rte->eref->colnames) &&
8287 dpns->inner_plan)
8288 {
8291
8292 tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8293 if (!tle)
8294 elog(ERROR, "invalid attnum %d for relation \"%s\"",
8295 attnum, rte->eref->aliasname);
8296
8297 Assert(netlevelsup == 0);
8298 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8299
8300 /*
8301 * Force parentheses because our caller probably assumed a Var is a
8302 * simple expression.
8303 */
8304 if (!IsA(tle->expr, Var))
8306 get_rule_expr((Node *) tle->expr, context, true);
8307 if (!IsA(tle->expr, Var))
8309
8311 return NULL;
8312 }
8313
8314 /*
8315 * If it's an unnamed join, look at the expansion of the alias variable.
8316 * If it's a simple reference to one of the input vars, then recursively
8317 * print the name of that var instead. When it's not a simple reference,
8318 * we have to just print the unqualified join column name. (This can only
8319 * happen with "dangerous" merged columns in a JOIN USING; we took pains
8320 * previously to make the unqualified column name unique in such cases.)
8321 *
8322 * This wouldn't work in decompiling plan trees, because we don't store
8323 * joinaliasvars lists after planning; but a plan tree should never
8324 * contain a join alias variable.
8325 */
8326 if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
8327 {
8328 if (rte->joinaliasvars == NIL)
8329 elog(ERROR, "cannot decompile join alias var in plan tree");
8330 if (attnum > 0)
8331 {
8332 Var *aliasvar;
8333
8334 aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
8335 /* we intentionally don't strip implicit coercions here */
8336 if (aliasvar && IsA(aliasvar, Var))
8337 {
8338 return get_variable(aliasvar, var->varlevelsup + levelsup,
8339 istoplevel, context);
8340 }
8341 }
8342
8343 /*
8344 * Unnamed join has no refname. (Note: since it's unnamed, there is
8345 * no way the user could have referenced it to create a whole-row Var
8346 * for it. So we don't have to cover that case below.)
8347 */
8348 Assert(refname == NULL);
8349 }
8350
8352 attname = NULL;
8353 else if (attnum > 0)
8354 {
8355 /* Get column name to use from the colinfo struct */
8356 if (attnum > colinfo->num_cols)
8357 elog(ERROR, "invalid attnum %d for relation \"%s\"",
8358 attnum, rte->eref->aliasname);
8359 attname = colinfo->colnames[attnum - 1];
8360
8361 /*
8362 * If we find a Var referencing a dropped column, it seems better to
8363 * print something (anything) than to fail. In general this should
8364 * not happen, but it used to be possible for some cases involving
8365 * functions returning named composite types, and perhaps there are
8366 * still bugs out there.
8367 */
8368 if (attname == NULL)
8369 attname = "?dropped?column?";
8370 }
8371 else
8372 {
8373 /* System column - name is fixed, get it from the catalog */
8375 }
8376
8377 need_prefix = (context->varprefix || attname == NULL ||
8379
8380 /*
8381 * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
8382 * clause, we may need to add a table-name prefix to prevent
8383 * findTargetlistEntrySQL92 from misinterpreting the name as an
8384 * output-column name. To avoid cluttering the output with unnecessary
8385 * prefixes, do so only if there is a name match to a SELECT tlist item
8386 * that is different from the Var.
8387 */
8388 if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
8389 {
8390 int colno = 0;
8391
8393 {
8394 char *colname;
8395
8396 if (tle->resjunk)
8397 continue; /* ignore junk entries */
8398 colno++;
8399
8400 /* This must match colname-choosing logic in get_target_list() */
8401 if (context->resultDesc && colno <= context->resultDesc->natts)
8402 colname = NameStr(TupleDescAttr(context->resultDesc,
8403 colno - 1)->attname);
8404 else
8405 colname = tle->resname;
8406
8407 if (colname && strcmp(colname, attname) == 0 &&
8408 !equal(var, tle->expr))
8409 {
8410 need_prefix = true;
8411 break;
8412 }
8413 }
8414 }
8415
8416 if (refname && need_prefix)
8417 {
8420 }
8421 if (attname)
8423 else
8424 {
8426 if (istoplevel)
8427 appendStringInfo(buf, "::%s",
8428 format_type_with_typemod(var->vartype,
8429 var->vartypmod));
8430 }
8431
8432 return attname;
8433}
8434
8435/*
8436 * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
8437 * routine is actually a callback for resolve_special_varno, which handles
8438 * finding the correct TargetEntry. We get the expression contained in that
8439 * TargetEntry and just need to deparse it, a job we can throw back on
8440 * get_rule_expr.
8441 */
8442static void
8443get_special_variable(Node *node, deparse_context *context, void *callback_arg)
8444{
8445 StringInfo buf = context->buf;
8446
8447 /*
8448 * For a non-Var referent, force parentheses because our caller probably
8449 * assumed a Var is a simple expression.
8450 */
8451 if (!IsA(node, Var))
8453 get_rule_expr(node, context, true);
8454 if (!IsA(node, Var))
8456}
8457
8458/*
8459 * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
8460 * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
8461 * invoke the callback provided.
8462 */
8463static void
8465 rsv_callback callback, void *callback_arg)
8466{
8467 Var *var;
8469
8470 /* This function is recursive, so let's be paranoid. */
8472
8473 /* If it's not a Var, invoke the callback. */
8474 if (!IsA(node, Var))
8475 {
8476 (*callback) (node, context, callback_arg);
8477 return;
8478 }
8479
8480 /* Find appropriate nesting depth */
8481 var = (Var *) node;
8483 var->varlevelsup);
8484
8485 /*
8486 * If varno is special, recurse. (Don't worry about varnosyn; if we're
8487 * here, we already decided not to use that.)
8488 */
8489 if (var->varno == OUTER_VAR && dpns->outer_tlist)
8490 {
8494
8495 tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
8496 if (!tle)
8497 elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
8498
8499 /*
8500 * If we're descending to the first child of an Append or MergeAppend,
8501 * update appendparents. This will affect deparsing of all Vars
8502 * appearing within the eventually-resolved subexpression.
8503 */
8505
8506 if (IsA(dpns->plan, Append))
8507 context->appendparents = bms_union(context->appendparents,
8508 ((Append *) dpns->plan)->apprelids);
8509 else if (IsA(dpns->plan, MergeAppend))
8510 context->appendparents = bms_union(context->appendparents,
8511 ((MergeAppend *) dpns->plan)->apprelids);
8512
8513 push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8514 resolve_special_varno((Node *) tle->expr, context,
8515 callback, callback_arg);
8518 return;
8519 }
8520 else if (var->varno == INNER_VAR && dpns->inner_tlist)
8521 {
8524
8525 tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
8526 if (!tle)
8527 elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
8528
8529 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8530 resolve_special_varno((Node *) tle->expr, context,
8531 callback, callback_arg);
8533 return;
8534 }
8535 else if (var->varno == INDEX_VAR && dpns->index_tlist)
8536 {
8538
8539 tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
8540 if (!tle)
8541 elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
8542
8543 resolve_special_varno((Node *) tle->expr, context,
8544 callback, callback_arg);
8545 return;
8546 }
8547 else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
8548 elog(ERROR, "bogus varno: %d", var->varno);
8549
8550 /* Not special. Just invoke the callback. */
8551 (*callback) (node, context, callback_arg);
8552}
8553
8554/*
8555 * Get the name of a field of an expression of composite type. The
8556 * expression is usually a Var, but we handle other cases too.
8557 *
8558 * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
8559 *
8560 * This is fairly straightforward when the expression has a named composite
8561 * type; we need only look up the type in the catalogs. However, the type
8562 * could also be RECORD. Since no actual table or view column is allowed to
8563 * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
8564 * or to a subquery output. We drill down to find the ultimate defining
8565 * expression and attempt to infer the field name from it. We ereport if we
8566 * can't determine the name.
8567 *
8568 * Similarly, a PARAM of type RECORD has to refer to some expression of
8569 * a determinable composite type.
8570 */
8571static const char *
8573 int levelsup, deparse_context *context)
8574{
8577 int netlevelsup;
8579 int varno;
8580 AttrNumber varattno;
8582 Node *expr;
8583
8584 /*
8585 * If it's a RowExpr that was expanded from a whole-row Var, use the
8586 * column names attached to it. (We could let get_expr_result_tupdesc()
8587 * handle this, but it's much cheaper to just pull out the name we need.)
8588 */
8589 if (IsA(var, RowExpr))
8590 {
8591 RowExpr *r = (RowExpr *) var;
8592
8593 if (fieldno > 0 && fieldno <= list_length(r->colnames))
8594 return strVal(list_nth(r->colnames, fieldno - 1));
8595 }
8596
8597 /*
8598 * If it's a Param of type RECORD, try to find what the Param refers to.
8599 */
8600 if (IsA(var, Param))
8601 {
8602 Param *param = (Param *) var;
8604
8605 expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8606 if (expr)
8607 {
8608 /* Found a match, so recurse to decipher the field name */
8610 const char *result;
8611
8614 0, context);
8616 return result;
8617 }
8618 }
8619
8620 /*
8621 * If it's a Var of type RECORD, we have to find what the Var refers to;
8622 * if not, we can use get_expr_result_tupdesc().
8623 */
8624 if (!IsA(var, Var) ||
8625 var->vartype != RECORDOID)
8626 {
8627 tupleDesc = get_expr_result_tupdesc((Node *) var, false);
8628 /* Got the tupdesc, so we can extract the field name */
8631 }
8632
8633 /* Find appropriate nesting depth */
8634 netlevelsup = var->varlevelsup + levelsup;
8635 if (netlevelsup >= list_length(context->namespaces))
8636 elog(ERROR, "bogus varlevelsup: %d offset %d",
8637 var->varlevelsup, levelsup);
8639 netlevelsup);
8640
8641 /*
8642 * If we have a syntactic referent for the Var, and we're working from a
8643 * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8644 * on the semantic referent. (See comments in get_variable().)
8645 */
8646 if (var->varnosyn > 0 && dpns->plan == NULL)
8647 {
8648 varno = var->varnosyn;
8649 varattno = var->varattnosyn;
8650 }
8651 else
8652 {
8653 varno = var->varno;
8654 varattno = var->varattno;
8655 }
8656
8657 /*
8658 * Try to find the relevant RTE in this rtable. In a plan tree, it's
8659 * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8660 * down into the subplans, or INDEX_VAR, which is resolved similarly.
8661 *
8662 * Note: unlike get_variable and resolve_special_varno, we need not worry
8663 * about inheritance mapping: a child Var should have the same datatype as
8664 * its parent, and here we're really only interested in the Var's type.
8665 */
8666 if (varno >= 1 && varno <= list_length(dpns->rtable))
8667 {
8668 rte = rt_fetch(varno, dpns->rtable);
8669 attnum = varattno;
8670 }
8671 else if (varno == OUTER_VAR && dpns->outer_tlist)
8672 {
8675 const char *result;
8676
8677 tle = get_tle_by_resno(dpns->outer_tlist, varattno);
8678 if (!tle)
8679 elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
8680
8681 Assert(netlevelsup == 0);
8682 push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8683
8685 levelsup, context);
8686
8688 return result;
8689 }
8690 else if (varno == INNER_VAR && dpns->inner_tlist)
8691 {
8694 const char *result;
8695
8696 tle = get_tle_by_resno(dpns->inner_tlist, varattno);
8697 if (!tle)
8698 elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
8699
8700 Assert(netlevelsup == 0);
8701 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8702
8704 levelsup, context);
8705
8707 return result;
8708 }
8709 else if (varno == INDEX_VAR && dpns->index_tlist)
8710 {
8712 const char *result;
8713
8714 tle = get_tle_by_resno(dpns->index_tlist, varattno);
8715 if (!tle)
8716 elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
8717
8718 Assert(netlevelsup == 0);
8719
8721 levelsup, context);
8722
8723 return result;
8724 }
8725 else
8726 {
8727 elog(ERROR, "bogus varno: %d", varno);
8728 return NULL; /* keep compiler quiet */
8729 }
8730
8732 {
8733 /* Var is whole-row reference to RTE, so select the right field */
8735 }
8736
8737 /*
8738 * This part has essentially the same logic as the parser's
8739 * expandRecordVariable() function, but we are dealing with a different
8740 * representation of the input context, and we only need one field name
8741 * not a TupleDesc. Also, we need special cases for finding subquery and
8742 * CTE subplans when deparsing Plan trees.
8743 */
8744 expr = (Node *) var; /* default if we can't drill down */
8745
8746 switch (rte->rtekind)
8747 {
8748 case RTE_RELATION:
8749 case RTE_VALUES:
8751 case RTE_GRAPH_TABLE:
8752 case RTE_RESULT:
8753
8754 /*
8755 * This case should not occur: a column of a table, values list,
8756 * or ENR shouldn't have type RECORD. Fall through and fail (most
8757 * likely) at the bottom.
8758 */
8759 break;
8760 case RTE_SUBQUERY:
8761 /* Subselect-in-FROM: examine sub-select's output expr */
8762 {
8763 if (rte->subquery)
8764 {
8765 TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
8766 attnum);
8767
8768 if (ste == NULL || ste->resjunk)
8769 elog(ERROR, "subquery %s does not have attribute %d",
8770 rte->eref->aliasname, attnum);
8771 expr = (Node *) ste->expr;
8772 if (IsA(expr, Var))
8773 {
8774 /*
8775 * Recurse into the sub-select to see what its Var
8776 * refers to. We have to build an additional level of
8777 * namespace to keep in step with varlevelsup in the
8778 * subselect; furthermore, the subquery RTE might be
8779 * from an outer query level, in which case the
8780 * namespace for the subselect must have that outer
8781 * level as parent namespace.
8782 */
8783 List *save_nslist = context->namespaces;
8786 const char *result;
8787
8789 netlevelsup);
8790
8791 set_deparse_for_query(&mydpns, rte->subquery,
8793
8795
8797 0, context);
8798
8799 context->namespaces = save_nslist;
8800
8801 return result;
8802 }
8803 /* else fall through to inspect the expression */
8804 }
8805 else
8806 {
8807 /*
8808 * We're deparsing a Plan tree so we don't have complete
8809 * RTE entries (in particular, rte->subquery is NULL). But
8810 * the only place we'd normally see a Var directly
8811 * referencing a SUBQUERY RTE is in a SubqueryScan plan
8812 * node, and we can look into the child plan's tlist
8813 * instead. An exception occurs if the subquery was
8814 * proven empty and optimized away: then we'd find such a
8815 * Var in a childless Result node, and there's nothing in
8816 * the plan tree that would let us figure out what it had
8817 * originally referenced. In that case, fall back on
8818 * printing "fN", analogously to the default column names
8819 * for RowExprs.
8820 */
8823 const char *result;
8824
8825 if (!dpns->inner_plan)
8826 {
8827 char *dummy_name = palloc(32);
8828
8829 Assert(dpns->plan && IsA(dpns->plan, Result));
8830 snprintf(dummy_name, 32, "f%d", fieldno);
8831 return dummy_name;
8832 }
8833 Assert(dpns->plan && IsA(dpns->plan, SubqueryScan));
8834
8835 tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8836 if (!tle)
8837 elog(ERROR, "bogus varattno for subquery var: %d",
8838 attnum);
8839 Assert(netlevelsup == 0);
8840 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8841
8843 levelsup, context);
8844
8846 return result;
8847 }
8848 }
8849 break;
8850 case RTE_JOIN:
8851 /* Join RTE --- recursively inspect the alias variable */
8852 if (rte->joinaliasvars == NIL)
8853 elog(ERROR, "cannot decompile join alias var in plan tree");
8854 Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
8855 expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
8856 Assert(expr != NULL);
8857 /* we intentionally don't strip implicit coercions here */
8858 if (IsA(expr, Var))
8859 return get_name_for_var_field((Var *) expr, fieldno,
8860 var->varlevelsup + levelsup,
8861 context);
8862 /* else fall through to inspect the expression */
8863 break;
8864 case RTE_FUNCTION:
8865 case RTE_TABLEFUNC:
8866
8867 /*
8868 * We couldn't get here unless a function is declared with one of
8869 * its result columns as RECORD, which is not allowed.
8870 */
8871 break;
8872 case RTE_CTE:
8873 /* CTE reference: examine subquery's output expr */
8874 {
8875 CommonTableExpr *cte = NULL;
8876 Index ctelevelsup;
8877 ListCell *lc;
8878
8879 /*
8880 * Try to find the referenced CTE using the namespace stack.
8881 */
8882 ctelevelsup = rte->ctelevelsup + netlevelsup;
8883 if (ctelevelsup >= list_length(context->namespaces))
8884 lc = NULL;
8885 else
8886 {
8888
8890 list_nth(context->namespaces, ctelevelsup);
8891 foreach(lc, ctedpns->ctes)
8892 {
8893 cte = (CommonTableExpr *) lfirst(lc);
8894 if (strcmp(cte->ctename, rte->ctename) == 0)
8895 break;
8896 }
8897 }
8898 if (lc != NULL)
8899 {
8900 Query *ctequery = (Query *) cte->ctequery;
8902 attnum);
8903
8904 if (ste == NULL || ste->resjunk)
8905 elog(ERROR, "CTE %s does not have attribute %d",
8906 rte->eref->aliasname, attnum);
8907 expr = (Node *) ste->expr;
8908 if (IsA(expr, Var))
8909 {
8910 /*
8911 * Recurse into the CTE to see what its Var refers to.
8912 * We have to build an additional level of namespace
8913 * to keep in step with varlevelsup in the CTE;
8914 * furthermore it could be an outer CTE (compare
8915 * SUBQUERY case above).
8916 */
8917 List *save_nslist = context->namespaces;
8920 const char *result;
8921
8923 ctelevelsup);
8924
8925 set_deparse_for_query(&mydpns, ctequery,
8927
8929
8931 0, context);
8932
8933 context->namespaces = save_nslist;
8934
8935 return result;
8936 }
8937 /* else fall through to inspect the expression */
8938 }
8939 else
8940 {
8941 /*
8942 * We're deparsing a Plan tree so we don't have a CTE
8943 * list. But the only places we'd normally see a Var
8944 * directly referencing a CTE RTE are in CteScan or
8945 * WorkTableScan plan nodes. For those cases,
8946 * set_deparse_plan arranged for dpns->inner_plan to be
8947 * the plan node that emits the CTE or RecursiveUnion
8948 * result, and we can look at its tlist instead. As
8949 * above, this can fail if the CTE has been proven empty,
8950 * in which case fall back to "fN".
8951 */
8954 const char *result;
8955
8956 if (!dpns->inner_plan)
8957 {
8958 char *dummy_name = palloc(32);
8959
8960 Assert(dpns->plan && IsA(dpns->plan, Result));
8961 snprintf(dummy_name, 32, "f%d", fieldno);
8962 return dummy_name;
8963 }
8964 Assert(dpns->plan && (IsA(dpns->plan, CteScan) ||
8965 IsA(dpns->plan, WorkTableScan)));
8966
8967 tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8968 if (!tle)
8969 elog(ERROR, "bogus varattno for subquery var: %d",
8970 attnum);
8971 Assert(netlevelsup == 0);
8972 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8973
8975 levelsup, context);
8976
8978 return result;
8979 }
8980 }
8981 break;
8982 case RTE_GROUP:
8983
8984 /*
8985 * We couldn't get here: any Vars that reference the RTE_GROUP RTE
8986 * should have been replaced with the underlying grouping
8987 * expressions.
8988 */
8989 break;
8990 }
8991
8992 /*
8993 * We now have an expression we can't expand any more, so see if
8994 * get_expr_result_tupdesc() can do anything with it.
8995 */
8996 tupleDesc = get_expr_result_tupdesc(expr, false);
8997 /* Got the tupdesc, so we can extract the field name */
9000}
9001
9002/*
9003 * Try to find the referenced expression for a PARAM_EXEC Param that might
9004 * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
9005 *
9006 * If successful, return the expression and set *dpns_p and *ancestor_cell_p
9007 * appropriately for calling push_ancestor_plan(). If no referent can be
9008 * found, return NULL.
9009 */
9010static Node *
9013{
9014 /* Initialize output parameters to prevent compiler warnings */
9015 *dpns_p = NULL;
9017
9018 /*
9019 * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
9020 * SubPlan argument. This will necessarily be in some ancestor of the
9021 * current expression's Plan node.
9022 */
9023 if (param->paramkind == PARAM_EXEC)
9024 {
9027 ListCell *lc;
9028
9029 dpns = (deparse_namespace *) linitial(context->namespaces);
9030 child_plan = dpns->plan;
9031
9032 foreach(lc, dpns->ancestors)
9033 {
9034 Node *ancestor = (Node *) lfirst(lc);
9035 ListCell *lc2;
9036
9037 /*
9038 * NestLoops transmit params to their inner child only.
9039 */
9040 if (IsA(ancestor, NestLoop) &&
9042 {
9044
9045 foreach(lc2, nl->nestParams)
9046 {
9048
9049 if (nlp->paramno == param->paramid)
9050 {
9051 /* Found a match, so return it */
9052 *dpns_p = dpns;
9054 return (Node *) nlp->paramval;
9055 }
9056 }
9057 }
9058
9059 /*
9060 * If ancestor is a SubPlan, check the arguments it provides.
9061 */
9062 if (IsA(ancestor, SubPlan))
9063 {
9064 SubPlan *subplan = (SubPlan *) ancestor;
9065 ListCell *lc3;
9066 ListCell *lc4;
9067
9068 forboth(lc3, subplan->parParam, lc4, subplan->args)
9069 {
9070 int paramid = lfirst_int(lc3);
9071 Node *arg = (Node *) lfirst(lc4);
9072
9073 if (paramid == param->paramid)
9074 {
9075 /*
9076 * Found a match, so return it. But, since Vars in
9077 * the arg are to be evaluated in the surrounding
9078 * context, we have to point to the next ancestor item
9079 * that is *not* a SubPlan.
9080 */
9081 ListCell *rest;
9082
9083 for_each_cell(rest, dpns->ancestors,
9084 lnext(dpns->ancestors, lc))
9085 {
9086 Node *ancestor2 = (Node *) lfirst(rest);
9087
9088 if (!IsA(ancestor2, SubPlan))
9089 {
9090 *dpns_p = dpns;
9092 return arg;
9093 }
9094 }
9095 elog(ERROR, "SubPlan cannot be outermost ancestor");
9096 }
9097 }
9098
9099 /* SubPlan isn't a kind of Plan, so skip the rest */
9100 continue;
9101 }
9102
9103 /*
9104 * We need not consider the ancestor's initPlan list, since
9105 * initplans never have any parParams.
9106 */
9107
9108 /* No luck, crawl up to next ancestor */
9109 child_plan = (Plan *) ancestor;
9110 }
9111 }
9112
9113 /* No referent found */
9114 return NULL;
9115}
9116
9117/*
9118 * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.
9119 *
9120 * If successful, return the generating subplan/initplan and set *column_p
9121 * to the subplan's 0-based output column number.
9122 * Otherwise, return NULL.
9123 */
9124static SubPlan *
9125find_param_generator(Param *param, deparse_context *context, int *column_p)
9126{
9127 /* Initialize output parameter to prevent compiler warnings */
9128 *column_p = 0;
9129
9130 /*
9131 * If it's a PARAM_EXEC parameter, search the current plan node as well as
9132 * ancestor nodes looking for a subplan or initplan that emits the value
9133 * for the Param. It could appear in the setParams of an initplan or
9134 * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
9135 */
9136 if (param->paramkind == PARAM_EXEC)
9137 {
9138 SubPlan *result;
9140 ListCell *lc;
9141
9142 dpns = (deparse_namespace *) linitial(context->namespaces);
9143
9144 /* First check the innermost plan node's initplans */
9146 if (result)
9147 return result;
9148
9149 /*
9150 * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
9151 * which can be referenced by Params elsewhere in the targetlist.
9152 * (Such Params should always be in the same targetlist, so there's no
9153 * need to do this work at upper plan nodes.)
9154 */
9155 foreach_node(TargetEntry, tle, dpns->plan->targetlist)
9156 {
9157 if (tle->expr && IsA(tle->expr, SubPlan))
9158 {
9159 SubPlan *subplan = (SubPlan *) tle->expr;
9160
9161 if (subplan->subLinkType == MULTIEXPR_SUBLINK)
9162 {
9163 foreach_int(paramid, subplan->setParam)
9164 {
9165 if (paramid == param->paramid)
9166 {
9167 /* Found a match, so return it. */
9168 *column_p = foreach_current_index(paramid);
9169 return subplan;
9170 }
9171 }
9172 }
9173 }
9174 }
9175
9176 /* No luck, so check the ancestor nodes */
9177 foreach(lc, dpns->ancestors)
9178 {
9179 Node *ancestor = (Node *) lfirst(lc);
9180
9181 /*
9182 * If ancestor is a SubPlan, check the paramIds it provides.
9183 */
9184 if (IsA(ancestor, SubPlan))
9185 {
9186 SubPlan *subplan = (SubPlan *) ancestor;
9187
9188 foreach_int(paramid, subplan->paramIds)
9189 {
9190 if (paramid == param->paramid)
9191 {
9192 /* Found a match, so return it. */
9193 *column_p = foreach_current_index(paramid);
9194 return subplan;
9195 }
9196 }
9197
9198 /* SubPlan isn't a kind of Plan, so skip the rest */
9199 continue;
9200 }
9201
9202 /*
9203 * Otherwise, it's some kind of Plan node, so check its initplans.
9204 */
9206 column_p);
9207 if (result)
9208 return result;
9209
9210 /* No luck, crawl up to next ancestor */
9211 }
9212 }
9213
9214 /* No generator found */
9215 return NULL;
9216}
9217
9218/*
9219 * Subroutine for find_param_generator: search one Plan node's initplans
9220 */
9221static SubPlan *
9223{
9224 foreach_node(SubPlan, subplan, plan->initPlan)
9225 {
9226 foreach_int(paramid, subplan->setParam)
9227 {
9228 if (paramid == param->paramid)
9229 {
9230 /* Found a match, so return it. */
9231 *column_p = foreach_current_index(paramid);
9232 return subplan;
9233 }
9234 }
9235 }
9236 return NULL;
9237}
9238
9239/*
9240 * Display a Param appropriately.
9241 */
9242static void
9243get_parameter(Param *param, deparse_context *context)
9244{
9245 Node *expr;
9248 SubPlan *subplan;
9249 int column;
9250
9251 /*
9252 * If it's a PARAM_EXEC parameter, try to locate the expression from which
9253 * the parameter was computed. This stanza handles only cases in which
9254 * the Param represents an input to the subplan we are currently in.
9255 */
9256 expr = find_param_referent(param, context, &dpns, &ancestor_cell);
9257 if (expr)
9258 {
9259 /* Found a match, so print it */
9261 bool save_varprefix;
9262 bool need_paren;
9263
9264 /* Switch attention to the ancestor plan node */
9266
9267 /*
9268 * Force prefixing of Vars, since they won't belong to the relation
9269 * being scanned in the original plan node.
9270 */
9271 save_varprefix = context->varprefix;
9272 context->varprefix = true;
9273
9274 /*
9275 * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
9276 * upper-level Param, which wouldn't need extra parentheses.
9277 * Otherwise, insert parens to ensure the expression looks atomic.
9278 */
9279 need_paren = !(IsA(expr, Var) ||
9280 IsA(expr, Aggref) ||
9281 IsA(expr, GroupingFunc) ||
9282 IsA(expr, Param));
9283 if (need_paren)
9284 appendStringInfoChar(context->buf, '(');
9285
9286 get_rule_expr(expr, context, false);
9287
9288 if (need_paren)
9289 appendStringInfoChar(context->buf, ')');
9290
9291 context->varprefix = save_varprefix;
9292
9294
9295 return;
9296 }
9297
9298 /*
9299 * Alternatively, maybe it's a subplan output, which we print as a
9300 * reference to the subplan. (We could drill down into the subplan and
9301 * print the relevant targetlist expression, but that has been deemed too
9302 * confusing since it would violate normal SQL scope rules. Also, we're
9303 * relying on this reference to show that the testexpr containing the
9304 * Param has anything to do with that subplan at all.)
9305 */
9306 subplan = find_param_generator(param, context, &column);
9307 if (subplan)
9308 {
9309 const char *nameprefix;
9310
9311 if (subplan->isInitPlan)
9312 nameprefix = "InitPlan ";
9313 else
9314 nameprefix = "SubPlan ";
9315
9316 appendStringInfo(context->buf, "(%s%s%s).col%d",
9317 subplan->useHashTable ? "hashed " : "",
9318 nameprefix,
9319 subplan->plan_name, column + 1);
9320
9321 return;
9322 }
9323
9324 /*
9325 * If it's an external parameter, see if the outermost namespace provides
9326 * function argument names.
9327 */
9328 if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
9329 {
9330 dpns = llast(context->namespaces);
9331 if (dpns->argnames &&
9332 param->paramid > 0 &&
9333 param->paramid <= dpns->numargs)
9334 {
9335 char *argname = dpns->argnames[param->paramid - 1];
9336
9337 if (argname)
9338 {
9339 bool should_qualify = false;
9340 ListCell *lc;
9341
9342 /*
9343 * Qualify the parameter name if there are any other deparse
9344 * namespaces with range tables. This avoids qualifying in
9345 * trivial cases like "RETURN a + b", but makes it safe in all
9346 * other cases.
9347 */
9348 foreach(lc, context->namespaces)
9349 {
9351
9352 if (depns->rtable_names != NIL)
9353 {
9354 should_qualify = true;
9355 break;
9356 }
9357 }
9358 if (should_qualify)
9359 {
9360 appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
9361 appendStringInfoChar(context->buf, '.');
9362 }
9363
9364 appendStringInfoString(context->buf, quote_identifier(argname));
9365 return;
9366 }
9367 }
9368 }
9369
9370 /*
9371 * Not PARAM_EXEC, or couldn't find referent: just print $N.
9372 *
9373 * It's a bug if we get here for anything except PARAM_EXTERN Params, but
9374 * in production builds printing $N seems more useful than failing.
9375 */
9376 Assert(param->paramkind == PARAM_EXTERN);
9377
9378 appendStringInfo(context->buf, "$%d", param->paramid);
9379}
9380
9381/*
9382 * get_simple_binary_op_name
9383 *
9384 * helper function for isSimpleNode
9385 * will return single char binary operator name, or NULL if it's not
9386 */
9387static const char *
9389{
9390 List *args = expr->args;
9391
9392 if (list_length(args) == 2)
9393 {
9394 /* binary operator */
9395 Node *arg1 = (Node *) linitial(args);
9396 Node *arg2 = (Node *) lsecond(args);
9397 const char *op;
9398
9400 if (strlen(op) == 1)
9401 return op;
9402 }
9403 return NULL;
9404}
9405
9406
9407/*
9408 * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
9409 *
9410 * true : simple in the context of parent node's type
9411 * false : not simple
9412 */
9413static bool
9414isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
9415{
9416 if (!node)
9417 return false;
9418
9419 switch (nodeTag(node))
9420 {
9421 case T_Var:
9422 case T_Const:
9423 case T_Param:
9425 case T_SetToDefault:
9426 case T_CurrentOfExpr:
9427 /* single words: always simple */
9428 return true;
9429
9430 case T_SubscriptingRef:
9431 case T_ArrayExpr:
9432 case T_RowExpr:
9433 case T_CoalesceExpr:
9434 case T_MinMaxExpr:
9435 case T_SQLValueFunction:
9436 case T_XmlExpr:
9437 case T_NextValueExpr:
9438 case T_NullIfExpr:
9439 case T_Aggref:
9440 case T_GroupingFunc:
9441 case T_WindowFunc:
9442 case T_MergeSupportFunc:
9443 case T_FuncExpr:
9445 case T_JsonExpr:
9446 /* function-like: name(..) or name[..] */
9447 return true;
9448
9449 /* CASE keywords act as parentheses */
9450 case T_CaseExpr:
9451 return true;
9452
9453 case T_FieldSelect:
9454
9455 /*
9456 * appears simple since . has top precedence, unless parent is
9457 * T_FieldSelect itself!
9458 */
9459 return !IsA(parentNode, FieldSelect);
9460
9461 case T_FieldStore:
9462
9463 /*
9464 * treat like FieldSelect (probably doesn't matter)
9465 */
9466 return !IsA(parentNode, FieldStore);
9467
9468 case T_CoerceToDomain:
9469 /* maybe simple, check args */
9470 return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
9471 node, prettyFlags);
9472 case T_RelabelType:
9473 return isSimpleNode((Node *) ((RelabelType *) node)->arg,
9474 node, prettyFlags);
9475 case T_CoerceViaIO:
9476 return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
9477 node, prettyFlags);
9478 case T_ArrayCoerceExpr:
9479 return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
9480 node, prettyFlags);
9482 return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
9483 node, prettyFlags);
9484 case T_ReturningExpr:
9485 return isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr,
9486 node, prettyFlags);
9487
9488 case T_OpExpr:
9489 {
9490 /* depends on parent node type; needs further checking */
9491 if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
9492 {
9493 const char *op;
9494 const char *parentOp;
9495 bool is_lopriop;
9496 bool is_hipriop;
9497 bool is_lopriparent;
9498 bool is_hipriparent;
9499
9500 op = get_simple_binary_op_name((OpExpr *) node);
9501 if (!op)
9502 return false;
9503
9504 /* We know only the basic operators + - and * / % */
9505 is_lopriop = (strchr("+-", *op) != NULL);
9506 is_hipriop = (strchr("*/%", *op) != NULL);
9507 if (!(is_lopriop || is_hipriop))
9508 return false;
9509
9511 if (!parentOp)
9512 return false;
9513
9514 is_lopriparent = (strchr("+-", *parentOp) != NULL);
9515 is_hipriparent = (strchr("*/%", *parentOp) != NULL);
9517 return false;
9518
9520 return true; /* op binds tighter than parent */
9521
9523 return false;
9524
9525 /*
9526 * Operators are same priority --- can skip parens only if
9527 * we have (a - b) - c, not a - (b - c).
9528 */
9529 if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
9530 return true;
9531
9532 return false;
9533 }
9534 /* else do the same stuff as for T_SubLink et al. */
9535 }
9537
9538 case T_SubLink:
9539 case T_NullTest:
9540 case T_BooleanTest:
9541 case T_DistinctExpr:
9542 case T_JsonIsPredicate:
9543 switch (nodeTag(parentNode))
9544 {
9545 case T_FuncExpr:
9546 {
9547 /* special handling for casts and COERCE_SQL_SYNTAX */
9548 CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9549
9550 if (type == COERCE_EXPLICIT_CAST ||
9553 return false;
9554 return true; /* own parentheses */
9555 }
9556 case T_BoolExpr: /* lower precedence */
9557 case T_SubscriptingRef: /* other separators */
9558 case T_ArrayExpr: /* other separators */
9559 case T_RowExpr: /* other separators */
9560 case T_CoalesceExpr: /* own parentheses */
9561 case T_MinMaxExpr: /* own parentheses */
9562 case T_XmlExpr: /* own parentheses */
9563 case T_NullIfExpr: /* other separators */
9564 case T_Aggref: /* own parentheses */
9565 case T_GroupingFunc: /* own parentheses */
9566 case T_WindowFunc: /* own parentheses */
9567 case T_CaseExpr: /* other separators */
9568 return true;
9569 default:
9570 return false;
9571 }
9572
9573 case T_BoolExpr:
9574 switch (nodeTag(parentNode))
9575 {
9576 case T_BoolExpr:
9577 if (prettyFlags & PRETTYFLAG_PAREN)
9578 {
9581
9582 type = ((BoolExpr *) node)->boolop;
9583 parentType = ((BoolExpr *) parentNode)->boolop;
9584 switch (type)
9585 {
9586 case NOT_EXPR:
9587 case AND_EXPR:
9589 return true;
9590 break;
9591 case OR_EXPR:
9592 if (parentType == OR_EXPR)
9593 return true;
9594 break;
9595 }
9596 }
9597 return false;
9598 case T_FuncExpr:
9599 {
9600 /* special handling for casts and COERCE_SQL_SYNTAX */
9601 CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9602
9603 if (type == COERCE_EXPLICIT_CAST ||
9606 return false;
9607 return true; /* own parentheses */
9608 }
9609 case T_SubscriptingRef: /* other separators */
9610 case T_ArrayExpr: /* other separators */
9611 case T_RowExpr: /* other separators */
9612 case T_CoalesceExpr: /* own parentheses */
9613 case T_MinMaxExpr: /* own parentheses */
9614 case T_XmlExpr: /* own parentheses */
9615 case T_NullIfExpr: /* other separators */
9616 case T_Aggref: /* own parentheses */
9617 case T_GroupingFunc: /* own parentheses */
9618 case T_WindowFunc: /* own parentheses */
9619 case T_CaseExpr: /* other separators */
9620 case T_JsonExpr: /* own parentheses */
9621 return true;
9622 default:
9623 return false;
9624 }
9625
9626 case T_JsonValueExpr:
9627 /* maybe simple, check args */
9628 return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
9629 node, prettyFlags);
9630
9631 default:
9632 break;
9633 }
9634 /* those we don't know: in dubio complexo */
9635 return false;
9636}
9637
9638
9639/*
9640 * appendContextKeyword - append a keyword to buffer
9641 *
9642 * If prettyPrint is enabled, perform a line break, and adjust indentation.
9643 * Otherwise, just append the keyword.
9644 */
9645static void
9646appendContextKeyword(deparse_context *context, const char *str,
9647 int indentBefore, int indentAfter, int indentPlus)
9648{
9649 StringInfo buf = context->buf;
9650
9651 if (PRETTY_INDENT(context))
9652 {
9653 int indentAmount;
9654
9655 context->indentLevel += indentBefore;
9656
9657 /* remove any trailing spaces currently in the buffer ... */
9659 /* ... then add a newline and some spaces */
9661
9662 if (context->indentLevel < PRETTYINDENT_LIMIT)
9663 indentAmount = Max(context->indentLevel, 0) + indentPlus;
9664 else
9665 {
9666 /*
9667 * If we're indented more than PRETTYINDENT_LIMIT characters, try
9668 * to conserve horizontal space by reducing the per-level
9669 * indentation. For best results the scale factor here should
9670 * divide all the indent amounts that get added to indentLevel
9671 * (PRETTYINDENT_STD, etc). It's important that the indentation
9672 * not grow unboundedly, else deeply-nested trees use O(N^2)
9673 * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
9674 */
9676 (context->indentLevel - PRETTYINDENT_LIMIT) /
9677 (PRETTYINDENT_STD / 2);
9679 /* scale/wrap logic affects indentLevel, but not indentPlus */
9681 }
9683
9685
9686 context->indentLevel += indentAfter;
9687 if (context->indentLevel < 0)
9688 context->indentLevel = 0;
9689 }
9690 else
9692}
9693
9694/*
9695 * removeStringInfoSpaces - delete trailing spaces from a buffer.
9696 *
9697 * Possibly this should move to stringinfo.c at some point.
9698 */
9699static void
9701{
9702 while (str->len > 0 && str->data[str->len - 1] == ' ')
9703 str->data[--(str->len)] = '\0';
9704}
9705
9706
9707/*
9708 * get_rule_expr_paren - deparse expr using get_rule_expr,
9709 * embracing the string with parentheses if necessary for prettyPrint.
9710 *
9711 * Never embrace if prettyFlags=0, because it's done in the calling node.
9712 *
9713 * Any node that does *not* embrace its argument node by sql syntax (with
9714 * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
9715 * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
9716 * added.
9717 */
9718static void
9721{
9722 bool need_paren;
9723
9724 need_paren = PRETTY_PAREN(context) &&
9725 !isSimpleNode(node, parentNode, context->prettyFlags);
9726
9727 if (need_paren)
9728 appendStringInfoChar(context->buf, '(');
9729
9730 get_rule_expr(node, context, showimplicit);
9731
9732 if (need_paren)
9733 appendStringInfoChar(context->buf, ')');
9734}
9735
9736static void
9738 const char *on)
9739{
9740 /*
9741 * The order of array elements must correspond to the order of
9742 * JsonBehaviorType members.
9743 */
9744 const char *behavior_names[] =
9745 {
9746 " NULL",
9747 " ERROR",
9748 " EMPTY",
9749 " TRUE",
9750 " FALSE",
9751 " UNKNOWN",
9752 " EMPTY ARRAY",
9753 " EMPTY OBJECT",
9754 " DEFAULT "
9755 };
9756
9757 if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
9758 elog(ERROR, "invalid json behavior type: %d", behavior->btype);
9759
9760 appendStringInfoString(context->buf, behavior_names[behavior->btype]);
9761
9762 if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
9763 get_rule_expr(behavior->expr, context, false);
9764
9765 appendStringInfo(context->buf, " ON %s", on);
9766}
9767
9768/*
9769 * get_json_expr_options
9770 *
9771 * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and
9772 * JSON_TABLE columns.
9773 */
9774static void
9777{
9778 if (jsexpr->op == JSON_QUERY_OP)
9779 {
9780 if (jsexpr->wrapper == JSW_CONDITIONAL)
9781 appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
9782 else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
9783 appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
9784 /* The default */
9785 else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
9786 appendStringInfoString(context->buf, " WITHOUT WRAPPER");
9787
9788 if (jsexpr->omit_quotes)
9789 appendStringInfoString(context->buf, " OMIT QUOTES");
9790 /* The default */
9791 else
9792 appendStringInfoString(context->buf, " KEEP QUOTES");
9793 }
9794
9795 if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
9796 get_json_behavior(jsexpr->on_empty, context, "EMPTY");
9797
9798 if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
9799 get_json_behavior(jsexpr->on_error, context, "ERROR");
9800}
9801
9802/* ----------
9803 * get_rule_expr - Parse back an expression
9804 *
9805 * Note: showimplicit determines whether we display any implicit cast that
9806 * is present at the top of the expression tree. It is a passed argument,
9807 * not a field of the context struct, because we change the value as we
9808 * recurse down into the expression. In general we suppress implicit casts
9809 * when the result type is known with certainty (eg, the arguments of an
9810 * OR must be boolean). We display implicit casts for arguments of functions
9811 * and operators, since this is needed to be certain that the same function
9812 * or operator will be chosen when the expression is re-parsed.
9813 * ----------
9814 */
9815static void
9816get_rule_expr(Node *node, deparse_context *context,
9817 bool showimplicit)
9818{
9819 StringInfo buf = context->buf;
9820
9821 if (node == NULL)
9822 return;
9823
9824 /* Guard against excessively long or deeply-nested queries */
9827
9828 /*
9829 * Each level of get_rule_expr must emit an indivisible term
9830 * (parenthesized if necessary) to ensure result is reparsed into the same
9831 * expression tree. The only exception is that when the input is a List,
9832 * we emit the component items comma-separated with no surrounding
9833 * decoration; this is convenient for most callers.
9834 */
9835 switch (nodeTag(node))
9836 {
9837 case T_Var:
9838 (void) get_variable((Var *) node, 0, false, context);
9839 break;
9840
9841 case T_Const:
9842 get_const_expr((Const *) node, context, 0);
9843 break;
9844
9845 case T_Param:
9846 get_parameter((Param *) node, context);
9847 break;
9848
9849 case T_Aggref:
9850 get_agg_expr((Aggref *) node, context, (Aggref *) node);
9851 break;
9852
9853 case T_GroupingFunc:
9854 {
9855 GroupingFunc *gexpr = (GroupingFunc *) node;
9856
9857 appendStringInfoString(buf, "GROUPING(");
9858 get_rule_expr((Node *) gexpr->args, context, true);
9860 }
9861 break;
9862
9863 case T_WindowFunc:
9864 get_windowfunc_expr((WindowFunc *) node, context);
9865 break;
9866
9867 case T_MergeSupportFunc:
9868 appendStringInfoString(buf, "MERGE_ACTION()");
9869 break;
9870
9871 case T_SubscriptingRef:
9872 {
9873 SubscriptingRef *sbsref = (SubscriptingRef *) node;
9874 bool need_parens;
9875
9876 /*
9877 * If the argument is a CaseTestExpr, we must be inside a
9878 * FieldStore, ie, we are assigning to an element of an array
9879 * within a composite column. Since we already punted on
9880 * displaying the FieldStore's target information, just punt
9881 * here too, and display only the assignment source
9882 * expression.
9883 */
9884 if (IsA(sbsref->refexpr, CaseTestExpr))
9885 {
9886 Assert(sbsref->refassgnexpr);
9887 get_rule_expr((Node *) sbsref->refassgnexpr,
9888 context, showimplicit);
9889 break;
9890 }
9891
9892 /*
9893 * Parenthesize the argument unless it's a simple Var or a
9894 * FieldSelect. (In particular, if it's another
9895 * SubscriptingRef, we *must* parenthesize to avoid
9896 * confusion.)
9897 */
9898 need_parens = !IsA(sbsref->refexpr, Var) &&
9899 !IsA(sbsref->refexpr, FieldSelect);
9900 if (need_parens)
9902 get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
9903 if (need_parens)
9905
9906 /*
9907 * If there's a refassgnexpr, we want to print the node in the
9908 * format "container[subscripts] := refassgnexpr". This is
9909 * not legal SQL, so decompilation of INSERT or UPDATE
9910 * statements should always use processIndirection as part of
9911 * the statement-level syntax. We should only see this when
9912 * EXPLAIN tries to print the targetlist of a plan resulting
9913 * from such a statement.
9914 */
9915 if (sbsref->refassgnexpr)
9916 {
9917 Node *refassgnexpr;
9918
9919 /*
9920 * Use processIndirection to print this node's subscripts
9921 * as well as any additional field selections or
9922 * subscripting in immediate descendants. It returns the
9923 * RHS expr that is actually being "assigned".
9924 */
9925 refassgnexpr = processIndirection(node, context);
9926 appendStringInfoString(buf, " := ");
9927 get_rule_expr(refassgnexpr, context, showimplicit);
9928 }
9929 else
9930 {
9931 /* Just an ordinary container fetch, so print subscripts */
9932 printSubscripts(sbsref, context);
9933 }
9934 }
9935 break;
9936
9937 case T_FuncExpr:
9938 get_func_expr((FuncExpr *) node, context, showimplicit);
9939 break;
9940
9941 case T_NamedArgExpr:
9942 {
9943 NamedArgExpr *na = (NamedArgExpr *) node;
9944
9945 appendStringInfo(buf, "%s => ", quote_identifier(na->name));
9946 get_rule_expr((Node *) na->arg, context, showimplicit);
9947 }
9948 break;
9949
9950 case T_OpExpr:
9951 get_oper_expr((OpExpr *) node, context);
9952 break;
9953
9954 case T_DistinctExpr:
9955 {
9956 DistinctExpr *expr = (DistinctExpr *) node;
9957 List *args = expr->args;
9958 Node *arg1 = (Node *) linitial(args);
9959 Node *arg2 = (Node *) lsecond(args);
9960
9961 if (!PRETTY_PAREN(context))
9963 get_rule_expr_paren(arg1, context, true, node);
9964 appendStringInfoString(buf, " IS DISTINCT FROM ");
9965 get_rule_expr_paren(arg2, context, true, node);
9966 if (!PRETTY_PAREN(context))
9968 }
9969 break;
9970
9971 case T_NullIfExpr:
9972 {
9973 NullIfExpr *nullifexpr = (NullIfExpr *) node;
9974
9975 appendStringInfoString(buf, "NULLIF(");
9976 get_rule_expr((Node *) nullifexpr->args, context, true);
9978 }
9979 break;
9980
9982 {
9983 ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
9984 List *args = expr->args;
9985 Node *arg1 = (Node *) linitial(args);
9986 Node *arg2 = (Node *) lsecond(args);
9987
9988 if (!PRETTY_PAREN(context))
9990 get_rule_expr_paren(arg1, context, true, node);
9991 appendStringInfo(buf, " %s %s (",
9993 exprType(arg1),
9995 expr->useOr ? "ANY" : "ALL");
9996 get_rule_expr_paren(arg2, context, true, node);
9997
9998 /*
9999 * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
10000 * a bare sub-SELECT. Since we're here, the sub-SELECT must
10001 * be meant as a scalar sub-SELECT yielding an array value to
10002 * be used in ScalarArrayOpExpr; but the grammar will
10003 * preferentially interpret such a construct as an ANY/ALL
10004 * SubLink. To prevent misparsing the output that way, insert
10005 * a dummy coercion (which will be stripped by parse analysis,
10006 * so no inefficiency is added in dump and reload). This is
10007 * indeed most likely what the user wrote to get the construct
10008 * accepted in the first place.
10009 */
10010 if (IsA(arg2, SubLink) &&
10011 ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
10012 appendStringInfo(buf, "::%s",
10014 exprTypmod(arg2)));
10016 if (!PRETTY_PAREN(context))
10018 }
10019 break;
10020
10021 case T_BoolExpr:
10022 {
10023 BoolExpr *expr = (BoolExpr *) node;
10024 Node *first_arg = linitial(expr->args);
10025 ListCell *arg;
10026
10027 switch (expr->boolop)
10028 {
10029 case AND_EXPR:
10030 if (!PRETTY_PAREN(context))
10033 false, node);
10034 for_each_from(arg, expr->args, 1)
10035 {
10036 appendStringInfoString(buf, " AND ");
10037 get_rule_expr_paren((Node *) lfirst(arg), context,
10038 false, node);
10039 }
10040 if (!PRETTY_PAREN(context))
10042 break;
10043
10044 case OR_EXPR:
10045 if (!PRETTY_PAREN(context))
10048 false, node);
10049 for_each_from(arg, expr->args, 1)
10050 {
10051 appendStringInfoString(buf, " OR ");
10052 get_rule_expr_paren((Node *) lfirst(arg), context,
10053 false, node);
10054 }
10055 if (!PRETTY_PAREN(context))
10057 break;
10058
10059 case NOT_EXPR:
10060 if (!PRETTY_PAREN(context))
10062 appendStringInfoString(buf, "NOT ");
10064 false, node);
10065 if (!PRETTY_PAREN(context))
10067 break;
10068
10069 default:
10070 elog(ERROR, "unrecognized boolop: %d",
10071 (int) expr->boolop);
10072 }
10073 }
10074 break;
10075
10076 case T_SubLink:
10077 get_sublink_expr((SubLink *) node, context);
10078 break;
10079
10080 case T_SubPlan:
10081 {
10082 SubPlan *subplan = (SubPlan *) node;
10083
10084 /*
10085 * We cannot see an already-planned subplan in rule deparsing,
10086 * only while EXPLAINing a query plan. We don't try to
10087 * reconstruct the original SQL, just reference the subplan
10088 * that appears elsewhere in EXPLAIN's result. It does seem
10089 * useful to show the subLinkType and testexpr (if any), and
10090 * we also note whether the subplan will be hashed.
10091 */
10092 switch (subplan->subLinkType)
10093 {
10094 case EXISTS_SUBLINK:
10095 appendStringInfoString(buf, "EXISTS(");
10096 Assert(subplan->testexpr == NULL);
10097 break;
10098 case ALL_SUBLINK:
10099 appendStringInfoString(buf, "(ALL ");
10100 Assert(subplan->testexpr != NULL);
10101 break;
10102 case ANY_SUBLINK:
10103 appendStringInfoString(buf, "(ANY ");
10104 Assert(subplan->testexpr != NULL);
10105 break;
10106 case ROWCOMPARE_SUBLINK:
10107 /* Parenthesizing the testexpr seems sufficient */
10109 Assert(subplan->testexpr != NULL);
10110 break;
10111 case EXPR_SUBLINK:
10112 /* No need to decorate these subplan references */
10114 Assert(subplan->testexpr == NULL);
10115 break;
10116 case MULTIEXPR_SUBLINK:
10117 /* MULTIEXPR isn't executed in the normal way */
10118 appendStringInfoString(buf, "(rescan ");
10119 Assert(subplan->testexpr == NULL);
10120 break;
10121 case ARRAY_SUBLINK:
10122 appendStringInfoString(buf, "ARRAY(");
10123 Assert(subplan->testexpr == NULL);
10124 break;
10125 case CTE_SUBLINK:
10126 /* This case is unreachable within expressions */
10127 appendStringInfoString(buf, "CTE(");
10128 Assert(subplan->testexpr == NULL);
10129 break;
10130 }
10131
10132 if (subplan->testexpr != NULL)
10133 {
10135
10136 /*
10137 * Push SubPlan into ancestors list while deparsing
10138 * testexpr, so that we can handle PARAM_EXEC references
10139 * to the SubPlan's paramIds. (This makes it look like
10140 * the SubPlan is an "ancestor" of the current plan node,
10141 * which is a little weird, but it does no harm.) In this
10142 * path, we don't need to mention the SubPlan explicitly,
10143 * because the referencing Params will show its existence.
10144 */
10145 dpns = (deparse_namespace *) linitial(context->namespaces);
10146 dpns->ancestors = lcons(subplan, dpns->ancestors);
10147
10148 get_rule_expr(subplan->testexpr, context, showimplicit);
10150
10151 dpns->ancestors = list_delete_first(dpns->ancestors);
10152 }
10153 else
10154 {
10155 const char *nameprefix;
10156
10157 /* No referencing Params, so show the SubPlan's name */
10158 if (subplan->isInitPlan)
10159 nameprefix = "InitPlan ";
10160 else
10161 nameprefix = "SubPlan ";
10162 if (subplan->useHashTable)
10163 appendStringInfo(buf, "hashed %s%s)",
10164 nameprefix, subplan->plan_name);
10165 else
10166 appendStringInfo(buf, "%s%s)",
10167 nameprefix, subplan->plan_name);
10168 }
10169 }
10170 break;
10171
10173 {
10175 ListCell *lc;
10176
10177 /*
10178 * This case cannot be reached in normal usage, since no
10179 * AlternativeSubPlan can appear either in parsetrees or
10180 * finished plan trees. We keep it just in case somebody
10181 * wants to use this code to print planner data structures.
10182 */
10183 appendStringInfoString(buf, "(alternatives: ");
10184 foreach(lc, asplan->subplans)
10185 {
10186 SubPlan *splan = lfirst_node(SubPlan, lc);
10187 const char *nameprefix;
10188
10189 if (splan->isInitPlan)
10190 nameprefix = "InitPlan ";
10191 else
10192 nameprefix = "SubPlan ";
10193 if (splan->useHashTable)
10194 appendStringInfo(buf, "hashed %s%s", nameprefix,
10195 splan->plan_name);
10196 else
10198 splan->plan_name);
10199 if (lnext(asplan->subplans, lc))
10200 appendStringInfoString(buf, " or ");
10201 }
10203 }
10204 break;
10205
10206 case T_FieldSelect:
10207 {
10208 FieldSelect *fselect = (FieldSelect *) node;
10209 Node *arg = (Node *) fselect->arg;
10210 int fno = fselect->fieldnum;
10211 const char *fieldname;
10212 bool need_parens;
10213
10214 /*
10215 * Parenthesize the argument unless it's a SubscriptingRef or
10216 * another FieldSelect. Note in particular that it would be
10217 * WRONG to not parenthesize a Var argument; simplicity is not
10218 * the issue here, having the right number of names is.
10219 */
10221 !IsA(arg, FieldSelect);
10222 if (need_parens)
10224 get_rule_expr(arg, context, true);
10225 if (need_parens)
10227
10228 /*
10229 * Get and print the field name.
10230 */
10231 fieldname = get_name_for_var_field((Var *) arg, fno,
10232 0, context);
10233 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
10234 }
10235 break;
10236
10237 case T_FieldStore:
10238 {
10239 FieldStore *fstore = (FieldStore *) node;
10240 bool need_parens;
10241
10242 /*
10243 * There is no good way to represent a FieldStore as real SQL,
10244 * so decompilation of INSERT or UPDATE statements should
10245 * always use processIndirection as part of the
10246 * statement-level syntax. We should only get here when
10247 * EXPLAIN tries to print the targetlist of a plan resulting
10248 * from such a statement. The plan case is even harder than
10249 * ordinary rules would be, because the planner tries to
10250 * collapse multiple assignments to the same field or subfield
10251 * into one FieldStore; so we can see a list of target fields
10252 * not just one, and the arguments could be FieldStores
10253 * themselves. We don't bother to try to print the target
10254 * field names; we just print the source arguments, with a
10255 * ROW() around them if there's more than one. This isn't
10256 * terribly complete, but it's probably good enough for
10257 * EXPLAIN's purposes; especially since anything more would be
10258 * either hopelessly confusing or an even poorer
10259 * representation of what the plan is actually doing.
10260 */
10261 need_parens = (list_length(fstore->newvals) != 1);
10262 if (need_parens)
10263 appendStringInfoString(buf, "ROW(");
10264 get_rule_expr((Node *) fstore->newvals, context, showimplicit);
10265 if (need_parens)
10267 }
10268 break;
10269
10270 case T_RelabelType:
10271 {
10272 RelabelType *relabel = (RelabelType *) node;
10273 Node *arg = (Node *) relabel->arg;
10274
10275 if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
10276 !showimplicit)
10277 {
10278 /* don't show the implicit cast */
10279 get_rule_expr_paren(arg, context, false, node);
10280 }
10281 else
10282 {
10283 get_coercion_expr(arg, context,
10284 relabel->resulttype,
10285 relabel->resulttypmod,
10286 node);
10287 }
10288 }
10289 break;
10290
10291 case T_CoerceViaIO:
10292 {
10293 CoerceViaIO *iocoerce = (CoerceViaIO *) node;
10294 Node *arg = (Node *) iocoerce->arg;
10295
10296 if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
10297 !showimplicit)
10298 {
10299 /* don't show the implicit cast */
10300 get_rule_expr_paren(arg, context, false, node);
10301 }
10302 else
10303 {
10304 get_coercion_expr(arg, context,
10305 iocoerce->resulttype,
10306 -1,
10307 node);
10308 }
10309 }
10310 break;
10311
10312 case T_ArrayCoerceExpr:
10313 {
10315 Node *arg = (Node *) acoerce->arg;
10316
10317 if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
10318 !showimplicit)
10319 {
10320 /* don't show the implicit cast */
10321 get_rule_expr_paren(arg, context, false, node);
10322 }
10323 else
10324 {
10325 get_coercion_expr(arg, context,
10326 acoerce->resulttype,
10327 acoerce->resulttypmod,
10328 node);
10329 }
10330 }
10331 break;
10332
10334 {
10336 Node *arg = (Node *) convert->arg;
10337
10338 if (convert->convertformat == COERCE_IMPLICIT_CAST &&
10339 !showimplicit)
10340 {
10341 /* don't show the implicit cast */
10342 get_rule_expr_paren(arg, context, false, node);
10343 }
10344 else
10345 {
10346 get_coercion_expr(arg, context,
10347 convert->resulttype, -1,
10348 node);
10349 }
10350 }
10351 break;
10352
10353 case T_CollateExpr:
10354 {
10355 CollateExpr *collate = (CollateExpr *) node;
10356 Node *arg = (Node *) collate->arg;
10357
10358 if (!PRETTY_PAREN(context))
10360 get_rule_expr_paren(arg, context, showimplicit, node);
10361 appendStringInfo(buf, " COLLATE %s",
10362 generate_collation_name(collate->collOid));
10363 if (!PRETTY_PAREN(context))
10365 }
10366 break;
10367
10368 case T_CaseExpr:
10369 {
10370 CaseExpr *caseexpr = (CaseExpr *) node;
10371 ListCell *temp;
10372
10373 appendContextKeyword(context, "CASE",
10374 0, PRETTYINDENT_VAR, 0);
10375 if (caseexpr->arg)
10376 {
10378 get_rule_expr((Node *) caseexpr->arg, context, true);
10379 }
10380 foreach(temp, caseexpr->args)
10381 {
10383 Node *w = (Node *) when->expr;
10384
10385 if (caseexpr->arg)
10386 {
10387 /*
10388 * The parser should have produced WHEN clauses of the
10389 * form "CaseTestExpr = RHS", possibly with an
10390 * implicit coercion inserted above the CaseTestExpr.
10391 * For accurate decompilation of rules it's essential
10392 * that we show just the RHS. However in an
10393 * expression that's been through the optimizer, the
10394 * WHEN clause could be almost anything (since the
10395 * equality operator could have been expanded into an
10396 * inline function). If we don't recognize the form
10397 * of the WHEN clause, just punt and display it as-is.
10398 */
10399 if (IsA(w, OpExpr))
10400 {
10401 List *args = ((OpExpr *) w)->args;
10402
10403 if (list_length(args) == 2 &&
10405 CaseTestExpr))
10406 w = (Node *) lsecond(args);
10407 }
10408 }
10409
10410 if (!PRETTY_INDENT(context))
10412 appendContextKeyword(context, "WHEN ",
10413 0, 0, 0);
10414 get_rule_expr(w, context, false);
10415 appendStringInfoString(buf, " THEN ");
10416 get_rule_expr((Node *) when->result, context, true);
10417 }
10418 if (!PRETTY_INDENT(context))
10420 appendContextKeyword(context, "ELSE ",
10421 0, 0, 0);
10422 get_rule_expr((Node *) caseexpr->defresult, context, true);
10423 if (!PRETTY_INDENT(context))
10425 appendContextKeyword(context, "END",
10426 -PRETTYINDENT_VAR, 0, 0);
10427 }
10428 break;
10429
10430 case T_CaseTestExpr:
10431 {
10432 /*
10433 * Normally we should never get here, since for expressions
10434 * that can contain this node type we attempt to avoid
10435 * recursing to it. But in an optimized expression we might
10436 * be unable to avoid that (see comments for CaseExpr). If we
10437 * do see one, print it as CASE_TEST_EXPR.
10438 */
10439 appendStringInfoString(buf, "CASE_TEST_EXPR");
10440 }
10441 break;
10442
10443 case T_ArrayExpr:
10444 {
10445 ArrayExpr *arrayexpr = (ArrayExpr *) node;
10446
10447 appendStringInfoString(buf, "ARRAY[");
10448 get_rule_expr((Node *) arrayexpr->elements, context, true);
10450
10451 /*
10452 * If the array isn't empty, we assume its elements are
10453 * coerced to the desired type. If it's empty, though, we
10454 * need an explicit coercion to the array type.
10455 */
10456 if (arrayexpr->elements == NIL)
10457 appendStringInfo(buf, "::%s",
10458 format_type_with_typemod(arrayexpr->array_typeid, -1));
10459 }
10460 break;
10461
10462 case T_RowExpr:
10463 {
10464 RowExpr *rowexpr = (RowExpr *) node;
10465 TupleDesc tupdesc = NULL;
10466 ListCell *arg;
10467 int i;
10468 char *sep;
10469
10470 /*
10471 * If it's a named type and not RECORD, we may have to skip
10472 * dropped columns and/or claim there are NULLs for added
10473 * columns.
10474 */
10475 if (rowexpr->row_typeid != RECORDOID)
10476 {
10477 tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
10478 Assert(list_length(rowexpr->args) <= tupdesc->natts);
10479 }
10480
10481 /*
10482 * SQL99 allows "ROW" to be omitted when there is more than
10483 * one column, but for simplicity we always print it.
10484 */
10485 appendStringInfoString(buf, "ROW(");
10486 sep = "";
10487 i = 0;
10488 foreach(arg, rowexpr->args)
10489 {
10490 Node *e = (Node *) lfirst(arg);
10491
10492 if (tupdesc == NULL ||
10494 {
10496 /* Whole-row Vars need special treatment here */
10497 get_rule_expr_toplevel(e, context, true);
10498 sep = ", ";
10499 }
10500 i++;
10501 }
10502 if (tupdesc != NULL)
10503 {
10504 while (i < tupdesc->natts)
10505 {
10506 if (!TupleDescCompactAttr(tupdesc, i)->attisdropped)
10507 {
10509 appendStringInfoString(buf, "NULL");
10510 sep = ", ";
10511 }
10512 i++;
10513 }
10514
10515 ReleaseTupleDesc(tupdesc);
10516 }
10518 if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
10519 appendStringInfo(buf, "::%s",
10520 format_type_with_typemod(rowexpr->row_typeid, -1));
10521 }
10522 break;
10523
10524 case T_RowCompareExpr:
10525 {
10527
10528 /*
10529 * SQL99 allows "ROW" to be omitted when there is more than
10530 * one column, but for simplicity we always print it. Within
10531 * a ROW expression, whole-row Vars need special treatment, so
10532 * use get_rule_list_toplevel.
10533 */
10534 appendStringInfoString(buf, "(ROW(");
10535 get_rule_list_toplevel(rcexpr->largs, context, true);
10536
10537 /*
10538 * We assume that the name of the first-column operator will
10539 * do for all the rest too. This is definitely open to
10540 * failure, eg if some but not all operators were renamed
10541 * since the construct was parsed, but there seems no way to
10542 * be perfect.
10543 */
10544 appendStringInfo(buf, ") %s ROW(",
10546 exprType(linitial(rcexpr->largs)),
10547 exprType(linitial(rcexpr->rargs))));
10548 get_rule_list_toplevel(rcexpr->rargs, context, true);
10550 }
10551 break;
10552
10553 case T_CoalesceExpr:
10554 {
10556
10557 appendStringInfoString(buf, "COALESCE(");
10558 get_rule_expr((Node *) coalesceexpr->args, context, true);
10560 }
10561 break;
10562
10563 case T_MinMaxExpr:
10564 {
10565 MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
10566
10567 switch (minmaxexpr->op)
10568 {
10569 case IS_GREATEST:
10570 appendStringInfoString(buf, "GREATEST(");
10571 break;
10572 case IS_LEAST:
10573 appendStringInfoString(buf, "LEAST(");
10574 break;
10575 }
10576 get_rule_expr((Node *) minmaxexpr->args, context, true);
10578 }
10579 break;
10580
10581 case T_SQLValueFunction:
10582 {
10583 SQLValueFunction *svf = (SQLValueFunction *) node;
10584
10585 /*
10586 * Note: this code knows that typmod for time, timestamp, and
10587 * timestamptz just prints as integer.
10588 */
10589 switch (svf->op)
10590 {
10591 case SVFOP_CURRENT_DATE:
10592 appendStringInfoString(buf, "CURRENT_DATE");
10593 break;
10594 case SVFOP_CURRENT_TIME:
10595 appendStringInfoString(buf, "CURRENT_TIME");
10596 break;
10598 appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
10599 break;
10601 appendStringInfoString(buf, "CURRENT_TIMESTAMP");
10602 break;
10604 appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
10605 svf->typmod);
10606 break;
10607 case SVFOP_LOCALTIME:
10608 appendStringInfoString(buf, "LOCALTIME");
10609 break;
10610 case SVFOP_LOCALTIME_N:
10611 appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
10612 break;
10614 appendStringInfoString(buf, "LOCALTIMESTAMP");
10615 break;
10617 appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
10618 svf->typmod);
10619 break;
10620 case SVFOP_CURRENT_ROLE:
10621 appendStringInfoString(buf, "CURRENT_ROLE");
10622 break;
10623 case SVFOP_CURRENT_USER:
10624 appendStringInfoString(buf, "CURRENT_USER");
10625 break;
10626 case SVFOP_USER:
10627 appendStringInfoString(buf, "USER");
10628 break;
10629 case SVFOP_SESSION_USER:
10630 appendStringInfoString(buf, "SESSION_USER");
10631 break;
10633 appendStringInfoString(buf, "CURRENT_CATALOG");
10634 break;
10636 appendStringInfoString(buf, "CURRENT_SCHEMA");
10637 break;
10638 }
10639 }
10640 break;
10641
10642 case T_XmlExpr:
10643 {
10644 XmlExpr *xexpr = (XmlExpr *) node;
10645 bool needcomma = false;
10646 ListCell *arg;
10647 ListCell *narg;
10648 Const *con;
10649
10650 switch (xexpr->op)
10651 {
10652 case IS_XMLCONCAT:
10653 appendStringInfoString(buf, "XMLCONCAT(");
10654 break;
10655 case IS_XMLELEMENT:
10656 appendStringInfoString(buf, "XMLELEMENT(");
10657 break;
10658 case IS_XMLFOREST:
10659 appendStringInfoString(buf, "XMLFOREST(");
10660 break;
10661 case IS_XMLPARSE:
10662 appendStringInfoString(buf, "XMLPARSE(");
10663 break;
10664 case IS_XMLPI:
10665 appendStringInfoString(buf, "XMLPI(");
10666 break;
10667 case IS_XMLROOT:
10668 appendStringInfoString(buf, "XMLROOT(");
10669 break;
10670 case IS_XMLSERIALIZE:
10671 appendStringInfoString(buf, "XMLSERIALIZE(");
10672 break;
10673 case IS_DOCUMENT:
10674 break;
10675 }
10676 if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
10677 {
10678 if (xexpr->xmloption == XMLOPTION_DOCUMENT)
10679 appendStringInfoString(buf, "DOCUMENT ");
10680 else
10681 appendStringInfoString(buf, "CONTENT ");
10682 }
10683 if (xexpr->name)
10684 {
10685 appendStringInfo(buf, "NAME %s",
10687 needcomma = true;
10688 }
10689 if (xexpr->named_args)
10690 {
10691 if (xexpr->op != IS_XMLFOREST)
10692 {
10693 if (needcomma)
10695 appendStringInfoString(buf, "XMLATTRIBUTES(");
10696 needcomma = false;
10697 }
10698 forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
10699 {
10700 Node *e = (Node *) lfirst(arg);
10701 char *argname = strVal(lfirst(narg));
10702
10703 if (needcomma)
10705 get_rule_expr(e, context, true);
10706 appendStringInfo(buf, " AS %s",
10708 needcomma = true;
10709 }
10710 if (xexpr->op != IS_XMLFOREST)
10712 }
10713 if (xexpr->args)
10714 {
10715 if (needcomma)
10717 switch (xexpr->op)
10718 {
10719 case IS_XMLCONCAT:
10720 case IS_XMLELEMENT:
10721 case IS_XMLFOREST:
10722 case IS_XMLPI:
10723 case IS_XMLSERIALIZE:
10724 /* no extra decoration needed */
10725 get_rule_expr((Node *) xexpr->args, context, true);
10726 break;
10727 case IS_XMLPARSE:
10728 Assert(list_length(xexpr->args) == 2);
10729
10730 get_rule_expr((Node *) linitial(xexpr->args),
10731 context, true);
10732
10733 con = lsecond_node(Const, xexpr->args);
10734 Assert(!con->constisnull);
10735 if (DatumGetBool(con->constvalue))
10737 " PRESERVE WHITESPACE");
10738 else
10740 " STRIP WHITESPACE");
10741 break;
10742 case IS_XMLROOT:
10743 Assert(list_length(xexpr->args) == 3);
10744
10745 get_rule_expr((Node *) linitial(xexpr->args),
10746 context, true);
10747
10748 appendStringInfoString(buf, ", VERSION ");
10749 con = (Const *) lsecond(xexpr->args);
10750 if (IsA(con, Const) &&
10751 con->constisnull)
10752 appendStringInfoString(buf, "NO VALUE");
10753 else
10754 get_rule_expr((Node *) con, context, false);
10755
10756 con = lthird_node(Const, xexpr->args);
10757 if (con->constisnull)
10758 /* suppress STANDALONE NO VALUE */ ;
10759 else
10760 {
10761 switch (DatumGetInt32(con->constvalue))
10762 {
10763 case XML_STANDALONE_YES:
10765 ", STANDALONE YES");
10766 break;
10767 case XML_STANDALONE_NO:
10769 ", STANDALONE NO");
10770 break;
10773 ", STANDALONE NO VALUE");
10774 break;
10775 default:
10776 break;
10777 }
10778 }
10779 break;
10780 case IS_DOCUMENT:
10781 get_rule_expr_paren((Node *) xexpr->args, context, false, node);
10782 break;
10783 }
10784 }
10785 if (xexpr->op == IS_XMLSERIALIZE)
10786 {
10787 appendStringInfo(buf, " AS %s",
10788 format_type_with_typemod(xexpr->type,
10789 xexpr->typmod));
10790 if (xexpr->indent)
10791 appendStringInfoString(buf, " INDENT");
10792 else
10793 appendStringInfoString(buf, " NO INDENT");
10794 }
10795
10796 if (xexpr->op == IS_DOCUMENT)
10797 appendStringInfoString(buf, " IS DOCUMENT");
10798 else
10800 }
10801 break;
10802
10803 case T_NullTest:
10804 {
10805 NullTest *ntest = (NullTest *) node;
10806
10807 if (!PRETTY_PAREN(context))
10809 get_rule_expr_paren((Node *) ntest->arg, context, true, node);
10810
10811 /*
10812 * For scalar inputs, we prefer to print as IS [NOT] NULL,
10813 * which is shorter and traditional. If it's a rowtype input
10814 * but we're applying a scalar test, must print IS [NOT]
10815 * DISTINCT FROM NULL to be semantically correct.
10816 */
10817 if (ntest->argisrow ||
10818 !type_is_rowtype(exprType((Node *) ntest->arg)))
10819 {
10820 switch (ntest->nulltesttype)
10821 {
10822 case IS_NULL:
10823 appendStringInfoString(buf, " IS NULL");
10824 break;
10825 case IS_NOT_NULL:
10826 appendStringInfoString(buf, " IS NOT NULL");
10827 break;
10828 default:
10829 elog(ERROR, "unrecognized nulltesttype: %d",
10830 (int) ntest->nulltesttype);
10831 }
10832 }
10833 else
10834 {
10835 switch (ntest->nulltesttype)
10836 {
10837 case IS_NULL:
10838 appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
10839 break;
10840 case IS_NOT_NULL:
10841 appendStringInfoString(buf, " IS DISTINCT FROM NULL");
10842 break;
10843 default:
10844 elog(ERROR, "unrecognized nulltesttype: %d",
10845 (int) ntest->nulltesttype);
10846 }
10847 }
10848 if (!PRETTY_PAREN(context))
10850 }
10851 break;
10852
10853 case T_BooleanTest:
10854 {
10855 BooleanTest *btest = (BooleanTest *) node;
10856
10857 if (!PRETTY_PAREN(context))
10859 get_rule_expr_paren((Node *) btest->arg, context, false, node);
10860 switch (btest->booltesttype)
10861 {
10862 case IS_TRUE:
10863 appendStringInfoString(buf, " IS TRUE");
10864 break;
10865 case IS_NOT_TRUE:
10866 appendStringInfoString(buf, " IS NOT TRUE");
10867 break;
10868 case IS_FALSE:
10869 appendStringInfoString(buf, " IS FALSE");
10870 break;
10871 case IS_NOT_FALSE:
10872 appendStringInfoString(buf, " IS NOT FALSE");
10873 break;
10874 case IS_UNKNOWN:
10875 appendStringInfoString(buf, " IS UNKNOWN");
10876 break;
10877 case IS_NOT_UNKNOWN:
10878 appendStringInfoString(buf, " IS NOT UNKNOWN");
10879 break;
10880 default:
10881 elog(ERROR, "unrecognized booltesttype: %d",
10882 (int) btest->booltesttype);
10883 }
10884 if (!PRETTY_PAREN(context))
10886 }
10887 break;
10888
10889 case T_CoerceToDomain:
10890 {
10892 Node *arg = (Node *) ctest->arg;
10893
10894 if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
10895 !showimplicit)
10896 {
10897 /* don't show the implicit cast */
10898 get_rule_expr(arg, context, false);
10899 }
10900 else
10901 {
10902 get_coercion_expr(arg, context,
10903 ctest->resulttype,
10904 ctest->resulttypmod,
10905 node);
10906 }
10907 }
10908 break;
10909
10911 appendStringInfoString(buf, "VALUE");
10912 break;
10913
10914 case T_SetToDefault:
10915 appendStringInfoString(buf, "DEFAULT");
10916 break;
10917
10918 case T_CurrentOfExpr:
10919 {
10920 CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
10921
10922 if (cexpr->cursor_name)
10923 appendStringInfo(buf, "CURRENT OF %s",
10925 else
10926 appendStringInfo(buf, "CURRENT OF $%d",
10927 cexpr->cursor_param);
10928 }
10929 break;
10930
10931 case T_NextValueExpr:
10932 {
10934
10935 /*
10936 * This isn't exactly nextval(), but that seems close enough
10937 * for EXPLAIN's purposes.
10938 */
10939 appendStringInfoString(buf, "nextval(");
10942 NIL));
10944 }
10945 break;
10946
10947 case T_InferenceElem:
10948 {
10949 InferenceElem *iexpr = (InferenceElem *) node;
10950 bool save_varprefix;
10951 bool need_parens;
10952
10953 /*
10954 * InferenceElem can only refer to target relation, so a
10955 * prefix is not useful, and indeed would cause parse errors.
10956 */
10957 save_varprefix = context->varprefix;
10958 context->varprefix = false;
10959
10960 /*
10961 * Parenthesize the element unless it's a simple Var or a bare
10962 * function call. Follows pg_get_indexdef_worker().
10963 */
10964 need_parens = !IsA(iexpr->expr, Var);
10965 if (IsA(iexpr->expr, FuncExpr) &&
10966 ((FuncExpr *) iexpr->expr)->funcformat ==
10968 need_parens = false;
10969
10970 if (need_parens)
10972 get_rule_expr((Node *) iexpr->expr,
10973 context, false);
10974 if (need_parens)
10976
10977 context->varprefix = save_varprefix;
10978
10979 if (iexpr->infercollid)
10980 appendStringInfo(buf, " COLLATE %s",
10981 generate_collation_name(iexpr->infercollid));
10982
10983 /* Add the operator class name, if not default */
10984 if (iexpr->inferopclass)
10985 {
10986 Oid inferopclass = iexpr->inferopclass;
10988
10989 get_opclass_name(inferopclass, inferopcinputtype, buf);
10990 }
10991 }
10992 break;
10993
10994 case T_ReturningExpr:
10995 {
10997
10998 /*
10999 * We cannot see a ReturningExpr in rule deparsing, only while
11000 * EXPLAINing a query plan (ReturningExpr nodes are only ever
11001 * adding during query rewriting). Just display the expression
11002 * returned (an expanded view column).
11003 */
11004 get_rule_expr((Node *) retExpr->retexpr, context, showimplicit);
11005 }
11006 break;
11007
11009 {
11011 ListCell *cell;
11012 char *sep;
11013
11014 if (spec->is_default)
11015 {
11016 appendStringInfoString(buf, "DEFAULT");
11017 break;
11018 }
11019
11020 switch (spec->strategy)
11021 {
11023 Assert(spec->modulus > 0 && spec->remainder >= 0);
11024 Assert(spec->modulus > spec->remainder);
11025
11026 appendStringInfoString(buf, "FOR VALUES");
11027 appendStringInfo(buf, " WITH (modulus %d, remainder %d)",
11028 spec->modulus, spec->remainder);
11029 break;
11030
11032 Assert(spec->listdatums != NIL);
11033
11034 appendStringInfoString(buf, "FOR VALUES IN (");
11035 sep = "";
11036 foreach(cell, spec->listdatums)
11037 {
11038 Const *val = lfirst_node(Const, cell);
11039
11041 get_const_expr(val, context, -1);
11042 sep = ", ";
11043 }
11044
11046 break;
11047
11049 Assert(spec->lowerdatums != NIL &&
11050 spec->upperdatums != NIL &&
11051 list_length(spec->lowerdatums) ==
11052 list_length(spec->upperdatums));
11053
11054 appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
11055 get_range_partbound_string(spec->lowerdatums),
11056 get_range_partbound_string(spec->upperdatums));
11057 break;
11058
11059 default:
11060 elog(ERROR, "unrecognized partition strategy: %d",
11061 (int) spec->strategy);
11062 break;
11063 }
11064 }
11065 break;
11066
11067 case T_JsonValueExpr:
11068 {
11069 JsonValueExpr *jve = (JsonValueExpr *) node;
11070
11071 get_rule_expr((Node *) jve->raw_expr, context, false);
11072 get_json_format(jve->format, context->buf);
11073 }
11074 break;
11075
11077 get_json_constructor((JsonConstructorExpr *) node, context, false);
11078 break;
11079
11080 case T_JsonIsPredicate:
11081 {
11082 JsonIsPredicate *pred = (JsonIsPredicate *) node;
11083
11084 if (!PRETTY_PAREN(context))
11085 appendStringInfoChar(context->buf, '(');
11086
11087 get_rule_expr_paren(pred->expr, context, true, node);
11088
11089 appendStringInfoString(context->buf, " IS JSON");
11090
11091 /* TODO: handle FORMAT clause */
11092
11093 switch (pred->item_type)
11094 {
11095 case JS_TYPE_SCALAR:
11096 appendStringInfoString(context->buf, " SCALAR");
11097 break;
11098 case JS_TYPE_ARRAY:
11099 appendStringInfoString(context->buf, " ARRAY");
11100 break;
11101 case JS_TYPE_OBJECT:
11102 appendStringInfoString(context->buf, " OBJECT");
11103 break;
11104 default:
11105 break;
11106 }
11107
11108 if (pred->unique_keys)
11109 appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
11110
11111 if (!PRETTY_PAREN(context))
11112 appendStringInfoChar(context->buf, ')');
11113 }
11114 break;
11115
11116 case T_JsonExpr:
11117 {
11118 JsonExpr *jexpr = (JsonExpr *) node;
11119
11120 switch (jexpr->op)
11121 {
11122 case JSON_EXISTS_OP:
11123 appendStringInfoString(buf, "JSON_EXISTS(");
11124 break;
11125 case JSON_QUERY_OP:
11126 appendStringInfoString(buf, "JSON_QUERY(");
11127 break;
11128 case JSON_VALUE_OP:
11129 appendStringInfoString(buf, "JSON_VALUE(");
11130 break;
11131 default:
11132 elog(ERROR, "unrecognized JsonExpr op: %d",
11133 (int) jexpr->op);
11134 }
11135
11136 get_rule_expr(jexpr->formatted_expr, context, showimplicit);
11137
11139
11140 get_json_path_spec(jexpr->path_spec, context, showimplicit);
11141
11142 if (jexpr->passing_values)
11143 {
11144 ListCell *lc1,
11145 *lc2;
11146 bool needcomma = false;
11147
11148 appendStringInfoString(buf, " PASSING ");
11149
11150 forboth(lc1, jexpr->passing_names,
11151 lc2, jexpr->passing_values)
11152 {
11153 if (needcomma)
11155 needcomma = true;
11156
11157 get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
11158 appendStringInfo(buf, " AS %s",
11160 }
11161 }
11162
11163 if (jexpr->op != JSON_EXISTS_OP ||
11164 jexpr->returning->typid != BOOLOID)
11165 get_json_returning(jexpr->returning, context->buf,
11166 jexpr->op == JSON_QUERY_OP);
11167
11169 jexpr->op != JSON_EXISTS_OP ?
11172
11174 }
11175 break;
11176
11177 case T_List:
11178 {
11179 char *sep;
11180 ListCell *l;
11181
11182 sep = "";
11183 foreach(l, (List *) node)
11184 {
11186 get_rule_expr((Node *) lfirst(l), context, showimplicit);
11187 sep = ", ";
11188 }
11189 }
11190 break;
11191
11192 case T_TableFunc:
11193 get_tablefunc((TableFunc *) node, context, showimplicit);
11194 break;
11195
11196 case T_GraphPropertyRef:
11197 {
11199
11201 break;
11202 }
11203
11204 default:
11205 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
11206 break;
11207 }
11208}
11209
11210/*
11211 * get_rule_expr_toplevel - Parse back a toplevel expression
11212 *
11213 * Same as get_rule_expr(), except that if the expr is just a Var, we pass
11214 * istoplevel = true not false to get_variable(). This causes whole-row Vars
11215 * to get printed with decoration that will prevent expansion of "*".
11216 * We need to use this in contexts such as ROW() and VALUES(), where the
11217 * parser would expand "foo.*" appearing at top level. (In principle we'd
11218 * use this in get_target_list() too, but that has additional worries about
11219 * whether to print AS, so it needs to invoke get_variable() directly anyway.)
11220 */
11221static void
11223 bool showimplicit)
11224{
11225 if (node && IsA(node, Var))
11226 (void) get_variable((Var *) node, 0, true, context);
11227 else
11228 get_rule_expr(node, context, showimplicit);
11229}
11230
11231/*
11232 * get_rule_list_toplevel - Parse back a list of toplevel expressions
11233 *
11234 * Apply get_rule_expr_toplevel() to each element of a List.
11235 *
11236 * This adds commas between the expressions, but caller is responsible
11237 * for printing surrounding decoration.
11238 */
11239static void
11241 bool showimplicit)
11242{
11243 const char *sep;
11244 ListCell *lc;
11245
11246 sep = "";
11247 foreach(lc, lst)
11248 {
11249 Node *e = (Node *) lfirst(lc);
11250
11251 appendStringInfoString(context->buf, sep);
11253 sep = ", ";
11254 }
11255}
11256
11257/*
11258 * get_rule_expr_funccall - Parse back a function-call expression
11259 *
11260 * Same as get_rule_expr(), except that we guarantee that the output will
11261 * look like a function call, or like one of the things the grammar treats as
11262 * equivalent to a function call (see the func_expr_windowless production).
11263 * This is needed in places where the grammar uses func_expr_windowless and
11264 * you can't substitute a parenthesized a_expr. If what we have isn't going
11265 * to look like a function call, wrap it in a dummy CAST() expression, which
11266 * will satisfy the grammar --- and, indeed, is likely what the user wrote to
11267 * produce such a thing.
11268 */
11269static void
11271 bool showimplicit)
11272{
11273 if (looks_like_function(node))
11274 get_rule_expr(node, context, showimplicit);
11275 else
11276 {
11277 StringInfo buf = context->buf;
11278
11279 appendStringInfoString(buf, "CAST(");
11280 /* no point in showing any top-level implicit cast */
11281 get_rule_expr(node, context, false);
11282 appendStringInfo(buf, " AS %s)",
11284 exprTypmod(node)));
11285 }
11286}
11287
11288/*
11289 * Helper function to identify node types that satisfy func_expr_windowless.
11290 * If in doubt, "false" is always a safe answer.
11291 */
11292static bool
11294{
11295 if (node == NULL)
11296 return false; /* probably shouldn't happen */
11297 switch (nodeTag(node))
11298 {
11299 case T_FuncExpr:
11300 /* OK, unless it's going to deparse as a cast */
11301 return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
11302 ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
11303 case T_NullIfExpr:
11304 case T_CoalesceExpr:
11305 case T_MinMaxExpr:
11306 case T_SQLValueFunction:
11307 case T_XmlExpr:
11308 case T_JsonExpr:
11309 /* these are all accepted by func_expr_common_subexpr */
11310 return true;
11311 default:
11312 break;
11313 }
11314 return false;
11315}
11316
11317
11318/*
11319 * get_oper_expr - Parse back an OpExpr node
11320 */
11321static void
11322get_oper_expr(OpExpr *expr, deparse_context *context)
11323{
11324 StringInfo buf = context->buf;
11325 Oid opno = expr->opno;
11326 List *args = expr->args;
11327
11328 if (!PRETTY_PAREN(context))
11330 if (list_length(args) == 2)
11331 {
11332 /* binary operator */
11333 Node *arg1 = (Node *) linitial(args);
11334 Node *arg2 = (Node *) lsecond(args);
11335
11336 get_rule_expr_paren(arg1, context, true, (Node *) expr);
11337 appendStringInfo(buf, " %s ",
11339 exprType(arg1),
11340 exprType(arg2)));
11341 get_rule_expr_paren(arg2, context, true, (Node *) expr);
11342 }
11343 else
11344 {
11345 /* prefix operator */
11346 Node *arg = (Node *) linitial(args);
11347
11348 appendStringInfo(buf, "%s ",
11350 InvalidOid,
11351 exprType(arg)));
11352 get_rule_expr_paren(arg, context, true, (Node *) expr);
11353 }
11354 if (!PRETTY_PAREN(context))
11356}
11357
11358/*
11359 * get_func_expr - Parse back a FuncExpr node
11360 */
11361static void
11362get_func_expr(FuncExpr *expr, deparse_context *context,
11363 bool showimplicit)
11364{
11365 StringInfo buf = context->buf;
11366 Oid funcoid = expr->funcid;
11367 Oid argtypes[FUNC_MAX_ARGS];
11368 int nargs;
11369 List *argnames;
11370 bool use_variadic;
11371 ListCell *l;
11372
11373 /*
11374 * If the function call came from an implicit coercion, then just show the
11375 * first argument --- unless caller wants to see implicit coercions.
11376 */
11377 if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
11378 {
11379 get_rule_expr_paren((Node *) linitial(expr->args), context,
11380 false, (Node *) expr);
11381 return;
11382 }
11383
11384 /*
11385 * If the function call came from a cast, then show the first argument
11386 * plus an explicit cast operation.
11387 */
11388 if (expr->funcformat == COERCE_EXPLICIT_CAST ||
11389 expr->funcformat == COERCE_IMPLICIT_CAST)
11390 {
11391 Node *arg = linitial(expr->args);
11392 Oid rettype = expr->funcresulttype;
11394
11395 /* Get the typmod if this is a length-coercion function */
11397
11398 get_coercion_expr(arg, context,
11399 rettype, coercedTypmod,
11400 (Node *) expr);
11401
11402 return;
11403 }
11404
11405 /*
11406 * If the function was called using one of the SQL spec's random special
11407 * syntaxes, try to reproduce that. If we don't recognize the function,
11408 * fall through.
11409 */
11410 if (expr->funcformat == COERCE_SQL_SYNTAX)
11411 {
11412 if (get_func_sql_syntax(expr, context))
11413 return;
11414 }
11415
11416 /*
11417 * Normal function: display as proname(args). First we need to extract
11418 * the argument datatypes.
11419 */
11420 if (list_length(expr->args) > FUNC_MAX_ARGS)
11421 ereport(ERROR,
11423 errmsg("too many arguments")));
11424 nargs = 0;
11425 argnames = NIL;
11426 foreach(l, expr->args)
11427 {
11428 Node *arg = (Node *) lfirst(l);
11429
11430 if (IsA(arg, NamedArgExpr))
11431 argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
11432 argtypes[nargs] = exprType(arg);
11433 nargs++;
11434 }
11435
11436 appendStringInfo(buf, "%s(",
11438 argnames, argtypes,
11439 expr->funcvariadic,
11440 &use_variadic,
11441 context->inGroupBy));
11442 nargs = 0;
11443 foreach(l, expr->args)
11444 {
11445 if (nargs++ > 0)
11447 if (use_variadic && lnext(expr->args, l) == NULL)
11448 appendStringInfoString(buf, "VARIADIC ");
11449 get_rule_expr((Node *) lfirst(l), context, true);
11450 }
11452}
11453
11454/*
11455 * get_agg_expr - Parse back an Aggref node
11456 */
11457static void
11458get_agg_expr(Aggref *aggref, deparse_context *context,
11460{
11461 get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
11462 false);
11463}
11464
11465/*
11466 * get_agg_expr_helper - subroutine for get_agg_expr and
11467 * get_json_agg_constructor
11468 */
11469static void
11471 Aggref *original_aggref, const char *funcname,
11472 const char *options, bool is_json_objectagg)
11473{
11474 StringInfo buf = context->buf;
11475 Oid argtypes[FUNC_MAX_ARGS];
11476 int nargs;
11477 bool use_variadic = false;
11478
11479 /*
11480 * For a combining aggregate, we look up and deparse the corresponding
11481 * partial aggregate instead. This is necessary because our input
11482 * argument list has been replaced; the new argument list always has just
11483 * one element, which will point to a partial Aggref that supplies us with
11484 * transition states to combine.
11485 */
11486 if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
11487 {
11489
11490 Assert(list_length(aggref->args) == 1);
11491 tle = linitial_node(TargetEntry, aggref->args);
11492 resolve_special_varno((Node *) tle->expr, context,
11494 return;
11495 }
11496
11497 /*
11498 * Mark as PARTIAL, if appropriate. We look to the original aggref so as
11499 * to avoid printing this when recursing from the code just above.
11500 */
11502 appendStringInfoString(buf, "PARTIAL ");
11503
11504 /* Extract the argument types as seen by the parser */
11505 nargs = get_aggregate_argtypes(aggref, argtypes);
11506
11507 if (!funcname)
11508 funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
11509 argtypes, aggref->aggvariadic,
11510 &use_variadic,
11511 context->inGroupBy);
11512
11513 /* Print the aggregate name, schema-qualified if needed */
11514 appendStringInfo(buf, "%s(%s", funcname,
11515 (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
11516
11517 if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
11518 {
11519 /*
11520 * Ordered-set aggregates do not use "*" syntax. Also, we needn't
11521 * worry about inserting VARIADIC. So we can just dump the direct
11522 * args as-is.
11523 */
11524 Assert(!aggref->aggvariadic);
11525 get_rule_expr((Node *) aggref->aggdirectargs, context, true);
11526 Assert(aggref->aggorder != NIL);
11527 appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
11528 get_rule_orderby(aggref->aggorder, aggref->args, false, context);
11529 }
11530 else
11531 {
11532 /* aggstar can be set only in zero-argument aggregates */
11533 if (aggref->aggstar)
11535 else
11536 {
11537 ListCell *l;
11538 int i;
11539
11540 i = 0;
11541 foreach(l, aggref->args)
11542 {
11544 Node *arg = (Node *) tle->expr;
11545
11547 if (tle->resjunk)
11548 continue;
11549 if (i++ > 0)
11550 {
11552 {
11553 /*
11554 * the ABSENT ON NULL and WITH UNIQUE args are printed
11555 * separately, so ignore them here
11556 */
11557 if (i > 2)
11558 break;
11559
11561 }
11562 else
11564 }
11565 if (use_variadic && i == nargs)
11566 appendStringInfoString(buf, "VARIADIC ");
11567 get_rule_expr(arg, context, true);
11568 }
11569 }
11570
11571 if (aggref->aggorder != NIL)
11572 {
11573 appendStringInfoString(buf, " ORDER BY ");
11574 get_rule_orderby(aggref->aggorder, aggref->args, false, context);
11575 }
11576 }
11577
11578 if (options)
11580
11581 if (aggref->aggfilter != NULL)
11582 {
11583 appendStringInfoString(buf, ") FILTER (WHERE ");
11584 get_rule_expr((Node *) aggref->aggfilter, context, false);
11585 }
11586
11588}
11589
11590/*
11591 * This is a helper function for get_agg_expr(). It's used when we deparse
11592 * a combining Aggref; resolve_special_varno locates the corresponding partial
11593 * Aggref and then calls this.
11594 */
11595static void
11596get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
11597{
11598 Aggref *aggref;
11599 Aggref *original_aggref = callback_arg;
11600
11601 if (!IsA(node, Aggref))
11602 elog(ERROR, "combining Aggref does not point to an Aggref");
11603
11604 aggref = (Aggref *) node;
11605 get_agg_expr(aggref, context, original_aggref);
11606}
11607
11608/*
11609 * get_windowfunc_expr - Parse back a WindowFunc node
11610 */
11611static void
11613{
11614 get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
11615}
11616
11617
11618/*
11619 * get_windowfunc_expr_helper - subroutine for get_windowfunc_expr and
11620 * get_json_agg_constructor
11621 */
11622static void
11624 const char *funcname, const char *options,
11625 bool is_json_objectagg)
11626{
11627 StringInfo buf = context->buf;
11628 Oid argtypes[FUNC_MAX_ARGS];
11629 int nargs;
11630 List *argnames;
11631 ListCell *l;
11632
11633 if (list_length(wfunc->args) > FUNC_MAX_ARGS)
11634 ereport(ERROR,
11636 errmsg("too many arguments")));
11637 nargs = 0;
11638 argnames = NIL;
11639 foreach(l, wfunc->args)
11640 {
11641 Node *arg = (Node *) lfirst(l);
11642
11643 if (IsA(arg, NamedArgExpr))
11644 argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
11645 argtypes[nargs] = exprType(arg);
11646 nargs++;
11647 }
11648
11649 if (!funcname)
11650 funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
11651 argtypes, false, NULL,
11652 context->inGroupBy);
11653
11654 appendStringInfo(buf, "%s(", funcname);
11655
11656 /* winstar can be set only in zero-argument aggregates */
11657 if (wfunc->winstar)
11659 else
11660 {
11662 {
11663 get_rule_expr((Node *) linitial(wfunc->args), context, false);
11665 get_rule_expr((Node *) lsecond(wfunc->args), context, false);
11666 }
11667 else
11668 get_rule_expr((Node *) wfunc->args, context, true);
11669 }
11670
11671 if (options)
11673
11674 if (wfunc->aggfilter != NULL)
11675 {
11676 appendStringInfoString(buf, ") FILTER (WHERE ");
11677 get_rule_expr((Node *) wfunc->aggfilter, context, false);
11678 }
11679
11681
11682 if (wfunc->ignore_nulls == PARSER_IGNORE_NULLS)
11683 appendStringInfoString(buf, "IGNORE NULLS ");
11684
11685 appendStringInfoString(buf, "OVER ");
11686
11687 if (context->windowClause)
11688 {
11689 /* Query-decompilation case: search the windowClause list */
11690 foreach(l, context->windowClause)
11691 {
11692 WindowClause *wc = (WindowClause *) lfirst(l);
11693
11694 if (wc->winref == wfunc->winref)
11695 {
11696 if (wc->name)
11698 else
11699 get_rule_windowspec(wc, context->targetList, context);
11700 break;
11701 }
11702 }
11703 if (l == NULL)
11704 elog(ERROR, "could not find window clause for winref %u",
11705 wfunc->winref);
11706 }
11707 else
11708 {
11709 /*
11710 * In EXPLAIN, search the namespace stack for a matching WindowAgg
11711 * node (probably it's always the first entry), and print winname.
11712 */
11713 foreach(l, context->namespaces)
11714 {
11716
11717 if (dpns->plan && IsA(dpns->plan, WindowAgg))
11718 {
11720
11721 if (wagg->winref == wfunc->winref)
11722 {
11724 break;
11725 }
11726 }
11727 }
11728 if (l == NULL)
11729 elog(ERROR, "could not find window clause for winref %u",
11730 wfunc->winref);
11731 }
11732}
11733
11734/*
11735 * get_func_sql_syntax - Parse back a SQL-syntax function call
11736 *
11737 * Returns true if we successfully deparsed, false if we did not
11738 * recognize the function.
11739 */
11740static bool
11742{
11743 StringInfo buf = context->buf;
11744 Oid funcoid = expr->funcid;
11745
11746 switch (funcoid)
11747 {
11754 /* AT TIME ZONE ... note reversed argument order */
11756 get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
11757 (Node *) expr);
11758 appendStringInfoString(buf, " AT TIME ZONE ");
11759 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11760 (Node *) expr);
11762 return true;
11763
11766 case F_TIMEZONE_TIMETZ:
11767 /* AT LOCAL */
11769 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11770 (Node *) expr);
11771 appendStringInfoString(buf, " AT LOCAL)");
11772 return true;
11773
11787 /* (x1, x2) OVERLAPS (y1, y2) */
11789 get_rule_expr((Node *) linitial(expr->args), context, false);
11791 get_rule_expr((Node *) lsecond(expr->args), context, false);
11792 appendStringInfoString(buf, ") OVERLAPS (");
11793 get_rule_expr((Node *) lthird(expr->args), context, false);
11795 get_rule_expr((Node *) lfourth(expr->args), context, false);
11797 return true;
11798
11805 /* EXTRACT (x FROM y) */
11806 appendStringInfoString(buf, "EXTRACT(");
11807 {
11808 Const *con = (Const *) linitial(expr->args);
11809
11810 Assert(IsA(con, Const) &&
11811 con->consttype == TEXTOID &&
11812 !con->constisnull);
11814 }
11815 appendStringInfoString(buf, " FROM ");
11816 get_rule_expr((Node *) lsecond(expr->args), context, false);
11818 return true;
11819
11820 case F_IS_NORMALIZED:
11821 /* IS xxx NORMALIZED */
11823 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11824 (Node *) expr);
11826 if (list_length(expr->args) == 2)
11827 {
11828 Const *con = (Const *) lsecond(expr->args);
11829
11830 Assert(IsA(con, Const) &&
11831 con->consttype == TEXTOID &&
11832 !con->constisnull);
11833 appendStringInfo(buf, " %s",
11834 TextDatumGetCString(con->constvalue));
11835 }
11836 appendStringInfoString(buf, " NORMALIZED)");
11837 return true;
11838
11839 case F_PG_COLLATION_FOR:
11840 /* COLLATION FOR */
11841 appendStringInfoString(buf, "COLLATION FOR (");
11842 get_rule_expr((Node *) linitial(expr->args), context, false);
11844 return true;
11845
11846 case F_NORMALIZE:
11847 /* NORMALIZE() */
11848 appendStringInfoString(buf, "NORMALIZE(");
11849 get_rule_expr((Node *) linitial(expr->args), context, false);
11850 if (list_length(expr->args) == 2)
11851 {
11852 Const *con = (Const *) lsecond(expr->args);
11853
11854 Assert(IsA(con, Const) &&
11855 con->consttype == TEXTOID &&
11856 !con->constisnull);
11857 appendStringInfo(buf, ", %s",
11858 TextDatumGetCString(con->constvalue));
11859 }
11861 return true;
11862
11869 /* OVERLAY() */
11870 appendStringInfoString(buf, "OVERLAY(");
11871 get_rule_expr((Node *) linitial(expr->args), context, false);
11872 appendStringInfoString(buf, " PLACING ");
11873 get_rule_expr((Node *) lsecond(expr->args), context, false);
11874 appendStringInfoString(buf, " FROM ");
11875 get_rule_expr((Node *) lthird(expr->args), context, false);
11876 if (list_length(expr->args) == 4)
11877 {
11878 appendStringInfoString(buf, " FOR ");
11879 get_rule_expr((Node *) lfourth(expr->args), context, false);
11880 }
11882 return true;
11883
11884 case F_POSITION_BIT_BIT:
11887 /* POSITION() ... extra parens since args are b_expr not a_expr */
11888 appendStringInfoString(buf, "POSITION((");
11889 get_rule_expr((Node *) lsecond(expr->args), context, false);
11890 appendStringInfoString(buf, ") IN (");
11891 get_rule_expr((Node *) linitial(expr->args), context, false);
11893 return true;
11894
11901 /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
11902 appendStringInfoString(buf, "SUBSTRING(");
11903 get_rule_expr((Node *) linitial(expr->args), context, false);
11904 appendStringInfoString(buf, " FROM ");
11905 get_rule_expr((Node *) lsecond(expr->args), context, false);
11906 if (list_length(expr->args) == 3)
11907 {
11908 appendStringInfoString(buf, " FOR ");
11909 get_rule_expr((Node *) lthird(expr->args), context, false);
11910 }
11912 return true;
11913
11915 /* SUBSTRING SIMILAR/ESCAPE */
11916 appendStringInfoString(buf, "SUBSTRING(");
11917 get_rule_expr((Node *) linitial(expr->args), context, false);
11918 appendStringInfoString(buf, " SIMILAR ");
11919 get_rule_expr((Node *) lsecond(expr->args), context, false);
11920 appendStringInfoString(buf, " ESCAPE ");
11921 get_rule_expr((Node *) lthird(expr->args), context, false);
11923 return true;
11924
11926 case F_BTRIM_TEXT:
11927 case F_BTRIM_TEXT_TEXT:
11928 /* TRIM() */
11929 appendStringInfoString(buf, "TRIM(BOTH");
11930 if (list_length(expr->args) == 2)
11931 {
11933 get_rule_expr((Node *) lsecond(expr->args), context, false);
11934 }
11935 appendStringInfoString(buf, " FROM ");
11936 get_rule_expr((Node *) linitial(expr->args), context, false);
11938 return true;
11939
11941 case F_LTRIM_TEXT:
11942 case F_LTRIM_TEXT_TEXT:
11943 /* TRIM() */
11944 appendStringInfoString(buf, "TRIM(LEADING");
11945 if (list_length(expr->args) == 2)
11946 {
11948 get_rule_expr((Node *) lsecond(expr->args), context, false);
11949 }
11950 appendStringInfoString(buf, " FROM ");
11951 get_rule_expr((Node *) linitial(expr->args), context, false);
11953 return true;
11954
11956 case F_RTRIM_TEXT:
11957 case F_RTRIM_TEXT_TEXT:
11958 /* TRIM() */
11959 appendStringInfoString(buf, "TRIM(TRAILING");
11960 if (list_length(expr->args) == 2)
11961 {
11963 get_rule_expr((Node *) lsecond(expr->args), context, false);
11964 }
11965 appendStringInfoString(buf, " FROM ");
11966 get_rule_expr((Node *) linitial(expr->args), context, false);
11968 return true;
11969
11970 case F_SYSTEM_USER:
11971 appendStringInfoString(buf, "SYSTEM_USER");
11972 return true;
11973
11974 case F_XMLEXISTS:
11975 /* XMLEXISTS ... extra parens because args are c_expr */
11976 appendStringInfoString(buf, "XMLEXISTS((");
11977 get_rule_expr((Node *) linitial(expr->args), context, false);
11978 appendStringInfoString(buf, ") PASSING (");
11979 get_rule_expr((Node *) lsecond(expr->args), context, false);
11981 return true;
11982 }
11983 return false;
11984}
11985
11986/* ----------
11987 * get_coercion_expr
11988 *
11989 * Make a string representation of a value coerced to a specific type
11990 * ----------
11991 */
11992static void
11994 Oid resulttype, int32 resulttypmod,
11996{
11997 StringInfo buf = context->buf;
11998
11999 /*
12000 * Since parse_coerce.c doesn't immediately collapse application of
12001 * length-coercion functions to constants, what we'll typically see in
12002 * such cases is a Const with typmod -1 and a length-coercion function
12003 * right above it. Avoid generating redundant output. However, beware of
12004 * suppressing casts when the user actually wrote something like
12005 * 'foo'::text::char(3).
12006 *
12007 * Note: it might seem that we are missing the possibility of needing to
12008 * print a COLLATE clause for such a Const. However, a Const could only
12009 * have nondefault collation in a post-constant-folding tree, in which the
12010 * length coercion would have been folded too. See also the special
12011 * handling of CollateExpr in coerce_to_target_type(): any collation
12012 * marking will be above the coercion node, not below it.
12013 */
12014 if (arg && IsA(arg, Const) &&
12015 ((Const *) arg)->consttype == resulttype &&
12016 ((Const *) arg)->consttypmod == -1)
12017 {
12018 /* Show the constant without normal ::typename decoration */
12019 get_const_expr((Const *) arg, context, -1);
12020 }
12021 else
12022 {
12023 if (!PRETTY_PAREN(context))
12025 get_rule_expr_paren(arg, context, false, parentNode);
12026 if (!PRETTY_PAREN(context))
12028 }
12029
12030 /*
12031 * Never emit resulttype(arg) functional notation. A pg_proc entry could
12032 * take precedence, and a resulttype in pg_temp would require schema
12033 * qualification that format_type_with_typemod() would usually omit. We've
12034 * standardized on arg::resulttype, but CAST(arg AS resulttype) notation
12035 * would work fine.
12036 */
12037 appendStringInfo(buf, "::%s",
12039}
12040
12041/* ----------
12042 * get_const_expr
12043 *
12044 * Make a string representation of a Const
12045 *
12046 * showtype can be -1 to never show "::typename" decoration, or +1 to always
12047 * show it, or 0 to show it only if the constant wouldn't be assumed to be
12048 * the right type by default.
12049 *
12050 * If the Const's collation isn't default for its type, show that too.
12051 * We mustn't do this when showtype is -1 (since that means the caller will
12052 * print "::typename", and we can't put a COLLATE clause in between). It's
12053 * caller's responsibility that collation isn't missed in such cases.
12054 * ----------
12055 */
12056static void
12057get_const_expr(Const *constval, deparse_context *context, int showtype)
12058{
12059 StringInfo buf = context->buf;
12060 Oid typoutput;
12061 bool typIsVarlena;
12062 char *extval;
12063 bool needlabel = false;
12064
12065 if (constval->constisnull)
12066 {
12067 /*
12068 * Always label the type of a NULL constant to prevent misdecisions
12069 * about type when reparsing.
12070 */
12071 appendStringInfoString(buf, "NULL");
12072 if (showtype >= 0)
12073 {
12074 appendStringInfo(buf, "::%s",
12076 constval->consttypmod));
12077 get_const_collation(constval, context);
12078 }
12079 return;
12080 }
12081
12082 getTypeOutputInfo(constval->consttype,
12083 &typoutput, &typIsVarlena);
12084
12085 extval = OidOutputFunctionCall(typoutput, constval->constvalue);
12086
12087 switch (constval->consttype)
12088 {
12089 case INT4OID:
12090
12091 /*
12092 * INT4 can be printed without any decoration, unless it is
12093 * negative; in that case print it as '-nnn'::integer to ensure
12094 * that the output will re-parse as a constant, not as a constant
12095 * plus operator. In most cases we could get away with printing
12096 * (-nnn) instead, because of the way that gram.y handles negative
12097 * literals; but that doesn't work for INT_MIN, and it doesn't
12098 * seem that much prettier anyway.
12099 */
12100 if (extval[0] != '-')
12102 else
12103 {
12104 appendStringInfo(buf, "'%s'", extval);
12105 needlabel = true; /* we must attach a cast */
12106 }
12107 break;
12108
12109 case NUMERICOID:
12110
12111 /*
12112 * NUMERIC can be printed without quotes if it looks like a float
12113 * constant (not an integer, and not Infinity or NaN) and doesn't
12114 * have a leading sign (for the same reason as for INT4).
12115 */
12116 if (isdigit((unsigned char) extval[0]) &&
12117 strcspn(extval, "eE.") != strlen(extval))
12118 {
12120 }
12121 else
12122 {
12123 appendStringInfo(buf, "'%s'", extval);
12124 needlabel = true; /* we must attach a cast */
12125 }
12126 break;
12127
12128 case BOOLOID:
12129 if (strcmp(extval, "t") == 0)
12130 appendStringInfoString(buf, "true");
12131 else
12132 appendStringInfoString(buf, "false");
12133 break;
12134
12135 default:
12137 break;
12138 }
12139
12140 pfree(extval);
12141
12142 if (showtype < 0)
12143 return;
12144
12145 /*
12146 * For showtype == 0, append ::typename unless the constant will be
12147 * implicitly typed as the right type when it is read in.
12148 *
12149 * XXX this code has to be kept in sync with the behavior of the parser,
12150 * especially make_const.
12151 */
12152 switch (constval->consttype)
12153 {
12154 case BOOLOID:
12155 case UNKNOWNOID:
12156 /* These types can be left unlabeled */
12157 needlabel = false;
12158 break;
12159 case INT4OID:
12160 /* We determined above whether a label is needed */
12161 break;
12162 case NUMERICOID:
12163
12164 /*
12165 * Float-looking constants will be typed as numeric, which we
12166 * checked above; but if there's a nondefault typmod we need to
12167 * show it.
12168 */
12169 needlabel |= (constval->consttypmod >= 0);
12170 break;
12171 default:
12172 needlabel = true;
12173 break;
12174 }
12175 if (needlabel || showtype > 0)
12176 appendStringInfo(buf, "::%s",
12178 constval->consttypmod));
12179
12180 get_const_collation(constval, context);
12181}
12182
12183/*
12184 * helper for get_const_expr: append COLLATE if needed
12185 */
12186static void
12187get_const_collation(Const *constval, deparse_context *context)
12188{
12189 StringInfo buf = context->buf;
12190
12191 if (OidIsValid(constval->constcollid))
12192 {
12193 Oid typcollation = get_typcollation(constval->consttype);
12194
12195 if (constval->constcollid != typcollation)
12196 {
12197 appendStringInfo(buf, " COLLATE %s",
12198 generate_collation_name(constval->constcollid));
12199 }
12200 }
12201}
12202
12203/*
12204 * get_json_path_spec - Parse back a JSON path specification
12205 */
12206static void
12207get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
12208{
12209 if (IsA(path_spec, Const))
12210 get_const_expr((Const *) path_spec, context, -1);
12211 else
12212 get_rule_expr(path_spec, context, showimplicit);
12213}
12214
12215/*
12216 * get_json_format - Parse back a JsonFormat node
12217 */
12218static void
12220{
12221 if (format->format_type == JS_FORMAT_DEFAULT)
12222 return;
12223
12225 format->format_type == JS_FORMAT_JSONB ?
12226 " FORMAT JSONB" : " FORMAT JSON");
12227
12228 if (format->encoding != JS_ENC_DEFAULT)
12229 {
12230 const char *encoding;
12231
12232 encoding =
12233 format->encoding == JS_ENC_UTF16 ? "UTF16" :
12234 format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
12235
12236 appendStringInfo(buf, " ENCODING %s", encoding);
12237 }
12238}
12239
12240/*
12241 * get_json_returning - Parse back a JsonReturning structure
12242 */
12243static void
12246{
12247 if (!OidIsValid(returning->typid))
12248 return;
12249
12250 appendStringInfo(buf, " RETURNING %s",
12252 returning->typmod));
12253
12255 returning->format->format_type !=
12256 (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
12257 get_json_format(returning->format, buf);
12258}
12259
12260/*
12261 * get_json_constructor - Parse back a JsonConstructorExpr node
12262 */
12263static void
12265 bool showimplicit)
12266{
12267 StringInfo buf = context->buf;
12268 const char *funcname;
12269 bool is_json_object;
12270 int curridx;
12271 ListCell *lc;
12272
12273 if (ctor->type == JSCTOR_JSON_OBJECTAGG)
12274 {
12275 get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
12276 return;
12277 }
12278 else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
12279 {
12280 get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
12281 return;
12282 }
12283 else if (ctor->type == JSCTOR_JSON_ARRAY_QUERY)
12284 {
12285 Query *query = castNode(Query, ctor->orig_query);
12286
12287 appendStringInfo(buf, "JSON_ARRAY(");
12288
12289 get_query_def(query, buf, context->namespaces, NULL, false,
12290 context->prettyFlags, context->wrapColumn,
12291 context->indentLevel);
12292
12295
12296 return;
12297 }
12298
12299 switch (ctor->type)
12300 {
12301 case JSCTOR_JSON_OBJECT:
12302 funcname = "JSON_OBJECT";
12303 break;
12304 case JSCTOR_JSON_ARRAY:
12305 funcname = "JSON_ARRAY";
12306 break;
12307 case JSCTOR_JSON_PARSE:
12308 funcname = "JSON";
12309 break;
12310 case JSCTOR_JSON_SCALAR:
12311 funcname = "JSON_SCALAR";
12312 break;
12314 funcname = "JSON_SERIALIZE";
12315 break;
12316 default:
12317 elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
12318 }
12319
12320 appendStringInfo(buf, "%s(", funcname);
12321
12323 foreach(lc, ctor->args)
12324 {
12326 if (curridx > 0)
12327 {
12328 const char *sep;
12329
12330 sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
12332 }
12333
12334 get_rule_expr((Node *) lfirst(lc), context, true);
12335 }
12336
12339}
12340
12341/*
12342 * Append options, if any, to the JSON constructor being deparsed
12343 */
12344static void
12346{
12347 if (ctor->absent_on_null)
12348 {
12349 if (ctor->type == JSCTOR_JSON_OBJECT ||
12350 ctor->type == JSCTOR_JSON_OBJECTAGG)
12351 appendStringInfoString(buf, " ABSENT ON NULL");
12352 }
12353 else
12354 {
12355 if (ctor->type == JSCTOR_JSON_ARRAY ||
12356 ctor->type == JSCTOR_JSON_ARRAYAGG)
12357 appendStringInfoString(buf, " NULL ON NULL");
12358 }
12359
12360 if (ctor->unique)
12361 appendStringInfoString(buf, " WITH UNIQUE KEYS");
12362
12363 /*
12364 * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
12365 * support one.
12366 */
12367 if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
12368 get_json_returning(ctor->returning, buf, true);
12369}
12370
12371/*
12372 * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
12373 */
12374static void
12376 const char *funcname, bool is_json_objectagg)
12377{
12379
12382
12383 if (IsA(ctor->func, Aggref))
12384 get_agg_expr_helper((Aggref *) ctor->func, context,
12385 (Aggref *) ctor->func,
12387 else if (IsA(ctor->func, WindowFunc))
12388 get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
12389 funcname, options.data,
12391 else
12392 elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
12393 nodeTag(ctor->func));
12394}
12395
12396/*
12397 * simple_quote_literal - Format a string as a SQL literal, append to buf
12398 */
12399static void
12401{
12402 const char *valptr;
12403
12404 /*
12405 * We always form the string literal according to standard SQL rules.
12406 */
12408 for (valptr = val; *valptr; valptr++)
12409 {
12410 char ch = *valptr;
12411
12412 if (SQL_STR_DOUBLE(ch, false))
12415 }
12417}
12418
12419
12420/* ----------
12421 * get_sublink_expr - Parse back a sublink
12422 * ----------
12423 */
12424static void
12426{
12427 StringInfo buf = context->buf;
12428 Query *query = (Query *) (sublink->subselect);
12429 char *opname = NULL;
12430 bool need_paren;
12431
12432 if (sublink->subLinkType == ARRAY_SUBLINK)
12433 appendStringInfoString(buf, "ARRAY(");
12434 else
12436
12437 /*
12438 * Note that we print the name of only the first operator, when there are
12439 * multiple combining operators. This is an approximation that could go
12440 * wrong in various scenarios (operators in different schemas, renamed
12441 * operators, etc) but there is not a whole lot we can do about it, since
12442 * the syntax allows only one operator to be shown.
12443 */
12444 if (sublink->testexpr)
12445 {
12446 if (IsA(sublink->testexpr, OpExpr))
12447 {
12448 /* single combining operator */
12449 OpExpr *opexpr = (OpExpr *) sublink->testexpr;
12450
12451 get_rule_expr(linitial(opexpr->args), context, true);
12452 opname = generate_operator_name(opexpr->opno,
12453 exprType(linitial(opexpr->args)),
12454 exprType(lsecond(opexpr->args)));
12455 }
12456 else if (IsA(sublink->testexpr, BoolExpr))
12457 {
12458 /* multiple combining operators, = or <> cases */
12459 char *sep;
12460 ListCell *l;
12461
12463 sep = "";
12464 foreach(l, ((BoolExpr *) sublink->testexpr)->args)
12465 {
12466 OpExpr *opexpr = lfirst_node(OpExpr, l);
12467
12469 get_rule_expr(linitial(opexpr->args), context, true);
12470 if (!opname)
12472 exprType(linitial(opexpr->args)),
12473 exprType(lsecond(opexpr->args)));
12474 sep = ", ";
12475 }
12477 }
12478 else if (IsA(sublink->testexpr, RowCompareExpr))
12479 {
12480 /* multiple combining operators, < <= > >= cases */
12482
12484 get_rule_expr((Node *) rcexpr->largs, context, true);
12486 exprType(linitial(rcexpr->largs)),
12487 exprType(linitial(rcexpr->rargs)));
12489 }
12490 else
12491 elog(ERROR, "unrecognized testexpr type: %d",
12492 (int) nodeTag(sublink->testexpr));
12493 }
12494
12495 need_paren = true;
12496
12497 switch (sublink->subLinkType)
12498 {
12499 case EXISTS_SUBLINK:
12500 appendStringInfoString(buf, "EXISTS ");
12501 break;
12502
12503 case ANY_SUBLINK:
12504 if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
12505 appendStringInfoString(buf, " IN ");
12506 else
12507 appendStringInfo(buf, " %s ANY ", opname);
12508 break;
12509
12510 case ALL_SUBLINK:
12511 appendStringInfo(buf, " %s ALL ", opname);
12512 break;
12513
12514 case ROWCOMPARE_SUBLINK:
12515 appendStringInfo(buf, " %s ", opname);
12516 break;
12517
12518 case EXPR_SUBLINK:
12519 case MULTIEXPR_SUBLINK:
12520 case ARRAY_SUBLINK:
12521 need_paren = false;
12522 break;
12523
12524 case CTE_SUBLINK: /* shouldn't occur in a SubLink */
12525 default:
12526 elog(ERROR, "unrecognized sublink type: %d",
12527 (int) sublink->subLinkType);
12528 break;
12529 }
12530
12531 if (need_paren)
12533
12534 get_query_def(query, buf, context->namespaces, NULL, false,
12535 context->prettyFlags, context->wrapColumn,
12536 context->indentLevel);
12537
12538 if (need_paren)
12540 else
12542}
12543
12544
12545/* ----------
12546 * get_xmltable - Parse back a XMLTABLE function
12547 * ----------
12548 */
12549static void
12551{
12552 StringInfo buf = context->buf;
12553
12554 appendStringInfoString(buf, "XMLTABLE(");
12555
12556 if (tf->ns_uris != NIL)
12557 {
12558 ListCell *lc1,
12559 *lc2;
12560 bool first = true;
12561
12562 appendStringInfoString(buf, "XMLNAMESPACES (");
12563 forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
12564 {
12565 Node *expr = (Node *) lfirst(lc1);
12567
12568 if (!first)
12570 else
12571 first = false;
12572
12573 if (ns_node != NULL)
12574 {
12575 get_rule_expr(expr, context, showimplicit);
12576 appendStringInfo(buf, " AS %s",
12578 }
12579 else
12580 {
12581 appendStringInfoString(buf, "DEFAULT ");
12582 get_rule_expr(expr, context, showimplicit);
12583 }
12584 }
12586 }
12587
12589 get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
12590 appendStringInfoString(buf, ") PASSING (");
12591 get_rule_expr((Node *) tf->docexpr, context, showimplicit);
12593
12594 if (tf->colexprs != NIL)
12595 {
12596 ListCell *l1;
12597 ListCell *l2;
12598 ListCell *l3;
12599 ListCell *l4;
12600 ListCell *l5;
12601 int colnum = 0;
12602
12603 appendStringInfoString(buf, " COLUMNS ");
12604 forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
12605 l4, tf->colexprs, l5, tf->coldefexprs)
12606 {
12607 char *colname = strVal(lfirst(l1));
12608 Oid typid = lfirst_oid(l2);
12609 int32 typmod = lfirst_int(l3);
12610 Node *colexpr = (Node *) lfirst(l4);
12611 Node *coldefexpr = (Node *) lfirst(l5);
12612 bool ordinality = (tf->ordinalitycol == colnum);
12613 bool notnull = bms_is_member(colnum, tf->notnulls);
12614
12615 if (colnum > 0)
12617 colnum++;
12618
12619 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12620 ordinality ? "FOR ORDINALITY" :
12621 format_type_with_typemod(typid, typmod));
12622 if (ordinality)
12623 continue;
12624
12625 if (coldefexpr != NULL)
12626 {
12627 appendStringInfoString(buf, " DEFAULT (");
12628 get_rule_expr((Node *) coldefexpr, context, showimplicit);
12630 }
12631 if (colexpr != NULL)
12632 {
12633 appendStringInfoString(buf, " PATH (");
12634 get_rule_expr((Node *) colexpr, context, showimplicit);
12636 }
12637 if (notnull)
12638 appendStringInfoString(buf, " NOT NULL");
12639 }
12640 }
12641
12643}
12644
12645/*
12646 * get_json_table_nested_columns - Parse back nested JSON_TABLE columns
12647 */
12648static void
12650 deparse_context *context, bool showimplicit,
12651 bool needcomma)
12652{
12654 {
12656
12657 if (needcomma)
12658 appendStringInfoChar(context->buf, ',');
12659
12660 appendStringInfoChar(context->buf, ' ');
12661 appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
12662 get_const_expr(scan->path->value, context, -1);
12663 appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
12664 get_json_table_columns(tf, scan, context, showimplicit);
12665 }
12666 else if (IsA(plan, JsonTableSiblingJoin))
12667 {
12669
12671 needcomma);
12673 true);
12674 }
12675}
12676
12677/*
12678 * get_json_table_columns - Parse back JSON_TABLE columns
12679 */
12680static void
12682 deparse_context *context,
12683 bool showimplicit)
12684{
12685 StringInfo buf = context->buf;
12690 int colnum = 0;
12691
12693 appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12694
12695 if (PRETTY_INDENT(context))
12696 context->indentLevel += PRETTYINDENT_VAR;
12697
12698 forfour(lc_colname, tf->colnames,
12699 lc_coltype, tf->coltypes,
12700 lc_coltypmod, tf->coltypmods,
12701 lc_colvalexpr, tf->colvalexprs)
12702 {
12703 char *colname = strVal(lfirst(lc_colname));
12704 JsonExpr *colexpr;
12705 Oid typid;
12706 int32 typmod;
12707 bool ordinality;
12709
12710 typid = lfirst_oid(lc_coltype);
12711 typmod = lfirst_int(lc_coltypmod);
12713
12714 /* Skip columns that don't belong to this scan. */
12715 if (scan->colMin < 0 || colnum < scan->colMin)
12716 {
12717 colnum++;
12718 continue;
12719 }
12720 if (colnum > scan->colMax)
12721 break;
12722
12723 if (colnum > scan->colMin)
12725
12726 colnum++;
12727
12728 ordinality = !colexpr;
12729
12730 appendContextKeyword(context, "", 0, 0, 0);
12731
12732 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12733 ordinality ? "FOR ORDINALITY" :
12734 format_type_with_typemod(typid, typmod));
12735 if (ordinality)
12736 continue;
12737
12738 /*
12739 * Set default_behavior to guide get_json_expr_options() on whether to
12740 * emit the ON ERROR / EMPTY clauses.
12741 */
12742 if (colexpr->op == JSON_EXISTS_OP)
12743 {
12744 appendStringInfoString(buf, " EXISTS");
12746 }
12747 else
12748 {
12749 if (colexpr->op == JSON_QUERY_OP)
12750 {
12751 char typcategory;
12752 bool typispreferred;
12753
12755
12758 colexpr->format->format_type == JS_FORMAT_JSONB ?
12759 " FORMAT JSONB" : " FORMAT JSON");
12760 }
12761
12763 }
12764
12765 appendStringInfoString(buf, " PATH ");
12766
12767 get_json_path_spec(colexpr->path_spec, context, showimplicit);
12768
12769 get_json_expr_options(colexpr, context, default_behavior);
12770 }
12771
12772 if (scan->child)
12774 scan->colMin >= 0);
12775
12776 if (PRETTY_INDENT(context))
12777 context->indentLevel -= PRETTYINDENT_VAR;
12778
12779 appendContextKeyword(context, ")", 0, 0, 0);
12780}
12781
12782/* ----------
12783 * get_json_table - Parse back a JSON_TABLE function
12784 * ----------
12785 */
12786static void
12788{
12789 StringInfo buf = context->buf;
12792
12793 appendStringInfoString(buf, "JSON_TABLE(");
12794
12795 if (PRETTY_INDENT(context))
12796 context->indentLevel += PRETTYINDENT_VAR;
12797
12798 appendContextKeyword(context, "", 0, 0, 0);
12799
12800 get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12801
12803
12804 get_const_expr(root->path->value, context, -1);
12805
12806 appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12807
12808 if (jexpr->passing_values)
12809 {
12810 ListCell *lc1,
12811 *lc2;
12812 bool needcomma = false;
12813
12815 appendContextKeyword(context, "PASSING ", 0, 0, 0);
12816
12817 if (PRETTY_INDENT(context))
12818 context->indentLevel += PRETTYINDENT_VAR;
12819
12820 forboth(lc1, jexpr->passing_names,
12821 lc2, jexpr->passing_values)
12822 {
12823 if (needcomma)
12825 needcomma = true;
12826
12827 appendContextKeyword(context, "", 0, 0, 0);
12828
12829 get_rule_expr((Node *) lfirst(lc2), context, false);
12830 appendStringInfo(buf, " AS %s",
12832 );
12833 }
12834
12835 if (PRETTY_INDENT(context))
12836 context->indentLevel -= PRETTYINDENT_VAR;
12837 }
12838
12839 get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12840 showimplicit);
12841
12842 if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12843 get_json_behavior(jexpr->on_error, context, "ERROR");
12844
12845 if (PRETTY_INDENT(context))
12846 context->indentLevel -= PRETTYINDENT_VAR;
12847
12848 appendContextKeyword(context, ")", 0, 0, 0);
12849}
12850
12851/* ----------
12852 * get_tablefunc - Parse back a table function
12853 * ----------
12854 */
12855static void
12857{
12858 /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12859
12860 if (tf->functype == TFT_XMLTABLE)
12861 get_xmltable(tf, context, showimplicit);
12862 else if (tf->functype == TFT_JSON_TABLE)
12863 get_json_table(tf, context, showimplicit);
12864}
12865
12866/* ----------
12867 * get_from_clause - Parse back a FROM clause
12868 *
12869 * "prefix" is the keyword that denotes the start of the list of FROM
12870 * elements. It is FROM when used to parse back SELECT and UPDATE, but
12871 * is USING when parsing back DELETE.
12872 * ----------
12873 */
12874static void
12875get_from_clause(Query *query, const char *prefix, deparse_context *context)
12876{
12877 StringInfo buf = context->buf;
12878 bool first = true;
12879 ListCell *l;
12880
12881 /*
12882 * We use the query's jointree as a guide to what to print. However, we
12883 * must ignore auto-added RTEs that are marked not inFromCl. (These can
12884 * only appear at the top level of the jointree, so it's sufficient to
12885 * check here.) This check also ensures we ignore the rule pseudo-RTEs
12886 * for NEW and OLD.
12887 */
12888 foreach(l, query->jointree->fromlist)
12889 {
12890 Node *jtnode = (Node *) lfirst(l);
12891
12892 if (IsA(jtnode, RangeTblRef))
12893 {
12894 int varno = ((RangeTblRef *) jtnode)->rtindex;
12895 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12896
12897 if (!rte->inFromCl)
12898 continue;
12899 }
12900
12901 if (first)
12902 {
12903 appendContextKeyword(context, prefix,
12905 first = false;
12906
12907 get_from_clause_item(jtnode, query, context);
12908 }
12909 else
12910 {
12912
12914
12915 /*
12916 * Put the new FROM item's text into itembuf so we can decide
12917 * after we've got it whether or not it needs to go on a new line.
12918 */
12920 context->buf = &itembuf;
12921
12922 get_from_clause_item(jtnode, query, context);
12923
12924 /* Restore context's output buffer */
12925 context->buf = buf;
12926
12927 /* Consider line-wrapping if enabled */
12928 if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
12929 {
12930 /* Does the new item start with a new line? */
12931 if (itembuf.len > 0 && itembuf.data[0] == '\n')
12932 {
12933 /* If so, we shouldn't add anything */
12934 /* instead, remove any trailing spaces currently in buf */
12936 }
12937 else
12938 {
12939 char *trailing_nl;
12940
12941 /* Locate the start of the current line in the buffer */
12942 trailing_nl = strrchr(buf->data, '\n');
12943 if (trailing_nl == NULL)
12944 trailing_nl = buf->data;
12945 else
12946 trailing_nl++;
12947
12948 /*
12949 * Add a newline, plus some indentation, if the new item
12950 * would cause an overflow.
12951 */
12952 if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
12956 }
12957 }
12958
12959 /* Add the new item */
12961
12962 /* clean up */
12963 pfree(itembuf.data);
12964 }
12965 }
12966}
12967
12968static void
12969get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
12970{
12971 StringInfo buf = context->buf;
12973
12974 if (IsA(jtnode, RangeTblRef))
12975 {
12976 int varno = ((RangeTblRef *) jtnode)->rtindex;
12977 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12980
12981 if (rte->lateral)
12982 appendStringInfoString(buf, "LATERAL ");
12983
12984 /* Print the FROM item proper */
12985 switch (rte->rtekind)
12986 {
12987 case RTE_RELATION:
12988 /* Normal relation RTE */
12989 appendStringInfo(buf, "%s%s",
12992 context->namespaces));
12993 break;
12994 case RTE_SUBQUERY:
12995 /* Subquery RTE */
12997 get_query_def(rte->subquery, buf, context->namespaces, NULL,
12998 true,
12999 context->prettyFlags, context->wrapColumn,
13000 context->indentLevel);
13002 break;
13003 case RTE_FUNCTION:
13004 /* Function RTE */
13005 rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
13006
13007 /*
13008 * Omit ROWS FROM() syntax for just one function, unless it
13009 * has both a coldeflist and WITH ORDINALITY. If it has both,
13010 * we must use ROWS FROM() syntax to avoid ambiguity about
13011 * whether the coldeflist includes the ordinality column.
13012 */
13013 if (list_length(rte->functions) == 1 &&
13014 (rtfunc1->funccolnames == NIL || !rte->funcordinality))
13015 {
13016 get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
13017 /* we'll print the coldeflist below, if it has one */
13018 }
13019 else
13020 {
13021 bool all_unnest;
13022 ListCell *lc;
13023
13024 /*
13025 * If all the function calls in the list are to unnest,
13026 * and none need a coldeflist, then collapse the list back
13027 * down to UNNEST(args). (If we had more than one
13028 * built-in unnest function, this would get more
13029 * difficult.)
13030 *
13031 * XXX This is pretty ugly, since it makes not-terribly-
13032 * future-proof assumptions about what the parser would do
13033 * with the output; but the alternative is to emit our
13034 * nonstandard ROWS FROM() notation for what might have
13035 * been a perfectly spec-compliant multi-argument
13036 * UNNEST().
13037 */
13038 all_unnest = true;
13039 foreach(lc, rte->functions)
13040 {
13042
13043 if (!IsA(rtfunc->funcexpr, FuncExpr) ||
13044 ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
13045 rtfunc->funccolnames != NIL)
13046 {
13047 all_unnest = false;
13048 break;
13049 }
13050 }
13051
13052 if (all_unnest)
13053 {
13054 List *allargs = NIL;
13055
13056 foreach(lc, rte->functions)
13057 {
13059 List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
13060
13061 allargs = list_concat(allargs, args);
13062 }
13063
13064 appendStringInfoString(buf, "UNNEST(");
13065 get_rule_expr((Node *) allargs, context, true);
13067 }
13068 else
13069 {
13070 int funcno = 0;
13071
13072 appendStringInfoString(buf, "ROWS FROM(");
13073 foreach(lc, rte->functions)
13074 {
13076
13077 if (funcno > 0)
13079 get_rule_expr_funccall(rtfunc->funcexpr, context, true);
13080 if (rtfunc->funccolnames != NIL)
13081 {
13082 /* Reconstruct the column definition list */
13083 appendStringInfoString(buf, " AS ");
13085 NULL,
13086 context);
13087 }
13088 funcno++;
13089 }
13091 }
13092 /* prevent printing duplicate coldeflist below */
13093 rtfunc1 = NULL;
13094 }
13095 if (rte->funcordinality)
13096 appendStringInfoString(buf, " WITH ORDINALITY");
13097 break;
13098 case RTE_TABLEFUNC:
13099 get_tablefunc(rte->tablefunc, context, true);
13100 break;
13101 case RTE_GRAPH_TABLE:
13102 appendStringInfoString(buf, "GRAPH_TABLE (");
13104 appendStringInfoString(buf, " MATCH ");
13105 get_graph_pattern_def(rte->graph_pattern, context);
13106 appendStringInfoString(buf, " COLUMNS (");
13107 {
13108 bool first = true;
13109
13110 foreach_node(TargetEntry, te, rte->graph_table_columns)
13111 {
13112 if (!first)
13114 else
13115 first = false;
13116
13117 get_rule_expr((Node *) te->expr, context, false);
13118 appendStringInfoString(buf, " AS ");
13120 }
13121 }
13123 break;
13124 case RTE_VALUES:
13125 /* Values list RTE */
13127 get_values_def(rte->values_lists, context);
13129 break;
13130 case RTE_CTE:
13132 break;
13133 default:
13134 elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
13135 break;
13136 }
13137
13138 /* Print the relation alias, if needed */
13139 get_rte_alias(rte, varno, false, context);
13140
13141 /* Print the column definitions or aliases, if needed */
13142 if (rtfunc1 && rtfunc1->funccolnames != NIL)
13143 {
13144 /* Reconstruct the columndef list, which is also the aliases */
13146 }
13147 else
13148 {
13149 /* Else print column aliases as needed */
13151 }
13152
13153 /* Tablesample clause must go after any alias */
13154 if (rte->rtekind == RTE_RELATION && rte->tablesample)
13155 get_tablesample_def(rte->tablesample, context);
13156 }
13157 else if (IsA(jtnode, JoinExpr))
13158 {
13159 JoinExpr *j = (JoinExpr *) jtnode;
13162
13163 need_paren_on_right = PRETTY_PAREN(context) &&
13164 !IsA(j->rarg, RangeTblRef) &&
13165 !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
13166
13167 if (!PRETTY_PAREN(context) || j->alias != NULL)
13169
13170 get_from_clause_item(j->larg, query, context);
13171
13172 switch (j->jointype)
13173 {
13174 case JOIN_INNER:
13175 if (j->quals)
13176 appendContextKeyword(context, " JOIN ",
13180 else
13181 appendContextKeyword(context, " CROSS JOIN ",
13185 break;
13186 case JOIN_LEFT:
13187 appendContextKeyword(context, " LEFT JOIN ",
13191 break;
13192 case JOIN_FULL:
13193 appendContextKeyword(context, " FULL JOIN ",
13197 break;
13198 case JOIN_RIGHT:
13199 appendContextKeyword(context, " RIGHT JOIN ",
13203 break;
13204 default:
13205 elog(ERROR, "unrecognized join type: %d",
13206 (int) j->jointype);
13207 }
13208
13211 get_from_clause_item(j->rarg, query, context);
13214
13215 if (j->usingClause)
13216 {
13217 ListCell *lc;
13218 bool first = true;
13219
13220 appendStringInfoString(buf, " USING (");
13221 /* Use the assigned names, not what's in usingClause */
13222 foreach(lc, colinfo->usingNames)
13223 {
13224 char *colname = (char *) lfirst(lc);
13225
13226 if (first)
13227 first = false;
13228 else
13231 }
13233
13234 if (j->join_using_alias)
13235 appendStringInfo(buf, " AS %s",
13236 quote_identifier(j->join_using_alias->aliasname));
13237 }
13238 else if (j->quals)
13239 {
13240 appendStringInfoString(buf, " ON ");
13241 if (!PRETTY_PAREN(context))
13243 get_rule_expr(j->quals, context, false);
13244 if (!PRETTY_PAREN(context))
13246 }
13247 else if (j->jointype != JOIN_INNER)
13248 {
13249 /* If we didn't say CROSS JOIN above, we must provide an ON */
13250 appendStringInfoString(buf, " ON TRUE");
13251 }
13252
13253 if (!PRETTY_PAREN(context) || j->alias != NULL)
13255
13256 /* Yes, it's correct to put alias after the right paren ... */
13257 if (j->alias != NULL)
13258 {
13259 /*
13260 * Note that it's correct to emit an alias clause if and only if
13261 * there was one originally. Otherwise we'd be converting a named
13262 * join to unnamed or vice versa, which creates semantic
13263 * subtleties we don't want. However, we might print a different
13264 * alias name than was there originally.
13265 */
13266 appendStringInfo(buf, " %s",
13268 context)));
13270 }
13271 }
13272 else
13273 elog(ERROR, "unrecognized node type: %d",
13274 (int) nodeTag(jtnode));
13275}
13276
13277/*
13278 * get_rte_alias - print the relation's alias, if needed
13279 *
13280 * If printed, the alias is preceded by a space, or by " AS " if use_as is true.
13281 */
13282static void
13283get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
13284 deparse_context *context)
13285{
13287 char *refname = get_rtable_name(varno, context);
13289 bool printalias = false;
13290
13291 if (rte->alias != NULL)
13292 {
13293 /* Always print alias if user provided one */
13294 printalias = true;
13295 }
13296 else if (colinfo->printaliases)
13297 {
13298 /* Always print alias if we need to print column aliases */
13299 printalias = true;
13300 }
13301 else if (rte->rtekind == RTE_RELATION)
13302 {
13303 /*
13304 * No need to print alias if it's same as relation name (this would
13305 * normally be the case, but not if set_rtable_names had to resolve a
13306 * conflict).
13307 */
13308 if (strcmp(refname, get_relation_name(rte->relid)) != 0)
13309 printalias = true;
13310 }
13311 else if (rte->rtekind == RTE_FUNCTION)
13312 {
13313 /*
13314 * For a function RTE, always print alias. This covers possible
13315 * renaming of the function and/or instability of the FigureColname
13316 * rules for things that aren't simple functions. Note we'd need to
13317 * force it anyway for the columndef list case.
13318 */
13319 printalias = true;
13320 }
13321 else if (rte->rtekind == RTE_SUBQUERY ||
13322 rte->rtekind == RTE_VALUES)
13323 {
13324 /*
13325 * For a subquery, always print alias. This makes the output
13326 * SQL-spec-compliant, even though we allow such aliases to be omitted
13327 * on input.
13328 */
13329 printalias = true;
13330 }
13331 else if (rte->rtekind == RTE_CTE)
13332 {
13333 /*
13334 * No need to print alias if it's same as CTE name (this would
13335 * normally be the case, but not if set_rtable_names had to resolve a
13336 * conflict).
13337 */
13338 if (strcmp(refname, rte->ctename) != 0)
13339 printalias = true;
13340 }
13341
13342 if (printalias)
13343 appendStringInfo(context->buf, "%s%s",
13344 use_as ? " AS " : " ",
13345 quote_identifier(refname));
13346}
13347
13348/*
13349 * get_for_portion_of - print FOR PORTION OF if needed
13350 * XXX: Newlines would help here, at least when pretty-printing. But then the
13351 * alias and SET will be on their own line with a leading space.
13352 */
13353static void
13355{
13356 if (forPortionOf)
13357 {
13358 appendStringInfo(context->buf, " FOR PORTION OF %s",
13359 quote_identifier(forPortionOf->range_name));
13360
13361 /*
13362 * Try to write it as FROM ... TO ... if we received it that way,
13363 * otherwise (targetRange).
13364 */
13365 if (forPortionOf->targetFrom && forPortionOf->targetTo)
13366 {
13367 appendStringInfoString(context->buf, " FROM ");
13368 get_rule_expr(forPortionOf->targetFrom, context, false);
13369 appendStringInfoString(context->buf, " TO ");
13370 get_rule_expr(forPortionOf->targetTo, context, false);
13371 }
13372 else
13373 {
13374 appendStringInfoString(context->buf, " (");
13375 get_rule_expr(forPortionOf->targetRange, context, false);
13376 appendStringInfoChar(context->buf, ')');
13377 }
13378 }
13379}
13380
13381/*
13382 * get_column_alias_list - print column alias list for an RTE
13383 *
13384 * Caller must already have printed the relation's alias name.
13385 */
13386static void
13388{
13389 StringInfo buf = context->buf;
13390 int i;
13391 bool first = true;
13392
13393 /* Don't print aliases if not needed */
13394 if (!colinfo->printaliases)
13395 return;
13396
13397 for (i = 0; i < colinfo->num_new_cols; i++)
13398 {
13399 char *colname = colinfo->new_colnames[i];
13400
13401 if (first)
13402 {
13404 first = false;
13405 }
13406 else
13409 }
13410 if (!first)
13412}
13413
13414/*
13415 * get_from_clause_coldeflist - reproduce FROM clause coldeflist
13416 *
13417 * When printing a top-level coldeflist (which is syntactically also the
13418 * relation's column alias list), use column names from colinfo. But when
13419 * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
13420 * original coldeflist's names, which are available in rtfunc->funccolnames.
13421 * Pass NULL for colinfo to select the latter behavior.
13422 *
13423 * The coldeflist is appended immediately (no space) to buf. Caller is
13424 * responsible for ensuring that an alias or AS is present before it.
13425 */
13426static void
13429 deparse_context *context)
13430{
13431 StringInfo buf = context->buf;
13432 ListCell *l1;
13433 ListCell *l2;
13434 ListCell *l3;
13435 ListCell *l4;
13436 int i;
13437
13439
13440 i = 0;
13441 forfour(l1, rtfunc->funccoltypes,
13442 l2, rtfunc->funccoltypmods,
13443 l3, rtfunc->funccolcollations,
13444 l4, rtfunc->funccolnames)
13445 {
13446 Oid atttypid = lfirst_oid(l1);
13447 int32 atttypmod = lfirst_int(l2);
13448 Oid attcollation = lfirst_oid(l3);
13449 char *attname;
13450
13451 if (colinfo)
13452 attname = colinfo->colnames[i];
13453 else
13454 attname = strVal(lfirst(l4));
13455
13456 Assert(attname); /* shouldn't be any dropped columns here */
13457
13458 if (i > 0)
13460 appendStringInfo(buf, "%s %s",
13463 if (OidIsValid(attcollation) &&
13464 attcollation != get_typcollation(atttypid))
13465 appendStringInfo(buf, " COLLATE %s",
13466 generate_collation_name(attcollation));
13467
13468 i++;
13469 }
13470
13472}
13473
13474/*
13475 * get_tablesample_def - print a TableSampleClause
13476 */
13477static void
13479{
13480 StringInfo buf = context->buf;
13481 Oid argtypes[1];
13482 int nargs;
13483 ListCell *l;
13484
13485 /*
13486 * We should qualify the handler's function name if it wouldn't be
13487 * resolved by lookup in the current search path.
13488 */
13489 argtypes[0] = INTERNALOID;
13490 appendStringInfo(buf, " TABLESAMPLE %s (",
13491 generate_function_name(tablesample->tsmhandler, 1,
13492 NIL, argtypes,
13493 false, NULL, false));
13494
13495 nargs = 0;
13496 foreach(l, tablesample->args)
13497 {
13498 if (nargs++ > 0)
13500 get_rule_expr((Node *) lfirst(l), context, false);
13501 }
13503
13504 if (tablesample->repeatable != NULL)
13505 {
13506 appendStringInfoString(buf, " REPEATABLE (");
13507 get_rule_expr((Node *) tablesample->repeatable, context, false);
13509 }
13510}
13511
13512/*
13513 * get_opclass_name - fetch name of an index operator class
13514 *
13515 * The opclass name is appended (after a space) to buf.
13516 *
13517 * Output is suppressed if the opclass is the default for the given
13518 * actual_datatype. (If you don't want this behavior, just pass
13519 * InvalidOid for actual_datatype.)
13520 */
13521static void
13524{
13527 char *opcname;
13528 char *nspname;
13529
13532 elog(ERROR, "cache lookup failed for opclass %u", opclass);
13534
13536 GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
13537 {
13538 /* Okay, we need the opclass name. Do we need to qualify it? */
13539 opcname = NameStr(opcrec->opcname);
13540 if (OpclassIsVisible(opclass))
13542 else
13543 {
13544 nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
13545 appendStringInfo(buf, " %s.%s",
13546 quote_identifier(nspname),
13548 }
13549 }
13551}
13552
13553/*
13554 * generate_opclass_name
13555 * Compute the name to display for an opclass specified by OID
13556 *
13557 * The result includes all necessary quoting and schema-prefixing.
13558 */
13559char *
13561{
13563
13565 get_opclass_name(opclass, InvalidOid, &buf);
13566
13567 return &buf.data[1]; /* get_opclass_name() prepends space */
13568}
13569
13570/*
13571 * processIndirection - take care of array and subfield assignment
13572 *
13573 * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
13574 * appear in the input, printing them as decoration for the base column
13575 * name (which we assume the caller just printed). We might also need to
13576 * strip CoerceToDomain nodes, but only ones that appear above assignment
13577 * nodes.
13578 *
13579 * Returns the subexpression that's to be assigned.
13580 */
13581static Node *
13583{
13584 StringInfo buf = context->buf;
13586
13587 for (;;)
13588 {
13589 if (node == NULL)
13590 break;
13591 if (IsA(node, FieldStore))
13592 {
13593 FieldStore *fstore = (FieldStore *) node;
13594 Oid typrelid;
13595 char *fieldname;
13596
13597 /* lookup tuple type */
13598 typrelid = get_typ_typrelid(fstore->resulttype);
13599 if (!OidIsValid(typrelid))
13600 elog(ERROR, "argument type %s of FieldStore is not a tuple type",
13601 format_type_be(fstore->resulttype));
13602
13603 /*
13604 * Print the field name. There should only be one target field in
13605 * stored rules. There could be more than that in executable
13606 * target lists, but this function cannot be used for that case.
13607 */
13608 Assert(list_length(fstore->fieldnums) == 1);
13609 fieldname = get_attname(typrelid,
13610 linitial_int(fstore->fieldnums), false);
13611 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
13612
13613 /*
13614 * We ignore arg since it should be an uninteresting reference to
13615 * the target column or subcolumn.
13616 */
13617 node = (Node *) linitial(fstore->newvals);
13618 }
13619 else if (IsA(node, SubscriptingRef))
13620 {
13621 SubscriptingRef *sbsref = (SubscriptingRef *) node;
13622
13623 if (sbsref->refassgnexpr == NULL)
13624 break;
13625
13626 printSubscripts(sbsref, context);
13627
13628 /*
13629 * We ignore refexpr since it should be an uninteresting reference
13630 * to the target column or subcolumn.
13631 */
13632 node = (Node *) sbsref->refassgnexpr;
13633 }
13634 else if (IsA(node, CoerceToDomain))
13635 {
13636 cdomain = (CoerceToDomain *) node;
13637 /* If it's an explicit domain coercion, we're done */
13638 if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
13639 break;
13640 /* Tentatively descend past the CoerceToDomain */
13641 node = (Node *) cdomain->arg;
13642 }
13643 else
13644 break;
13645 }
13646
13647 /*
13648 * If we descended past a CoerceToDomain whose argument turned out not to
13649 * be a FieldStore or array assignment, back up to the CoerceToDomain.
13650 * (This is not enough to be fully correct if there are nested implicit
13651 * CoerceToDomains, but such cases shouldn't ever occur.)
13652 */
13653 if (cdomain && node == (Node *) cdomain->arg)
13654 node = (Node *) cdomain;
13655
13656 return node;
13657}
13658
13659static void
13661{
13662 StringInfo buf = context->buf;
13665
13666 lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
13667 foreach(uplist_item, sbsref->refupperindexpr)
13668 {
13670 if (lowlist_item)
13671 {
13672 /* If subexpression is NULL, get_rule_expr prints nothing */
13673 get_rule_expr((Node *) lfirst(lowlist_item), context, false);
13676 }
13677 /* If subexpression is NULL, get_rule_expr prints nothing */
13678 get_rule_expr((Node *) lfirst(uplist_item), context, false);
13680 }
13681}
13682
13683/*
13684 * quote_identifier - Quote an identifier only if needed
13685 *
13686 * When quotes are needed, we palloc the required space; slightly
13687 * space-wasteful but well worth it for notational simplicity.
13688 */
13689const char *
13690quote_identifier(const char *ident)
13691{
13692 /*
13693 * Can avoid quoting if ident starts with a lowercase letter or underscore
13694 * and contains only lowercase letters, digits, and underscores, *and* is
13695 * not any SQL keyword. Otherwise, supply quotes.
13696 */
13697 int nquotes = 0;
13698 bool safe;
13699 const char *ptr;
13700 char *result;
13701 char *optr;
13702
13703 /*
13704 * would like to use <ctype.h> macros here, but they might yield unwanted
13705 * locale-specific results...
13706 */
13707 safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
13708
13709 for (ptr = ident; *ptr; ptr++)
13710 {
13711 char ch = *ptr;
13712
13713 if ((ch >= 'a' && ch <= 'z') ||
13714 (ch >= '0' && ch <= '9') ||
13715 (ch == '_'))
13716 {
13717 /* okay */
13718 }
13719 else
13720 {
13721 safe = false;
13722 if (ch == '"')
13723 nquotes++;
13724 }
13725 }
13726
13728 safe = false;
13729
13730 if (safe)
13731 {
13732 /*
13733 * Check for keyword. We quote keywords except for unreserved ones.
13734 * (In some cases we could avoid quoting a col_name or type_func_name
13735 * keyword, but it seems much harder than it's worth to tell that.)
13736 *
13737 * Note: ScanKeywordLookup() does case-insensitive comparison, but
13738 * that's fine, since we already know we have all-lower-case.
13739 */
13741
13743 safe = false;
13744 }
13745
13746 if (safe)
13747 return ident; /* no change needed */
13748
13749 result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13750
13751 optr = result;
13752 *optr++ = '"';
13753 for (ptr = ident; *ptr; ptr++)
13754 {
13755 char ch = *ptr;
13756
13757 if (ch == '"')
13758 *optr++ = '"';
13759 *optr++ = ch;
13760 }
13761 *optr++ = '"';
13762 *optr = '\0';
13763
13764 return result;
13765}
13766
13767/*
13768 * quote_qualified_identifier - Quote a possibly-qualified identifier
13769 *
13770 * Return a name of the form qualifier.ident, or just ident if qualifier
13771 * is NULL, quoting each component if necessary. The result is palloc'd.
13772 */
13773char *
13775 const char *ident)
13776{
13778
13780 if (qualifier)
13783 return buf.data;
13784}
13785
13786/*
13787 * get_relation_name
13788 * Get the unqualified name of a relation specified by OID
13789 *
13790 * This differs from the underlying get_rel_name() function in that it will
13791 * throw error instead of silently returning NULL if the OID is bad.
13792 */
13793static char *
13795{
13796 char *relname = get_rel_name(relid);
13797
13798 if (!relname)
13799 elog(ERROR, "cache lookup failed for relation %u", relid);
13800 return relname;
13801}
13802
13803/*
13804 * generate_relation_name
13805 * Compute the name to display for a relation specified by OID
13806 *
13807 * The result includes all necessary quoting and schema-prefixing.
13808 *
13809 * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
13810 * We will forcibly qualify the relation name if it equals any CTE name
13811 * visible in the namespace list.
13812 */
13813static char *
13814generate_relation_name(Oid relid, List *namespaces)
13815{
13816 HeapTuple tp;
13818 bool need_qual;
13820 char *relname;
13821 char *nspname;
13822 char *result;
13823
13825 if (!HeapTupleIsValid(tp))
13826 elog(ERROR, "cache lookup failed for relation %u", relid);
13828 relname = NameStr(reltup->relname);
13829
13830 /* Check for conflicting CTE name */
13831 need_qual = false;
13832 foreach(nslist, namespaces)
13833 {
13836
13837 foreach(ctlist, dpns->ctes)
13838 {
13840
13841 if (strcmp(cte->ctename, relname) == 0)
13842 {
13843 need_qual = true;
13844 break;
13845 }
13846 }
13847 if (need_qual)
13848 break;
13849 }
13850
13851 /* Otherwise, qualify the name if not visible in search path */
13852 if (!need_qual)
13853 need_qual = !RelationIsVisible(relid);
13854
13855 if (need_qual)
13856 nspname = get_namespace_name_or_temp(reltup->relnamespace);
13857 else
13858 nspname = NULL;
13859
13861
13862 ReleaseSysCache(tp);
13863
13864 return result;
13865}
13866
13867/*
13868 * generate_qualified_relation_name
13869 * Compute the name to display for a relation specified by OID
13870 *
13871 * As above, but unconditionally schema-qualify the name.
13872 */
13873static char *
13875{
13876 HeapTuple tp;
13878 char *result;
13879
13881 if (!HeapTupleIsValid(tp))
13882 elog(ERROR, "cache lookup failed for relation %u", relid);
13884
13885 result = get_qualified_objname(reltup->relnamespace,
13886 NameStr(reltup->relname));
13887 ReleaseSysCache(tp);
13888
13889 return result;
13890}
13891
13892/*
13893 * generate_function_name
13894 * Compute the name to display for a function specified by OID,
13895 * given that it is being called with the specified actual arg names and
13896 * types. (Those matter because of ambiguous-function resolution rules.)
13897 *
13898 * If we're dealing with a potentially variadic function (in practice, this
13899 * means a FuncExpr or Aggref, not some other way of calling a function), then
13900 * has_variadic must specify whether variadic arguments have been merged,
13901 * and *use_variadic_p will be set to indicate whether to print VARIADIC in
13902 * the output. For non-FuncExpr cases, has_variadic should be false and
13903 * use_variadic_p can be NULL.
13904 *
13905 * inGroupBy must be true if we're deparsing a GROUP BY clause.
13906 *
13907 * The result includes all necessary quoting and schema-prefixing.
13908 */
13909static char *
13910generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
13911 bool has_variadic, bool *use_variadic_p,
13912 bool inGroupBy)
13913{
13914 char *result;
13917 char *proname;
13918 bool use_variadic;
13919 char *nspname;
13921 int fgc_flags;
13922 Oid p_funcid;
13923 Oid p_rettype;
13924 bool p_retset;
13925 int p_nvargs;
13926 Oid p_vatype;
13928 bool force_qualify = false;
13929
13932 elog(ERROR, "cache lookup failed for function %u", funcid);
13934 proname = NameStr(procform->proname);
13935
13936 /*
13937 * Due to parser hacks to avoid needing to reserve CUBE, we need to force
13938 * qualification of some function names within GROUP BY.
13939 */
13940 if (inGroupBy)
13941 {
13942 if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
13943 force_qualify = true;
13944 }
13945
13946 /*
13947 * Determine whether VARIADIC should be printed. We must do this first
13948 * since it affects the lookup rules in func_get_detail().
13949 *
13950 * We always print VARIADIC if the function has a merged variadic-array
13951 * argument. Note that this is always the case for functions taking a
13952 * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
13953 * and printed the array elements as separate arguments, the call could
13954 * match a newer non-VARIADIC function.
13955 */
13956 if (use_variadic_p)
13957 {
13958 /* Parser should not have set funcvariadic unless fn is variadic */
13959 Assert(!has_variadic || OidIsValid(procform->provariadic));
13962 }
13963 else
13964 {
13966 use_variadic = false;
13967 }
13968
13969 /*
13970 * The idea here is to schema-qualify only if the parser would fail to
13971 * resolve the correct function given the unqualified func name with the
13972 * specified argtypes and VARIADIC flag. But if we already decided to
13973 * force qualification, then we can skip the lookup and pretend we didn't
13974 * find it.
13975 */
13976 if (!force_qualify)
13978 NIL, argnames, nargs, argtypes,
13979 !use_variadic, true, false,
13980 &fgc_flags,
13984 else
13985 {
13988 }
13989
13990 if ((p_result == FUNCDETAIL_NORMAL ||
13993 p_funcid == funcid)
13994 nspname = NULL;
13995 else
13996 nspname = get_namespace_name_or_temp(procform->pronamespace);
13997
13999
14001
14002 return result;
14003}
14004
14005/*
14006 * generate_operator_name
14007 * Compute the name to display for an operator specified by OID,
14008 * given that it is being called with the specified actual arg types.
14009 * (Arg types matter because of ambiguous-operator resolution rules.
14010 * Pass InvalidOid for unused arg of a unary operator.)
14011 *
14012 * The result includes all necessary quoting and schema-prefixing,
14013 * plus the OPERATOR() decoration needed to use a qualified operator name
14014 * in an expression.
14015 */
14016static char *
14018{
14022 char *oprname;
14023 char *nspname;
14025
14027
14030 elog(ERROR, "cache lookup failed for operator %u", operid);
14032 oprname = NameStr(operform->oprname);
14033
14034 /*
14035 * The idea here is to schema-qualify only if the parser would fail to
14036 * resolve the correct operator given the unqualified op name with the
14037 * specified argtypes.
14038 */
14039 switch (operform->oprkind)
14040 {
14041 case 'b':
14043 true, -1);
14044 break;
14045 case 'l':
14047 true, -1);
14048 break;
14049 default:
14050 elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
14051 p_result = NULL; /* keep compiler quiet */
14052 break;
14053 }
14054
14055 if (p_result != NULL && oprid(p_result) == operid)
14056 nspname = NULL;
14057 else
14058 {
14059 nspname = get_namespace_name_or_temp(operform->oprnamespace);
14060 appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
14061 }
14062
14063 appendStringInfoString(&buf, oprname);
14064
14065 if (nspname)
14067
14068 if (p_result != NULL)
14070
14072
14073 return buf.data;
14074}
14075
14076/*
14077 * generate_operator_clause --- generate a binary-operator WHERE clause
14078 *
14079 * This is used for internally-generated-and-executed SQL queries, where
14080 * precision is essential and readability is secondary. The basic
14081 * requirement is to append "leftop op rightop" to buf, where leftop and
14082 * rightop are given as strings and are assumed to yield types leftoptype
14083 * and rightoptype; the operator is identified by OID. The complexity
14084 * comes from needing to be sure that the parser will select the desired
14085 * operator when the query is parsed. We always name the operator using
14086 * OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
14087 * We have to emit casts too, if either input isn't already the input type
14088 * of the operator; else we are at the mercy of the parser's heuristics for
14089 * ambiguous-operator resolution. The caller must ensure that leftop and
14090 * rightop are suitable arguments for a cast operation; it's best to insert
14091 * parentheses if they aren't just variables or parameters.
14092 */
14093void
14095 const char *leftop, Oid leftoptype,
14096 Oid opoid,
14097 const char *rightop, Oid rightoptype)
14098{
14101 char *oprname;
14102 char *nspname;
14103
14106 elog(ERROR, "cache lookup failed for operator %u", opoid);
14108 Assert(operform->oprkind == 'b');
14109 oprname = NameStr(operform->oprname);
14110
14111 nspname = get_namespace_name(operform->oprnamespace);
14112
14114 if (leftoptype != operform->oprleft)
14115 add_cast_to(buf, operform->oprleft);
14116 appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
14117 appendStringInfoString(buf, oprname);
14118 appendStringInfo(buf, ") %s", rightop);
14119 if (rightoptype != operform->oprright)
14120 add_cast_to(buf, operform->oprright);
14121
14123}
14124
14125/*
14126 * Add a cast specification to buf. We spell out the type name the hard way,
14127 * intentionally not using format_type_be(). This is to avoid corner cases
14128 * for CHARACTER, BIT, and perhaps other types, where specifying the type
14129 * using SQL-standard syntax results in undesirable data truncation. By
14130 * doing it this way we can be certain that the cast will have default (-1)
14131 * target typmod.
14132 */
14133static void
14135{
14138 char *typname;
14139 char *nspname;
14140
14143 elog(ERROR, "cache lookup failed for type %u", typid);
14145
14146 typname = NameStr(typform->typname);
14147 nspname = get_namespace_name_or_temp(typform->typnamespace);
14148
14149 appendStringInfo(buf, "::%s.%s",
14151
14153}
14154
14155/*
14156 * generate_qualified_type_name
14157 * Compute the name to display for a type specified by OID
14158 *
14159 * This is different from format_type_be() in that we unconditionally
14160 * schema-qualify the name. That also means no special syntax for
14161 * SQL-standard type names ... although in current usage, this should
14162 * only get used for domains, so such cases wouldn't occur anyway.
14163 */
14164static char *
14166{
14167 HeapTuple tp;
14169 char *typname;
14170 char *nspname;
14171 char *result;
14172
14174 if (!HeapTupleIsValid(tp))
14175 elog(ERROR, "cache lookup failed for type %u", typid);
14177 typname = NameStr(typtup->typname);
14178
14179 nspname = get_namespace_name_or_temp(typtup->typnamespace);
14180 if (!nspname)
14181 elog(ERROR, "cache lookup failed for namespace %u",
14182 typtup->typnamespace);
14183
14185
14186 ReleaseSysCache(tp);
14187
14188 return result;
14189}
14190
14191/*
14192 * generate_collation_name
14193 * Compute the name to display for a collation specified by OID
14194 *
14195 * The result includes all necessary quoting and schema-prefixing.
14196 */
14197char *
14199{
14200 HeapTuple tp;
14202 char *collname;
14203 char *nspname;
14204 char *result;
14205
14207 if (!HeapTupleIsValid(tp))
14208 elog(ERROR, "cache lookup failed for collation %u", collid);
14210 collname = NameStr(colltup->collname);
14211
14213 nspname = get_namespace_name_or_temp(colltup->collnamespace);
14214 else
14215 nspname = NULL;
14216
14217 result = quote_qualified_identifier(nspname, collname);
14218
14219 ReleaseSysCache(tp);
14220
14221 return result;
14222}
14223
14224/*
14225 * Given a C string, produce a TEXT datum.
14226 *
14227 * We assume that the input was palloc'd and may be freed.
14228 */
14229static text *
14230string_to_text(char *str)
14231{
14232 text *result;
14233
14235 pfree(str);
14236 return result;
14237}
14238
14239/*
14240 * Generate a C string representing a relation options from text[] datum.
14241 */
14242void
14244{
14245 Datum *options;
14246 int noptions;
14247 int i;
14248
14250 &options, NULL, &noptions);
14251
14252 for (i = 0; i < noptions; i++)
14253 {
14255 char *name;
14256 char *separator;
14257 char *value;
14258
14259 /*
14260 * Each array element should have the form name=value. If the "=" is
14261 * missing for some reason, treat it like an empty value.
14262 */
14263 name = option;
14264 separator = strchr(option, '=');
14265 if (separator)
14266 {
14267 *separator = '\0';
14268 value = separator + 1;
14269 }
14270 else
14271 value = "";
14272
14273 if (i > 0)
14276
14277 /*
14278 * In general we need to quote the value; but to avoid unnecessary
14279 * clutter, do not quote if it is an identifier that would not need
14280 * quoting. (We could also allow numbers, but that is a bit trickier
14281 * than it looks --- for example, are leading zeroes significant? We
14282 * don't want to assume very much here about what custom reloptions
14283 * might mean.)
14284 */
14287 else
14289
14290 pfree(option);
14291 }
14292}
14293
14294/*
14295 * Generate a C string representing a relation's reloptions, or NULL if none.
14296 */
14297static char *
14299{
14300 char *result = NULL;
14301 HeapTuple tuple;
14302 Datum reloptions;
14303 bool isnull;
14304
14305 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14306 if (!HeapTupleIsValid(tuple))
14307 elog(ERROR, "cache lookup failed for relation %u", relid);
14308
14309 reloptions = SysCacheGetAttr(RELOID, tuple,
14310 Anum_pg_class_reloptions, &isnull);
14311 if (!isnull)
14312 {
14314
14316 get_reloptions(&buf, reloptions);
14317
14318 result = buf.data;
14319 }
14320
14321 ReleaseSysCache(tuple);
14322
14323 return result;
14324}
14325
14326/*
14327 * get_range_partbound_string
14328 * A C string representation of one range partition bound
14329 */
14330char *
14332{
14333 deparse_context context;
14335 ListCell *cell;
14336 char *sep;
14337
14339 memset(&context, 0, sizeof(deparse_context));
14340 context.buf = &buf;
14341
14343 sep = "";
14344 foreach(cell, bound_datums)
14345 {
14346 PartitionRangeDatum *datum =
14348
14351 appendStringInfoString(&buf, "MINVALUE");
14352 else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
14353 appendStringInfoString(&buf, "MAXVALUE");
14354 else
14355 {
14356 Const *val = castNode(Const, datum->value);
14357
14358 get_const_expr(val, &context, -1);
14359 }
14360 sep = ", ";
14361 }
14363
14364 return buf.data;
14365}
const IndexAmRoutine * GetIndexAmRoutine(Oid amhandler)
Definition amapi.c:33
#define ARR_NDIM(a)
Definition array.h:290
#define ARR_DATA_PTR(a)
Definition array.h:322
#define DatumGetArrayTypeP(X)
Definition array.h:261
#define ARR_ELEMTYPE(a)
Definition array.h:292
#define ARR_DIMS(a)
Definition array.h:294
#define ARR_HASNULL(a)
Definition array.h:291
#define ARR_LBOUND(a)
Definition array.h:296
ArrayBuildState * accumArrayResult(ArrayBuildState *astate, Datum dvalue, bool disnull, Oid element_type, MemoryContext rcontext)
Datum array_ref(ArrayType *array, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
void deconstruct_array_builtin(const ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext)
int16 AttrNumber
Definition attnum.h:21
#define InvalidAttrNumber
Definition attnum.h:23
char * get_tablespace_name(Oid spc_oid)
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:216
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:412
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:510
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:251
#define bms_is_empty(a)
Definition bitmapset.h:118
#define TextDatumGetCString(d)
Definition builtins.h:99
#define NameStr(name)
Definition c.h:835
NameData * Name
Definition c.h:833
#define Max(x, y)
Definition c.h:1085
#define Assert(condition)
Definition c.h:943
int16_t int16
Definition c.h:619
#define SQL_STR_DOUBLE(ch, escape_backslash)
Definition c.h:1252
int32_t int32
Definition c.h:620
uint16_t uint16
Definition c.h:623
#define lengthof(array)
Definition c.h:873
unsigned int Index
Definition c.h:698
float float4
Definition c.h:713
#define pg_fallthrough
Definition c.h:161
#define OidIsValid(objectId)
Definition c.h:858
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
Oid collid
const uint8 ScanKeywordCategories[SCANKEYWORDS_NUM_KEYWORDS]
Definition keywords.c:29
static DataChecksumsWorkerOperation operation
@ DEPENDENCY_AUTO
Definition dependency.h:34
@ DEPENDENCY_INTERNAL
Definition dependency.h:35
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition dynahash.c:889
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition dynahash.c:360
void hash_destroy(HTAB *hashp)
Definition dynahash.c:802
Datum arg
Definition elog.c:1323
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
bool equal(const void *a, const void *b)
Definition equalfuncs.c:223
#define palloc_object(type)
Definition fe_memutils.h:89
#define palloc0_array(type, count)
Definition fe_memutils.h:92
#define palloc0_object(type)
Definition fe_memutils.h:90
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition fmgr.c:1764
#define PG_GETARG_OID(n)
Definition fmgr.h:275
#define PG_GETARG_TEXT_PP(n)
Definition fmgr.h:310
#define DatumGetByteaPP(X)
Definition fmgr.h:292
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:688
#define PG_RETURN_NULL()
Definition fmgr.h:346
#define PG_RETURN_TEXT_P(x)
Definition fmgr.h:374
#define PG_RETURN_NAME(x)
Definition fmgr.h:365
#define PG_GETARG_INT32(n)
Definition fmgr.h:269
#define PG_GETARG_BOOL(n)
Definition fmgr.h:274
#define PG_RETURN_DATUM(x)
Definition fmgr.h:354
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
char * format_type_with_typemod(Oid type_oid, int32 typemod)
char * format_type_be(Oid type_oid)
int get_func_trftypes(HeapTuple procTup, Oid **p_trftypes)
Definition funcapi.c:1478
int get_func_arg_info(HeapTuple procTup, Oid **p_argtypes, char ***p_argnames, char **p_argmodes)
Definition funcapi.c:1382
TupleDesc get_expr_result_tupdesc(Node *expr, bool noError)
Definition funcapi.c:553
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:612
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:523
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
int GetConfigOptionFlags(const char *name, bool missing_ok)
Definition guc.c:4354
#define GUC_LIST_QUOTE
Definition guc.h:215
const char * str
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition heaptuple.c:456
#define HASH_STRINGS
Definition hsearch.h:91
@ HASH_FIND
Definition hsearch.h:108
@ HASH_ENTER
Definition hsearch.h:109
#define HASH_CONTEXT
Definition hsearch.h:97
#define HASH_ELEM
Definition hsearch.h:90
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
static void * GETSTRUCT(const HeapTupleData *tuple)
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
#define stmt
#define ident
#define funcname
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition indexcmds.c:2371
long val
Definition informix.c:689
static struct @177 value
static char * encoding
Definition initdb.c:139
int b
Definition isn.c:74
int a
Definition isn.c:73
int j
Definition isn.c:78
int i
Definition isn.c:77
PGDLLIMPORT const ScanKeywordList ScanKeywords
#define UNRESERVED_KEYWORD
Definition keywords.h:20
int ScanKeywordLookup(const char *str, const ScanKeywordList *keywords)
Definition kwlookup.c:38
List * lappend(List *list, void *datum)
Definition list.c:339
void list_sort(List *list, list_sort_comparator cmp)
Definition list.c:1674
List * list_copy_tail(const List *oldlist, int nskip)
Definition list.c:1613
List * list_delete_first(List *list)
Definition list.c:943
List * list_concat(List *list1, const List *list2)
Definition list.c:561
List * list_copy(const List *oldlist)
Definition list.c:1573
List * lcons(void *datum, List *list)
Definition list.c:495
void list_free(List *list)
Definition list.c:1546
#define NoLock
Definition lockdefs.h:34
#define AccessShareLock
Definition lockdefs.h:36
@ LockWaitSkip
Definition lockoptions.h:42
@ LockWaitError
Definition lockoptions.h:44
LockClauseStrength
Definition lockoptions.h:22
@ LCS_FORUPDATE
Definition lockoptions.h:28
@ LCS_NONE
Definition lockoptions.h:23
@ LCS_FORSHARE
Definition lockoptions.h:26
@ LCS_FORKEYSHARE
Definition lockoptions.h:25
@ LCS_FORNOKEYUPDATE
Definition lockoptions.h:27
char * get_rel_name(Oid relid)
Definition lsyscache.c:2159
char * get_propgraph_property_name(Oid propoid)
Definition lsyscache.c:4015
AttrNumber get_attnum(Oid relid, const char *attname)
Definition lsyscache.c:1015
Oid get_opclass_input_type(Oid opclass)
Definition lsyscache.c:1395
bool type_is_rowtype(Oid typid)
Definition lsyscache.c:2888
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition lsyscache.c:3140
Datum get_attoptions(Oid relid, int16 attnum)
Definition lsyscache.c:1127
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2234
Oid get_typcollation(Oid typid)
Definition lsyscache.c:3289
char * get_language_name(Oid langoid, bool missing_ok)
Definition lsyscache.c:1344
char * get_namespace_name_or_temp(Oid nspid)
Definition lsyscache.c:3623
char * get_propgraph_label_name(Oid labeloid)
Definition lsyscache.c:3997
char * get_constraint_name(Oid conoid)
Definition lsyscache.c:1238
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition lsyscache.c:984
Oid get_rel_tablespace(Oid relid)
Definition lsyscache.c:2285
Oid get_typ_typrelid(Oid typid)
Definition lsyscache.c:2964
Oid get_base_element_type(Oid typid)
Definition lsyscache.c:3065
char * get_qualified_objname(Oid nspid, char *objname)
Definition lsyscache.c:3637
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3599
void get_type_category_preferred(Oid typid, char *typcategory, bool *typispreferred)
Definition lsyscache.c:2943
void get_atttypetypmodcoll(Oid relid, AttrNumber attnum, Oid *typid, int32 *typmod, Oid *collid)
Definition lsyscache.c:1100
Alias * makeAlias(const char *aliasname, List *colnames)
Definition makefuncs.c:438
int pg_mbcliplen(const char *mbstr, int len, int limit)
Definition mbutils.c:1212
char * pstrdup(const char *in)
Definition mcxt.c:1910
void pfree(void *pointer)
Definition mcxt.c:1619
void * palloc0(Size size)
Definition mcxt.c:1420
void * palloc(Size size)
Definition mcxt.c:1390
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
Datum namein(PG_FUNCTION_ARGS)
Definition name.c:48
bool CollationIsVisible(Oid collid)
Definition namespace.c:2476
bool RelationIsVisible(Oid relid)
Definition namespace.c:914
bool OpclassIsVisible(Oid opcid)
Definition namespace.c:2223
RangeVar * makeRangeVarFromNameList(const List *names)
Definition namespace.c:3626
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition namespace.h:98
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
bool exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
Definition nodeFuncs.c:562
int32 exprTypmod(const Node *expr)
Definition nodeFuncs.c:304
Oid exprCollation(const Node *expr)
Definition nodeFuncs.c:826
Node * strip_implicit_coercions(Node *node)
Definition nodeFuncs.c:710
#define DO_AGGSPLIT_SKIPFINAL(as)
Definition nodes.h:396
#define IsA(nodeptr, _type_)
Definition nodes.h:164
#define nodeTag(nodeptr)
Definition nodes.h:139
#define DO_AGGSPLIT_COMBINE(as)
Definition nodes.h:395
@ ONCONFLICT_SELECT
Definition nodes.h:431
@ ONCONFLICT_UPDATE
Definition nodes.h:430
@ ONCONFLICT_NOTHING
Definition nodes.h:429
@ CMD_MERGE
Definition nodes.h:279
@ CMD_UTILITY
Definition nodes.h:280
@ CMD_INSERT
Definition nodes.h:277
@ CMD_DELETE
Definition nodes.h:278
@ CMD_UPDATE
Definition nodes.h:276
@ CMD_SELECT
Definition nodes.h:275
@ CMD_NOTHING
Definition nodes.h:282
@ LIMIT_OPTION_WITH_TIES
Definition nodes.h:443
#define makeNode(_type_)
Definition nodes.h:161
#define castNode(_type_, nodeptr)
Definition nodes.h:182
@ JOIN_FULL
Definition nodes.h:305
@ JOIN_INNER
Definition nodes.h:303
@ JOIN_RIGHT
Definition nodes.h:306
@ JOIN_LEFT
Definition nodes.h:304
static char * errmsg
Datum lower(PG_FUNCTION_ARGS)
Datum upper(PG_FUNCTION_ARGS)
#define repalloc0_array(pointer, type, oldcount, count)
Definition palloc.h:122
int get_aggregate_argtypes(Aggref *aggref, Oid *inputTypes)
Definition parse_agg.c:2122
FuncDetailCode func_get_detail(List *funcname, List *fargs, List *fargnames, int nargs, Oid *argtypes, bool expand_variadic, bool expand_defaults, bool include_out_arguments, int *fgc_flags, Oid *funcid, Oid *rettype, bool *retset, int *nvargs, Oid *vatype, Oid **true_typeids, List **argdefaults)
FuncDetailCode
Definition parse_func.h:23
@ FUNCDETAIL_NORMAL
Definition parse_func.h:26
@ FUNCDETAIL_WINDOWFUNC
Definition parse_func.h:29
@ FUNCDETAIL_NOTFOUND
Definition parse_func.h:24
@ FUNCDETAIL_AGGREGATE
Definition parse_func.h:28
Operator left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
Definition parse_oper.c:529
Oid oprid(Operator op)
Definition parse_oper.c:241
Operator oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId, bool noError, int location)
Definition parse_oper.c:376
char * get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
TargetEntry * get_tle_by_resno(List *tlist, AttrNumber resno)
void expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, VarReturningType returning_type, int location, bool include_dropped, List **colnames, List **colvars)
#define FRAMEOPTION_END_CURRENT_ROW
Definition parsenodes.h:622
#define FRAMEOPTION_END_OFFSET
Definition parsenodes.h:633
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition parsenodes.h:627
@ GROUPING_SET_CUBE
@ GROUPING_SET_SIMPLE
@ GROUPING_SET_ROLLUP
@ GROUPING_SET_SETS
@ GROUPING_SET_EMPTY
#define FKCONSTR_ACTION_RESTRICT
@ SETOP_INTERSECT
@ SETOP_UNION
@ SETOP_EXCEPT
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition parsenodes.h:624
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition parsenodes.h:617
#define GetCTETargetList(cte)
#define FKCONSTR_ACTION_SETDEFAULT
@ PARTITION_STRATEGY_HASH
Definition parsenodes.h:919
@ PARTITION_STRATEGY_LIST
Definition parsenodes.h:917
@ PARTITION_STRATEGY_RANGE
Definition parsenodes.h:918
#define FRAMEOPTION_START_CURRENT_ROW
Definition parsenodes.h:621
#define FKCONSTR_MATCH_SIMPLE
@ RTE_JOIN
@ RTE_CTE
@ RTE_NAMEDTUPLESTORE
@ RTE_VALUES
@ RTE_SUBQUERY
@ RTE_RESULT
@ RTE_FUNCTION
@ RTE_TABLEFUNC
@ RTE_GROUP
@ RTE_GRAPH_TABLE
@ RTE_RELATION
#define FRAMEOPTION_START_OFFSET
Definition parsenodes.h:631
@ PARTITION_RANGE_DATUM_MAXVALUE
Definition parsenodes.h:971
@ PARTITION_RANGE_DATUM_MINVALUE
Definition parsenodes.h:969
@ EDGE_PATTERN_RIGHT
@ VERTEX_PATTERN
@ EDGE_PATTERN_LEFT
@ PAREN_EXPR
@ EDGE_PATTERN_ANY
#define FKCONSTR_MATCH_PARTIAL
#define FRAMEOPTION_END_OFFSET_FOLLOWING
Definition parsenodes.h:626
#define FRAMEOPTION_EXCLUDE_TIES
Definition parsenodes.h:629
#define FRAMEOPTION_RANGE
Definition parsenodes.h:613
#define FRAMEOPTION_EXCLUDE_GROUP
Definition parsenodes.h:628
#define FKCONSTR_ACTION_CASCADE
#define FRAMEOPTION_GROUPS
Definition parsenodes.h:615
#define FRAMEOPTION_BETWEEN
Definition parsenodes.h:616
#define FKCONSTR_ACTION_SETNULL
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition parsenodes.h:620
#define FRAMEOPTION_START_OFFSET_PRECEDING
Definition parsenodes.h:623
#define FRAMEOPTION_START_OFFSET_FOLLOWING
Definition parsenodes.h:625
#define FRAMEOPTION_NONDEFAULT
Definition parsenodes.h:612
#define FKCONSTR_MATCH_FULL
#define FKCONSTR_ACTION_NOACTION
#define FRAMEOPTION_ROWS
Definition parsenodes.h:614
@ CTEMaterializeNever
@ CTEMaterializeAlways
@ CTEMaterializeDefault
#define rt_fetch(rangetable_index, rangetable)
Definition parsetree.h:31
Expr * get_partition_qual_relid(Oid relid)
Definition partcache.c:299
END_CATALOG_STRUCT typedef FormData_pg_aggregate * Form_pg_aggregate
END_CATALOG_STRUCT typedef FormData_pg_am * Form_pg_am
Definition pg_am.h:52
NameData attname
int16 attnum
FormData_pg_attribute * Form_pg_attribute
END_CATALOG_STRUCT typedef FormData_pg_authid * Form_pg_authid
Definition pg_authid.h:60
static char format
NameData relname
Definition pg_class.h:40
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
END_CATALOG_STRUCT typedef FormData_pg_collation * Form_pg_collation
#define NAMEDATALEN
#define FUNC_MAX_ARGS
AttrNumber extractNotNullColumn(HeapTuple constrTup)
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
const void * data
END_CATALOG_STRUCT typedef FormData_pg_depend * Form_pg_depend
Definition pg_depend.h:76
END_CATALOG_STRUCT typedef FormData_pg_index * Form_pg_index
Definition pg_index.h:74
#define lfirst(lc)
Definition pg_list.h:172
#define llast(l)
Definition pg_list.h:198
#define lfirst_node(type, lc)
Definition pg_list.h:176
static int list_length(const List *l)
Definition pg_list.h:152
#define linitial_node(type, l)
Definition pg_list.h:181
#define NIL
Definition pg_list.h:68
#define lsecond_node(type, l)
Definition pg_list.h:186
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:550
#define foreach_current_index(var_or_cell)
Definition pg_list.h:435
#define lfirst_int(lc)
Definition pg_list.h:173
#define lthird(l)
Definition pg_list.h:188
#define list_make1(x1)
Definition pg_list.h:244
#define linitial_int(l)
Definition pg_list.h:179
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
#define for_each_cell(cell, lst, initcell)
Definition pg_list.h:470
#define for_each_from(cell, lst, N)
Definition pg_list.h:446
static void * list_nth(const List *list, int n)
Definition pg_list.h:331
#define lsecond_int(l)
Definition pg_list.h:184
#define linitial(l)
Definition pg_list.h:178
#define lsecond(l)
Definition pg_list.h:183
#define foreach_node(type, var, lst)
Definition pg_list.h:528
#define forfour(cell1, list1, cell2, list2, cell3, list3, cell4, list4)
Definition pg_list.h:607
static ListCell * list_head(const List *l)
Definition pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition pg_list.h:375
#define lfourth(l)
Definition pg_list.h:193
#define linitial_oid(l)
Definition pg_list.h:180
#define forfive(cell1, list1, cell2, list2, cell3, list3, cell4, list4, cell5, list5)
Definition pg_list.h:620
#define lfirst_oid(lc)
Definition pg_list.h:174
#define list_make2(x1, x2)
Definition pg_list.h:246
#define foreach_int(var, lst)
Definition pg_list.h:502
static int list_cell_number(const List *l, const ListCell *c)
Definition pg_list.h:365
#define lthird_node(type, l)
Definition pg_list.h:191
END_CATALOG_STRUCT typedef FormData_pg_opclass * Form_pg_opclass
Definition pg_opclass.h:87
END_CATALOG_STRUCT typedef FormData_pg_operator * Form_pg_operator
Definition pg_operator.h:87
END_CATALOG_STRUCT typedef FormData_pg_partitioned_table * Form_pg_partitioned_table
END_CATALOG_STRUCT typedef FormData_pg_proc * Form_pg_proc
Definition pg_proc.h:140
NameData proname
Definition pg_proc.h:37
END_CATALOG_STRUCT typedef FormData_pg_propgraph_element * Form_pg_propgraph_element
END_CATALOG_STRUCT typedef FormData_pg_propgraph_element_label * Form_pg_propgraph_element_label
END_CATALOG_STRUCT typedef FormData_pg_propgraph_label_property * Form_pg_propgraph_label_property
static size_t noptions
#define plan(x)
Definition pg_regress.c:164
END_CATALOG_STRUCT typedef FormData_pg_statistic_ext * Form_pg_statistic_ext
static char buf[DEFAULT_XLOG_SEG_SIZE]
END_CATALOG_STRUCT typedef FormData_pg_trigger * Form_pg_trigger
Definition pg_trigger.h:84
END_CATALOG_STRUCT typedef FormData_pg_type * Form_pg_type
Definition pg_type.h:265
NameData typname
Definition pg_type.h:43
#define innerPlan(node)
Definition plannodes.h:266
#define outerPlan(node)
Definition plannodes.h:267
#define sprintf
Definition port.h:263
#define snprintf
Definition port.h:261
static bool DatumGetBool(Datum X)
Definition postgres.h:100
static Name DatumGetName(Datum X)
Definition postgres.h:393
static Oid DatumGetObjectId(Datum X)
Definition postgres.h:242
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
static char DatumGetChar(Datum X)
Definition postgres.h:122
static Datum CStringGetDatum(const char *X)
Definition postgres.h:383
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
static int16 DatumGetInt16(Datum X)
Definition postgres.h:162
static int32 DatumGetInt32(Datum X)
Definition postgres.h:202
#define PointerGetDatum(X)
Definition postgres.h:354
#define InvalidOid
unsigned int Oid
e
static int fb(int x)
char string[11]
@ IS_NOT_TRUE
Definition primnodes.h:2016
@ IS_NOT_FALSE
Definition primnodes.h:2016
@ IS_NOT_UNKNOWN
Definition primnodes.h:2016
@ IS_TRUE
Definition primnodes.h:2016
@ IS_UNKNOWN
Definition primnodes.h:2016
@ IS_FALSE
Definition primnodes.h:2016
@ ARRAY_SUBLINK
Definition primnodes.h:1036
@ ANY_SUBLINK
Definition primnodes.h:1032
@ MULTIEXPR_SUBLINK
Definition primnodes.h:1035
@ CTE_SUBLINK
Definition primnodes.h:1037
@ EXPR_SUBLINK
Definition primnodes.h:1034
@ ROWCOMPARE_SUBLINK
Definition primnodes.h:1033
@ ALL_SUBLINK
Definition primnodes.h:1031
@ EXISTS_SUBLINK
Definition primnodes.h:1030
@ JS_FORMAT_JSONB
Definition primnodes.h:1667
@ JS_FORMAT_DEFAULT
Definition primnodes.h:1665
@ JS_FORMAT_JSON
Definition primnodes.h:1666
@ IS_LEAST
Definition primnodes.h:1530
@ IS_GREATEST
Definition primnodes.h:1529
@ TFT_XMLTABLE
Definition primnodes.h:101
@ TFT_JSON_TABLE
Definition primnodes.h:102
BoolExprType
Definition primnodes.h:963
@ AND_EXPR
Definition primnodes.h:964
@ OR_EXPR
Definition primnodes.h:964
@ NOT_EXPR
Definition primnodes.h:964
@ JS_ENC_DEFAULT
Definition primnodes.h:1653
@ JS_ENC_UTF32
Definition primnodes.h:1656
@ JS_ENC_UTF16
Definition primnodes.h:1655
@ XMLOPTION_DOCUMENT
Definition primnodes.h:1619
@ SVFOP_CURRENT_CATALOG
Definition primnodes.h:1576
@ SVFOP_LOCALTIME_N
Definition primnodes.h:1569
@ SVFOP_CURRENT_TIMESTAMP
Definition primnodes.h:1566
@ SVFOP_LOCALTIME
Definition primnodes.h:1568
@ SVFOP_CURRENT_TIMESTAMP_N
Definition primnodes.h:1567
@ SVFOP_CURRENT_ROLE
Definition primnodes.h:1572
@ SVFOP_USER
Definition primnodes.h:1574
@ SVFOP_CURRENT_SCHEMA
Definition primnodes.h:1577
@ SVFOP_LOCALTIMESTAMP_N
Definition primnodes.h:1571
@ SVFOP_CURRENT_DATE
Definition primnodes.h:1563
@ SVFOP_CURRENT_TIME_N
Definition primnodes.h:1565
@ SVFOP_CURRENT_TIME
Definition primnodes.h:1564
@ SVFOP_LOCALTIMESTAMP
Definition primnodes.h:1570
@ SVFOP_CURRENT_USER
Definition primnodes.h:1573
@ SVFOP_SESSION_USER
Definition primnodes.h:1575
@ PARAM_MULTIEXPR
Definition primnodes.h:388
@ PARAM_EXTERN
Definition primnodes.h:385
@ PARAM_EXEC
Definition primnodes.h:386
@ JSW_UNCONDITIONAL
Definition primnodes.h:1793
@ JSW_CONDITIONAL
Definition primnodes.h:1792
@ JSW_UNSPEC
Definition primnodes.h:1790
@ JSW_NONE
Definition primnodes.h:1791
#define PARSER_IGNORE_NULLS
Definition primnodes.h:590
@ IS_DOCUMENT
Definition primnodes.h:1614
@ IS_XMLFOREST
Definition primnodes.h:1609
@ IS_XMLCONCAT
Definition primnodes.h:1607
@ IS_XMLPI
Definition primnodes.h:1611
@ IS_XMLPARSE
Definition primnodes.h:1610
@ IS_XMLSERIALIZE
Definition primnodes.h:1613
@ IS_XMLROOT
Definition primnodes.h:1612
@ IS_XMLELEMENT
Definition primnodes.h:1608
@ VAR_RETURNING_OLD
Definition primnodes.h:258
@ VAR_RETURNING_NEW
Definition primnodes.h:259
@ VAR_RETURNING_DEFAULT
Definition primnodes.h:257
JsonBehaviorType
Definition primnodes.h:1804
@ JSON_BEHAVIOR_DEFAULT
Definition primnodes.h:1813
@ JSON_BEHAVIOR_FALSE
Definition primnodes.h:1809
@ JSON_BEHAVIOR_NULL
Definition primnodes.h:1805
@ JSON_BEHAVIOR_EMPTY_ARRAY
Definition primnodes.h:1811
@ JSON_QUERY_OP
Definition primnodes.h:1843
@ JSON_EXISTS_OP
Definition primnodes.h:1842
@ JSON_VALUE_OP
Definition primnodes.h:1844
CoercionForm
Definition primnodes.h:766
@ COERCE_SQL_SYNTAX
Definition primnodes.h:770
@ COERCE_IMPLICIT_CAST
Definition primnodes.h:769
@ COERCE_EXPLICIT_CAST
Definition primnodes.h:768
@ COERCE_EXPLICIT_CALL
Definition primnodes.h:767
@ OVERRIDING_SYSTEM_VALUE
Definition primnodes.h:31
@ OVERRIDING_USER_VALUE
Definition primnodes.h:30
@ IS_NULL
Definition primnodes.h:1992
@ IS_NOT_NULL
Definition primnodes.h:1992
@ JS_TYPE_ARRAY
Definition primnodes.h:1763
@ JS_TYPE_OBJECT
Definition primnodes.h:1762
@ JS_TYPE_SCALAR
Definition primnodes.h:1764
@ MERGE_WHEN_NOT_MATCHED_BY_TARGET
Definition primnodes.h:2038
@ MERGE_WHEN_NOT_MATCHED_BY_SOURCE
Definition primnodes.h:2037
@ MERGE_WHEN_MATCHED
Definition primnodes.h:2036
#define OUTER_VAR
Definition primnodes.h:244
@ JSCTOR_JSON_SERIALIZE
Definition primnodes.h:1724
@ JSCTOR_JSON_ARRAYAGG
Definition primnodes.h:1721
@ JSCTOR_JSON_PARSE
Definition primnodes.h:1722
@ JSCTOR_JSON_OBJECT
Definition primnodes.h:1717
@ JSCTOR_JSON_SCALAR
Definition primnodes.h:1723
@ JSCTOR_JSON_ARRAY_QUERY
Definition primnodes.h:1719
@ JSCTOR_JSON_ARRAY
Definition primnodes.h:1718
@ JSCTOR_JSON_OBJECTAGG
Definition primnodes.h:1720
#define INNER_VAR
Definition primnodes.h:243
#define INDEX_VAR
Definition primnodes.h:245
tree ctl root
Definition radixtree.h:1857
void * stringToNode(const char *str)
Definition read.c:90
#define RelationGetDescr(relation)
Definition rel.h:542
#define RelationGetRelationName(relation)
Definition rel.h:550
void AcquireRewriteLocks(Query *parsetree, bool forExecute, bool forUpdatePushedDown)
Query * getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
#define ViewSelectRuleName
Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
Definition ruleutils.c:896
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags, int wrapColumn)
Definition ruleutils.c:5901
static void removeStringInfoSpaces(StringInfo str)
Definition ruleutils.c:9701
static bool looks_like_function(Node *node)
Datum pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:2454
static char * get_rtable_name(int rtindex, deparse_context *context)
Definition ruleutils.c:5495
Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
Definition ruleutils.c:727
static int decompile_column_index_array(Datum column_index_array, Oid relId, bool withPeriod, StringInfo buf)
Definition ruleutils.c:2979
static void make_propgraphdef_labels(StringInfo buf, Oid elid, const char *elalias, Oid elrelid)
Definition ruleutils.c:1797
static void set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte, deparse_columns *colinfo)
Definition ruleutils.c:4737
static void appendContextKeyword(deparse_context *context, const char *str, int indentBefore, int indentAfter, int indentPlus)
Definition ruleutils.c:9647
List * deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
Definition ruleutils.c:4115
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition ruleutils.c:1985
Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS)
Definition ruleutils.c:1209
static void set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
Definition ruleutils.c:4572
static Plan * find_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)
Definition ruleutils.c:5595
static text * string_to_text(char *str)
static void get_values_def(List *values_lists, deparse_context *context)
Definition ruleutils.c:6089
#define PRETTYINDENT_LIMIT
Definition ruleutils.c:90
Datum pg_get_viewdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:689
static char * make_colname_unique(char *colname, deparse_namespace *dpns, deparse_columns *colinfo)
Definition ruleutils.c:5285
static void get_json_behavior(JsonBehavior *behavior, deparse_context *context, const char *on)
Definition ruleutils.c:9738
static const char * get_simple_binary_op_name(OpExpr *expr)
Definition ruleutils.c:9389
static void get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context, const char *funcname, bool is_json_objectagg)
Datum pg_get_constraintdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:2504
static void set_deparse_for_query(deparse_namespace *dpns, Query *query, List *parent_namespaces)
Definition ruleutils.c:4391
static void print_function_trftypes(StringInfo buf, HeapTuple proctup)
Definition ruleutils.c:3820
void(* rsv_callback)(Node *node, deparse_context *context, void *callback_arg)
Definition ruleutils.c:330
#define PRETTYINDENT_STD
Definition ruleutils.c:86
static void get_graph_label_expr(Node *label_expr, deparse_context *context)
Definition ruleutils.c:7982
char * quote_qualified_identifier(const char *qualifier, const char *ident)
static void get_setop_query(Node *setOp, Query *query, deparse_context *context)
Definition ruleutils.c:6782
#define PRETTYINDENT_JOIN
Definition ruleutils.c:87
static bool is_input_argument(int nth, const char *argmodes)
Definition ruleutils.c:3808
static void get_query_def(Query *query, StringInfo buf, List *parentnamespace, TupleDesc resultDesc, bool colNamesVisible, int prettyFlags, int wrapColumn, int startIndent)
Definition ruleutils.c:5986
static void get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
Datum pg_get_functiondef(PG_FUNCTION_ARGS)
Definition ruleutils.c:3285
Datum pg_get_function_result(PG_FUNCTION_ARGS)
Definition ruleutils.c:3592
Datum pg_get_indexdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:1189
static void get_sublink_expr(SubLink *sublink, deparse_context *context)
static const char * get_name_for_var_field(Var *var, int fieldno, int levelsup, deparse_context *context)
Definition ruleutils.c:8573
static char * get_lock_clause_strength(LockClauseStrength strength)
Definition ruleutils.c:6378
static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as, deparse_context *context)
#define only_marker(rte)
Definition ruleutils.c:561
Datum pg_get_function_arg_default(PG_FUNCTION_ARGS)
Definition ruleutils.c:3848
static void get_parameter(Param *param, deparse_context *context)
Definition ruleutils.c:9244
static void build_colinfo_names_hash(deparse_columns *colinfo)
Definition ruleutils.c:5340
Datum pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
Definition ruleutils.c:2196
static void get_delete_query_def(Query *query, deparse_context *context)
Definition ruleutils.c:7743
Datum pg_get_ruledef(PG_FUNCTION_ARGS)
Definition ruleutils.c:571
static void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan, deparse_context *context, bool showimplicit)
#define PRETTYFLAG_INDENT
Definition ruleutils.c:94
static void get_column_alias_list(deparse_columns *colinfo, deparse_context *context)
Datum pg_get_statisticsobjdef_columns(PG_FUNCTION_ARGS)
Definition ruleutils.c:1995
Datum pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:1967
static void add_to_names_hash(deparse_columns *colinfo, const char *name)
Definition ruleutils.c:5398
static void simple_quote_literal(StringInfo buf, const char *val)
static bool colname_is_unique(const char *colname, deparse_namespace *dpns, deparse_columns *colinfo)
Definition ruleutils.c:5210
static void get_from_clause_coldeflist(RangeTblFunction *rtfunc, deparse_columns *colinfo, deparse_context *context)
char * pg_get_indexdef_columns_extended(Oid indexrelid, uint16 flags)
Definition ruleutils.c:1260
char * pg_get_partkeydef_columns(Oid relid, bool pretty)
Definition ruleutils.c:2282
static void get_from_clause(Query *query, const char *prefix, deparse_context *context)
List * deparse_context_for(const char *aliasname, Oid relid)
Definition ruleutils.c:4070
static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
Datum pg_get_partkeydef(PG_FUNCTION_ARGS)
Definition ruleutils.c:2267
static char * generate_qualified_relation_name(Oid relid)
static void set_simple_column_names(deparse_namespace *dpns)
Definition ruleutils.c:4460
static void get_json_expr_options(JsonExpr *jsexpr, deparse_context *context, JsonBehaviorType default_behavior)
Definition ruleutils.c:9776
char * pg_get_indexdef_columns(Oid indexrelid, bool pretty)
Definition ruleutils.c:1246
#define PRETTY_INDENT(context)
Definition ruleutils.c:107
#define PRETTYFLAG_PAREN
Definition ruleutils.c:93
static void get_rule_groupingset(GroupingSet *gset, List *targetlist, bool omit_parens, deparse_context *context)
Definition ruleutils.c:6999
static int propdata_by_name_cmp(const ListCell *a, const ListCell *b)
Definition ruleutils.c:1865
static void get_for_portion_of(ForPortionOfExpr *forPortionOf, deparse_context *context)
static char * pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool keysOnly, bool showTblSpc, bool inherits, int prettyFlags, bool missing_ok)
Definition ruleutils.c:1281
static char * pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok)
Definition ruleutils.c:2551
static void get_rule_expr_funccall(Node *node, deparse_context *context, bool showimplicit)
static char * generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes, bool has_variadic, bool *use_variadic_p, bool inGroupBy)
static void expand_colnames_array_to(deparse_columns *colinfo, int n)
Definition ruleutils.c:5324
static void get_returning_clause(Query *query, deparse_context *context)
Definition ruleutils.c:6743
List * set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
Definition ruleutils.c:4187
static SubPlan * find_param_generator(Param *param, deparse_context *context, int *column_p)
Definition ruleutils.c:9126
static void add_cast_to(StringInfo buf, Oid typid)
static void get_special_variable(Node *node, deparse_context *context, void *callback_arg)
Definition ruleutils.c:8444
static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context, const char *funcname, const char *options, bool is_json_objectagg)
static void get_rule_list_toplevel(List *lst, deparse_context *context, bool showimplicit)
static char * pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
Definition ruleutils.c:2012
#define deparse_columns_fetch(rangetable_index, dpns)
Definition ruleutils.c:317
static void get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context, bool showimplicit)
static const char *const query_getrulebyoid
Definition ruleutils.c:339
static void get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
static SubPlan * find_param_generator_initplan(Param *param, Plan *plan, int *column_p)
Definition ruleutils.c:9223
static void pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
Definition ruleutils.c:5693
static void get_window_frame_options(int frameOptions, Node *startOffset, Node *endOffset, deparse_context *context)
Definition ruleutils.c:7206
static void get_rule_expr_paren(Node *node, deparse_context *context, bool showimplicit, Node *parentNode)
Definition ruleutils.c:9720
bool quote_all_identifiers
Definition ruleutils.c:344
static void get_agg_expr(Aggref *aggref, deparse_context *context, Aggref *original_aggref)
static void get_const_collation(Const *constval, deparse_context *context)
static void get_target_list(List *targetList, deparse_context *context)
Definition ruleutils.c:6607
static SPIPlanPtr plan_getrulebyoid
Definition ruleutils.c:338
static void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan, deparse_context *context, bool showimplicit, bool needcomma)
static char * deparse_expression_pretty(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit, int prettyFlags, int startIndent)
Definition ruleutils.c:4034
Datum pg_get_ruledef_ext(PG_FUNCTION_ARGS)
Definition ruleutils.c:589
char * pg_get_indexdef_string(Oid indexrelid)
Definition ruleutils.c:1236
static void get_insert_query_def(Query *query, deparse_context *context)
Definition ruleutils.c:7307
char * pg_get_querydef(Query *query, bool pretty)
Definition ruleutils.c:1599
static void print_function_sqlbody(StringInfo buf, HeapTuple proctup)
Definition ruleutils.c:3918
static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
Definition ruleutils.c:4502
const char * quote_identifier(const char *ident)
static Node * get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno, deparse_context *context)
Definition ruleutils.c:6930
static char * pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
Definition ruleutils.c:800
static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell, deparse_namespace *save_dpns)
Definition ruleutils.c:5672
static SPIPlanPtr plan_getviewrule
Definition ruleutils.c:340
#define WRAP_COLUMN_DEFAULT
Definition ruleutils.c:103
static char * flatten_reloptions(Oid relid)
static text * pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
Definition ruleutils.c:3068
static Node * processIndirection(Node *node, deparse_context *context)
static void get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
void get_reloptions(StringInfo buf, Datum reloptions)
#define PRETTY_PAREN(context)
Definition ruleutils.c:106
Datum pg_get_triggerdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:882
List * select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
Definition ruleutils.c:4217
Datum pg_get_function_sqlbody(PG_FUNCTION_ARGS)
Definition ruleutils.c:3972
static void get_path_pattern_expr_def(List *path_pattern_expr, deparse_context *context)
Definition ruleutils.c:8031
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition ruleutils.c:3033
static char * generate_qualified_type_name(Oid typid)
static void get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
static void get_utility_query_def(Query *query, deparse_context *context)
Definition ruleutils.c:7952
static char * get_relation_name(Oid relid)
Datum pg_get_expr_ext(PG_FUNCTION_ARGS)
Definition ruleutils.c:3050
static int list_oid_str_pair_cmp_by_str(const ListCell *p1, const ListCell *p2)
Definition ruleutils.c:1784
static void get_rule_windowclause(Query *query, deparse_context *context)
Definition ruleutils.c:7117
static void get_rule_windowspec(WindowClause *wc, List *targetList, deparse_context *context)
Definition ruleutils.c:7149
static void get_json_returning(JsonReturning *returning, StringInfo buf, bool json_format_by_default)
Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
Definition ruleutils.c:772
static Node * find_param_referent(Param *param, deparse_context *context, deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
Definition ruleutils.c:9012
static void get_rule_orderby(List *orderList, List *targetList, bool force_colno, deparse_context *context)
Definition ruleutils.c:7059
static void pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
Definition ruleutils.c:5642
char * generate_collation_name(Oid collid)
static void get_graph_pattern_def(GraphPattern *graph_pattern, deparse_context *context)
Definition ruleutils.c:8117
char * pg_get_constraintdef_command(Oid constraintId)
Definition ruleutils.c:2542
char * pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
Definition ruleutils.c:2486
static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte, deparse_columns *colinfo)
Definition ruleutils.c:4940
Datum pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
Definition ruleutils.c:2521
static void make_propgraphdef_properties(StringInfo buf, Oid ellabelid, Oid elrelid)
Definition ruleutils.c:1881
static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces, Bitmapset *rels_used)
Definition ruleutils.c:4246
char * get_window_frame_options_for_explain(int frameOptions, Node *startOffset, Node *endOffset, List *dpcontext, bool forceprefix)
Definition ruleutils.c:7275
static void get_update_query_targetlist_def(Query *query, List *targetList, deparse_context *context, RangeTblEntry *rte)
Definition ruleutils.c:7591
static void get_rule_expr(Node *node, deparse_context *context, bool showimplicit)
Definition ruleutils.c:9817
Datum pg_get_viewdef_ext(PG_FUNCTION_ARGS)
Definition ruleutils.c:708
static char * pg_get_partkeydef_worker(Oid relid, int prettyFlags, bool attrsOnly, bool missing_ok)
Definition ruleutils.c:2295
static void get_oper_expr(OpExpr *expr, deparse_context *context)
Datum pg_get_propgraphdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:1618
Datum pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
Definition ruleutils.c:3567
static char * pg_get_triggerdef_worker(Oid trigid, bool pretty)
Definition ruleutils.c:911
#define GET_PRETTY_FLAGS(pretty)
Definition ruleutils.c:98
static void get_func_expr(FuncExpr *expr, deparse_context *context, bool showimplicit)
static void get_const_expr(Const *constval, deparse_context *context, int showtype)
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition ruleutils.c:4007
void generate_operator_clause(StringInfo buf, const char *leftop, Oid leftoptype, Oid opoid, const char *rightop, Oid rightoptype)
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags)
Definition ruleutils.c:5709
static const char *const query_getviewrule
Definition ruleutils.c:341
static char * pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
Definition ruleutils.c:608
static int print_function_arguments(StringInfo buf, HeapTuple proctup, bool print_table_args, bool print_defaults)
Definition ruleutils.c:3660
static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte, deparse_columns *colinfo)
Definition ruleutils.c:5427
static void print_function_rettype(StringInfo buf, HeapTuple proctup)
Definition ruleutils.c:3622
char * generate_opclass_name(Oid opclass)
static void set_deparse_plan(deparse_namespace *dpns, Plan *plan)
Definition ruleutils.c:5514
static void make_propgraphdef_elements(StringInfo buf, Oid pgrelid, char pgekind)
Definition ruleutils.c:1660
static void get_merge_query_def(Query *query, deparse_context *context)
Definition ruleutils.c:7793
static void resolve_special_varno(Node *node, deparse_context *context, rsv_callback callback, void *callback_arg)
Definition ruleutils.c:8465
static void get_json_format(JsonFormat *format, StringInfo buf)
char * get_range_partbound_string(List *bound_datums)
static void get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
static void get_rule_expr_toplevel(Node *node, deparse_context *context, bool showimplicit)
static RangeTblEntry * get_simple_values_rte(Query *query, TupleDesc resultDesc)
Definition ruleutils.c:6405
static void get_coercion_expr(Node *arg, deparse_context *context, Oid resulttype, int32 resulttypmod, Node *parentNode)
static void push_child_plan(deparse_namespace *dpns, Plan *plan, deparse_namespace *save_dpns)
Definition ruleutils.c:5625
static void get_update_query_def(Query *query, deparse_context *context)
Definition ruleutils.c:7536
static void get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
static void get_basic_select_query(Query *query, deparse_context *context)
Definition ruleutils.c:6474
static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
Definition ruleutils.c:9415
static void get_with_clause(Query *query, deparse_context *context)
Definition ruleutils.c:6132
#define PRETTYINDENT_VAR
Definition ruleutils.c:88
static void destroy_colinfo_names_hash(deparse_columns *colinfo)
Definition ruleutils.c:5411
static char * generate_relation_name(Oid relid, List *namespaces)
static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
static char * get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
Definition ruleutils.c:8162
Datum pg_get_serial_sequence(PG_FUNCTION_ARGS)
Definition ruleutils.c:3191
static char * generate_operator_name(Oid operid, Oid arg1, Oid arg2)
static void get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
Datum pg_get_function_arguments(PG_FUNCTION_ARGS)
Definition ruleutils.c:3541
static void get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
#define PRETTYFLAG_SCHEMA
Definition ruleutils.c:95
static void get_select_query_def(Query *query, deparse_context *context)
Definition ruleutils.c:6271
static void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf)
Datum pg_get_viewdef_name(PG_FUNCTION_ARGS)
Definition ruleutils.c:747
Datum pg_get_userbyid(PG_FUNCTION_ARGS)
Definition ruleutils.c:3153
static void get_agg_expr_helper(Aggref *aggref, deparse_context *context, Aggref *original_aggref, const char *funcname, const char *options, bool is_json_objectagg)
#define RULE_INDEXDEF_PRETTY
Definition ruleutils.h:24
#define RULE_INDEXDEF_KEYS_ONLY
Definition ruleutils.h:25
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
Snapshot GetTransactionSnapshot(void)
Definition snapmgr.c:272
void UnregisterSnapshot(Snapshot snapshot)
Definition snapmgr.c:866
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition snapmgr.c:824
int SPI_fnumber(TupleDesc tupdesc, const char *fname)
Definition spi.c:1176
uint64 SPI_processed
Definition spi.c:45
SPITupleTable * SPI_tuptable
Definition spi.c:46
int SPI_execute_plan(SPIPlanPtr plan, const Datum *Values, const char *Nulls, bool read_only, long tcount)
Definition spi.c:673
int SPI_connect(void)
Definition spi.c:95
int SPI_finish(void)
Definition spi.c:183
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition spi.c:861
int SPI_keepplan(SPIPlanPtr plan)
Definition spi.c:977
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
Definition spi.c:1221
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
Definition spi.c:1253
#define SPI_OK_FINISH
Definition spi.h:83
#define SPI_OK_SELECT
Definition spi.h:86
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:206
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:89
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48
void check_stack_depth(void)
Definition stack_depth.c:95
#define BTEqualStrategyNumber
Definition stratnum.h:31
void resetStringInfo(StringInfo str)
Definition stringinfo.c:126
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
Definition stringinfo.c:281
void appendStringInfoSpaces(StringInfo str, int count)
Definition stringinfo.c:260
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition stringinfo.c:242
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
Oid aggfnoid
Definition primnodes.h:464
List * aggdistinct
Definition primnodes.h:494
List * aggdirectargs
Definition primnodes.h:485
List * args
Definition primnodes.h:488
Expr * aggfilter
Definition primnodes.h:497
List * aggorder
Definition primnodes.h:491
Index parent_relid
Definition pathnodes.h:3301
BoolExprType boolop
Definition primnodes.h:972
List * args
Definition primnodes.h:973
CTEMaterialize ctematerialized
bool attisdropped
Definition tupdesc.h:78
Oid consttype
Definition primnodes.h:330
char * cursor_name
Definition primnodes.h:2138
List * newvals
Definition primnodes.h:1195
Node * quals
Definition primnodes.h:2397
List * fromlist
Definition primnodes.h:2396
Oid funcid
Definition primnodes.h:783
List * args
Definition primnodes.h:801
Node * whereClause
List * path_pattern_list
JsonBehaviorType btype
Definition primnodes.h:1830
JsonBehavior * on_empty
Definition primnodes.h:1879
JsonFormat * format
Definition primnodes.h:1866
Node * path_spec
Definition primnodes.h:1869
JsonWrapper wrapper
Definition primnodes.h:1890
JsonExprOp op
Definition primnodes.h:1857
JsonBehavior * on_error
Definition primnodes.h:1880
bool omit_quotes
Definition primnodes.h:1893
JsonFormatType format_type
Definition primnodes.h:1678
JsonValueType item_type
Definition primnodes.h:1776
JsonFormat * format
Definition primnodes.h:1690
JsonTablePath * path
Definition primnodes.h:1938
JsonTablePlan * child
Definition primnodes.h:1947
Const * value
Definition primnodes.h:1911
JsonTablePlan * rplan
Definition primnodes.h:1968
JsonTablePlan * lplan
Definition primnodes.h:1967
Definition pg_list.h:54
Definition nodes.h:135
Oid opno
Definition primnodes.h:851
List * args
Definition primnodes.h:869
int paramid
Definition primnodes.h:397
ParamKind paramkind
Definition primnodes.h:396
PartitionRangeDatumKind kind
Definition parsenodes.h:978
List * appendRelations
Definition plannodes.h:124
List * subplans
Definition plannodes.h:129
List * rtable
Definition plannodes.h:107
List * rowMarks
Definition parsenodes.h:237
bool groupDistinct
Definition parsenodes.h:220
Node * mergeJoinCondition
Definition parsenodes.h:199
Node * limitCount
Definition parsenodes.h:234
FromExpr * jointree
Definition parsenodes.h:185
List * returningList
Definition parsenodes.h:217
Node * setOperations
Definition parsenodes.h:239
List * cteList
Definition parsenodes.h:176
OnConflictExpr * onConflict
Definition parsenodes.h:206
ForPortionOfExpr * forPortionOf
Definition parsenodes.h:151
List * groupClause
Definition parsenodes.h:219
Node * havingQual
Definition parsenodes.h:225
List * rtable
Definition parsenodes.h:178
Node * limitOffset
Definition parsenodes.h:233
CmdType commandType
Definition parsenodes.h:121
LimitOption limitOption
Definition parsenodes.h:235
Node * utilityStmt
Definition parsenodes.h:141
List * mergeActionList
Definition parsenodes.h:188
List * windowClause
Definition parsenodes.h:227
List * targetList
Definition parsenodes.h:201
List * groupingSets
Definition parsenodes.h:223
bool groupByAll
Definition parsenodes.h:221
List * distinctClause
Definition parsenodes.h:229
List * sortClause
Definition parsenodes.h:231
List * args
Definition primnodes.h:1450
LockClauseStrength strength
LockWaitPolicy waitPolicy
TupleDesc tupdesc
Definition spi.h:25
HeapTuple * vals
Definition spi.h:26
SQLValueFunctionOp op
Definition primnodes.h:1583
SetOperation op
Definition value.h:64
char * plan_name
Definition primnodes.h:1105
List * args
Definition primnodes.h:1125
List * paramIds
Definition primnodes.h:1101
bool isInitPlan
Definition primnodes.h:1112
bool useHashTable
Definition primnodes.h:1113
Node * testexpr
Definition primnodes.h:1100
List * parParam
Definition primnodes.h:1124
List * setParam
Definition primnodes.h:1122
SubLinkType subLinkType
Definition primnodes.h:1098
Expr * refassgnexpr
Definition primnodes.h:736
List * refupperindexpr
Definition primnodes.h:726
List * reflowerindexpr
Definition primnodes.h:732
Node * docexpr
Definition primnodes.h:121
Node * rowexpr
Definition primnodes.h:123
List * colexprs
Definition primnodes.h:133
TableFuncType functype
Definition primnodes.h:115
AttrNumber varattno
Definition primnodes.h:275
int varno
Definition primnodes.h:270
VarReturningType varreturningtype
Definition primnodes.h:298
Index varlevelsup
Definition primnodes.h:295
Node * startOffset
List * partitionClause
Node * endOffset
List * orderClause
List * args
Definition primnodes.h:606
Index winref
Definition primnodes.h:612
Expr * aggfilter
Definition primnodes.h:608
int ignore_nulls
Definition primnodes.h:618
List * args
Definition primnodes.h:1635
bool indent
Definition primnodes.h:1639
List * named_args
Definition primnodes.h:1631
XmlExprOp op
Definition primnodes.h:1627
TupleDesc resultDesc
Definition ruleutils.c:121
StringInfo buf
Definition ruleutils.c:119
List * targetList
Definition ruleutils.c:122
List * namespaces
Definition ruleutils.c:120
List * windowClause
Definition ruleutils.c:123
Bitmapset * appendparents
Definition ruleutils.c:131
Definition c.h:830
Definition c.h:815
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition c.h:822
Definition c.h:776
Definition type.h:90
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
Datum SysCacheGetAttrNotNull(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition syscache.c:626
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
TargetEntry * get_sortgroupref_tle(Index sortref, List *targetList)
Definition tlist.c:354
int count_nonjunk_tlist_entries(List *tlist)
Definition tlist.c:195
#define ReleaseTupleDesc(tupdesc)
Definition tupdesc.h:240
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:195
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition typcache.c:1947
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition typcache.c:389
#define TYPECACHE_GT_OPR
Definition typcache.h:140
#define TYPECACHE_LT_OPR
Definition typcache.h:139
String * makeString(char *str)
Definition value.c:63
#define strVal(v)
Definition value.h:82
Node * flatten_group_exprs(PlannerInfo *root, Query *query, Node *node)
Definition var.c:999
Relids pull_varnos(PlannerInfo *root, Node *node)
Definition var.c:114
static char * VARDATA_ANY(const void *PTR)
Definition varatt.h:486
text * cstring_to_text_with_len(const char *s, int len)
Definition varlena.c:196
bool SplitGUCList(char *rawstring, char separator, List **namelist)
Definition varlena.c:3063
text * cstring_to_text(const char *s)
Definition varlena.c:184
char * text_to_cstring(const text *t)
Definition varlena.c:217
List * textToQualifiedNameList(text *textval)
Definition varlena.c:2722
const char * type
const char * name
char * map_xml_name_to_sql_identifier(const char *name)
Definition xml.c:2478
@ XML_STANDALONE_NO_VALUE
Definition xml.h:29
@ XML_STANDALONE_YES
Definition xml.h:27
@ XML_STANDALONE_NO
Definition xml.h:28
static void convert(const int_fast32_t val, char *const buf)
Definition zic.c:1994

◆ GET_PRETTY_FLAGS

#define GET_PRETTY_FLAGS (   pretty)

◆ only_marker

#define only_marker (   rte)    ((rte)->inh ? "" : "ONLY ")

Definition at line 561 of file ruleutils.c.

◆ PRETTY_INDENT

#define PRETTY_INDENT (   context)    ((context)->prettyFlags & PRETTYFLAG_INDENT)

Definition at line 107 of file ruleutils.c.

◆ PRETTY_PAREN

#define PRETTY_PAREN (   context)    ((context)->prettyFlags & PRETTYFLAG_PAREN)

Definition at line 106 of file ruleutils.c.

◆ PRETTY_SCHEMA

#define PRETTY_SCHEMA (   context)    ((context)->prettyFlags & PRETTYFLAG_SCHEMA)

Definition at line 108 of file ruleutils.c.

◆ PRETTYFLAG_INDENT

#define PRETTYFLAG_INDENT   0x0002

Definition at line 94 of file ruleutils.c.

◆ PRETTYFLAG_PAREN

#define PRETTYFLAG_PAREN   0x0001

Definition at line 93 of file ruleutils.c.

◆ PRETTYFLAG_SCHEMA

#define PRETTYFLAG_SCHEMA   0x0004

Definition at line 95 of file ruleutils.c.

◆ PRETTYINDENT_JOIN

#define PRETTYINDENT_JOIN   4

Definition at line 87 of file ruleutils.c.

◆ PRETTYINDENT_LIMIT

#define PRETTYINDENT_LIMIT   40 /* wrap limit */

Definition at line 90 of file ruleutils.c.

◆ PRETTYINDENT_STD

#define PRETTYINDENT_STD   8

Definition at line 86 of file ruleutils.c.

◆ PRETTYINDENT_VAR

#define PRETTYINDENT_VAR   4

Definition at line 88 of file ruleutils.c.

◆ WRAP_COLUMN_DEFAULT

#define WRAP_COLUMN_DEFAULT   0

Definition at line 103 of file ruleutils.c.

Typedef Documentation

◆ rsv_callback

typedef void(* rsv_callback) (Node *node, deparse_context *context, void *callback_arg)

Definition at line 330 of file ruleutils.c.

Function Documentation

◆ add_cast_to()

static void add_cast_to ( StringInfo  buf,
Oid  typid 
)
static

Definition at line 14135 of file ruleutils.c.

14136{
14139 char *typname;
14140 char *nspname;
14141
14144 elog(ERROR, "cache lookup failed for type %u", typid);
14146
14147 typname = NameStr(typform->typname);
14148 nspname = get_namespace_name_or_temp(typform->typnamespace);
14149
14150 appendStringInfo(buf, "::%s.%s",
14152
14154}

References appendStringInfo(), buf, elog, ERROR, fb(), Form_pg_type, get_namespace_name_or_temp(), GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), quote_identifier(), ReleaseSysCache(), SearchSysCache1(), and typname.

Referenced by generate_operator_clause().

◆ add_to_names_hash()

static void add_to_names_hash ( deparse_columns colinfo,
const char name 
)
static

Definition at line 5398 of file ruleutils.c.

5399{
5400 if (colinfo->names_hash)
5401 (void) hash_search(colinfo->names_hash,
5402 name,
5403 HASH_ENTER,
5404 NULL);
5405}

References fb(), HASH_ENTER, hash_search(), and name.

Referenced by build_colinfo_names_hash(), set_join_column_names(), and set_relation_column_names().

◆ appendContextKeyword()

static void appendContextKeyword ( deparse_context context,
const char str,
int  indentBefore,
int  indentAfter,
int  indentPlus 
)
static

Definition at line 9647 of file ruleutils.c.

9649{
9650 StringInfo buf = context->buf;
9651
9652 if (PRETTY_INDENT(context))
9653 {
9654 int indentAmount;
9655
9656 context->indentLevel += indentBefore;
9657
9658 /* remove any trailing spaces currently in the buffer ... */
9660 /* ... then add a newline and some spaces */
9662
9663 if (context->indentLevel < PRETTYINDENT_LIMIT)
9664 indentAmount = Max(context->indentLevel, 0) + indentPlus;
9665 else
9666 {
9667 /*
9668 * If we're indented more than PRETTYINDENT_LIMIT characters, try
9669 * to conserve horizontal space by reducing the per-level
9670 * indentation. For best results the scale factor here should
9671 * divide all the indent amounts that get added to indentLevel
9672 * (PRETTYINDENT_STD, etc). It's important that the indentation
9673 * not grow unboundedly, else deeply-nested trees use O(N^2)
9674 * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
9675 */
9677 (context->indentLevel - PRETTYINDENT_LIMIT) /
9678 (PRETTYINDENT_STD / 2);
9680 /* scale/wrap logic affects indentLevel, but not indentPlus */
9682 }
9684
9686
9687 context->indentLevel += indentAfter;
9688 if (context->indentLevel < 0)
9689 context->indentLevel = 0;
9690 }
9691 else
9693}

References appendStringInfoChar(), appendStringInfoSpaces(), appendStringInfoString(), deparse_context::buf, buf, fb(), deparse_context::indentLevel, Max, PRETTY_INDENT, PRETTYINDENT_LIMIT, PRETTYINDENT_STD, removeStringInfoSpaces(), and str.

Referenced by get_basic_select_query(), get_delete_query_def(), get_from_clause(), get_from_clause_item(), get_insert_query_def(), get_json_table(), get_json_table_columns(), get_json_table_nested_columns(), get_merge_query_def(), get_returning_clause(), get_rule_expr(), get_rule_windowclause(), get_select_query_def(), get_setop_query(), get_target_list(), get_update_query_def(), get_utility_query_def(), and get_with_clause().

◆ build_colinfo_names_hash()

static void build_colinfo_names_hash ( deparse_columns colinfo)
static

Definition at line 5340 of file ruleutils.c.

5341{
5343 int i;
5344 ListCell *lc;
5345
5346 /*
5347 * Use a hash table only for RTEs with at least 32 columns. (The cutoff
5348 * is somewhat arbitrary, but let's choose it so that this code does get
5349 * exercised in the regression tests.)
5350 */
5351 if (colinfo->num_cols < 32)
5352 return;
5353
5354 /*
5355 * Set up the hash table. The entries are just strings with no other
5356 * payload.
5357 */
5358 hash_ctl.keysize = NAMEDATALEN;
5359 hash_ctl.entrysize = NAMEDATALEN;
5361 colinfo->names_hash = hash_create("deparse_columns names",
5362 colinfo->num_cols + colinfo->num_new_cols,
5363 &hash_ctl,
5365
5366 /*
5367 * Preload the hash table with any names already present (these would have
5368 * come from set_using_names).
5369 */
5370 for (i = 0; i < colinfo->num_cols; i++)
5371 {
5372 char *oldname = colinfo->colnames[i];
5373
5374 if (oldname)
5376 }
5377
5378 for (i = 0; i < colinfo->num_new_cols; i++)
5379 {
5380 char *oldname = colinfo->new_colnames[i];
5381
5382 if (oldname)
5384 }
5385
5386 foreach(lc, colinfo->parentUsing)
5387 {
5388 char *oldname = (char *) lfirst(lc);
5389
5391 }
5392}

References add_to_names_hash(), CurrentMemoryContext, fb(), HASH_CONTEXT, hash_create(), HASH_ELEM, HASH_STRINGS, i, lfirst, and NAMEDATALEN.

Referenced by set_join_column_names(), and set_relation_column_names().

◆ colname_is_unique()

static bool colname_is_unique ( const char colname,
deparse_namespace dpns,
deparse_columns colinfo 
)
static

Definition at line 5210 of file ruleutils.c.

5212{
5213 int i;
5214 ListCell *lc;
5215
5216 /*
5217 * If we have a hash table, consult that instead of linearly scanning the
5218 * colinfo's strings.
5219 */
5220 if (colinfo->names_hash)
5221 {
5222 if (hash_search(colinfo->names_hash,
5223 colname,
5224 HASH_FIND,
5225 NULL) != NULL)
5226 return false;
5227 }
5228 else
5229 {
5230 /* Check against already-assigned column aliases within RTE */
5231 for (i = 0; i < colinfo->num_cols; i++)
5232 {
5233 char *oldname = colinfo->colnames[i];
5234
5235 if (oldname && strcmp(oldname, colname) == 0)
5236 return false;
5237 }
5238
5239 /*
5240 * If we're building a new_colnames array, check that too (this will
5241 * be partially but not completely redundant with the previous checks)
5242 */
5243 for (i = 0; i < colinfo->num_new_cols; i++)
5244 {
5245 char *oldname = colinfo->new_colnames[i];
5246
5247 if (oldname && strcmp(oldname, colname) == 0)
5248 return false;
5249 }
5250
5251 /*
5252 * Also check against names already assigned for parent-join USING
5253 * cols
5254 */
5255 foreach(lc, colinfo->parentUsing)
5256 {
5257 char *oldname = (char *) lfirst(lc);
5258
5259 if (strcmp(oldname, colname) == 0)
5260 return false;
5261 }
5262 }
5263
5264 /*
5265 * Also check against USING-column names that must be globally unique.
5266 * These are not hashed, but there should be few of them.
5267 */
5268 foreach(lc, dpns->using_names)
5269 {
5270 char *oldname = (char *) lfirst(lc);
5271
5272 if (strcmp(oldname, colname) == 0)
5273 return false;
5274 }
5275
5276 return true;
5277}

References fb(), HASH_FIND, hash_search(), i, and lfirst.

Referenced by make_colname_unique().

◆ decompile_column_index_array()

static int decompile_column_index_array ( Datum  column_index_array,
Oid  relId,
bool  withPeriod,
StringInfo  buf 
)
static

Definition at line 2979 of file ruleutils.c.

2981{
2982 Datum *keys;
2983 int nKeys;
2984 int j;
2985
2986 /* Extract data from array of int16 */
2988 &keys, NULL, &nKeys);
2989
2990 for (j = 0; j < nKeys; j++)
2991 {
2992 char *colName;
2993
2994 colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2995
2996 if (j == 0)
2998 else
2999 appendStringInfo(buf, ", %s%s",
3000 (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
3002 }
3003
3004 return nKeys;
3005}

References appendStringInfo(), appendStringInfoString(), buf, DatumGetArrayTypeP, DatumGetInt16(), deconstruct_array_builtin(), fb(), get_attname(), j, and quote_identifier().

Referenced by make_propgraphdef_elements(), and pg_get_constraintdef_worker().

◆ deparse_context_for()

List * deparse_context_for ( const char aliasname,
Oid  relid 
)

Definition at line 4070 of file ruleutils.c.

4071{
4074
4076
4077 /* Build a minimal RTE for the rel */
4079 rte->rtekind = RTE_RELATION;
4080 rte->relid = relid;
4081 rte->relkind = RELKIND_RELATION; /* no need for exactness here */
4082 rte->rellockmode = AccessShareLock;
4083 rte->alias = makeAlias(aliasname, NIL);
4084 rte->eref = rte->alias;
4085 rte->lateral = false;
4086 rte->inh = false;
4087 rte->inFromCl = true;
4088
4089 /* Build one-element rtable */
4090 dpns->rtable = list_make1(rte);
4091 dpns->subplans = NIL;
4092 dpns->ctes = NIL;
4093 dpns->appendrels = NULL;
4096
4097 /* Return a one-deep namespace stack */
4098 return list_make1(dpns);
4099}

References AccessShareLock, fb(), list_make1, makeAlias(), makeNode, NIL, palloc0_object, RTE_RELATION, set_rtable_names(), and set_simple_column_names().

Referenced by check_element_properties(), make_propgraphdef_properties(), pg_get_constraintdef_worker(), pg_get_expr_worker(), pg_get_indexdef_worker(), pg_get_partconstrdef_string(), pg_get_partition_constraintdef(), pg_get_partkeydef_worker(), pg_get_statisticsobj_worker(), pg_get_statisticsobjdef_expressions(), transformPartitionBound(), and transformPartitionRangeBounds().

◆ deparse_context_for_plan_tree()

List * deparse_context_for_plan_tree ( PlannedStmt pstmt,
List rtable_names 
)

Definition at line 4115 of file ruleutils.c.

4116{
4118
4120
4121 /* Initialize fields that stay the same across the whole plan tree */
4122 dpns->rtable = pstmt->rtable;
4123 dpns->rtable_names = rtable_names;
4124 dpns->subplans = pstmt->subplans;
4125 dpns->ctes = NIL;
4126 if (pstmt->appendRelations)
4127 {
4128 /* Set up the array, indexed by child relid */
4129 int ntables = list_length(dpns->rtable);
4130 ListCell *lc;
4131
4132 dpns->appendrels = (AppendRelInfo **)
4133 palloc0((ntables + 1) * sizeof(AppendRelInfo *));
4134 foreach(lc, pstmt->appendRelations)
4135 {
4137 Index crelid = appinfo->child_relid;
4138
4139 Assert(crelid > 0 && crelid <= ntables);
4140 Assert(dpns->appendrels[crelid] == NULL);
4141 dpns->appendrels[crelid] = appinfo;
4142 }
4143 }
4144 else
4145 dpns->appendrels = NULL; /* don't need it */
4146
4147 /*
4148 * Set up column name aliases, ignoring any join RTEs; they don't matter
4149 * because plan trees don't contain any join alias Vars.
4150 */
4152
4153 /* Return a one-deep namespace stack */
4154 return list_make1(dpns);
4155}

References PlannedStmt::appendRelations, Assert, fb(), lfirst_node, list_length(), list_make1, NIL, palloc0(), palloc0_object, PlannedStmt::rtable, set_simple_column_names(), and PlannedStmt::subplans.

Referenced by ExplainPrintPlan().

◆ deparse_expression()

◆ deparse_expression_pretty()

static char * deparse_expression_pretty ( Node expr,
List dpcontext,
bool  forceprefix,
bool  showimplicit,
int  prettyFlags,
int  startIndent 
)
static

◆ destroy_colinfo_names_hash()

static void destroy_colinfo_names_hash ( deparse_columns colinfo)
static

Definition at line 5411 of file ruleutils.c.

5412{
5413 if (colinfo->names_hash)
5414 {
5415 hash_destroy(colinfo->names_hash);
5416 colinfo->names_hash = NULL;
5417 }
5418}

References fb(), and hash_destroy().

Referenced by set_join_column_names(), and set_relation_column_names().

◆ expand_colnames_array_to()

static void expand_colnames_array_to ( deparse_columns colinfo,
int  n 
)
static

Definition at line 5324 of file ruleutils.c.

5325{
5326 if (n > colinfo->num_cols)
5327 {
5328 if (colinfo->colnames == NULL)
5329 colinfo->colnames = palloc0_array(char *, n);
5330 else
5331 colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
5332 colinfo->num_cols = n;
5333 }
5334}

References fb(), palloc0_array, and repalloc0_array.

Referenced by set_join_column_names(), set_relation_column_names(), and set_using_names().

◆ find_param_generator()

static SubPlan * find_param_generator ( Param param,
deparse_context context,
int column_p 
)
static

Definition at line 9126 of file ruleutils.c.

9127{
9128 /* Initialize output parameter to prevent compiler warnings */
9129 *column_p = 0;
9130
9131 /*
9132 * If it's a PARAM_EXEC parameter, search the current plan node as well as
9133 * ancestor nodes looking for a subplan or initplan that emits the value
9134 * for the Param. It could appear in the setParams of an initplan or
9135 * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
9136 */
9137 if (param->paramkind == PARAM_EXEC)
9138 {
9139 SubPlan *result;
9141 ListCell *lc;
9142
9143 dpns = (deparse_namespace *) linitial(context->namespaces);
9144
9145 /* First check the innermost plan node's initplans */
9147 if (result)
9148 return result;
9149
9150 /*
9151 * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
9152 * which can be referenced by Params elsewhere in the targetlist.
9153 * (Such Params should always be in the same targetlist, so there's no
9154 * need to do this work at upper plan nodes.)
9155 */
9156 foreach_node(TargetEntry, tle, dpns->plan->targetlist)
9157 {
9158 if (tle->expr && IsA(tle->expr, SubPlan))
9159 {
9160 SubPlan *subplan = (SubPlan *) tle->expr;
9161
9162 if (subplan->subLinkType == MULTIEXPR_SUBLINK)
9163 {
9164 foreach_int(paramid, subplan->setParam)
9165 {
9166 if (paramid == param->paramid)
9167 {
9168 /* Found a match, so return it. */
9169 *column_p = foreach_current_index(paramid);
9170 return subplan;
9171 }
9172 }
9173 }
9174 }
9175 }
9176
9177 /* No luck, so check the ancestor nodes */
9178 foreach(lc, dpns->ancestors)
9179 {
9180 Node *ancestor = (Node *) lfirst(lc);
9181
9182 /*
9183 * If ancestor is a SubPlan, check the paramIds it provides.
9184 */
9185 if (IsA(ancestor, SubPlan))
9186 {
9187 SubPlan *subplan = (SubPlan *) ancestor;
9188
9189 foreach_int(paramid, subplan->paramIds)
9190 {
9191 if (paramid == param->paramid)
9192 {
9193 /* Found a match, so return it. */
9194 *column_p = foreach_current_index(paramid);
9195 return subplan;
9196 }
9197 }
9198
9199 /* SubPlan isn't a kind of Plan, so skip the rest */
9200 continue;
9201 }
9202
9203 /*
9204 * Otherwise, it's some kind of Plan node, so check its initplans.
9205 */
9207 column_p);
9208 if (result)
9209 return result;
9210
9211 /* No luck, crawl up to next ancestor */
9212 }
9213 }
9214
9215 /* No generator found */
9216 return NULL;
9217}

References fb(), find_param_generator_initplan(), foreach_current_index, foreach_int, foreach_node, IsA, lfirst, linitial, MULTIEXPR_SUBLINK, deparse_context::namespaces, PARAM_EXEC, Param::paramid, SubPlan::paramIds, Param::paramkind, result, SubPlan::setParam, and SubPlan::subLinkType.

Referenced by get_parameter().

◆ find_param_generator_initplan()

static SubPlan * find_param_generator_initplan ( Param param,
Plan plan,
int column_p 
)
static

Definition at line 9223 of file ruleutils.c.

9224{
9225 foreach_node(SubPlan, subplan, plan->initPlan)
9226 {
9227 foreach_int(paramid, subplan->setParam)
9228 {
9229 if (paramid == param->paramid)
9230 {
9231 /* Found a match, so return it. */
9232 *column_p = foreach_current_index(paramid);
9233 return subplan;
9234 }
9235 }
9236 }
9237 return NULL;
9238}

References fb(), foreach_current_index, foreach_int, foreach_node, Param::paramid, and plan.

Referenced by find_param_generator().

◆ find_param_referent()

static Node * find_param_referent ( Param param,
deparse_context context,
deparse_namespace **  dpns_p,
ListCell **  ancestor_cell_p 
)
static

Definition at line 9012 of file ruleutils.c.

9014{
9015 /* Initialize output parameters to prevent compiler warnings */
9016 *dpns_p = NULL;
9018
9019 /*
9020 * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
9021 * SubPlan argument. This will necessarily be in some ancestor of the
9022 * current expression's Plan node.
9023 */
9024 if (param->paramkind == PARAM_EXEC)
9025 {
9028 ListCell *lc;
9029
9030 dpns = (deparse_namespace *) linitial(context->namespaces);
9031 child_plan = dpns->plan;
9032
9033 foreach(lc, dpns->ancestors)
9034 {
9035 Node *ancestor = (Node *) lfirst(lc);
9036 ListCell *lc2;
9037
9038 /*
9039 * NestLoops transmit params to their inner child only.
9040 */
9041 if (IsA(ancestor, NestLoop) &&
9043 {
9045
9046 foreach(lc2, nl->nestParams)
9047 {
9049
9050 if (nlp->paramno == param->paramid)
9051 {
9052 /* Found a match, so return it */
9053 *dpns_p = dpns;
9055 return (Node *) nlp->paramval;
9056 }
9057 }
9058 }
9059
9060 /*
9061 * If ancestor is a SubPlan, check the arguments it provides.
9062 */
9063 if (IsA(ancestor, SubPlan))
9064 {
9065 SubPlan *subplan = (SubPlan *) ancestor;
9066 ListCell *lc3;
9067 ListCell *lc4;
9068
9069 forboth(lc3, subplan->parParam, lc4, subplan->args)
9070 {
9071 int paramid = lfirst_int(lc3);
9072 Node *arg = (Node *) lfirst(lc4);
9073
9074 if (paramid == param->paramid)
9075 {
9076 /*
9077 * Found a match, so return it. But, since Vars in
9078 * the arg are to be evaluated in the surrounding
9079 * context, we have to point to the next ancestor item
9080 * that is *not* a SubPlan.
9081 */
9082 ListCell *rest;
9083
9084 for_each_cell(rest, dpns->ancestors,
9085 lnext(dpns->ancestors, lc))
9086 {
9087 Node *ancestor2 = (Node *) lfirst(rest);
9088
9089 if (!IsA(ancestor2, SubPlan))
9090 {
9091 *dpns_p = dpns;
9093 return arg;
9094 }
9095 }
9096 elog(ERROR, "SubPlan cannot be outermost ancestor");
9097 }
9098 }
9099
9100 /* SubPlan isn't a kind of Plan, so skip the rest */
9101 continue;
9102 }
9103
9104 /*
9105 * We need not consider the ancestor's initPlan list, since
9106 * initplans never have any parParams.
9107 */
9108
9109 /* No luck, crawl up to next ancestor */
9110 child_plan = (Plan *) ancestor;
9111 }
9112 }
9113
9114 /* No referent found */
9115 return NULL;
9116}

References arg, SubPlan::args, elog, ERROR, fb(), for_each_cell, forboth, innerPlan, IsA, lfirst, lfirst_int, linitial, lnext(), deparse_context::namespaces, PARAM_EXEC, Param::paramid, Param::paramkind, NestLoopParam::paramval, and SubPlan::parParam.

Referenced by get_name_for_var_field(), and get_parameter().

◆ find_recursive_union()

static Plan * find_recursive_union ( deparse_namespace dpns,
WorkTableScan wtscan 
)
static

Definition at line 5595 of file ruleutils.c.

5596{
5597 ListCell *lc;
5598
5599 foreach(lc, dpns->ancestors)
5600 {
5601 Plan *ancestor = (Plan *) lfirst(lc);
5602
5603 if (IsA(ancestor, RecursiveUnion) &&
5604 ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5605 return ancestor;
5606 }
5607 elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5608 wtscan->wtParam);
5609 return NULL;
5610}

References elog, ERROR, fb(), IsA, and lfirst.

Referenced by set_deparse_plan().

◆ flatten_reloptions()

static char * flatten_reloptions ( Oid  relid)
static

Definition at line 14299 of file ruleutils.c.

14300{
14301 char *result = NULL;
14302 HeapTuple tuple;
14303 Datum reloptions;
14304 bool isnull;
14305
14306 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14307 if (!HeapTupleIsValid(tuple))
14308 elog(ERROR, "cache lookup failed for relation %u", relid);
14309
14310 reloptions = SysCacheGetAttr(RELOID, tuple,
14311 Anum_pg_class_reloptions, &isnull);
14312 if (!isnull)
14313 {
14315
14317 get_reloptions(&buf, reloptions);
14318
14319 result = buf.data;
14320 }
14321
14322 ReleaseSysCache(tuple);
14323
14324 return result;
14325}

References buf, elog, ERROR, fb(), get_reloptions(), HeapTupleIsValid, initStringInfo(), ObjectIdGetDatum(), ReleaseSysCache(), result, SearchSysCache1(), and SysCacheGetAttr().

Referenced by pg_get_constraintdef_worker(), and pg_get_indexdef_worker().

◆ generate_collation_name()

char * generate_collation_name ( Oid  collid)

Definition at line 14199 of file ruleutils.c.

14200{
14201 HeapTuple tp;
14203 char *collname;
14204 char *nspname;
14205 char *result;
14206
14208 if (!HeapTupleIsValid(tp))
14209 elog(ERROR, "cache lookup failed for collation %u", collid);
14211 collname = NameStr(colltup->collname);
14212
14214 nspname = get_namespace_name_or_temp(colltup->collnamespace);
14215 else
14216 nspname = NULL;
14217
14218 result = quote_qualified_identifier(nspname, collname);
14219
14220 ReleaseSysCache(tp);
14221
14222 return result;
14223}

References CollationIsVisible(), collid, elog, ERROR, fb(), Form_pg_collation, get_namespace_name_or_temp(), GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), quote_qualified_identifier(), ReleaseSysCache(), result, and SearchSysCache1().

Referenced by get_const_collation(), get_from_clause_coldeflist(), get_rule_expr(), pg_collation_for(), pg_get_indexdef_worker(), and pg_get_partkeydef_worker().

◆ generate_function_name()

static char * generate_function_name ( Oid  funcid,
int  nargs,
List argnames,
Oid argtypes,
bool  has_variadic,
bool use_variadic_p,
bool  inGroupBy 
)
static

Definition at line 13911 of file ruleutils.c.

13914{
13915 char *result;
13918 char *proname;
13919 bool use_variadic;
13920 char *nspname;
13922 int fgc_flags;
13923 Oid p_funcid;
13924 Oid p_rettype;
13925 bool p_retset;
13926 int p_nvargs;
13927 Oid p_vatype;
13929 bool force_qualify = false;
13930
13933 elog(ERROR, "cache lookup failed for function %u", funcid);
13935 proname = NameStr(procform->proname);
13936
13937 /*
13938 * Due to parser hacks to avoid needing to reserve CUBE, we need to force
13939 * qualification of some function names within GROUP BY.
13940 */
13941 if (inGroupBy)
13942 {
13943 if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
13944 force_qualify = true;
13945 }
13946
13947 /*
13948 * Determine whether VARIADIC should be printed. We must do this first
13949 * since it affects the lookup rules in func_get_detail().
13950 *
13951 * We always print VARIADIC if the function has a merged variadic-array
13952 * argument. Note that this is always the case for functions taking a
13953 * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
13954 * and printed the array elements as separate arguments, the call could
13955 * match a newer non-VARIADIC function.
13956 */
13957 if (use_variadic_p)
13958 {
13959 /* Parser should not have set funcvariadic unless fn is variadic */
13960 Assert(!has_variadic || OidIsValid(procform->provariadic));
13963 }
13964 else
13965 {
13967 use_variadic = false;
13968 }
13969
13970 /*
13971 * The idea here is to schema-qualify only if the parser would fail to
13972 * resolve the correct function given the unqualified func name with the
13973 * specified argtypes and VARIADIC flag. But if we already decided to
13974 * force qualification, then we can skip the lookup and pretend we didn't
13975 * find it.
13976 */
13977 if (!force_qualify)
13979 NIL, argnames, nargs, argtypes,
13980 !use_variadic, true, false,
13981 &fgc_flags,
13985 else
13986 {
13989 }
13990
13991 if ((p_result == FUNCDETAIL_NORMAL ||
13994 p_funcid == funcid)
13995 nspname = NULL;
13996 else
13997 nspname = get_namespace_name_or_temp(procform->pronamespace);
13998
14000
14002
14003 return result;
14004}

References Assert, elog, ERROR, fb(), Form_pg_proc, func_get_detail(), FUNCDETAIL_AGGREGATE, FUNCDETAIL_NORMAL, FUNCDETAIL_NOTFOUND, FUNCDETAIL_WINDOWFUNC, get_namespace_name_or_temp(), GETSTRUCT(), HeapTupleIsValid, InvalidOid, list_make1, makeString(), NameStr, NIL, ObjectIdGetDatum(), OidIsValid, proname, quote_qualified_identifier(), ReleaseSysCache(), result, and SearchSysCache1().

Referenced by get_agg_expr_helper(), get_func_expr(), get_tablesample_def(), get_windowfunc_expr_helper(), pg_get_functiondef(), and pg_get_triggerdef_worker().

◆ generate_opclass_name()

char * generate_opclass_name ( Oid  opclass)

Definition at line 13561 of file ruleutils.c.

13562{
13564
13566 get_opclass_name(opclass, InvalidOid, &buf);
13567
13568 return &buf.data[1]; /* get_opclass_name() prepends space */
13569}

References buf, get_opclass_name(), initStringInfo(), and InvalidOid.

Referenced by index_opclass_options().

◆ generate_operator_clause()

void generate_operator_clause ( StringInfo  buf,
const char leftop,
Oid  leftoptype,
Oid  opoid,
const char rightop,
Oid  rightoptype 
)

Definition at line 14095 of file ruleutils.c.

14099{
14102 char *oprname;
14103 char *nspname;
14104
14107 elog(ERROR, "cache lookup failed for operator %u", opoid);
14109 Assert(operform->oprkind == 'b');
14110 oprname = NameStr(operform->oprname);
14111
14112 nspname = get_namespace_name(operform->oprnamespace);
14113
14115 if (leftoptype != operform->oprleft)
14116 add_cast_to(buf, operform->oprleft);
14117 appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
14118 appendStringInfoString(buf, oprname);
14119 appendStringInfo(buf, ") %s", rightop);
14120 if (rightoptype != operform->oprright)
14121 add_cast_to(buf, operform->oprright);
14122
14124}

References add_cast_to(), appendStringInfo(), appendStringInfoString(), Assert, buf, elog, ERROR, fb(), Form_pg_operator, get_namespace_name(), GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), quote_identifier(), ReleaseSysCache(), and SearchSysCache1().

Referenced by refresh_by_match_merge(), and ri_GenerateQual().

◆ generate_operator_name()

static char * generate_operator_name ( Oid  operid,
Oid  arg1,
Oid  arg2 
)
static

Definition at line 14018 of file ruleutils.c.

14019{
14023 char *oprname;
14024 char *nspname;
14026
14028
14031 elog(ERROR, "cache lookup failed for operator %u", operid);
14033 oprname = NameStr(operform->oprname);
14034
14035 /*
14036 * The idea here is to schema-qualify only if the parser would fail to
14037 * resolve the correct operator given the unqualified op name with the
14038 * specified argtypes.
14039 */
14040 switch (operform->oprkind)
14041 {
14042 case 'b':
14044 true, -1);
14045 break;
14046 case 'l':
14048 true, -1);
14049 break;
14050 default:
14051 elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
14052 p_result = NULL; /* keep compiler quiet */
14053 break;
14054 }
14055
14056 if (p_result != NULL && oprid(p_result) == operid)
14057 nspname = NULL;
14058 else
14059 {
14060 nspname = get_namespace_name_or_temp(operform->oprnamespace);
14061 appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
14062 }
14063
14064 appendStringInfoString(&buf, oprname);
14065
14066 if (nspname)
14068
14069 if (p_result != NULL)
14071
14073
14074 return buf.data;
14075}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), buf, elog, ERROR, fb(), Form_pg_operator, get_namespace_name_or_temp(), GETSTRUCT(), HeapTupleIsValid, initStringInfo(), left_oper(), list_make1, makeString(), NameStr, ObjectIdGetDatum(), oper(), oprid(), quote_identifier(), ReleaseSysCache(), and SearchSysCache1().

Referenced by get_oper_expr(), get_rule_expr(), get_rule_orderby(), get_simple_binary_op_name(), get_sublink_expr(), and pg_get_indexdef_worker().

◆ generate_qualified_relation_name()

static char * generate_qualified_relation_name ( Oid  relid)
static

Definition at line 13875 of file ruleutils.c.

13876{
13877 HeapTuple tp;
13879 char *result;
13880
13882 if (!HeapTupleIsValid(tp))
13883 elog(ERROR, "cache lookup failed for relation %u", relid);
13885
13886 result = get_qualified_objname(reltup->relnamespace,
13887 NameStr(reltup->relname));
13888 ReleaseSysCache(tp);
13889
13890 return result;
13891}

References elog, ERROR, fb(), get_qualified_objname(), GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), ReleaseSysCache(), result, and SearchSysCache1().

Referenced by make_ruledef(), pg_get_constraintdef_worker(), pg_get_indexdef_worker(), pg_get_serial_sequence(), and pg_get_triggerdef_worker().

◆ generate_qualified_type_name()

static char * generate_qualified_type_name ( Oid  typid)
static

Definition at line 14166 of file ruleutils.c.

14167{
14168 HeapTuple tp;
14170 char *typname;
14171 char *nspname;
14172 char *result;
14173
14175 if (!HeapTupleIsValid(tp))
14176 elog(ERROR, "cache lookup failed for type %u", typid);
14178 typname = NameStr(typtup->typname);
14179
14180 nspname = get_namespace_name_or_temp(typtup->typnamespace);
14181 if (!nspname)
14182 elog(ERROR, "cache lookup failed for namespace %u",
14183 typtup->typnamespace);
14184
14186
14187 ReleaseSysCache(tp);
14188
14189 return result;
14190}

References elog, ERROR, fb(), Form_pg_type, get_namespace_name_or_temp(), GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), quote_qualified_identifier(), ReleaseSysCache(), result, SearchSysCache1(), and typname.

Referenced by pg_get_constraintdef_worker().

◆ generate_relation_name()

static char * generate_relation_name ( Oid  relid,
List namespaces 
)
static

Definition at line 13815 of file ruleutils.c.

13816{
13817 HeapTuple tp;
13819 bool need_qual;
13821 char *relname;
13822 char *nspname;
13823 char *result;
13824
13826 if (!HeapTupleIsValid(tp))
13827 elog(ERROR, "cache lookup failed for relation %u", relid);
13829 relname = NameStr(reltup->relname);
13830
13831 /* Check for conflicting CTE name */
13832 need_qual = false;
13833 foreach(nslist, namespaces)
13834 {
13837
13838 foreach(ctlist, dpns->ctes)
13839 {
13841
13842 if (strcmp(cte->ctename, relname) == 0)
13843 {
13844 need_qual = true;
13845 break;
13846 }
13847 }
13848 if (need_qual)
13849 break;
13850 }
13851
13852 /* Otherwise, qualify the name if not visible in search path */
13853 if (!need_qual)
13854 need_qual = !RelationIsVisible(relid);
13855
13856 if (need_qual)
13857 nspname = get_namespace_name_or_temp(reltup->relnamespace);
13858 else
13859 nspname = NULL;
13860
13862
13863 ReleaseSysCache(tp);
13864
13865 return result;
13866}

References CommonTableExpr::ctename, elog, ERROR, fb(), get_namespace_name_or_temp(), GETSTRUCT(), HeapTupleIsValid, lfirst, NameStr, ObjectIdGetDatum(), quote_qualified_identifier(), RelationIsVisible(), ReleaseSysCache(), relname, result, and SearchSysCache1().

Referenced by get_delete_query_def(), get_from_clause_item(), get_insert_query_def(), get_merge_query_def(), get_rule_expr(), get_update_query_def(), make_propgraphdef_elements(), make_ruledef(), pg_get_constraintdef_worker(), pg_get_indexdef_worker(), pg_get_statisticsobj_worker(), and pg_get_triggerdef_worker().

◆ get_agg_combine_expr()

static void get_agg_combine_expr ( Node node,
deparse_context context,
void callback_arg 
)
static

Definition at line 11597 of file ruleutils.c.

11598{
11599 Aggref *aggref;
11600 Aggref *original_aggref = callback_arg;
11601
11602 if (!IsA(node, Aggref))
11603 elog(ERROR, "combining Aggref does not point to an Aggref");
11604
11605 aggref = (Aggref *) node;
11606 get_agg_expr(aggref, context, original_aggref);
11607}

References elog, ERROR, fb(), get_agg_expr(), and IsA.

Referenced by get_agg_expr_helper().

◆ get_agg_expr()

static void get_agg_expr ( Aggref aggref,
deparse_context context,
Aggref original_aggref 
)
static

Definition at line 11459 of file ruleutils.c.

11461{
11462 get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
11463 false);
11464}

References fb(), and get_agg_expr_helper().

Referenced by get_agg_combine_expr(), and get_rule_expr().

◆ get_agg_expr_helper()

static void get_agg_expr_helper ( Aggref aggref,
deparse_context context,
Aggref original_aggref,
const char funcname,
const char options,
bool  is_json_objectagg 
)
static

Definition at line 11471 of file ruleutils.c.

11474{
11475 StringInfo buf = context->buf;
11476 Oid argtypes[FUNC_MAX_ARGS];
11477 int nargs;
11478 bool use_variadic = false;
11479
11480 /*
11481 * For a combining aggregate, we look up and deparse the corresponding
11482 * partial aggregate instead. This is necessary because our input
11483 * argument list has been replaced; the new argument list always has just
11484 * one element, which will point to a partial Aggref that supplies us with
11485 * transition states to combine.
11486 */
11487 if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
11488 {
11490
11491 Assert(list_length(aggref->args) == 1);
11492 tle = linitial_node(TargetEntry, aggref->args);
11493 resolve_special_varno((Node *) tle->expr, context,
11495 return;
11496 }
11497
11498 /*
11499 * Mark as PARTIAL, if appropriate. We look to the original aggref so as
11500 * to avoid printing this when recursing from the code just above.
11501 */
11503 appendStringInfoString(buf, "PARTIAL ");
11504
11505 /* Extract the argument types as seen by the parser */
11506 nargs = get_aggregate_argtypes(aggref, argtypes);
11507
11508 if (!funcname)
11509 funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
11510 argtypes, aggref->aggvariadic,
11511 &use_variadic,
11512 context->inGroupBy);
11513
11514 /* Print the aggregate name, schema-qualified if needed */
11515 appendStringInfo(buf, "%s(%s", funcname,
11516 (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
11517
11518 if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
11519 {
11520 /*
11521 * Ordered-set aggregates do not use "*" syntax. Also, we needn't
11522 * worry about inserting VARIADIC. So we can just dump the direct
11523 * args as-is.
11524 */
11525 Assert(!aggref->aggvariadic);
11526 get_rule_expr((Node *) aggref->aggdirectargs, context, true);
11527 Assert(aggref->aggorder != NIL);
11528 appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
11529 get_rule_orderby(aggref->aggorder, aggref->args, false, context);
11530 }
11531 else
11532 {
11533 /* aggstar can be set only in zero-argument aggregates */
11534 if (aggref->aggstar)
11536 else
11537 {
11538 ListCell *l;
11539 int i;
11540
11541 i = 0;
11542 foreach(l, aggref->args)
11543 {
11545 Node *arg = (Node *) tle->expr;
11546
11548 if (tle->resjunk)
11549 continue;
11550 if (i++ > 0)
11551 {
11553 {
11554 /*
11555 * the ABSENT ON NULL and WITH UNIQUE args are printed
11556 * separately, so ignore them here
11557 */
11558 if (i > 2)
11559 break;
11560
11562 }
11563 else
11565 }
11566 if (use_variadic && i == nargs)
11567 appendStringInfoString(buf, "VARIADIC ");
11568 get_rule_expr(arg, context, true);
11569 }
11570 }
11571
11572 if (aggref->aggorder != NIL)
11573 {
11574 appendStringInfoString(buf, " ORDER BY ");
11575 get_rule_orderby(aggref->aggorder, aggref->args, false, context);
11576 }
11577 }
11578
11579 if (options)
11581
11582 if (aggref->aggfilter != NULL)
11583 {
11584 appendStringInfoString(buf, ") FILTER (WHERE ");
11585 get_rule_expr((Node *) aggref->aggfilter, context, false);
11586 }
11587
11589}

References Aggref::aggdirectargs, Aggref::aggdistinct, Aggref::aggfilter, Aggref::aggfnoid, Aggref::aggorder, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, Aggref::args, Assert, deparse_context::buf, buf, DO_AGGSPLIT_COMBINE, DO_AGGSPLIT_SKIPFINAL, fb(), FUNC_MAX_ARGS, funcname, generate_function_name(), get_agg_combine_expr(), get_aggregate_argtypes(), get_rule_expr(), get_rule_orderby(), i, deparse_context::inGroupBy, IsA, lfirst, linitial_node, list_length(), NIL, and resolve_special_varno().

Referenced by get_agg_expr(), and get_json_agg_constructor().

◆ get_basic_select_query()

static void get_basic_select_query ( Query query,
deparse_context context 
)
static

Definition at line 6474 of file ruleutils.c.

6475{
6476 StringInfo buf = context->buf;
6478 char *sep;
6479 ListCell *l;
6480
6481 if (PRETTY_INDENT(context))
6482 {
6483 context->indentLevel += PRETTYINDENT_STD;
6485 }
6486
6487 /*
6488 * If the query looks like SELECT * FROM (VALUES ...), then print just the
6489 * VALUES part. This reverses what transformValuesClause() did at parse
6490 * time.
6491 */
6492 values_rte = get_simple_values_rte(query, context->resultDesc);
6493 if (values_rte)
6494 {
6495 get_values_def(values_rte->values_lists, context);
6496 return;
6497 }
6498
6499 /*
6500 * Build up the query string - first we say SELECT
6501 */
6502 if (query->isReturn)
6503 appendStringInfoString(buf, "RETURN");
6504 else
6505 appendStringInfoString(buf, "SELECT");
6506
6507 /* Add the DISTINCT clause if given */
6508 if (query->distinctClause != NIL)
6509 {
6510 if (query->hasDistinctOn)
6511 {
6512 appendStringInfoString(buf, " DISTINCT ON (");
6513 sep = "";
6514 foreach(l, query->distinctClause)
6515 {
6517
6519 get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6520 false, context);
6521 sep = ", ";
6522 }
6524 }
6525 else
6526 appendStringInfoString(buf, " DISTINCT");
6527 }
6528
6529 /* Then we tell what to select (the targetlist) */
6530 get_target_list(query->targetList, context);
6531
6532 /* Add the FROM clause if needed */
6533 get_from_clause(query, " FROM ", context);
6534
6535 /* Add the WHERE clause if given */
6536 if (query->jointree->quals != NULL)
6537 {
6538 appendContextKeyword(context, " WHERE ",
6540 get_rule_expr(query->jointree->quals, context, false);
6541 }
6542
6543 /* Add the GROUP BY clause if given */
6544 if (query->groupClause != NULL || query->groupingSets != NULL)
6545 {
6546 bool save_ingroupby;
6547
6548 appendContextKeyword(context, " GROUP BY ",
6550 if (query->groupDistinct)
6551 appendStringInfoString(buf, "DISTINCT ");
6552
6553 save_ingroupby = context->inGroupBy;
6554 context->inGroupBy = true;
6555
6556 if (query->groupByAll)
6558 else if (query->groupingSets == NIL)
6559 {
6560 sep = "";
6561 foreach(l, query->groupClause)
6562 {
6564
6566 get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
6567 false, context);
6568 sep = ", ";
6569 }
6570 }
6571 else
6572 {
6573 sep = "";
6574 foreach(l, query->groupingSets)
6575 {
6576 GroupingSet *grp = lfirst(l);
6577
6579 get_rule_groupingset(grp, query->targetList, true, context);
6580 sep = ", ";
6581 }
6582 }
6583
6584 context->inGroupBy = save_ingroupby;
6585 }
6586
6587 /* Add the HAVING clause if given */
6588 if (query->havingQual != NULL)
6589 {
6590 appendContextKeyword(context, " HAVING ",
6592 get_rule_expr(query->havingQual, context, false);
6593 }
6594
6595 /* Add the WINDOW clause if needed */
6596 if (query->windowClause != NIL)
6597 get_rule_windowclause(query, context);
6598}

References appendContextKeyword(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, Query::distinctClause, fb(), get_from_clause(), get_rule_expr(), get_rule_groupingset(), get_rule_sortgroupclause(), get_rule_windowclause(), get_simple_values_rte(), get_target_list(), get_values_def(), Query::groupByAll, Query::groupClause, Query::groupDistinct, Query::groupingSets, Query::havingQual, deparse_context::indentLevel, deparse_context::inGroupBy, Query::jointree, lfirst, NIL, PRETTY_INDENT, PRETTYINDENT_STD, FromExpr::quals, deparse_context::resultDesc, Query::targetList, and Query::windowClause.

Referenced by get_select_query_def().

◆ get_coercion_expr()

static void get_coercion_expr ( Node arg,
deparse_context context,
Oid  resulttype,
int32  resulttypmod,
Node parentNode 
)
static

Definition at line 11994 of file ruleutils.c.

11997{
11998 StringInfo buf = context->buf;
11999
12000 /*
12001 * Since parse_coerce.c doesn't immediately collapse application of
12002 * length-coercion functions to constants, what we'll typically see in
12003 * such cases is a Const with typmod -1 and a length-coercion function
12004 * right above it. Avoid generating redundant output. However, beware of
12005 * suppressing casts when the user actually wrote something like
12006 * 'foo'::text::char(3).
12007 *
12008 * Note: it might seem that we are missing the possibility of needing to
12009 * print a COLLATE clause for such a Const. However, a Const could only
12010 * have nondefault collation in a post-constant-folding tree, in which the
12011 * length coercion would have been folded too. See also the special
12012 * handling of CollateExpr in coerce_to_target_type(): any collation
12013 * marking will be above the coercion node, not below it.
12014 */
12015 if (arg && IsA(arg, Const) &&
12016 ((Const *) arg)->consttype == resulttype &&
12017 ((Const *) arg)->consttypmod == -1)
12018 {
12019 /* Show the constant without normal ::typename decoration */
12020 get_const_expr((Const *) arg, context, -1);
12021 }
12022 else
12023 {
12024 if (!PRETTY_PAREN(context))
12026 get_rule_expr_paren(arg, context, false, parentNode);
12027 if (!PRETTY_PAREN(context))
12029 }
12030
12031 /*
12032 * Never emit resulttype(arg) functional notation. A pg_proc entry could
12033 * take precedence, and a resulttype in pg_temp would require schema
12034 * qualification that format_type_with_typemod() would usually omit. We've
12035 * standardized on arg::resulttype, but CAST(arg AS resulttype) notation
12036 * would work fine.
12037 */
12038 appendStringInfo(buf, "::%s",
12040}

References appendStringInfo(), appendStringInfoChar(), arg, deparse_context::buf, buf, fb(), format_type_with_typemod(), get_const_expr(), get_rule_expr_paren(), IsA, and PRETTY_PAREN.

Referenced by get_func_expr(), and get_rule_expr().

◆ get_column_alias_list()

static void get_column_alias_list ( deparse_columns colinfo,
deparse_context context 
)
static

Definition at line 13388 of file ruleutils.c.

13389{
13390 StringInfo buf = context->buf;
13391 int i;
13392 bool first = true;
13393
13394 /* Don't print aliases if not needed */
13395 if (!colinfo->printaliases)
13396 return;
13397
13398 for (i = 0; i < colinfo->num_new_cols; i++)
13399 {
13400 char *colname = colinfo->new_colnames[i];
13401
13402 if (first)
13403 {
13405 first = false;
13406 }
13407 else
13410 }
13411 if (!first)
13413}

References appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, fb(), i, and quote_identifier().

Referenced by get_from_clause_item().

◆ get_const_collation()

static void get_const_collation ( Const constval,
deparse_context context 
)
static

Definition at line 12188 of file ruleutils.c.

12189{
12190 StringInfo buf = context->buf;
12191
12192 if (OidIsValid(constval->constcollid))
12193 {
12194 Oid typcollation = get_typcollation(constval->consttype);
12195
12196 if (constval->constcollid != typcollation)
12197 {
12198 appendStringInfo(buf, " COLLATE %s",
12199 generate_collation_name(constval->constcollid));
12200 }
12201 }
12202}

References appendStringInfo(), deparse_context::buf, buf, Const::consttype, generate_collation_name(), get_typcollation(), and OidIsValid.

Referenced by get_const_expr().

◆ get_const_expr()

static void get_const_expr ( Const constval,
deparse_context context,
int  showtype 
)
static

Definition at line 12058 of file ruleutils.c.

12059{
12060 StringInfo buf = context->buf;
12061 Oid typoutput;
12062 bool typIsVarlena;
12063 char *extval;
12064 bool needlabel = false;
12065
12066 if (constval->constisnull)
12067 {
12068 /*
12069 * Always label the type of a NULL constant to prevent misdecisions
12070 * about type when reparsing.
12071 */
12072 appendStringInfoString(buf, "NULL");
12073 if (showtype >= 0)
12074 {
12075 appendStringInfo(buf, "::%s",
12077 constval->consttypmod));
12078 get_const_collation(constval, context);
12079 }
12080 return;
12081 }
12082
12083 getTypeOutputInfo(constval->consttype,
12084 &typoutput, &typIsVarlena);
12085
12086 extval = OidOutputFunctionCall(typoutput, constval->constvalue);
12087
12088 switch (constval->consttype)
12089 {
12090 case INT4OID:
12091
12092 /*
12093 * INT4 can be printed without any decoration, unless it is
12094 * negative; in that case print it as '-nnn'::integer to ensure
12095 * that the output will re-parse as a constant, not as a constant
12096 * plus operator. In most cases we could get away with printing
12097 * (-nnn) instead, because of the way that gram.y handles negative
12098 * literals; but that doesn't work for INT_MIN, and it doesn't
12099 * seem that much prettier anyway.
12100 */
12101 if (extval[0] != '-')
12103 else
12104 {
12105 appendStringInfo(buf, "'%s'", extval);
12106 needlabel = true; /* we must attach a cast */
12107 }
12108 break;
12109
12110 case NUMERICOID:
12111
12112 /*
12113 * NUMERIC can be printed without quotes if it looks like a float
12114 * constant (not an integer, and not Infinity or NaN) and doesn't
12115 * have a leading sign (for the same reason as for INT4).
12116 */
12117 if (isdigit((unsigned char) extval[0]) &&
12118 strcspn(extval, "eE.") != strlen(extval))
12119 {
12121 }
12122 else
12123 {
12124 appendStringInfo(buf, "'%s'", extval);
12125 needlabel = true; /* we must attach a cast */
12126 }
12127 break;
12128
12129 case BOOLOID:
12130 if (strcmp(extval, "t") == 0)
12131 appendStringInfoString(buf, "true");
12132 else
12133 appendStringInfoString(buf, "false");
12134 break;
12135
12136 default:
12138 break;
12139 }
12140
12141 pfree(extval);
12142
12143 if (showtype < 0)
12144 return;
12145
12146 /*
12147 * For showtype == 0, append ::typename unless the constant will be
12148 * implicitly typed as the right type when it is read in.
12149 *
12150 * XXX this code has to be kept in sync with the behavior of the parser,
12151 * especially make_const.
12152 */
12153 switch (constval->consttype)
12154 {
12155 case BOOLOID:
12156 case UNKNOWNOID:
12157 /* These types can be left unlabeled */
12158 needlabel = false;
12159 break;
12160 case INT4OID:
12161 /* We determined above whether a label is needed */
12162 break;
12163 case NUMERICOID:
12164
12165 /*
12166 * Float-looking constants will be typed as numeric, which we
12167 * checked above; but if there's a nondefault typmod we need to
12168 * show it.
12169 */
12170 needlabel |= (constval->consttypmod >= 0);
12171 break;
12172 default:
12173 needlabel = true;
12174 break;
12175 }
12176 if (needlabel || showtype > 0)
12177 appendStringInfo(buf, "::%s",
12179 constval->consttypmod));
12180
12181 get_const_collation(constval, context);
12182}

References appendStringInfo(), appendStringInfoString(), deparse_context::buf, buf, Const::consttype, fb(), format_type_with_typemod(), get_const_collation(), getTypeOutputInfo(), OidOutputFunctionCall(), pfree(), and simple_quote_literal().

Referenced by get_coercion_expr(), get_json_path_spec(), get_json_table(), get_json_table_nested_columns(), get_range_partbound_string(), get_rule_expr(), and get_rule_sortgroupclause().

◆ get_delete_query_def()

static void get_delete_query_def ( Query query,
deparse_context context 
)
static

Definition at line 7743 of file ruleutils.c.

7744{
7745 StringInfo buf = context->buf;
7747
7748 /* Insert the WITH clause if given */
7749 get_with_clause(query, context);
7750
7751 /*
7752 * Start the query with DELETE FROM relname
7753 */
7754 rte = rt_fetch(query->resultRelation, query->rtable);
7755 Assert(rte->rtekind == RTE_RELATION);
7756 if (PRETTY_INDENT(context))
7757 {
7759 context->indentLevel += PRETTYINDENT_STD;
7760 }
7761 appendStringInfo(buf, "DELETE FROM %s%s",
7763 generate_relation_name(rte->relid, NIL));
7764
7765 /* Print the FOR PORTION OF, if needed */
7766 get_for_portion_of(query->forPortionOf, context);
7767
7768 /* Print the relation alias, if needed */
7769 get_rte_alias(rte, query->resultRelation, false, context);
7770
7771 /* Add the USING clause if given */
7772 get_from_clause(query, " USING ", context);
7773
7774 /* Add a WHERE clause if given */
7775 if (query->jointree->quals != NULL)
7776 {
7777 appendContextKeyword(context, " WHERE ",
7779 get_rule_expr(query->jointree->quals, context, false);
7780 }
7781
7782 /* Add RETURNING if present */
7783 if (query->returningList)
7784 get_returning_clause(query, context);
7785}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), Assert, deparse_context::buf, buf, fb(), Query::forPortionOf, generate_relation_name(), get_for_portion_of(), get_from_clause(), get_returning_clause(), get_rte_alias(), get_rule_expr(), get_with_clause(), deparse_context::indentLevel, Query::jointree, NIL, only_marker, PRETTY_INDENT, PRETTYINDENT_STD, FromExpr::quals, Query::returningList, rt_fetch, Query::rtable, and RTE_RELATION.

Referenced by get_query_def().

◆ get_for_portion_of()

static void get_for_portion_of ( ForPortionOfExpr forPortionOf,
deparse_context context 
)
static

Definition at line 13355 of file ruleutils.c.

13356{
13357 if (forPortionOf)
13358 {
13359 appendStringInfo(context->buf, " FOR PORTION OF %s",
13360 quote_identifier(forPortionOf->range_name));
13361
13362 /*
13363 * Try to write it as FROM ... TO ... if we received it that way,
13364 * otherwise (targetRange).
13365 */
13366 if (forPortionOf->targetFrom && forPortionOf->targetTo)
13367 {
13368 appendStringInfoString(context->buf, " FROM ");
13369 get_rule_expr(forPortionOf->targetFrom, context, false);
13370 appendStringInfoString(context->buf, " TO ");
13371 get_rule_expr(forPortionOf->targetTo, context, false);
13372 }
13373 else
13374 {
13375 appendStringInfoString(context->buf, " (");
13376 get_rule_expr(forPortionOf->targetRange, context, false);
13377 appendStringInfoChar(context->buf, ')');
13378 }
13379 }
13380}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, get_rule_expr(), quote_identifier(), ForPortionOfExpr::range_name, ForPortionOfExpr::targetFrom, ForPortionOfExpr::targetRange, and ForPortionOfExpr::targetTo.

Referenced by get_delete_query_def(), and get_update_query_def().

◆ get_from_clause()

static void get_from_clause ( Query query,
const char prefix,
deparse_context context 
)
static

Definition at line 12876 of file ruleutils.c.

12877{
12878 StringInfo buf = context->buf;
12879 bool first = true;
12880 ListCell *l;
12881
12882 /*
12883 * We use the query's jointree as a guide to what to print. However, we
12884 * must ignore auto-added RTEs that are marked not inFromCl. (These can
12885 * only appear at the top level of the jointree, so it's sufficient to
12886 * check here.) This check also ensures we ignore the rule pseudo-RTEs
12887 * for NEW and OLD.
12888 */
12889 foreach(l, query->jointree->fromlist)
12890 {
12891 Node *jtnode = (Node *) lfirst(l);
12892
12893 if (IsA(jtnode, RangeTblRef))
12894 {
12895 int varno = ((RangeTblRef *) jtnode)->rtindex;
12896 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12897
12898 if (!rte->inFromCl)
12899 continue;
12900 }
12901
12902 if (first)
12903 {
12904 appendContextKeyword(context, prefix,
12906 first = false;
12907
12908 get_from_clause_item(jtnode, query, context);
12909 }
12910 else
12911 {
12913
12915
12916 /*
12917 * Put the new FROM item's text into itembuf so we can decide
12918 * after we've got it whether or not it needs to go on a new line.
12919 */
12921 context->buf = &itembuf;
12922
12923 get_from_clause_item(jtnode, query, context);
12924
12925 /* Restore context's output buffer */
12926 context->buf = buf;
12927
12928 /* Consider line-wrapping if enabled */
12929 if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
12930 {
12931 /* Does the new item start with a new line? */
12932 if (itembuf.len > 0 && itembuf.data[0] == '\n')
12933 {
12934 /* If so, we shouldn't add anything */
12935 /* instead, remove any trailing spaces currently in buf */
12937 }
12938 else
12939 {
12940 char *trailing_nl;
12941
12942 /* Locate the start of the current line in the buffer */
12943 trailing_nl = strrchr(buf->data, '\n');
12944 if (trailing_nl == NULL)
12945 trailing_nl = buf->data;
12946 else
12947 trailing_nl++;
12948
12949 /*
12950 * Add a newline, plus some indentation, if the new item
12951 * would cause an overflow.
12952 */
12953 if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
12957 }
12958 }
12959
12960 /* Add the new item */
12962
12963 /* clean up */
12964 pfree(itembuf.data);
12965 }
12966 }
12967}

References appendBinaryStringInfo(), appendContextKeyword(), appendStringInfoString(), deparse_context::buf, buf, fb(), FromExpr::fromlist, get_from_clause_item(), initStringInfo(), IsA, Query::jointree, lfirst, pfree(), PRETTY_INDENT, PRETTYINDENT_STD, PRETTYINDENT_VAR, removeStringInfoSpaces(), rt_fetch, Query::rtable, and deparse_context::wrapColumn.

Referenced by get_basic_select_query(), get_delete_query_def(), get_merge_query_def(), and get_update_query_def().

◆ get_from_clause_coldeflist()

static void get_from_clause_coldeflist ( RangeTblFunction rtfunc,
deparse_columns colinfo,
deparse_context context 
)
static

Definition at line 13428 of file ruleutils.c.

13431{
13432 StringInfo buf = context->buf;
13433 ListCell *l1;
13434 ListCell *l2;
13435 ListCell *l3;
13436 ListCell *l4;
13437 int i;
13438
13440
13441 i = 0;
13442 forfour(l1, rtfunc->funccoltypes,
13443 l2, rtfunc->funccoltypmods,
13444 l3, rtfunc->funccolcollations,
13445 l4, rtfunc->funccolnames)
13446 {
13447 Oid atttypid = lfirst_oid(l1);
13448 int32 atttypmod = lfirst_int(l2);
13449 Oid attcollation = lfirst_oid(l3);
13450 char *attname;
13451
13452 if (colinfo)
13453 attname = colinfo->colnames[i];
13454 else
13455 attname = strVal(lfirst(l4));
13456
13457 Assert(attname); /* shouldn't be any dropped columns here */
13458
13459 if (i > 0)
13461 appendStringInfo(buf, "%s %s",
13464 if (OidIsValid(attcollation) &&
13465 attcollation != get_typcollation(atttypid))
13466 appendStringInfo(buf, " COLLATE %s",
13467 generate_collation_name(attcollation));
13468
13469 i++;
13470 }
13471
13473}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, attname, deparse_context::buf, buf, fb(), forfour, format_type_with_typemod(), generate_collation_name(), get_typcollation(), i, lfirst, lfirst_int, lfirst_oid, OidIsValid, quote_identifier(), and strVal.

Referenced by get_from_clause_item().

◆ get_from_clause_item()

static void get_from_clause_item ( Node jtnode,
Query query,
deparse_context context 
)
static

Definition at line 12970 of file ruleutils.c.

12971{
12972 StringInfo buf = context->buf;
12974
12975 if (IsA(jtnode, RangeTblRef))
12976 {
12977 int varno = ((RangeTblRef *) jtnode)->rtindex;
12978 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12981
12982 if (rte->lateral)
12983 appendStringInfoString(buf, "LATERAL ");
12984
12985 /* Print the FROM item proper */
12986 switch (rte->rtekind)
12987 {
12988 case RTE_RELATION:
12989 /* Normal relation RTE */
12990 appendStringInfo(buf, "%s%s",
12993 context->namespaces));
12994 break;
12995 case RTE_SUBQUERY:
12996 /* Subquery RTE */
12998 get_query_def(rte->subquery, buf, context->namespaces, NULL,
12999 true,
13000 context->prettyFlags, context->wrapColumn,
13001 context->indentLevel);
13003 break;
13004 case RTE_FUNCTION:
13005 /* Function RTE */
13006 rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
13007
13008 /*
13009 * Omit ROWS FROM() syntax for just one function, unless it
13010 * has both a coldeflist and WITH ORDINALITY. If it has both,
13011 * we must use ROWS FROM() syntax to avoid ambiguity about
13012 * whether the coldeflist includes the ordinality column.
13013 */
13014 if (list_length(rte->functions) == 1 &&
13015 (rtfunc1->funccolnames == NIL || !rte->funcordinality))
13016 {
13017 get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
13018 /* we'll print the coldeflist below, if it has one */
13019 }
13020 else
13021 {
13022 bool all_unnest;
13023 ListCell *lc;
13024
13025 /*
13026 * If all the function calls in the list are to unnest,
13027 * and none need a coldeflist, then collapse the list back
13028 * down to UNNEST(args). (If we had more than one
13029 * built-in unnest function, this would get more
13030 * difficult.)
13031 *
13032 * XXX This is pretty ugly, since it makes not-terribly-
13033 * future-proof assumptions about what the parser would do
13034 * with the output; but the alternative is to emit our
13035 * nonstandard ROWS FROM() notation for what might have
13036 * been a perfectly spec-compliant multi-argument
13037 * UNNEST().
13038 */
13039 all_unnest = true;
13040 foreach(lc, rte->functions)
13041 {
13043
13044 if (!IsA(rtfunc->funcexpr, FuncExpr) ||
13045 ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
13046 rtfunc->funccolnames != NIL)
13047 {
13048 all_unnest = false;
13049 break;
13050 }
13051 }
13052
13053 if (all_unnest)
13054 {
13055 List *allargs = NIL;
13056
13057 foreach(lc, rte->functions)
13058 {
13060 List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
13061
13062 allargs = list_concat(allargs, args);
13063 }
13064
13065 appendStringInfoString(buf, "UNNEST(");
13066 get_rule_expr((Node *) allargs, context, true);
13068 }
13069 else
13070 {
13071 int funcno = 0;
13072
13073 appendStringInfoString(buf, "ROWS FROM(");
13074 foreach(lc, rte->functions)
13075 {
13077
13078 if (funcno > 0)
13080 get_rule_expr_funccall(rtfunc->funcexpr, context, true);
13081 if (rtfunc->funccolnames != NIL)
13082 {
13083 /* Reconstruct the column definition list */
13084 appendStringInfoString(buf, " AS ");
13086 NULL,
13087 context);
13088 }
13089 funcno++;
13090 }
13092 }
13093 /* prevent printing duplicate coldeflist below */
13094 rtfunc1 = NULL;
13095 }
13096 if (rte->funcordinality)
13097 appendStringInfoString(buf, " WITH ORDINALITY");
13098 break;
13099 case RTE_TABLEFUNC:
13100 get_tablefunc(rte->tablefunc, context, true);
13101 break;
13102 case RTE_GRAPH_TABLE:
13103 appendStringInfoString(buf, "GRAPH_TABLE (");
13105 appendStringInfoString(buf, " MATCH ");
13106 get_graph_pattern_def(rte->graph_pattern, context);
13107 appendStringInfoString(buf, " COLUMNS (");
13108 {
13109 bool first = true;
13110
13111 foreach_node(TargetEntry, te, rte->graph_table_columns)
13112 {
13113 if (!first)
13115 else
13116 first = false;
13117
13118 get_rule_expr((Node *) te->expr, context, false);
13119 appendStringInfoString(buf, " AS ");
13121 }
13122 }
13124 break;
13125 case RTE_VALUES:
13126 /* Values list RTE */
13128 get_values_def(rte->values_lists, context);
13130 break;
13131 case RTE_CTE:
13133 break;
13134 default:
13135 elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
13136 break;
13137 }
13138
13139 /* Print the relation alias, if needed */
13140 get_rte_alias(rte, varno, false, context);
13141
13142 /* Print the column definitions or aliases, if needed */
13143 if (rtfunc1 && rtfunc1->funccolnames != NIL)
13144 {
13145 /* Reconstruct the columndef list, which is also the aliases */
13147 }
13148 else
13149 {
13150 /* Else print column aliases as needed */
13152 }
13153
13154 /* Tablesample clause must go after any alias */
13155 if (rte->rtekind == RTE_RELATION && rte->tablesample)
13156 get_tablesample_def(rte->tablesample, context);
13157 }
13158 else if (IsA(jtnode, JoinExpr))
13159 {
13160 JoinExpr *j = (JoinExpr *) jtnode;
13163
13164 need_paren_on_right = PRETTY_PAREN(context) &&
13165 !IsA(j->rarg, RangeTblRef) &&
13166 !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
13167
13168 if (!PRETTY_PAREN(context) || j->alias != NULL)
13170
13171 get_from_clause_item(j->larg, query, context);
13172
13173 switch (j->jointype)
13174 {
13175 case JOIN_INNER:
13176 if (j->quals)
13177 appendContextKeyword(context, " JOIN ",
13181 else
13182 appendContextKeyword(context, " CROSS JOIN ",
13186 break;
13187 case JOIN_LEFT:
13188 appendContextKeyword(context, " LEFT JOIN ",
13192 break;
13193 case JOIN_FULL:
13194 appendContextKeyword(context, " FULL JOIN ",
13198 break;
13199 case JOIN_RIGHT:
13200 appendContextKeyword(context, " RIGHT JOIN ",
13204 break;
13205 default:
13206 elog(ERROR, "unrecognized join type: %d",
13207 (int) j->jointype);
13208 }
13209
13212 get_from_clause_item(j->rarg, query, context);
13215
13216 if (j->usingClause)
13217 {
13218 ListCell *lc;
13219 bool first = true;
13220
13221 appendStringInfoString(buf, " USING (");
13222 /* Use the assigned names, not what's in usingClause */
13223 foreach(lc, colinfo->usingNames)
13224 {
13225 char *colname = (char *) lfirst(lc);
13226
13227 if (first)
13228 first = false;
13229 else
13232 }
13234
13235 if (j->join_using_alias)
13236 appendStringInfo(buf, " AS %s",
13237 quote_identifier(j->join_using_alias->aliasname));
13238 }
13239 else if (j->quals)
13240 {
13241 appendStringInfoString(buf, " ON ");
13242 if (!PRETTY_PAREN(context))
13244 get_rule_expr(j->quals, context, false);
13245 if (!PRETTY_PAREN(context))
13247 }
13248 else if (j->jointype != JOIN_INNER)
13249 {
13250 /* If we didn't say CROSS JOIN above, we must provide an ON */
13251 appendStringInfoString(buf, " ON TRUE");
13252 }
13253
13254 if (!PRETTY_PAREN(context) || j->alias != NULL)
13256
13257 /* Yes, it's correct to put alias after the right paren ... */
13258 if (j->alias != NULL)
13259 {
13260 /*
13261 * Note that it's correct to emit an alias clause if and only if
13262 * there was one originally. Otherwise we'd be converting a named
13263 * join to unnamed or vice versa, which creates semantic
13264 * subtleties we don't want. However, we might print a different
13265 * alias name than was there originally.
13266 */
13267 appendStringInfo(buf, " %s",
13269 context)));
13271 }
13272 }
13273 else
13274 elog(ERROR, "unrecognized node type: %d",
13275 (int) nodeTag(jtnode));
13276}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, deparse_columns_fetch, elog, ERROR, fb(), foreach_node, RangeTblFunction::funcexpr, generate_relation_name(), get_column_alias_list(), get_from_clause_coldeflist(), get_from_clause_item(), get_graph_pattern_def(), get_query_def(), get_rtable_name(), get_rte_alias(), get_rule_expr(), get_rule_expr_funccall(), get_tablefunc(), get_tablesample_def(), get_values_def(), deparse_context::indentLevel, IsA, j, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, lfirst, linitial, list_concat(), list_length(), deparse_context::namespaces, NIL, nodeTag, only_marker, PRETTY_PAREN, deparse_context::prettyFlags, PRETTYINDENT_JOIN, PRETTYINDENT_STD, quote_identifier(), rt_fetch, Query::rtable, RTE_CTE, RTE_FUNCTION, RTE_GRAPH_TABLE, RTE_RELATION, RTE_SUBQUERY, RTE_TABLEFUNC, RTE_VALUES, and deparse_context::wrapColumn.

Referenced by get_from_clause(), and get_from_clause_item().

◆ get_func_expr()

static void get_func_expr ( FuncExpr expr,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 11363 of file ruleutils.c.

11365{
11366 StringInfo buf = context->buf;
11367 Oid funcoid = expr->funcid;
11368 Oid argtypes[FUNC_MAX_ARGS];
11369 int nargs;
11370 List *argnames;
11371 bool use_variadic;
11372 ListCell *l;
11373
11374 /*
11375 * If the function call came from an implicit coercion, then just show the
11376 * first argument --- unless caller wants to see implicit coercions.
11377 */
11378 if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
11379 {
11380 get_rule_expr_paren((Node *) linitial(expr->args), context,
11381 false, (Node *) expr);
11382 return;
11383 }
11384
11385 /*
11386 * If the function call came from a cast, then show the first argument
11387 * plus an explicit cast operation.
11388 */
11389 if (expr->funcformat == COERCE_EXPLICIT_CAST ||
11390 expr->funcformat == COERCE_IMPLICIT_CAST)
11391 {
11392 Node *arg = linitial(expr->args);
11393 Oid rettype = expr->funcresulttype;
11395
11396 /* Get the typmod if this is a length-coercion function */
11398
11399 get_coercion_expr(arg, context,
11400 rettype, coercedTypmod,
11401 (Node *) expr);
11402
11403 return;
11404 }
11405
11406 /*
11407 * If the function was called using one of the SQL spec's random special
11408 * syntaxes, try to reproduce that. If we don't recognize the function,
11409 * fall through.
11410 */
11411 if (expr->funcformat == COERCE_SQL_SYNTAX)
11412 {
11413 if (get_func_sql_syntax(expr, context))
11414 return;
11415 }
11416
11417 /*
11418 * Normal function: display as proname(args). First we need to extract
11419 * the argument datatypes.
11420 */
11421 if (list_length(expr->args) > FUNC_MAX_ARGS)
11422 ereport(ERROR,
11424 errmsg("too many arguments")));
11425 nargs = 0;
11426 argnames = NIL;
11427 foreach(l, expr->args)
11428 {
11429 Node *arg = (Node *) lfirst(l);
11430
11431 if (IsA(arg, NamedArgExpr))
11432 argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
11433 argtypes[nargs] = exprType(arg);
11434 nargs++;
11435 }
11436
11437 appendStringInfo(buf, "%s(",
11439 argnames, argtypes,
11440 expr->funcvariadic,
11441 &use_variadic,
11442 context->inGroupBy));
11443 nargs = 0;
11444 foreach(l, expr->args)
11445 {
11446 if (nargs++ > 0)
11448 if (use_variadic && lnext(expr->args, l) == NULL)
11449 appendStringInfoString(buf, "VARIADIC ");
11450 get_rule_expr((Node *) lfirst(l), context, true);
11451 }
11453}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, FuncExpr::args, deparse_context::buf, buf, COERCE_EXPLICIT_CAST, COERCE_IMPLICIT_CAST, COERCE_SQL_SYNTAX, ereport, errcode(), errmsg, ERROR, exprIsLengthCoercion(), exprType(), fb(), FUNC_MAX_ARGS, FuncExpr::funcid, generate_function_name(), get_coercion_expr(), get_func_sql_syntax(), get_rule_expr(), get_rule_expr_paren(), deparse_context::inGroupBy, IsA, lappend(), lfirst, linitial, list_length(), lnext(), and NIL.

Referenced by get_rule_expr().

◆ get_func_sql_syntax()

static bool get_func_sql_syntax ( FuncExpr expr,
deparse_context context 
)
static

Definition at line 11742 of file ruleutils.c.

11743{
11744 StringInfo buf = context->buf;
11745 Oid funcoid = expr->funcid;
11746
11747 switch (funcoid)
11748 {
11755 /* AT TIME ZONE ... note reversed argument order */
11757 get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
11758 (Node *) expr);
11759 appendStringInfoString(buf, " AT TIME ZONE ");
11760 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11761 (Node *) expr);
11763 return true;
11764
11767 case F_TIMEZONE_TIMETZ:
11768 /* AT LOCAL */
11770 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11771 (Node *) expr);
11772 appendStringInfoString(buf, " AT LOCAL)");
11773 return true;
11774
11788 /* (x1, x2) OVERLAPS (y1, y2) */
11790 get_rule_expr((Node *) linitial(expr->args), context, false);
11792 get_rule_expr((Node *) lsecond(expr->args), context, false);
11793 appendStringInfoString(buf, ") OVERLAPS (");
11794 get_rule_expr((Node *) lthird(expr->args), context, false);
11796 get_rule_expr((Node *) lfourth(expr->args), context, false);
11798 return true;
11799
11806 /* EXTRACT (x FROM y) */
11807 appendStringInfoString(buf, "EXTRACT(");
11808 {
11809 Const *con = (Const *) linitial(expr->args);
11810
11811 Assert(IsA(con, Const) &&
11812 con->consttype == TEXTOID &&
11813 !con->constisnull);
11815 }
11816 appendStringInfoString(buf, " FROM ");
11817 get_rule_expr((Node *) lsecond(expr->args), context, false);
11819 return true;
11820
11821 case F_IS_NORMALIZED:
11822 /* IS xxx NORMALIZED */
11824 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11825 (Node *) expr);
11827 if (list_length(expr->args) == 2)
11828 {
11829 Const *con = (Const *) lsecond(expr->args);
11830
11831 Assert(IsA(con, Const) &&
11832 con->consttype == TEXTOID &&
11833 !con->constisnull);
11834 appendStringInfo(buf, " %s",
11835 TextDatumGetCString(con->constvalue));
11836 }
11837 appendStringInfoString(buf, " NORMALIZED)");
11838 return true;
11839
11840 case F_PG_COLLATION_FOR:
11841 /* COLLATION FOR */
11842 appendStringInfoString(buf, "COLLATION FOR (");
11843 get_rule_expr((Node *) linitial(expr->args), context, false);
11845 return true;
11846
11847 case F_NORMALIZE:
11848 /* NORMALIZE() */
11849 appendStringInfoString(buf, "NORMALIZE(");
11850 get_rule_expr((Node *) linitial(expr->args), context, false);
11851 if (list_length(expr->args) == 2)
11852 {
11853 Const *con = (Const *) lsecond(expr->args);
11854
11855 Assert(IsA(con, Const) &&
11856 con->consttype == TEXTOID &&
11857 !con->constisnull);
11858 appendStringInfo(buf, ", %s",
11859 TextDatumGetCString(con->constvalue));
11860 }
11862 return true;
11863
11870 /* OVERLAY() */
11871 appendStringInfoString(buf, "OVERLAY(");
11872 get_rule_expr((Node *) linitial(expr->args), context, false);
11873 appendStringInfoString(buf, " PLACING ");
11874 get_rule_expr((Node *) lsecond(expr->args), context, false);
11875 appendStringInfoString(buf, " FROM ");
11876 get_rule_expr((Node *) lthird(expr->args), context, false);
11877 if (list_length(expr->args) == 4)
11878 {
11879 appendStringInfoString(buf, " FOR ");
11880 get_rule_expr((Node *) lfourth(expr->args), context, false);
11881 }
11883 return true;
11884
11885 case F_POSITION_BIT_BIT:
11888 /* POSITION() ... extra parens since args are b_expr not a_expr */
11889 appendStringInfoString(buf, "POSITION((");
11890 get_rule_expr((Node *) lsecond(expr->args), context, false);
11891 appendStringInfoString(buf, ") IN (");
11892 get_rule_expr((Node *) linitial(expr->args), context, false);
11894 return true;
11895
11902 /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
11903 appendStringInfoString(buf, "SUBSTRING(");
11904 get_rule_expr((Node *) linitial(expr->args), context, false);
11905 appendStringInfoString(buf, " FROM ");
11906 get_rule_expr((Node *) lsecond(expr->args), context, false);
11907 if (list_length(expr->args) == 3)
11908 {
11909 appendStringInfoString(buf, " FOR ");
11910 get_rule_expr((Node *) lthird(expr->args), context, false);
11911 }
11913 return true;
11914
11916 /* SUBSTRING SIMILAR/ESCAPE */
11917 appendStringInfoString(buf, "SUBSTRING(");
11918 get_rule_expr((Node *) linitial(expr->args), context, false);
11919 appendStringInfoString(buf, " SIMILAR ");
11920 get_rule_expr((Node *) lsecond(expr->args), context, false);
11921 appendStringInfoString(buf, " ESCAPE ");
11922 get_rule_expr((Node *) lthird(expr->args), context, false);
11924 return true;
11925
11927 case F_BTRIM_TEXT:
11928 case F_BTRIM_TEXT_TEXT:
11929 /* TRIM() */
11930 appendStringInfoString(buf, "TRIM(BOTH");
11931 if (list_length(expr->args) == 2)
11932 {
11934 get_rule_expr((Node *) lsecond(expr->args), context, false);
11935 }
11936 appendStringInfoString(buf, " FROM ");
11937 get_rule_expr((Node *) linitial(expr->args), context, false);
11939 return true;
11940
11942 case F_LTRIM_TEXT:
11943 case F_LTRIM_TEXT_TEXT:
11944 /* TRIM() */
11945 appendStringInfoString(buf, "TRIM(LEADING");
11946 if (list_length(expr->args) == 2)
11947 {
11949 get_rule_expr((Node *) lsecond(expr->args), context, false);
11950 }
11951 appendStringInfoString(buf, " FROM ");
11952 get_rule_expr((Node *) linitial(expr->args), context, false);
11954 return true;
11955
11957 case F_RTRIM_TEXT:
11958 case F_RTRIM_TEXT_TEXT:
11959 /* TRIM() */
11960 appendStringInfoString(buf, "TRIM(TRAILING");
11961 if (list_length(expr->args) == 2)
11962 {
11964 get_rule_expr((Node *) lsecond(expr->args), context, false);
11965 }
11966 appendStringInfoString(buf, " FROM ");
11967 get_rule_expr((Node *) linitial(expr->args), context, false);
11969 return true;
11970
11971 case F_SYSTEM_USER:
11972 appendStringInfoString(buf, "SYSTEM_USER");
11973 return true;
11974
11975 case F_XMLEXISTS:
11976 /* XMLEXISTS ... extra parens because args are c_expr */
11977 appendStringInfoString(buf, "XMLEXISTS((");
11978 get_rule_expr((Node *) linitial(expr->args), context, false);
11979 appendStringInfoString(buf, ") PASSING (");
11980 get_rule_expr((Node *) lsecond(expr->args), context, false);
11982 return true;
11983 }
11984 return false;
11985}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), FuncExpr::args, Assert, deparse_context::buf, buf, Const::consttype, fb(), FuncExpr::funcid, get_rule_expr(), get_rule_expr_paren(), IsA, lfourth, linitial, list_length(), lsecond, lthird, and TextDatumGetCString.

Referenced by get_func_expr().

◆ get_graph_label_expr()

static void get_graph_label_expr ( Node label_expr,
deparse_context context 
)
static

Definition at line 7982 of file ruleutils.c.

7983{
7984 StringInfo buf = context->buf;
7985
7987
7988 switch (nodeTag(label_expr))
7989 {
7990 case T_GraphLabelRef:
7991 {
7993
7995 break;
7996 }
7997
7998 case T_BoolExpr:
7999 {
8001 ListCell *lc;
8002 bool first = true;
8003
8004 Assert(be->boolop == OR_EXPR);
8005
8006 foreach(lc, be->args)
8007 {
8008 if (!first)
8009 {
8010 if (be->boolop == OR_EXPR)
8012 }
8013 else
8014 first = false;
8015 get_graph_label_expr(lfirst(lc), context);
8016 }
8017
8018 break;
8019 }
8020
8021 default:
8022 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(label_expr));
8023 break;
8024 }
8025}

References appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, check_stack_depth(), elog, ERROR, fb(), get_graph_label_expr(), get_propgraph_label_name(), lfirst, nodeTag, OR_EXPR, and quote_identifier().

Referenced by get_graph_label_expr(), and get_path_pattern_expr_def().

◆ get_graph_pattern_def()

static void get_graph_pattern_def ( GraphPattern graph_pattern,
deparse_context context 
)
static

Definition at line 8117 of file ruleutils.c.

8118{
8119 StringInfo buf = context->buf;
8120 ListCell *lc;
8121 bool first = true;
8122
8123 foreach(lc, graph_pattern->path_pattern_list)
8124 {
8126
8127 if (!first)
8129 else
8130 first = false;
8131
8133 }
8134
8135 if (graph_pattern->whereClause)
8136 {
8137 appendStringInfoString(buf, "WHERE ");
8138 get_rule_expr(graph_pattern->whereClause, context, false);
8139 }
8140}

References appendStringInfoString(), deparse_context::buf, buf, fb(), get_path_pattern_expr_def(), get_rule_expr(), lfirst_node, GraphPattern::path_pattern_list, and GraphPattern::whereClause.

Referenced by get_from_clause_item().

◆ get_insert_query_def()

static void get_insert_query_def ( Query query,
deparse_context context 
)
static

Definition at line 7307 of file ruleutils.c.

7308{
7309 StringInfo buf = context->buf;
7313 char *sep;
7314 ListCell *l;
7316
7317 /* Insert the WITH clause if given */
7318 get_with_clause(query, context);
7319
7320 /*
7321 * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
7322 * single RTE for the SELECT or VALUES. Plain VALUES has neither.
7323 */
7324 foreach(l, query->rtable)
7325 {
7326 rte = (RangeTblEntry *) lfirst(l);
7327
7328 if (rte->rtekind == RTE_SUBQUERY)
7329 {
7330 if (select_rte)
7331 elog(ERROR, "too many subquery RTEs in INSERT");
7332 select_rte = rte;
7333 }
7334
7335 if (rte->rtekind == RTE_VALUES)
7336 {
7337 if (values_rte)
7338 elog(ERROR, "too many values RTEs in INSERT");
7339 values_rte = rte;
7340 }
7341 }
7342 if (select_rte && values_rte)
7343 elog(ERROR, "both subquery and values RTEs in INSERT");
7344
7345 /*
7346 * Start the query with INSERT INTO relname
7347 */
7348 rte = rt_fetch(query->resultRelation, query->rtable);
7349 Assert(rte->rtekind == RTE_RELATION);
7350
7351 if (PRETTY_INDENT(context))
7352 {
7353 context->indentLevel += PRETTYINDENT_STD;
7355 }
7356 appendStringInfo(buf, "INSERT INTO %s",
7357 generate_relation_name(rte->relid, NIL));
7358
7359 /* Print the relation alias, if needed; INSERT requires explicit AS */
7360 get_rte_alias(rte, query->resultRelation, true, context);
7361
7362 /* always want a space here */
7364
7365 /*
7366 * Add the insert-column-names list. Any indirection decoration needed on
7367 * the column names can be inferred from the top targetlist.
7368 */
7370 sep = "";
7371 if (query->targetList)
7373 foreach(l, query->targetList)
7374 {
7376
7377 if (tle->resjunk)
7378 continue; /* ignore junk entries */
7379
7381 sep = ", ";
7382
7383 /*
7384 * Put out name of target column; look in the catalogs, not at
7385 * tle->resname, since resname will fail to track RENAME.
7386 */
7389 tle->resno,
7390 false)));
7391
7392 /*
7393 * Print any indirection needed (subfields or subscripts), and strip
7394 * off the top-level nodes representing the indirection assignments.
7395 * Add the stripped expressions to strippedexprs. (If it's a
7396 * single-VALUES statement, the stripped expressions are the VALUES to
7397 * print below. Otherwise they're just Vars and not really
7398 * interesting.)
7399 */
7401 processIndirection((Node *) tle->expr,
7402 context));
7403 }
7404 if (query->targetList)
7406
7407 if (query->override)
7408 {
7409 if (query->override == OVERRIDING_SYSTEM_VALUE)
7410 appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
7411 else if (query->override == OVERRIDING_USER_VALUE)
7412 appendStringInfoString(buf, "OVERRIDING USER VALUE ");
7413 }
7414
7415 if (select_rte)
7416 {
7417 /* Add the SELECT */
7418 get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
7419 false,
7420 context->prettyFlags, context->wrapColumn,
7421 context->indentLevel);
7422 }
7423 else if (values_rte)
7424 {
7425 /* Add the multi-VALUES expression lists */
7426 get_values_def(values_rte->values_lists, context);
7427 }
7428 else if (strippedexprs)
7429 {
7430 /* Add the single-VALUES expression list */
7431 appendContextKeyword(context, "VALUES (",
7433 get_rule_list_toplevel(strippedexprs, context, false);
7435 }
7436 else
7437 {
7438 /* No expressions, so it must be DEFAULT VALUES */
7439 appendStringInfoString(buf, "DEFAULT VALUES");
7440 }
7441
7442 /* Add ON CONFLICT if present */
7443 if (query->onConflict)
7444 {
7446
7447 appendStringInfoString(buf, " ON CONFLICT");
7448
7449 if (confl->arbiterElems)
7450 {
7451 /* Add the single-VALUES expression list */
7453 get_rule_expr((Node *) confl->arbiterElems, context, false);
7455
7456 /* Add a WHERE clause (for partial indexes) if given */
7457 if (confl->arbiterWhere != NULL)
7458 {
7459 bool save_varprefix;
7460
7461 /*
7462 * Force non-prefixing of Vars, since parser assumes that they
7463 * belong to target relation. WHERE clause does not use
7464 * InferenceElem, so this is separately required.
7465 */
7466 save_varprefix = context->varprefix;
7467 context->varprefix = false;
7468
7469 appendContextKeyword(context, " WHERE ",
7471 get_rule_expr(confl->arbiterWhere, context, false);
7472
7473 context->varprefix = save_varprefix;
7474 }
7475 }
7476 else if (OidIsValid(confl->constraint))
7477 {
7478 char *constraint = get_constraint_name(confl->constraint);
7479
7480 if (!constraint)
7481 elog(ERROR, "cache lookup failed for constraint %u",
7482 confl->constraint);
7483 appendStringInfo(buf, " ON CONSTRAINT %s",
7484 quote_identifier(constraint));
7485 }
7486
7487 if (confl->action == ONCONFLICT_NOTHING)
7488 {
7489 appendStringInfoString(buf, " DO NOTHING");
7490 }
7491 else if (confl->action == ONCONFLICT_UPDATE)
7492 {
7493 appendStringInfoString(buf, " DO UPDATE SET ");
7494 /* Deparse targetlist */
7495 get_update_query_targetlist_def(query, confl->onConflictSet,
7496 context, rte);
7497
7498 /* Add a WHERE clause if given */
7499 if (confl->onConflictWhere != NULL)
7500 {
7501 appendContextKeyword(context, " WHERE ",
7503 get_rule_expr(confl->onConflictWhere, context, false);
7504 }
7505 }
7506 else
7507 {
7508 Assert(confl->action == ONCONFLICT_SELECT);
7509 appendStringInfoString(buf, " DO SELECT");
7510
7511 /* Add FOR [KEY] UPDATE/SHARE clause if present */
7512 if (confl->lockStrength != LCS_NONE)
7514
7515 /* Add a WHERE clause if given */
7516 if (confl->onConflictWhere != NULL)
7517 {
7518 appendContextKeyword(context, " WHERE ",
7520 get_rule_expr(confl->onConflictWhere, context, false);
7521 }
7522 }
7523 }
7524
7525 /* Add RETURNING if present */
7526 if (query->returningList)
7527 get_returning_clause(query, context);
7528}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, elog, ERROR, fb(), generate_relation_name(), get_attname(), get_constraint_name(), get_lock_clause_strength(), get_query_def(), get_returning_clause(), get_rte_alias(), get_rule_expr(), get_rule_list_toplevel(), get_update_query_targetlist_def(), get_values_def(), get_with_clause(), deparse_context::indentLevel, lappend(), LCS_NONE, lfirst, deparse_context::namespaces, NIL, OidIsValid, Query::onConflict, ONCONFLICT_NOTHING, ONCONFLICT_SELECT, ONCONFLICT_UPDATE, OVERRIDING_SYSTEM_VALUE, OVERRIDING_USER_VALUE, PRETTY_INDENT, deparse_context::prettyFlags, PRETTYINDENT_STD, processIndirection(), quote_identifier(), Query::returningList, rt_fetch, Query::rtable, RTE_RELATION, RTE_SUBQUERY, RTE_VALUES, Query::targetList, deparse_context::varprefix, and deparse_context::wrapColumn.

Referenced by get_query_def().

◆ get_json_agg_constructor()

static void get_json_agg_constructor ( JsonConstructorExpr ctor,
deparse_context context,
const char funcname,
bool  is_json_objectagg 
)
static

Definition at line 12376 of file ruleutils.c.

12378{
12380
12383
12384 if (IsA(ctor->func, Aggref))
12385 get_agg_expr_helper((Aggref *) ctor->func, context,
12386 (Aggref *) ctor->func,
12388 else if (IsA(ctor->func, WindowFunc))
12389 get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
12390 funcname, options.data,
12392 else
12393 elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
12394 nodeTag(ctor->func));
12395}

References elog, ERROR, fb(), funcname, get_agg_expr_helper(), get_json_constructor_options(), get_windowfunc_expr_helper(), initStringInfo(), IsA, and nodeTag.

Referenced by get_json_constructor().

◆ get_json_behavior()

static void get_json_behavior ( JsonBehavior behavior,
deparse_context context,
const char on 
)
static

Definition at line 9738 of file ruleutils.c.

9740{
9741 /*
9742 * The order of array elements must correspond to the order of
9743 * JsonBehaviorType members.
9744 */
9745 const char *behavior_names[] =
9746 {
9747 " NULL",
9748 " ERROR",
9749 " EMPTY",
9750 " TRUE",
9751 " FALSE",
9752 " UNKNOWN",
9753 " EMPTY ARRAY",
9754 " EMPTY OBJECT",
9755 " DEFAULT "
9756 };
9757
9758 if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
9759 elog(ERROR, "invalid json behavior type: %d", behavior->btype);
9760
9761 appendStringInfoString(context->buf, behavior_names[behavior->btype]);
9762
9763 if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
9764 get_rule_expr(behavior->expr, context, false);
9765
9766 appendStringInfo(context->buf, " ON %s", on);
9767}

References appendStringInfo(), appendStringInfoString(), JsonBehavior::btype, deparse_context::buf, elog, ERROR, JsonBehavior::expr, fb(), get_rule_expr(), JSON_BEHAVIOR_DEFAULT, and lengthof.

Referenced by get_json_expr_options(), and get_json_table().

◆ get_json_constructor()

static void get_json_constructor ( JsonConstructorExpr ctor,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 12265 of file ruleutils.c.

12267{
12268 StringInfo buf = context->buf;
12269 const char *funcname;
12270 bool is_json_object;
12271 int curridx;
12272 ListCell *lc;
12273
12274 if (ctor->type == JSCTOR_JSON_OBJECTAGG)
12275 {
12276 get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
12277 return;
12278 }
12279 else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
12280 {
12281 get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
12282 return;
12283 }
12284 else if (ctor->type == JSCTOR_JSON_ARRAY_QUERY)
12285 {
12286 Query *query = castNode(Query, ctor->orig_query);
12287
12288 appendStringInfo(buf, "JSON_ARRAY(");
12289
12290 get_query_def(query, buf, context->namespaces, NULL, false,
12291 context->prettyFlags, context->wrapColumn,
12292 context->indentLevel);
12293
12296
12297 return;
12298 }
12299
12300 switch (ctor->type)
12301 {
12302 case JSCTOR_JSON_OBJECT:
12303 funcname = "JSON_OBJECT";
12304 break;
12305 case JSCTOR_JSON_ARRAY:
12306 funcname = "JSON_ARRAY";
12307 break;
12308 case JSCTOR_JSON_PARSE:
12309 funcname = "JSON";
12310 break;
12311 case JSCTOR_JSON_SCALAR:
12312 funcname = "JSON_SCALAR";
12313 break;
12315 funcname = "JSON_SERIALIZE";
12316 break;
12317 default:
12318 elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
12319 }
12320
12321 appendStringInfo(buf, "%s(", funcname);
12322
12324 foreach(lc, ctor->args)
12325 {
12327 if (curridx > 0)
12328 {
12329 const char *sep;
12330
12331 sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
12333 }
12334
12335 get_rule_expr((Node *) lfirst(lc), context, true);
12336 }
12337
12340}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, castNode, elog, ERROR, fb(), foreach_current_index, funcname, get_json_agg_constructor(), get_json_constructor_options(), get_query_def(), get_rule_expr(), deparse_context::indentLevel, JSCTOR_JSON_ARRAY, JSCTOR_JSON_ARRAY_QUERY, JSCTOR_JSON_ARRAYAGG, JSCTOR_JSON_OBJECT, JSCTOR_JSON_OBJECTAGG, JSCTOR_JSON_PARSE, JSCTOR_JSON_SCALAR, JSCTOR_JSON_SERIALIZE, lfirst, deparse_context::namespaces, deparse_context::prettyFlags, and deparse_context::wrapColumn.

Referenced by get_rule_expr().

◆ get_json_constructor_options()

static void get_json_constructor_options ( JsonConstructorExpr ctor,
StringInfo  buf 
)
static

Definition at line 12346 of file ruleutils.c.

12347{
12348 if (ctor->absent_on_null)
12349 {
12350 if (ctor->type == JSCTOR_JSON_OBJECT ||
12351 ctor->type == JSCTOR_JSON_OBJECTAGG)
12352 appendStringInfoString(buf, " ABSENT ON NULL");
12353 }
12354 else
12355 {
12356 if (ctor->type == JSCTOR_JSON_ARRAY ||
12357 ctor->type == JSCTOR_JSON_ARRAYAGG)
12358 appendStringInfoString(buf, " NULL ON NULL");
12359 }
12360
12361 if (ctor->unique)
12362 appendStringInfoString(buf, " WITH UNIQUE KEYS");
12363
12364 /*
12365 * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
12366 * support one.
12367 */
12368 if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
12369 get_json_returning(ctor->returning, buf, true);
12370}

References appendStringInfoString(), buf, fb(), get_json_returning(), JSCTOR_JSON_ARRAY, JSCTOR_JSON_ARRAYAGG, JSCTOR_JSON_OBJECT, JSCTOR_JSON_OBJECTAGG, JSCTOR_JSON_PARSE, and JSCTOR_JSON_SCALAR.

Referenced by get_json_agg_constructor(), and get_json_constructor().

◆ get_json_expr_options()

static void get_json_expr_options ( JsonExpr jsexpr,
deparse_context context,
JsonBehaviorType  default_behavior 
)
static

Definition at line 9776 of file ruleutils.c.

9778{
9779 if (jsexpr->op == JSON_QUERY_OP)
9780 {
9781 if (jsexpr->wrapper == JSW_CONDITIONAL)
9782 appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
9783 else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
9784 appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
9785 /* The default */
9786 else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
9787 appendStringInfoString(context->buf, " WITHOUT WRAPPER");
9788
9789 if (jsexpr->omit_quotes)
9790 appendStringInfoString(context->buf, " OMIT QUOTES");
9791 /* The default */
9792 else
9793 appendStringInfoString(context->buf, " KEEP QUOTES");
9794 }
9795
9796 if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
9797 get_json_behavior(jsexpr->on_empty, context, "EMPTY");
9798
9799 if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
9800 get_json_behavior(jsexpr->on_error, context, "ERROR");
9801}

References appendStringInfoString(), JsonBehavior::btype, deparse_context::buf, fb(), get_json_behavior(), JSON_QUERY_OP, JSW_CONDITIONAL, JSW_NONE, JSW_UNCONDITIONAL, JSW_UNSPEC, JsonExpr::omit_quotes, JsonExpr::on_empty, JsonExpr::on_error, JsonExpr::op, and JsonExpr::wrapper.

Referenced by get_json_table_columns(), and get_rule_expr().

◆ get_json_format()

static void get_json_format ( JsonFormat format,
StringInfo  buf 
)
static

Definition at line 12220 of file ruleutils.c.

12221{
12222 if (format->format_type == JS_FORMAT_DEFAULT)
12223 return;
12224
12226 format->format_type == JS_FORMAT_JSONB ?
12227 " FORMAT JSONB" : " FORMAT JSON");
12228
12229 if (format->encoding != JS_ENC_DEFAULT)
12230 {
12231 const char *encoding;
12232
12233 encoding =
12234 format->encoding == JS_ENC_UTF16 ? "UTF16" :
12235 format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
12236
12237 appendStringInfo(buf, " ENCODING %s", encoding);
12238 }
12239}

References appendStringInfo(), appendStringInfoString(), buf, encoding, format, JS_ENC_DEFAULT, JS_ENC_UTF16, JS_ENC_UTF32, JS_FORMAT_DEFAULT, and JS_FORMAT_JSONB.

Referenced by get_json_returning(), and get_rule_expr().

◆ get_json_path_spec()

static void get_json_path_spec ( Node path_spec,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 12208 of file ruleutils.c.

12209{
12210 if (IsA(path_spec, Const))
12211 get_const_expr((Const *) path_spec, context, -1);
12212 else
12213 get_rule_expr(path_spec, context, showimplicit);
12214}

References fb(), get_const_expr(), get_rule_expr(), and IsA.

Referenced by get_json_table_columns(), and get_rule_expr().

◆ get_json_returning()

static void get_json_returning ( JsonReturning returning,
StringInfo  buf,
bool  json_format_by_default 
)
static

Definition at line 12245 of file ruleutils.c.

12247{
12248 if (!OidIsValid(returning->typid))
12249 return;
12250
12251 appendStringInfo(buf, " RETURNING %s",
12253 returning->typmod));
12254
12256 returning->format->format_type !=
12257 (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
12258 get_json_format(returning->format, buf);
12259}

References appendStringInfo(), buf, fb(), JsonReturning::format, JsonFormat::format_type, format_type_with_typemod(), get_json_format(), JS_FORMAT_JSON, JS_FORMAT_JSONB, OidIsValid, JsonReturning::typid, and JsonReturning::typmod.

Referenced by get_json_constructor_options(), and get_rule_expr().

◆ get_json_table()

static void get_json_table ( TableFunc tf,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 12788 of file ruleutils.c.

12789{
12790 StringInfo buf = context->buf;
12793
12794 appendStringInfoString(buf, "JSON_TABLE(");
12795
12796 if (PRETTY_INDENT(context))
12797 context->indentLevel += PRETTYINDENT_VAR;
12798
12799 appendContextKeyword(context, "", 0, 0, 0);
12800
12801 get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12802
12804
12805 get_const_expr(root->path->value, context, -1);
12806
12807 appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12808
12809 if (jexpr->passing_values)
12810 {
12811 ListCell *lc1,
12812 *lc2;
12813 bool needcomma = false;
12814
12816 appendContextKeyword(context, "PASSING ", 0, 0, 0);
12817
12818 if (PRETTY_INDENT(context))
12819 context->indentLevel += PRETTYINDENT_VAR;
12820
12821 forboth(lc1, jexpr->passing_names,
12822 lc2, jexpr->passing_values)
12823 {
12824 if (needcomma)
12826 needcomma = true;
12827
12828 appendContextKeyword(context, "", 0, 0, 0);
12829
12830 get_rule_expr((Node *) lfirst(lc2), context, false);
12831 appendStringInfo(buf, " AS %s",
12833 );
12834 }
12835
12836 if (PRETTY_INDENT(context))
12837 context->indentLevel -= PRETTYINDENT_VAR;
12838 }
12839
12840 get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12841 showimplicit);
12842
12843 if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12844 get_json_behavior(jexpr->on_error, context, "ERROR");
12845
12846 if (PRETTY_INDENT(context))
12847 context->indentLevel -= PRETTYINDENT_VAR;
12848
12849 appendContextKeyword(context, ")", 0, 0, 0);
12850}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, castNode, TableFunc::docexpr, fb(), forboth, get_const_expr(), get_json_behavior(), get_json_table_columns(), get_rule_expr(), deparse_context::indentLevel, JSON_BEHAVIOR_EMPTY_ARRAY, lfirst, lfirst_node, PRETTY_INDENT, PRETTYINDENT_VAR, quote_identifier(), and root.

Referenced by get_tablefunc().

◆ get_json_table_columns()

static void get_json_table_columns ( TableFunc tf,
JsonTablePathScan scan,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 12682 of file ruleutils.c.

12685{
12686 StringInfo buf = context->buf;
12691 int colnum = 0;
12692
12694 appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12695
12696 if (PRETTY_INDENT(context))
12697 context->indentLevel += PRETTYINDENT_VAR;
12698
12699 forfour(lc_colname, tf->colnames,
12700 lc_coltype, tf->coltypes,
12701 lc_coltypmod, tf->coltypmods,
12702 lc_colvalexpr, tf->colvalexprs)
12703 {
12704 char *colname = strVal(lfirst(lc_colname));
12705 JsonExpr *colexpr;
12706 Oid typid;
12707 int32 typmod;
12708 bool ordinality;
12710
12711 typid = lfirst_oid(lc_coltype);
12712 typmod = lfirst_int(lc_coltypmod);
12714
12715 /* Skip columns that don't belong to this scan. */
12716 if (scan->colMin < 0 || colnum < scan->colMin)
12717 {
12718 colnum++;
12719 continue;
12720 }
12721 if (colnum > scan->colMax)
12722 break;
12723
12724 if (colnum > scan->colMin)
12726
12727 colnum++;
12728
12729 ordinality = !colexpr;
12730
12731 appendContextKeyword(context, "", 0, 0, 0);
12732
12733 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12734 ordinality ? "FOR ORDINALITY" :
12735 format_type_with_typemod(typid, typmod));
12736 if (ordinality)
12737 continue;
12738
12739 /*
12740 * Set default_behavior to guide get_json_expr_options() on whether to
12741 * emit the ON ERROR / EMPTY clauses.
12742 */
12743 if (colexpr->op == JSON_EXISTS_OP)
12744 {
12745 appendStringInfoString(buf, " EXISTS");
12747 }
12748 else
12749 {
12750 if (colexpr->op == JSON_QUERY_OP)
12751 {
12752 char typcategory;
12753 bool typispreferred;
12754
12756
12759 colexpr->format->format_type == JS_FORMAT_JSONB ?
12760 " FORMAT JSONB" : " FORMAT JSON");
12761 }
12762
12764 }
12765
12766 appendStringInfoString(buf, " PATH ");
12767
12768 get_json_path_spec(colexpr->path_spec, context, showimplicit);
12769
12770 get_json_expr_options(colexpr, context, default_behavior);
12771 }
12772
12773 if (scan->child)
12775 scan->colMin >= 0);
12776
12777 if (PRETTY_INDENT(context))
12778 context->indentLevel -= PRETTYINDENT_VAR;
12779
12780 appendContextKeyword(context, ")", 0, 0, 0);
12781}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, castNode, JsonTablePathScan::child, JsonTablePathScan::colMax, JsonTablePathScan::colMin, fb(), forfour, JsonExpr::format, JsonFormat::format_type, format_type_with_typemod(), get_json_expr_options(), get_json_path_spec(), get_json_table_nested_columns(), get_type_category_preferred(), deparse_context::indentLevel, JS_FORMAT_JSONB, JSON_BEHAVIOR_FALSE, JSON_BEHAVIOR_NULL, JSON_EXISTS_OP, JSON_QUERY_OP, lfirst, lfirst_int, lfirst_oid, JsonExpr::op, JsonExpr::path_spec, PRETTY_INDENT, PRETTYINDENT_VAR, quote_identifier(), and strVal.

Referenced by get_json_table(), and get_json_table_nested_columns().

◆ get_json_table_nested_columns()

static void get_json_table_nested_columns ( TableFunc tf,
JsonTablePlan plan,
deparse_context context,
bool  showimplicit,
bool  needcomma 
)
static

Definition at line 12650 of file ruleutils.c.

12653{
12655 {
12657
12658 if (needcomma)
12659 appendStringInfoChar(context->buf, ',');
12660
12661 appendStringInfoChar(context->buf, ' ');
12662 appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
12663 get_const_expr(scan->path->value, context, -1);
12664 appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
12665 get_json_table_columns(tf, scan, context, showimplicit);
12666 }
12667 else if (IsA(plan, JsonTableSiblingJoin))
12668 {
12670
12672 needcomma);
12674 true);
12675 }
12676}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), deparse_context::buf, castNode, fb(), get_const_expr(), get_json_table_columns(), get_json_table_nested_columns(), IsA, JsonTableSiblingJoin::lplan, JsonTablePath::name, JsonTablePathScan::path, plan, quote_identifier(), JsonTableSiblingJoin::rplan, and JsonTablePath::value.

Referenced by get_json_table_columns(), and get_json_table_nested_columns().

◆ get_lock_clause_strength()

static char * get_lock_clause_strength ( LockClauseStrength  strength)
static

Definition at line 6378 of file ruleutils.c.

6379{
6380 switch (strength)
6381 {
6382 case LCS_NONE:
6383 /* we intentionally throw an error for LCS_NONE */
6384 elog(ERROR, "unrecognized LockClauseStrength %d",
6385 (int) strength);
6386 break;
6387 case LCS_FORKEYSHARE:
6388 return " FOR KEY SHARE";
6389 case LCS_FORSHARE:
6390 return " FOR SHARE";
6391 case LCS_FORNOKEYUPDATE:
6392 return " FOR NO KEY UPDATE";
6393 case LCS_FORUPDATE:
6394 return " FOR UPDATE";
6395 }
6396 return NULL; /* keep compiler quiet */
6397}

References elog, ERROR, fb(), LCS_FORKEYSHARE, LCS_FORNOKEYUPDATE, LCS_FORSHARE, LCS_FORUPDATE, and LCS_NONE.

Referenced by get_insert_query_def(), and get_select_query_def().

◆ get_merge_query_def()

static void get_merge_query_def ( Query query,
deparse_context context 
)
static

Definition at line 7793 of file ruleutils.c.

7794{
7795 StringInfo buf = context->buf;
7797 ListCell *lc;
7799
7800 /* Insert the WITH clause if given */
7801 get_with_clause(query, context);
7802
7803 /*
7804 * Start the query with MERGE INTO relname
7805 */
7806 rte = rt_fetch(query->resultRelation, query->rtable);
7807 Assert(rte->rtekind == RTE_RELATION);
7808 if (PRETTY_INDENT(context))
7809 {
7811 context->indentLevel += PRETTYINDENT_STD;
7812 }
7813 appendStringInfo(buf, "MERGE INTO %s%s",
7815 generate_relation_name(rte->relid, NIL));
7816
7817 /* Print the relation alias, if needed */
7818 get_rte_alias(rte, query->resultRelation, false, context);
7819
7820 /* Print the source relation and join clause */
7821 get_from_clause(query, " USING ", context);
7822 appendContextKeyword(context, " ON ",
7824 get_rule_expr(query->mergeJoinCondition, context, false);
7825
7826 /*
7827 * Test for any NOT MATCHED BY SOURCE actions. If there are none, then
7828 * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
7829 * SQL standard. Otherwise, we have a non-SQL-standard query, so output
7830 * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
7831 * more explicit.
7832 */
7833 haveNotMatchedBySource = false;
7834 foreach(lc, query->mergeActionList)
7835 {
7837
7838 if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
7839 {
7841 break;
7842 }
7843 }
7844
7845 /* Print each merge action */
7846 foreach(lc, query->mergeActionList)
7847 {
7849
7850 appendContextKeyword(context, " WHEN ",
7852 switch (action->matchKind)
7853 {
7854 case MERGE_WHEN_MATCHED:
7855 appendStringInfoString(buf, "MATCHED");
7856 break;
7858 appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
7859 break;
7862 appendStringInfoString(buf, "NOT MATCHED BY TARGET");
7863 else
7864 appendStringInfoString(buf, "NOT MATCHED");
7865 break;
7866 default:
7867 elog(ERROR, "unrecognized matchKind: %d",
7868 (int) action->matchKind);
7869 }
7870
7871 if (action->qual)
7872 {
7873 appendContextKeyword(context, " AND ",
7875 get_rule_expr(action->qual, context, false);
7876 }
7877 appendContextKeyword(context, " THEN ",
7879
7880 if (action->commandType == CMD_INSERT)
7881 {
7882 /* This generally matches get_insert_query_def() */
7884 const char *sep = "";
7885 ListCell *lc2;
7886
7887 appendStringInfoString(buf, "INSERT");
7888
7889 if (action->targetList)
7891 foreach(lc2, action->targetList)
7892 {
7894
7895 Assert(!tle->resjunk);
7896
7898 sep = ", ";
7899
7902 tle->resno,
7903 false)));
7905 processIndirection((Node *) tle->expr,
7906 context));
7907 }
7908 if (action->targetList)
7910
7911 if (action->override)
7912 {
7913 if (action->override == OVERRIDING_SYSTEM_VALUE)
7914 appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
7915 else if (action->override == OVERRIDING_USER_VALUE)
7916 appendStringInfoString(buf, " OVERRIDING USER VALUE");
7917 }
7918
7919 if (strippedexprs)
7920 {
7921 appendContextKeyword(context, " VALUES (",
7923 get_rule_list_toplevel(strippedexprs, context, false);
7925 }
7926 else
7927 appendStringInfoString(buf, " DEFAULT VALUES");
7928 }
7929 else if (action->commandType == CMD_UPDATE)
7930 {
7931 appendStringInfoString(buf, "UPDATE SET ");
7932 get_update_query_targetlist_def(query, action->targetList,
7933 context, rte);
7934 }
7935 else if (action->commandType == CMD_DELETE)
7936 appendStringInfoString(buf, "DELETE");
7937 else if (action->commandType == CMD_NOTHING)
7938 appendStringInfoString(buf, "DO NOTHING");
7939 }
7940
7941 /* Add RETURNING if present */
7942 if (query->returningList)
7943 get_returning_clause(query, context);
7944}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_UPDATE, elog, ERROR, fb(), generate_relation_name(), get_attname(), get_from_clause(), get_returning_clause(), get_rte_alias(), get_rule_expr(), get_rule_list_toplevel(), get_update_query_targetlist_def(), get_with_clause(), deparse_context::indentLevel, lappend(), lfirst, lfirst_node, MERGE_WHEN_MATCHED, MERGE_WHEN_NOT_MATCHED_BY_SOURCE, MERGE_WHEN_NOT_MATCHED_BY_TARGET, Query::mergeActionList, Query::mergeJoinCondition, NIL, only_marker, OVERRIDING_SYSTEM_VALUE, OVERRIDING_USER_VALUE, PRETTY_INDENT, PRETTYINDENT_STD, processIndirection(), quote_identifier(), Query::returningList, rt_fetch, Query::rtable, and RTE_RELATION.

Referenced by get_query_def().

◆ get_name_for_var_field()

static const char * get_name_for_var_field ( Var var,
int  fieldno,
int  levelsup,
deparse_context context 
)
static

Definition at line 8573 of file ruleutils.c.

8575{
8578 int netlevelsup;
8580 int varno;
8581 AttrNumber varattno;
8583 Node *expr;
8584
8585 /*
8586 * If it's a RowExpr that was expanded from a whole-row Var, use the
8587 * column names attached to it. (We could let get_expr_result_tupdesc()
8588 * handle this, but it's much cheaper to just pull out the name we need.)
8589 */
8590 if (IsA(var, RowExpr))
8591 {
8592 RowExpr *r = (RowExpr *) var;
8593
8594 if (fieldno > 0 && fieldno <= list_length(r->colnames))
8595 return strVal(list_nth(r->colnames, fieldno - 1));
8596 }
8597
8598 /*
8599 * If it's a Param of type RECORD, try to find what the Param refers to.
8600 */
8601 if (IsA(var, Param))
8602 {
8603 Param *param = (Param *) var;
8605
8606 expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8607 if (expr)
8608 {
8609 /* Found a match, so recurse to decipher the field name */
8611 const char *result;
8612
8615 0, context);
8617 return result;
8618 }
8619 }
8620
8621 /*
8622 * If it's a Var of type RECORD, we have to find what the Var refers to;
8623 * if not, we can use get_expr_result_tupdesc().
8624 */
8625 if (!IsA(var, Var) ||
8626 var->vartype != RECORDOID)
8627 {
8628 tupleDesc = get_expr_result_tupdesc((Node *) var, false);
8629 /* Got the tupdesc, so we can extract the field name */
8632 }
8633
8634 /* Find appropriate nesting depth */
8635 netlevelsup = var->varlevelsup + levelsup;
8636 if (netlevelsup >= list_length(context->namespaces))
8637 elog(ERROR, "bogus varlevelsup: %d offset %d",
8638 var->varlevelsup, levelsup);
8640 netlevelsup);
8641
8642 /*
8643 * If we have a syntactic referent for the Var, and we're working from a
8644 * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8645 * on the semantic referent. (See comments in get_variable().)
8646 */
8647 if (var->varnosyn > 0 && dpns->plan == NULL)
8648 {
8649 varno = var->varnosyn;
8650 varattno = var->varattnosyn;
8651 }
8652 else
8653 {
8654 varno = var->varno;
8655 varattno = var->varattno;
8656 }
8657
8658 /*
8659 * Try to find the relevant RTE in this rtable. In a plan tree, it's
8660 * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8661 * down into the subplans, or INDEX_VAR, which is resolved similarly.
8662 *
8663 * Note: unlike get_variable and resolve_special_varno, we need not worry
8664 * about inheritance mapping: a child Var should have the same datatype as
8665 * its parent, and here we're really only interested in the Var's type.
8666 */
8667 if (varno >= 1 && varno <= list_length(dpns->rtable))
8668 {
8669 rte = rt_fetch(varno, dpns->rtable);
8670 attnum = varattno;
8671 }
8672 else if (varno == OUTER_VAR && dpns->outer_tlist)
8673 {
8676 const char *result;
8677
8678 tle = get_tle_by_resno(dpns->outer_tlist, varattno);
8679 if (!tle)
8680 elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
8681
8682 Assert(netlevelsup == 0);
8683 push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8684
8686 levelsup, context);
8687
8689 return result;
8690 }
8691 else if (varno == INNER_VAR && dpns->inner_tlist)
8692 {
8695 const char *result;
8696
8697 tle = get_tle_by_resno(dpns->inner_tlist, varattno);
8698 if (!tle)
8699 elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
8700
8701 Assert(netlevelsup == 0);
8702 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8703
8705 levelsup, context);
8706
8708 return result;
8709 }
8710 else if (varno == INDEX_VAR && dpns->index_tlist)
8711 {
8713 const char *result;
8714
8715 tle = get_tle_by_resno(dpns->index_tlist, varattno);
8716 if (!tle)
8717 elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
8718
8719 Assert(netlevelsup == 0);
8720
8722 levelsup, context);
8723
8724 return result;
8725 }
8726 else
8727 {
8728 elog(ERROR, "bogus varno: %d", varno);
8729 return NULL; /* keep compiler quiet */
8730 }
8731
8733 {
8734 /* Var is whole-row reference to RTE, so select the right field */
8736 }
8737
8738 /*
8739 * This part has essentially the same logic as the parser's
8740 * expandRecordVariable() function, but we are dealing with a different
8741 * representation of the input context, and we only need one field name
8742 * not a TupleDesc. Also, we need special cases for finding subquery and
8743 * CTE subplans when deparsing Plan trees.
8744 */
8745 expr = (Node *) var; /* default if we can't drill down */
8746
8747 switch (rte->rtekind)
8748 {
8749 case RTE_RELATION:
8750 case RTE_VALUES:
8752 case RTE_GRAPH_TABLE:
8753 case RTE_RESULT:
8754
8755 /*
8756 * This case should not occur: a column of a table, values list,
8757 * or ENR shouldn't have type RECORD. Fall through and fail (most
8758 * likely) at the bottom.
8759 */
8760 break;
8761 case RTE_SUBQUERY:
8762 /* Subselect-in-FROM: examine sub-select's output expr */
8763 {
8764 if (rte->subquery)
8765 {
8766 TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
8767 attnum);
8768
8769 if (ste == NULL || ste->resjunk)
8770 elog(ERROR, "subquery %s does not have attribute %d",
8771 rte->eref->aliasname, attnum);
8772 expr = (Node *) ste->expr;
8773 if (IsA(expr, Var))
8774 {
8775 /*
8776 * Recurse into the sub-select to see what its Var
8777 * refers to. We have to build an additional level of
8778 * namespace to keep in step with varlevelsup in the
8779 * subselect; furthermore, the subquery RTE might be
8780 * from an outer query level, in which case the
8781 * namespace for the subselect must have that outer
8782 * level as parent namespace.
8783 */
8784 List *save_nslist = context->namespaces;
8787 const char *result;
8788
8790 netlevelsup);
8791
8792 set_deparse_for_query(&mydpns, rte->subquery,
8794
8796
8798 0, context);
8799
8800 context->namespaces = save_nslist;
8801
8802 return result;
8803 }
8804 /* else fall through to inspect the expression */
8805 }
8806 else
8807 {
8808 /*
8809 * We're deparsing a Plan tree so we don't have complete
8810 * RTE entries (in particular, rte->subquery is NULL). But
8811 * the only place we'd normally see a Var directly
8812 * referencing a SUBQUERY RTE is in a SubqueryScan plan
8813 * node, and we can look into the child plan's tlist
8814 * instead. An exception occurs if the subquery was
8815 * proven empty and optimized away: then we'd find such a
8816 * Var in a childless Result node, and there's nothing in
8817 * the plan tree that would let us figure out what it had
8818 * originally referenced. In that case, fall back on
8819 * printing "fN", analogously to the default column names
8820 * for RowExprs.
8821 */
8824 const char *result;
8825
8826 if (!dpns->inner_plan)
8827 {
8828 char *dummy_name = palloc(32);
8829
8830 Assert(dpns->plan && IsA(dpns->plan, Result));
8831 snprintf(dummy_name, 32, "f%d", fieldno);
8832 return dummy_name;
8833 }
8834 Assert(dpns->plan && IsA(dpns->plan, SubqueryScan));
8835
8836 tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8837 if (!tle)
8838 elog(ERROR, "bogus varattno for subquery var: %d",
8839 attnum);
8840 Assert(netlevelsup == 0);
8841 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8842
8844 levelsup, context);
8845
8847 return result;
8848 }
8849 }
8850 break;
8851 case RTE_JOIN:
8852 /* Join RTE --- recursively inspect the alias variable */
8853 if (rte->joinaliasvars == NIL)
8854 elog(ERROR, "cannot decompile join alias var in plan tree");
8855 Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
8856 expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
8857 Assert(expr != NULL);
8858 /* we intentionally don't strip implicit coercions here */
8859 if (IsA(expr, Var))
8860 return get_name_for_var_field((Var *) expr, fieldno,
8861 var->varlevelsup + levelsup,
8862 context);
8863 /* else fall through to inspect the expression */
8864 break;
8865 case RTE_FUNCTION:
8866 case RTE_TABLEFUNC:
8867
8868 /*
8869 * We couldn't get here unless a function is declared with one of
8870 * its result columns as RECORD, which is not allowed.
8871 */
8872 break;
8873 case RTE_CTE:
8874 /* CTE reference: examine subquery's output expr */
8875 {
8876 CommonTableExpr *cte = NULL;
8877 Index ctelevelsup;
8878 ListCell *lc;
8879
8880 /*
8881 * Try to find the referenced CTE using the namespace stack.
8882 */
8883 ctelevelsup = rte->ctelevelsup + netlevelsup;
8884 if (ctelevelsup >= list_length(context->namespaces))
8885 lc = NULL;
8886 else
8887 {
8889
8891 list_nth(context->namespaces, ctelevelsup);
8892 foreach(lc, ctedpns->ctes)
8893 {
8894 cte = (CommonTableExpr *) lfirst(lc);
8895 if (strcmp(cte->ctename, rte->ctename) == 0)
8896 break;
8897 }
8898 }
8899 if (lc != NULL)
8900 {
8901 Query *ctequery = (Query *) cte->ctequery;
8903 attnum);
8904
8905 if (ste == NULL || ste->resjunk)
8906 elog(ERROR, "CTE %s does not have attribute %d",
8907 rte->eref->aliasname, attnum);
8908 expr = (Node *) ste->expr;
8909 if (IsA(expr, Var))
8910 {
8911 /*
8912 * Recurse into the CTE to see what its Var refers to.
8913 * We have to build an additional level of namespace
8914 * to keep in step with varlevelsup in the CTE;
8915 * furthermore it could be an outer CTE (compare
8916 * SUBQUERY case above).
8917 */
8918 List *save_nslist = context->namespaces;
8921 const char *result;
8922
8924 ctelevelsup);
8925
8926 set_deparse_for_query(&mydpns, ctequery,
8928
8930
8932 0, context);
8933
8934 context->namespaces = save_nslist;
8935
8936 return result;
8937 }
8938 /* else fall through to inspect the expression */
8939 }
8940 else
8941 {
8942 /*
8943 * We're deparsing a Plan tree so we don't have a CTE
8944 * list. But the only places we'd normally see a Var
8945 * directly referencing a CTE RTE are in CteScan or
8946 * WorkTableScan plan nodes. For those cases,
8947 * set_deparse_plan arranged for dpns->inner_plan to be
8948 * the plan node that emits the CTE or RecursiveUnion
8949 * result, and we can look at its tlist instead. As
8950 * above, this can fail if the CTE has been proven empty,
8951 * in which case fall back to "fN".
8952 */
8955 const char *result;
8956
8957 if (!dpns->inner_plan)
8958 {
8959 char *dummy_name = palloc(32);
8960
8961 Assert(dpns->plan && IsA(dpns->plan, Result));
8962 snprintf(dummy_name, 32, "f%d", fieldno);
8963 return dummy_name;
8964 }
8965 Assert(dpns->plan && (IsA(dpns->plan, CteScan) ||
8966 IsA(dpns->plan, WorkTableScan)));
8967
8968 tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8969 if (!tle)
8970 elog(ERROR, "bogus varattno for subquery var: %d",
8971 attnum);
8972 Assert(netlevelsup == 0);
8973 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8974
8976 levelsup, context);
8977
8979 return result;
8980 }
8981 }
8982 break;
8983 case RTE_GROUP:
8984
8985 /*
8986 * We couldn't get here: any Vars that reference the RTE_GROUP RTE
8987 * should have been replaced with the underlying grouping
8988 * expressions.
8989 */
8990 break;
8991 }
8992
8993 /*
8994 * We now have an expression we can't expand any more, so see if
8995 * get_expr_result_tupdesc() can do anything with it.
8996 */
8997 tupleDesc = get_expr_result_tupdesc(expr, false);
8998 /* Got the tupdesc, so we can extract the field name */
9001}

References Assert, attname, attnum, CommonTableExpr::ctename, CommonTableExpr::ctequery, elog, ERROR, fb(), find_param_referent(), get_expr_result_tupdesc(), get_name_for_var_field(), get_rte_attribute_name(), get_tle_by_resno(), GetCTETargetList, INDEX_VAR, INNER_VAR, InvalidAttrNumber, IsA, lcons(), lfirst, list_copy_tail(), list_length(), list_nth(), deparse_context::namespaces, NameStr, NIL, OUTER_VAR, palloc(), pop_ancestor_plan(), pop_child_plan(), push_ancestor_plan(), push_child_plan(), result, rt_fetch, RTE_CTE, RTE_FUNCTION, RTE_GRAPH_TABLE, RTE_GROUP, RTE_JOIN, RTE_NAMEDTUPLESTORE, RTE_RELATION, RTE_RESULT, RTE_SUBQUERY, RTE_TABLEFUNC, RTE_VALUES, set_deparse_for_query(), snprintf, strVal, TupleDescAttr(), Var::varattno, Var::varlevelsup, and Var::varno.

Referenced by get_name_for_var_field(), and get_rule_expr().

◆ get_opclass_name()

static void get_opclass_name ( Oid  opclass,
Oid  actual_datatype,
StringInfo  buf 
)
static

Definition at line 13523 of file ruleutils.c.

13525{
13528 char *opcname;
13529 char *nspname;
13530
13533 elog(ERROR, "cache lookup failed for opclass %u", opclass);
13535
13537 GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
13538 {
13539 /* Okay, we need the opclass name. Do we need to qualify it? */
13540 opcname = NameStr(opcrec->opcname);
13541 if (OpclassIsVisible(opclass))
13543 else
13544 {
13545 nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
13546 appendStringInfo(buf, " %s.%s",
13547 quote_identifier(nspname),
13549 }
13550 }
13552}

References appendStringInfo(), buf, elog, ERROR, fb(), Form_pg_opclass, get_namespace_name_or_temp(), GetDefaultOpClass(), GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), OidIsValid, OpclassIsVisible(), quote_identifier(), ReleaseSysCache(), and SearchSysCache1().

Referenced by generate_opclass_name(), get_rule_expr(), pg_get_indexdef_worker(), and pg_get_partkeydef_worker().

◆ get_oper_expr()

static void get_oper_expr ( OpExpr expr,
deparse_context context 
)
static

Definition at line 11323 of file ruleutils.c.

11324{
11325 StringInfo buf = context->buf;
11326 Oid opno = expr->opno;
11327 List *args = expr->args;
11328
11329 if (!PRETTY_PAREN(context))
11331 if (list_length(args) == 2)
11332 {
11333 /* binary operator */
11334 Node *arg1 = (Node *) linitial(args);
11335 Node *arg2 = (Node *) lsecond(args);
11336
11337 get_rule_expr_paren(arg1, context, true, (Node *) expr);
11338 appendStringInfo(buf, " %s ",
11340 exprType(arg1),
11341 exprType(arg2)));
11342 get_rule_expr_paren(arg2, context, true, (Node *) expr);
11343 }
11344 else
11345 {
11346 /* prefix operator */
11347 Node *arg = (Node *) linitial(args);
11348
11349 appendStringInfo(buf, "%s ",
11351 InvalidOid,
11352 exprType(arg)));
11353 get_rule_expr_paren(arg, context, true, (Node *) expr);
11354 }
11355 if (!PRETTY_PAREN(context))
11357}

References appendStringInfo(), appendStringInfoChar(), arg, OpExpr::args, deparse_context::buf, buf, exprType(), fb(), generate_operator_name(), get_rule_expr_paren(), InvalidOid, linitial, list_length(), lsecond, OpExpr::opno, and PRETTY_PAREN.

Referenced by get_rule_expr().

◆ get_parameter()

static void get_parameter ( Param param,
deparse_context context 
)
static

Definition at line 9244 of file ruleutils.c.

9245{
9246 Node *expr;
9249 SubPlan *subplan;
9250 int column;
9251
9252 /*
9253 * If it's a PARAM_EXEC parameter, try to locate the expression from which
9254 * the parameter was computed. This stanza handles only cases in which
9255 * the Param represents an input to the subplan we are currently in.
9256 */
9257 expr = find_param_referent(param, context, &dpns, &ancestor_cell);
9258 if (expr)
9259 {
9260 /* Found a match, so print it */
9262 bool save_varprefix;
9263 bool need_paren;
9264
9265 /* Switch attention to the ancestor plan node */
9267
9268 /*
9269 * Force prefixing of Vars, since they won't belong to the relation
9270 * being scanned in the original plan node.
9271 */
9272 save_varprefix = context->varprefix;
9273 context->varprefix = true;
9274
9275 /*
9276 * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
9277 * upper-level Param, which wouldn't need extra parentheses.
9278 * Otherwise, insert parens to ensure the expression looks atomic.
9279 */
9280 need_paren = !(IsA(expr, Var) ||
9281 IsA(expr, Aggref) ||
9282 IsA(expr, GroupingFunc) ||
9283 IsA(expr, Param));
9284 if (need_paren)
9285 appendStringInfoChar(context->buf, '(');
9286
9287 get_rule_expr(expr, context, false);
9288
9289 if (need_paren)
9290 appendStringInfoChar(context->buf, ')');
9291
9292 context->varprefix = save_varprefix;
9293
9295
9296 return;
9297 }
9298
9299 /*
9300 * Alternatively, maybe it's a subplan output, which we print as a
9301 * reference to the subplan. (We could drill down into the subplan and
9302 * print the relevant targetlist expression, but that has been deemed too
9303 * confusing since it would violate normal SQL scope rules. Also, we're
9304 * relying on this reference to show that the testexpr containing the
9305 * Param has anything to do with that subplan at all.)
9306 */
9307 subplan = find_param_generator(param, context, &column);
9308 if (subplan)
9309 {
9310 const char *nameprefix;
9311
9312 if (subplan->isInitPlan)
9313 nameprefix = "InitPlan ";
9314 else
9315 nameprefix = "SubPlan ";
9316
9317 appendStringInfo(context->buf, "(%s%s%s).col%d",
9318 subplan->useHashTable ? "hashed " : "",
9319 nameprefix,
9320 subplan->plan_name, column + 1);
9321
9322 return;
9323 }
9324
9325 /*
9326 * If it's an external parameter, see if the outermost namespace provides
9327 * function argument names.
9328 */
9329 if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
9330 {
9331 dpns = llast(context->namespaces);
9332 if (dpns->argnames &&
9333 param->paramid > 0 &&
9334 param->paramid <= dpns->numargs)
9335 {
9336 char *argname = dpns->argnames[param->paramid - 1];
9337
9338 if (argname)
9339 {
9340 bool should_qualify = false;
9341 ListCell *lc;
9342
9343 /*
9344 * Qualify the parameter name if there are any other deparse
9345 * namespaces with range tables. This avoids qualifying in
9346 * trivial cases like "RETURN a + b", but makes it safe in all
9347 * other cases.
9348 */
9349 foreach(lc, context->namespaces)
9350 {
9352
9353 if (depns->rtable_names != NIL)
9354 {
9355 should_qualify = true;
9356 break;
9357 }
9358 }
9359 if (should_qualify)
9360 {
9361 appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
9362 appendStringInfoChar(context->buf, '.');
9363 }
9364
9365 appendStringInfoString(context->buf, quote_identifier(argname));
9366 return;
9367 }
9368 }
9369 }
9370
9371 /*
9372 * Not PARAM_EXEC, or couldn't find referent: just print $N.
9373 *
9374 * It's a bug if we get here for anything except PARAM_EXTERN Params, but
9375 * in production builds printing $N seems more useful than failing.
9376 */
9377 Assert(param->paramkind == PARAM_EXTERN);
9378
9379 appendStringInfo(context->buf, "$%d", param->paramid);
9380}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, fb(), find_param_generator(), find_param_referent(), get_rule_expr(), IsA, SubPlan::isInitPlan, lfirst, llast, deparse_context::namespaces, NIL, PARAM_EXTERN, Param::paramid, Param::paramkind, SubPlan::plan_name, pop_ancestor_plan(), push_ancestor_plan(), quote_identifier(), SubPlan::useHashTable, and deparse_context::varprefix.

Referenced by get_rule_expr().

◆ get_path_pattern_expr_def()

static void get_path_pattern_expr_def ( List path_pattern_expr,
deparse_context context 
)
static

Definition at line 8031 of file ruleutils.c.

8032{
8033 StringInfo buf = context->buf;
8034 ListCell *lc;
8035
8036 foreach(lc, path_pattern_expr)
8037 {
8039 const char *sep = "";
8040
8041 switch (gep->kind)
8042 {
8043 case VERTEX_PATTERN:
8045 break;
8046 case EDGE_PATTERN_LEFT:
8048 break;
8049 case EDGE_PATTERN_RIGHT:
8050 case EDGE_PATTERN_ANY:
8052 break;
8053 case PAREN_EXPR:
8055 break;
8056 }
8057
8058 if (gep->variable)
8059 {
8061 sep = " ";
8062 }
8063
8064 if (gep->labelexpr)
8065 {
8068 get_graph_label_expr(gep->labelexpr, context);
8069 sep = " ";
8070 }
8071
8072 if (gep->subexpr)
8073 {
8075 get_path_pattern_expr_def(gep->subexpr, context);
8076 sep = " ";
8077 }
8078
8079 if (gep->whereClause)
8080 {
8082 appendStringInfoString(buf, "WHERE ");
8083 get_rule_expr(gep->whereClause, context, false);
8084 }
8085
8086 switch (gep->kind)
8087 {
8088 case VERTEX_PATTERN:
8090 break;
8091 case EDGE_PATTERN_LEFT:
8092 case EDGE_PATTERN_ANY:
8094 break;
8095 case EDGE_PATTERN_RIGHT:
8097 break;
8098 case PAREN_EXPR:
8100 break;
8101 }
8102
8103 if (gep->quantifier)
8104 {
8105 int lower = linitial_int(gep->quantifier);
8106 int upper = lsecond_int(gep->quantifier);
8107
8108 appendStringInfo(buf, "{%d,%d}", lower, upper);
8109 }
8110 }
8111}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, EDGE_PATTERN_ANY, EDGE_PATTERN_LEFT, EDGE_PATTERN_RIGHT, fb(), get_graph_label_expr(), get_path_pattern_expr_def(), get_rule_expr(), lfirst_node, linitial_int, lower(), lsecond_int, PAREN_EXPR, quote_identifier(), upper(), and VERTEX_PATTERN.

Referenced by get_graph_pattern_def(), and get_path_pattern_expr_def().

◆ get_query_def()

static void get_query_def ( Query query,
StringInfo  buf,
List parentnamespace,
TupleDesc  resultDesc,
bool  colNamesVisible,
int  prettyFlags,
int  wrapColumn,
int  startIndent 
)
static

Definition at line 5986 of file ruleutils.c.

5989{
5990 deparse_context context;
5992 int rtable_size;
5993
5994 /* Guard against excessively long or deeply-nested queries */
5997
5998 rtable_size = query->hasGroupRTE ?
5999 list_length(query->rtable) - 1 :
6000 list_length(query->rtable);
6001
6002 /*
6003 * Replace any Vars in the query's targetlist and havingQual that
6004 * reference GROUP outputs with the underlying grouping expressions.
6005 *
6006 * We can safely pass NULL for the root here. Preserving varnullingrels
6007 * makes no difference to the deparsed source text.
6008 */
6009 if (query->hasGroupRTE)
6010 {
6011 query->targetList = (List *)
6012 flatten_group_exprs(NULL, query, (Node *) query->targetList);
6013 query->havingQual =
6014 flatten_group_exprs(NULL, query, query->havingQual);
6015 }
6016
6017 /*
6018 * Before we begin to examine the query, acquire locks on referenced
6019 * relations, and fix up deleted columns in JOIN RTEs. This ensures
6020 * consistent results. Note we assume it's OK to scribble on the passed
6021 * querytree!
6022 *
6023 * We are only deparsing the query (we are not about to execute it), so we
6024 * only need AccessShareLock on the relations it mentions.
6025 */
6026 AcquireRewriteLocks(query, false, false);
6027
6028 context.buf = buf;
6030 context.resultDesc = NULL;
6031 context.targetList = NIL;
6032 context.windowClause = NIL;
6033 context.varprefix = (parentnamespace != NIL ||
6034 rtable_size != 1);
6035 context.prettyFlags = prettyFlags;
6036 context.wrapColumn = wrapColumn;
6037 context.indentLevel = startIndent;
6038 context.colNamesVisible = colNamesVisible;
6039 context.inGroupBy = false;
6040 context.varInOrderBy = false;
6041 context.appendparents = NULL;
6042
6044
6045 switch (query->commandType)
6046 {
6047 case CMD_SELECT:
6048 /* We set context.resultDesc only if it's a SELECT */
6049 context.resultDesc = resultDesc;
6050 get_select_query_def(query, &context);
6051 break;
6052
6053 case CMD_UPDATE:
6054 get_update_query_def(query, &context);
6055 break;
6056
6057 case CMD_INSERT:
6058 get_insert_query_def(query, &context);
6059 break;
6060
6061 case CMD_DELETE:
6062 get_delete_query_def(query, &context);
6063 break;
6064
6065 case CMD_MERGE:
6066 get_merge_query_def(query, &context);
6067 break;
6068
6069 case CMD_NOTHING:
6070 appendStringInfoString(buf, "NOTHING");
6071 break;
6072
6073 case CMD_UTILITY:
6074 get_utility_query_def(query, &context);
6075 break;
6076
6077 default:
6078 elog(ERROR, "unrecognized query command type: %d",
6079 query->commandType);
6080 break;
6081 }
6082}

References AcquireRewriteLocks(), deparse_context::appendparents, appendStringInfoString(), deparse_context::buf, buf, CHECK_FOR_INTERRUPTS, check_stack_depth(), CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_NOTHING, CMD_SELECT, CMD_UPDATE, CMD_UTILITY, deparse_context::colNamesVisible, Query::commandType, elog, ERROR, fb(), flatten_group_exprs(), get_delete_query_def(), get_insert_query_def(), get_merge_query_def(), get_select_query_def(), get_update_query_def(), get_utility_query_def(), Query::havingQual, deparse_context::indentLevel, deparse_context::inGroupBy, lcons(), list_copy(), list_length(), deparse_context::namespaces, NIL, deparse_context::prettyFlags, deparse_context::resultDesc, Query::rtable, set_deparse_for_query(), deparse_context::targetList, Query::targetList, deparse_context::varInOrderBy, deparse_context::varprefix, deparse_context::windowClause, and deparse_context::wrapColumn.

Referenced by get_from_clause_item(), get_insert_query_def(), get_json_constructor(), get_setop_query(), get_sublink_expr(), get_with_clause(), make_ruledef(), make_viewdef(), pg_get_querydef(), and print_function_sqlbody().

◆ get_range_partbound_string()

char * get_range_partbound_string ( List bound_datums)

Definition at line 14332 of file ruleutils.c.

14333{
14334 deparse_context context;
14336 ListCell *cell;
14337 char *sep;
14338
14340 memset(&context, 0, sizeof(deparse_context));
14341 context.buf = &buf;
14342
14344 sep = "";
14345 foreach(cell, bound_datums)
14346 {
14347 PartitionRangeDatum *datum =
14349
14352 appendStringInfoString(&buf, "MINVALUE");
14353 else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
14354 appendStringInfoString(&buf, "MAXVALUE");
14355 else
14356 {
14357 Const *val = castNode(Const, datum->value);
14358
14359 get_const_expr(val, &context, -1);
14360 }
14361 sep = ", ";
14362 }
14364
14365 return buf.data;
14366}

References appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, castNode, fb(), get_const_expr(), initStringInfo(), PartitionRangeDatum::kind, lfirst_node, PARTITION_RANGE_DATUM_MAXVALUE, PARTITION_RANGE_DATUM_MINVALUE, val, and PartitionRangeDatum::value.

Referenced by check_new_partition_bound(), check_partition_bounds_for_split_range(), and get_rule_expr().

◆ get_relation_name()

static char * get_relation_name ( Oid  relid)
static

Definition at line 13795 of file ruleutils.c.

13796{
13797 char *relname = get_rel_name(relid);
13798
13799 if (!relname)
13800 elog(ERROR, "cache lookup failed for relation %u", relid);
13801 return relname;
13802}

References elog, ERROR, get_rel_name(), and relname.

Referenced by get_rte_alias(), make_propgraphdef_properties(), pg_get_constraintdef_worker(), pg_get_indexdef_worker(), pg_get_partition_constraintdef(), pg_get_partkeydef_worker(), pg_get_statisticsobj_worker(), and pg_get_statisticsobjdef_expressions().

◆ get_reloptions()

void get_reloptions ( StringInfo  buf,
Datum  reloptions 
)

Definition at line 14244 of file ruleutils.c.

14245{
14246 Datum *options;
14247 int noptions;
14248 int i;
14249
14251 &options, NULL, &noptions);
14252
14253 for (i = 0; i < noptions; i++)
14254 {
14256 char *name;
14257 char *separator;
14258 char *value;
14259
14260 /*
14261 * Each array element should have the form name=value. If the "=" is
14262 * missing for some reason, treat it like an empty value.
14263 */
14264 name = option;
14265 separator = strchr(option, '=');
14266 if (separator)
14267 {
14268 *separator = '\0';
14269 value = separator + 1;
14270 }
14271 else
14272 value = "";
14273
14274 if (i > 0)
14277
14278 /*
14279 * In general we need to quote the value; but to avoid unnecessary
14280 * clutter, do not quote if it is an identifier that would not need
14281 * quoting. (We could also allow numbers, but that is a bit trickier
14282 * than it looks --- for example, are leading zeroes significant? We
14283 * don't want to assume very much here about what custom reloptions
14284 * might mean.)
14285 */
14288 else
14290
14291 pfree(option);
14292 }
14293}

References appendStringInfo(), appendStringInfoString(), buf, DatumGetArrayTypeP, deconstruct_array_builtin(), fb(), i, name, noptions, pfree(), quote_identifier(), simple_quote_literal(), TextDatumGetCString, and value.

Referenced by flatten_reloptions(), pg_get_indexdef_worker(), and pg_get_tablespace_ddl_internal().

◆ get_returning_clause()

static void get_returning_clause ( Query query,
deparse_context context 
)
static

Definition at line 6743 of file ruleutils.c.

6744{
6745 StringInfo buf = context->buf;
6746
6747 if (query->returningList)
6748 {
6749 bool have_with = false;
6750
6751 appendContextKeyword(context, " RETURNING",
6753
6754 /* Add WITH (OLD/NEW) options, if they're not the defaults */
6755 if (query->returningOldAlias && strcmp(query->returningOldAlias, "old") != 0)
6756 {
6757 appendStringInfo(buf, " WITH (OLD AS %s",
6758 quote_identifier(query->returningOldAlias));
6759 have_with = true;
6760 }
6761 if (query->returningNewAlias && strcmp(query->returningNewAlias, "new") != 0)
6762 {
6763 if (have_with)
6764 appendStringInfo(buf, ", NEW AS %s",
6765 quote_identifier(query->returningNewAlias));
6766 else
6767 {
6768 appendStringInfo(buf, " WITH (NEW AS %s",
6769 quote_identifier(query->returningNewAlias));
6770 have_with = true;
6771 }
6772 }
6773 if (have_with)
6775
6776 /* Add the returning expressions themselves */
6777 get_target_list(query->returningList, context);
6778 }
6779}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), deparse_context::buf, buf, fb(), get_target_list(), PRETTYINDENT_STD, quote_identifier(), and Query::returningList.

Referenced by get_delete_query_def(), get_insert_query_def(), get_merge_query_def(), and get_update_query_def().

◆ get_rtable_name()

static char * get_rtable_name ( int  rtindex,
deparse_context context 
)
static

Definition at line 5495 of file ruleutils.c.

5496{
5498
5499 Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
5500 return (char *) list_nth(dpns->rtable_names, rtindex - 1);
5501}

References Assert, fb(), linitial, list_length(), list_nth(), and deparse_context::namespaces.

Referenced by get_from_clause_item(), get_rte_alias(), and get_select_query_def().

◆ get_rte_alias()

static void get_rte_alias ( RangeTblEntry rte,
int  varno,
bool  use_as,
deparse_context context 
)
static

Definition at line 13284 of file ruleutils.c.

13286{
13288 char *refname = get_rtable_name(varno, context);
13290 bool printalias = false;
13291
13292 if (rte->alias != NULL)
13293 {
13294 /* Always print alias if user provided one */
13295 printalias = true;
13296 }
13297 else if (colinfo->printaliases)
13298 {
13299 /* Always print alias if we need to print column aliases */
13300 printalias = true;
13301 }
13302 else if (rte->rtekind == RTE_RELATION)
13303 {
13304 /*
13305 * No need to print alias if it's same as relation name (this would
13306 * normally be the case, but not if set_rtable_names had to resolve a
13307 * conflict).
13308 */
13309 if (strcmp(refname, get_relation_name(rte->relid)) != 0)
13310 printalias = true;
13311 }
13312 else if (rte->rtekind == RTE_FUNCTION)
13313 {
13314 /*
13315 * For a function RTE, always print alias. This covers possible
13316 * renaming of the function and/or instability of the FigureColname
13317 * rules for things that aren't simple functions. Note we'd need to
13318 * force it anyway for the columndef list case.
13319 */
13320 printalias = true;
13321 }
13322 else if (rte->rtekind == RTE_SUBQUERY ||
13323 rte->rtekind == RTE_VALUES)
13324 {
13325 /*
13326 * For a subquery, always print alias. This makes the output
13327 * SQL-spec-compliant, even though we allow such aliases to be omitted
13328 * on input.
13329 */
13330 printalias = true;
13331 }
13332 else if (rte->rtekind == RTE_CTE)
13333 {
13334 /*
13335 * No need to print alias if it's same as CTE name (this would
13336 * normally be the case, but not if set_rtable_names had to resolve a
13337 * conflict).
13338 */
13339 if (strcmp(refname, rte->ctename) != 0)
13340 printalias = true;
13341 }
13342
13343 if (printalias)
13344 appendStringInfo(context->buf, "%s%s",
13345 use_as ? " AS " : " ",
13346 quote_identifier(refname));
13347}

References appendStringInfo(), deparse_context::buf, deparse_columns_fetch, fb(), get_relation_name(), get_rtable_name(), linitial, deparse_context::namespaces, quote_identifier(), RTE_CTE, RTE_FUNCTION, RTE_RELATION, RTE_SUBQUERY, and RTE_VALUES.

Referenced by get_delete_query_def(), get_from_clause_item(), get_insert_query_def(), get_merge_query_def(), and get_update_query_def().

◆ get_rule_expr()

static void get_rule_expr ( Node node,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 9817 of file ruleutils.c.

9819{
9820 StringInfo buf = context->buf;
9821
9822 if (node == NULL)
9823 return;
9824
9825 /* Guard against excessively long or deeply-nested queries */
9828
9829 /*
9830 * Each level of get_rule_expr must emit an indivisible term
9831 * (parenthesized if necessary) to ensure result is reparsed into the same
9832 * expression tree. The only exception is that when the input is a List,
9833 * we emit the component items comma-separated with no surrounding
9834 * decoration; this is convenient for most callers.
9835 */
9836 switch (nodeTag(node))
9837 {
9838 case T_Var:
9839 (void) get_variable((Var *) node, 0, false, context);
9840 break;
9841
9842 case T_Const:
9843 get_const_expr((Const *) node, context, 0);
9844 break;
9845
9846 case T_Param:
9847 get_parameter((Param *) node, context);
9848 break;
9849
9850 case T_Aggref:
9851 get_agg_expr((Aggref *) node, context, (Aggref *) node);
9852 break;
9853
9854 case T_GroupingFunc:
9855 {
9856 GroupingFunc *gexpr = (GroupingFunc *) node;
9857
9858 appendStringInfoString(buf, "GROUPING(");
9859 get_rule_expr((Node *) gexpr->args, context, true);
9861 }
9862 break;
9863
9864 case T_WindowFunc:
9865 get_windowfunc_expr((WindowFunc *) node, context);
9866 break;
9867
9868 case T_MergeSupportFunc:
9869 appendStringInfoString(buf, "MERGE_ACTION()");
9870 break;
9871
9872 case T_SubscriptingRef:
9873 {
9874 SubscriptingRef *sbsref = (SubscriptingRef *) node;
9875 bool need_parens;
9876
9877 /*
9878 * If the argument is a CaseTestExpr, we must be inside a
9879 * FieldStore, ie, we are assigning to an element of an array
9880 * within a composite column. Since we already punted on
9881 * displaying the FieldStore's target information, just punt
9882 * here too, and display only the assignment source
9883 * expression.
9884 */
9885 if (IsA(sbsref->refexpr, CaseTestExpr))
9886 {
9887 Assert(sbsref->refassgnexpr);
9888 get_rule_expr((Node *) sbsref->refassgnexpr,
9889 context, showimplicit);
9890 break;
9891 }
9892
9893 /*
9894 * Parenthesize the argument unless it's a simple Var or a
9895 * FieldSelect. (In particular, if it's another
9896 * SubscriptingRef, we *must* parenthesize to avoid
9897 * confusion.)
9898 */
9899 need_parens = !IsA(sbsref->refexpr, Var) &&
9900 !IsA(sbsref->refexpr, FieldSelect);
9901 if (need_parens)
9903 get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
9904 if (need_parens)
9906
9907 /*
9908 * If there's a refassgnexpr, we want to print the node in the
9909 * format "container[subscripts] := refassgnexpr". This is
9910 * not legal SQL, so decompilation of INSERT or UPDATE
9911 * statements should always use processIndirection as part of
9912 * the statement-level syntax. We should only see this when
9913 * EXPLAIN tries to print the targetlist of a plan resulting
9914 * from such a statement.
9915 */
9916 if (sbsref->refassgnexpr)
9917 {
9918 Node *refassgnexpr;
9919
9920 /*
9921 * Use processIndirection to print this node's subscripts
9922 * as well as any additional field selections or
9923 * subscripting in immediate descendants. It returns the
9924 * RHS expr that is actually being "assigned".
9925 */
9926 refassgnexpr = processIndirection(node, context);
9927 appendStringInfoString(buf, " := ");
9928 get_rule_expr(refassgnexpr, context, showimplicit);
9929 }
9930 else
9931 {
9932 /* Just an ordinary container fetch, so print subscripts */
9933 printSubscripts(sbsref, context);
9934 }
9935 }
9936 break;
9937
9938 case T_FuncExpr:
9939 get_func_expr((FuncExpr *) node, context, showimplicit);
9940 break;
9941
9942 case T_NamedArgExpr:
9943 {
9944 NamedArgExpr *na = (NamedArgExpr *) node;
9945
9946 appendStringInfo(buf, "%s => ", quote_identifier(na->name));
9947 get_rule_expr((Node *) na->arg, context, showimplicit);
9948 }
9949 break;
9950
9951 case T_OpExpr:
9952 get_oper_expr((OpExpr *) node, context);
9953 break;
9954
9955 case T_DistinctExpr:
9956 {
9957 DistinctExpr *expr = (DistinctExpr *) node;
9958 List *args = expr->args;
9959 Node *arg1 = (Node *) linitial(args);
9960 Node *arg2 = (Node *) lsecond(args);
9961
9962 if (!PRETTY_PAREN(context))
9964 get_rule_expr_paren(arg1, context, true, node);
9965 appendStringInfoString(buf, " IS DISTINCT FROM ");
9966 get_rule_expr_paren(arg2, context, true, node);
9967 if (!PRETTY_PAREN(context))
9969 }
9970 break;
9971
9972 case T_NullIfExpr:
9973 {
9974 NullIfExpr *nullifexpr = (NullIfExpr *) node;
9975
9976 appendStringInfoString(buf, "NULLIF(");
9977 get_rule_expr((Node *) nullifexpr->args, context, true);
9979 }
9980 break;
9981
9983 {
9984 ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
9985 List *args = expr->args;
9986 Node *arg1 = (Node *) linitial(args);
9987 Node *arg2 = (Node *) lsecond(args);
9988
9989 if (!PRETTY_PAREN(context))
9991 get_rule_expr_paren(arg1, context, true, node);
9992 appendStringInfo(buf, " %s %s (",
9994 exprType(arg1),
9996 expr->useOr ? "ANY" : "ALL");
9997 get_rule_expr_paren(arg2, context, true, node);
9998
9999 /*
10000 * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
10001 * a bare sub-SELECT. Since we're here, the sub-SELECT must
10002 * be meant as a scalar sub-SELECT yielding an array value to
10003 * be used in ScalarArrayOpExpr; but the grammar will
10004 * preferentially interpret such a construct as an ANY/ALL
10005 * SubLink. To prevent misparsing the output that way, insert
10006 * a dummy coercion (which will be stripped by parse analysis,
10007 * so no inefficiency is added in dump and reload). This is
10008 * indeed most likely what the user wrote to get the construct
10009 * accepted in the first place.
10010 */
10011 if (IsA(arg2, SubLink) &&
10012 ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
10013 appendStringInfo(buf, "::%s",
10015 exprTypmod(arg2)));
10017 if (!PRETTY_PAREN(context))
10019 }
10020 break;
10021
10022 case T_BoolExpr:
10023 {
10024 BoolExpr *expr = (BoolExpr *) node;
10025 Node *first_arg = linitial(expr->args);
10026 ListCell *arg;
10027
10028 switch (expr->boolop)
10029 {
10030 case AND_EXPR:
10031 if (!PRETTY_PAREN(context))
10034 false, node);
10035 for_each_from(arg, expr->args, 1)
10036 {
10037 appendStringInfoString(buf, " AND ");
10038 get_rule_expr_paren((Node *) lfirst(arg), context,
10039 false, node);
10040 }
10041 if (!PRETTY_PAREN(context))
10043 break;
10044
10045 case OR_EXPR:
10046 if (!PRETTY_PAREN(context))
10049 false, node);
10050 for_each_from(arg, expr->args, 1)
10051 {
10052 appendStringInfoString(buf, " OR ");
10053 get_rule_expr_paren((Node *) lfirst(arg), context,
10054 false, node);
10055 }
10056 if (!PRETTY_PAREN(context))
10058 break;
10059
10060 case NOT_EXPR:
10061 if (!PRETTY_PAREN(context))
10063 appendStringInfoString(buf, "NOT ");
10065 false, node);
10066 if (!PRETTY_PAREN(context))
10068 break;
10069
10070 default:
10071 elog(ERROR, "unrecognized boolop: %d",
10072 (int) expr->boolop);
10073 }
10074 }
10075 break;
10076
10077 case T_SubLink:
10078 get_sublink_expr((SubLink *) node, context);
10079 break;
10080
10081 case T_SubPlan:
10082 {
10083 SubPlan *subplan = (SubPlan *) node;
10084
10085 /*
10086 * We cannot see an already-planned subplan in rule deparsing,
10087 * only while EXPLAINing a query plan. We don't try to
10088 * reconstruct the original SQL, just reference the subplan
10089 * that appears elsewhere in EXPLAIN's result. It does seem
10090 * useful to show the subLinkType and testexpr (if any), and
10091 * we also note whether the subplan will be hashed.
10092 */
10093 switch (subplan->subLinkType)
10094 {
10095 case EXISTS_SUBLINK:
10096 appendStringInfoString(buf, "EXISTS(");
10097 Assert(subplan->testexpr == NULL);
10098 break;
10099 case ALL_SUBLINK:
10100 appendStringInfoString(buf, "(ALL ");
10101 Assert(subplan->testexpr != NULL);
10102 break;
10103 case ANY_SUBLINK:
10104 appendStringInfoString(buf, "(ANY ");
10105 Assert(subplan->testexpr != NULL);
10106 break;
10107 case ROWCOMPARE_SUBLINK:
10108 /* Parenthesizing the testexpr seems sufficient */
10110 Assert(subplan->testexpr != NULL);
10111 break;
10112 case EXPR_SUBLINK:
10113 /* No need to decorate these subplan references */
10115 Assert(subplan->testexpr == NULL);
10116 break;
10117 case MULTIEXPR_SUBLINK:
10118 /* MULTIEXPR isn't executed in the normal way */
10119 appendStringInfoString(buf, "(rescan ");
10120 Assert(subplan->testexpr == NULL);
10121 break;
10122 case ARRAY_SUBLINK:
10123 appendStringInfoString(buf, "ARRAY(");
10124 Assert(subplan->testexpr == NULL);
10125 break;
10126 case CTE_SUBLINK:
10127 /* This case is unreachable within expressions */
10128 appendStringInfoString(buf, "CTE(");
10129 Assert(subplan->testexpr == NULL);
10130 break;
10131 }
10132
10133 if (subplan->testexpr != NULL)
10134 {
10136
10137 /*
10138 * Push SubPlan into ancestors list while deparsing
10139 * testexpr, so that we can handle PARAM_EXEC references
10140 * to the SubPlan's paramIds. (This makes it look like
10141 * the SubPlan is an "ancestor" of the current plan node,
10142 * which is a little weird, but it does no harm.) In this
10143 * path, we don't need to mention the SubPlan explicitly,
10144 * because the referencing Params will show its existence.
10145 */
10146 dpns = (deparse_namespace *) linitial(context->namespaces);
10147 dpns->ancestors = lcons(subplan, dpns->ancestors);
10148
10149 get_rule_expr(subplan->testexpr, context, showimplicit);
10151
10152 dpns->ancestors = list_delete_first(dpns->ancestors);
10153 }
10154 else
10155 {
10156 const char *nameprefix;
10157
10158 /* No referencing Params, so show the SubPlan's name */
10159 if (subplan->isInitPlan)
10160 nameprefix = "InitPlan ";
10161 else
10162 nameprefix = "SubPlan ";
10163 if (subplan->useHashTable)
10164 appendStringInfo(buf, "hashed %s%s)",
10165 nameprefix, subplan->plan_name);
10166 else
10167 appendStringInfo(buf, "%s%s)",
10168 nameprefix, subplan->plan_name);
10169 }
10170 }
10171 break;
10172
10174 {
10176 ListCell *lc;
10177
10178 /*
10179 * This case cannot be reached in normal usage, since no
10180 * AlternativeSubPlan can appear either in parsetrees or
10181 * finished plan trees. We keep it just in case somebody
10182 * wants to use this code to print planner data structures.
10183 */
10184 appendStringInfoString(buf, "(alternatives: ");
10185 foreach(lc, asplan->subplans)
10186 {
10187 SubPlan *splan = lfirst_node(SubPlan, lc);
10188 const char *nameprefix;
10189
10190 if (splan->isInitPlan)
10191 nameprefix = "InitPlan ";
10192 else
10193 nameprefix = "SubPlan ";
10194 if (splan->useHashTable)
10195 appendStringInfo(buf, "hashed %s%s", nameprefix,
10196 splan->plan_name);
10197 else
10199 splan->plan_name);
10200 if (lnext(asplan->subplans, lc))
10201 appendStringInfoString(buf, " or ");
10202 }
10204 }
10205 break;
10206
10207 case T_FieldSelect:
10208 {
10209 FieldSelect *fselect = (FieldSelect *) node;
10210 Node *arg = (Node *) fselect->arg;
10211 int fno = fselect->fieldnum;
10212 const char *fieldname;
10213 bool need_parens;
10214
10215 /*
10216 * Parenthesize the argument unless it's a SubscriptingRef or
10217 * another FieldSelect. Note in particular that it would be
10218 * WRONG to not parenthesize a Var argument; simplicity is not
10219 * the issue here, having the right number of names is.
10220 */
10222 !IsA(arg, FieldSelect);
10223 if (need_parens)
10225 get_rule_expr(arg, context, true);
10226 if (need_parens)
10228
10229 /*
10230 * Get and print the field name.
10231 */
10232 fieldname = get_name_for_var_field((Var *) arg, fno,
10233 0, context);
10234 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
10235 }
10236 break;
10237
10238 case T_FieldStore:
10239 {
10240 FieldStore *fstore = (FieldStore *) node;
10241 bool need_parens;
10242
10243 /*
10244 * There is no good way to represent a FieldStore as real SQL,
10245 * so decompilation of INSERT or UPDATE statements should
10246 * always use processIndirection as part of the
10247 * statement-level syntax. We should only get here when
10248 * EXPLAIN tries to print the targetlist of a plan resulting
10249 * from such a statement. The plan case is even harder than
10250 * ordinary rules would be, because the planner tries to
10251 * collapse multiple assignments to the same field or subfield
10252 * into one FieldStore; so we can see a list of target fields
10253 * not just one, and the arguments could be FieldStores
10254 * themselves. We don't bother to try to print the target
10255 * field names; we just print the source arguments, with a
10256 * ROW() around them if there's more than one. This isn't
10257 * terribly complete, but it's probably good enough for
10258 * EXPLAIN's purposes; especially since anything more would be
10259 * either hopelessly confusing or an even poorer
10260 * representation of what the plan is actually doing.
10261 */
10262 need_parens = (list_length(fstore->newvals) != 1);
10263 if (need_parens)
10264 appendStringInfoString(buf, "ROW(");
10265 get_rule_expr((Node *) fstore->newvals, context, showimplicit);
10266 if (need_parens)
10268 }
10269 break;
10270
10271 case T_RelabelType:
10272 {
10273 RelabelType *relabel = (RelabelType *) node;
10274 Node *arg = (Node *) relabel->arg;
10275
10276 if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
10277 !showimplicit)
10278 {
10279 /* don't show the implicit cast */
10280 get_rule_expr_paren(arg, context, false, node);
10281 }
10282 else
10283 {
10284 get_coercion_expr(arg, context,
10285 relabel->resulttype,
10286 relabel->resulttypmod,
10287 node);
10288 }
10289 }
10290 break;
10291
10292 case T_CoerceViaIO:
10293 {
10294 CoerceViaIO *iocoerce = (CoerceViaIO *) node;
10295 Node *arg = (Node *) iocoerce->arg;
10296
10297 if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
10298 !showimplicit)
10299 {
10300 /* don't show the implicit cast */
10301 get_rule_expr_paren(arg, context, false, node);
10302 }
10303 else
10304 {
10305 get_coercion_expr(arg, context,
10306 iocoerce->resulttype,
10307 -1,
10308 node);
10309 }
10310 }
10311 break;
10312
10313 case T_ArrayCoerceExpr:
10314 {
10316 Node *arg = (Node *) acoerce->arg;
10317
10318 if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
10319 !showimplicit)
10320 {
10321 /* don't show the implicit cast */
10322 get_rule_expr_paren(arg, context, false, node);
10323 }
10324 else
10325 {
10326 get_coercion_expr(arg, context,
10327 acoerce->resulttype,
10328 acoerce->resulttypmod,
10329 node);
10330 }
10331 }
10332 break;
10333
10335 {
10337 Node *arg = (Node *) convert->arg;
10338
10339 if (convert->convertformat == COERCE_IMPLICIT_CAST &&
10340 !showimplicit)
10341 {
10342 /* don't show the implicit cast */
10343 get_rule_expr_paren(arg, context, false, node);
10344 }
10345 else
10346 {
10347 get_coercion_expr(arg, context,
10348 convert->resulttype, -1,
10349 node);
10350 }
10351 }
10352 break;
10353
10354 case T_CollateExpr:
10355 {
10356 CollateExpr *collate = (CollateExpr *) node;
10357 Node *arg = (Node *) collate->arg;
10358
10359 if (!PRETTY_PAREN(context))
10361 get_rule_expr_paren(arg, context, showimplicit, node);
10362 appendStringInfo(buf, " COLLATE %s",
10363 generate_collation_name(collate->collOid));
10364 if (!PRETTY_PAREN(context))
10366 }
10367 break;
10368
10369 case T_CaseExpr:
10370 {
10371 CaseExpr *caseexpr = (CaseExpr *) node;
10372 ListCell *temp;
10373
10374 appendContextKeyword(context, "CASE",
10375 0, PRETTYINDENT_VAR, 0);
10376 if (caseexpr->arg)
10377 {
10379 get_rule_expr((Node *) caseexpr->arg, context, true);
10380 }
10381 foreach(temp, caseexpr->args)
10382 {
10384 Node *w = (Node *) when->expr;
10385
10386 if (caseexpr->arg)
10387 {
10388 /*
10389 * The parser should have produced WHEN clauses of the
10390 * form "CaseTestExpr = RHS", possibly with an
10391 * implicit coercion inserted above the CaseTestExpr.
10392 * For accurate decompilation of rules it's essential
10393 * that we show just the RHS. However in an
10394 * expression that's been through the optimizer, the
10395 * WHEN clause could be almost anything (since the
10396 * equality operator could have been expanded into an
10397 * inline function). If we don't recognize the form
10398 * of the WHEN clause, just punt and display it as-is.
10399 */
10400 if (IsA(w, OpExpr))
10401 {
10402 List *args = ((OpExpr *) w)->args;
10403
10404 if (list_length(args) == 2 &&
10406 CaseTestExpr))
10407 w = (Node *) lsecond(args);
10408 }
10409 }
10410
10411 if (!PRETTY_INDENT(context))
10413 appendContextKeyword(context, "WHEN ",
10414 0, 0, 0);
10415 get_rule_expr(w, context, false);
10416 appendStringInfoString(buf, " THEN ");
10417 get_rule_expr((Node *) when->result, context, true);
10418 }
10419 if (!PRETTY_INDENT(context))
10421 appendContextKeyword(context, "ELSE ",
10422 0, 0, 0);
10423 get_rule_expr((Node *) caseexpr->defresult, context, true);
10424 if (!PRETTY_INDENT(context))
10426 appendContextKeyword(context, "END",
10427 -PRETTYINDENT_VAR, 0, 0);
10428 }
10429 break;
10430
10431 case T_CaseTestExpr:
10432 {
10433 /*
10434 * Normally we should never get here, since for expressions
10435 * that can contain this node type we attempt to avoid
10436 * recursing to it. But in an optimized expression we might
10437 * be unable to avoid that (see comments for CaseExpr). If we
10438 * do see one, print it as CASE_TEST_EXPR.
10439 */
10440 appendStringInfoString(buf, "CASE_TEST_EXPR");
10441 }
10442 break;
10443
10444 case T_ArrayExpr:
10445 {
10446 ArrayExpr *arrayexpr = (ArrayExpr *) node;
10447
10448 appendStringInfoString(buf, "ARRAY[");
10449 get_rule_expr((Node *) arrayexpr->elements, context, true);
10451
10452 /*
10453 * If the array isn't empty, we assume its elements are
10454 * coerced to the desired type. If it's empty, though, we
10455 * need an explicit coercion to the array type.
10456 */
10457 if (arrayexpr->elements == NIL)
10458 appendStringInfo(buf, "::%s",
10459 format_type_with_typemod(arrayexpr->array_typeid, -1));
10460 }
10461 break;
10462
10463 case T_RowExpr:
10464 {
10465 RowExpr *rowexpr = (RowExpr *) node;
10466 TupleDesc tupdesc = NULL;
10467 ListCell *arg;
10468 int i;
10469 char *sep;
10470
10471 /*
10472 * If it's a named type and not RECORD, we may have to skip
10473 * dropped columns and/or claim there are NULLs for added
10474 * columns.
10475 */
10476 if (rowexpr->row_typeid != RECORDOID)
10477 {
10478 tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
10479 Assert(list_length(rowexpr->args) <= tupdesc->natts);
10480 }
10481
10482 /*
10483 * SQL99 allows "ROW" to be omitted when there is more than
10484 * one column, but for simplicity we always print it.
10485 */
10486 appendStringInfoString(buf, "ROW(");
10487 sep = "";
10488 i = 0;
10489 foreach(arg, rowexpr->args)
10490 {
10491 Node *e = (Node *) lfirst(arg);
10492
10493 if (tupdesc == NULL ||
10495 {
10497 /* Whole-row Vars need special treatment here */
10498 get_rule_expr_toplevel(e, context, true);
10499 sep = ", ";
10500 }
10501 i++;
10502 }
10503 if (tupdesc != NULL)
10504 {
10505 while (i < tupdesc->natts)
10506 {
10507 if (!TupleDescCompactAttr(tupdesc, i)->attisdropped)
10508 {
10510 appendStringInfoString(buf, "NULL");
10511 sep = ", ";
10512 }
10513 i++;
10514 }
10515
10516 ReleaseTupleDesc(tupdesc);
10517 }
10519 if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
10520 appendStringInfo(buf, "::%s",
10521 format_type_with_typemod(rowexpr->row_typeid, -1));
10522 }
10523 break;
10524
10525 case T_RowCompareExpr:
10526 {
10528
10529 /*
10530 * SQL99 allows "ROW" to be omitted when there is more than
10531 * one column, but for simplicity we always print it. Within
10532 * a ROW expression, whole-row Vars need special treatment, so
10533 * use get_rule_list_toplevel.
10534 */
10535 appendStringInfoString(buf, "(ROW(");
10536 get_rule_list_toplevel(rcexpr->largs, context, true);
10537
10538 /*
10539 * We assume that the name of the first-column operator will
10540 * do for all the rest too. This is definitely open to
10541 * failure, eg if some but not all operators were renamed
10542 * since the construct was parsed, but there seems no way to
10543 * be perfect.
10544 */
10545 appendStringInfo(buf, ") %s ROW(",
10547 exprType(linitial(rcexpr->largs)),
10548 exprType(linitial(rcexpr->rargs))));
10549 get_rule_list_toplevel(rcexpr->rargs, context, true);
10551 }
10552 break;
10553
10554 case T_CoalesceExpr:
10555 {
10557
10558 appendStringInfoString(buf, "COALESCE(");
10559 get_rule_expr((Node *) coalesceexpr->args, context, true);
10561 }
10562 break;
10563
10564 case T_MinMaxExpr:
10565 {
10566 MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
10567
10568 switch (minmaxexpr->op)
10569 {
10570 case IS_GREATEST:
10571 appendStringInfoString(buf, "GREATEST(");
10572 break;
10573 case IS_LEAST:
10574 appendStringInfoString(buf, "LEAST(");
10575 break;
10576 }
10577 get_rule_expr((Node *) minmaxexpr->args, context, true);
10579 }
10580 break;
10581
10582 case T_SQLValueFunction:
10583 {
10584 SQLValueFunction *svf = (SQLValueFunction *) node;
10585
10586 /*
10587 * Note: this code knows that typmod for time, timestamp, and
10588 * timestamptz just prints as integer.
10589 */
10590 switch (svf->op)
10591 {
10592 case SVFOP_CURRENT_DATE:
10593 appendStringInfoString(buf, "CURRENT_DATE");
10594 break;
10595 case SVFOP_CURRENT_TIME:
10596 appendStringInfoString(buf, "CURRENT_TIME");
10597 break;
10599 appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
10600 break;
10602 appendStringInfoString(buf, "CURRENT_TIMESTAMP");
10603 break;
10605 appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
10606 svf->typmod);
10607 break;
10608 case SVFOP_LOCALTIME:
10609 appendStringInfoString(buf, "LOCALTIME");
10610 break;
10611 case SVFOP_LOCALTIME_N:
10612 appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
10613 break;
10615 appendStringInfoString(buf, "LOCALTIMESTAMP");
10616 break;
10618 appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
10619 svf->typmod);
10620 break;
10621 case SVFOP_CURRENT_ROLE:
10622 appendStringInfoString(buf, "CURRENT_ROLE");
10623 break;
10624 case SVFOP_CURRENT_USER:
10625 appendStringInfoString(buf, "CURRENT_USER");
10626 break;
10627 case SVFOP_USER:
10628 appendStringInfoString(buf, "USER");
10629 break;
10630 case SVFOP_SESSION_USER:
10631 appendStringInfoString(buf, "SESSION_USER");
10632 break;
10634 appendStringInfoString(buf, "CURRENT_CATALOG");
10635 break;
10637 appendStringInfoString(buf, "CURRENT_SCHEMA");
10638 break;
10639 }
10640 }
10641 break;
10642
10643 case T_XmlExpr:
10644 {
10645 XmlExpr *xexpr = (XmlExpr *) node;
10646 bool needcomma = false;
10647 ListCell *arg;
10648 ListCell *narg;
10649 Const *con;
10650
10651 switch (xexpr->op)
10652 {
10653 case IS_XMLCONCAT:
10654 appendStringInfoString(buf, "XMLCONCAT(");
10655 break;
10656 case IS_XMLELEMENT:
10657 appendStringInfoString(buf, "XMLELEMENT(");
10658 break;
10659 case IS_XMLFOREST:
10660 appendStringInfoString(buf, "XMLFOREST(");
10661 break;
10662 case IS_XMLPARSE:
10663 appendStringInfoString(buf, "XMLPARSE(");
10664 break;
10665 case IS_XMLPI:
10666 appendStringInfoString(buf, "XMLPI(");
10667 break;
10668 case IS_XMLROOT:
10669 appendStringInfoString(buf, "XMLROOT(");
10670 break;
10671 case IS_XMLSERIALIZE:
10672 appendStringInfoString(buf, "XMLSERIALIZE(");
10673 break;
10674 case IS_DOCUMENT:
10675 break;
10676 }
10677 if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
10678 {
10679 if (xexpr->xmloption == XMLOPTION_DOCUMENT)
10680 appendStringInfoString(buf, "DOCUMENT ");
10681 else
10682 appendStringInfoString(buf, "CONTENT ");
10683 }
10684 if (xexpr->name)
10685 {
10686 appendStringInfo(buf, "NAME %s",
10688 needcomma = true;
10689 }
10690 if (xexpr->named_args)
10691 {
10692 if (xexpr->op != IS_XMLFOREST)
10693 {
10694 if (needcomma)
10696 appendStringInfoString(buf, "XMLATTRIBUTES(");
10697 needcomma = false;
10698 }
10699 forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
10700 {
10701 Node *e = (Node *) lfirst(arg);
10702 char *argname = strVal(lfirst(narg));
10703
10704 if (needcomma)
10706 get_rule_expr(e, context, true);
10707 appendStringInfo(buf, " AS %s",
10709 needcomma = true;
10710 }
10711 if (xexpr->op != IS_XMLFOREST)
10713 }
10714 if (xexpr->args)
10715 {
10716 if (needcomma)
10718 switch (xexpr->op)
10719 {
10720 case IS_XMLCONCAT:
10721 case IS_XMLELEMENT:
10722 case IS_XMLFOREST:
10723 case IS_XMLPI:
10724 case IS_XMLSERIALIZE:
10725 /* no extra decoration needed */
10726 get_rule_expr((Node *) xexpr->args, context, true);
10727 break;
10728 case IS_XMLPARSE:
10729 Assert(list_length(xexpr->args) == 2);
10730
10731 get_rule_expr((Node *) linitial(xexpr->args),
10732 context, true);
10733
10734 con = lsecond_node(Const, xexpr->args);
10735 Assert(!con->constisnull);
10736 if (DatumGetBool(con->constvalue))
10738 " PRESERVE WHITESPACE");
10739 else
10741 " STRIP WHITESPACE");
10742 break;
10743 case IS_XMLROOT:
10744 Assert(list_length(xexpr->args) == 3);
10745
10746 get_rule_expr((Node *) linitial(xexpr->args),
10747 context, true);
10748
10749 appendStringInfoString(buf, ", VERSION ");
10750 con = (Const *) lsecond(xexpr->args);
10751 if (IsA(con, Const) &&
10752 con->constisnull)
10753 appendStringInfoString(buf, "NO VALUE");
10754 else
10755 get_rule_expr((Node *) con, context, false);
10756
10757 con = lthird_node(Const, xexpr->args);
10758 if (con->constisnull)
10759 /* suppress STANDALONE NO VALUE */ ;
10760 else
10761 {
10762 switch (DatumGetInt32(con->constvalue))
10763 {
10764 case XML_STANDALONE_YES:
10766 ", STANDALONE YES");
10767 break;
10768 case XML_STANDALONE_NO:
10770 ", STANDALONE NO");
10771 break;
10774 ", STANDALONE NO VALUE");
10775 break;
10776 default:
10777 break;
10778 }
10779 }
10780 break;
10781 case IS_DOCUMENT:
10782 get_rule_expr_paren((Node *) xexpr->args, context, false, node);
10783 break;
10784 }
10785 }
10786 if (xexpr->op == IS_XMLSERIALIZE)
10787 {
10788 appendStringInfo(buf, " AS %s",
10789 format_type_with_typemod(xexpr->type,
10790 xexpr->typmod));
10791 if (xexpr->indent)
10792 appendStringInfoString(buf, " INDENT");
10793 else
10794 appendStringInfoString(buf, " NO INDENT");
10795 }
10796
10797 if (xexpr->op == IS_DOCUMENT)
10798 appendStringInfoString(buf, " IS DOCUMENT");
10799 else
10801 }
10802 break;
10803
10804 case T_NullTest:
10805 {
10806 NullTest *ntest = (NullTest *) node;
10807
10808 if (!PRETTY_PAREN(context))
10810 get_rule_expr_paren((Node *) ntest->arg, context, true, node);
10811
10812 /*
10813 * For scalar inputs, we prefer to print as IS [NOT] NULL,
10814 * which is shorter and traditional. If it's a rowtype input
10815 * but we're applying a scalar test, must print IS [NOT]
10816 * DISTINCT FROM NULL to be semantically correct.
10817 */
10818 if (ntest->argisrow ||
10819 !type_is_rowtype(exprType((Node *) ntest->arg)))
10820 {
10821 switch (ntest->nulltesttype)
10822 {
10823 case IS_NULL:
10824 appendStringInfoString(buf, " IS NULL");
10825 break;
10826 case IS_NOT_NULL:
10827 appendStringInfoString(buf, " IS NOT NULL");
10828 break;
10829 default:
10830 elog(ERROR, "unrecognized nulltesttype: %d",
10831 (int) ntest->nulltesttype);
10832 }
10833 }
10834 else
10835 {
10836 switch (ntest->nulltesttype)
10837 {
10838 case IS_NULL:
10839 appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
10840 break;
10841 case IS_NOT_NULL:
10842 appendStringInfoString(buf, " IS DISTINCT FROM NULL");
10843 break;
10844 default:
10845 elog(ERROR, "unrecognized nulltesttype: %d",
10846 (int) ntest->nulltesttype);
10847 }
10848 }
10849 if (!PRETTY_PAREN(context))
10851 }
10852 break;
10853
10854 case T_BooleanTest:
10855 {
10856 BooleanTest *btest = (BooleanTest *) node;
10857
10858 if (!PRETTY_PAREN(context))
10860 get_rule_expr_paren((Node *) btest->arg, context, false, node);
10861 switch (btest->booltesttype)
10862 {
10863 case IS_TRUE:
10864 appendStringInfoString(buf, " IS TRUE");
10865 break;
10866 case IS_NOT_TRUE:
10867 appendStringInfoString(buf, " IS NOT TRUE");
10868 break;
10869 case IS_FALSE:
10870 appendStringInfoString(buf, " IS FALSE");
10871 break;
10872 case IS_NOT_FALSE:
10873 appendStringInfoString(buf, " IS NOT FALSE");
10874 break;
10875 case IS_UNKNOWN:
10876 appendStringInfoString(buf, " IS UNKNOWN");
10877 break;
10878 case IS_NOT_UNKNOWN:
10879 appendStringInfoString(buf, " IS NOT UNKNOWN");
10880 break;
10881 default:
10882 elog(ERROR, "unrecognized booltesttype: %d",
10883 (int) btest->booltesttype);
10884 }
10885 if (!PRETTY_PAREN(context))
10887 }
10888 break;
10889
10890 case T_CoerceToDomain:
10891 {
10893 Node *arg = (Node *) ctest->arg;
10894
10895 if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
10896 !showimplicit)
10897 {
10898 /* don't show the implicit cast */
10899 get_rule_expr(arg, context, false);
10900 }
10901 else
10902 {
10903 get_coercion_expr(arg, context,
10904 ctest->resulttype,
10905 ctest->resulttypmod,
10906 node);
10907 }
10908 }
10909 break;
10910
10912 appendStringInfoString(buf, "VALUE");
10913 break;
10914
10915 case T_SetToDefault:
10916 appendStringInfoString(buf, "DEFAULT");
10917 break;
10918
10919 case T_CurrentOfExpr:
10920 {
10921 CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
10922
10923 if (cexpr->cursor_name)
10924 appendStringInfo(buf, "CURRENT OF %s",
10926 else
10927 appendStringInfo(buf, "CURRENT OF $%d",
10928 cexpr->cursor_param);
10929 }
10930 break;
10931
10932 case T_NextValueExpr:
10933 {
10935
10936 /*
10937 * This isn't exactly nextval(), but that seems close enough
10938 * for EXPLAIN's purposes.
10939 */
10940 appendStringInfoString(buf, "nextval(");
10943 NIL));
10945 }
10946 break;
10947
10948 case T_InferenceElem:
10949 {
10950 InferenceElem *iexpr = (InferenceElem *) node;
10951 bool save_varprefix;
10952 bool need_parens;
10953
10954 /*
10955 * InferenceElem can only refer to target relation, so a
10956 * prefix is not useful, and indeed would cause parse errors.
10957 */
10958 save_varprefix = context->varprefix;
10959 context->varprefix = false;
10960
10961 /*
10962 * Parenthesize the element unless it's a simple Var or a bare
10963 * function call. Follows pg_get_indexdef_worker().
10964 */
10965 need_parens = !IsA(iexpr->expr, Var);
10966 if (IsA(iexpr->expr, FuncExpr) &&
10967 ((FuncExpr *) iexpr->expr)->funcformat ==
10969 need_parens = false;
10970
10971 if (need_parens)
10973 get_rule_expr((Node *) iexpr->expr,
10974 context, false);
10975 if (need_parens)
10977
10978 context->varprefix = save_varprefix;
10979
10980 if (iexpr->infercollid)
10981 appendStringInfo(buf, " COLLATE %s",
10982 generate_collation_name(iexpr->infercollid));
10983
10984 /* Add the operator class name, if not default */
10985 if (iexpr->inferopclass)
10986 {
10987 Oid inferopclass = iexpr->inferopclass;
10989
10990 get_opclass_name(inferopclass, inferopcinputtype, buf);
10991 }
10992 }
10993 break;
10994
10995 case T_ReturningExpr:
10996 {
10998
10999 /*
11000 * We cannot see a ReturningExpr in rule deparsing, only while
11001 * EXPLAINing a query plan (ReturningExpr nodes are only ever
11002 * adding during query rewriting). Just display the expression
11003 * returned (an expanded view column).
11004 */
11005 get_rule_expr((Node *) retExpr->retexpr, context, showimplicit);
11006 }
11007 break;
11008
11010 {
11012 ListCell *cell;
11013 char *sep;
11014
11015 if (spec->is_default)
11016 {
11017 appendStringInfoString(buf, "DEFAULT");
11018 break;
11019 }
11020
11021 switch (spec->strategy)
11022 {
11024 Assert(spec->modulus > 0 && spec->remainder >= 0);
11025 Assert(spec->modulus > spec->remainder);
11026
11027 appendStringInfoString(buf, "FOR VALUES");
11028 appendStringInfo(buf, " WITH (modulus %d, remainder %d)",
11029 spec->modulus, spec->remainder);
11030 break;
11031
11033 Assert(spec->listdatums != NIL);
11034
11035 appendStringInfoString(buf, "FOR VALUES IN (");
11036 sep = "";
11037 foreach(cell, spec->listdatums)
11038 {
11039 Const *val = lfirst_node(Const, cell);
11040
11042 get_const_expr(val, context, -1);
11043 sep = ", ";
11044 }
11045
11047 break;
11048
11050 Assert(spec->lowerdatums != NIL &&
11051 spec->upperdatums != NIL &&
11052 list_length(spec->lowerdatums) ==
11053 list_length(spec->upperdatums));
11054
11055 appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
11056 get_range_partbound_string(spec->lowerdatums),
11057 get_range_partbound_string(spec->upperdatums));
11058 break;
11059
11060 default:
11061 elog(ERROR, "unrecognized partition strategy: %d",
11062 (int) spec->strategy);
11063 break;
11064 }
11065 }
11066 break;
11067
11068 case T_JsonValueExpr:
11069 {
11070 JsonValueExpr *jve = (JsonValueExpr *) node;
11071
11072 get_rule_expr((Node *) jve->raw_expr, context, false);
11073 get_json_format(jve->format, context->buf);
11074 }
11075 break;
11076
11078 get_json_constructor((JsonConstructorExpr *) node, context, false);
11079 break;
11080
11081 case T_JsonIsPredicate:
11082 {
11083 JsonIsPredicate *pred = (JsonIsPredicate *) node;
11084
11085 if (!PRETTY_PAREN(context))
11086 appendStringInfoChar(context->buf, '(');
11087
11088 get_rule_expr_paren(pred->expr, context, true, node);
11089
11090 appendStringInfoString(context->buf, " IS JSON");
11091
11092 /* TODO: handle FORMAT clause */
11093
11094 switch (pred->item_type)
11095 {
11096 case JS_TYPE_SCALAR:
11097 appendStringInfoString(context->buf, " SCALAR");
11098 break;
11099 case JS_TYPE_ARRAY:
11100 appendStringInfoString(context->buf, " ARRAY");
11101 break;
11102 case JS_TYPE_OBJECT:
11103 appendStringInfoString(context->buf, " OBJECT");
11104 break;
11105 default:
11106 break;
11107 }
11108
11109 if (pred->unique_keys)
11110 appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
11111
11112 if (!PRETTY_PAREN(context))
11113 appendStringInfoChar(context->buf, ')');
11114 }
11115 break;
11116
11117 case T_JsonExpr:
11118 {
11119 JsonExpr *jexpr = (JsonExpr *) node;
11120
11121 switch (jexpr->op)
11122 {
11123 case JSON_EXISTS_OP:
11124 appendStringInfoString(buf, "JSON_EXISTS(");
11125 break;
11126 case JSON_QUERY_OP:
11127 appendStringInfoString(buf, "JSON_QUERY(");
11128 break;
11129 case JSON_VALUE_OP:
11130 appendStringInfoString(buf, "JSON_VALUE(");
11131 break;
11132 default:
11133 elog(ERROR, "unrecognized JsonExpr op: %d",
11134 (int) jexpr->op);
11135 }
11136
11137 get_rule_expr(jexpr->formatted_expr, context, showimplicit);
11138
11140
11141 get_json_path_spec(jexpr->path_spec, context, showimplicit);
11142
11143 if (jexpr->passing_values)
11144 {
11145 ListCell *lc1,
11146 *lc2;
11147 bool needcomma = false;
11148
11149 appendStringInfoString(buf, " PASSING ");
11150
11151 forboth(lc1, jexpr->passing_names,
11152 lc2, jexpr->passing_values)
11153 {
11154 if (needcomma)
11156 needcomma = true;
11157
11158 get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
11159 appendStringInfo(buf, " AS %s",
11161 }
11162 }
11163
11164 if (jexpr->op != JSON_EXISTS_OP ||
11165 jexpr->returning->typid != BOOLOID)
11166 get_json_returning(jexpr->returning, context->buf,
11167 jexpr->op == JSON_QUERY_OP);
11168
11170 jexpr->op != JSON_EXISTS_OP ?
11173
11175 }
11176 break;
11177
11178 case T_List:
11179 {
11180 char *sep;
11181 ListCell *l;
11182
11183 sep = "";
11184 foreach(l, (List *) node)
11185 {
11187 get_rule_expr((Node *) lfirst(l), context, showimplicit);
11188 sep = ", ";
11189 }
11190 }
11191 break;
11192
11193 case T_TableFunc:
11194 get_tablefunc((TableFunc *) node, context, showimplicit);
11195 break;
11196
11197 case T_GraphPropertyRef:
11198 {
11200
11202 break;
11203 }
11204
11205 default:
11206 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
11207 break;
11208 }
11209}

References ALL_SUBLINK, deparse_namespace::ancestors, AND_EXPR, ANY_SUBLINK, appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, CoerceViaIO::arg, CollateExpr::arg, OpExpr::args, ScalarArrayOpExpr::args, BoolExpr::args, RowExpr::args, XmlExpr::args, ARRAY_SUBLINK, Assert, CompactAttribute::attisdropped, BoolExpr::boolop, deparse_context::buf, buf, CHECK_FOR_INTERRUPTS, check_stack_depth(), COERCE_EXPLICIT_CALL, COERCE_EXPLICIT_CAST, COERCE_IMPLICIT_CAST, CollateExpr::collOid, convert(), CTE_SUBLINK, CurrentOfExpr::cursor_name, CurrentOfExpr::cursor_param, DatumGetBool(), DatumGetInt32(), elog, ERROR, EXISTS_SUBLINK, JsonIsPredicate::expr, EXPR_SUBLINK, exprType(), exprTypmod(), fb(), for_each_from, forboth, format_type_with_typemod(), generate_collation_name(), generate_operator_name(), generate_relation_name(), get_agg_expr(), get_base_element_type(), get_coercion_expr(), get_const_expr(), get_func_expr(), get_json_constructor(), get_json_expr_options(), get_json_format(), get_json_path_spec(), get_json_returning(), get_name_for_var_field(), get_opclass_input_type(), get_opclass_name(), get_oper_expr(), get_parameter(), get_propgraph_property_name(), get_range_partbound_string(), get_rule_expr(), get_rule_expr_paren(), get_rule_expr_toplevel(), get_rule_list_toplevel(), get_sublink_expr(), get_tablefunc(), get_variable(), get_windowfunc_expr(), i, XmlExpr::indent, IS_DOCUMENT, IS_FALSE, IS_GREATEST, IS_LEAST, IS_NOT_FALSE, IS_NOT_NULL, IS_NOT_TRUE, IS_NOT_UNKNOWN, IS_NULL, IS_TRUE, IS_UNKNOWN, IS_XMLCONCAT, IS_XMLELEMENT, IS_XMLFOREST, IS_XMLPARSE, IS_XMLPI, IS_XMLROOT, IS_XMLSERIALIZE, IsA, SubPlan::isInitPlan, JsonIsPredicate::item_type, JS_TYPE_ARRAY, JS_TYPE_OBJECT, JS_TYPE_SCALAR, JSON_BEHAVIOR_FALSE, JSON_BEHAVIOR_NULL, JSON_EXISTS_OP, JSON_QUERY_OP, JSON_VALUE_OP, lcons(), lfirst, lfirst_node, linitial, linitial_oid, list_delete_first(), list_length(), lnext(), lookup_rowtype_tupdesc(), lsecond, lsecond_node, lthird_node, map_xml_name_to_sql_identifier(), MULTIEXPR_SUBLINK, XmlExpr::named_args, deparse_context::namespaces, TupleDescData::natts, FieldStore::newvals, NIL, nodeTag, NOT_EXPR, SQLValueFunction::op, XmlExpr::op, ScalarArrayOpExpr::opno, OR_EXPR, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, SubPlan::plan_name, PRETTY_INDENT, PRETTY_PAREN, PRETTYINDENT_VAR, printSubscripts(), processIndirection(), quote_identifier(), SubscriptingRef::refassgnexpr, SubscriptingRef::refexpr, ReleaseTupleDesc, CoerceViaIO::resulttype, ROWCOMPARE_SUBLINK, simple_quote_literal(), strip_implicit_coercions(), strVal, SubPlan::subLinkType, SVFOP_CURRENT_CATALOG, SVFOP_CURRENT_DATE, SVFOP_CURRENT_ROLE, SVFOP_CURRENT_SCHEMA, SVFOP_CURRENT_TIME, SVFOP_CURRENT_TIME_N, SVFOP_CURRENT_TIMESTAMP, SVFOP_CURRENT_TIMESTAMP_N, SVFOP_CURRENT_USER, SVFOP_LOCALTIME, SVFOP_LOCALTIME_N, SVFOP_LOCALTIMESTAMP, SVFOP_LOCALTIMESTAMP_N, SVFOP_SESSION_USER, SVFOP_USER, SubPlan::testexpr, TupleDescCompactAttr(), type_is_rowtype(), SQLValueFunction::typmod, JsonIsPredicate::unique_keys, SubPlan::useHashTable, ScalarArrayOpExpr::useOr, val, deparse_context::varprefix, XML_STANDALONE_NO, XML_STANDALONE_NO_VALUE, XML_STANDALONE_YES, and XMLOPTION_DOCUMENT.

Referenced by deparse_expression_pretty(), get_agg_expr_helper(), get_basic_select_query(), get_delete_query_def(), get_for_portion_of(), get_from_clause_item(), get_func_expr(), get_func_sql_syntax(), get_graph_pattern_def(), get_insert_query_def(), get_json_behavior(), get_json_constructor(), get_json_path_spec(), get_json_table(), get_merge_query_def(), get_parameter(), get_path_pattern_expr_def(), get_rule_expr(), get_rule_expr_funccall(), get_rule_expr_paren(), get_rule_expr_toplevel(), get_rule_sortgroupclause(), get_select_query_def(), get_special_variable(), get_sublink_expr(), get_tablesample_def(), get_target_list(), get_update_query_def(), get_update_query_targetlist_def(), get_variable(), get_window_frame_options(), get_windowfunc_expr_helper(), get_with_clause(), get_xmltable(), make_ruledef(), pg_get_triggerdef_worker(), and printSubscripts().

◆ get_rule_expr_funccall()

static void get_rule_expr_funccall ( Node node,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 11271 of file ruleutils.c.

11273{
11274 if (looks_like_function(node))
11275 get_rule_expr(node, context, showimplicit);
11276 else
11277 {
11278 StringInfo buf = context->buf;
11279
11280 appendStringInfoString(buf, "CAST(");
11281 /* no point in showing any top-level implicit cast */
11282 get_rule_expr(node, context, false);
11283 appendStringInfo(buf, " AS %s)",
11285 exprTypmod(node)));
11286 }
11287}

References appendStringInfo(), appendStringInfoString(), deparse_context::buf, buf, exprType(), exprTypmod(), fb(), format_type_with_typemod(), get_rule_expr(), and looks_like_function().

Referenced by get_from_clause_item().

◆ get_rule_expr_paren()

static void get_rule_expr_paren ( Node node,
deparse_context context,
bool  showimplicit,
Node parentNode 
)
static

Definition at line 9720 of file ruleutils.c.

9722{
9723 bool need_paren;
9724
9725 need_paren = PRETTY_PAREN(context) &&
9726 !isSimpleNode(node, parentNode, context->prettyFlags);
9727
9728 if (need_paren)
9729 appendStringInfoChar(context->buf, '(');
9730
9731 get_rule_expr(node, context, showimplicit);
9732
9733 if (need_paren)
9734 appendStringInfoChar(context->buf, ')');
9735}

References appendStringInfoChar(), deparse_context::buf, fb(), get_rule_expr(), isSimpleNode(), PRETTY_PAREN, and deparse_context::prettyFlags.

Referenced by get_coercion_expr(), get_func_expr(), get_func_sql_syntax(), get_oper_expr(), and get_rule_expr().

◆ get_rule_expr_toplevel()

static void get_rule_expr_toplevel ( Node node,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 11223 of file ruleutils.c.

11225{
11226 if (node && IsA(node, Var))
11227 (void) get_variable((Var *) node, 0, true, context);
11228 else
11229 get_rule_expr(node, context, showimplicit);
11230}

References fb(), get_rule_expr(), get_variable(), and IsA.

Referenced by get_rule_expr(), get_rule_list_toplevel(), and get_values_def().

◆ get_rule_groupingset()

static void get_rule_groupingset ( GroupingSet gset,
List targetlist,
bool  omit_parens,
deparse_context context 
)
static

Definition at line 6999 of file ruleutils.c.

7001{
7002 ListCell *l;
7003 StringInfo buf = context->buf;
7004 bool omit_child_parens = true;
7005 char *sep = "";
7006
7007 switch (gset->kind)
7008 {
7009 case GROUPING_SET_EMPTY:
7011 return;
7012
7014 {
7015 if (!omit_parens || list_length(gset->content) != 1)
7017
7018 foreach(l, gset->content)
7019 {
7020 Index ref = lfirst_int(l);
7021
7023 get_rule_sortgroupclause(ref, targetlist,
7024 false, context);
7025 sep = ", ";
7026 }
7027
7028 if (!omit_parens || list_length(gset->content) != 1)
7030 }
7031 return;
7032
7034 appendStringInfoString(buf, "ROLLUP(");
7035 break;
7036 case GROUPING_SET_CUBE:
7037 appendStringInfoString(buf, "CUBE(");
7038 break;
7039 case GROUPING_SET_SETS:
7040 appendStringInfoString(buf, "GROUPING SETS (");
7041 omit_child_parens = false;
7042 break;
7043 }
7044
7045 foreach(l, gset->content)
7046 {
7048 get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
7049 sep = ", ";
7050 }
7051
7053}

References appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, fb(), get_rule_groupingset(), get_rule_sortgroupclause(), GROUPING_SET_CUBE, GROUPING_SET_EMPTY, GROUPING_SET_ROLLUP, GROUPING_SET_SETS, GROUPING_SET_SIMPLE, lfirst, lfirst_int, and list_length().

Referenced by get_basic_select_query(), and get_rule_groupingset().

◆ get_rule_list_toplevel()

static void get_rule_list_toplevel ( List lst,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 11241 of file ruleutils.c.

11243{
11244 const char *sep;
11245 ListCell *lc;
11246
11247 sep = "";
11248 foreach(lc, lst)
11249 {
11250 Node *e = (Node *) lfirst(lc);
11251
11252 appendStringInfoString(context->buf, sep);
11254 sep = ", ";
11255 }
11256}

References appendStringInfoString(), deparse_context::buf, fb(), get_rule_expr_toplevel(), and lfirst.

Referenced by get_insert_query_def(), get_merge_query_def(), and get_rule_expr().

◆ get_rule_orderby()

static void get_rule_orderby ( List orderList,
List targetList,
bool  force_colno,
deparse_context context 
)
static

Definition at line 7059 of file ruleutils.c.

7061{
7062 StringInfo buf = context->buf;
7063 const char *sep;
7064 ListCell *l;
7065
7066 sep = "";
7067 foreach(l, orderList)
7068 {
7070 Node *sortexpr;
7072 TypeCacheEntry *typentry;
7073
7075 sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
7076 force_colno, context);
7078 /* See whether operator is default < or > for datatype */
7079 typentry = lookup_type_cache(sortcoltype,
7081 if (srt->sortop == typentry->lt_opr)
7082 {
7083 /* ASC is default, so emit nothing for it */
7084 if (srt->nulls_first)
7085 appendStringInfoString(buf, " NULLS FIRST");
7086 }
7087 else if (srt->sortop == typentry->gt_opr)
7088 {
7089 appendStringInfoString(buf, " DESC");
7090 /* DESC defaults to NULLS FIRST */
7091 if (!srt->nulls_first)
7092 appendStringInfoString(buf, " NULLS LAST");
7093 }
7094 else
7095 {
7096 appendStringInfo(buf, " USING %s",
7099 sortcoltype));
7100 /* be specific to eliminate ambiguity */
7101 if (srt->nulls_first)
7102 appendStringInfoString(buf, " NULLS FIRST");
7103 else
7104 appendStringInfoString(buf, " NULLS LAST");
7105 }
7106 sep = ", ";
7107 }
7108}

References appendStringInfo(), appendStringInfoString(), deparse_context::buf, buf, exprType(), fb(), generate_operator_name(), get_rule_sortgroupclause(), TypeCacheEntry::gt_opr, lfirst, lookup_type_cache(), TypeCacheEntry::lt_opr, TYPECACHE_GT_OPR, and TYPECACHE_LT_OPR.

Referenced by get_agg_expr_helper(), get_rule_windowspec(), and get_select_query_def().

◆ get_rule_sortgroupclause()

static Node * get_rule_sortgroupclause ( Index  ref,
List tlist,
bool  force_colno,
deparse_context context 
)
static

Definition at line 6930 of file ruleutils.c.

6932{
6933 StringInfo buf = context->buf;
6935 Node *expr;
6936
6937 tle = get_sortgroupref_tle(ref, tlist);
6938 expr = (Node *) tle->expr;
6939
6940 /*
6941 * Use column-number form if requested by caller. Otherwise, if
6942 * expression is a constant, force it to be dumped with an explicit cast
6943 * as decoration --- this is because a simple integer constant is
6944 * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
6945 * we dump it without any decoration. Similarly, if it's just a Var,
6946 * there is risk of misinterpretation if the column name is reassigned in
6947 * the SELECT list, so we may need to force table qualification. And, if
6948 * it's anything more complex than a simple Var, then force extra parens
6949 * around it, to ensure it can't be misinterpreted as a cube() or rollup()
6950 * construct.
6951 */
6952 if (force_colno)
6953 {
6954 Assert(!tle->resjunk);
6955 appendStringInfo(buf, "%d", tle->resno);
6956 }
6957 else if (!expr)
6958 /* do nothing, probably can't happen */ ;
6959 else if (IsA(expr, Const))
6960 get_const_expr((Const *) expr, context, 1);
6961 else if (IsA(expr, Var))
6962 {
6963 /* Tell get_variable to check for name conflict */
6964 bool save_varinorderby = context->varInOrderBy;
6965
6966 context->varInOrderBy = true;
6967 (void) get_variable((Var *) expr, 0, false, context);
6969 }
6970 else
6971 {
6972 /*
6973 * We must force parens for function-like expressions even if
6974 * PRETTY_PAREN is off, since those are the ones in danger of
6975 * misparsing. For other expressions we need to force them only if
6976 * PRETTY_PAREN is on, since otherwise the expression will output them
6977 * itself. (We can't skip the parens.)
6978 */
6979 bool need_paren = (PRETTY_PAREN(context)
6980 || IsA(expr, FuncExpr)
6981 || IsA(expr, Aggref)
6982 || IsA(expr, WindowFunc)
6983 || IsA(expr, JsonConstructorExpr));
6984
6985 if (need_paren)
6986 appendStringInfoChar(context->buf, '(');
6987 get_rule_expr(expr, context, true);
6988 if (need_paren)
6989 appendStringInfoChar(context->buf, ')');
6990 }
6991
6992 return expr;
6993}

References appendStringInfo(), appendStringInfoChar(), Assert, deparse_context::buf, buf, fb(), get_const_expr(), get_rule_expr(), get_sortgroupref_tle(), get_variable(), IsA, PRETTY_PAREN, and deparse_context::varInOrderBy.

Referenced by get_basic_select_query(), get_rule_groupingset(), get_rule_orderby(), and get_rule_windowspec().

◆ get_rule_windowclause()

static void get_rule_windowclause ( Query query,
deparse_context context 
)
static

Definition at line 7117 of file ruleutils.c.

7118{
7119 StringInfo buf = context->buf;
7120 const char *sep;
7121 ListCell *l;
7122
7123 sep = NULL;
7124 foreach(l, query->windowClause)
7125 {
7126 WindowClause *wc = (WindowClause *) lfirst(l);
7127
7128 if (wc->name == NULL)
7129 continue; /* ignore anonymous windows */
7130
7131 if (sep == NULL)
7132 appendContextKeyword(context, " WINDOW ",
7134 else
7136
7137 appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
7138
7139 get_rule_windowspec(wc, query->targetList, context);
7140
7141 sep = ", ";
7142 }
7143}

References appendContextKeyword(), appendStringInfo(), appendStringInfoString(), deparse_context::buf, buf, fb(), get_rule_windowspec(), lfirst, PRETTYINDENT_STD, quote_identifier(), Query::targetList, and Query::windowClause.

Referenced by get_basic_select_query().

◆ get_rule_windowspec()

static void get_rule_windowspec ( WindowClause wc,
List targetList,
deparse_context context 
)
static

Definition at line 7149 of file ruleutils.c.

7151{
7152 StringInfo buf = context->buf;
7153 bool needspace = false;
7154 const char *sep;
7155 ListCell *l;
7156
7158 if (wc->refname)
7159 {
7161 needspace = true;
7162 }
7163 /* partition clauses are always inherited, so only print if no refname */
7164 if (wc->partitionClause && !wc->refname)
7165 {
7166 if (needspace)
7168 appendStringInfoString(buf, "PARTITION BY ");
7169 sep = "";
7170 foreach(l, wc->partitionClause)
7171 {
7173
7175 get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
7176 false, context);
7177 sep = ", ";
7178 }
7179 needspace = true;
7180 }
7181 /* print ordering clause only if not inherited */
7182 if (wc->orderClause && !wc->copiedOrder)
7183 {
7184 if (needspace)
7186 appendStringInfoString(buf, "ORDER BY ");
7187 get_rule_orderby(wc->orderClause, targetList, false, context);
7188 needspace = true;
7189 }
7190 /* framing clause is never inherited, so print unless it's default */
7192 {
7193 if (needspace)
7196 wc->startOffset, wc->endOffset,
7197 context);
7198 }
7200}

References appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, WindowClause::endOffset, fb(), FRAMEOPTION_NONDEFAULT, WindowClause::frameOptions, get_rule_orderby(), get_rule_sortgroupclause(), get_window_frame_options(), lfirst, WindowClause::orderClause, WindowClause::partitionClause, quote_identifier(), and WindowClause::startOffset.

Referenced by get_rule_windowclause(), and get_windowfunc_expr_helper().

◆ get_select_query_def()

static void get_select_query_def ( Query query,
deparse_context context 
)
static

Definition at line 6271 of file ruleutils.c.

6272{
6273 StringInfo buf = context->buf;
6274 bool force_colno;
6275 ListCell *l;
6276
6277 /* Insert the WITH clause if given */
6278 get_with_clause(query, context);
6279
6280 /* Subroutines may need to consult the SELECT targetlist and windowClause */
6281 context->targetList = query->targetList;
6282 context->windowClause = query->windowClause;
6283
6284 /*
6285 * If the Query node has a setOperations tree, then it's the top level of
6286 * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
6287 * fields are interesting in the top query itself.
6288 */
6289 if (query->setOperations)
6290 {
6291 get_setop_query(query->setOperations, query, context);
6292 /* ORDER BY clauses must be simple in this case */
6293 force_colno = true;
6294 }
6295 else
6296 {
6297 get_basic_select_query(query, context);
6298 force_colno = false;
6299 }
6300
6301 /* Add the ORDER BY clause if given */
6302 if (query->sortClause != NIL)
6303 {
6304 appendContextKeyword(context, " ORDER BY ",
6306 get_rule_orderby(query->sortClause, query->targetList,
6307 force_colno, context);
6308 }
6309
6310 /*
6311 * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
6312 * standard spelling of LIMIT.
6313 */
6314 if (query->limitOffset != NULL)
6315 {
6316 appendContextKeyword(context, " OFFSET ",
6318 get_rule_expr(query->limitOffset, context, false);
6319 }
6320 if (query->limitCount != NULL)
6321 {
6322 if (query->limitOption == LIMIT_OPTION_WITH_TIES)
6323 {
6324 /*
6325 * The limitCount arg is a c_expr, so it needs parens. Simple
6326 * literals and function expressions would not need parens, but
6327 * unfortunately it's hard to tell if the expression will be
6328 * printed as a simple literal like 123 or as a typecast
6329 * expression, like '-123'::int4. The grammar accepts the former
6330 * without quoting, but not the latter.
6331 */
6332 appendContextKeyword(context, " FETCH FIRST ",
6335 get_rule_expr(query->limitCount, context, false);
6337 appendStringInfoString(buf, " ROWS WITH TIES");
6338 }
6339 else
6340 {
6341 appendContextKeyword(context, " LIMIT ",
6343 if (IsA(query->limitCount, Const) &&
6344 ((Const *) query->limitCount)->constisnull)
6346 else
6347 get_rule_expr(query->limitCount, context, false);
6348 }
6349 }
6350
6351 /* Add FOR [KEY] UPDATE/SHARE clauses if present */
6352 if (query->hasForUpdate)
6353 {
6354 foreach(l, query->rowMarks)
6355 {
6356 RowMarkClause *rc = (RowMarkClause *) lfirst(l);
6357
6358 /* don't print implicit clauses */
6359 if (rc->pushedDown)
6360 continue;
6361
6362 appendContextKeyword(context,
6365
6366 appendStringInfo(buf, " OF %s",
6368 context)));
6369 if (rc->waitPolicy == LockWaitError)
6370 appendStringInfoString(buf, " NOWAIT");
6371 else if (rc->waitPolicy == LockWaitSkip)
6372 appendStringInfoString(buf, " SKIP LOCKED");
6373 }
6374 }
6375}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, fb(), get_basic_select_query(), get_lock_clause_strength(), get_rtable_name(), get_rule_expr(), get_rule_orderby(), get_setop_query(), get_with_clause(), IsA, lfirst, LIMIT_OPTION_WITH_TIES, Query::limitCount, Query::limitOffset, Query::limitOption, LockWaitError, LockWaitSkip, NIL, PRETTYINDENT_STD, RowMarkClause::pushedDown, quote_identifier(), Query::rowMarks, RowMarkClause::rti, Query::setOperations, Query::sortClause, RowMarkClause::strength, deparse_context::targetList, Query::targetList, RowMarkClause::waitPolicy, deparse_context::windowClause, and Query::windowClause.

Referenced by get_query_def().

◆ get_setop_query()

static void get_setop_query ( Node setOp,
Query query,
deparse_context context 
)
static

Definition at line 6782 of file ruleutils.c.

6783{
6784 StringInfo buf = context->buf;
6785 bool need_paren;
6786
6787 /* Guard against excessively long or deeply-nested queries */
6790
6791 if (IsA(setOp, RangeTblRef))
6792 {
6794 RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
6795 Query *subquery = rte->subquery;
6796
6797 Assert(subquery != NULL);
6798
6799 /*
6800 * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.
6801 * Also add parens if the leaf query contains its own set operations.
6802 * (That shouldn't happen unless one of the other clauses is also
6803 * present, see transformSetOperationTree; but let's be safe.)
6804 */
6805 need_paren = (subquery->cteList ||
6806 subquery->sortClause ||
6807 subquery->rowMarks ||
6808 subquery->limitOffset ||
6809 subquery->limitCount ||
6810 subquery->setOperations);
6811 if (need_paren)
6813 get_query_def(subquery, buf, context->namespaces,
6814 context->resultDesc, context->colNamesVisible,
6815 context->prettyFlags, context->wrapColumn,
6816 context->indentLevel);
6817 if (need_paren)
6819 }
6820 else if (IsA(setOp, SetOperationStmt))
6821 {
6823 int subindent;
6825
6826 /*
6827 * We force parens when nesting two SetOperationStmts, except when the
6828 * lefthand input is another setop of the same kind. Syntactically,
6829 * we could omit parens in rather more cases, but it seems best to use
6830 * parens to flag cases where the setop operator changes. If we use
6831 * parens, we also increase the indentation level for the child query.
6832 *
6833 * There are some cases in which parens are needed around a leaf query
6834 * too, but those are more easily handled at the next level down (see
6835 * code above).
6836 */
6837 if (IsA(op->larg, SetOperationStmt))
6838 {
6840
6841 if (op->op == lop->op && op->all == lop->all)
6842 need_paren = false;
6843 else
6844 need_paren = true;
6845 }
6846 else
6847 need_paren = false;
6848
6849 if (need_paren)
6850 {
6853 appendContextKeyword(context, "", subindent, 0, 0);
6854 }
6855 else
6856 subindent = 0;
6857
6858 get_setop_query(op->larg, query, context);
6859
6860 if (need_paren)
6861 appendContextKeyword(context, ") ", -subindent, 0, 0);
6862 else if (PRETTY_INDENT(context))
6863 appendContextKeyword(context, "", -subindent, 0, 0);
6864 else
6866
6867 switch (op->op)
6868 {
6869 case SETOP_UNION:
6870 appendStringInfoString(buf, "UNION ");
6871 break;
6872 case SETOP_INTERSECT:
6873 appendStringInfoString(buf, "INTERSECT ");
6874 break;
6875 case SETOP_EXCEPT:
6876 appendStringInfoString(buf, "EXCEPT ");
6877 break;
6878 default:
6879 elog(ERROR, "unrecognized set op: %d",
6880 (int) op->op);
6881 }
6882 if (op->all)
6883 appendStringInfoString(buf, "ALL ");
6884
6885 /* Always parenthesize if RHS is another setop */
6887
6888 /*
6889 * The indentation code here is deliberately a bit different from that
6890 * for the lefthand input, because we want the line breaks in
6891 * different places.
6892 */
6893 if (need_paren)
6894 {
6897 }
6898 else
6899 subindent = 0;
6900 appendContextKeyword(context, "", subindent, 0, 0);
6901
6902 /*
6903 * The output column names of the RHS sub-select don't matter.
6904 */
6906 context->colNamesVisible = false;
6907
6908 get_setop_query(op->rarg, query, context);
6909
6911
6912 if (PRETTY_INDENT(context))
6913 context->indentLevel -= subindent;
6914 if (need_paren)
6915 appendContextKeyword(context, ")", 0, 0, 0);
6916 }
6917 else
6918 {
6919 elog(ERROR, "unrecognized node type: %d",
6920 (int) nodeTag(setOp));
6921 }
6922}

References SetOperationStmt::all, appendContextKeyword(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, CHECK_FOR_INTERRUPTS, check_stack_depth(), deparse_context::colNamesVisible, Query::cteList, elog, ERROR, fb(), get_query_def(), get_setop_query(), deparse_context::indentLevel, IsA, SetOperationStmt::larg, Query::limitCount, Query::limitOffset, deparse_context::namespaces, nodeTag, SetOperationStmt::op, PRETTY_INDENT, deparse_context::prettyFlags, PRETTYINDENT_STD, SetOperationStmt::rarg, deparse_context::resultDesc, Query::rowMarks, rt_fetch, Query::rtable, SETOP_EXCEPT, SETOP_INTERSECT, SETOP_UNION, Query::setOperations, Query::sortClause, and deparse_context::wrapColumn.

Referenced by get_select_query_def(), and get_setop_query().

◆ get_simple_binary_op_name()

static const char * get_simple_binary_op_name ( OpExpr expr)
static

Definition at line 9389 of file ruleutils.c.

9390{
9391 List *args = expr->args;
9392
9393 if (list_length(args) == 2)
9394 {
9395 /* binary operator */
9396 Node *arg1 = (Node *) linitial(args);
9397 Node *arg2 = (Node *) lsecond(args);
9398 const char *op;
9399
9401 if (strlen(op) == 1)
9402 return op;
9403 }
9404 return NULL;
9405}

References OpExpr::args, exprType(), fb(), generate_operator_name(), linitial, list_length(), lsecond, and OpExpr::opno.

Referenced by isSimpleNode().

◆ get_simple_values_rte()

static RangeTblEntry * get_simple_values_rte ( Query query,
TupleDesc  resultDesc 
)
static

Definition at line 6405 of file ruleutils.c.

6406{
6408 ListCell *lc;
6409
6410 /*
6411 * We want to detect a match even if the Query also contains OLD or NEW
6412 * rule RTEs. So the idea is to scan the rtable and see if there is only
6413 * one inFromCl RTE that is a VALUES RTE.
6414 */
6415 foreach(lc, query->rtable)
6416 {
6418
6419 if (rte->rtekind == RTE_VALUES && rte->inFromCl)
6420 {
6421 if (result)
6422 return NULL; /* multiple VALUES (probably not possible) */
6423 result = rte;
6424 }
6425 else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
6426 continue; /* ignore rule entries */
6427 else
6428 return NULL; /* something else -> not simple VALUES */
6429 }
6430
6431 /*
6432 * We don't need to check the targetlist in any great detail, because
6433 * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
6434 * appear inside auto-generated sub-queries with very restricted
6435 * structure. However, DefineView might have modified the tlist by
6436 * injecting new column aliases, or we might have some other column
6437 * aliases forced by a resultDesc. We can only simplify if the RTE's
6438 * column names match the names that get_target_list() would select.
6439 */
6440 if (result)
6441 {
6442 ListCell *lcn;
6443 int colno;
6444
6445 if (list_length(query->targetList) != list_length(result->eref->colnames))
6446 return NULL; /* this probably cannot happen */
6447 colno = 0;
6448 forboth(lc, query->targetList, lcn, result->eref->colnames)
6449 {
6451 char *cname = strVal(lfirst(lcn));
6452 char *colname;
6453
6454 if (tle->resjunk)
6455 return NULL; /* this probably cannot happen */
6456
6457 /* compute name that get_target_list would use for column */
6458 colno++;
6459 if (resultDesc && colno <= resultDesc->natts)
6460 colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6461 else
6462 colname = tle->resname;
6463
6464 /* does it match the VALUES RTE? */
6465 if (colname == NULL || strcmp(colname, cname) != 0)
6466 return NULL; /* column name has been changed */
6467 }
6468 }
6469
6470 return result;
6471}

References attname, fb(), forboth, lfirst, list_length(), NameStr, result, Query::rtable, RTE_RELATION, RTE_VALUES, strVal, Query::targetList, and TupleDescAttr().

Referenced by get_basic_select_query().

◆ get_special_variable()

static void get_special_variable ( Node node,
deparse_context context,
void callback_arg 
)
static

Definition at line 8444 of file ruleutils.c.

8445{
8446 StringInfo buf = context->buf;
8447
8448 /*
8449 * For a non-Var referent, force parentheses because our caller probably
8450 * assumed a Var is a simple expression.
8451 */
8452 if (!IsA(node, Var))
8454 get_rule_expr(node, context, true);
8455 if (!IsA(node, Var))
8457}

References appendStringInfoChar(), deparse_context::buf, buf, get_rule_expr(), and IsA.

Referenced by get_variable().

◆ get_sublink_expr()

static void get_sublink_expr ( SubLink sublink,
deparse_context context 
)
static

Definition at line 12426 of file ruleutils.c.

12427{
12428 StringInfo buf = context->buf;
12429 Query *query = (Query *) (sublink->subselect);
12430 char *opname = NULL;
12431 bool need_paren;
12432
12433 if (sublink->subLinkType == ARRAY_SUBLINK)
12434 appendStringInfoString(buf, "ARRAY(");
12435 else
12437
12438 /*
12439 * Note that we print the name of only the first operator, when there are
12440 * multiple combining operators. This is an approximation that could go
12441 * wrong in various scenarios (operators in different schemas, renamed
12442 * operators, etc) but there is not a whole lot we can do about it, since
12443 * the syntax allows only one operator to be shown.
12444 */
12445 if (sublink->testexpr)
12446 {
12447 if (IsA(sublink->testexpr, OpExpr))
12448 {
12449 /* single combining operator */
12450 OpExpr *opexpr = (OpExpr *) sublink->testexpr;
12451
12452 get_rule_expr(linitial(opexpr->args), context, true);
12453 opname = generate_operator_name(opexpr->opno,
12454 exprType(linitial(opexpr->args)),
12455 exprType(lsecond(opexpr->args)));
12456 }
12457 else if (IsA(sublink->testexpr, BoolExpr))
12458 {
12459 /* multiple combining operators, = or <> cases */
12460 char *sep;
12461 ListCell *l;
12462
12464 sep = "";
12465 foreach(l, ((BoolExpr *) sublink->testexpr)->args)
12466 {
12467 OpExpr *opexpr = lfirst_node(OpExpr, l);
12468
12470 get_rule_expr(linitial(opexpr->args), context, true);
12471 if (!opname)
12473 exprType(linitial(opexpr->args)),
12474 exprType(lsecond(opexpr->args)));
12475 sep = ", ";
12476 }
12478 }
12479 else if (IsA(sublink->testexpr, RowCompareExpr))
12480 {
12481 /* multiple combining operators, < <= > >= cases */
12483
12485 get_rule_expr((Node *) rcexpr->largs, context, true);
12487 exprType(linitial(rcexpr->largs)),
12488 exprType(linitial(rcexpr->rargs)));
12490 }
12491 else
12492 elog(ERROR, "unrecognized testexpr type: %d",
12493 (int) nodeTag(sublink->testexpr));
12494 }
12495
12496 need_paren = true;
12497
12498 switch (sublink->subLinkType)
12499 {
12500 case EXISTS_SUBLINK:
12501 appendStringInfoString(buf, "EXISTS ");
12502 break;
12503
12504 case ANY_SUBLINK:
12505 if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
12506 appendStringInfoString(buf, " IN ");
12507 else
12508 appendStringInfo(buf, " %s ANY ", opname);
12509 break;
12510
12511 case ALL_SUBLINK:
12512 appendStringInfo(buf, " %s ALL ", opname);
12513 break;
12514
12515 case ROWCOMPARE_SUBLINK:
12516 appendStringInfo(buf, " %s ", opname);
12517 break;
12518
12519 case EXPR_SUBLINK:
12520 case MULTIEXPR_SUBLINK:
12521 case ARRAY_SUBLINK:
12522 need_paren = false;
12523 break;
12524
12525 case CTE_SUBLINK: /* shouldn't occur in a SubLink */
12526 default:
12527 elog(ERROR, "unrecognized sublink type: %d",
12528 (int) sublink->subLinkType);
12529 break;
12530 }
12531
12532 if (need_paren)
12534
12535 get_query_def(query, buf, context->namespaces, NULL, false,
12536 context->prettyFlags, context->wrapColumn,
12537 context->indentLevel);
12538
12539 if (need_paren)
12541 else
12543}

References ALL_SUBLINK, ANY_SUBLINK, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), OpExpr::args, ARRAY_SUBLINK, deparse_context::buf, buf, CTE_SUBLINK, elog, ERROR, EXISTS_SUBLINK, EXPR_SUBLINK, exprType(), fb(), generate_operator_name(), get_query_def(), get_rule_expr(), deparse_context::indentLevel, IsA, lfirst_node, linitial, linitial_oid, lsecond, MULTIEXPR_SUBLINK, deparse_context::namespaces, nodeTag, OpExpr::opno, deparse_context::prettyFlags, ROWCOMPARE_SUBLINK, and deparse_context::wrapColumn.

Referenced by get_rule_expr().

◆ get_tablefunc()

static void get_tablefunc ( TableFunc tf,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 12857 of file ruleutils.c.

12858{
12859 /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12860
12861 if (tf->functype == TFT_XMLTABLE)
12862 get_xmltable(tf, context, showimplicit);
12863 else if (tf->functype == TFT_JSON_TABLE)
12864 get_json_table(tf, context, showimplicit);
12865}

References fb(), TableFunc::functype, get_json_table(), get_xmltable(), TFT_JSON_TABLE, and TFT_XMLTABLE.

Referenced by get_from_clause_item(), and get_rule_expr().

◆ get_tablesample_def()

static void get_tablesample_def ( TableSampleClause tablesample,
deparse_context context 
)
static

Definition at line 13479 of file ruleutils.c.

13480{
13481 StringInfo buf = context->buf;
13482 Oid argtypes[1];
13483 int nargs;
13484 ListCell *l;
13485
13486 /*
13487 * We should qualify the handler's function name if it wouldn't be
13488 * resolved by lookup in the current search path.
13489 */
13490 argtypes[0] = INTERNALOID;
13491 appendStringInfo(buf, " TABLESAMPLE %s (",
13492 generate_function_name(tablesample->tsmhandler, 1,
13493 NIL, argtypes,
13494 false, NULL, false));
13495
13496 nargs = 0;
13497 foreach(l, tablesample->args)
13498 {
13499 if (nargs++ > 0)
13501 get_rule_expr((Node *) lfirst(l), context, false);
13502 }
13504
13505 if (tablesample->repeatable != NULL)
13506 {
13507 appendStringInfoString(buf, " REPEATABLE (");
13508 get_rule_expr((Node *) tablesample->repeatable, context, false);
13510 }
13511}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), TableSampleClause::args, deparse_context::buf, buf, fb(), generate_function_name(), get_rule_expr(), lfirst, NIL, TableSampleClause::repeatable, and TableSampleClause::tsmhandler.

Referenced by get_from_clause_item().

◆ get_target_list()

static void get_target_list ( List targetList,
deparse_context context 
)
static

Definition at line 6607 of file ruleutils.c.

6608{
6609 StringInfo buf = context->buf;
6611 bool last_was_multiline = false;
6612 char *sep;
6613 int colno;
6614 ListCell *l;
6615
6616 /* we use targetbuf to hold each TLE's text temporarily */
6618
6619 sep = " ";
6620 colno = 0;
6621 foreach(l, targetList)
6622 {
6624 char *colname;
6625 char *attname;
6626
6627 if (tle->resjunk)
6628 continue; /* ignore junk entries */
6629
6631 sep = ", ";
6632 colno++;
6633
6634 /*
6635 * Put the new field text into targetbuf so we can decide after we've
6636 * got it whether or not it needs to go on a new line.
6637 */
6639 context->buf = &targetbuf;
6640
6641 /*
6642 * We special-case Var nodes rather than using get_rule_expr. This is
6643 * needed because get_rule_expr will display a whole-row Var as
6644 * "foo.*", which is the preferred notation in most contexts, but at
6645 * the top level of a SELECT list it's not right (the parser will
6646 * expand that notation into multiple columns, yielding behavior
6647 * different from a whole-row Var). We need to call get_variable
6648 * directly so that we can tell it to do the right thing, and so that
6649 * we can get the attribute name which is the default AS label.
6650 */
6651 if (tle->expr && (IsA(tle->expr, Var)))
6652 {
6653 attname = get_variable((Var *) tle->expr, 0, true, context);
6654 }
6655 else
6656 {
6657 get_rule_expr((Node *) tle->expr, context, true);
6658
6659 /*
6660 * When colNamesVisible is true, we should always show the
6661 * assigned column name explicitly. Otherwise, show it only if
6662 * it's not FigureColname's fallback.
6663 */
6664 attname = context->colNamesVisible ? NULL : "?column?";
6665 }
6666
6667 /*
6668 * Figure out what the result column should be called. In the context
6669 * of a view, use the view's tuple descriptor (so as to pick up the
6670 * effects of any column RENAME that's been done on the view).
6671 * Otherwise, just use what we can find in the TLE.
6672 */
6673 if (context->resultDesc && colno <= context->resultDesc->natts)
6674 colname = NameStr(TupleDescAttr(context->resultDesc,
6675 colno - 1)->attname);
6676 else
6677 colname = tle->resname;
6678
6679 /* Show AS unless the column's name is correct as-is */
6680 if (colname) /* resname could be NULL */
6681 {
6682 if (attname == NULL || strcmp(attname, colname) != 0)
6683 appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
6684 }
6685
6686 /* Restore context's output buffer */
6687 context->buf = buf;
6688
6689 /* Consider line-wrapping if enabled */
6690 if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
6691 {
6692 int leading_nl_pos;
6693
6694 /* Does the new field start with a new line? */
6695 if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
6696 leading_nl_pos = 0;
6697 else
6698 leading_nl_pos = -1;
6699
6700 /* If so, we shouldn't add anything */
6701 if (leading_nl_pos >= 0)
6702 {
6703 /* instead, remove any trailing spaces currently in buf */
6705 }
6706 else
6707 {
6708 char *trailing_nl;
6709
6710 /* Locate the start of the current line in the output buffer */
6711 trailing_nl = strrchr(buf->data, '\n');
6712 if (trailing_nl == NULL)
6713 trailing_nl = buf->data;
6714 else
6715 trailing_nl++;
6716
6717 /*
6718 * Add a newline, plus some indentation, if the new field is
6719 * not the first and either the new field would cause an
6720 * overflow or the last field used more than one line.
6721 */
6722 if (colno > 1 &&
6723 ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
6727 }
6728
6729 /* Remember this field's multiline status for next iteration */
6731 (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
6732 }
6733
6734 /* Add the new field */
6736 }
6737
6738 /* clean up */
6739 pfree(targetbuf.data);
6740}

References appendBinaryStringInfo(), appendContextKeyword(), appendStringInfo(), appendStringInfoString(), attname, deparse_context::buf, buf, deparse_context::colNamesVisible, fb(), get_rule_expr(), get_variable(), initStringInfo(), IsA, lfirst, NameStr, TupleDescData::natts, pfree(), PRETTY_INDENT, PRETTYINDENT_STD, PRETTYINDENT_VAR, quote_identifier(), removeStringInfoSpaces(), resetStringInfo(), deparse_context::resultDesc, TupleDescAttr(), and deparse_context::wrapColumn.

Referenced by get_basic_select_query(), and get_returning_clause().

◆ get_update_query_def()

static void get_update_query_def ( Query query,
deparse_context context 
)
static

Definition at line 7536 of file ruleutils.c.

7537{
7538 StringInfo buf = context->buf;
7540
7541 /* Insert the WITH clause if given */
7542 get_with_clause(query, context);
7543
7544 /*
7545 * Start the query with UPDATE relname SET
7546 */
7547 rte = rt_fetch(query->resultRelation, query->rtable);
7548 Assert(rte->rtekind == RTE_RELATION);
7549 if (PRETTY_INDENT(context))
7550 {
7552 context->indentLevel += PRETTYINDENT_STD;
7553 }
7554 appendStringInfo(buf, "UPDATE %s%s",
7556 generate_relation_name(rte->relid, NIL));
7557
7558 /* Print the FOR PORTION OF, if needed */
7559 get_for_portion_of(query->forPortionOf, context);
7560
7561 /* Print the relation alias, if needed */
7562 get_rte_alias(rte, query->resultRelation, false, context);
7563
7564 appendStringInfoString(buf, " SET ");
7565
7566 /* Deparse targetlist */
7567 get_update_query_targetlist_def(query, query->targetList, context, rte);
7568
7569 /* Add the FROM clause if needed */
7570 get_from_clause(query, " FROM ", context);
7571
7572 /* Add a WHERE clause if given */
7573 if (query->jointree->quals != NULL)
7574 {
7575 appendContextKeyword(context, " WHERE ",
7577 get_rule_expr(query->jointree->quals, context, false);
7578 }
7579
7580 /* Add RETURNING if present */
7581 if (query->returningList)
7582 get_returning_clause(query, context);
7583}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, fb(), Query::forPortionOf, generate_relation_name(), get_for_portion_of(), get_from_clause(), get_returning_clause(), get_rte_alias(), get_rule_expr(), get_update_query_targetlist_def(), get_with_clause(), deparse_context::indentLevel, Query::jointree, NIL, only_marker, PRETTY_INDENT, PRETTYINDENT_STD, FromExpr::quals, Query::returningList, rt_fetch, Query::rtable, RTE_RELATION, and Query::targetList.

Referenced by get_query_def().

◆ get_update_query_targetlist_def()

static void get_update_query_targetlist_def ( Query query,
List targetList,
deparse_context context,
RangeTblEntry rte 
)
static

Definition at line 7591 of file ruleutils.c.

7593{
7594 StringInfo buf = context->buf;
7595 ListCell *l;
7598 const char *sep;
7601
7602 /*
7603 * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
7604 * into a list. We expect them to appear, in ID order, in resjunk tlist
7605 * entries.
7606 */
7607 ma_sublinks = NIL;
7608 if (query->hasSubLinks) /* else there can't be any */
7609 {
7610 foreach(l, targetList)
7611 {
7613
7614 if (tle->resjunk && IsA(tle->expr, SubLink))
7615 {
7616 SubLink *sl = (SubLink *) tle->expr;
7617
7618 if (sl->subLinkType == MULTIEXPR_SUBLINK)
7619 {
7621 Assert(sl->subLinkId == list_length(ma_sublinks));
7622 }
7623 }
7624 }
7625 }
7629
7630 /* Add the comma separated list of 'attname = value' */
7631 sep = "";
7632 foreach(l, targetList)
7633 {
7635 Node *expr;
7636
7637 if (tle->resjunk)
7638 continue; /* ignore junk entries */
7639
7640 /* Emit separator (OK whether we're in multiassignment or not) */
7642 sep = ", ";
7643
7644 /*
7645 * Check to see if we're starting a multiassignment group: if so,
7646 * output a left paren.
7647 */
7648 if (next_ma_cell != NULL && cur_ma_sublink == NULL)
7649 {
7650 /*
7651 * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
7652 * Param. That could be buried under FieldStores and
7653 * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
7654 * and underneath those there could be an implicit type coercion.
7655 * Because we would ignore implicit type coercions anyway, we
7656 * don't need to be as careful as processIndirection() is about
7657 * descending past implicit CoerceToDomains.
7658 */
7659 expr = (Node *) tle->expr;
7660 while (expr)
7661 {
7662 if (IsA(expr, FieldStore))
7663 {
7664 FieldStore *fstore = (FieldStore *) expr;
7665
7666 expr = (Node *) linitial(fstore->newvals);
7667 }
7668 else if (IsA(expr, SubscriptingRef))
7669 {
7670 SubscriptingRef *sbsref = (SubscriptingRef *) expr;
7671
7672 if (sbsref->refassgnexpr == NULL)
7673 break;
7674
7675 expr = (Node *) sbsref->refassgnexpr;
7676 }
7677 else if (IsA(expr, CoerceToDomain))
7678 {
7680
7681 if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
7682 break;
7683 expr = (Node *) cdomain->arg;
7684 }
7685 else
7686 break;
7687 }
7688 expr = strip_implicit_coercions(expr);
7689
7690 if (expr && IsA(expr, Param) &&
7691 ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
7692 {
7696 Assert(((Param *) expr)->paramid ==
7697 ((cur_ma_sublink->subLinkId << 16) | 1));
7699 }
7700 }
7701
7702 /*
7703 * Put out name of target column; look in the catalogs, not at
7704 * tle->resname, since resname will fail to track RENAME.
7705 */
7708 tle->resno,
7709 false)));
7710
7711 /*
7712 * Print any indirection needed (subfields or subscripts), and strip
7713 * off the top-level nodes representing the indirection assignments.
7714 */
7715 expr = processIndirection((Node *) tle->expr, context);
7716
7717 /*
7718 * If we're in a multiassignment, skip printing anything more, unless
7719 * this is the last column; in which case, what we print should be the
7720 * sublink, not the Param.
7721 */
7722 if (cur_ma_sublink != NULL)
7723 {
7724 if (--remaining_ma_columns > 0)
7725 continue; /* not the last column of multiassignment */
7727 expr = (Node *) cur_ma_sublink;
7729 }
7730
7732
7733 get_rule_expr(expr, context, false);
7734 }
7735}

References appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, COERCE_IMPLICIT_CAST, count_nonjunk_tlist_entries(), fb(), get_attname(), get_rule_expr(), IsA, lappend(), lfirst, linitial, list_head(), list_length(), lnext(), MULTIEXPR_SUBLINK, FieldStore::newvals, NIL, PARAM_MULTIEXPR, processIndirection(), quote_identifier(), SubscriptingRef::refassgnexpr, and strip_implicit_coercions().

Referenced by get_insert_query_def(), get_merge_query_def(), and get_update_query_def().

◆ get_utility_query_def()

static void get_utility_query_def ( Query query,
deparse_context context 
)
static

Definition at line 7952 of file ruleutils.c.

7953{
7954 StringInfo buf = context->buf;
7955
7956 if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7957 {
7958 NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7959
7960 appendContextKeyword(context, "",
7961 0, PRETTYINDENT_STD, 1);
7962 appendStringInfo(buf, "NOTIFY %s",
7963 quote_identifier(stmt->conditionname));
7964 if (stmt->payload)
7965 {
7967 simple_quote_literal(buf, stmt->payload);
7968 }
7969 }
7970 else
7971 {
7972 /* Currently only NOTIFY utility commands can appear in rules */
7973 elog(ERROR, "unexpected utility statement type");
7974 }
7975}

References appendContextKeyword(), appendStringInfo(), appendStringInfoString(), deparse_context::buf, buf, elog, ERROR, IsA, PRETTYINDENT_STD, quote_identifier(), simple_quote_literal(), stmt, and Query::utilityStmt.

Referenced by get_query_def().

◆ get_values_def()

static void get_values_def ( List values_lists,
deparse_context context 
)
static

Definition at line 6089 of file ruleutils.c.

6090{
6091 StringInfo buf = context->buf;
6092 bool first_list = true;
6093 ListCell *vtl;
6094
6095 appendStringInfoString(buf, "VALUES ");
6096
6097 foreach(vtl, values_lists)
6098 {
6099 List *sublist = (List *) lfirst(vtl);
6100 bool first_col = true;
6101 ListCell *lc;
6102
6103 if (first_list)
6104 first_list = false;
6105 else
6107
6109 foreach(lc, sublist)
6110 {
6111 Node *col = (Node *) lfirst(lc);
6112
6113 if (first_col)
6114 first_col = false;
6115 else
6117
6118 /*
6119 * Print the value. Whole-row Vars need special treatment.
6120 */
6121 get_rule_expr_toplevel(col, context, false);
6122 }
6124 }
6125}

References appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, fb(), get_rule_expr_toplevel(), and lfirst.

Referenced by get_basic_select_query(), get_from_clause_item(), and get_insert_query_def().

◆ get_variable()

static char * get_variable ( Var var,
int  levelsup,
bool  istoplevel,
deparse_context context 
)
static

Definition at line 8162 of file ruleutils.c.

8163{
8164 StringInfo buf = context->buf;
8167 int netlevelsup;
8169 int varno;
8170 AttrNumber varattno;
8172 char *refname;
8173 char *attname;
8174 bool need_prefix;
8175
8176 /* Find appropriate nesting depth */
8177 netlevelsup = var->varlevelsup + levelsup;
8178 if (netlevelsup >= list_length(context->namespaces))
8179 elog(ERROR, "bogus varlevelsup: %d offset %d",
8180 var->varlevelsup, levelsup);
8182 netlevelsup);
8183
8184 /*
8185 * If we have a syntactic referent for the Var, and we're working from a
8186 * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8187 * on the semantic referent. (Forcing use of the semantic referent when
8188 * printing plan trees is a design choice that's perhaps more motivated by
8189 * backwards compatibility than anything else. But it does have the
8190 * advantage of making plans more explicit.)
8191 */
8192 if (var->varnosyn > 0 && dpns->plan == NULL)
8193 {
8194 varno = var->varnosyn;
8195 varattno = var->varattnosyn;
8196 }
8197 else
8198 {
8199 varno = var->varno;
8200 varattno = var->varattno;
8201 }
8202
8203 /*
8204 * Try to find the relevant RTE in this rtable. In a plan tree, it's
8205 * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8206 * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
8207 * find the aliases previously assigned for this RTE.
8208 */
8209 if (varno >= 1 && varno <= list_length(dpns->rtable))
8210 {
8211 /*
8212 * We might have been asked to map child Vars to some parent relation.
8213 */
8214 if (context->appendparents && dpns->appendrels)
8215 {
8216 int pvarno = varno;
8217 AttrNumber pvarattno = varattno;
8218 AppendRelInfo *appinfo = dpns->appendrels[pvarno];
8219 bool found = false;
8220
8221 /* Only map up to inheritance parents, not UNION ALL appendrels */
8222 while (appinfo &&
8223 rt_fetch(appinfo->parent_relid,
8224 dpns->rtable)->rtekind == RTE_RELATION)
8225 {
8226 found = false;
8227 if (pvarattno > 0) /* system columns stay as-is */
8228 {
8229 if (pvarattno > appinfo->num_child_cols)
8230 break; /* safety check */
8231 pvarattno = appinfo->parent_colnos[pvarattno - 1];
8232 if (pvarattno == 0)
8233 break; /* Var is local to child */
8234 }
8235
8237 found = true;
8238
8239 /* If the parent is itself a child, continue up. */
8240 Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
8241 appinfo = dpns->appendrels[pvarno];
8242 }
8243
8244 /*
8245 * If we found an ancestral rel, and that rel is included in
8246 * appendparents, print that column not the original one.
8247 */
8248 if (found && bms_is_member(pvarno, context->appendparents))
8249 {
8250 varno = pvarno;
8251 varattno = pvarattno;
8252 }
8253 }
8254
8255 rte = rt_fetch(varno, dpns->rtable);
8256
8257 /* might be returning old/new column value */
8259 refname = dpns->ret_old_alias;
8260 else if (var->varreturningtype == VAR_RETURNING_NEW)
8261 refname = dpns->ret_new_alias;
8262 else
8263 refname = (char *) list_nth(dpns->rtable_names, varno - 1);
8264
8266 attnum = varattno;
8267 }
8268 else
8269 {
8270 resolve_special_varno((Node *) var, context,
8272 return NULL;
8273 }
8274
8275 /*
8276 * The planner will sometimes emit Vars referencing resjunk elements of a
8277 * subquery's target list (this is currently only possible if it chooses
8278 * to generate a "physical tlist" for a SubqueryScan or CteScan node).
8279 * Although we prefer to print subquery-referencing Vars using the
8280 * subquery's alias, that's not possible for resjunk items since they have
8281 * no alias. So in that case, drill down to the subplan and print the
8282 * contents of the referenced tlist item. This works because in a plan
8283 * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
8284 * we'll have set dpns->inner_plan to reference the child plan node.
8285 */
8286 if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
8287 attnum > list_length(rte->eref->colnames) &&
8288 dpns->inner_plan)
8289 {
8292
8293 tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8294 if (!tle)
8295 elog(ERROR, "invalid attnum %d for relation \"%s\"",
8296 attnum, rte->eref->aliasname);
8297
8298 Assert(netlevelsup == 0);
8299 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8300
8301 /*
8302 * Force parentheses because our caller probably assumed a Var is a
8303 * simple expression.
8304 */
8305 if (!IsA(tle->expr, Var))
8307 get_rule_expr((Node *) tle->expr, context, true);
8308 if (!IsA(tle->expr, Var))
8310
8312 return NULL;
8313 }
8314
8315 /*
8316 * If it's an unnamed join, look at the expansion of the alias variable.
8317 * If it's a simple reference to one of the input vars, then recursively
8318 * print the name of that var instead. When it's not a simple reference,
8319 * we have to just print the unqualified join column name. (This can only
8320 * happen with "dangerous" merged columns in a JOIN USING; we took pains
8321 * previously to make the unqualified column name unique in such cases.)
8322 *
8323 * This wouldn't work in decompiling plan trees, because we don't store
8324 * joinaliasvars lists after planning; but a plan tree should never
8325 * contain a join alias variable.
8326 */
8327 if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
8328 {
8329 if (rte->joinaliasvars == NIL)
8330 elog(ERROR, "cannot decompile join alias var in plan tree");
8331 if (attnum > 0)
8332 {
8333 Var *aliasvar;
8334
8335 aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
8336 /* we intentionally don't strip implicit coercions here */
8337 if (aliasvar && IsA(aliasvar, Var))
8338 {
8339 return get_variable(aliasvar, var->varlevelsup + levelsup,
8340 istoplevel, context);
8341 }
8342 }
8343
8344 /*
8345 * Unnamed join has no refname. (Note: since it's unnamed, there is
8346 * no way the user could have referenced it to create a whole-row Var
8347 * for it. So we don't have to cover that case below.)
8348 */
8349 Assert(refname == NULL);
8350 }
8351
8353 attname = NULL;
8354 else if (attnum > 0)
8355 {
8356 /* Get column name to use from the colinfo struct */
8357 if (attnum > colinfo->num_cols)
8358 elog(ERROR, "invalid attnum %d for relation \"%s\"",
8359 attnum, rte->eref->aliasname);
8360 attname = colinfo->colnames[attnum - 1];
8361
8362 /*
8363 * If we find a Var referencing a dropped column, it seems better to
8364 * print something (anything) than to fail. In general this should
8365 * not happen, but it used to be possible for some cases involving
8366 * functions returning named composite types, and perhaps there are
8367 * still bugs out there.
8368 */
8369 if (attname == NULL)
8370 attname = "?dropped?column?";
8371 }
8372 else
8373 {
8374 /* System column - name is fixed, get it from the catalog */
8376 }
8377
8378 need_prefix = (context->varprefix || attname == NULL ||
8380
8381 /*
8382 * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
8383 * clause, we may need to add a table-name prefix to prevent
8384 * findTargetlistEntrySQL92 from misinterpreting the name as an
8385 * output-column name. To avoid cluttering the output with unnecessary
8386 * prefixes, do so only if there is a name match to a SELECT tlist item
8387 * that is different from the Var.
8388 */
8389 if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
8390 {
8391 int colno = 0;
8392
8394 {
8395 char *colname;
8396
8397 if (tle->resjunk)
8398 continue; /* ignore junk entries */
8399 colno++;
8400
8401 /* This must match colname-choosing logic in get_target_list() */
8402 if (context->resultDesc && colno <= context->resultDesc->natts)
8403 colname = NameStr(TupleDescAttr(context->resultDesc,
8404 colno - 1)->attname);
8405 else
8406 colname = tle->resname;
8407
8408 if (colname && strcmp(colname, attname) == 0 &&
8409 !equal(var, tle->expr))
8410 {
8411 need_prefix = true;
8412 break;
8413 }
8414 }
8415 }
8416
8417 if (refname && need_prefix)
8418 {
8421 }
8422 if (attname)
8424 else
8425 {
8427 if (istoplevel)
8428 appendStringInfo(buf, "::%s",
8429 format_type_with_typemod(var->vartype,
8430 var->vartypmod));
8431 }
8432
8433 return attname;
8434}

References deparse_context::appendparents, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, attname, attnum, bms_is_member(), deparse_context::buf, buf, deparse_columns_fetch, elog, equal(), ERROR, fb(), foreach_node, format_type_with_typemod(), get_rte_attribute_name(), get_rule_expr(), get_special_variable(), get_tle_by_resno(), get_variable(), deparse_context::inGroupBy, InvalidAttrNumber, IsA, list_length(), list_nth(), deparse_context::namespaces, NameStr, TupleDescData::natts, NIL, AppendRelInfo::parent_relid, pop_child_plan(), push_child_plan(), quote_identifier(), resolve_special_varno(), deparse_context::resultDesc, rt_fetch, RTE_CTE, RTE_JOIN, RTE_RELATION, RTE_SUBQUERY, deparse_context::targetList, TupleDescAttr(), VAR_RETURNING_DEFAULT, VAR_RETURNING_NEW, VAR_RETURNING_OLD, Var::varattno, deparse_context::varInOrderBy, Var::varlevelsup, Var::varno, deparse_context::varprefix, and Var::varreturningtype.

Referenced by get_rule_expr(), get_rule_expr_toplevel(), get_rule_sortgroupclause(), get_target_list(), and get_variable().

◆ get_window_frame_options()

static void get_window_frame_options ( int  frameOptions,
Node startOffset,
Node endOffset,
deparse_context context 
)
static

Definition at line 7206 of file ruleutils.c.

7209{
7210 StringInfo buf = context->buf;
7211
7212 if (frameOptions & FRAMEOPTION_NONDEFAULT)
7213 {
7214 if (frameOptions & FRAMEOPTION_RANGE)
7215 appendStringInfoString(buf, "RANGE ");
7216 else if (frameOptions & FRAMEOPTION_ROWS)
7217 appendStringInfoString(buf, "ROWS ");
7218 else if (frameOptions & FRAMEOPTION_GROUPS)
7219 appendStringInfoString(buf, "GROUPS ");
7220 else
7221 Assert(false);
7222 if (frameOptions & FRAMEOPTION_BETWEEN)
7223 appendStringInfoString(buf, "BETWEEN ");
7224 if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
7225 appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
7226 else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
7227 appendStringInfoString(buf, "CURRENT ROW ");
7228 else if (frameOptions & FRAMEOPTION_START_OFFSET)
7229 {
7230 get_rule_expr(startOffset, context, false);
7231 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
7232 appendStringInfoString(buf, " PRECEDING ");
7233 else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
7234 appendStringInfoString(buf, " FOLLOWING ");
7235 else
7236 Assert(false);
7237 }
7238 else
7239 Assert(false);
7240 if (frameOptions & FRAMEOPTION_BETWEEN)
7241 {
7242 appendStringInfoString(buf, "AND ");
7243 if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
7244 appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
7245 else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
7246 appendStringInfoString(buf, "CURRENT ROW ");
7247 else if (frameOptions & FRAMEOPTION_END_OFFSET)
7248 {
7249 get_rule_expr(endOffset, context, false);
7250 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
7251 appendStringInfoString(buf, " PRECEDING ");
7252 else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
7253 appendStringInfoString(buf, " FOLLOWING ");
7254 else
7255 Assert(false);
7256 }
7257 else
7258 Assert(false);
7259 }
7260 if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
7261 appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
7262 else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
7263 appendStringInfoString(buf, "EXCLUDE GROUP ");
7264 else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
7265 appendStringInfoString(buf, "EXCLUDE TIES ");
7266 /* we will now have a trailing space; remove it */
7267 buf->data[--(buf->len)] = '\0';
7268 }
7269}

References appendStringInfoString(), Assert, deparse_context::buf, buf, FRAMEOPTION_BETWEEN, FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_OFFSET, FRAMEOPTION_END_OFFSET_FOLLOWING, FRAMEOPTION_END_OFFSET_PRECEDING, FRAMEOPTION_END_UNBOUNDED_FOLLOWING, FRAMEOPTION_EXCLUDE_CURRENT_ROW, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_GROUPS, FRAMEOPTION_NONDEFAULT, FRAMEOPTION_RANGE, FRAMEOPTION_ROWS, FRAMEOPTION_START_CURRENT_ROW, FRAMEOPTION_START_OFFSET, FRAMEOPTION_START_OFFSET_FOLLOWING, FRAMEOPTION_START_OFFSET_PRECEDING, FRAMEOPTION_START_UNBOUNDED_PRECEDING, and get_rule_expr().

Referenced by get_rule_windowspec(), and get_window_frame_options_for_explain().

◆ get_window_frame_options_for_explain()

char * get_window_frame_options_for_explain ( int  frameOptions,
Node startOffset,
Node endOffset,
List dpcontext,
bool  forceprefix 
)

Definition at line 7275 of file ruleutils.c.

7278{
7280 deparse_context context;
7281
7283 context.buf = &buf;
7284 context.namespaces = dpcontext;
7285 context.resultDesc = NULL;
7286 context.targetList = NIL;
7287 context.windowClause = NIL;
7288 context.varprefix = forceprefix;
7289 context.prettyFlags = 0;
7291 context.indentLevel = 0;
7292 context.colNamesVisible = true;
7293 context.inGroupBy = false;
7294 context.varInOrderBy = false;
7295 context.appendparents = NULL;
7296
7297 get_window_frame_options(frameOptions, startOffset, endOffset, &context);
7298
7299 return buf.data;
7300}

References deparse_context::appendparents, deparse_context::buf, buf, deparse_context::colNamesVisible, fb(), get_window_frame_options(), deparse_context::indentLevel, deparse_context::inGroupBy, initStringInfo(), deparse_context::namespaces, NIL, deparse_context::prettyFlags, deparse_context::resultDesc, deparse_context::targetList, deparse_context::varInOrderBy, deparse_context::varprefix, deparse_context::windowClause, WRAP_COLUMN_DEFAULT, and deparse_context::wrapColumn.

Referenced by show_window_def().

◆ get_windowfunc_expr()

static void get_windowfunc_expr ( WindowFunc wfunc,
deparse_context context 
)
static

Definition at line 11613 of file ruleutils.c.

11614{
11615 get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
11616}

References fb(), and get_windowfunc_expr_helper().

Referenced by get_rule_expr().

◆ get_windowfunc_expr_helper()

static void get_windowfunc_expr_helper ( WindowFunc wfunc,
deparse_context context,
const char funcname,
const char options,
bool  is_json_objectagg 
)
static

Definition at line 11624 of file ruleutils.c.

11627{
11628 StringInfo buf = context->buf;
11629 Oid argtypes[FUNC_MAX_ARGS];
11630 int nargs;
11631 List *argnames;
11632 ListCell *l;
11633
11634 if (list_length(wfunc->args) > FUNC_MAX_ARGS)
11635 ereport(ERROR,
11637 errmsg("too many arguments")));
11638 nargs = 0;
11639 argnames = NIL;
11640 foreach(l, wfunc->args)
11641 {
11642 Node *arg = (Node *) lfirst(l);
11643
11644 if (IsA(arg, NamedArgExpr))
11645 argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
11646 argtypes[nargs] = exprType(arg);
11647 nargs++;
11648 }
11649
11650 if (!funcname)
11651 funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
11652 argtypes, false, NULL,
11653 context->inGroupBy);
11654
11655 appendStringInfo(buf, "%s(", funcname);
11656
11657 /* winstar can be set only in zero-argument aggregates */
11658 if (wfunc->winstar)
11660 else
11661 {
11663 {
11664 get_rule_expr((Node *) linitial(wfunc->args), context, false);
11666 get_rule_expr((Node *) lsecond(wfunc->args), context, false);
11667 }
11668 else
11669 get_rule_expr((Node *) wfunc->args, context, true);
11670 }
11671
11672 if (options)
11674
11675 if (wfunc->aggfilter != NULL)
11676 {
11677 appendStringInfoString(buf, ") FILTER (WHERE ");
11678 get_rule_expr((Node *) wfunc->aggfilter, context, false);
11679 }
11680
11682
11683 if (wfunc->ignore_nulls == PARSER_IGNORE_NULLS)
11684 appendStringInfoString(buf, "IGNORE NULLS ");
11685
11686 appendStringInfoString(buf, "OVER ");
11687
11688 if (context->windowClause)
11689 {
11690 /* Query-decompilation case: search the windowClause list */
11691 foreach(l, context->windowClause)
11692 {
11693 WindowClause *wc = (WindowClause *) lfirst(l);
11694
11695 if (wc->winref == wfunc->winref)
11696 {
11697 if (wc->name)
11699 else
11700 get_rule_windowspec(wc, context->targetList, context);
11701 break;
11702 }
11703 }
11704 if (l == NULL)
11705 elog(ERROR, "could not find window clause for winref %u",
11706 wfunc->winref);
11707 }
11708 else
11709 {
11710 /*
11711 * In EXPLAIN, search the namespace stack for a matching WindowAgg
11712 * node (probably it's always the first entry), and print winname.
11713 */
11714 foreach(l, context->namespaces)
11715 {
11717
11718 if (dpns->plan && IsA(dpns->plan, WindowAgg))
11719 {
11721
11722 if (wagg->winref == wfunc->winref)
11723 {
11725 break;
11726 }
11727 }
11728 }
11729 if (l == NULL)
11730 elog(ERROR, "could not find window clause for winref %u",
11731 wfunc->winref);
11732 }
11733}

References WindowFunc::aggfilter, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, WindowFunc::args, deparse_context::buf, buf, elog, ereport, errcode(), errmsg, ERROR, exprType(), fb(), FUNC_MAX_ARGS, funcname, generate_function_name(), get_rule_expr(), get_rule_windowspec(), WindowFunc::ignore_nulls, deparse_context::inGroupBy, IsA, lappend(), lfirst, linitial, list_length(), lsecond, deparse_context::namespaces, NIL, PARSER_IGNORE_NULLS, WindowAgg::plan, quote_identifier(), deparse_context::targetList, deparse_context::windowClause, WindowFunc::winfnoid, WindowClause::winref, and WindowFunc::winref.

Referenced by get_json_agg_constructor(), and get_windowfunc_expr().

◆ get_with_clause()

static void get_with_clause ( Query query,
deparse_context context 
)
static

Definition at line 6132 of file ruleutils.c.

6133{
6134 StringInfo buf = context->buf;
6135 const char *sep;
6136 ListCell *l;
6137
6138 if (query->cteList == NIL)
6139 return;
6140
6141 if (PRETTY_INDENT(context))
6142 {
6143 context->indentLevel += PRETTYINDENT_STD;
6145 }
6146
6147 if (query->hasRecursive)
6148 sep = "WITH RECURSIVE ";
6149 else
6150 sep = "WITH ";
6151 foreach(l, query->cteList)
6152 {
6154
6157 if (cte->aliascolnames)
6158 {
6159 bool first = true;
6160 ListCell *col;
6161
6163 foreach(col, cte->aliascolnames)
6164 {
6165 if (first)
6166 first = false;
6167 else
6171 }
6173 }
6174 appendStringInfoString(buf, " AS ");
6175 switch (cte->ctematerialized)
6176 {
6178 break;
6180 appendStringInfoString(buf, "MATERIALIZED ");
6181 break;
6183 appendStringInfoString(buf, "NOT MATERIALIZED ");
6184 break;
6185 }
6187 if (PRETTY_INDENT(context))
6188 appendContextKeyword(context, "", 0, 0, 0);
6189 get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
6190 true,
6191 context->prettyFlags, context->wrapColumn,
6192 context->indentLevel);
6193 if (PRETTY_INDENT(context))
6194 appendContextKeyword(context, "", 0, 0, 0);
6196
6197 if (cte->search_clause)
6198 {
6199 bool first = true;
6200 ListCell *lc;
6201
6202 appendStringInfo(buf, " SEARCH %s FIRST BY ",
6203 cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
6204
6205 foreach(lc, cte->search_clause->search_col_list)
6206 {
6207 if (first)
6208 first = false;
6209 else
6213 }
6214
6215 appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
6216 }
6217
6218 if (cte->cycle_clause)
6219 {
6220 bool first = true;
6221 ListCell *lc;
6222
6223 appendStringInfoString(buf, " CYCLE ");
6224
6225 foreach(lc, cte->cycle_clause->cycle_col_list)
6226 {
6227 if (first)
6228 first = false;
6229 else
6233 }
6234
6235 appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
6236
6237 {
6238 Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
6239 Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
6240
6241 if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
6242 cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
6243 {
6244 appendStringInfoString(buf, " TO ");
6245 get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
6246 appendStringInfoString(buf, " DEFAULT ");
6247 get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
6248 }
6249 }
6250
6251 appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
6252 }
6253
6254 sep = ", ";
6255 }
6256
6257 if (PRETTY_INDENT(context))
6258 {
6259 context->indentLevel -= PRETTYINDENT_STD;
6260 appendContextKeyword(context, "", 0, 0, 0);
6261 }
6262 else
6264}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, castNode, Const::consttype, Query::cteList, CTEMaterializeAlways, CommonTableExpr::ctematerialized, CTEMaterializeDefault, CTEMaterializeNever, CommonTableExpr::ctename, CommonTableExpr::ctequery, DatumGetBool(), fb(), get_query_def(), get_rule_expr(), deparse_context::indentLevel, lfirst, deparse_context::namespaces, NIL, PRETTY_INDENT, deparse_context::prettyFlags, PRETTYINDENT_STD, quote_identifier(), strVal, and deparse_context::wrapColumn.

Referenced by get_delete_query_def(), get_insert_query_def(), get_merge_query_def(), get_select_query_def(), and get_update_query_def().

◆ get_xmltable()

static void get_xmltable ( TableFunc tf,
deparse_context context,
bool  showimplicit 
)
static

Definition at line 12551 of file ruleutils.c.

12552{
12553 StringInfo buf = context->buf;
12554
12555 appendStringInfoString(buf, "XMLTABLE(");
12556
12557 if (tf->ns_uris != NIL)
12558 {
12559 ListCell *lc1,
12560 *lc2;
12561 bool first = true;
12562
12563 appendStringInfoString(buf, "XMLNAMESPACES (");
12564 forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
12565 {
12566 Node *expr = (Node *) lfirst(lc1);
12568
12569 if (!first)
12571 else
12572 first = false;
12573
12574 if (ns_node != NULL)
12575 {
12576 get_rule_expr(expr, context, showimplicit);
12577 appendStringInfo(buf, " AS %s",
12579 }
12580 else
12581 {
12582 appendStringInfoString(buf, "DEFAULT ");
12583 get_rule_expr(expr, context, showimplicit);
12584 }
12585 }
12587 }
12588
12590 get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
12591 appendStringInfoString(buf, ") PASSING (");
12592 get_rule_expr((Node *) tf->docexpr, context, showimplicit);
12594
12595 if (tf->colexprs != NIL)
12596 {
12597 ListCell *l1;
12598 ListCell *l2;
12599 ListCell *l3;
12600 ListCell *l4;
12601 ListCell *l5;
12602 int colnum = 0;
12603
12604 appendStringInfoString(buf, " COLUMNS ");
12605 forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
12606 l4, tf->colexprs, l5, tf->coldefexprs)
12607 {
12608 char *colname = strVal(lfirst(l1));
12609 Oid typid = lfirst_oid(l2);
12610 int32 typmod = lfirst_int(l3);
12611 Node *colexpr = (Node *) lfirst(l4);
12612 Node *coldefexpr = (Node *) lfirst(l5);
12613 bool ordinality = (tf->ordinalitycol == colnum);
12614 bool notnull = bms_is_member(colnum, tf->notnulls);
12615
12616 if (colnum > 0)
12618 colnum++;
12619
12620 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12621 ordinality ? "FOR ORDINALITY" :
12622 format_type_with_typemod(typid, typmod));
12623 if (ordinality)
12624 continue;
12625
12626 if (coldefexpr != NULL)
12627 {
12628 appendStringInfoString(buf, " DEFAULT (");
12629 get_rule_expr((Node *) coldefexpr, context, showimplicit);
12631 }
12632 if (colexpr != NULL)
12633 {
12634 appendStringInfoString(buf, " PATH (");
12635 get_rule_expr((Node *) colexpr, context, showimplicit);
12637 }
12638 if (notnull)
12639 appendStringInfoString(buf, " NOT NULL");
12640 }
12641 }
12642
12644}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), bms_is_member(), deparse_context::buf, buf, TableFunc::colexprs, TableFunc::docexpr, fb(), forboth, forfive, format_type_with_typemod(), get_rule_expr(), lfirst, lfirst_int, lfirst_node, lfirst_oid, NIL, quote_identifier(), TableFunc::rowexpr, and strVal.

Referenced by get_tablefunc().

◆ has_dangerous_join_using()

static bool has_dangerous_join_using ( deparse_namespace dpns,
Node jtnode 
)
static

Definition at line 4502 of file ruleutils.c.

4503{
4504 if (IsA(jtnode, RangeTblRef))
4505 {
4506 /* nothing to do here */
4507 }
4508 else if (IsA(jtnode, FromExpr))
4509 {
4510 FromExpr *f = (FromExpr *) jtnode;
4511 ListCell *lc;
4512
4513 foreach(lc, f->fromlist)
4514 {
4516 return true;
4517 }
4518 }
4519 else if (IsA(jtnode, JoinExpr))
4520 {
4521 JoinExpr *j = (JoinExpr *) jtnode;
4522
4523 /* Is it an unnamed JOIN with USING? */
4524 if (j->alias == NULL && j->usingClause)
4525 {
4526 /*
4527 * Yes, so check each join alias var to see if any of them are not
4528 * simple references to underlying columns. If so, we have a
4529 * dangerous situation and must pick unique aliases.
4530 */
4531 RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4532
4533 /* We need only examine the merged columns */
4534 for (int i = 0; i < jrte->joinmergedcols; i++)
4535 {
4536 Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4537
4538 if (!IsA(aliasvar, Var))
4539 return true;
4540 }
4541 }
4542
4543 /* Nope, but inspect children */
4544 if (has_dangerous_join_using(dpns, j->larg))
4545 return true;
4546 if (has_dangerous_join_using(dpns, j->rarg))
4547 return true;
4548 }
4549 else
4550 elog(ERROR, "unrecognized node type: %d",
4551 (int) nodeTag(jtnode));
4552 return false;
4553}

References elog, ERROR, fb(), FromExpr::fromlist, has_dangerous_join_using(), i, IsA, j, lfirst, list_nth(), nodeTag, and rt_fetch.

Referenced by has_dangerous_join_using(), and set_deparse_for_query().

◆ identify_join_columns()

static void identify_join_columns ( JoinExpr j,
RangeTblEntry jrte,
deparse_columns colinfo 
)
static

Definition at line 5427 of file ruleutils.c.

5429{
5430 int numjoincols;
5431 int jcolno;
5432 int rcolno;
5433 ListCell *lc;
5434
5435 /* Extract left/right child RT indexes */
5436 if (IsA(j->larg, RangeTblRef))
5437 colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
5438 else if (IsA(j->larg, JoinExpr))
5439 colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
5440 else
5441 elog(ERROR, "unrecognized node type in jointree: %d",
5442 (int) nodeTag(j->larg));
5443 if (IsA(j->rarg, RangeTblRef))
5444 colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
5445 else if (IsA(j->rarg, JoinExpr))
5446 colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
5447 else
5448 elog(ERROR, "unrecognized node type in jointree: %d",
5449 (int) nodeTag(j->rarg));
5450
5451 /* Assert children will be processed earlier than join in second pass */
5452 Assert(colinfo->leftrti < j->rtindex);
5453 Assert(colinfo->rightrti < j->rtindex);
5454
5455 /* Initialize result arrays with zeroes */
5456 numjoincols = list_length(jrte->joinaliasvars);
5457 Assert(numjoincols == list_length(jrte->eref->colnames));
5458 colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
5459 colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
5460
5461 /*
5462 * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
5463 * Recall that the column(s) merged due to USING are the first column(s)
5464 * of the join output. We need not do anything special while scanning
5465 * joinleftcols, but while scanning joinrightcols we must distinguish
5466 * merged from unmerged columns.
5467 */
5468 jcolno = 0;
5469 foreach(lc, jrte->joinleftcols)
5470 {
5471 int leftattno = lfirst_int(lc);
5472
5473 colinfo->leftattnos[jcolno++] = leftattno;
5474 }
5475 rcolno = 0;
5476 foreach(lc, jrte->joinrightcols)
5477 {
5478 int rightattno = lfirst_int(lc);
5479
5480 if (rcolno < jrte->joinmergedcols) /* merged column? */
5481 colinfo->rightattnos[rcolno] = rightattno;
5482 else
5483 colinfo->rightattnos[jcolno++] = rightattno;
5484 rcolno++;
5485 }
5487}

References Assert, elog, ERROR, fb(), IsA, j, lfirst_int, list_length(), nodeTag, and palloc0().

Referenced by set_using_names().

◆ is_input_argument()

static bool is_input_argument ( int  nth,
const char argmodes 
)
static

Definition at line 3808 of file ruleutils.c.

3809{
3810 return (!argmodes
3814}

References fb().

Referenced by pg_get_function_arg_default().

◆ isSimpleNode()

static bool isSimpleNode ( Node node,
Node parentNode,
int  prettyFlags 
)
static

Definition at line 9415 of file ruleutils.c.

9416{
9417 if (!node)
9418 return false;
9419
9420 switch (nodeTag(node))
9421 {
9422 case T_Var:
9423 case T_Const:
9424 case T_Param:
9426 case T_SetToDefault:
9427 case T_CurrentOfExpr:
9428 /* single words: always simple */
9429 return true;
9430
9431 case T_SubscriptingRef:
9432 case T_ArrayExpr:
9433 case T_RowExpr:
9434 case T_CoalesceExpr:
9435 case T_MinMaxExpr:
9436 case T_SQLValueFunction:
9437 case T_XmlExpr:
9438 case T_NextValueExpr:
9439 case T_NullIfExpr:
9440 case T_Aggref:
9441 case T_GroupingFunc:
9442 case T_WindowFunc:
9443 case T_MergeSupportFunc:
9444 case T_FuncExpr:
9446 case T_JsonExpr:
9447 /* function-like: name(..) or name[..] */
9448 return true;
9449
9450 /* CASE keywords act as parentheses */
9451 case T_CaseExpr:
9452 return true;
9453
9454 case T_FieldSelect:
9455
9456 /*
9457 * appears simple since . has top precedence, unless parent is
9458 * T_FieldSelect itself!
9459 */
9460 return !IsA(parentNode, FieldSelect);
9461
9462 case T_FieldStore:
9463
9464 /*
9465 * treat like FieldSelect (probably doesn't matter)
9466 */
9467 return !IsA(parentNode, FieldStore);
9468
9469 case T_CoerceToDomain:
9470 /* maybe simple, check args */
9471 return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
9472 node, prettyFlags);
9473 case T_RelabelType:
9474 return isSimpleNode((Node *) ((RelabelType *) node)->arg,
9475 node, prettyFlags);
9476 case T_CoerceViaIO:
9477 return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
9478 node, prettyFlags);
9479 case T_ArrayCoerceExpr:
9480 return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
9481 node, prettyFlags);
9483 return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
9484 node, prettyFlags);
9485 case T_ReturningExpr:
9486 return isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr,
9487 node, prettyFlags);
9488
9489 case T_OpExpr:
9490 {
9491 /* depends on parent node type; needs further checking */
9492 if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
9493 {
9494 const char *op;
9495 const char *parentOp;
9496 bool is_lopriop;
9497 bool is_hipriop;
9498 bool is_lopriparent;
9499 bool is_hipriparent;
9500
9501 op = get_simple_binary_op_name((OpExpr *) node);
9502 if (!op)
9503 return false;
9504
9505 /* We know only the basic operators + - and * / % */
9506 is_lopriop = (strchr("+-", *op) != NULL);
9507 is_hipriop = (strchr("*/%", *op) != NULL);
9508 if (!(is_lopriop || is_hipriop))
9509 return false;
9510
9512 if (!parentOp)
9513 return false;
9514
9515 is_lopriparent = (strchr("+-", *parentOp) != NULL);
9516 is_hipriparent = (strchr("*/%", *parentOp) != NULL);
9518 return false;
9519
9521 return true; /* op binds tighter than parent */
9522
9524 return false;
9525
9526 /*
9527 * Operators are same priority --- can skip parens only if
9528 * we have (a - b) - c, not a - (b - c).
9529 */
9530 if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
9531 return true;
9532
9533 return false;
9534 }
9535 /* else do the same stuff as for T_SubLink et al. */
9536 }
9538
9539 case T_SubLink:
9540 case T_NullTest:
9541 case T_BooleanTest:
9542 case T_DistinctExpr:
9543 case T_JsonIsPredicate:
9544 switch (nodeTag(parentNode))
9545 {
9546 case T_FuncExpr:
9547 {
9548 /* special handling for casts and COERCE_SQL_SYNTAX */
9549 CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9550
9551 if (type == COERCE_EXPLICIT_CAST ||
9554 return false;
9555 return true; /* own parentheses */
9556 }
9557 case T_BoolExpr: /* lower precedence */
9558 case T_SubscriptingRef: /* other separators */
9559 case T_ArrayExpr: /* other separators */
9560 case T_RowExpr: /* other separators */
9561 case T_CoalesceExpr: /* own parentheses */
9562 case T_MinMaxExpr: /* own parentheses */
9563 case T_XmlExpr: /* own parentheses */
9564 case T_NullIfExpr: /* other separators */
9565 case T_Aggref: /* own parentheses */
9566 case T_GroupingFunc: /* own parentheses */
9567 case T_WindowFunc: /* own parentheses */
9568 case T_CaseExpr: /* other separators */
9569 return true;
9570 default:
9571 return false;
9572 }
9573
9574 case T_BoolExpr:
9575 switch (nodeTag(parentNode))
9576 {
9577 case T_BoolExpr:
9578 if (prettyFlags & PRETTYFLAG_PAREN)
9579 {
9582
9583 type = ((BoolExpr *) node)->boolop;
9584 parentType = ((BoolExpr *) parentNode)->boolop;
9585 switch (type)
9586 {
9587 case NOT_EXPR:
9588 case AND_EXPR:
9590 return true;
9591 break;
9592 case OR_EXPR:
9593 if (parentType == OR_EXPR)
9594 return true;
9595 break;
9596 }
9597 }
9598 return false;
9599 case T_FuncExpr:
9600 {
9601 /* special handling for casts and COERCE_SQL_SYNTAX */
9602 CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9603
9604 if (type == COERCE_EXPLICIT_CAST ||
9607 return false;
9608 return true; /* own parentheses */
9609 }
9610 case T_SubscriptingRef: /* other separators */
9611 case T_ArrayExpr: /* other separators */
9612 case T_RowExpr: /* other separators */
9613 case T_CoalesceExpr: /* own parentheses */
9614 case T_MinMaxExpr: /* own parentheses */
9615 case T_XmlExpr: /* own parentheses */
9616 case T_NullIfExpr: /* other separators */
9617 case T_Aggref: /* own parentheses */
9618 case T_GroupingFunc: /* own parentheses */
9619 case T_WindowFunc: /* own parentheses */
9620 case T_CaseExpr: /* other separators */
9621 case T_JsonExpr: /* own parentheses */
9622 return true;
9623 default:
9624 return false;
9625 }
9626
9627 case T_JsonValueExpr:
9628 /* maybe simple, check args */
9629 return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
9630 node, prettyFlags);
9631
9632 default:
9633 break;
9634 }
9635 /* those we don't know: in dubio complexo */
9636 return false;
9637}

References AND_EXPR, arg, COERCE_EXPLICIT_CAST, COERCE_IMPLICIT_CAST, COERCE_SQL_SYNTAX, fb(), get_simple_binary_op_name(), IsA, isSimpleNode(), linitial, nodeTag, NOT_EXPR, OR_EXPR, pg_fallthrough, PRETTYFLAG_PAREN, and type.

Referenced by get_rule_expr_paren(), and isSimpleNode().

◆ list_oid_str_pair_cmp_by_str()

static int list_oid_str_pair_cmp_by_str ( const ListCell p1,
const ListCell p2 
)
static

Definition at line 1784 of file ruleutils.c.

1785{
1786 struct oid_str_pair *v1 = lfirst(p1);
1787 struct oid_str_pair *v2 = lfirst(p2);
1788
1789 return strcmp(v1->str, v2->str);
1790}

References fb(), lfirst, and oid_str_pair::str.

Referenced by make_propgraphdef_labels().

◆ looks_like_function()

static bool looks_like_function ( Node node)
static

Definition at line 11294 of file ruleutils.c.

11295{
11296 if (node == NULL)
11297 return false; /* probably shouldn't happen */
11298 switch (nodeTag(node))
11299 {
11300 case T_FuncExpr:
11301 /* OK, unless it's going to deparse as a cast */
11302 return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
11303 ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
11304 case T_NullIfExpr:
11305 case T_CoalesceExpr:
11306 case T_MinMaxExpr:
11307 case T_SQLValueFunction:
11308 case T_XmlExpr:
11309 case T_JsonExpr:
11310 /* these are all accepted by func_expr_common_subexpr */
11311 return true;
11312 default:
11313 break;
11314 }
11315 return false;
11316}

References COERCE_EXPLICIT_CALL, COERCE_SQL_SYNTAX, fb(), and nodeTag.

Referenced by get_rule_expr_funccall(), pg_get_indexdef_worker(), pg_get_partkeydef_worker(), and pg_get_statisticsobj_worker().

◆ make_colname_unique()

static char * make_colname_unique ( char colname,
deparse_namespace dpns,
deparse_columns colinfo 
)
static

Definition at line 5285 of file ruleutils.c.

5287{
5288 /*
5289 * If the selected name isn't unique, append digits to make it so. For a
5290 * very long input name, we might have to truncate to stay within
5291 * NAMEDATALEN.
5292 */
5293 if (!colname_is_unique(colname, dpns, colinfo))
5294 {
5295 int colnamelen = strlen(colname);
5296 char *modname = (char *) palloc(colnamelen + 16);
5297 int i = 0;
5298
5299 do
5300 {
5301 i++;
5302 for (;;)
5303 {
5304 memcpy(modname, colname, colnamelen);
5305 sprintf(modname + colnamelen, "_%d", i);
5306 if (strlen(modname) < NAMEDATALEN)
5307 break;
5308 /* drop chars from colname to keep all the digits */
5310 colnamelen - 1);
5311 }
5312 } while (!colname_is_unique(modname, dpns, colinfo));
5313 colname = modname;
5314 }
5315 return colname;
5316}

References colname_is_unique(), fb(), i, memcpy(), NAMEDATALEN, palloc(), pg_mbcliplen(), and sprintf.

Referenced by set_join_column_names(), set_relation_column_names(), and set_using_names().

◆ make_propgraphdef_elements()

static void make_propgraphdef_elements ( StringInfo  buf,
Oid  pgrelid,
char  pgekind 
)
static

Definition at line 1660 of file ruleutils.c.

1661{
1664 SysScanDesc scan;
1665 bool first;
1666 HeapTuple tup;
1667
1669
1670 ScanKeyInit(&scankey[0],
1674
1676
1677 first = true;
1678 while ((tup = systable_getnext(scan)))
1679 {
1681 char *relname;
1682 Datum datum;
1683 bool isnull;
1684
1685 if (pgeform->pgekind != pgekind)
1686 continue;
1687
1688 if (first)
1689 {
1690 appendStringInfo(buf, "\n %s TABLES (\n", pgekind == PGEKIND_VERTEX ? "VERTEX" : "EDGE");
1691 first = false;
1692 }
1693 else
1695
1696 relname = get_rel_name(pgeform->pgerelid);
1697 if (relname && strcmp(relname, NameStr(pgeform->pgealias)) == 0)
1698 appendStringInfo(buf, " %s",
1699 generate_relation_name(pgeform->pgerelid, NIL));
1700 else
1701 appendStringInfo(buf, " %s AS %s",
1703 quote_identifier(NameStr(pgeform->pgealias)));
1704
1706 if (!isnull)
1707 {
1708 appendStringInfoString(buf, " KEY (");
1709 decompile_column_index_array(datum, pgeform->pgerelid, false, buf);
1711 }
1712 else
1713 elog(ERROR, "null pgekey for element %u", pgeform->oid);
1714
1715 if (pgekind == PGEKIND_EDGE)
1716 {
1717 Datum srckey;
1718 Datum srcref;
1719 Datum destkey;
1720 Datum destref;
1723
1725 srckey = isnull ? 0 : datum;
1727 srcref = isnull ? 0 : datum;
1729 destkey = isnull ? 0 : datum;
1731 destref = isnull ? 0 : datum;
1732
1733 appendStringInfoString(buf, " SOURCE");
1735 if (!tup2)
1736 elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgesrcvertexid);
1738 if (srckey)
1739 {
1740 appendStringInfoString(buf, " KEY (");
1741 decompile_column_index_array(srckey, pgeform->pgerelid, false, buf);
1742 appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1743 decompile_column_index_array(srcref, pgeform2->pgerelid, false, buf);
1745 }
1746 else
1749
1750 appendStringInfoString(buf, " DESTINATION");
1752 if (!tup2)
1753 elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgedestvertexid);
1755 if (destkey)
1756 {
1757 appendStringInfoString(buf, " KEY (");
1758 decompile_column_index_array(destkey, pgeform->pgerelid, false, buf);
1759 appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1760 decompile_column_index_array(destref, pgeform2->pgerelid, false, buf);
1762 }
1763 else
1766 }
1767
1768 make_propgraphdef_labels(buf, pgeform->oid, NameStr(pgeform->pgealias), pgeform->pgerelid);
1769 }
1770 if (!first)
1771 appendStringInfoString(buf, "\n )");
1772
1773 systable_endscan(scan);
1775}

References AccessShareLock, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), BTEqualStrategyNumber, buf, decompile_column_index_array(), elog, ERROR, fb(), Form_pg_propgraph_element, generate_relation_name(), get_rel_name(), GETSTRUCT(), heap_getattr(), make_propgraphdef_labels(), NameStr, NIL, ObjectIdGetDatum(), quote_identifier(), RelationGetDescr, ReleaseSysCache(), relname, ScanKeyInit(), SearchSysCache1(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by pg_get_propgraphdef().

◆ make_propgraphdef_labels()

static void make_propgraphdef_labels ( StringInfo  buf,
Oid  elid,
const char elalias,
Oid  elrelid 
)
static

Definition at line 1797 of file ruleutils.c.

1798{
1801 SysScanDesc scan;
1802 int count;
1803 HeapTuple tup;
1804 List *label_list = NIL;
1805
1807
1808 ScanKeyInit(&scankey[0],
1812
1813 /*
1814 * We want to output the labels in a deterministic order. So we first
1815 * read all the data, then sort, then print it.
1816 */
1818
1819 while ((tup = systable_getnext(scan)))
1820 {
1822 struct oid_str_pair *osp;
1823
1824 osp = palloc_object(struct oid_str_pair);
1825 osp->oid = pgelform->oid;
1826 osp->str = get_propgraph_label_name(pgelform->pgellabelid);
1827
1829 }
1830
1831 systable_endscan(scan);
1833
1834 count = list_length(label_list);
1835
1836 /* Each element has at least one label. */
1837 Assert(count > 0);
1838
1839 /*
1840 * It is enough for the comparison function to compare just labels, since
1841 * all the labels of an element table should have distinct names.
1842 */
1844
1846 {
1847 if (strcmp(osp->str, elalias) == 0)
1848 {
1849 /* If the default label is the only label, don't print anything. */
1850 if (count != 1)
1851 appendStringInfoString(buf, " DEFAULT LABEL");
1852 }
1853 else
1854 appendStringInfo(buf, " LABEL %s", quote_identifier(osp->str));
1855
1857 }
1858}

References AccessShareLock, appendStringInfo(), appendStringInfoString(), Assert, BTEqualStrategyNumber, buf, fb(), foreach_ptr, Form_pg_propgraph_element_label, get_propgraph_label_name(), GETSTRUCT(), lappend(), list_length(), list_oid_str_pair_cmp_by_str(), list_sort(), make_propgraphdef_properties(), NIL, ObjectIdGetDatum(), palloc_object, quote_identifier(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by make_propgraphdef_elements().

◆ make_propgraphdef_properties()

static void make_propgraphdef_properties ( StringInfo  buf,
Oid  ellabelid,
Oid  elrelid 
)
static

Definition at line 1881 of file ruleutils.c.

1882{
1885 SysScanDesc scan;
1886 HeapTuple tup;
1887 List *outlist = NIL;
1888
1890
1891 ScanKeyInit(&scankey[0],
1895
1896 /*
1897 * We want to output the properties in a deterministic order. So we first
1898 * read all the data, then sort, then print it.
1899 */
1901
1902 while ((tup = systable_getnext(scan)))
1903 {
1906 bool isnull;
1907 char *tmp;
1908 Node *expr;
1909 char *propname;
1910
1912 Assert(!isnull);
1914 expr = stringToNode(tmp);
1915 pfree(tmp);
1916
1918
1919 outlist = lappend(outlist, list_make2(makeString(propname), expr));
1920 }
1921
1922 systable_endscan(scan);
1924
1926
1927 if (outlist)
1928 {
1929 List *context;
1930 ListCell *lc;
1931 bool first = true;
1932
1934
1935 appendStringInfoString(buf, " PROPERTIES (");
1936
1937 foreach(lc, outlist)
1938 {
1940 char *propname = strVal(linitial(data));
1941 Node *expr = lsecond(data);
1942
1943 if (first)
1944 first = false;
1945 else
1947
1948 if (IsA(expr, Var) && strcmp(propname, get_attname(elrelid, castNode(Var, expr)->varattno, false)) == 0)
1950 else
1951 appendStringInfo(buf, "%s AS %s",
1952 deparse_expression_pretty(expr, context, false, false, 0, 0),
1954 }
1955
1957 }
1958 else
1959 appendStringInfoString(buf, " NO PROPERTIES");
1960}

References AccessShareLock, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, BTEqualStrategyNumber, buf, castNode, data, deparse_context_for(), deparse_expression_pretty(), fb(), Form_pg_propgraph_label_property, get_attname(), get_propgraph_property_name(), get_relation_name(), GETSTRUCT(), heap_getattr(), IsA, lappend(), lfirst_node, linitial, list_make2, list_sort(), lsecond, makeString(), NIL, ObjectIdGetDatum(), pfree(), propdata_by_name_cmp(), quote_identifier(), RelationGetDescr, ScanKeyInit(), stringToNode(), strVal, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and TextDatumGetCString.

Referenced by make_propgraphdef_labels().

◆ make_ruledef()

static void make_ruledef ( StringInfo  buf,
HeapTuple  ruletup,
TupleDesc  rulettc,
int  prettyFlags 
)
static

Definition at line 5709 of file ruleutils.c.

5711{
5712 char *rulename;
5713 char ev_type;
5714 Oid ev_class;
5715 bool is_instead;
5716 char *ev_qual;
5717 char *ev_action;
5718 List *actions;
5721 int fno;
5722 Datum dat;
5723 bool isnull;
5724
5725 /*
5726 * Get the attribute values from the rules tuple
5727 */
5728 fno = SPI_fnumber(rulettc, "rulename");
5729 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5730 Assert(!isnull);
5731 rulename = NameStr(*(DatumGetName(dat)));
5732
5733 fno = SPI_fnumber(rulettc, "ev_type");
5734 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5735 Assert(!isnull);
5736 ev_type = DatumGetChar(dat);
5737
5738 fno = SPI_fnumber(rulettc, "ev_class");
5739 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5740 Assert(!isnull);
5742
5743 fno = SPI_fnumber(rulettc, "is_instead");
5744 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5745 Assert(!isnull);
5746 is_instead = DatumGetBool(dat);
5747
5748 fno = SPI_fnumber(rulettc, "ev_qual");
5750 Assert(ev_qual != NULL);
5751
5752 fno = SPI_fnumber(rulettc, "ev_action");
5754 Assert(ev_action != NULL);
5755 actions = (List *) stringToNode(ev_action);
5756 if (actions == NIL)
5757 elog(ERROR, "invalid empty ev_action list");
5758
5760
5761 /*
5762 * Build the rules definition text
5763 */
5764 appendStringInfo(buf, "CREATE RULE %s AS",
5765 quote_identifier(rulename));
5766
5767 if (prettyFlags & PRETTYFLAG_INDENT)
5768 appendStringInfoString(buf, "\n ON ");
5769 else
5770 appendStringInfoString(buf, " ON ");
5771
5772 /* The event the rule is fired for */
5773 switch (ev_type)
5774 {
5775 case '1':
5776 appendStringInfoString(buf, "SELECT");
5778 break;
5779
5780 case '2':
5781 appendStringInfoString(buf, "UPDATE");
5782 break;
5783
5784 case '3':
5785 appendStringInfoString(buf, "INSERT");
5786 break;
5787
5788 case '4':
5789 appendStringInfoString(buf, "DELETE");
5790 break;
5791
5792 default:
5793 ereport(ERROR,
5795 errmsg("rule \"%s\" has unsupported event type %d",
5796 rulename, ev_type)));
5797 break;
5798 }
5799
5800 /* The relation the rule is fired on */
5801 appendStringInfo(buf, " TO %s",
5802 (prettyFlags & PRETTYFLAG_SCHEMA) ?
5805
5806 /* If the rule has an event qualification, add it */
5807 if (strcmp(ev_qual, "<>") != 0)
5808 {
5809 Node *qual;
5810 Query *query;
5811 deparse_context context;
5813
5814 if (prettyFlags & PRETTYFLAG_INDENT)
5816 appendStringInfoString(buf, " WHERE ");
5817
5818 qual = stringToNode(ev_qual);
5819
5820 /*
5821 * We need to make a context for recognizing any Vars in the qual
5822 * (which can only be references to OLD and NEW). Use the rtable of
5823 * the first query in the action list for this purpose.
5824 */
5825 query = (Query *) linitial(actions);
5826
5827 /*
5828 * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5829 * into the SELECT, and that's what we need to look at. (Ugly kluge
5830 * ... try to fix this when we redesign querytrees.)
5831 */
5832 query = getInsertSelectQuery(query, NULL);
5833
5834 /* Must acquire locks right away; see notes in get_query_def() */
5835 AcquireRewriteLocks(query, false, false);
5836
5837 context.buf = buf;
5838 context.namespaces = list_make1(&dpns);
5839 context.resultDesc = NULL;
5840 context.targetList = NIL;
5841 context.windowClause = NIL;
5842 context.varprefix = (list_length(query->rtable) != 1);
5843 context.prettyFlags = prettyFlags;
5845 context.indentLevel = PRETTYINDENT_STD;
5846 context.colNamesVisible = true;
5847 context.inGroupBy = false;
5848 context.varInOrderBy = false;
5849 context.appendparents = NULL;
5850
5851 set_deparse_for_query(&dpns, query, NIL);
5852
5853 get_rule_expr(qual, &context, false);
5854 }
5855
5856 appendStringInfoString(buf, " DO ");
5857
5858 /* The INSTEAD keyword (if so) */
5859 if (is_instead)
5860 appendStringInfoString(buf, "INSTEAD ");
5861
5862 /* Finally the rules actions */
5863 if (list_length(actions) > 1)
5864 {
5866 Query *query;
5867
5869 foreach(action, actions)
5870 {
5871 query = (Query *) lfirst(action);
5872 get_query_def(query, buf, NIL, viewResultDesc, true,
5873 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5874 if (prettyFlags)
5876 else
5878 }
5880 }
5881 else
5882 {
5883 Query *query;
5884
5885 query = (Query *) linitial(actions);
5886 get_query_def(query, buf, NIL, viewResultDesc, true,
5887 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5889 }
5890
5892}

References AccessShareLock, AcquireRewriteLocks(), deparse_context::appendparents, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, deparse_context::colNamesVisible, DatumGetBool(), DatumGetChar(), DatumGetName(), DatumGetObjectId(), elog, ereport, errcode(), errmsg, ERROR, fb(), generate_qualified_relation_name(), generate_relation_name(), get_query_def(), get_rule_expr(), getInsertSelectQuery(), deparse_context::indentLevel, deparse_context::inGroupBy, lfirst, linitial, list_length(), list_make1, deparse_context::namespaces, NameStr, NIL, PRETTYFLAG_INDENT, PRETTYFLAG_SCHEMA, deparse_context::prettyFlags, PRETTYINDENT_STD, quote_identifier(), RelationGetDescr, deparse_context::resultDesc, Query::rtable, set_deparse_for_query(), SPI_fnumber(), SPI_getbinval(), SPI_getvalue(), stringToNode(), table_close(), table_open(), deparse_context::targetList, deparse_context::varInOrderBy, deparse_context::varprefix, deparse_context::windowClause, WRAP_COLUMN_DEFAULT, and deparse_context::wrapColumn.

Referenced by pg_get_ruledef_worker().

◆ make_viewdef()

static void make_viewdef ( StringInfo  buf,
HeapTuple  ruletup,
TupleDesc  rulettc,
int  prettyFlags,
int  wrapColumn 
)
static

Definition at line 5901 of file ruleutils.c.

5903{
5904 Query *query;
5905 char ev_type;
5906 Oid ev_class;
5907 bool is_instead;
5908 char *ev_qual;
5909 char *ev_action;
5910 List *actions;
5912 int fno;
5913 Datum dat;
5914 bool isnull;
5915
5916 /*
5917 * Get the attribute values from the rules tuple
5918 */
5919 fno = SPI_fnumber(rulettc, "ev_type");
5920 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5921 Assert(!isnull);
5922 ev_type = DatumGetChar(dat);
5923
5924 fno = SPI_fnumber(rulettc, "ev_class");
5925 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5926 Assert(!isnull);
5928
5929 fno = SPI_fnumber(rulettc, "is_instead");
5930 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5931 Assert(!isnull);
5932 is_instead = DatumGetBool(dat);
5933
5934 fno = SPI_fnumber(rulettc, "ev_qual");
5936 Assert(ev_qual != NULL);
5937
5938 fno = SPI_fnumber(rulettc, "ev_action");
5940 Assert(ev_action != NULL);
5941 actions = (List *) stringToNode(ev_action);
5942
5943 if (list_length(actions) != 1)
5944 {
5945 /* keep output buffer empty and leave */
5946 return;
5947 }
5948
5949 query = (Query *) linitial(actions);
5950
5951 if (ev_type != '1' || !is_instead ||
5952 strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5953 {
5954 /* keep output buffer empty and leave */
5955 return;
5956 }
5957
5959
5961 prettyFlags, wrapColumn, 0);
5963
5965}

References AccessShareLock, appendStringInfoChar(), Assert, buf, CMD_SELECT, Query::commandType, DatumGetBool(), DatumGetChar(), DatumGetObjectId(), fb(), get_query_def(), linitial, list_length(), NIL, RelationGetDescr, SPI_fnumber(), SPI_getbinval(), SPI_getvalue(), stringToNode(), table_close(), and table_open().

Referenced by pg_get_viewdef_worker().

◆ pg_get_constraintdef()

Datum pg_get_constraintdef ( PG_FUNCTION_ARGS  )

Definition at line 2504 of file ruleutils.c.

2505{
2507 int prettyFlags;
2508 char *res;
2509
2510 prettyFlags = PRETTYFLAG_INDENT;
2511
2512 res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2513
2514 if (res == NULL)
2516
2518}

References fb(), pg_get_constraintdef_worker(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, and string_to_text().

◆ pg_get_constraintdef_command()

char * pg_get_constraintdef_command ( Oid  constraintId)

Definition at line 2542 of file ruleutils.c.

2543{
2544 return pg_get_constraintdef_worker(constraintId, true, 0, false);
2545}

References fb(), and pg_get_constraintdef_worker().

Referenced by RememberConstraintForRebuilding().

◆ pg_get_constraintdef_ext()

Datum pg_get_constraintdef_ext ( PG_FUNCTION_ARGS  )

Definition at line 2521 of file ruleutils.c.

2522{
2524 bool pretty = PG_GETARG_BOOL(1);
2525 int prettyFlags;
2526 char *res;
2527
2528 prettyFlags = GET_PRETTY_FLAGS(pretty);
2529
2530 res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2531
2532 if (res == NULL)
2534
2536}

References fb(), GET_PRETTY_FLAGS, pg_get_constraintdef_worker(), PG_GETARG_BOOL, PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, and string_to_text().

◆ pg_get_constraintdef_worker()

static char * pg_get_constraintdef_worker ( Oid  constraintId,
bool  fullCommand,
int  prettyFlags,
bool  missing_ok 
)
static

Definition at line 2551 of file ruleutils.c.

2553{
2554 HeapTuple tup;
2557 SysScanDesc scandesc;
2561
2562 ScanKeyInit(&scankey[0],
2566
2567 scandesc = systable_beginscan(relation,
2569 true,
2570 snapshot,
2571 1,
2572 scankey);
2573
2574 /*
2575 * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2576 * via SearchSysCache, which works fine.
2577 */
2578 tup = systable_getnext(scandesc);
2579
2580 UnregisterSnapshot(snapshot);
2581
2582 if (!HeapTupleIsValid(tup))
2583 {
2584 if (missing_ok)
2585 {
2586 systable_endscan(scandesc);
2587 table_close(relation, AccessShareLock);
2588 return NULL;
2589 }
2590 elog(ERROR, "could not find tuple for constraint %u", constraintId);
2591 }
2592
2594
2596
2597 if (fullCommand)
2598 {
2599 if (OidIsValid(conForm->conrelid))
2600 {
2601 /*
2602 * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2603 * constraints, and other types of constraints don't inherit
2604 * anyway so it doesn't matter whether we say ONLY or not. Someday
2605 * we might need to let callers specify whether to put ONLY in the
2606 * command.
2607 */
2608 appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2610 quote_identifier(NameStr(conForm->conname)));
2611 }
2612 else
2613 {
2614 /* Must be a domain constraint */
2615 Assert(OidIsValid(conForm->contypid));
2616 appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2618 quote_identifier(NameStr(conForm->conname)));
2619 }
2620 }
2621
2622 switch (conForm->contype)
2623 {
2624 case CONSTRAINT_FOREIGN:
2625 {
2626 Datum val;
2627 bool isnull;
2628 const char *string;
2629
2630 /* Start off the constraint definition */
2631 appendStringInfoString(&buf, "FOREIGN KEY (");
2632
2633 /* Fetch and build referencing-column list */
2636
2637 /* If it is a temporal foreign key then it uses PERIOD. */
2638 decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
2639
2640 /* add foreign relation name */
2641 appendStringInfo(&buf, ") REFERENCES %s(",
2643 NIL));
2644
2645 /* Fetch and build referenced-column list */
2648
2649 decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
2650
2652
2653 /* Add match type */
2654 switch (conForm->confmatchtype)
2655 {
2657 string = " MATCH FULL";
2658 break;
2660 string = " MATCH PARTIAL";
2661 break;
2663 string = "";
2664 break;
2665 default:
2666 elog(ERROR, "unrecognized confmatchtype: %d",
2667 conForm->confmatchtype);
2668 string = ""; /* keep compiler quiet */
2669 break;
2670 }
2671 appendStringInfoString(&buf, string);
2672
2673 /* Add ON UPDATE and ON DELETE clauses, if needed */
2674 switch (conForm->confupdtype)
2675 {
2677 string = NULL; /* suppress default */
2678 break;
2680 string = "RESTRICT";
2681 break;
2683 string = "CASCADE";
2684 break;
2686 string = "SET NULL";
2687 break;
2689 string = "SET DEFAULT";
2690 break;
2691 default:
2692 elog(ERROR, "unrecognized confupdtype: %d",
2693 conForm->confupdtype);
2694 string = NULL; /* keep compiler quiet */
2695 break;
2696 }
2697 if (string)
2698 appendStringInfo(&buf, " ON UPDATE %s", string);
2699
2700 switch (conForm->confdeltype)
2701 {
2703 string = NULL; /* suppress default */
2704 break;
2706 string = "RESTRICT";
2707 break;
2709 string = "CASCADE";
2710 break;
2712 string = "SET NULL";
2713 break;
2715 string = "SET DEFAULT";
2716 break;
2717 default:
2718 elog(ERROR, "unrecognized confdeltype: %d",
2719 conForm->confdeltype);
2720 string = NULL; /* keep compiler quiet */
2721 break;
2722 }
2723 if (string)
2724 appendStringInfo(&buf, " ON DELETE %s", string);
2725
2726 /*
2727 * Add columns specified to SET NULL or SET DEFAULT if
2728 * provided.
2729 */
2732 if (!isnull)
2733 {
2735 decompile_column_index_array(val, conForm->conrelid, false, &buf);
2737 }
2738
2739 break;
2740 }
2741 case CONSTRAINT_PRIMARY:
2742 case CONSTRAINT_UNIQUE:
2743 {
2744 Datum val;
2745 Oid indexId;
2746 int keyatts;
2748
2749 /* Start off the constraint definition */
2750 if (conForm->contype == CONSTRAINT_PRIMARY)
2751 appendStringInfoString(&buf, "PRIMARY KEY ");
2752 else
2753 appendStringInfoString(&buf, "UNIQUE ");
2754
2755 indexId = conForm->conindid;
2756
2759 elog(ERROR, "cache lookup failed for index %u", indexId);
2760 if (conForm->contype == CONSTRAINT_UNIQUE &&
2761 ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
2762 appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2763
2765
2766 /* Fetch and build target column list */
2769
2770 keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
2771 if (conForm->conperiod)
2772 appendStringInfoString(&buf, " WITHOUT OVERLAPS");
2773
2775
2776 /* Build including column list (from pg_index.indkeys) */
2779 if (DatumGetInt32(val) > keyatts)
2780 {
2781 Datum cols;
2782 Datum *keys;
2783 int nKeys;
2784 int j;
2785
2786 appendStringInfoString(&buf, " INCLUDE (");
2787
2790
2792 &keys, NULL, &nKeys);
2793
2794 for (j = keyatts; j < nKeys; j++)
2795 {
2796 char *colName;
2797
2798 colName = get_attname(conForm->conrelid,
2799 DatumGetInt16(keys[j]), false);
2800 if (j > keyatts)
2803 }
2804
2806 }
2808
2809 /* XXX why do we only print these bits if fullCommand? */
2811 {
2813 Oid tblspc;
2814
2815 if (options)
2816 {
2817 appendStringInfo(&buf, " WITH (%s)", options);
2818 pfree(options);
2819 }
2820
2821 /*
2822 * Print the tablespace, unless it's the database default.
2823 * This is to help ALTER TABLE usage of this facility,
2824 * which needs this behavior to recreate exact catalog
2825 * state.
2826 */
2828 if (OidIsValid(tblspc))
2829 appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2831 }
2832
2833 break;
2834 }
2835 case CONSTRAINT_CHECK:
2836 {
2837 Datum val;
2838 char *conbin;
2839 char *consrc;
2840 Node *expr;
2841 List *context;
2842
2843 /* Fetch constraint expression in parsetree form */
2846
2848 expr = stringToNode(conbin);
2849
2850 /* Set up deparsing context for Var nodes in constraint */
2851 if (conForm->conrelid != InvalidOid)
2852 {
2853 /* relation constraint */
2854 context = deparse_context_for(get_relation_name(conForm->conrelid),
2855 conForm->conrelid);
2856 }
2857 else
2858 {
2859 /* domain constraint --- can't have Vars */
2860 context = NIL;
2861 }
2862
2863 consrc = deparse_expression_pretty(expr, context, false, false,
2864 prettyFlags, 0);
2865
2866 /*
2867 * Now emit the constraint definition, adding NO INHERIT if
2868 * necessary.
2869 *
2870 * There are cases where the constraint expression will be
2871 * fully parenthesized and we don't need the outer parens ...
2872 * but there are other cases where we do need 'em. Be
2873 * conservative for now.
2874 *
2875 * Note that simply checking for leading '(' and trailing ')'
2876 * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2877 */
2878 appendStringInfo(&buf, "CHECK (%s)%s",
2879 consrc,
2880 conForm->connoinherit ? " NO INHERIT" : "");
2881 break;
2882 }
2883 case CONSTRAINT_NOTNULL:
2884 {
2885 if (conForm->conrelid)
2886 {
2888
2890
2891 appendStringInfo(&buf, "NOT NULL %s",
2893 attnum, false)));
2895 appendStringInfoString(&buf, " NO INHERIT");
2896 }
2897 else if (conForm->contypid)
2898 {
2899 /* conkey is null for domain not-null constraints */
2900 appendStringInfoString(&buf, "NOT NULL");
2901 }
2902 break;
2903 }
2904
2905 case CONSTRAINT_TRIGGER:
2906
2907 /*
2908 * There isn't an ALTER TABLE syntax for creating a user-defined
2909 * constraint trigger, but it seems better to print something than
2910 * throw an error; if we throw error then this function couldn't
2911 * safely be applied to all rows of pg_constraint.
2912 */
2913 appendStringInfoString(&buf, "TRIGGER");
2914 break;
2916 {
2917 Oid indexOid = conForm->conindid;
2918 Datum val;
2919 Datum *elems;
2920 int nElems;
2921 int i;
2922 Oid *operators;
2923
2924 /* Extract operator OIDs from the pg_constraint tuple */
2927
2929 &elems, NULL, &nElems);
2930
2931 operators = (Oid *) palloc(nElems * sizeof(Oid));
2932 for (i = 0; i < nElems; i++)
2933 operators[i] = DatumGetObjectId(elems[i]);
2934
2935 /* pg_get_indexdef_worker does the rest */
2936 /* suppress tablespace because pg_dump wants it that way */
2938 pg_get_indexdef_worker(indexOid,
2939 0,
2940 operators,
2941 false,
2942 false,
2943 false,
2944 false,
2945 prettyFlags,
2946 false));
2947 break;
2948 }
2949 default:
2950 elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2951 break;
2952 }
2953
2954 if (conForm->condeferrable)
2955 appendStringInfoString(&buf, " DEFERRABLE");
2956 if (conForm->condeferred)
2957 appendStringInfoString(&buf, " INITIALLY DEFERRED");
2958
2959 /* Validated status is irrelevant when the constraint is NOT ENFORCED. */
2960 if (!conForm->conenforced)
2961 appendStringInfoString(&buf, " NOT ENFORCED");
2962 else if (!conForm->convalidated)
2963 appendStringInfoString(&buf, " NOT VALID");
2964
2965 /* Cleanup */
2966 systable_endscan(scandesc);
2967 table_close(relation, AccessShareLock);
2968
2969 return buf.data;
2970}

References AccessShareLock, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, attnum, BTEqualStrategyNumber, buf, DatumGetArrayTypeP, DatumGetInt16(), DatumGetInt32(), DatumGetObjectId(), decompile_column_index_array(), deconstruct_array_builtin(), deparse_context_for(), deparse_expression_pretty(), elog, ERROR, extractNotNullColumn(), fb(), FKCONSTR_ACTION_CASCADE, FKCONSTR_ACTION_NOACTION, FKCONSTR_ACTION_RESTRICT, FKCONSTR_ACTION_SETDEFAULT, FKCONSTR_ACTION_SETNULL, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, flatten_reloptions(), Form_pg_constraint, Form_pg_index, generate_qualified_relation_name(), generate_qualified_type_name(), generate_relation_name(), get_attname(), get_rel_tablespace(), get_relation_name(), get_tablespace_name(), GETSTRUCT(), GetTransactionSnapshot(), HeapTupleIsValid, i, initStringInfo(), InvalidOid, j, NameStr, NIL, ObjectIdGetDatum(), OidIsValid, palloc(), pfree(), pg_get_indexdef_worker(), quote_identifier(), RegisterSnapshot(), ReleaseSysCache(), ScanKeyInit(), SearchSysCache1(), stringToNode(), SysCacheGetAttr(), SysCacheGetAttrNotNull(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TextDatumGetCString, UnregisterSnapshot(), and val.

Referenced by pg_get_constraintdef(), pg_get_constraintdef_command(), and pg_get_constraintdef_ext().

◆ pg_get_expr()

Datum pg_get_expr ( PG_FUNCTION_ARGS  )

Definition at line 3033 of file ruleutils.c.

3034{
3035 text *expr = PG_GETARG_TEXT_PP(0);
3036 Oid relid = PG_GETARG_OID(1);
3037 text *result;
3038 int prettyFlags;
3039
3040 prettyFlags = PRETTYFLAG_INDENT;
3041
3042 result = pg_get_expr_worker(expr, relid, prettyFlags);
3043 if (result)
3045 else
3047}

References pg_get_expr_worker(), PG_GETARG_OID, PG_GETARG_TEXT_PP, PG_RETURN_NULL, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, and result.

Referenced by decompile_conbin().

◆ pg_get_expr_ext()

Datum pg_get_expr_ext ( PG_FUNCTION_ARGS  )

Definition at line 3050 of file ruleutils.c.

3051{
3052 text *expr = PG_GETARG_TEXT_PP(0);
3053 Oid relid = PG_GETARG_OID(1);
3054 bool pretty = PG_GETARG_BOOL(2);
3055 text *result;
3056 int prettyFlags;
3057
3058 prettyFlags = GET_PRETTY_FLAGS(pretty);
3059
3060 result = pg_get_expr_worker(expr, relid, prettyFlags);
3061 if (result)
3063 else
3065}

References fb(), GET_PRETTY_FLAGS, pg_get_expr_worker(), PG_GETARG_BOOL, PG_GETARG_OID, PG_GETARG_TEXT_PP, PG_RETURN_NULL, PG_RETURN_TEXT_P, and result.

◆ pg_get_expr_worker()

static text * pg_get_expr_worker ( text expr,
Oid  relid,
int  prettyFlags 
)
static

Definition at line 3068 of file ruleutils.c.

3069{
3070 Node *node;
3071 Node *tst;
3072 Relids relids;
3073 List *context;
3074 char *exprstr;
3075 Relation rel = NULL;
3076 char *str;
3077
3078 /* Convert input pg_node_tree (really TEXT) object to C string */
3079 exprstr = text_to_cstring(expr);
3080
3081 /* Convert expression to node tree */
3082 node = (Node *) stringToNode(exprstr);
3083
3084 pfree(exprstr);
3085
3086 /*
3087 * Throw error if the input is a querytree rather than an expression tree.
3088 * While we could support queries here, there seems no very good reason
3089 * to. In most such catalog columns, we'll see a List of Query nodes, or
3090 * even nested Lists, so drill down to a non-List node before checking.
3091 */
3092 tst = node;
3093 while (tst && IsA(tst, List))
3094 tst = linitial((List *) tst);
3095 if (tst && IsA(tst, Query))
3096 ereport(ERROR,
3098 errmsg("input is a query, not an expression")));
3099
3100 /*
3101 * Throw error if the expression contains Vars we won't be able to
3102 * deparse.
3103 */
3104 relids = pull_varnos(NULL, node);
3105 if (OidIsValid(relid))
3106 {
3107 if (!bms_is_subset(relids, bms_make_singleton(1)))
3108 ereport(ERROR,
3110 errmsg("expression contains variables of more than one relation")));
3111 }
3112 else
3113 {
3114 if (!bms_is_empty(relids))
3115 ereport(ERROR,
3117 errmsg("expression contains variables")));
3118 }
3119
3120 /*
3121 * Prepare deparse context if needed. If we are deparsing with a relid,
3122 * we need to transiently open and lock the rel, to make sure it won't go
3123 * away underneath us. (set_relation_column_names would lock it anyway,
3124 * so this isn't really introducing any new behavior.)
3125 */
3126 if (OidIsValid(relid))
3127 {
3128 rel = try_relation_open(relid, AccessShareLock);
3129 if (rel == NULL)
3130 return NULL;
3131 context = deparse_context_for(RelationGetRelationName(rel), relid);
3132 }
3133 else
3134 context = NIL;
3135
3136 /* Deparse */
3137 str = deparse_expression_pretty(node, context, false, false,
3138 prettyFlags, 0);
3139
3140 if (rel != NULL)
3142
3143 return string_to_text(str);
3144}

References AccessShareLock, bms_is_empty, bms_is_subset(), bms_make_singleton(), deparse_context_for(), deparse_expression_pretty(), ereport, errcode(), errmsg, ERROR, fb(), IsA, linitial, NIL, OidIsValid, pfree(), pull_varnos(), relation_close(), RelationGetRelationName, str, string_to_text(), stringToNode(), text_to_cstring(), and try_relation_open().

Referenced by pg_get_expr(), and pg_get_expr_ext().

◆ pg_get_function_arg_default()

Datum pg_get_function_arg_default ( PG_FUNCTION_ARGS  )

Definition at line 3848 of file ruleutils.c.

3849{
3850 Oid funcid = PG_GETARG_OID(0);
3853 Form_pg_proc proc;
3854 int numargs;
3855 Oid *argtypes;
3856 char **argnames;
3857 char *argmodes;
3858 int i;
3860 Node *node;
3861 char *str;
3862 int nth_inputarg;
3864 bool isnull;
3865 int nth_default;
3866
3870
3871 numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3873 {
3876 }
3877
3878 nth_inputarg = 0;
3879 for (i = 0; i < nth_arg; i++)
3881 nth_inputarg++;
3882
3885 &isnull);
3886 if (isnull)
3887 {
3890 }
3891
3894 pfree(str);
3895
3896 proc = (Form_pg_proc) GETSTRUCT(proctup);
3897
3898 /*
3899 * Calculate index into proargdefaults: proargdefaults corresponds to the
3900 * last N input arguments, where N = pronargdefaults.
3901 */
3902 nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3903
3905 {
3908 }
3910 str = deparse_expression(node, NIL, false, false);
3911
3913
3915}

References castNode, deparse_expression(), fb(), Form_pg_proc, get_func_arg_info(), GETSTRUCT(), HeapTupleIsValid, i, is_input_argument(), list_length(), list_nth(), NIL, ObjectIdGetDatum(), pfree(), PG_GETARG_INT32, PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, ReleaseSysCache(), SearchSysCache1(), str, string_to_text(), stringToNode(), SysCacheGetAttr(), and TextDatumGetCString.

◆ pg_get_function_arguments()

◆ pg_get_function_identity_arguments()

◆ pg_get_function_result()

◆ pg_get_function_sqlbody()

Datum pg_get_function_sqlbody ( PG_FUNCTION_ARGS  )

Definition at line 3972 of file ruleutils.c.

3973{
3974 Oid funcid = PG_GETARG_OID(0);
3977 bool isnull;
3978
3980
3981 /* Look up the function */
3985
3987 if (isnull)
3988 {
3991 }
3992
3994
3996
3998}

References buf, cstring_to_text_with_len(), fb(), HeapTupleIsValid, initStringInfo(), ObjectIdGetDatum(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, print_function_sqlbody(), ReleaseSysCache(), SearchSysCache1(), and SysCacheGetAttr().

◆ pg_get_functiondef()

Datum pg_get_functiondef ( PG_FUNCTION_ARGS  )

Definition at line 3285 of file ruleutils.c.

3286{
3287 Oid funcid = PG_GETARG_OID(0);
3291 Form_pg_proc proc;
3292 bool isfunction;
3293 Datum tmp;
3294 bool isnull;
3295 const char *prosrc;
3296 const char *name;
3297 const char *nsp;
3299 int oldlen;
3300
3302
3303 /* Look up the function */
3307
3308 proc = (Form_pg_proc) GETSTRUCT(proctup);
3309 name = NameStr(proc->proname);
3310
3311 if (proc->prokind == PROKIND_AGGREGATE)
3312 ereport(ERROR,
3314 errmsg("\"%s\" is an aggregate function", name)));
3315
3316 isfunction = (proc->prokind != PROKIND_PROCEDURE);
3317
3318 /*
3319 * We always qualify the function name, to ensure the right function gets
3320 * replaced.
3321 */
3322 nsp = get_namespace_name_or_temp(proc->pronamespace);
3323 appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
3324 isfunction ? "FUNCTION" : "PROCEDURE",
3326 (void) print_function_arguments(&buf, proctup, false, true);
3327 appendStringInfoString(&buf, ")\n");
3328 if (isfunction)
3329 {
3330 appendStringInfoString(&buf, " RETURNS ");
3332 appendStringInfoChar(&buf, '\n');
3333 }
3334
3336
3337 appendStringInfo(&buf, " LANGUAGE %s\n",
3338 quote_identifier(get_language_name(proc->prolang, false)));
3339
3340 /* Emit some miscellaneous options on one line */
3341 oldlen = buf.len;
3342
3343 if (proc->prokind == PROKIND_WINDOW)
3344 appendStringInfoString(&buf, " WINDOW");
3345 switch (proc->provolatile)
3346 {
3348 appendStringInfoString(&buf, " IMMUTABLE");
3349 break;
3350 case PROVOLATILE_STABLE:
3351 appendStringInfoString(&buf, " STABLE");
3352 break;
3354 break;
3355 }
3356
3357 switch (proc->proparallel)
3358 {
3359 case PROPARALLEL_SAFE:
3360 appendStringInfoString(&buf, " PARALLEL SAFE");
3361 break;
3363 appendStringInfoString(&buf, " PARALLEL RESTRICTED");
3364 break;
3365 case PROPARALLEL_UNSAFE:
3366 break;
3367 }
3368
3369 if (proc->proisstrict)
3370 appendStringInfoString(&buf, " STRICT");
3371 if (proc->prosecdef)
3372 appendStringInfoString(&buf, " SECURITY DEFINER");
3373 if (proc->proleakproof)
3374 appendStringInfoString(&buf, " LEAKPROOF");
3375
3376 /* This code for the default cost and rows should match functioncmds.c */
3377 if (proc->prolang == INTERNALlanguageId ||
3378 proc->prolang == ClanguageId)
3379 procost = 1;
3380 else
3381 procost = 100;
3382 if (proc->procost != procost)
3383 appendStringInfo(&buf, " COST %g", proc->procost);
3384
3385 if (proc->prorows > 0 && proc->prorows != 1000)
3386 appendStringInfo(&buf, " ROWS %g", proc->prorows);
3387
3388 if (proc->prosupport)
3389 {
3390 Oid argtypes[1];
3391
3392 /*
3393 * We should qualify the support function's name if it wouldn't be
3394 * resolved by lookup in the current search path.
3395 */
3396 argtypes[0] = INTERNALOID;
3397 appendStringInfo(&buf, " SUPPORT %s",
3398 generate_function_name(proc->prosupport, 1,
3399 NIL, argtypes,
3400 false, NULL, false));
3401 }
3402
3403 if (oldlen != buf.len)
3404 appendStringInfoChar(&buf, '\n');
3405
3406 /* Emit any proconfig options, one per line */
3408 if (!isnull)
3409 {
3411 int i;
3412
3414 Assert(ARR_NDIM(a) == 1);
3415 Assert(ARR_LBOUND(a)[0] == 1);
3416
3417 for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3418 {
3419 Datum d;
3420
3421 d = array_ref(a, 1, &i,
3422 -1 /* varlenarray */ ,
3423 -1 /* TEXT's typlen */ ,
3424 false /* TEXT's typbyval */ ,
3425 TYPALIGN_INT /* TEXT's typalign */ ,
3426 &isnull);
3427 if (!isnull)
3428 {
3430 char *pos;
3431
3432 pos = strchr(configitem, '=');
3433 if (pos == NULL)
3434 continue;
3435 *pos++ = '\0';
3436
3437 appendStringInfo(&buf, " SET %s TO ",
3439
3440 /*
3441 * Variables that are marked GUC_LIST_QUOTE were already fully
3442 * quoted by flatten_set_variable_args() before they were put
3443 * into the proconfig array. However, because the quoting
3444 * rules used there aren't exactly like SQL's, we have to
3445 * break the list value apart and then quote the elements as
3446 * string literals. (The elements may be double-quoted as-is,
3447 * but we can't just feed them to the SQL parser; it would do
3448 * the wrong thing with elements that are zero-length or
3449 * longer than NAMEDATALEN.) Also, we need a special case for
3450 * empty lists.
3451 *
3452 * Variables that are not so marked should just be emitted as
3453 * simple string literals. If the variable is not known to
3454 * guc.c, we'll do that; this makes it unsafe to use
3455 * GUC_LIST_QUOTE for extension variables.
3456 */
3458 {
3459 List *namelist;
3460 ListCell *lc;
3461
3462 /* Parse string into list of identifiers */
3463 if (!SplitGUCList(pos, ',', &namelist))
3464 {
3465 /* this shouldn't fail really */
3466 elog(ERROR, "invalid list syntax in proconfig item");
3467 }
3468 /* Special case: represent an empty list as NULL */
3469 if (namelist == NIL)
3470 appendStringInfoString(&buf, "NULL");
3471 foreach(lc, namelist)
3472 {
3473 char *curname = (char *) lfirst(lc);
3474
3476 if (lnext(namelist, lc))
3478 }
3479 }
3480 else
3482 appendStringInfoChar(&buf, '\n');
3483 }
3484 }
3485 }
3486
3487 /* And finally the function definition ... */
3489 if (proc->prolang == SQLlanguageId && !isnull)
3490 {
3492 }
3493 else
3494 {
3495 appendStringInfoString(&buf, "AS ");
3496
3498 if (!isnull)
3499 {
3501 appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3502 }
3503
3505 prosrc = TextDatumGetCString(tmp);
3506
3507 /*
3508 * We always use dollar quoting. Figure out a suitable delimiter.
3509 *
3510 * Since the user is likely to be editing the function body string, we
3511 * shouldn't use a short delimiter that he might easily create a
3512 * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3513 * if needed.
3514 */
3516 appendStringInfoChar(&dq, '$');
3517 appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3518 while (strstr(prosrc, dq.data) != NULL)
3519 appendStringInfoChar(&dq, 'x');
3520 appendStringInfoChar(&dq, '$');
3521
3522 appendBinaryStringInfo(&buf, dq.data, dq.len);
3523 appendStringInfoString(&buf, prosrc);
3524 appendBinaryStringInfo(&buf, dq.data, dq.len);
3525 }
3526
3527 appendStringInfoChar(&buf, '\n');
3528
3530
3532}

References a, appendBinaryStringInfo(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), ARR_DIMS, ARR_ELEMTYPE, ARR_LBOUND, ARR_NDIM, array_ref(), Assert, buf, DatumGetArrayTypeP, elog, ereport, errcode(), errmsg, ERROR, fb(), Form_pg_proc, generate_function_name(), get_language_name(), get_namespace_name_or_temp(), GetConfigOptionFlags(), GETSTRUCT(), GUC_LIST_QUOTE, HeapTupleIsValid, i, initStringInfo(), lfirst, lnext(), name, NameStr, NIL, ObjectIdGetDatum(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, print_function_arguments(), print_function_rettype(), print_function_sqlbody(), print_function_trftypes(), quote_identifier(), quote_qualified_identifier(), ReleaseSysCache(), SearchSysCache1(), simple_quote_literal(), SplitGUCList(), string_to_text(), SysCacheGetAttr(), SysCacheGetAttrNotNull(), and TextDatumGetCString.

◆ pg_get_indexdef()

Datum pg_get_indexdef ( PG_FUNCTION_ARGS  )

Definition at line 1189 of file ruleutils.c.

1190{
1191 Oid indexrelid = PG_GETARG_OID(0);
1192 int prettyFlags;
1193 char *res;
1194
1195 prettyFlags = PRETTYFLAG_INDENT;
1196
1197 res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1198 false, false,
1199 false, false,
1200 prettyFlags, true);
1201
1202 if (res == NULL)
1204
1206}

References fb(), pg_get_indexdef_worker(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, and string_to_text().

◆ pg_get_indexdef_columns()

char * pg_get_indexdef_columns ( Oid  indexrelid,
bool  pretty 
)

Definition at line 1246 of file ruleutils.c.

1247{
1248 int prettyFlags;
1249
1250 prettyFlags = GET_PRETTY_FLAGS(pretty);
1251
1252 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1253 true, true,
1254 false, false,
1255 prettyFlags, false);
1256}

References fb(), GET_PRETTY_FLAGS, and pg_get_indexdef_worker().

Referenced by BuildIndexValueDescription().

◆ pg_get_indexdef_columns_extended()

char * pg_get_indexdef_columns_extended ( Oid  indexrelid,
uint16  flags 
)

Definition at line 1260 of file ruleutils.c.

1261{
1262 bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1263 bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1264 int prettyFlags;
1265
1266 prettyFlags = GET_PRETTY_FLAGS(pretty);
1267
1268 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1269 true, keys_only,
1270 false, false,
1271 prettyFlags, false);
1272}

References fb(), GET_PRETTY_FLAGS, pg_get_indexdef_worker(), RULE_INDEXDEF_KEYS_ONLY, and RULE_INDEXDEF_PRETTY.

Referenced by gist_page_items().

◆ pg_get_indexdef_ext()

Datum pg_get_indexdef_ext ( PG_FUNCTION_ARGS  )

Definition at line 1209 of file ruleutils.c.

1210{
1211 Oid indexrelid = PG_GETARG_OID(0);
1212 int32 colno = PG_GETARG_INT32(1);
1213 bool pretty = PG_GETARG_BOOL(2);
1214 int prettyFlags;
1215 char *res;
1216
1217 prettyFlags = GET_PRETTY_FLAGS(pretty);
1218
1219 res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1220 colno != 0, false,
1221 false, false,
1222 prettyFlags, true);
1223
1224 if (res == NULL)
1226
1228}

References fb(), GET_PRETTY_FLAGS, pg_get_indexdef_worker(), PG_GETARG_BOOL, PG_GETARG_INT32, PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, and string_to_text().

◆ pg_get_indexdef_string()

char * pg_get_indexdef_string ( Oid  indexrelid)

Definition at line 1236 of file ruleutils.c.

1237{
1238 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1239 false, false,
1240 true, true,
1241 0, false);
1242}

References fb(), and pg_get_indexdef_worker().

Referenced by RememberIndexForRebuilding().

◆ pg_get_indexdef_worker()

static char * pg_get_indexdef_worker ( Oid  indexrelid,
int  colno,
const Oid excludeOps,
bool  attrsOnly,
bool  keysOnly,
bool  showTblSpc,
bool  inherits,
int  prettyFlags,
bool  missing_ok 
)
static

Definition at line 1281 of file ruleutils.c.

1286{
1287 /* might want a separate isConstraint parameter later */
1288 bool isConstraint = (excludeOps != NULL);
1296 List *indexprs;
1298 List *context;
1299 Oid indrelid;
1300 int keyno;
1308 char *str;
1309 char *sep;
1310
1311 /*
1312 * Fetch the pg_index tuple by the Oid of the index
1313 */
1316 {
1317 if (missing_ok)
1318 return NULL;
1319 elog(ERROR, "cache lookup failed for index %u", indexrelid);
1320 }
1322
1323 indrelid = idxrec->indrelid;
1324 Assert(indexrelid == idxrec->indexrelid);
1325
1326 /* Must get indcollation, indclass, and indoption the hard way */
1330
1334
1338
1339 /*
1340 * Fetch the pg_class tuple of the index relation
1341 */
1344 elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1346
1347 /*
1348 * Fetch the pg_am tuple of the index' access method
1349 */
1351 if (!HeapTupleIsValid(ht_am))
1352 elog(ERROR, "cache lookup failed for access method %u",
1353 idxrelrec->relam);
1355
1356 /* Fetch the index AM's API struct */
1357 amroutine = GetIndexAmRoutine(amrec->amhandler);
1358
1359 /*
1360 * Get the index expressions, if any. (NOTE: we do not use the relcache
1361 * versions of the expressions and predicate, because we want to display
1362 * non-const-folded expressions.)
1363 */
1365 {
1367 char *exprsString;
1368
1374 }
1375 else
1376 indexprs = NIL;
1377
1379
1381
1382 /*
1383 * Start the index definition. Note that the index's name should never be
1384 * schema-qualified, but the indexed rel's name may be.
1385 */
1387
1388 if (!attrsOnly)
1389 {
1390 if (!isConstraint)
1391 appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
1392 idxrec->indisunique ? "UNIQUE " : "",
1395 && !inherits ? "ONLY " : "",
1396 (prettyFlags & PRETTYFLAG_SCHEMA) ?
1399 quote_identifier(NameStr(amrec->amname)));
1400 else /* currently, must be EXCLUDE constraint */
1401 appendStringInfo(&buf, "EXCLUDE USING %s (",
1402 quote_identifier(NameStr(amrec->amname)));
1403 }
1404
1405 /*
1406 * Report the indexed attributes
1407 */
1408 sep = "";
1409 for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1410 {
1411 AttrNumber attnum = idxrec->indkey.values[keyno];
1414
1415 /*
1416 * Ignore non-key attributes if told to.
1417 */
1418 if (keysOnly && keyno >= idxrec->indnkeyatts)
1419 break;
1420
1421 /* Otherwise, print INCLUDE to divide key and non-key attrs. */
1422 if (!colno && keyno == idxrec->indnkeyatts)
1423 {
1424 appendStringInfoString(&buf, ") INCLUDE (");
1425 sep = "";
1426 }
1427
1428 if (!colno)
1430 sep = ", ";
1431
1432 if (attnum != 0)
1433 {
1434 /* Simple index column */
1435 char *attname;
1437
1439 if (!colno || colno == keyno + 1)
1444 }
1445 else
1446 {
1447 /* expressional index */
1448 Node *indexkey;
1449
1450 if (indexpr_item == NULL)
1451 elog(ERROR, "too few entries in indexprs list");
1454 /* Deparse */
1455 str = deparse_expression_pretty(indexkey, context, false, false,
1456 prettyFlags, 0);
1457 if (!colno || colno == keyno + 1)
1458 {
1459 /* Need parens if it's not a bare function call */
1462 else
1463 appendStringInfo(&buf, "(%s)", str);
1464 }
1467 }
1468
1469 /* Print additional decoration for (selected) key columns */
1471 (!colno || colno == keyno + 1))
1472 {
1473 int16 opt = indoption->values[keyno];
1474 Oid indcoll = indcollation->values[keyno];
1475 Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1476 bool has_options = attoptions != (Datum) 0;
1477
1478 /* Add collation, if not default for column */
1480 appendStringInfo(&buf, " COLLATE %s",
1482
1483 /* Add the operator class name, if not default */
1484 get_opclass_name(indclass->values[keyno],
1486
1487 if (has_options)
1488 {
1490 get_reloptions(&buf, attoptions);
1492 }
1493
1494 /* Add options if relevant */
1495 if (amroutine->amcanorder)
1496 {
1497 /* if it supports sort ordering, report DESC and NULLS opts */
1498 if (opt & INDOPTION_DESC)
1499 {
1500 appendStringInfoString(&buf, " DESC");
1501 /* NULLS FIRST is the default in this case */
1502 if (!(opt & INDOPTION_NULLS_FIRST))
1503 appendStringInfoString(&buf, " NULLS LAST");
1504 }
1505 else
1506 {
1507 if (opt & INDOPTION_NULLS_FIRST)
1508 appendStringInfoString(&buf, " NULLS FIRST");
1509 }
1510 }
1511
1512 /* Add the exclusion operator if relevant */
1513 if (excludeOps != NULL)
1514 appendStringInfo(&buf, " WITH %s",
1516 keycoltype,
1517 keycoltype));
1518 }
1519 }
1520
1521 if (!attrsOnly)
1522 {
1524
1525 if (idxrec->indnullsnotdistinct)
1526 appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1527
1528 /*
1529 * If it has options, append "WITH (options)"
1530 */
1531 str = flatten_reloptions(indexrelid);
1532 if (str)
1533 {
1534 appendStringInfo(&buf, " WITH (%s)", str);
1535 pfree(str);
1536 }
1537
1538 /*
1539 * Print tablespace, but only if requested
1540 */
1541 if (showTblSpc)
1542 {
1543 Oid tblspc;
1544
1545 tblspc = get_rel_tablespace(indexrelid);
1546 if (OidIsValid(tblspc))
1547 {
1548 if (isConstraint)
1549 appendStringInfoString(&buf, " USING INDEX");
1550 appendStringInfo(&buf, " TABLESPACE %s",
1552 }
1553 }
1554
1555 /*
1556 * If it's a partial index, decompile and append the predicate
1557 */
1559 {
1560 Node *node;
1562 char *predString;
1563
1564 /* Convert text string to node tree */
1568 node = (Node *) stringToNode(predString);
1570
1571 /* Deparse */
1572 str = deparse_expression_pretty(node, context, false, false,
1573 prettyFlags, 0);
1574 if (isConstraint)
1575 appendStringInfo(&buf, " WHERE (%s)", str);
1576 else
1577 appendStringInfo(&buf, " WHERE %s", str);
1578 }
1579 }
1580
1581 /* Clean up */
1585
1586 return buf.data;
1587}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, attname, attnum, buf, DatumGetPointer(), deparse_context_for(), deparse_expression_pretty(), elog, ERROR, exprCollation(), exprType(), fb(), flatten_reloptions(), Form_pg_am, Form_pg_index, generate_collation_name(), generate_operator_name(), generate_qualified_relation_name(), generate_relation_name(), get_attname(), get_attoptions(), get_atttypetypmodcoll(), get_opclass_name(), get_rel_tablespace(), get_relation_name(), get_reloptions(), get_tablespace_name(), GetIndexAmRoutine(), GETSTRUCT(), heap_attisnull(), HeapTupleIsValid, initStringInfo(), InvalidOid, lfirst, list_head(), lnext(), looks_like_function(), NameStr, NIL, ObjectIdGetDatum(), OidIsValid, pfree(), PRETTYFLAG_SCHEMA, quote_identifier(), ReleaseSysCache(), SearchSysCache1(), str, stringToNode(), SysCacheGetAttrNotNull(), and TextDatumGetCString.

Referenced by pg_get_constraintdef_worker(), pg_get_indexdef(), pg_get_indexdef_columns(), pg_get_indexdef_columns_extended(), pg_get_indexdef_ext(), and pg_get_indexdef_string().

◆ pg_get_partconstrdef_string()

char * pg_get_partconstrdef_string ( Oid  partitionId,
char aliasname 
)

Definition at line 2486 of file ruleutils.c.

2487{
2489 List *context;
2490
2492 context = deparse_context_for(aliasname, partitionId);
2493
2494 return deparse_expression((Node *) constr_expr, context, true, false);
2495}

References deparse_context_for(), deparse_expression(), fb(), and get_partition_qual_relid().

Referenced by RI_PartitionRemove_Check().

◆ pg_get_partition_constraintdef()

Datum pg_get_partition_constraintdef ( PG_FUNCTION_ARGS  )

Definition at line 2454 of file ruleutils.c.

2455{
2458 int prettyFlags;
2459 List *context;
2460 char *consrc;
2461
2463
2464 /* Quick exit if no partition constraint */
2465 if (constr_expr == NULL)
2467
2468 /*
2469 * Deparse and return the constraint expression.
2470 */
2471 prettyFlags = PRETTYFLAG_INDENT;
2473 consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2474 false, prettyFlags, 0);
2475
2477}

References deparse_context_for(), deparse_expression_pretty(), fb(), get_partition_qual_relid(), get_relation_name(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, and string_to_text().

◆ pg_get_partkeydef()

Datum pg_get_partkeydef ( PG_FUNCTION_ARGS  )

Definition at line 2267 of file ruleutils.c.

2268{
2269 Oid relid = PG_GETARG_OID(0);
2270 char *res;
2271
2272 res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
2273
2274 if (res == NULL)
2276
2278}

References fb(), pg_get_partkeydef_worker(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, and string_to_text().

◆ pg_get_partkeydef_columns()

char * pg_get_partkeydef_columns ( Oid  relid,
bool  pretty 
)

Definition at line 2282 of file ruleutils.c.

2283{
2284 int prettyFlags;
2285
2286 prettyFlags = GET_PRETTY_FLAGS(pretty);
2287
2288 return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
2289}

References fb(), GET_PRETTY_FLAGS, and pg_get_partkeydef_worker().

Referenced by ExecBuildSlotPartitionKeyDescription().

◆ pg_get_partkeydef_worker()

static char * pg_get_partkeydef_worker ( Oid  relid,
int  prettyFlags,
bool  attrsOnly,
bool  missing_ok 
)
static

Definition at line 2295 of file ruleutils.c.

2297{
2299 HeapTuple tuple;
2301 oidvector *partcollation;
2302 List *partexprs;
2304 List *context;
2305 Datum datum;
2307 int keyno;
2308 char *str;
2309 char *sep;
2310
2312 if (!HeapTupleIsValid(tuple))
2313 {
2314 if (missing_ok)
2315 return NULL;
2316 elog(ERROR, "cache lookup failed for partition key of %u", relid);
2317 }
2318
2320
2321 Assert(form->partrelid == relid);
2322
2323 /* Must get partclass and partcollation the hard way */
2324 datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2326 partclass = (oidvector *) DatumGetPointer(datum);
2327
2328 datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2330 partcollation = (oidvector *) DatumGetPointer(datum);
2331
2332
2333 /*
2334 * Get the expressions, if any. (NOTE: we do not use the relcache
2335 * versions of the expressions, because we want to display
2336 * non-const-folded expressions.)
2337 */
2339 {
2341 char *exprsString;
2342
2346 partexprs = (List *) stringToNode(exprsString);
2347
2348 if (!IsA(partexprs, List))
2349 elog(ERROR, "unexpected node type found in partexprs: %d",
2350 (int) nodeTag(partexprs));
2351
2353 }
2354 else
2355 partexprs = NIL;
2356
2357 partexpr_item = list_head(partexprs);
2358 context = deparse_context_for(get_relation_name(relid), relid);
2359
2361
2362 switch (form->partstrat)
2363 {
2365 if (!attrsOnly)
2366 appendStringInfoString(&buf, "HASH");
2367 break;
2369 if (!attrsOnly)
2370 appendStringInfoString(&buf, "LIST");
2371 break;
2373 if (!attrsOnly)
2374 appendStringInfoString(&buf, "RANGE");
2375 break;
2376 default:
2377 elog(ERROR, "unexpected partition strategy: %d",
2378 (int) form->partstrat);
2379 }
2380
2381 if (!attrsOnly)
2383 sep = "";
2384 for (keyno = 0; keyno < form->partnatts; keyno++)
2385 {
2386 AttrNumber attnum = form->partattrs.values[keyno];
2389 Oid partcoll;
2390
2392 sep = ", ";
2393 if (attnum != 0)
2394 {
2395 /* Simple attribute reference */
2396 char *attname;
2398
2399 attname = get_attname(relid, attnum, false);
2404 }
2405 else
2406 {
2407 /* Expression */
2408 Node *partkey;
2409
2410 if (partexpr_item == NULL)
2411 elog(ERROR, "too few entries in partexprs list");
2413 partexpr_item = lnext(partexprs, partexpr_item);
2414
2415 /* Deparse */
2416 str = deparse_expression_pretty(partkey, context, false, false,
2417 prettyFlags, 0);
2418 /* Need parens if it's not a bare function call */
2421 else
2422 appendStringInfo(&buf, "(%s)", str);
2423
2426 }
2427
2428 /* Add collation, if not default for column */
2429 partcoll = partcollation->values[keyno];
2431 appendStringInfo(&buf, " COLLATE %s",
2433
2434 /* Add the operator class name, if not default */
2435 if (!attrsOnly)
2436 get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2437 }
2438
2439 if (!attrsOnly)
2441
2442 /* Clean up */
2443 ReleaseSysCache(tuple);
2444
2445 return buf.data;
2446}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, attname, attnum, buf, DatumGetPointer(), deparse_context_for(), deparse_expression_pretty(), elog, ERROR, exprCollation(), exprType(), fb(), Form_pg_partitioned_table, generate_collation_name(), get_attname(), get_atttypetypmodcoll(), get_opclass_name(), get_relation_name(), GETSTRUCT(), heap_attisnull(), HeapTupleIsValid, initStringInfo(), IsA, lfirst, list_head(), lnext(), looks_like_function(), NIL, nodeTag, ObjectIdGetDatum(), OidIsValid, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, pfree(), quote_identifier(), ReleaseSysCache(), SearchSysCache1(), str, stringToNode(), SysCacheGetAttrNotNull(), TextDatumGetCString, and oidvector::values.

Referenced by pg_get_partkeydef(), and pg_get_partkeydef_columns().

◆ pg_get_propgraphdef()

◆ pg_get_querydef()

char * pg_get_querydef ( Query query,
bool  pretty 
)

Definition at line 1599 of file ruleutils.c.

1600{
1602 int prettyFlags;
1603
1604 prettyFlags = GET_PRETTY_FLAGS(pretty);
1605
1607
1608 get_query_def(query, &buf, NIL, NULL, true,
1609 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1610
1611 return buf.data;
1612}

References buf, fb(), GET_PRETTY_FLAGS, get_query_def(), initStringInfo(), NIL, and WRAP_COLUMN_DEFAULT.

◆ pg_get_ruledef()

Datum pg_get_ruledef ( PG_FUNCTION_ARGS  )

Definition at line 571 of file ruleutils.c.

572{
574 int prettyFlags;
575 char *res;
576
577 prettyFlags = PRETTYFLAG_INDENT;
578
579 res = pg_get_ruledef_worker(ruleoid, prettyFlags);
580
581 if (res == NULL)
583
585}

References fb(), pg_get_ruledef_worker(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, and string_to_text().

◆ pg_get_ruledef_ext()

Datum pg_get_ruledef_ext ( PG_FUNCTION_ARGS  )

Definition at line 589 of file ruleutils.c.

590{
592 bool pretty = PG_GETARG_BOOL(1);
593 int prettyFlags;
594 char *res;
595
596 prettyFlags = GET_PRETTY_FLAGS(pretty);
597
598 res = pg_get_ruledef_worker(ruleoid, prettyFlags);
599
600 if (res == NULL)
602
604}

References fb(), GET_PRETTY_FLAGS, pg_get_ruledef_worker(), PG_GETARG_BOOL, PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, and string_to_text().

◆ pg_get_ruledef_worker()

static char * pg_get_ruledef_worker ( Oid  ruleoid,
int  prettyFlags 
)
static

Definition at line 608 of file ruleutils.c.

609{
610 Datum args[1];
611 char nulls[1];
612 int spirc;
616
617 /*
618 * Do this first so that string is alloc'd in outer context not SPI's.
619 */
621
622 /*
623 * Connect to SPI manager
624 */
625 SPI_connect();
626
627 /*
628 * On the first call prepare the plan to lookup pg_rewrite. We read
629 * pg_rewrite over the SPI manager instead of using the syscache to be
630 * checked for read access on pg_rewrite.
631 */
632 if (plan_getrulebyoid == NULL)
633 {
634 Oid argtypes[1];
636
637 argtypes[0] = OIDOID;
638 plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
639 if (plan == NULL)
640 elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
643 }
644
645 /*
646 * Get the pg_rewrite tuple for this rule
647 */
649 nulls[0] = ' ';
650 spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
651 if (spirc != SPI_OK_SELECT)
652 elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
653 if (SPI_processed != 1)
654 {
655 /*
656 * There is no tuple data available here, just keep the output buffer
657 * empty.
658 */
659 }
660 else
661 {
662 /*
663 * Get the rule's definition and put it into executor's memory
664 */
667 make_ruledef(&buf, ruletup, rulettc, prettyFlags);
668 }
669
670 /*
671 * Disconnect from SPI manager
672 */
673 if (SPI_finish() != SPI_OK_FINISH)
674 elog(ERROR, "SPI_finish failed");
675
676 if (buf.len == 0)
677 return NULL;
678
679 return buf.data;
680}

References buf, elog, ERROR, fb(), initStringInfo(), make_ruledef(), ObjectIdGetDatum(), plan, plan_getrulebyoid, query_getrulebyoid, SPI_connect(), SPI_execute_plan(), SPI_finish(), SPI_keepplan(), SPI_OK_FINISH, SPI_OK_SELECT, SPI_prepare(), SPI_processed, SPI_tuptable, SPITupleTable::tupdesc, and SPITupleTable::vals.

Referenced by pg_get_ruledef(), and pg_get_ruledef_ext().

◆ pg_get_serial_sequence()

Datum pg_get_serial_sequence ( PG_FUNCTION_ARGS  )

Definition at line 3191 of file ruleutils.c.

3192{
3193 text *tablename = PG_GETARG_TEXT_PP(0);
3196 Oid tableOid;
3197 char *column;
3201 ScanKeyData key[3];
3202 SysScanDesc scan;
3203 HeapTuple tup;
3204
3205 /* Look up table name. Can't lock it - we might not have privileges. */
3207 tableOid = RangeVarGetRelid(tablerv, NoLock, false);
3208
3209 /* Get the number of the column */
3211
3212 attnum = get_attnum(tableOid, column);
3214 ereport(ERROR,
3216 errmsg("column \"%s\" of relation \"%s\" does not exist",
3217 column, tablerv->relname)));
3218
3219 /* Search the dependency table for the dependent sequence */
3221
3222 ScanKeyInit(&key[0],
3226 ScanKeyInit(&key[1],
3229 ObjectIdGetDatum(tableOid));
3230 ScanKeyInit(&key[2],
3234
3236 NULL, 3, key);
3237
3238 while (HeapTupleIsValid(tup = systable_getnext(scan)))
3239 {
3241
3242 /*
3243 * Look for an auto dependency (serial column) or internal dependency
3244 * (identity column) of a sequence on a column. (We need the relkind
3245 * test because indexes can also have auto dependencies on columns.)
3246 */
3247 if (deprec->classid == RelationRelationId &&
3248 deprec->objsubid == 0 &&
3249 (deprec->deptype == DEPENDENCY_AUTO ||
3250 deprec->deptype == DEPENDENCY_INTERNAL) &&
3252 {
3253 sequenceId = deprec->objid;
3254 break;
3255 }
3256 }
3257
3258 systable_endscan(scan);
3260
3262 {
3263 char *result;
3264
3266
3268 }
3269
3271}

References AccessShareLock, attnum, BTEqualStrategyNumber, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, ereport, errcode(), errmsg, ERROR, fb(), Form_pg_depend, generate_qualified_relation_name(), get_attnum(), get_rel_relkind(), GETSTRUCT(), HeapTupleIsValid, Int32GetDatum(), InvalidAttrNumber, InvalidOid, makeRangeVarFromNameList(), NoLock, ObjectIdGetDatum(), OidIsValid, PG_GETARG_TEXT_PP, PG_RETURN_NULL, PG_RETURN_TEXT_P, RangeVarGetRelid, result, ScanKeyInit(), string_to_text(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), text_to_cstring(), and textToQualifiedNameList().

◆ pg_get_statisticsobj_worker()

static char * pg_get_statisticsobj_worker ( Oid  statextid,
bool  columns_only,
bool  missing_ok 
)
static

Definition at line 2012 of file ruleutils.c.

2013{
2017 int colno;
2018 char *nsp;
2019 ArrayType *arr;
2020 char *enabled;
2021 Datum datum;
2022 bool ndistinct_enabled;
2024 bool mcv_enabled;
2025 int i;
2026 List *context;
2027 ListCell *lc;
2028 List *exprs = NIL;
2029 bool has_exprs;
2030 int ncolumns;
2031
2033
2035 {
2036 if (missing_ok)
2037 return NULL;
2038 elog(ERROR, "cache lookup failed for statistics object %u", statextid);
2039 }
2040
2041 /* has the statistics expressions? */
2043
2045
2046 /*
2047 * Get the statistics expressions, if any. (NOTE: we do not use the
2048 * relcache versions of the expressions, because we want to display
2049 * non-const-folded expressions.)
2050 */
2051 if (has_exprs)
2052 {
2054 char *exprsString;
2055
2059 exprs = (List *) stringToNode(exprsString);
2061 }
2062 else
2063 exprs = NIL;
2064
2065 /* count the number of columns (attributes and expressions) */
2066 ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
2067
2069
2070 if (!columns_only)
2071 {
2072 nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
2073 appendStringInfo(&buf, "CREATE STATISTICS %s",
2075 NameStr(statextrec->stxname)));
2076
2077 /*
2078 * Decode the stxkind column so that we know which stats types to
2079 * print.
2080 */
2083 arr = DatumGetArrayTypeP(datum);
2084 if (ARR_NDIM(arr) != 1 ||
2085 ARR_HASNULL(arr) ||
2086 ARR_ELEMTYPE(arr) != CHAROID)
2087 elog(ERROR, "stxkind is not a 1-D char array");
2088 enabled = (char *) ARR_DATA_PTR(arr);
2089
2090 ndistinct_enabled = false;
2091 dependencies_enabled = false;
2092 mcv_enabled = false;
2093
2094 for (i = 0; i < ARR_DIMS(arr)[0]; i++)
2095 {
2096 if (enabled[i] == STATS_EXT_NDISTINCT)
2097 ndistinct_enabled = true;
2098 else if (enabled[i] == STATS_EXT_DEPENDENCIES)
2099 dependencies_enabled = true;
2100 else if (enabled[i] == STATS_EXT_MCV)
2101 mcv_enabled = true;
2102
2103 /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
2104 }
2105
2106 /*
2107 * If any option is disabled, then we'll need to append the types
2108 * clause to show which options are enabled. We omit the types clause
2109 * on purpose when all options are enabled, so a pg_dump/pg_restore
2110 * will create all statistics types on a newer postgres version, if
2111 * the statistics had all options enabled on the original version.
2112 *
2113 * But if the statistics is defined on just a single column, it has to
2114 * be an expression statistics. In that case we don't need to specify
2115 * kinds.
2116 */
2118 (ncolumns > 1))
2119 {
2120 bool gotone = false;
2121
2123
2125 {
2126 appendStringInfoString(&buf, "ndistinct");
2127 gotone = true;
2128 }
2129
2131 {
2132 appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
2133 gotone = true;
2134 }
2135
2136 if (mcv_enabled)
2137 appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
2138
2140 }
2141
2142 appendStringInfoString(&buf, " ON ");
2143 }
2144
2145 /* decode simple column references */
2146 for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
2147 {
2148 AttrNumber attnum = statextrec->stxkeys.values[colno];
2149 char *attname;
2150
2151 if (colno > 0)
2153
2154 attname = get_attname(statextrec->stxrelid, attnum, false);
2155
2157 }
2158
2160 statextrec->stxrelid);
2161
2162 foreach(lc, exprs)
2163 {
2164 Node *expr = (Node *) lfirst(lc);
2165 char *str;
2166 int prettyFlags = PRETTYFLAG_PAREN;
2167
2168 str = deparse_expression_pretty(expr, context, false, false,
2169 prettyFlags, 0);
2170
2171 if (colno > 0)
2173
2174 /* Need parens if it's not a bare function call */
2175 if (looks_like_function(expr))
2177 else
2178 appendStringInfo(&buf, "(%s)", str);
2179
2180 colno++;
2181 }
2182
2183 if (!columns_only)
2184 appendStringInfo(&buf, " FROM %s",
2186
2188
2189 return buf.data;
2190}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_NDIM, attname, attnum, buf, DatumGetArrayTypeP, deparse_context_for(), deparse_expression_pretty(), elog, ERROR, fb(), Form_pg_statistic_ext, generate_relation_name(), get_attname(), get_namespace_name_or_temp(), get_relation_name(), GETSTRUCT(), heap_attisnull(), HeapTupleIsValid, i, initStringInfo(), lfirst, list_length(), looks_like_function(), NameStr, NIL, ObjectIdGetDatum(), pfree(), PRETTYFLAG_PAREN, quote_identifier(), quote_qualified_identifier(), ReleaseSysCache(), SearchSysCache1(), str, stringToNode(), SysCacheGetAttrNotNull(), and TextDatumGetCString.

Referenced by pg_get_statisticsobjdef(), pg_get_statisticsobjdef_columns(), and pg_get_statisticsobjdef_string().

◆ pg_get_statisticsobjdef()

Datum pg_get_statisticsobjdef ( PG_FUNCTION_ARGS  )

Definition at line 1967 of file ruleutils.c.

1968{
1970 char *res;
1971
1972 res = pg_get_statisticsobj_worker(statextid, false, true);
1973
1974 if (res == NULL)
1976
1978}

References fb(), pg_get_statisticsobj_worker(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, and string_to_text().

◆ pg_get_statisticsobjdef_columns()

Datum pg_get_statisticsobjdef_columns ( PG_FUNCTION_ARGS  )

Definition at line 1995 of file ruleutils.c.

1996{
1998 char *res;
1999
2000 res = pg_get_statisticsobj_worker(statextid, true, true);
2001
2002 if (res == NULL)
2004
2006}

References fb(), pg_get_statisticsobj_worker(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, and string_to_text().

◆ pg_get_statisticsobjdef_expressions()

Datum pg_get_statisticsobjdef_expressions ( PG_FUNCTION_ARGS  )

Definition at line 2196 of file ruleutils.c.

2197{
2201 Datum datum;
2202 List *context;
2203 ListCell *lc;
2204 List *exprs = NIL;
2205 bool has_exprs;
2206 char *tmp;
2207 ArrayBuildState *astate = NULL;
2208
2210
2213
2214 /* Does the stats object have expressions? */
2216
2217 /* no expressions? we're done */
2218 if (!has_exprs)
2219 {
2222 }
2223
2225
2226 /*
2227 * Get the statistics expressions, and deparse them into text values.
2228 */
2231 tmp = TextDatumGetCString(datum);
2232 exprs = (List *) stringToNode(tmp);
2233 pfree(tmp);
2234
2236 statextrec->stxrelid);
2237
2238 foreach(lc, exprs)
2239 {
2240 Node *expr = (Node *) lfirst(lc);
2241 char *str;
2242 int prettyFlags = PRETTYFLAG_INDENT;
2243
2244 str = deparse_expression_pretty(expr, context, false, false,
2245 prettyFlags, 0);
2246
2247 astate = accumArrayResult(astate,
2249 false,
2250 TEXTOID,
2252 }
2253
2255
2257}

References accumArrayResult(), cstring_to_text(), CurrentMemoryContext, deparse_context_for(), deparse_expression_pretty(), fb(), Form_pg_statistic_ext, get_relation_name(), GETSTRUCT(), heap_attisnull(), HeapTupleIsValid, lfirst, makeArrayResult(), NIL, ObjectIdGetDatum(), pfree(), PG_GETARG_OID, PG_RETURN_DATUM, PG_RETURN_NULL, PointerGetDatum, PRETTYFLAG_INDENT, ReleaseSysCache(), SearchSysCache1(), str, stringToNode(), SysCacheGetAttrNotNull(), and TextDatumGetCString.

◆ pg_get_statisticsobjdef_string()

char * pg_get_statisticsobjdef_string ( Oid  statextid)

Definition at line 1985 of file ruleutils.c.

1986{
1987 return pg_get_statisticsobj_worker(statextid, false, false);
1988}

References fb(), and pg_get_statisticsobj_worker().

Referenced by RememberStatisticsForRebuilding().

◆ pg_get_triggerdef()

Datum pg_get_triggerdef ( PG_FUNCTION_ARGS  )

Definition at line 882 of file ruleutils.c.

883{
885 char *res;
886
887 res = pg_get_triggerdef_worker(trigid, false);
888
889 if (res == NULL)
891
893}

References fb(), pg_get_triggerdef_worker(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, and string_to_text().

◆ pg_get_triggerdef_ext()

Datum pg_get_triggerdef_ext ( PG_FUNCTION_ARGS  )

Definition at line 896 of file ruleutils.c.

897{
899 bool pretty = PG_GETARG_BOOL(1);
900 char *res;
901
903
904 if (res == NULL)
906
908}

References fb(), pg_get_triggerdef_worker(), PG_GETARG_BOOL, PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, and string_to_text().

◆ pg_get_triggerdef_worker()

static char * pg_get_triggerdef_worker ( Oid  trigid,
bool  pretty 
)
static

Definition at line 911 of file ruleutils.c.

912{
917 ScanKeyData skey[1];
919 int findx = 0;
920 char *tgname;
921 char *tgoldtable;
922 char *tgnewtable;
923 Datum value;
924 bool isnull;
925
926 /*
927 * Fetch the pg_trigger tuple by the Oid of the trigger
928 */
930
931 ScanKeyInit(&skey[0],
935
937 NULL, 1, skey);
938
940
942 {
945 return NULL;
946 }
947
949
950 /*
951 * Start the trigger definition. Note that the trigger's name should never
952 * be schema-qualified, but the trigger rel's name may be.
953 */
955
956 tgname = NameStr(trigrec->tgname);
957 appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
958 OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
959 quote_identifier(tgname));
960
961 if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
962 appendStringInfoString(&buf, "BEFORE");
963 else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
964 appendStringInfoString(&buf, "AFTER");
965 else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
966 appendStringInfoString(&buf, "INSTEAD OF");
967 else
968 elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
969
970 if (TRIGGER_FOR_INSERT(trigrec->tgtype))
971 {
972 appendStringInfoString(&buf, " INSERT");
973 findx++;
974 }
975 if (TRIGGER_FOR_DELETE(trigrec->tgtype))
976 {
977 if (findx > 0)
978 appendStringInfoString(&buf, " OR DELETE");
979 else
980 appendStringInfoString(&buf, " DELETE");
981 findx++;
982 }
983 if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
984 {
985 if (findx > 0)
986 appendStringInfoString(&buf, " OR UPDATE");
987 else
988 appendStringInfoString(&buf, " UPDATE");
989 findx++;
990 /* tgattr is first var-width field, so OK to access directly */
991 if (trigrec->tgattr.dim1 > 0)
992 {
993 int i;
994
995 appendStringInfoString(&buf, " OF ");
996 for (i = 0; i < trigrec->tgattr.dim1; i++)
997 {
998 char *attname;
999
1000 if (i > 0)
1002 attname = get_attname(trigrec->tgrelid,
1003 trigrec->tgattr.values[i], false);
1005 }
1006 }
1007 }
1008 if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
1009 {
1010 if (findx > 0)
1011 appendStringInfoString(&buf, " OR TRUNCATE");
1012 else
1013 appendStringInfoString(&buf, " TRUNCATE");
1014 findx++;
1015 }
1016
1017 /*
1018 * In non-pretty mode, always schema-qualify the target table name for
1019 * safety. In pretty mode, schema-qualify only if not visible.
1020 */
1021 appendStringInfo(&buf, " ON %s ",
1022 pretty ?
1025
1026 if (OidIsValid(trigrec->tgconstraint))
1027 {
1028 if (OidIsValid(trigrec->tgconstrrelid))
1029 appendStringInfo(&buf, "FROM %s ",
1030 generate_relation_name(trigrec->tgconstrrelid, NIL));
1031 if (!trigrec->tgdeferrable)
1032 appendStringInfoString(&buf, "NOT ");
1033 appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
1034 if (trigrec->tginitdeferred)
1035 appendStringInfoString(&buf, "DEFERRED ");
1036 else
1037 appendStringInfoString(&buf, "IMMEDIATE ");
1038 }
1039
1041 tgrel->rd_att, &isnull);
1042 if (!isnull)
1043 tgoldtable = NameStr(*DatumGetName(value));
1044 else
1045 tgoldtable = NULL;
1047 tgrel->rd_att, &isnull);
1048 if (!isnull)
1049 tgnewtable = NameStr(*DatumGetName(value));
1050 else
1051 tgnewtable = NULL;
1052 if (tgoldtable != NULL || tgnewtable != NULL)
1053 {
1054 appendStringInfoString(&buf, "REFERENCING ");
1055 if (tgoldtable != NULL)
1056 appendStringInfo(&buf, "OLD TABLE AS %s ",
1057 quote_identifier(tgoldtable));
1058 if (tgnewtable != NULL)
1059 appendStringInfo(&buf, "NEW TABLE AS %s ",
1060 quote_identifier(tgnewtable));
1061 }
1062
1063 if (TRIGGER_FOR_ROW(trigrec->tgtype))
1064 appendStringInfoString(&buf, "FOR EACH ROW ");
1065 else
1066 appendStringInfoString(&buf, "FOR EACH STATEMENT ");
1067
1068 /* If the trigger has a WHEN qualification, add that */
1070 tgrel->rd_att, &isnull);
1071 if (!isnull)
1072 {
1073 Node *qual;
1074 char relkind;
1075 deparse_context context;
1079
1080 appendStringInfoString(&buf, "WHEN (");
1081
1083
1084 relkind = get_rel_relkind(trigrec->tgrelid);
1085
1086 /* Build minimal OLD and NEW RTEs for the rel */
1088 oldrte->rtekind = RTE_RELATION;
1089 oldrte->relid = trigrec->tgrelid;
1090 oldrte->relkind = relkind;
1091 oldrte->rellockmode = AccessShareLock;
1092 oldrte->alias = makeAlias("old", NIL);
1093 oldrte->eref = oldrte->alias;
1094 oldrte->lateral = false;
1095 oldrte->inh = false;
1096 oldrte->inFromCl = true;
1097
1099 newrte->rtekind = RTE_RELATION;
1100 newrte->relid = trigrec->tgrelid;
1101 newrte->relkind = relkind;
1102 newrte->rellockmode = AccessShareLock;
1103 newrte->alias = makeAlias("new", NIL);
1104 newrte->eref = newrte->alias;
1105 newrte->lateral = false;
1106 newrte->inh = false;
1107 newrte->inFromCl = true;
1108
1109 /* Build two-element rtable */
1110 memset(&dpns, 0, sizeof(dpns));
1111 dpns.rtable = list_make2(oldrte, newrte);
1112 dpns.subplans = NIL;
1113 dpns.ctes = NIL;
1114 dpns.appendrels = NULL;
1117
1118 /* Set up context with one-deep namespace stack */
1119 context.buf = &buf;
1120 context.namespaces = list_make1(&dpns);
1121 context.resultDesc = NULL;
1122 context.targetList = NIL;
1123 context.windowClause = NIL;
1124 context.varprefix = true;
1127 context.indentLevel = PRETTYINDENT_STD;
1128 context.colNamesVisible = true;
1129 context.inGroupBy = false;
1130 context.varInOrderBy = false;
1131 context.appendparents = NULL;
1132
1133 get_rule_expr(qual, &context, false);
1134
1136 }
1137
1138 appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
1140 NIL, NULL,
1141 false, NULL, false));
1142
1143 if (trigrec->tgnargs > 0)
1144 {
1145 char *p;
1146 int i;
1147
1149 tgrel->rd_att, &isnull);
1150 if (isnull)
1151 elog(ERROR, "tgargs is null for trigger %u", trigid);
1152 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
1153 for (i = 0; i < trigrec->tgnargs; i++)
1154 {
1155 if (i > 0)
1158 /* advance p to next string embedded in tgargs */
1159 while (*p)
1160 p++;
1161 p++;
1162 }
1163 }
1164
1165 /* We deliberately do not put semi-colon at end */
1167
1168 /* Clean up */
1170
1172
1173 return buf.data;
1174}

References AccessShareLock, deparse_context::appendparents, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), attname, BTEqualStrategyNumber, deparse_context::buf, buf, deparse_context::colNamesVisible, DatumGetByteaPP, DatumGetName(), elog, ERROR, fastgetattr(), fb(), Form_pg_trigger, generate_function_name(), generate_qualified_relation_name(), generate_relation_name(), get_attname(), GET_PRETTY_FLAGS, get_rel_relkind(), get_rule_expr(), GETSTRUCT(), HeapTupleIsValid, i, deparse_context::indentLevel, deparse_context::inGroupBy, initStringInfo(), list_make1, list_make2, makeAlias(), makeNode, deparse_context::namespaces, NameStr, NIL, ObjectIdGetDatum(), OidIsValid, deparse_context::prettyFlags, PRETTYINDENT_STD, quote_identifier(), deparse_context::resultDesc, RTE_RELATION, ScanKeyInit(), set_rtable_names(), set_simple_column_names(), simple_quote_literal(), stringToNode(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), deparse_context::targetList, TextDatumGetCString, value, VARDATA_ANY(), deparse_context::varInOrderBy, deparse_context::varprefix, deparse_context::windowClause, WRAP_COLUMN_DEFAULT, and deparse_context::wrapColumn.

Referenced by pg_get_triggerdef(), and pg_get_triggerdef_ext().

◆ pg_get_userbyid()

Datum pg_get_userbyid ( PG_FUNCTION_ARGS  )

Definition at line 3153 of file ruleutils.c.

3154{
3155 Oid roleid = PG_GETARG_OID(0);
3156 Name result;
3159
3160 /*
3161 * Allocate space for the result
3162 */
3165
3166 /*
3167 * Get the pg_authid entry and print the result
3168 */
3171 {
3173 *result = role_rec->rolname;
3175 }
3176 else
3177 sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
3178
3180}

References fb(), Form_pg_authid, GETSTRUCT(), HeapTupleIsValid, NAMEDATALEN, NameStr, ObjectIdGetDatum(), palloc(), PG_GETARG_OID, PG_RETURN_NAME, ReleaseSysCache(), result, SearchSysCache1(), and sprintf.

◆ pg_get_viewdef()

Datum pg_get_viewdef ( PG_FUNCTION_ARGS  )

Definition at line 689 of file ruleutils.c.

690{
691 /* By OID */
692 Oid viewoid = PG_GETARG_OID(0);
693 int prettyFlags;
694 char *res;
695
696 prettyFlags = PRETTYFLAG_INDENT;
697
698 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
699
700 if (res == NULL)
702
704}

References fb(), pg_get_viewdef_worker(), PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, string_to_text(), and WRAP_COLUMN_DEFAULT.

◆ pg_get_viewdef_ext()

Datum pg_get_viewdef_ext ( PG_FUNCTION_ARGS  )

Definition at line 708 of file ruleutils.c.

709{
710 /* By OID */
711 Oid viewoid = PG_GETARG_OID(0);
712 bool pretty = PG_GETARG_BOOL(1);
713 int prettyFlags;
714 char *res;
715
716 prettyFlags = GET_PRETTY_FLAGS(pretty);
717
718 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
719
720 if (res == NULL)
722
724}

References fb(), GET_PRETTY_FLAGS, pg_get_viewdef_worker(), PG_GETARG_BOOL, PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, string_to_text(), and WRAP_COLUMN_DEFAULT.

◆ pg_get_viewdef_name()

Datum pg_get_viewdef_name ( PG_FUNCTION_ARGS  )

Definition at line 747 of file ruleutils.c.

748{
749 /* By qualified name */
751 int prettyFlags;
753 Oid viewoid;
754 char *res;
755
756 prettyFlags = PRETTYFLAG_INDENT;
757
758 /* Look up view name. Can't lock it - we might not have privileges. */
760 viewoid = RangeVarGetRelid(viewrel, NoLock, false);
761
762 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
763
764 if (res == NULL)
766
768}

References fb(), makeRangeVarFromNameList(), NoLock, pg_get_viewdef_worker(), PG_GETARG_TEXT_PP, PG_RETURN_NULL, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, RangeVarGetRelid, string_to_text(), textToQualifiedNameList(), and WRAP_COLUMN_DEFAULT.

◆ pg_get_viewdef_name_ext()

Datum pg_get_viewdef_name_ext ( PG_FUNCTION_ARGS  )

Definition at line 772 of file ruleutils.c.

773{
774 /* By qualified name */
776 bool pretty = PG_GETARG_BOOL(1);
777 int prettyFlags;
779 Oid viewoid;
780 char *res;
781
782 prettyFlags = GET_PRETTY_FLAGS(pretty);
783
784 /* Look up view name. Can't lock it - we might not have privileges. */
786 viewoid = RangeVarGetRelid(viewrel, NoLock, false);
787
788 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
789
790 if (res == NULL)
792
794}

References fb(), GET_PRETTY_FLAGS, makeRangeVarFromNameList(), NoLock, pg_get_viewdef_worker(), PG_GETARG_BOOL, PG_GETARG_TEXT_PP, PG_RETURN_NULL, PG_RETURN_TEXT_P, RangeVarGetRelid, string_to_text(), textToQualifiedNameList(), and WRAP_COLUMN_DEFAULT.

◆ pg_get_viewdef_worker()

static char * pg_get_viewdef_worker ( Oid  viewoid,
int  prettyFlags,
int  wrapColumn 
)
static

Definition at line 800 of file ruleutils.c.

801{
802 Datum args[2];
803 char nulls[2];
804 int spirc;
808
809 /*
810 * Do this first so that string is alloc'd in outer context not SPI's.
811 */
813
814 /*
815 * Connect to SPI manager
816 */
817 SPI_connect();
818
819 /*
820 * On the first call prepare the plan to lookup pg_rewrite. We read
821 * pg_rewrite over the SPI manager instead of using the syscache to be
822 * checked for read access on pg_rewrite.
823 */
824 if (plan_getviewrule == NULL)
825 {
826 Oid argtypes[2];
828
829 argtypes[0] = OIDOID;
830 argtypes[1] = NAMEOID;
831 plan = SPI_prepare(query_getviewrule, 2, argtypes);
832 if (plan == NULL)
833 elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
836 }
837
838 /*
839 * Get the pg_rewrite tuple for the view's SELECT rule
840 */
841 args[0] = ObjectIdGetDatum(viewoid);
843 nulls[0] = ' ';
844 nulls[1] = ' ';
845 spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
846 if (spirc != SPI_OK_SELECT)
847 elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
848 if (SPI_processed != 1)
849 {
850 /*
851 * There is no tuple data available here, just keep the output buffer
852 * empty.
853 */
854 }
855 else
856 {
857 /*
858 * Get the rule's definition and put it into executor's memory
859 */
862 make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
863 }
864
865 /*
866 * Disconnect from SPI manager
867 */
868 if (SPI_finish() != SPI_OK_FINISH)
869 elog(ERROR, "SPI_finish failed");
870
871 if (buf.len == 0)
872 return NULL;
873
874 return buf.data;
875}

References buf, CStringGetDatum(), DirectFunctionCall1, elog, ERROR, fb(), initStringInfo(), make_viewdef(), namein(), ObjectIdGetDatum(), plan, plan_getviewrule, query_getviewrule, SPI_connect(), SPI_execute_plan(), SPI_finish(), SPI_keepplan(), SPI_OK_FINISH, SPI_OK_SELECT, SPI_prepare(), SPI_processed, SPI_tuptable, SPITupleTable::tupdesc, SPITupleTable::vals, and ViewSelectRuleName.

Referenced by pg_get_viewdef(), pg_get_viewdef_ext(), pg_get_viewdef_name(), pg_get_viewdef_name_ext(), and pg_get_viewdef_wrap().

◆ pg_get_viewdef_wrap()

Datum pg_get_viewdef_wrap ( PG_FUNCTION_ARGS  )

Definition at line 727 of file ruleutils.c.

728{
729 /* By OID */
730 Oid viewoid = PG_GETARG_OID(0);
731 int wrap = PG_GETARG_INT32(1);
732 int prettyFlags;
733 char *res;
734
735 /* calling this implies we want pretty printing */
736 prettyFlags = GET_PRETTY_FLAGS(true);
737
738 res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
739
740 if (res == NULL)
742
744}

References fb(), GET_PRETTY_FLAGS, pg_get_viewdef_worker(), PG_GETARG_INT32, PG_GETARG_OID, PG_RETURN_NULL, PG_RETURN_TEXT_P, and string_to_text().

◆ pop_ancestor_plan()

static void pop_ancestor_plan ( deparse_namespace dpns,
deparse_namespace save_dpns 
)
static

Definition at line 5693 of file ruleutils.c.

5694{
5695 /* Free the ancestor list made in push_ancestor_plan */
5696 list_free(dpns->ancestors);
5697
5698 /* Restore fields changed by push_ancestor_plan */
5699 *dpns = *save_dpns;
5700}

References fb(), and list_free().

Referenced by get_name_for_var_field(), and get_parameter().

◆ pop_child_plan()

static void pop_child_plan ( deparse_namespace dpns,
deparse_namespace save_dpns 
)
static

Definition at line 5642 of file ruleutils.c.

5643{
5644 List *ancestors;
5645
5646 /* Get rid of ancestors list cell added by push_child_plan */
5647 ancestors = list_delete_first(dpns->ancestors);
5648
5649 /* Restore fields changed by push_child_plan */
5650 *dpns = *save_dpns;
5651
5652 /* Make sure dpns->ancestors is right (may be unnecessary) */
5653 dpns->ancestors = ancestors;
5654}

References fb(), and list_delete_first().

Referenced by get_name_for_var_field(), get_variable(), and resolve_special_varno().

◆ print_function_arguments()

static int print_function_arguments ( StringInfo  buf,
HeapTuple  proctup,
bool  print_table_args,
bool  print_defaults 
)
static

Definition at line 3660 of file ruleutils.c.

3662{
3664 int numargs;
3665 Oid *argtypes;
3666 char **argnames;
3667 char *argmodes;
3668 int insertorderbyat = -1;
3669 int argsprinted;
3670 int inputargno;
3671 int nlackdefaults;
3672 List *argdefaults = NIL;
3674 int i;
3675
3676 numargs = get_func_arg_info(proctup,
3677 &argtypes, &argnames, &argmodes);
3678
3679 nlackdefaults = numargs;
3680 if (print_defaults && proc->pronargdefaults > 0)
3681 {
3683 bool isnull;
3684
3687 &isnull);
3688 if (!isnull)
3689 {
3690 char *str;
3691
3694 pfree(str);
3696 /* nlackdefaults counts only *input* arguments lacking defaults */
3697 nlackdefaults = proc->pronargs - list_length(argdefaults);
3698 }
3699 }
3700
3701 /* Check for special treatment of ordered-set aggregates */
3702 if (proc->prokind == PROKIND_AGGREGATE)
3703 {
3706
3709 elog(ERROR, "cache lookup failed for aggregate %u",
3710 proc->oid);
3712 if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3713 insertorderbyat = agg->aggnumdirectargs;
3715 }
3716
3717 argsprinted = 0;
3718 inputargno = 0;
3719 for (i = 0; i < numargs; i++)
3720 {
3721 Oid argtype = argtypes[i];
3722 char *argname = argnames ? argnames[i] : NULL;
3724 const char *modename;
3725 bool isinput;
3726
3727 switch (argmode)
3728 {
3729 case PROARGMODE_IN:
3730
3731 /*
3732 * For procedures, explicitly mark all argument modes, so as
3733 * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3734 */
3735 if (proc->prokind == PROKIND_PROCEDURE)
3736 modename = "IN ";
3737 else
3738 modename = "";
3739 isinput = true;
3740 break;
3741 case PROARGMODE_INOUT:
3742 modename = "INOUT ";
3743 isinput = true;
3744 break;
3745 case PROARGMODE_OUT:
3746 modename = "OUT ";
3747 isinput = false;
3748 break;
3750 modename = "VARIADIC ";
3751 isinput = true;
3752 break;
3753 case PROARGMODE_TABLE:
3754 modename = "";
3755 isinput = false;
3756 break;
3757 default:
3758 elog(ERROR, "invalid parameter mode '%c'", argmode);
3759 modename = NULL; /* keep compiler quiet */
3760 isinput = false;
3761 break;
3762 }
3763 if (isinput)
3764 inputargno++; /* this is a 1-based counter */
3765
3767 continue;
3768
3770 {
3771 if (argsprinted)
3773 appendStringInfoString(buf, "ORDER BY ");
3774 }
3775 else if (argsprinted)
3777
3779 if (argname && argname[0])
3780 appendStringInfo(buf, "%s ", quote_identifier(argname));
3783 {
3784 Node *expr;
3785
3787 expr = (Node *) lfirst(nextargdefault);
3789
3790 appendStringInfo(buf, " DEFAULT %s",
3791 deparse_expression(expr, NIL, false, false));
3792 }
3793 argsprinted++;
3794
3795 /* nasty hack: print the last arg twice for variadic ordered-set agg */
3796 if (argsprinted == insertorderbyat && i == numargs - 1)
3797 {
3798 i--;
3799 /* aggs shouldn't have defaults anyway, but just to be sure ... */
3800 print_defaults = false;
3801 }
3802 }
3803
3804 return argsprinted;
3805}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, buf, castNode, deparse_expression(), elog, ERROR, fb(), Form_pg_aggregate, Form_pg_proc, format_type_be(), get_func_arg_info(), GETSTRUCT(), HeapTupleIsValid, i, lfirst, list_head(), list_length(), lnext(), NIL, ObjectIdGetDatum(), pfree(), quote_identifier(), ReleaseSysCache(), SearchSysCache1(), str, stringToNode(), SysCacheGetAttr(), and TextDatumGetCString.

Referenced by pg_get_function_arguments(), pg_get_function_identity_arguments(), pg_get_functiondef(), and print_function_rettype().

◆ print_function_rettype()

static void print_function_rettype ( StringInfo  buf,
HeapTuple  proctup 
)
static

Definition at line 3622 of file ruleutils.c.

3623{
3625 int ntabargs = 0;
3627
3629
3630 if (proc->proretset)
3631 {
3632 /* It might be a table function; try to print the arguments */
3633 appendStringInfoString(&rbuf, "TABLE(");
3635 if (ntabargs > 0)
3637 else
3639 }
3640
3641 if (ntabargs == 0)
3642 {
3643 /* Not a table function, so do the normal thing */
3644 if (proc->proretset)
3645 appendStringInfoString(&rbuf, "SETOF ");
3646 appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3647 }
3648
3649 appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
3650}

References appendBinaryStringInfo(), appendStringInfoChar(), appendStringInfoString(), buf, fb(), Form_pg_proc, format_type_be(), GETSTRUCT(), initStringInfo(), print_function_arguments(), and resetStringInfo().

Referenced by pg_get_function_result(), and pg_get_functiondef().

◆ print_function_sqlbody()

static void print_function_sqlbody ( StringInfo  buf,
HeapTuple  proctup 
)
static

Definition at line 3918 of file ruleutils.c.

3919{
3920 int numargs;
3921 Oid *argtypes;
3922 char **argnames;
3923 char *argmodes;
3924 deparse_namespace dpns = {0};
3925 Datum tmp;
3926 Node *n;
3927
3929 numargs = get_func_arg_info(proctup,
3930 &argtypes, &argnames, &argmodes);
3931 dpns.numargs = numargs;
3932 dpns.argnames = argnames;
3933
3936
3937 if (IsA(n, List))
3938 {
3939 List *stmts;
3940 ListCell *lc;
3941
3942 stmts = linitial(castNode(List, n));
3943
3944 appendStringInfoString(buf, "BEGIN ATOMIC\n");
3945
3946 foreach(lc, stmts)
3947 {
3948 Query *query = lfirst_node(Query, lc);
3949
3950 /* It seems advisable to get at least AccessShareLock on rels */
3951 AcquireRewriteLocks(query, false, false);
3952 get_query_def(query, buf, list_make1(&dpns), NULL, false,
3956 }
3957
3959 }
3960 else
3961 {
3962 Query *query = castNode(Query, n);
3963
3964 /* It seems advisable to get at least AccessShareLock on rels */
3965 AcquireRewriteLocks(query, false, false);
3966 get_query_def(query, buf, list_make1(&dpns), NULL, false,
3967 0, WRAP_COLUMN_DEFAULT, 0);
3968 }
3969}

References AcquireRewriteLocks(), appendStringInfoChar(), appendStringInfoString(), buf, castNode, fb(), Form_pg_proc, get_func_arg_info(), get_query_def(), GETSTRUCT(), IsA, lfirst_node, linitial, list_make1, NameStr, PRETTYFLAG_INDENT, proname, pstrdup(), stringToNode(), SysCacheGetAttrNotNull(), TextDatumGetCString, and WRAP_COLUMN_DEFAULT.

Referenced by pg_get_function_sqlbody(), and pg_get_functiondef().

◆ print_function_trftypes()

static void print_function_trftypes ( StringInfo  buf,
HeapTuple  proctup 
)
static

Definition at line 3820 of file ruleutils.c.

3821{
3822 Oid *trftypes;
3823 int ntypes;
3824
3825 ntypes = get_func_trftypes(proctup, &trftypes);
3826 if (ntypes > 0)
3827 {
3828 int i;
3829
3830 appendStringInfoString(buf, " TRANSFORM ");
3831 for (i = 0; i < ntypes; i++)
3832 {
3833 if (i != 0)
3835 appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3836 }
3838 }
3839}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), buf, fb(), format_type_be(), get_func_trftypes(), and i.

Referenced by pg_get_functiondef().

◆ printSubscripts()

static void printSubscripts ( SubscriptingRef sbsref,
deparse_context context 
)
static

Definition at line 13661 of file ruleutils.c.

13662{
13663 StringInfo buf = context->buf;
13666
13667 lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
13668 foreach(uplist_item, sbsref->refupperindexpr)
13669 {
13671 if (lowlist_item)
13672 {
13673 /* If subexpression is NULL, get_rule_expr prints nothing */
13674 get_rule_expr((Node *) lfirst(lowlist_item), context, false);
13677 }
13678 /* If subexpression is NULL, get_rule_expr prints nothing */
13679 get_rule_expr((Node *) lfirst(uplist_item), context, false);
13681 }
13682}

References appendStringInfoChar(), deparse_context::buf, buf, fb(), get_rule_expr(), lfirst, list_head(), lnext(), SubscriptingRef::reflowerindexpr, and SubscriptingRef::refupperindexpr.

Referenced by get_rule_expr(), and processIndirection().

◆ processIndirection()

static Node * processIndirection ( Node node,
deparse_context context 
)
static

Definition at line 13583 of file ruleutils.c.

13584{
13585 StringInfo buf = context->buf;
13587
13588 for (;;)
13589 {
13590 if (node == NULL)
13591 break;
13592 if (IsA(node, FieldStore))
13593 {
13594 FieldStore *fstore = (FieldStore *) node;
13595 Oid typrelid;
13596 char *fieldname;
13597
13598 /* lookup tuple type */
13599 typrelid = get_typ_typrelid(fstore->resulttype);
13600 if (!OidIsValid(typrelid))
13601 elog(ERROR, "argument type %s of FieldStore is not a tuple type",
13602 format_type_be(fstore->resulttype));
13603
13604 /*
13605 * Print the field name. There should only be one target field in
13606 * stored rules. There could be more than that in executable
13607 * target lists, but this function cannot be used for that case.
13608 */
13609 Assert(list_length(fstore->fieldnums) == 1);
13610 fieldname = get_attname(typrelid,
13611 linitial_int(fstore->fieldnums), false);
13612 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
13613
13614 /*
13615 * We ignore arg since it should be an uninteresting reference to
13616 * the target column or subcolumn.
13617 */
13618 node = (Node *) linitial(fstore->newvals);
13619 }
13620 else if (IsA(node, SubscriptingRef))
13621 {
13622 SubscriptingRef *sbsref = (SubscriptingRef *) node;
13623
13624 if (sbsref->refassgnexpr == NULL)
13625 break;
13626
13627 printSubscripts(sbsref, context);
13628
13629 /*
13630 * We ignore refexpr since it should be an uninteresting reference
13631 * to the target column or subcolumn.
13632 */
13633 node = (Node *) sbsref->refassgnexpr;
13634 }
13635 else if (IsA(node, CoerceToDomain))
13636 {
13637 cdomain = (CoerceToDomain *) node;
13638 /* If it's an explicit domain coercion, we're done */
13639 if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
13640 break;
13641 /* Tentatively descend past the CoerceToDomain */
13642 node = (Node *) cdomain->arg;
13643 }
13644 else
13645 break;
13646 }
13647
13648 /*
13649 * If we descended past a CoerceToDomain whose argument turned out not to
13650 * be a FieldStore or array assignment, back up to the CoerceToDomain.
13651 * (This is not enough to be fully correct if there are nested implicit
13652 * CoerceToDomains, but such cases shouldn't ever occur.)
13653 */
13654 if (cdomain && node == (Node *) cdomain->arg)
13655 node = (Node *) cdomain;
13656
13657 return node;
13658}

References appendStringInfo(), Assert, deparse_context::buf, buf, COERCE_IMPLICIT_CAST, elog, ERROR, fb(), format_type_be(), get_attname(), get_typ_typrelid(), IsA, linitial, linitial_int, list_length(), FieldStore::newvals, OidIsValid, printSubscripts(), quote_identifier(), and SubscriptingRef::refassgnexpr.

Referenced by get_insert_query_def(), get_merge_query_def(), get_rule_expr(), and get_update_query_targetlist_def().

◆ propdata_by_name_cmp()

static int propdata_by_name_cmp ( const ListCell a,
const ListCell b 
)
static

Definition at line 1865 of file ruleutils.c.

1866{
1867 List *la = lfirst_node(List, a);
1868 List *lb = lfirst_node(List, b);
1869 char *pna = strVal(linitial(la));
1870 char *pnb = strVal(linitial(lb));
1871
1872 return strcmp(pna, pnb);
1873}

References a, b, fb(), lfirst_node, linitial, and strVal.

Referenced by make_propgraphdef_properties().

◆ push_ancestor_plan()

static void push_ancestor_plan ( deparse_namespace dpns,
ListCell ancestor_cell,
deparse_namespace save_dpns 
)
static

Definition at line 5672 of file ruleutils.c.

5674{
5676
5677 /* Save state for restoration later */
5678 *save_dpns = *dpns;
5679
5680 /* Build a new ancestor list with just this node's ancestors */
5681 dpns->ancestors =
5682 list_copy_tail(dpns->ancestors,
5683 list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5684
5685 /* Set attention on selected ancestor */
5687}

References fb(), lfirst, list_cell_number(), list_copy_tail(), plan, and set_deparse_plan().

Referenced by get_name_for_var_field(), and get_parameter().

◆ push_child_plan()

static void push_child_plan ( deparse_namespace dpns,
Plan plan,
deparse_namespace save_dpns 
)
static

Definition at line 5625 of file ruleutils.c.

5627{
5628 /* Save state for restoration later */
5629 *save_dpns = *dpns;
5630
5631 /* Link current plan node into ancestors list */
5632 dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5633
5634 /* Set attention on selected child */
5636}

References fb(), lcons(), plan, and set_deparse_plan().

Referenced by get_name_for_var_field(), get_variable(), and resolve_special_varno().

◆ quote_identifier()

const char * quote_identifier ( const char ident)

Definition at line 13691 of file ruleutils.c.

13692{
13693 /*
13694 * Can avoid quoting if ident starts with a lowercase letter or underscore
13695 * and contains only lowercase letters, digits, and underscores, *and* is
13696 * not any SQL keyword. Otherwise, supply quotes.
13697 */
13698 int nquotes = 0;
13699 bool safe;
13700 const char *ptr;
13701 char *result;
13702 char *optr;
13703
13704 /*
13705 * would like to use <ctype.h> macros here, but they might yield unwanted
13706 * locale-specific results...
13707 */
13708 safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
13709
13710 for (ptr = ident; *ptr; ptr++)
13711 {
13712 char ch = *ptr;
13713
13714 if ((ch >= 'a' && ch <= 'z') ||
13715 (ch >= '0' && ch <= '9') ||
13716 (ch == '_'))
13717 {
13718 /* okay */
13719 }
13720 else
13721 {
13722 safe = false;
13723 if (ch == '"')
13724 nquotes++;
13725 }
13726 }
13727
13729 safe = false;
13730
13731 if (safe)
13732 {
13733 /*
13734 * Check for keyword. We quote keywords except for unreserved ones.
13735 * (In some cases we could avoid quoting a col_name or type_func_name
13736 * keyword, but it seems much harder than it's worth to tell that.)
13737 *
13738 * Note: ScanKeywordLookup() does case-insensitive comparison, but
13739 * that's fine, since we already know we have all-lower-case.
13740 */
13742
13744 safe = false;
13745 }
13746
13747 if (safe)
13748 return ident; /* no change needed */
13749
13750 result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13751
13752 optr = result;
13753 *optr++ = '"';
13754 for (ptr = ident; *ptr; ptr++)
13755 {
13756 char ch = *ptr;
13757
13758 if (ch == '"')
13759 *optr++ = '"';
13760 *optr++ = ch;
13761 }
13762 *optr++ = '"';
13763 *optr = '\0';
13764
13765 return result;
13766}

References fb(), ident, palloc(), quote_all_identifiers, result, ScanKeywordCategories, ScanKeywordLookup(), ScanKeywords, and UNRESERVED_KEYWORD.

Referenced by add_cast_to(), appendFunctionName(), ATPrepAlterColumnType(), CheckMyDatabase(), copy_table(), createdb(), CreateSchemaCommand(), decompile_column_index_array(), deparseAnalyzeSql(), deparseColumnRef(), deparseOperatorName(), deparseRelation(), execute_extension_script(), ExplainIndexScanDetails(), ExplainNode(), ExplainTargetRel(), flatten_set_variable_args(), format_operator_extended(), generate_operator_clause(), generate_operator_name(), get_column_alias_list(), get_for_portion_of(), get_from_clause_coldeflist(), get_from_clause_item(), get_graph_label_expr(), get_insert_query_def(), get_json_table(), get_json_table_columns(), get_json_table_nested_columns(), get_merge_query_def(), get_opclass_name(), get_parameter(), get_path_pattern_expr_def(), get_reloptions(), get_returning_clause(), get_rte_alias(), get_rule_expr(), get_rule_windowclause(), get_rule_windowspec(), get_select_query_def(), get_target_list(), get_update_query_targetlist_def(), get_utility_query_def(), get_variable(), get_windowfunc_expr_helper(), get_with_clause(), get_xmltable(), getObjectIdentityParts(), libpqrcv_alter_slot(), make_propgraphdef_elements(), make_propgraphdef_labels(), make_propgraphdef_properties(), make_ruledef(), NameListToQuotedString(), old_9_6_invalidate_hash_indexes(), overexplain_alias(), overexplain_range_table(), pg_get_constraintdef_worker(), pg_get_database_ddl_internal(), pg_get_functiondef(), pg_get_indexdef_worker(), pg_get_partkeydef_worker(), pg_get_role_ddl_internal(), pg_get_statisticsobj_worker(), pg_get_tablespace_ddl_internal(), pg_get_triggerdef_worker(), pg_identify_object(), pgpa_format_index_target(), pgpa_identifier_string(), pgpa_output_relation_name(), PLy_quote_ident(), postgresExplainForeignScan(), postgresImportForeignSchema(), print_function_arguments(), process_extension_updates(), processIndirection(), quote_ident(), quote_object_name(), quote_qualified_identifier(), regdatabaseout(), regnamespaceout(), regoperout(), regroleout(), ReplicationSlotDropAtPubNode(), sepgsql_attribute_post_create(), sepgsql_database_post_create(), sepgsql_relation_post_create(), sepgsql_schema_post_create(), serialize_deflist(), set_frozenxids(), show_sortorder_options(), show_window_def(), test_inline_in_from_support_func(), text_format_string_conversion(), tuple_to_stringinfo(), and worker_spi_main().

◆ quote_qualified_identifier()

◆ removeStringInfoSpaces()

static void removeStringInfoSpaces ( StringInfo  str)
static

Definition at line 9701 of file ruleutils.c.

9702{
9703 while (str->len > 0 && str->data[str->len - 1] == ' ')
9704 str->data[--(str->len)] = '\0';
9705}

References str.

Referenced by appendContextKeyword(), get_from_clause(), and get_target_list().

◆ resolve_special_varno()

static void resolve_special_varno ( Node node,
deparse_context context,
rsv_callback  callback,
void callback_arg 
)
static

Definition at line 8465 of file ruleutils.c.

8467{
8468 Var *var;
8470
8471 /* This function is recursive, so let's be paranoid. */
8473
8474 /* If it's not a Var, invoke the callback. */
8475 if (!IsA(node, Var))
8476 {
8477 (*callback) (node, context, callback_arg);
8478 return;
8479 }
8480
8481 /* Find appropriate nesting depth */
8482 var = (Var *) node;
8484 var->varlevelsup);
8485
8486 /*
8487 * If varno is special, recurse. (Don't worry about varnosyn; if we're
8488 * here, we already decided not to use that.)
8489 */
8490 if (var->varno == OUTER_VAR && dpns->outer_tlist)
8491 {
8495
8496 tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
8497 if (!tle)
8498 elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
8499
8500 /*
8501 * If we're descending to the first child of an Append or MergeAppend,
8502 * update appendparents. This will affect deparsing of all Vars
8503 * appearing within the eventually-resolved subexpression.
8504 */
8506
8507 if (IsA(dpns->plan, Append))
8508 context->appendparents = bms_union(context->appendparents,
8509 ((Append *) dpns->plan)->apprelids);
8510 else if (IsA(dpns->plan, MergeAppend))
8511 context->appendparents = bms_union(context->appendparents,
8512 ((MergeAppend *) dpns->plan)->apprelids);
8513
8514 push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8515 resolve_special_varno((Node *) tle->expr, context,
8516 callback, callback_arg);
8519 return;
8520 }
8521 else if (var->varno == INNER_VAR && dpns->inner_tlist)
8522 {
8525
8526 tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
8527 if (!tle)
8528 elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
8529
8530 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8531 resolve_special_varno((Node *) tle->expr, context,
8532 callback, callback_arg);
8534 return;
8535 }
8536 else if (var->varno == INDEX_VAR && dpns->index_tlist)
8537 {
8539
8540 tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
8541 if (!tle)
8542 elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
8543
8544 resolve_special_varno((Node *) tle->expr, context,
8545 callback, callback_arg);
8546 return;
8547 }
8548 else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
8549 elog(ERROR, "bogus varno: %d", var->varno);
8550
8551 /* Not special. Just invoke the callback. */
8552 (*callback) (node, context, callback_arg);
8553}

References deparse_context::appendparents, bms_union(), callback(), check_stack_depth(), elog, ERROR, fb(), get_tle_by_resno(), INDEX_VAR, INNER_VAR, IsA, list_length(), list_nth(), deparse_context::namespaces, OUTER_VAR, pop_child_plan(), push_child_plan(), resolve_special_varno(), Var::varattno, Var::varlevelsup, and Var::varno.

Referenced by get_agg_expr_helper(), get_variable(), and resolve_special_varno().

◆ select_rtable_names_for_explain()

List * select_rtable_names_for_explain ( List rtable,
Bitmapset rels_used 
)

Definition at line 4217 of file ruleutils.c.

4218{
4220
4221 memset(&dpns, 0, sizeof(dpns));
4222 dpns.rtable = rtable;
4223 dpns.subplans = NIL;
4224 dpns.ctes = NIL;
4225 dpns.appendrels = NULL;
4227 /* We needn't bother computing column aliases yet */
4228
4229 return dpns.rtable_names;
4230}

References fb(), NIL, and set_rtable_names().

Referenced by ExplainPrintPlan().

◆ set_deparse_context_plan()

List * set_deparse_context_plan ( List dpcontext,
Plan plan,
List ancestors 
)

Definition at line 4187 of file ruleutils.c.

4188{
4190
4191 /* Should always have one-entry namespace list for Plan deparsing */
4194
4195 /* Set our attention on the specific plan node passed in */
4196 dpns->ancestors = ancestors;
4198
4199 /* For ModifyTable, set aliases for OLD and NEW in RETURNING */
4200 if (IsA(plan, ModifyTable))
4201 {
4202 dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias;
4203 dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias;
4204 }
4205
4206 return dpcontext;
4207}

References Assert, fb(), IsA, linitial, list_length(), plan, and set_deparse_plan().

Referenced by show_expression(), show_grouping_sets(), show_memoize_info(), show_plan_tlist(), show_sort_group_keys(), show_tablesample(), show_window_def(), and show_window_keys().

◆ set_deparse_for_query()

static void set_deparse_for_query ( deparse_namespace dpns,
Query query,
List parent_namespaces 
)
static

Definition at line 4391 of file ruleutils.c.

4393{
4394 ListCell *lc;
4395 ListCell *lc2;
4396
4397 /* Initialize *dpns and fill rtable/ctes links */
4398 memset(dpns, 0, sizeof(deparse_namespace));
4399 dpns->rtable = query->rtable;
4400 dpns->subplans = NIL;
4401 dpns->ctes = query->cteList;
4402 dpns->appendrels = NULL;
4403 dpns->ret_old_alias = query->returningOldAlias;
4404 dpns->ret_new_alias = query->returningNewAlias;
4405
4406 /* Assign a unique relation alias to each RTE */
4408
4409 /* Initialize dpns->rtable_columns to contain zeroed structs */
4410 dpns->rtable_columns = NIL;
4411 while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4412 dpns->rtable_columns = lappend(dpns->rtable_columns,
4413 palloc0(sizeof(deparse_columns)));
4414
4415 /* If it's a utility query, it won't have a jointree */
4416 if (query->jointree)
4417 {
4418 /* Detect whether global uniqueness of USING names is needed */
4419 dpns->unique_using =
4421
4422 /*
4423 * Select names for columns merged by USING, via a recursive pass over
4424 * the query jointree.
4425 */
4426 set_using_names(dpns, (Node *) query->jointree, NIL);
4427 }
4428
4429 /*
4430 * Now assign remaining column aliases for each RTE. We do this in a
4431 * linear scan of the rtable, so as to process RTEs whether or not they
4432 * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4433 * etc). JOIN RTEs must be processed after their children, but this is
4434 * okay because they appear later in the rtable list than their children
4435 * (cf Asserts in identify_join_columns()).
4436 */
4437 forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4438 {
4441
4442 if (rte->rtekind == RTE_JOIN)
4444 else
4446 }
4447}

References Query::cteList, fb(), forboth, has_dangerous_join_using(), Query::jointree, lappend(), lfirst, list_length(), NIL, palloc0(), Query::rtable, RTE_JOIN, set_join_column_names(), set_relation_column_names(), set_rtable_names(), and set_using_names().

Referenced by get_name_for_var_field(), get_query_def(), and make_ruledef().

◆ set_deparse_plan()

static void set_deparse_plan ( deparse_namespace dpns,
Plan plan 
)
static

Definition at line 5514 of file ruleutils.c.

5515{
5516 dpns->plan = plan;
5517
5518 /*
5519 * We special-case Append and MergeAppend to pretend that the first child
5520 * plan is the OUTER referent; we have to interpret OUTER Vars in their
5521 * tlists according to one of the children, and the first one is the most
5522 * natural choice.
5523 */
5524 if (IsA(plan, Append))
5525 dpns->outer_plan = linitial(((Append *) plan)->appendplans);
5526 else if (IsA(plan, MergeAppend))
5527 dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
5528 else
5529 dpns->outer_plan = outerPlan(plan);
5530
5531 if (dpns->outer_plan)
5532 dpns->outer_tlist = dpns->outer_plan->targetlist;
5533 else
5534 dpns->outer_tlist = NIL;
5535
5536 /*
5537 * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
5538 * use OUTER because that could someday conflict with the normal meaning.)
5539 * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
5540 * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
5541 * that as INNER referent.
5542 *
5543 * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
5544 * INNER referent. This is the join from the target relation to the data
5545 * source, and all INNER_VAR Vars in other parts of the query refer to its
5546 * targetlist.
5547 *
5548 * For ON CONFLICT DO SELECT/UPDATE we just need the inner tlist to point
5549 * to the excluded expression's tlist. (Similar to the SubqueryScan we
5550 * don't want to reuse OUTER, it's used for RETURNING in some modify table
5551 * cases, although not INSERT .. CONFLICT).
5552 */
5553 if (IsA(plan, SubqueryScan))
5554 dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5555 else if (IsA(plan, CteScan))
5556 dpns->inner_plan = list_nth(dpns->subplans,
5557 ((CteScan *) plan)->ctePlanId - 1);
5558 else if (IsA(plan, WorkTableScan))
5559 dpns->inner_plan = find_recursive_union(dpns,
5560 (WorkTableScan *) plan);
5561 else if (IsA(plan, ModifyTable))
5562 {
5563 if (((ModifyTable *) plan)->operation == CMD_MERGE)
5564 dpns->inner_plan = outerPlan(plan);
5565 else
5566 dpns->inner_plan = plan;
5567 }
5568 else
5569 dpns->inner_plan = innerPlan(plan);
5570
5572 dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
5573 else if (dpns->inner_plan)
5574 dpns->inner_tlist = dpns->inner_plan->targetlist;
5575 else
5576 dpns->inner_tlist = NIL;
5577
5578 /* Set up referent for INDEX_VAR Vars, if needed */
5579 if (IsA(plan, IndexOnlyScan))
5580 dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5581 else if (IsA(plan, ForeignScan))
5582 dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5583 else if (IsA(plan, CustomScan))
5584 dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5585 else
5586 dpns->index_tlist = NIL;
5587}

References CMD_INSERT, CMD_MERGE, fb(), find_recursive_union(), innerPlan, IsA, linitial, list_nth(), NIL, operation, outerPlan, and plan.

Referenced by push_ancestor_plan(), push_child_plan(), and set_deparse_context_plan().

◆ set_join_column_names()

static void set_join_column_names ( deparse_namespace dpns,
RangeTblEntry rte,
deparse_columns colinfo 
)
static

Definition at line 4940 of file ruleutils.c.

4942{
4945 bool changed_any;
4946 int noldcolumns;
4947 int nnewcolumns;
4950 int i;
4951 int j;
4952 int ic;
4953 int jc;
4954
4955 /* Look up the previously-filled-in child deparse_columns structs */
4958
4959 /*
4960 * Ensure colinfo->colnames has a slot for each column. (It could be long
4961 * enough already, if we pushed down a name for the last column.) Note:
4962 * it's possible that one or both inputs now have more columns than there
4963 * were when the query was parsed, but we'll deal with that below. We
4964 * only need entries in colnames for pre-existing columns.
4965 */
4966 noldcolumns = list_length(rte->eref->colnames);
4968 Assert(colinfo->num_cols == noldcolumns);
4969
4970 /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4972
4973 /*
4974 * Scan the join output columns, select an alias for each one, and store
4975 * it in colinfo->colnames. If there are USING columns, set_using_names()
4976 * already selected their names, so we can start the loop at the first
4977 * non-merged column.
4978 */
4979 changed_any = false;
4980 for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4981 {
4982 char *colname = colinfo->colnames[i];
4983 char *real_colname;
4984
4985 /* Join column must refer to at least one input column */
4986 Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4987
4988 /* Get the child column name */
4989 if (colinfo->leftattnos[i] > 0)
4990 real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4991 else if (colinfo->rightattnos[i] > 0)
4992 real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4993 else
4994 {
4995 /* We're joining system columns --- use eref name */
4996 real_colname = strVal(list_nth(rte->eref->colnames, i));
4997 }
4998
4999 /* If child col has been dropped, no need to assign a join colname */
5000 if (real_colname == NULL)
5001 {
5002 colinfo->colnames[i] = NULL;
5003 continue;
5004 }
5005
5006 /* In an unnamed join, just report child column names as-is */
5007 if (rte->alias == NULL)
5008 {
5009 colinfo->colnames[i] = real_colname;
5011 continue;
5012 }
5013
5014 /* If alias already assigned, that's what to use */
5015 if (colname == NULL)
5016 {
5017 /* If user wrote an alias, prefer that over real column name */
5018 if (rte->alias && i < list_length(rte->alias->colnames))
5019 colname = strVal(list_nth(rte->alias->colnames, i));
5020 else
5021 colname = real_colname;
5022
5023 /* Unique-ify and insert into colinfo */
5024 colname = make_colname_unique(colname, dpns, colinfo);
5025
5026 colinfo->colnames[i] = colname;
5027 add_to_names_hash(colinfo, colname);
5028 }
5029
5030 /* Remember if any assigned aliases differ from "real" name */
5031 if (!changed_any && strcmp(colname, real_colname) != 0)
5032 changed_any = true;
5033 }
5034
5035 /*
5036 * Calculate number of columns the join would have if it were re-parsed
5037 * now, and create storage for the new_colnames and is_new_col arrays.
5038 *
5039 * Note: colname_is_unique will be consulting new_colnames[] during the
5040 * loops below, so its not-yet-filled entries must be zeroes.
5041 */
5042 nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
5043 list_length(colinfo->usingNames);
5044 colinfo->num_new_cols = nnewcolumns;
5045 colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
5046 colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
5047
5048 /*
5049 * Generating the new_colnames array is a bit tricky since any new columns
5050 * added since parse time must be inserted in the right places. This code
5051 * must match the parser, which will order a join's columns as merged
5052 * columns first (in USING-clause order), then non-merged columns from the
5053 * left input (in attnum order), then non-merged columns from the right
5054 * input (ditto). If one of the inputs is itself a join, its columns will
5055 * be ordered according to the same rule, which means newly-added columns
5056 * might not be at the end. We can figure out what's what by consulting
5057 * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
5058 *
5059 * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
5060 * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
5061 * meanings for the current child RTE.
5062 */
5063
5064 /* Handle merged columns; they are first and can't be new */
5065 i = j = 0;
5066 while (i < noldcolumns &&
5067 colinfo->leftattnos[i] != 0 &&
5068 colinfo->rightattnos[i] != 0)
5069 {
5070 /* column name is already determined and known unique */
5071 colinfo->new_colnames[j] = colinfo->colnames[i];
5072 colinfo->is_new_col[j] = false;
5073
5074 /* build bitmapsets of child attnums of merged columns */
5075 if (colinfo->leftattnos[i] > 0)
5076 leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
5077 if (colinfo->rightattnos[i] > 0)
5079
5080 i++, j++;
5081 }
5082
5083 /* Handle non-merged left-child columns */
5084 ic = 0;
5085 for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
5086 {
5087 char *child_colname = leftcolinfo->new_colnames[jc];
5088
5089 if (!leftcolinfo->is_new_col[jc])
5090 {
5091 /* Advance ic to next non-dropped old column of left child */
5092 while (ic < leftcolinfo->num_cols &&
5093 leftcolinfo->colnames[ic] == NULL)
5094 ic++;
5095 Assert(ic < leftcolinfo->num_cols);
5096 ic++;
5097 /* If it is a merged column, we already processed it */
5099 continue;
5100 /* Else, advance i to the corresponding existing join column */
5101 while (i < colinfo->num_cols &&
5102 colinfo->colnames[i] == NULL)
5103 i++;
5104 Assert(i < colinfo->num_cols);
5105 Assert(ic == colinfo->leftattnos[i]);
5106 /* Use the already-assigned name of this column */
5107 colinfo->new_colnames[j] = colinfo->colnames[i];
5108 i++;
5109 }
5110 else
5111 {
5112 /*
5113 * Unique-ify the new child column name and assign, unless we're
5114 * in an unnamed join, in which case just copy
5115 */
5116 if (rte->alias != NULL)
5117 {
5118 colinfo->new_colnames[j] =
5120 if (!changed_any &&
5121 strcmp(colinfo->new_colnames[j], child_colname) != 0)
5122 changed_any = true;
5123 }
5124 else
5125 colinfo->new_colnames[j] = child_colname;
5126 add_to_names_hash(colinfo, colinfo->new_colnames[j]);
5127 }
5128
5129 colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
5130 j++;
5131 }
5132
5133 /* Handle non-merged right-child columns in exactly the same way */
5134 ic = 0;
5135 for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
5136 {
5137 char *child_colname = rightcolinfo->new_colnames[jc];
5138
5139 if (!rightcolinfo->is_new_col[jc])
5140 {
5141 /* Advance ic to next non-dropped old column of right child */
5142 while (ic < rightcolinfo->num_cols &&
5143 rightcolinfo->colnames[ic] == NULL)
5144 ic++;
5145 Assert(ic < rightcolinfo->num_cols);
5146 ic++;
5147 /* If it is a merged column, we already processed it */
5149 continue;
5150 /* Else, advance i to the corresponding existing join column */
5151 while (i < colinfo->num_cols &&
5152 colinfo->colnames[i] == NULL)
5153 i++;
5154 Assert(i < colinfo->num_cols);
5155 Assert(ic == colinfo->rightattnos[i]);
5156 /* Use the already-assigned name of this column */
5157 colinfo->new_colnames[j] = colinfo->colnames[i];
5158 i++;
5159 }
5160 else
5161 {
5162 /*
5163 * Unique-ify the new child column name and assign, unless we're
5164 * in an unnamed join, in which case just copy
5165 */
5166 if (rte->alias != NULL)
5167 {
5168 colinfo->new_colnames[j] =
5170 if (!changed_any &&
5171 strcmp(colinfo->new_colnames[j], child_colname) != 0)
5172 changed_any = true;
5173 }
5174 else
5175 colinfo->new_colnames[j] = child_colname;
5176 add_to_names_hash(colinfo, colinfo->new_colnames[j]);
5177 }
5178
5179 colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
5180 j++;
5181 }
5182
5183 /* Assert we processed the right number of columns */
5184#ifdef USE_ASSERT_CHECKING
5185 while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
5186 i++;
5187 Assert(i == colinfo->num_cols);
5188 Assert(j == nnewcolumns);
5189#endif
5190
5191 /* We're now done needing the colinfo's names_hash */
5193
5194 /*
5195 * For a named join, print column aliases if we changed any from the child
5196 * names. Unnamed joins cannot print aliases.
5197 */
5198 if (rte->alias != NULL)
5199 colinfo->printaliases = changed_any;
5200 else
5201 colinfo->printaliases = false;
5202}

References add_to_names_hash(), Assert, bms_add_member(), bms_is_member(), build_colinfo_names_hash(), deparse_columns_fetch, destroy_colinfo_names_hash(), expand_colnames_array_to(), fb(), i, j, list_length(), list_nth(), make_colname_unique(), palloc0(), and strVal.

Referenced by set_deparse_for_query().

◆ set_relation_column_names()

static void set_relation_column_names ( deparse_namespace dpns,
RangeTblEntry rte,
deparse_columns colinfo 
)
static

Definition at line 4737 of file ruleutils.c.

4739{
4740 int ncolumns;
4741 char **real_colnames;
4742 bool changed_any;
4743 int noldcolumns;
4744 int i;
4745 int j;
4746
4747 /*
4748 * Construct an array of the current "real" column names of the RTE.
4749 * real_colnames[] will be indexed by physical column number, with NULL
4750 * entries for dropped columns.
4751 */
4752 if (rte->rtekind == RTE_RELATION)
4753 {
4754 /* Relation --- look to the system catalogs for up-to-date info */
4755 Relation rel;
4756 TupleDesc tupdesc;
4757
4758 rel = relation_open(rte->relid, AccessShareLock);
4759 tupdesc = RelationGetDescr(rel);
4760
4761 ncolumns = tupdesc->natts;
4762 real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4763
4764 for (i = 0; i < ncolumns; i++)
4765 {
4766 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4767
4768 if (attr->attisdropped)
4769 real_colnames[i] = NULL;
4770 else
4771 real_colnames[i] = pstrdup(NameStr(attr->attname));
4772 }
4774 }
4775 else
4776 {
4777 /* Otherwise get the column names from eref or expandRTE() */
4778 List *colnames;
4779 ListCell *lc;
4780
4781 /*
4782 * Functions returning composites have the annoying property that some
4783 * of the composite type's columns might have been dropped since the
4784 * query was parsed. If possible, use expandRTE() to handle that
4785 * case, since it has the tedious logic needed to find out about
4786 * dropped columns. However, if we're explaining a plan, then we
4787 * don't have rte->functions because the planner thinks that won't be
4788 * needed later, and that breaks expandRTE(). So in that case we have
4789 * to rely on rte->eref, which may lead us to report a dropped
4790 * column's old name; that seems close enough for EXPLAIN's purposes.
4791 *
4792 * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4793 * which should be sufficiently up-to-date: no other RTE types can
4794 * have columns get dropped from under them after parsing.
4795 */
4796 if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4797 {
4798 /* Since we're not creating Vars, rtindex etc. don't matter */
4800 true /* include dropped */ , &colnames, NULL);
4801 }
4802 else
4803 colnames = rte->eref->colnames;
4804
4805 ncolumns = list_length(colnames);
4806 real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4807
4808 i = 0;
4809 foreach(lc, colnames)
4810 {
4811 /*
4812 * If the column name we find here is an empty string, then it's a
4813 * dropped column, so change to NULL.
4814 */
4815 char *cname = strVal(lfirst(lc));
4816
4817 if (cname[0] == '\0')
4818 cname = NULL;
4820 i++;
4821 }
4822 }
4823
4824 /*
4825 * Ensure colinfo->colnames has a slot for each column. (It could be long
4826 * enough already, if we pushed down a name for the last column.) Note:
4827 * it's possible that there are now more columns than there were when the
4828 * query was parsed, ie colnames could be longer than rte->eref->colnames.
4829 * We must assign unique aliases to the new columns too, else there could
4830 * be unresolved conflicts when the view/rule is reloaded.
4831 */
4833 Assert(colinfo->num_cols == ncolumns);
4834
4835 /*
4836 * Make sufficiently large new_colnames and is_new_col arrays, too.
4837 *
4838 * Note: because we leave colinfo->num_new_cols zero until after the loop,
4839 * colname_is_unique will not consult that array, which is fine because it
4840 * would only be duplicate effort.
4841 */
4842 colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4843 colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4844
4845 /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4847
4848 /*
4849 * Scan the columns, select a unique alias for each one, and store it in
4850 * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4851 * entries for dropped columns, the latter omits them. Also mark
4852 * new_colnames entries as to whether they are new since parse time; this
4853 * is the case for entries beyond the length of rte->eref->colnames.
4854 */
4855 noldcolumns = list_length(rte->eref->colnames);
4856 changed_any = false;
4857 j = 0;
4858 for (i = 0; i < ncolumns; i++)
4859 {
4860 char *real_colname = real_colnames[i];
4861 char *colname = colinfo->colnames[i];
4862
4863 /* Skip dropped columns */
4864 if (real_colname == NULL)
4865 {
4866 Assert(colname == NULL); /* colnames[i] is already NULL */
4867 continue;
4868 }
4869
4870 /* If alias already assigned, that's what to use */
4871 if (colname == NULL)
4872 {
4873 /* If user wrote an alias, prefer that over real column name */
4874 if (rte->alias && i < list_length(rte->alias->colnames))
4875 colname = strVal(list_nth(rte->alias->colnames, i));
4876 else
4877 colname = real_colname;
4878
4879 /* Unique-ify and insert into colinfo */
4880 colname = make_colname_unique(colname, dpns, colinfo);
4881
4882 colinfo->colnames[i] = colname;
4883 add_to_names_hash(colinfo, colname);
4884 }
4885
4886 /* Put names of non-dropped columns in new_colnames[] too */
4887 colinfo->new_colnames[j] = colname;
4888 /* And mark them as new or not */
4889 colinfo->is_new_col[j] = (i >= noldcolumns);
4890 j++;
4891
4892 /* Remember if any assigned aliases differ from "real" name */
4893 if (!changed_any && strcmp(colname, real_colname) != 0)
4894 changed_any = true;
4895 }
4896
4897 /* We're now done needing the colinfo's names_hash */
4899
4900 /*
4901 * Set correct length for new_colnames[] array. (Note: if columns have
4902 * been added, colinfo->num_cols includes them, which is not really quite
4903 * right but is harmless, since any new columns must be at the end where
4904 * they won't affect varattnos of pre-existing columns.)
4905 */
4906 colinfo->num_new_cols = j;
4907
4908 /*
4909 * For a relation RTE, we need only print the alias column names if any
4910 * are different from the underlying "real" names. For a function RTE,
4911 * always emit a complete column alias list; this is to protect against
4912 * possible instability of the default column names (eg, from altering
4913 * parameter names). For tablefunc RTEs, we never print aliases, because
4914 * the column names are part of the clause itself. For other RTE types,
4915 * print if we changed anything OR if there were user-written column
4916 * aliases (since the latter would be part of the underlying "reality").
4917 */
4918 if (rte->rtekind == RTE_RELATION)
4919 colinfo->printaliases = changed_any;
4920 else if (rte->rtekind == RTE_FUNCTION)
4921 colinfo->printaliases = true;
4922 else if (rte->rtekind == RTE_TABLEFUNC)
4923 colinfo->printaliases = false;
4924 else if (rte->alias && rte->alias->colnames != NIL)
4925 colinfo->printaliases = true;
4926 else
4927 colinfo->printaliases = changed_any;
4928}

References AccessShareLock, add_to_names_hash(), Assert, build_colinfo_names_hash(), destroy_colinfo_names_hash(), expand_colnames_array_to(), expandRTE(), fb(), i, j, lfirst, list_length(), list_nth(), make_colname_unique(), NameStr, TupleDescData::natts, NIL, palloc(), pstrdup(), relation_close(), relation_open(), RelationGetDescr, RTE_FUNCTION, RTE_RELATION, RTE_TABLEFUNC, strVal, TupleDescAttr(), and VAR_RETURNING_DEFAULT.

Referenced by set_deparse_for_query(), and set_simple_column_names().

◆ set_rtable_names()

static void set_rtable_names ( deparse_namespace dpns,
List parent_namespaces,
Bitmapset rels_used 
)
static

Definition at line 4246 of file ruleutils.c.

4248{
4250 HTAB *names_hash;
4252 bool found;
4253 int rtindex;
4254 ListCell *lc;
4255
4256 dpns->rtable_names = NIL;
4257 /* nothing more to do if empty rtable */
4258 if (dpns->rtable == NIL)
4259 return;
4260
4261 /*
4262 * We use a hash table to hold known names, so that this process is O(N)
4263 * not O(N^2) for N names.
4264 */
4265 hash_ctl.keysize = NAMEDATALEN;
4266 hash_ctl.entrysize = sizeof(NameHashEntry);
4268 names_hash = hash_create("set_rtable_names names",
4269 list_length(dpns->rtable),
4270 &hash_ctl,
4272
4273 /* Preload the hash table with names appearing in parent_namespaces */
4274 foreach(lc, parent_namespaces)
4275 {
4277 ListCell *lc2;
4278
4279 foreach(lc2, olddpns->rtable_names)
4280 {
4281 char *oldname = (char *) lfirst(lc2);
4282
4283 if (oldname == NULL)
4284 continue;
4285 hentry = (NameHashEntry *) hash_search(names_hash,
4286 oldname,
4287 HASH_ENTER,
4288 &found);
4289 /* we do not complain about duplicate names in parent namespaces */
4290 hentry->counter = 0;
4291 }
4292 }
4293
4294 /* Now we can scan the rtable */
4295 rtindex = 1;
4296 foreach(lc, dpns->rtable)
4297 {
4299 char *refname;
4300
4301 /* Just in case this takes an unreasonable amount of time ... */
4303
4304 if (rels_used && !bms_is_member(rtindex, rels_used))
4305 {
4306 /* Ignore unreferenced RTE */
4307 refname = NULL;
4308 }
4309 else if (rte->alias)
4310 {
4311 /* If RTE has a user-defined alias, prefer that */
4312 refname = rte->alias->aliasname;
4313 }
4314 else if (rte->rtekind == RTE_RELATION)
4315 {
4316 /* Use the current actual name of the relation */
4317 refname = get_rel_name(rte->relid);
4318 }
4319 else if (rte->rtekind == RTE_JOIN)
4320 {
4321 /* Unnamed join has no refname */
4322 refname = NULL;
4323 }
4324 else
4325 {
4326 /* Otherwise use whatever the parser assigned */
4327 refname = rte->eref->aliasname;
4328 }
4329
4330 /*
4331 * If the selected name isn't unique, append digits to make it so, and
4332 * make a new hash entry for it once we've got a unique name. For a
4333 * very long input name, we might have to truncate to stay within
4334 * NAMEDATALEN.
4335 */
4336 if (refname)
4337 {
4338 hentry = (NameHashEntry *) hash_search(names_hash,
4339 refname,
4340 HASH_ENTER,
4341 &found);
4342 if (found)
4343 {
4344 /* Name already in use, must choose a new one */
4345 int refnamelen = strlen(refname);
4346 char *modname = (char *) palloc(refnamelen + 16);
4348
4349 do
4350 {
4351 hentry->counter++;
4352 for (;;)
4353 {
4354 memcpy(modname, refname, refnamelen);
4355 sprintf(modname + refnamelen, "_%d", hentry->counter);
4356 if (strlen(modname) < NAMEDATALEN)
4357 break;
4358 /* drop chars from refname to keep all the digits */
4360 refnamelen - 1);
4361 }
4362 hentry2 = (NameHashEntry *) hash_search(names_hash,
4363 modname,
4364 HASH_ENTER,
4365 &found);
4366 } while (found);
4367 hentry2->counter = 0; /* init new hash entry */
4368 refname = modname;
4369 }
4370 else
4371 {
4372 /* Name not previously used, need only initialize hentry */
4373 hentry->counter = 0;
4374 }
4375 }
4376
4377 dpns->rtable_names = lappend(dpns->rtable_names, refname);
4378 rtindex++;
4379 }
4380
4381 hash_destroy(names_hash);
4382}

References bms_is_member(), CHECK_FOR_INTERRUPTS, NameHashEntry::counter, CurrentMemoryContext, fb(), get_rel_name(), HASH_CONTEXT, hash_create(), hash_destroy(), HASH_ELEM, HASH_ENTER, hash_search(), HASH_STRINGS, lappend(), lfirst, list_length(), memcpy(), NAMEDATALEN, NIL, palloc(), pg_mbcliplen(), RTE_JOIN, RTE_RELATION, and sprintf.

Referenced by deparse_context_for(), pg_get_triggerdef_worker(), select_rtable_names_for_explain(), and set_deparse_for_query().

◆ set_simple_column_names()

static void set_simple_column_names ( deparse_namespace dpns)
static

Definition at line 4460 of file ruleutils.c.

4461{
4462 ListCell *lc;
4463 ListCell *lc2;
4464
4465 /* Initialize dpns->rtable_columns to contain zeroed structs */
4466 dpns->rtable_columns = NIL;
4467 while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4468 dpns->rtable_columns = lappend(dpns->rtable_columns,
4469 palloc0(sizeof(deparse_columns)));
4470
4471 /* Assign unique column aliases within each non-join RTE */
4472 forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4473 {
4476
4477 if (rte->rtekind != RTE_JOIN)
4479 }
4480}

References fb(), forboth, lappend(), lfirst, list_length(), NIL, palloc0(), RTE_JOIN, and set_relation_column_names().

Referenced by deparse_context_for(), deparse_context_for_plan_tree(), and pg_get_triggerdef_worker().

◆ set_using_names()

static void set_using_names ( deparse_namespace dpns,
Node jtnode,
List parentUsing 
)
static

Definition at line 4572 of file ruleutils.c.

4573{
4574 if (IsA(jtnode, RangeTblRef))
4575 {
4576 /* nothing to do now */
4577 }
4578 else if (IsA(jtnode, FromExpr))
4579 {
4580 FromExpr *f = (FromExpr *) jtnode;
4581 ListCell *lc;
4582
4583 foreach(lc, f->fromlist)
4584 set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4585 }
4586 else if (IsA(jtnode, JoinExpr))
4587 {
4588 JoinExpr *j = (JoinExpr *) jtnode;
4589 RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4591 int *leftattnos;
4592 int *rightattnos;
4595 int i;
4596 ListCell *lc;
4597
4598 /* Get info about the shape of the join */
4600 leftattnos = colinfo->leftattnos;
4601 rightattnos = colinfo->rightattnos;
4602
4603 /* Look up the not-yet-filled-in child deparse_columns structs */
4606
4607 /*
4608 * If this join is unnamed, then we cannot substitute new aliases at
4609 * this level, so any name requirements pushed down to here must be
4610 * pushed down again to the children.
4611 */
4612 if (rte->alias == NULL)
4613 {
4614 for (i = 0; i < colinfo->num_cols; i++)
4615 {
4616 char *colname = colinfo->colnames[i];
4617
4618 if (colname == NULL)
4619 continue;
4620
4621 /* Push down to left column, unless it's a system column */
4622 if (leftattnos[i] > 0)
4623 {
4625 leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4626 }
4627
4628 /* Same on the righthand side */
4629 if (rightattnos[i] > 0)
4630 {
4632 rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4633 }
4634 }
4635 }
4636
4637 /*
4638 * If there's a USING clause, select the USING column names and push
4639 * those names down to the children. We have two strategies:
4640 *
4641 * If dpns->unique_using is true, we force all USING names to be
4642 * unique across the whole query level. In principle we'd only need
4643 * the names of dangerous USING columns to be globally unique, but to
4644 * safely assign all USING names in a single pass, we have to enforce
4645 * the same uniqueness rule for all of them. However, if a USING
4646 * column's name has been pushed down from the parent, we should use
4647 * it as-is rather than making a uniqueness adjustment. This is
4648 * necessary when we're at an unnamed join, and it creates no risk of
4649 * ambiguity. Also, if there's a user-written output alias for a
4650 * merged column, we prefer to use that rather than the input name;
4651 * this simplifies the logic and seems likely to lead to less aliasing
4652 * overall.
4653 *
4654 * If dpns->unique_using is false, we only need USING names to be
4655 * unique within their own join RTE. We still need to honor
4656 * pushed-down names, though.
4657 *
4658 * Though significantly different in results, these two strategies are
4659 * implemented by the same code, with only the difference of whether
4660 * to put assigned names into dpns->using_names.
4661 */
4662 if (j->usingClause)
4663 {
4664 /* Copy the input parentUsing list so we don't modify it */
4665 parentUsing = list_copy(parentUsing);
4666
4667 /* USING names must correspond to the first join output columns */
4669 i = 0;
4670 foreach(lc, j->usingClause)
4671 {
4672 char *colname = strVal(lfirst(lc));
4673
4674 /* Assert it's a merged column */
4675 Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4676
4677 /* Adopt passed-down name if any, else select unique name */
4678 if (colinfo->colnames[i] != NULL)
4679 colname = colinfo->colnames[i];
4680 else
4681 {
4682 /* Prefer user-written output alias if any */
4683 if (rte->alias && i < list_length(rte->alias->colnames))
4684 colname = strVal(list_nth(rte->alias->colnames, i));
4685 /* Make it appropriately unique */
4686 colname = make_colname_unique(colname, dpns, colinfo);
4687 if (dpns->unique_using)
4688 dpns->using_names = lappend(dpns->using_names,
4689 colname);
4690 /* Save it as output column name, too */
4691 colinfo->colnames[i] = colname;
4692 }
4693
4694 /* Remember selected names for use later */
4695 colinfo->usingNames = lappend(colinfo->usingNames, colname);
4696 parentUsing = lappend(parentUsing, colname);
4697
4698 /* Push down to left column, unless it's a system column */
4699 if (leftattnos[i] > 0)
4700 {
4702 leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4703 }
4704
4705 /* Same on the righthand side */
4706 if (rightattnos[i] > 0)
4707 {
4709 rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4710 }
4711
4712 i++;
4713 }
4714 }
4715
4716 /* Mark child deparse_columns structs with correct parentUsing info */
4717 leftcolinfo->parentUsing = parentUsing;
4718 rightcolinfo->parentUsing = parentUsing;
4719
4720 /* Now recursively assign USING column names in children */
4721 set_using_names(dpns, j->larg, parentUsing);
4722 set_using_names(dpns, j->rarg, parentUsing);
4723 }
4724 else
4725 elog(ERROR, "unrecognized node type: %d",
4726 (int) nodeTag(jtnode));
4727}

References Assert, deparse_columns_fetch, elog, ERROR, expand_colnames_array_to(), fb(), FromExpr::fromlist, i, identify_join_columns(), IsA, j, lappend(), lfirst, list_copy(), list_length(), list_nth(), make_colname_unique(), nodeTag, rt_fetch, set_using_names(), and strVal.

Referenced by set_deparse_for_query(), and set_using_names().

◆ simple_quote_literal()

static void simple_quote_literal ( StringInfo  buf,
const char val 
)
static

Definition at line 12401 of file ruleutils.c.

12402{
12403 const char *valptr;
12404
12405 /*
12406 * We always form the string literal according to standard SQL rules.
12407 */
12409 for (valptr = val; *valptr; valptr++)
12410 {
12411 char ch = *valptr;
12412
12413 if (SQL_STR_DOUBLE(ch, false))
12416 }
12418}

References appendStringInfoChar(), buf, fb(), SQL_STR_DOUBLE, and val.

Referenced by get_const_expr(), get_reloptions(), get_rule_expr(), get_utility_query_def(), pg_get_functiondef(), and pg_get_triggerdef_worker().

◆ string_to_text()

Variable Documentation

◆ plan_getrulebyoid

SPIPlanPtr plan_getrulebyoid = NULL
static

Definition at line 338 of file ruleutils.c.

Referenced by pg_get_ruledef_worker().

◆ plan_getviewrule

SPIPlanPtr plan_getviewrule = NULL
static

Definition at line 340 of file ruleutils.c.

Referenced by pg_get_viewdef_worker().

◆ query_getrulebyoid

const char* const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1"
static

Definition at line 339 of file ruleutils.c.

Referenced by pg_get_ruledef_worker().

◆ query_getviewrule

const char* const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2"
static

Definition at line 341 of file ruleutils.c.

Referenced by pg_get_viewdef_worker().

◆ quote_all_identifiers

bool quote_all_identifiers = false

Definition at line 344 of file ruleutils.c.

Referenced by main(), main(), quote_identifier(), and setup_connection().