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 get_json_agg_constructor_expr (Node *node, deparse_context *context, void *callback_arg)
 
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 bool json_table_plan_is_default (JsonTablePlan *plan)
 
static void get_json_table_plan (TableFunc *tf, JsonTablePlan *plan, deparse_context *context, bool parenthesize)
 
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 get_json_agg_constructor_expr(Node *node, deparse_context *context,
515 void *callback_arg);
516static void simple_quote_literal(StringInfo buf, const char *val);
517static void get_sublink_expr(SubLink *sublink, deparse_context *context);
518static void get_tablefunc(TableFunc *tf, deparse_context *context,
519 bool showimplicit);
520static void get_from_clause(Query *query, const char *prefix,
521 deparse_context *context);
522static void get_from_clause_item(Node *jtnode, Query *query,
523 deparse_context *context);
524static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
525 deparse_context *context);
527 deparse_context *context);
528static void get_for_portion_of(ForPortionOfExpr *forPortionOf,
529 deparse_context *context);
532 deparse_context *context);
533static void get_tablesample_def(TableSampleClause *tablesample,
534 deparse_context *context);
535static void get_opclass_name(Oid opclass, Oid actual_datatype,
537static Node *processIndirection(Node *node, deparse_context *context);
538static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
539static char *get_relation_name(Oid relid);
540static char *generate_relation_name(Oid relid, List *namespaces);
541static char *generate_qualified_relation_name(Oid relid);
542static char *generate_function_name(Oid funcid, int nargs,
543 List *argnames, Oid *argtypes,
544 bool has_variadic, bool *use_variadic_p,
545 bool inGroupBy);
547static void add_cast_to(StringInfo buf, Oid typid);
548static char *generate_qualified_type_name(Oid typid);
549static text *string_to_text(char *str);
550static char *flatten_reloptions(Oid relid);
551void get_reloptions(StringInfo buf, Datum reloptions);
552static void get_json_path_spec(Node *path_spec, deparse_context *context,
553 bool showimplicit);
555 deparse_context *context,
556 bool showimplicit);
558 deparse_context *context,
559 bool showimplicit,
560 bool needcomma);
561
562#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
563
564
565/* ----------
566 * pg_get_ruledef - Do it all and return a text
567 * that could be used as a statement
568 * to recreate the rule
569 * ----------
570 */
571Datum
573{
575 int prettyFlags;
576 char *res;
577
578 prettyFlags = PRETTYFLAG_INDENT;
579
580 res = pg_get_ruledef_worker(ruleoid, prettyFlags);
581
582 if (res == NULL)
584
586}
587
588
589Datum
591{
593 bool pretty = PG_GETARG_BOOL(1);
594 int prettyFlags;
595 char *res;
596
597 prettyFlags = GET_PRETTY_FLAGS(pretty);
598
599 res = pg_get_ruledef_worker(ruleoid, prettyFlags);
600
601 if (res == NULL)
603
605}
606
607
608static char *
609pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
610{
611 Datum args[1];
612 char nulls[1];
613 int spirc;
617
618 /*
619 * Do this first so that string is alloc'd in outer context not SPI's.
620 */
622
623 /*
624 * Connect to SPI manager
625 */
626 SPI_connect();
627
628 /*
629 * On the first call prepare the plan to lookup pg_rewrite. We read
630 * pg_rewrite over the SPI manager instead of using the syscache to be
631 * checked for read access on pg_rewrite.
632 */
633 if (plan_getrulebyoid == NULL)
634 {
635 Oid argtypes[1];
637
638 argtypes[0] = OIDOID;
639 plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
640 if (plan == NULL)
641 elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
644 }
645
646 /*
647 * Get the pg_rewrite tuple for this rule
648 */
650 nulls[0] = ' ';
651 spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
652 if (spirc != SPI_OK_SELECT)
653 elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
654 if (SPI_processed != 1)
655 {
656 /*
657 * There is no tuple data available here, just keep the output buffer
658 * empty.
659 */
660 }
661 else
662 {
663 /*
664 * Get the rule's definition and put it into executor's memory
665 */
668 make_ruledef(&buf, ruletup, rulettc, prettyFlags);
669 }
670
671 /*
672 * Disconnect from SPI manager
673 */
674 if (SPI_finish() != SPI_OK_FINISH)
675 elog(ERROR, "SPI_finish failed");
676
677 if (buf.len == 0)
678 return NULL;
679
680 return buf.data;
681}
682
683
684/* ----------
685 * pg_get_viewdef - Mainly the same thing, but we
686 * only return the SELECT part of a view
687 * ----------
688 */
689Datum
691{
692 /* By OID */
693 Oid viewoid = PG_GETARG_OID(0);
694 int prettyFlags;
695 char *res;
696
697 prettyFlags = PRETTYFLAG_INDENT;
698
699 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
700
701 if (res == NULL)
703
705}
706
707
708Datum
710{
711 /* By OID */
712 Oid viewoid = PG_GETARG_OID(0);
713 bool pretty = PG_GETARG_BOOL(1);
714 int prettyFlags;
715 char *res;
716
717 prettyFlags = GET_PRETTY_FLAGS(pretty);
718
719 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
720
721 if (res == NULL)
723
725}
726
727Datum
729{
730 /* By OID */
731 Oid viewoid = PG_GETARG_OID(0);
732 int wrap = PG_GETARG_INT32(1);
733 int prettyFlags;
734 char *res;
735
736 /* calling this implies we want pretty printing */
737 prettyFlags = GET_PRETTY_FLAGS(true);
738
739 res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
740
741 if (res == NULL)
743
745}
746
747Datum
749{
750 /* By qualified name */
752 int prettyFlags;
754 Oid viewoid;
755 char *res;
756
757 prettyFlags = PRETTYFLAG_INDENT;
758
759 /* Look up view name. Can't lock it - we might not have privileges. */
761 viewoid = RangeVarGetRelid(viewrel, NoLock, false);
762
763 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
764
765 if (res == NULL)
767
769}
770
771
772Datum
774{
775 /* By qualified name */
777 bool pretty = PG_GETARG_BOOL(1);
778 int prettyFlags;
780 Oid viewoid;
781 char *res;
782
783 prettyFlags = GET_PRETTY_FLAGS(pretty);
784
785 /* Look up view name. Can't lock it - we might not have privileges. */
787 viewoid = RangeVarGetRelid(viewrel, NoLock, false);
788
789 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
790
791 if (res == NULL)
793
795}
796
797/*
798 * Common code for by-OID and by-name variants of pg_get_viewdef
799 */
800static char *
801pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
802{
803 Datum args[2];
804 char nulls[2];
805 int spirc;
809
810 /*
811 * Do this first so that string is alloc'd in outer context not SPI's.
812 */
814
815 /*
816 * Connect to SPI manager
817 */
818 SPI_connect();
819
820 /*
821 * On the first call prepare the plan to lookup pg_rewrite. We read
822 * pg_rewrite over the SPI manager instead of using the syscache to be
823 * checked for read access on pg_rewrite.
824 */
825 if (plan_getviewrule == NULL)
826 {
827 Oid argtypes[2];
829
830 argtypes[0] = OIDOID;
831 argtypes[1] = NAMEOID;
832 plan = SPI_prepare(query_getviewrule, 2, argtypes);
833 if (plan == NULL)
834 elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
837 }
838
839 /*
840 * Get the pg_rewrite tuple for the view's SELECT rule
841 */
842 args[0] = ObjectIdGetDatum(viewoid);
844 nulls[0] = ' ';
845 nulls[1] = ' ';
846 spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
847 if (spirc != SPI_OK_SELECT)
848 elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
849 if (SPI_processed != 1)
850 {
851 /*
852 * There is no tuple data available here, just keep the output buffer
853 * empty.
854 */
855 }
856 else
857 {
858 /*
859 * Get the rule's definition and put it into executor's memory
860 */
863 make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
864 }
865
866 /*
867 * Disconnect from SPI manager
868 */
869 if (SPI_finish() != SPI_OK_FINISH)
870 elog(ERROR, "SPI_finish failed");
871
872 if (buf.len == 0)
873 return NULL;
874
875 return buf.data;
876}
877
878/* ----------
879 * pg_get_triggerdef - Get the definition of a trigger
880 * ----------
881 */
882Datum
884{
886 char *res;
887
888 res = pg_get_triggerdef_worker(trigid, false);
889
890 if (res == NULL)
892
894}
895
896Datum
898{
900 bool pretty = PG_GETARG_BOOL(1);
901 char *res;
902
904
905 if (res == NULL)
907
909}
910
911static char *
913{
918 ScanKeyData skey[1];
920 int findx = 0;
921 char *tgname;
922 char *tgoldtable;
923 char *tgnewtable;
924 Datum value;
925 bool isnull;
926
927 /*
928 * Fetch the pg_trigger tuple by the Oid of the trigger
929 */
931
932 ScanKeyInit(&skey[0],
936
938 NULL, 1, skey);
939
941
943 {
946 return NULL;
947 }
948
950
951 /*
952 * Start the trigger definition. Note that the trigger's name should never
953 * be schema-qualified, but the trigger rel's name may be.
954 */
956
957 tgname = NameStr(trigrec->tgname);
958 appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
959 OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
960 quote_identifier(tgname));
961
962 if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
963 appendStringInfoString(&buf, "BEFORE");
964 else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
965 appendStringInfoString(&buf, "AFTER");
966 else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
967 appendStringInfoString(&buf, "INSTEAD OF");
968 else
969 elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
970
971 if (TRIGGER_FOR_INSERT(trigrec->tgtype))
972 {
973 appendStringInfoString(&buf, " INSERT");
974 findx++;
975 }
976 if (TRIGGER_FOR_DELETE(trigrec->tgtype))
977 {
978 if (findx > 0)
979 appendStringInfoString(&buf, " OR DELETE");
980 else
981 appendStringInfoString(&buf, " DELETE");
982 findx++;
983 }
984 if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
985 {
986 if (findx > 0)
987 appendStringInfoString(&buf, " OR UPDATE");
988 else
989 appendStringInfoString(&buf, " UPDATE");
990 findx++;
991 /* tgattr is first var-width field, so OK to access directly */
992 if (trigrec->tgattr.dim1 > 0)
993 {
994 int i;
995
996 appendStringInfoString(&buf, " OF ");
997 for (i = 0; i < trigrec->tgattr.dim1; i++)
998 {
999 char *attname;
1000
1001 if (i > 0)
1003 attname = get_attname(trigrec->tgrelid,
1004 trigrec->tgattr.values[i], false);
1006 }
1007 }
1008 }
1009 if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
1010 {
1011 if (findx > 0)
1012 appendStringInfoString(&buf, " OR TRUNCATE");
1013 else
1014 appendStringInfoString(&buf, " TRUNCATE");
1015 findx++;
1016 }
1017
1018 /*
1019 * In non-pretty mode, always schema-qualify the target table name for
1020 * safety. In pretty mode, schema-qualify only if not visible.
1021 */
1022 appendStringInfo(&buf, " ON %s ",
1023 pretty ?
1026
1027 if (OidIsValid(trigrec->tgconstraint))
1028 {
1029 if (OidIsValid(trigrec->tgconstrrelid))
1030 appendStringInfo(&buf, "FROM %s ",
1031 generate_relation_name(trigrec->tgconstrrelid, NIL));
1032 if (!trigrec->tgdeferrable)
1033 appendStringInfoString(&buf, "NOT ");
1034 appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
1035 if (trigrec->tginitdeferred)
1036 appendStringInfoString(&buf, "DEFERRED ");
1037 else
1038 appendStringInfoString(&buf, "IMMEDIATE ");
1039 }
1040
1042 tgrel->rd_att, &isnull);
1043 if (!isnull)
1044 tgoldtable = NameStr(*DatumGetName(value));
1045 else
1046 tgoldtable = NULL;
1048 tgrel->rd_att, &isnull);
1049 if (!isnull)
1050 tgnewtable = NameStr(*DatumGetName(value));
1051 else
1052 tgnewtable = NULL;
1053 if (tgoldtable != NULL || tgnewtable != NULL)
1054 {
1055 appendStringInfoString(&buf, "REFERENCING ");
1056 if (tgoldtable != NULL)
1057 appendStringInfo(&buf, "OLD TABLE AS %s ",
1058 quote_identifier(tgoldtable));
1059 if (tgnewtable != NULL)
1060 appendStringInfo(&buf, "NEW TABLE AS %s ",
1061 quote_identifier(tgnewtable));
1062 }
1063
1064 if (TRIGGER_FOR_ROW(trigrec->tgtype))
1065 appendStringInfoString(&buf, "FOR EACH ROW ");
1066 else
1067 appendStringInfoString(&buf, "FOR EACH STATEMENT ");
1068
1069 /* If the trigger has a WHEN qualification, add that */
1071 tgrel->rd_att, &isnull);
1072 if (!isnull)
1073 {
1074 Node *qual;
1075 char relkind;
1076 deparse_context context;
1080
1081 appendStringInfoString(&buf, "WHEN (");
1082
1084
1085 relkind = get_rel_relkind(trigrec->tgrelid);
1086
1087 /* Build minimal OLD and NEW RTEs for the rel */
1089 oldrte->rtekind = RTE_RELATION;
1090 oldrte->relid = trigrec->tgrelid;
1091 oldrte->relkind = relkind;
1092 oldrte->rellockmode = AccessShareLock;
1093 oldrte->alias = makeAlias("old", NIL);
1094 oldrte->eref = oldrte->alias;
1095 oldrte->lateral = false;
1096 oldrte->inh = false;
1097 oldrte->inFromCl = true;
1098
1100 newrte->rtekind = RTE_RELATION;
1101 newrte->relid = trigrec->tgrelid;
1102 newrte->relkind = relkind;
1103 newrte->rellockmode = AccessShareLock;
1104 newrte->alias = makeAlias("new", NIL);
1105 newrte->eref = newrte->alias;
1106 newrte->lateral = false;
1107 newrte->inh = false;
1108 newrte->inFromCl = true;
1109
1110 /* Build two-element rtable */
1111 memset(&dpns, 0, sizeof(dpns));
1112 dpns.rtable = list_make2(oldrte, newrte);
1113 dpns.subplans = NIL;
1114 dpns.ctes = NIL;
1115 dpns.appendrels = NULL;
1118
1119 /* Set up context with one-deep namespace stack */
1120 context.buf = &buf;
1121 context.namespaces = list_make1(&dpns);
1122 context.resultDesc = NULL;
1123 context.targetList = NIL;
1124 context.windowClause = NIL;
1125 context.varprefix = true;
1128 context.indentLevel = PRETTYINDENT_STD;
1129 context.colNamesVisible = true;
1130 context.inGroupBy = false;
1131 context.varInOrderBy = false;
1132 context.appendparents = NULL;
1133
1134 get_rule_expr(qual, &context, false);
1135
1137 }
1138
1139 appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
1141 NIL, NULL,
1142 false, NULL, false));
1143
1144 if (trigrec->tgnargs > 0)
1145 {
1146 char *p;
1147 int i;
1148
1150 tgrel->rd_att, &isnull);
1151 if (isnull)
1152 elog(ERROR, "tgargs is null for trigger %u", trigid);
1153 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
1154 for (i = 0; i < trigrec->tgnargs; i++)
1155 {
1156 if (i > 0)
1159 /* advance p to next string embedded in tgargs */
1160 while (*p)
1161 p++;
1162 p++;
1163 }
1164 }
1165
1166 /* We deliberately do not put semi-colon at end */
1168
1169 /* Clean up */
1171
1173
1174 return buf.data;
1175}
1176
1177/* ----------
1178 * pg_get_indexdef - Get the definition of an index
1179 *
1180 * In the extended version, there is a colno argument as well as pretty bool.
1181 * if colno == 0, we want a complete index definition.
1182 * if colno > 0, we only want the Nth index key's variable or expression.
1183 *
1184 * Note that the SQL-function versions of this omit any info about the
1185 * index tablespace; this is intentional because pg_dump wants it that way.
1186 * However pg_get_indexdef_string() includes the index tablespace.
1187 * ----------
1188 */
1189Datum
1191{
1192 Oid indexrelid = PG_GETARG_OID(0);
1193 int prettyFlags;
1194 char *res;
1195
1196 prettyFlags = PRETTYFLAG_INDENT;
1197
1198 res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1199 false, false,
1200 false, false,
1201 prettyFlags, true);
1202
1203 if (res == NULL)
1205
1207}
1208
1209Datum
1211{
1212 Oid indexrelid = PG_GETARG_OID(0);
1213 int32 colno = PG_GETARG_INT32(1);
1214 bool pretty = PG_GETARG_BOOL(2);
1215 int prettyFlags;
1216 char *res;
1217
1218 prettyFlags = GET_PRETTY_FLAGS(pretty);
1219
1220 res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1221 colno != 0, false,
1222 false, false,
1223 prettyFlags, true);
1224
1225 if (res == NULL)
1227
1229}
1230
1231/*
1232 * Internal version for use by ALTER TABLE.
1233 * Includes a tablespace clause in the result.
1234 * Returns a palloc'd C string; no pretty-printing.
1235 */
1236char *
1237pg_get_indexdef_string(Oid indexrelid)
1238{
1239 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1240 false, false,
1241 true, true,
1242 0, false);
1243}
1244
1245/* Internal version that just reports the key-column definitions */
1246char *
1247pg_get_indexdef_columns(Oid indexrelid, bool pretty)
1248{
1249 int prettyFlags;
1250
1251 prettyFlags = GET_PRETTY_FLAGS(pretty);
1252
1253 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1254 true, true,
1255 false, false,
1256 prettyFlags, false);
1257}
1258
1259/* Internal version, extensible with flags to control its behavior */
1260char *
1262{
1263 bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1264 bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1265 int prettyFlags;
1266
1267 prettyFlags = GET_PRETTY_FLAGS(pretty);
1268
1269 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1270 true, keys_only,
1271 false, false,
1272 prettyFlags, false);
1273}
1274
1275/*
1276 * Internal workhorse to decompile an index definition.
1277 *
1278 * This is now used for exclusion constraints as well: if excludeOps is not
1279 * NULL then it points to an array of exclusion operator OIDs.
1280 */
1281static char *
1282pg_get_indexdef_worker(Oid indexrelid, int colno,
1283 const Oid *excludeOps,
1284 bool attrsOnly, bool keysOnly,
1285 bool showTblSpc, bool inherits,
1286 int prettyFlags, bool missing_ok)
1287{
1288 /* might want a separate isConstraint parameter later */
1289 bool isConstraint = (excludeOps != NULL);
1297 List *indexprs;
1299 List *context;
1300 Oid indrelid;
1301 int keyno;
1309 char *str;
1310 char *sep;
1311
1312 /*
1313 * Fetch the pg_index tuple by the Oid of the index
1314 */
1317 {
1318 if (missing_ok)
1319 return NULL;
1320 elog(ERROR, "cache lookup failed for index %u", indexrelid);
1321 }
1323
1324 indrelid = idxrec->indrelid;
1325 Assert(indexrelid == idxrec->indexrelid);
1326
1327 /* Must get indcollation, indclass, and indoption the hard way */
1331
1335
1339
1340 /*
1341 * Fetch the pg_class tuple of the index relation
1342 */
1345 elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1347
1348 /*
1349 * Fetch the pg_am tuple of the index' access method
1350 */
1352 if (!HeapTupleIsValid(ht_am))
1353 elog(ERROR, "cache lookup failed for access method %u",
1354 idxrelrec->relam);
1356
1357 /* Fetch the index AM's API struct */
1358 amroutine = GetIndexAmRoutine(amrec->amhandler);
1359
1360 /*
1361 * Get the index expressions, if any. (NOTE: we do not use the relcache
1362 * versions of the expressions and predicate, because we want to display
1363 * non-const-folded expressions.)
1364 */
1366 {
1368 char *exprsString;
1369
1375 }
1376 else
1377 indexprs = NIL;
1378
1380
1382
1383 /*
1384 * Start the index definition. Note that the index's name should never be
1385 * schema-qualified, but the indexed rel's name may be.
1386 */
1388
1389 if (!attrsOnly)
1390 {
1391 if (!isConstraint)
1392 appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
1393 idxrec->indisunique ? "UNIQUE " : "",
1396 && !inherits ? "ONLY " : "",
1397 (prettyFlags & PRETTYFLAG_SCHEMA) ?
1400 quote_identifier(NameStr(amrec->amname)));
1401 else /* currently, must be EXCLUDE constraint */
1402 appendStringInfo(&buf, "EXCLUDE USING %s (",
1403 quote_identifier(NameStr(amrec->amname)));
1404 }
1405
1406 /*
1407 * Report the indexed attributes
1408 */
1409 sep = "";
1410 for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1411 {
1412 AttrNumber attnum = idxrec->indkey.values[keyno];
1415
1416 /*
1417 * Ignore non-key attributes if told to.
1418 */
1419 if (keysOnly && keyno >= idxrec->indnkeyatts)
1420 break;
1421
1422 /* Otherwise, print INCLUDE to divide key and non-key attrs. */
1423 if (!colno && keyno == idxrec->indnkeyatts)
1424 {
1425 appendStringInfoString(&buf, ") INCLUDE (");
1426 sep = "";
1427 }
1428
1429 if (!colno)
1431 sep = ", ";
1432
1433 if (attnum != 0)
1434 {
1435 /* Simple index column */
1436 char *attname;
1438
1440 if (!colno || colno == keyno + 1)
1445 }
1446 else
1447 {
1448 /* expressional index */
1449 Node *indexkey;
1450
1451 if (indexpr_item == NULL)
1452 elog(ERROR, "too few entries in indexprs list");
1455 /* Deparse */
1456 str = deparse_expression_pretty(indexkey, context, false, false,
1457 prettyFlags, 0);
1458 if (!colno || colno == keyno + 1)
1459 {
1460 /* Need parens if it's not a bare function call */
1463 else
1464 appendStringInfo(&buf, "(%s)", str);
1465 }
1468 }
1469
1470 /* Print additional decoration for (selected) key columns */
1472 (!colno || colno == keyno + 1))
1473 {
1474 int16 opt = indoption->values[keyno];
1475 Oid indcoll = indcollation->values[keyno];
1476 Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1477 bool has_options = attoptions != (Datum) 0;
1478
1479 /* Add collation, if not default for column */
1481 appendStringInfo(&buf, " COLLATE %s",
1483
1484 /* Add the operator class name, if not default */
1485 get_opclass_name(indclass->values[keyno],
1487
1488 if (has_options)
1489 {
1491 get_reloptions(&buf, attoptions);
1493 }
1494
1495 /* Add options if relevant */
1496 if (amroutine->amcanorder)
1497 {
1498 /* if it supports sort ordering, report DESC and NULLS opts */
1499 if (opt & INDOPTION_DESC)
1500 {
1501 appendStringInfoString(&buf, " DESC");
1502 /* NULLS FIRST is the default in this case */
1503 if (!(opt & INDOPTION_NULLS_FIRST))
1504 appendStringInfoString(&buf, " NULLS LAST");
1505 }
1506 else
1507 {
1508 if (opt & INDOPTION_NULLS_FIRST)
1509 appendStringInfoString(&buf, " NULLS FIRST");
1510 }
1511 }
1512
1513 /* Add the exclusion operator if relevant */
1514 if (excludeOps != NULL)
1515 appendStringInfo(&buf, " WITH %s",
1517 keycoltype,
1518 keycoltype));
1519 }
1520 }
1521
1522 if (!attrsOnly)
1523 {
1525
1526 if (idxrec->indnullsnotdistinct)
1527 appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1528
1529 /*
1530 * If it has options, append "WITH (options)"
1531 */
1532 str = flatten_reloptions(indexrelid);
1533 if (str)
1534 {
1535 appendStringInfo(&buf, " WITH (%s)", str);
1536 pfree(str);
1537 }
1538
1539 /*
1540 * Print tablespace, but only if requested
1541 */
1542 if (showTblSpc)
1543 {
1544 Oid tblspc;
1545
1546 tblspc = get_rel_tablespace(indexrelid);
1547 if (OidIsValid(tblspc))
1548 {
1549 if (isConstraint)
1550 appendStringInfoString(&buf, " USING INDEX");
1551 appendStringInfo(&buf, " TABLESPACE %s",
1553 }
1554 }
1555
1556 /*
1557 * If it's a partial index, decompile and append the predicate
1558 */
1560 {
1561 Node *node;
1563 char *predString;
1564
1565 /* Convert text string to node tree */
1569 node = (Node *) stringToNode(predString);
1571
1572 /* Deparse */
1573 str = deparse_expression_pretty(node, context, false, false,
1574 prettyFlags, 0);
1575 if (isConstraint)
1576 appendStringInfo(&buf, " WHERE (%s)", str);
1577 else
1578 appendStringInfo(&buf, " WHERE %s", str);
1579 }
1580 }
1581
1582 /* Clean up */
1586
1587 return buf.data;
1588}
1589
1590/* ----------
1591 * pg_get_querydef
1592 *
1593 * Public entry point to deparse one query parsetree.
1594 * The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
1595 *
1596 * The result is a palloc'd C string.
1597 * ----------
1598 */
1599char *
1600pg_get_querydef(Query *query, bool pretty)
1601{
1603 int prettyFlags;
1604
1605 prettyFlags = GET_PRETTY_FLAGS(pretty);
1606
1608
1609 get_query_def(query, &buf, NIL, NULL, true,
1610 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1611
1612 return buf.data;
1613}
1614
1615/*
1616 * pg_get_propgraphdef - get the definition of a property graph
1617 */
1618Datum
1620{
1625 char *name;
1626 char *nsp;
1627
1629
1633
1635 name = NameStr(classform->relname);
1636
1637 if (classform->relkind != RELKIND_PROPGRAPH)
1638 ereport(ERROR,
1640 errmsg("\"%s\" is not a property graph", name)));
1641
1642 nsp = get_namespace_name(classform->relnamespace);
1643
1644 appendStringInfo(&buf, "CREATE PROPERTY GRAPH %s",
1646
1648
1651
1653}
1654
1655/*
1656 * Generates a VERTEX TABLES (...) or EDGE TABLES (...) clause. Pass in the
1657 * property graph relation OID and the element kind (vertex or edge). Result
1658 * is appended to buf.
1659 */
1660static void
1662{
1665 SysScanDesc scan;
1666 bool first;
1667 HeapTuple tup;
1668
1670
1671 ScanKeyInit(&scankey[0],
1675
1677
1678 first = true;
1679 while ((tup = systable_getnext(scan)))
1680 {
1682 char *relname;
1683 Datum datum;
1684 bool isnull;
1685
1686 if (pgeform->pgekind != pgekind)
1687 continue;
1688
1689 if (first)
1690 {
1691 appendStringInfo(buf, "\n %s TABLES (\n", pgekind == PGEKIND_VERTEX ? "VERTEX" : "EDGE");
1692 first = false;
1693 }
1694 else
1696
1697 relname = get_rel_name(pgeform->pgerelid);
1698 if (relname && strcmp(relname, NameStr(pgeform->pgealias)) == 0)
1699 appendStringInfo(buf, " %s",
1700 generate_relation_name(pgeform->pgerelid, NIL));
1701 else
1702 appendStringInfo(buf, " %s AS %s",
1704 quote_identifier(NameStr(pgeform->pgealias)));
1705
1707 if (!isnull)
1708 {
1709 appendStringInfoString(buf, " KEY (");
1710 decompile_column_index_array(datum, pgeform->pgerelid, false, buf);
1712 }
1713 else
1714 elog(ERROR, "null pgekey for element %u", pgeform->oid);
1715
1716 if (pgekind == PGEKIND_EDGE)
1717 {
1718 Datum srckey;
1719 Datum srcref;
1720 Datum destkey;
1721 Datum destref;
1724
1726 srckey = isnull ? 0 : datum;
1728 srcref = isnull ? 0 : datum;
1730 destkey = isnull ? 0 : datum;
1732 destref = isnull ? 0 : datum;
1733
1734 appendStringInfoString(buf, " SOURCE");
1736 if (!tup2)
1737 elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgesrcvertexid);
1739 if (srckey)
1740 {
1741 appendStringInfoString(buf, " KEY (");
1742 decompile_column_index_array(srckey, pgeform->pgerelid, false, buf);
1743 appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1744 decompile_column_index_array(srcref, pgeform2->pgerelid, false, buf);
1746 }
1747 else
1750
1751 appendStringInfoString(buf, " DESTINATION");
1753 if (!tup2)
1754 elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgedestvertexid);
1756 if (destkey)
1757 {
1758 appendStringInfoString(buf, " KEY (");
1759 decompile_column_index_array(destkey, pgeform->pgerelid, false, buf);
1760 appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1761 decompile_column_index_array(destref, pgeform2->pgerelid, false, buf);
1763 }
1764 else
1767 }
1768
1769 make_propgraphdef_labels(buf, pgeform->oid, NameStr(pgeform->pgealias), pgeform->pgerelid);
1770 }
1771 if (!first)
1772 appendStringInfoString(buf, "\n )");
1773
1774 systable_endscan(scan);
1776}
1777
1778struct oid_str_pair
1779{
1780 Oid oid;
1781 char *str;
1782};
1783
1784static int
1786{
1787 struct oid_str_pair *v1 = lfirst(p1);
1788 struct oid_str_pair *v2 = lfirst(p2);
1789
1790 return strcmp(v1->str, v2->str);
1791}
1792
1793/*
1794 * Generates label and properties list. Pass in the element OID, the element
1795 * alias, and the graph relation OID. Result is appended to buf.
1796 */
1797static void
1799{
1802 SysScanDesc scan;
1803 int count;
1804 HeapTuple tup;
1805 List *label_list = NIL;
1806
1808
1809 ScanKeyInit(&scankey[0],
1813
1814 /*
1815 * We want to output the labels in a deterministic order. So we first
1816 * read all the data, then sort, then print it.
1817 */
1819
1820 while ((tup = systable_getnext(scan)))
1821 {
1823 struct oid_str_pair *osp;
1824
1825 osp = palloc_object(struct oid_str_pair);
1826 osp->oid = pgelform->oid;
1827 osp->str = get_propgraph_label_name(pgelform->pgellabelid);
1828
1830 }
1831
1832 systable_endscan(scan);
1834
1835 count = list_length(label_list);
1836
1837 /* Each element has at least one label. */
1838 Assert(count > 0);
1839
1840 /*
1841 * It is enough for the comparison function to compare just labels, since
1842 * all the labels of an element table should have distinct names.
1843 */
1845
1847 {
1848 if (strcmp(osp->str, elalias) == 0)
1849 {
1850 /* If the default label is the only label, don't print anything. */
1851 if (count != 1)
1852 appendStringInfoString(buf, " DEFAULT LABEL");
1853 }
1854 else
1855 appendStringInfo(buf, " LABEL %s", quote_identifier(osp->str));
1856
1858 }
1859}
1860
1861/*
1862 * Helper function for make_propgraphdef_properties(): Sort (propname, expr)
1863 * pairs by name.
1864 */
1865static int
1867{
1868 List *la = lfirst_node(List, a);
1869 List *lb = lfirst_node(List, b);
1870 char *pna = strVal(linitial(la));
1871 char *pnb = strVal(linitial(lb));
1872
1873 return strcmp(pna, pnb);
1874}
1875
1876/*
1877 * Generates element table properties clause (PROPERTIES (...) or NO
1878 * PROPERTIES). Pass in label OID and element table OID. Result is appended
1879 * to buf.
1880 */
1881static void
1883{
1886 SysScanDesc scan;
1887 HeapTuple tup;
1888 List *outlist = NIL;
1889
1891
1892 ScanKeyInit(&scankey[0],
1896
1897 /*
1898 * We want to output the properties in a deterministic order. So we first
1899 * read all the data, then sort, then print it.
1900 */
1902
1903 while ((tup = systable_getnext(scan)))
1904 {
1907 bool isnull;
1908 char *tmp;
1909 Node *expr;
1910 char *propname;
1911
1913 Assert(!isnull);
1915 expr = stringToNode(tmp);
1916 pfree(tmp);
1917
1919
1920 outlist = lappend(outlist, list_make2(makeString(propname), expr));
1921 }
1922
1923 systable_endscan(scan);
1925
1927
1928 if (outlist)
1929 {
1930 List *context;
1931 ListCell *lc;
1932 bool first = true;
1933
1935
1936 appendStringInfoString(buf, " PROPERTIES (");
1937
1938 foreach(lc, outlist)
1939 {
1941 char *propname = strVal(linitial(data));
1942 Node *expr = lsecond(data);
1943
1944 if (first)
1945 first = false;
1946 else
1948
1949 if (IsA(expr, Var) && strcmp(propname, get_attname(elrelid, castNode(Var, expr)->varattno, false)) == 0)
1951 else
1952 appendStringInfo(buf, "%s AS %s",
1953 deparse_expression_pretty(expr, context, false, false, 0, 0),
1955 }
1956
1958 }
1959 else
1960 appendStringInfoString(buf, " NO PROPERTIES");
1961}
1962
1963/*
1964 * pg_get_statisticsobjdef
1965 * Get the definition of an extended statistics object
1966 */
1967Datum
1969{
1971 char *res;
1972
1973 res = pg_get_statisticsobj_worker(statextid, false, true);
1974
1975 if (res == NULL)
1977
1979}
1980
1981/*
1982 * Internal version for use by ALTER TABLE.
1983 * Returns a palloc'd C string; no pretty-printing.
1984 */
1985char *
1987{
1988 return pg_get_statisticsobj_worker(statextid, false, false);
1989}
1990
1991/*
1992 * pg_get_statisticsobjdef_columns
1993 * Get columns and expressions for an extended statistics object
1994 */
1995Datum
1997{
1999 char *res;
2000
2001 res = pg_get_statisticsobj_worker(statextid, true, true);
2002
2003 if (res == NULL)
2005
2007}
2008
2009/*
2010 * Internal workhorse to decompile an extended statistics object.
2011 */
2012static char *
2014{
2018 int colno;
2019 char *nsp;
2020 ArrayType *arr;
2021 char *enabled;
2022 Datum datum;
2023 bool ndistinct_enabled;
2025 bool mcv_enabled;
2026 int i;
2027 List *context;
2028 ListCell *lc;
2029 List *exprs = NIL;
2030 bool has_exprs;
2031 int ncolumns;
2032
2034
2036 {
2037 if (missing_ok)
2038 return NULL;
2039 elog(ERROR, "cache lookup failed for statistics object %u", statextid);
2040 }
2041
2042 /* has the statistics expressions? */
2044
2046
2047 /*
2048 * Get the statistics expressions, if any. (NOTE: we do not use the
2049 * relcache versions of the expressions, because we want to display
2050 * non-const-folded expressions.)
2051 */
2052 if (has_exprs)
2053 {
2055 char *exprsString;
2056
2060 exprs = (List *) stringToNode(exprsString);
2062 }
2063 else
2064 exprs = NIL;
2065
2066 /* count the number of columns (attributes and expressions) */
2067 ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
2068
2070
2071 if (!columns_only)
2072 {
2073 nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
2074 appendStringInfo(&buf, "CREATE STATISTICS %s",
2076 NameStr(statextrec->stxname)));
2077
2078 /*
2079 * Decode the stxkind column so that we know which stats types to
2080 * print.
2081 */
2084 arr = DatumGetArrayTypeP(datum);
2085 if (ARR_NDIM(arr) != 1 ||
2086 ARR_HASNULL(arr) ||
2087 ARR_ELEMTYPE(arr) != CHAROID)
2088 elog(ERROR, "stxkind is not a 1-D char array");
2089 enabled = (char *) ARR_DATA_PTR(arr);
2090
2091 ndistinct_enabled = false;
2092 dependencies_enabled = false;
2093 mcv_enabled = false;
2094
2095 for (i = 0; i < ARR_DIMS(arr)[0]; i++)
2096 {
2097 if (enabled[i] == STATS_EXT_NDISTINCT)
2098 ndistinct_enabled = true;
2099 else if (enabled[i] == STATS_EXT_DEPENDENCIES)
2100 dependencies_enabled = true;
2101 else if (enabled[i] == STATS_EXT_MCV)
2102 mcv_enabled = true;
2103
2104 /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
2105 }
2106
2107 /*
2108 * If any option is disabled, then we'll need to append the types
2109 * clause to show which options are enabled. We omit the types clause
2110 * on purpose when all options are enabled, so a pg_dump/pg_restore
2111 * will create all statistics types on a newer postgres version, if
2112 * the statistics had all options enabled on the original version.
2113 *
2114 * But if the statistics is defined on just a single column, it has to
2115 * be an expression statistics. In that case we don't need to specify
2116 * kinds.
2117 */
2119 (ncolumns > 1))
2120 {
2121 bool gotone = false;
2122
2124
2126 {
2127 appendStringInfoString(&buf, "ndistinct");
2128 gotone = true;
2129 }
2130
2132 {
2133 appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
2134 gotone = true;
2135 }
2136
2137 if (mcv_enabled)
2138 appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
2139
2141 }
2142
2143 appendStringInfoString(&buf, " ON ");
2144 }
2145
2146 /* decode simple column references */
2147 for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
2148 {
2149 AttrNumber attnum = statextrec->stxkeys.values[colno];
2150 char *attname;
2151
2152 if (colno > 0)
2154
2155 attname = get_attname(statextrec->stxrelid, attnum, false);
2156
2158 }
2159
2161 statextrec->stxrelid);
2162
2163 foreach(lc, exprs)
2164 {
2165 Node *expr = (Node *) lfirst(lc);
2166 char *str;
2167 int prettyFlags = PRETTYFLAG_PAREN;
2168
2169 str = deparse_expression_pretty(expr, context, false, false,
2170 prettyFlags, 0);
2171
2172 if (colno > 0)
2174
2175 /* Need parens if it's not a bare function call */
2176 if (looks_like_function(expr))
2178 else
2179 appendStringInfo(&buf, "(%s)", str);
2180
2181 colno++;
2182 }
2183
2184 if (!columns_only)
2185 appendStringInfo(&buf, " FROM %s",
2187
2189
2190 return buf.data;
2191}
2192
2193/*
2194 * Generate text array of expressions for statistics object.
2195 */
2196Datum
2198{
2202 Datum datum;
2203 List *context;
2204 ListCell *lc;
2205 List *exprs = NIL;
2206 bool has_exprs;
2207 char *tmp;
2208 ArrayBuildState *astate = NULL;
2209
2211
2214
2215 /* Does the stats object have expressions? */
2217
2218 /* no expressions? we're done */
2219 if (!has_exprs)
2220 {
2223 }
2224
2226
2227 /*
2228 * Get the statistics expressions, and deparse them into text values.
2229 */
2232 tmp = TextDatumGetCString(datum);
2233 exprs = (List *) stringToNode(tmp);
2234 pfree(tmp);
2235
2237 statextrec->stxrelid);
2238
2239 foreach(lc, exprs)
2240 {
2241 Node *expr = (Node *) lfirst(lc);
2242 char *str;
2243 int prettyFlags = PRETTYFLAG_INDENT;
2244
2245 str = deparse_expression_pretty(expr, context, false, false,
2246 prettyFlags, 0);
2247
2248 astate = accumArrayResult(astate,
2250 false,
2251 TEXTOID,
2253 }
2254
2256
2258}
2259
2260/*
2261 * pg_get_partkeydef
2262 *
2263 * Returns the partition key specification, ie, the following:
2264 *
2265 * { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
2266 */
2267Datum
2269{
2270 Oid relid = PG_GETARG_OID(0);
2271 char *res;
2272
2273 res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
2274
2275 if (res == NULL)
2277
2279}
2280
2281/* Internal version that just reports the column definitions */
2282char *
2284{
2285 int prettyFlags;
2286
2287 prettyFlags = GET_PRETTY_FLAGS(pretty);
2288
2289 return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
2290}
2291
2292/*
2293 * Internal workhorse to decompile a partition key definition.
2294 */
2295static char *
2296pg_get_partkeydef_worker(Oid relid, int prettyFlags,
2297 bool attrsOnly, bool missing_ok)
2298{
2300 HeapTuple tuple;
2302 oidvector *partcollation;
2303 List *partexprs;
2305 List *context;
2306 Datum datum;
2308 int keyno;
2309 char *str;
2310 char *sep;
2311
2313 if (!HeapTupleIsValid(tuple))
2314 {
2315 if (missing_ok)
2316 return NULL;
2317 elog(ERROR, "cache lookup failed for partition key of %u", relid);
2318 }
2319
2321
2322 Assert(form->partrelid == relid);
2323
2324 /* Must get partclass and partcollation the hard way */
2325 datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2327 partclass = (oidvector *) DatumGetPointer(datum);
2328
2329 datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
2331 partcollation = (oidvector *) DatumGetPointer(datum);
2332
2333
2334 /*
2335 * Get the expressions, if any. (NOTE: we do not use the relcache
2336 * versions of the expressions, because we want to display
2337 * non-const-folded expressions.)
2338 */
2340 {
2342 char *exprsString;
2343
2347 partexprs = (List *) stringToNode(exprsString);
2348
2349 if (!IsA(partexprs, List))
2350 elog(ERROR, "unexpected node type found in partexprs: %d",
2351 (int) nodeTag(partexprs));
2352
2354 }
2355 else
2356 partexprs = NIL;
2357
2358 partexpr_item = list_head(partexprs);
2359 context = deparse_context_for(get_relation_name(relid), relid);
2360
2362
2363 switch (form->partstrat)
2364 {
2366 if (!attrsOnly)
2367 appendStringInfoString(&buf, "HASH");
2368 break;
2370 if (!attrsOnly)
2371 appendStringInfoString(&buf, "LIST");
2372 break;
2374 if (!attrsOnly)
2375 appendStringInfoString(&buf, "RANGE");
2376 break;
2377 default:
2378 elog(ERROR, "unexpected partition strategy: %d",
2379 (int) form->partstrat);
2380 }
2381
2382 if (!attrsOnly)
2384 sep = "";
2385 for (keyno = 0; keyno < form->partnatts; keyno++)
2386 {
2387 AttrNumber attnum = form->partattrs.values[keyno];
2390 Oid partcoll;
2391
2393 sep = ", ";
2394 if (attnum != 0)
2395 {
2396 /* Simple attribute reference */
2397 char *attname;
2399
2400 attname = get_attname(relid, attnum, false);
2405 }
2406 else
2407 {
2408 /* Expression */
2409 Node *partkey;
2410
2411 if (partexpr_item == NULL)
2412 elog(ERROR, "too few entries in partexprs list");
2414 partexpr_item = lnext(partexprs, partexpr_item);
2415
2416 /* Deparse */
2417 str = deparse_expression_pretty(partkey, context, false, false,
2418 prettyFlags, 0);
2419 /* Need parens if it's not a bare function call */
2422 else
2423 appendStringInfo(&buf, "(%s)", str);
2424
2427 }
2428
2429 /* Add collation, if not default for column */
2430 partcoll = partcollation->values[keyno];
2432 appendStringInfo(&buf, " COLLATE %s",
2434
2435 /* Add the operator class name, if not default */
2436 if (!attrsOnly)
2437 get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2438 }
2439
2440 if (!attrsOnly)
2442
2443 /* Clean up */
2444 ReleaseSysCache(tuple);
2445
2446 return buf.data;
2447}
2448
2449/*
2450 * pg_get_partition_constraintdef
2451 *
2452 * Returns partition constraint expression as a string for the input relation
2453 */
2454Datum
2456{
2459 int prettyFlags;
2460 List *context;
2461 char *consrc;
2462
2464
2465 /* Quick exit if no partition constraint */
2466 if (constr_expr == NULL)
2468
2469 /*
2470 * Deparse and return the constraint expression.
2471 */
2472 prettyFlags = PRETTYFLAG_INDENT;
2474 consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2475 false, prettyFlags, 0);
2476
2478}
2479
2480/*
2481 * pg_get_partconstrdef_string
2482 *
2483 * Returns the partition constraint as a C-string for the input relation, with
2484 * the given alias. No pretty-printing.
2485 */
2486char *
2488{
2490 List *context;
2491
2493 context = deparse_context_for(aliasname, partitionId);
2494
2495 return deparse_expression((Node *) constr_expr, context, true, false);
2496}
2497
2498/*
2499 * pg_get_constraintdef
2500 *
2501 * Returns the definition for the constraint, ie, everything that needs to
2502 * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
2503 */
2504Datum
2506{
2508 int prettyFlags;
2509 char *res;
2510
2511 prettyFlags = PRETTYFLAG_INDENT;
2512
2513 res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2514
2515 if (res == NULL)
2517
2519}
2520
2521Datum
2523{
2525 bool pretty = PG_GETARG_BOOL(1);
2526 int prettyFlags;
2527 char *res;
2528
2529 prettyFlags = GET_PRETTY_FLAGS(pretty);
2530
2531 res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2532
2533 if (res == NULL)
2535
2537}
2538
2539/*
2540 * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
2541 */
2542char *
2544{
2545 return pg_get_constraintdef_worker(constraintId, true, 0, false);
2546}
2547
2548/*
2549 * As of 9.4, we now use an MVCC snapshot for this.
2550 */
2551static char *
2553 int prettyFlags, bool missing_ok)
2554{
2555 HeapTuple tup;
2558 SysScanDesc scandesc;
2562
2563 ScanKeyInit(&scankey[0],
2567
2568 scandesc = systable_beginscan(relation,
2570 true,
2571 snapshot,
2572 1,
2573 scankey);
2574
2575 /*
2576 * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2577 * via SearchSysCache, which works fine.
2578 */
2579 tup = systable_getnext(scandesc);
2580
2581 UnregisterSnapshot(snapshot);
2582
2583 if (!HeapTupleIsValid(tup))
2584 {
2585 if (missing_ok)
2586 {
2587 systable_endscan(scandesc);
2588 table_close(relation, AccessShareLock);
2589 return NULL;
2590 }
2591 elog(ERROR, "could not find tuple for constraint %u", constraintId);
2592 }
2593
2595
2597
2598 if (fullCommand)
2599 {
2600 if (OidIsValid(conForm->conrelid))
2601 {
2602 /*
2603 * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2604 * constraints, and other types of constraints don't inherit
2605 * anyway so it doesn't matter whether we say ONLY or not. Someday
2606 * we might need to let callers specify whether to put ONLY in the
2607 * command.
2608 */
2609 appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2611 quote_identifier(NameStr(conForm->conname)));
2612 }
2613 else
2614 {
2615 /* Must be a domain constraint */
2616 Assert(OidIsValid(conForm->contypid));
2617 appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2619 quote_identifier(NameStr(conForm->conname)));
2620 }
2621 }
2622
2623 switch (conForm->contype)
2624 {
2625 case CONSTRAINT_FOREIGN:
2626 {
2627 Datum val;
2628 bool isnull;
2629 const char *string;
2630
2631 /* Start off the constraint definition */
2632 appendStringInfoString(&buf, "FOREIGN KEY (");
2633
2634 /* Fetch and build referencing-column list */
2637
2638 /* If it is a temporal foreign key then it uses PERIOD. */
2639 decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
2640
2641 /* add foreign relation name */
2642 appendStringInfo(&buf, ") REFERENCES %s(",
2644 NIL));
2645
2646 /* Fetch and build referenced-column list */
2649
2650 decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
2651
2653
2654 /* Add match type */
2655 switch (conForm->confmatchtype)
2656 {
2658 string = " MATCH FULL";
2659 break;
2661 string = " MATCH PARTIAL";
2662 break;
2664 string = "";
2665 break;
2666 default:
2667 elog(ERROR, "unrecognized confmatchtype: %d",
2668 conForm->confmatchtype);
2669 string = ""; /* keep compiler quiet */
2670 break;
2671 }
2672 appendStringInfoString(&buf, string);
2673
2674 /* Add ON UPDATE and ON DELETE clauses, if needed */
2675 switch (conForm->confupdtype)
2676 {
2678 string = NULL; /* suppress default */
2679 break;
2681 string = "RESTRICT";
2682 break;
2684 string = "CASCADE";
2685 break;
2687 string = "SET NULL";
2688 break;
2690 string = "SET DEFAULT";
2691 break;
2692 default:
2693 elog(ERROR, "unrecognized confupdtype: %d",
2694 conForm->confupdtype);
2695 string = NULL; /* keep compiler quiet */
2696 break;
2697 }
2698 if (string)
2699 appendStringInfo(&buf, " ON UPDATE %s", string);
2700
2701 switch (conForm->confdeltype)
2702 {
2704 string = NULL; /* suppress default */
2705 break;
2707 string = "RESTRICT";
2708 break;
2710 string = "CASCADE";
2711 break;
2713 string = "SET NULL";
2714 break;
2716 string = "SET DEFAULT";
2717 break;
2718 default:
2719 elog(ERROR, "unrecognized confdeltype: %d",
2720 conForm->confdeltype);
2721 string = NULL; /* keep compiler quiet */
2722 break;
2723 }
2724 if (string)
2725 appendStringInfo(&buf, " ON DELETE %s", string);
2726
2727 /*
2728 * Add columns specified to SET NULL or SET DEFAULT if
2729 * provided.
2730 */
2733 if (!isnull)
2734 {
2736 decompile_column_index_array(val, conForm->conrelid, false, &buf);
2738 }
2739
2740 break;
2741 }
2742 case CONSTRAINT_PRIMARY:
2743 case CONSTRAINT_UNIQUE:
2744 {
2745 Datum val;
2746 Oid indexId;
2747 int keyatts;
2749
2750 /* Start off the constraint definition */
2751 if (conForm->contype == CONSTRAINT_PRIMARY)
2752 appendStringInfoString(&buf, "PRIMARY KEY ");
2753 else
2754 appendStringInfoString(&buf, "UNIQUE ");
2755
2756 indexId = conForm->conindid;
2757
2760 elog(ERROR, "cache lookup failed for index %u", indexId);
2761 if (conForm->contype == CONSTRAINT_UNIQUE &&
2762 ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
2763 appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2764
2766
2767 /* Fetch and build target column list */
2770
2771 keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
2772 if (conForm->conperiod)
2773 appendStringInfoString(&buf, " WITHOUT OVERLAPS");
2774
2776
2777 /* Build including column list (from pg_index.indkeys) */
2780 if (DatumGetInt32(val) > keyatts)
2781 {
2782 Datum cols;
2783 Datum *keys;
2784 int nKeys;
2785 int j;
2786
2787 appendStringInfoString(&buf, " INCLUDE (");
2788
2791
2793 &keys, NULL, &nKeys);
2794
2795 for (j = keyatts; j < nKeys; j++)
2796 {
2797 char *colName;
2798
2799 colName = get_attname(conForm->conrelid,
2800 DatumGetInt16(keys[j]), false);
2801 if (j > keyatts)
2804 }
2805
2807 }
2809
2810 /* XXX why do we only print these bits if fullCommand? */
2812 {
2814 Oid tblspc;
2815
2816 if (options)
2817 {
2818 appendStringInfo(&buf, " WITH (%s)", options);
2819 pfree(options);
2820 }
2821
2822 /*
2823 * Print the tablespace, unless it's the database default.
2824 * This is to help ALTER TABLE usage of this facility,
2825 * which needs this behavior to recreate exact catalog
2826 * state.
2827 */
2829 if (OidIsValid(tblspc))
2830 appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2832 }
2833
2834 break;
2835 }
2836 case CONSTRAINT_CHECK:
2837 {
2838 Datum val;
2839 char *conbin;
2840 char *consrc;
2841 Node *expr;
2842 List *context;
2843
2844 /* Fetch constraint expression in parsetree form */
2847
2849 expr = stringToNode(conbin);
2850
2851 /* Set up deparsing context for Var nodes in constraint */
2852 if (conForm->conrelid != InvalidOid)
2853 {
2854 /* relation constraint */
2855 context = deparse_context_for(get_relation_name(conForm->conrelid),
2856 conForm->conrelid);
2857 }
2858 else
2859 {
2860 /* domain constraint --- can't have Vars */
2861 context = NIL;
2862 }
2863
2864 consrc = deparse_expression_pretty(expr, context, false, false,
2865 prettyFlags, 0);
2866
2867 /*
2868 * Now emit the constraint definition, adding NO INHERIT if
2869 * necessary.
2870 *
2871 * There are cases where the constraint expression will be
2872 * fully parenthesized and we don't need the outer parens ...
2873 * but there are other cases where we do need 'em. Be
2874 * conservative for now.
2875 *
2876 * Note that simply checking for leading '(' and trailing ')'
2877 * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2878 */
2879 appendStringInfo(&buf, "CHECK (%s)%s",
2880 consrc,
2881 conForm->connoinherit ? " NO INHERIT" : "");
2882 break;
2883 }
2884 case CONSTRAINT_NOTNULL:
2885 {
2886 if (conForm->conrelid)
2887 {
2889
2891
2892 appendStringInfo(&buf, "NOT NULL %s",
2894 attnum, false)));
2896 appendStringInfoString(&buf, " NO INHERIT");
2897 }
2898 else if (conForm->contypid)
2899 {
2900 /* conkey is null for domain not-null constraints */
2901 appendStringInfoString(&buf, "NOT NULL");
2902 }
2903 break;
2904 }
2905
2906 case CONSTRAINT_TRIGGER:
2907
2908 /*
2909 * There isn't an ALTER TABLE syntax for creating a user-defined
2910 * constraint trigger, but it seems better to print something than
2911 * throw an error; if we throw error then this function couldn't
2912 * safely be applied to all rows of pg_constraint.
2913 */
2914 appendStringInfoString(&buf, "TRIGGER");
2915 break;
2917 {
2918 Oid indexOid = conForm->conindid;
2919 Datum val;
2920 Datum *elems;
2921 int nElems;
2922 int i;
2923 Oid *operators;
2924
2925 /* Extract operator OIDs from the pg_constraint tuple */
2928
2930 &elems, NULL, &nElems);
2931
2932 operators = (Oid *) palloc(nElems * sizeof(Oid));
2933 for (i = 0; i < nElems; i++)
2934 operators[i] = DatumGetObjectId(elems[i]);
2935
2936 /* pg_get_indexdef_worker does the rest */
2937 /* suppress tablespace because pg_dump wants it that way */
2939 pg_get_indexdef_worker(indexOid,
2940 0,
2941 operators,
2942 false,
2943 false,
2944 false,
2945 false,
2946 prettyFlags,
2947 false));
2948 break;
2949 }
2950 default:
2951 elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2952 break;
2953 }
2954
2955 if (conForm->condeferrable)
2956 appendStringInfoString(&buf, " DEFERRABLE");
2957 if (conForm->condeferred)
2958 appendStringInfoString(&buf, " INITIALLY DEFERRED");
2959
2960 /* Validated status is irrelevant when the constraint is NOT ENFORCED. */
2961 if (!conForm->conenforced)
2962 appendStringInfoString(&buf, " NOT ENFORCED");
2963 else if (!conForm->convalidated)
2964 appendStringInfoString(&buf, " NOT VALID");
2965
2966 /* Cleanup */
2967 systable_endscan(scandesc);
2968 table_close(relation, AccessShareLock);
2969
2970 return buf.data;
2971}
2972
2973
2974/*
2975 * Convert an int16[] Datum into a comma-separated list of column names
2976 * for the indicated relation; append the list to buf. Returns the number
2977 * of keys.
2978 */
2979static int
2982{
2983 Datum *keys;
2984 int nKeys;
2985 int j;
2986
2987 /* Extract data from array of int16 */
2989 &keys, NULL, &nKeys);
2990
2991 for (j = 0; j < nKeys; j++)
2992 {
2993 char *colName;
2994
2995 colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2996
2997 if (j == 0)
2999 else
3000 appendStringInfo(buf, ", %s%s",
3001 (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
3003 }
3004
3005 return nKeys;
3006}
3007
3008
3009/* ----------
3010 * pg_get_expr - Decompile an expression tree
3011 *
3012 * Input: an expression tree in nodeToString form, and a relation OID
3013 *
3014 * Output: reverse-listed expression
3015 *
3016 * Currently, the expression can only refer to a single relation, namely
3017 * the one specified by the second parameter. This is sufficient for
3018 * partial indexes, column default expressions, etc. We also support
3019 * Var-free expressions, for which the OID can be InvalidOid.
3020 *
3021 * If the OID is nonzero but not actually valid, don't throw an error,
3022 * just return NULL. This is a bit questionable, but it's what we've
3023 * done historically, and it can help avoid unwanted failures when
3024 * examining catalog entries for just-deleted relations.
3025 *
3026 * We expect this function to work, or throw a reasonably clean error,
3027 * for any node tree that can appear in a catalog pg_node_tree column.
3028 * Query trees, such as those appearing in pg_rewrite.ev_action, are
3029 * not supported. Nor are expressions in more than one relation, which
3030 * can appear in places like pg_rewrite.ev_qual.
3031 * ----------
3032 */
3033Datum
3035{
3036 text *expr = PG_GETARG_TEXT_PP(0);
3037 Oid relid = PG_GETARG_OID(1);
3038 text *result;
3039 int prettyFlags;
3040
3041 prettyFlags = PRETTYFLAG_INDENT;
3042
3043 result = pg_get_expr_worker(expr, relid, prettyFlags);
3044 if (result)
3046 else
3048}
3049
3050Datum
3052{
3053 text *expr = PG_GETARG_TEXT_PP(0);
3054 Oid relid = PG_GETARG_OID(1);
3055 bool pretty = PG_GETARG_BOOL(2);
3056 text *result;
3057 int prettyFlags;
3058
3059 prettyFlags = GET_PRETTY_FLAGS(pretty);
3060
3061 result = pg_get_expr_worker(expr, relid, prettyFlags);
3062 if (result)
3064 else
3066}
3067
3068static text *
3069pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
3070{
3071 Node *node;
3072 Node *tst;
3073 Relids relids;
3074 List *context;
3075 char *exprstr;
3076 Relation rel = NULL;
3077 char *str;
3078
3079 /* Convert input pg_node_tree (really TEXT) object to C string */
3080 exprstr = text_to_cstring(expr);
3081
3082 /* Convert expression to node tree */
3083 node = (Node *) stringToNode(exprstr);
3084
3085 pfree(exprstr);
3086
3087 /*
3088 * Throw error if the input is a querytree rather than an expression tree.
3089 * While we could support queries here, there seems no very good reason
3090 * to. In most such catalog columns, we'll see a List of Query nodes, or
3091 * even nested Lists, so drill down to a non-List node before checking.
3092 */
3093 tst = node;
3094 while (tst && IsA(tst, List))
3095 tst = linitial((List *) tst);
3096 if (tst && IsA(tst, Query))
3097 ereport(ERROR,
3099 errmsg("input is a query, not an expression")));
3100
3101 /*
3102 * Throw error if the expression contains Vars we won't be able to
3103 * deparse.
3104 */
3105 relids = pull_varnos(NULL, node);
3106 if (OidIsValid(relid))
3107 {
3108 if (!bms_is_subset(relids, bms_make_singleton(1)))
3109 ereport(ERROR,
3111 errmsg("expression contains variables of more than one relation")));
3112 }
3113 else
3114 {
3115 if (!bms_is_empty(relids))
3116 ereport(ERROR,
3118 errmsg("expression contains variables")));
3119 }
3120
3121 /*
3122 * Prepare deparse context if needed. If we are deparsing with a relid,
3123 * we need to transiently open and lock the rel, to make sure it won't go
3124 * away underneath us. (set_relation_column_names would lock it anyway,
3125 * so this isn't really introducing any new behavior.)
3126 */
3127 if (OidIsValid(relid))
3128 {
3129 rel = try_relation_open(relid, AccessShareLock);
3130 if (rel == NULL)
3131 return NULL;
3132 context = deparse_context_for(RelationGetRelationName(rel), relid);
3133 }
3134 else
3135 context = NIL;
3136
3137 /* Deparse */
3138 str = deparse_expression_pretty(node, context, false, false,
3139 prettyFlags, 0);
3140
3141 if (rel != NULL)
3143
3144 return string_to_text(str);
3145}
3146
3147
3148/* ----------
3149 * pg_get_userbyid - Get a user name by roleid and
3150 * fallback to 'unknown (OID=n)'
3151 * ----------
3152 */
3153Datum
3155{
3156 Oid roleid = PG_GETARG_OID(0);
3157 Name result;
3160
3161 /*
3162 * Allocate space for the result
3163 */
3166
3167 /*
3168 * Get the pg_authid entry and print the result
3169 */
3172 {
3174 *result = role_rec->rolname;
3176 }
3177 else
3178 sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
3179
3181}
3182
3183
3184/*
3185 * pg_get_serial_sequence
3186 * Get the name of the sequence used by an identity or serial column,
3187 * formatted suitably for passing to setval, nextval or currval.
3188 * First parameter is not treated as double-quoted, second parameter
3189 * is --- see documentation for reason.
3190 */
3191Datum
3193{
3194 text *tablename = PG_GETARG_TEXT_PP(0);
3197 Oid tableOid;
3198 char *column;
3202 ScanKeyData key[3];
3203 SysScanDesc scan;
3204 HeapTuple tup;
3205
3206 /* Look up table name. Can't lock it - we might not have privileges. */
3208 tableOid = RangeVarGetRelid(tablerv, NoLock, false);
3209
3210 /* Get the number of the column */
3212
3213 attnum = get_attnum(tableOid, column);
3215 ereport(ERROR,
3217 errmsg("column \"%s\" of relation \"%s\" does not exist",
3218 column, tablerv->relname)));
3219
3220 /* Search the dependency table for the dependent sequence */
3222
3223 ScanKeyInit(&key[0],
3227 ScanKeyInit(&key[1],
3230 ObjectIdGetDatum(tableOid));
3231 ScanKeyInit(&key[2],
3235
3237 NULL, 3, key);
3238
3239 while (HeapTupleIsValid(tup = systable_getnext(scan)))
3240 {
3242
3243 /*
3244 * Look for an auto dependency (serial column) or internal dependency
3245 * (identity column) of a sequence on a column. (We need the relkind
3246 * test because indexes can also have auto dependencies on columns.)
3247 */
3248 if (deprec->classid == RelationRelationId &&
3249 deprec->objsubid == 0 &&
3250 (deprec->deptype == DEPENDENCY_AUTO ||
3251 deprec->deptype == DEPENDENCY_INTERNAL) &&
3253 {
3254 sequenceId = deprec->objid;
3255 break;
3256 }
3257 }
3258
3259 systable_endscan(scan);
3261
3263 {
3264 char *result;
3265
3267
3269 }
3270
3272}
3273
3274
3275/*
3276 * pg_get_functiondef
3277 * Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
3278 * the specified function.
3279 *
3280 * Note: if you change the output format of this function, be careful not
3281 * to break psql's rules (in \ef and \sf) for identifying the start of the
3282 * function body. To wit: the function body starts on a line that begins with
3283 * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
3284 */
3285Datum
3287{
3288 Oid funcid = PG_GETARG_OID(0);
3292 Form_pg_proc proc;
3293 bool isfunction;
3294 Datum tmp;
3295 bool isnull;
3296 const char *prosrc;
3297 const char *name;
3298 const char *nsp;
3300 int oldlen;
3301
3303
3304 /* Look up the function */
3308
3309 proc = (Form_pg_proc) GETSTRUCT(proctup);
3310 name = NameStr(proc->proname);
3311
3312 if (proc->prokind == PROKIND_AGGREGATE)
3313 ereport(ERROR,
3315 errmsg("\"%s\" is an aggregate function", name)));
3316
3317 isfunction = (proc->prokind != PROKIND_PROCEDURE);
3318
3319 /*
3320 * We always qualify the function name, to ensure the right function gets
3321 * replaced.
3322 */
3323 nsp = get_namespace_name_or_temp(proc->pronamespace);
3324 appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
3325 isfunction ? "FUNCTION" : "PROCEDURE",
3327 (void) print_function_arguments(&buf, proctup, false, true);
3328 appendStringInfoString(&buf, ")\n");
3329 if (isfunction)
3330 {
3331 appendStringInfoString(&buf, " RETURNS ");
3333 appendStringInfoChar(&buf, '\n');
3334 }
3335
3337
3338 appendStringInfo(&buf, " LANGUAGE %s\n",
3339 quote_identifier(get_language_name(proc->prolang, false)));
3340
3341 /* Emit some miscellaneous options on one line */
3342 oldlen = buf.len;
3343
3344 if (proc->prokind == PROKIND_WINDOW)
3345 appendStringInfoString(&buf, " WINDOW");
3346 switch (proc->provolatile)
3347 {
3349 appendStringInfoString(&buf, " IMMUTABLE");
3350 break;
3351 case PROVOLATILE_STABLE:
3352 appendStringInfoString(&buf, " STABLE");
3353 break;
3355 break;
3356 }
3357
3358 switch (proc->proparallel)
3359 {
3360 case PROPARALLEL_SAFE:
3361 appendStringInfoString(&buf, " PARALLEL SAFE");
3362 break;
3364 appendStringInfoString(&buf, " PARALLEL RESTRICTED");
3365 break;
3366 case PROPARALLEL_UNSAFE:
3367 break;
3368 }
3369
3370 if (proc->proisstrict)
3371 appendStringInfoString(&buf, " STRICT");
3372 if (proc->prosecdef)
3373 appendStringInfoString(&buf, " SECURITY DEFINER");
3374 if (proc->proleakproof)
3375 appendStringInfoString(&buf, " LEAKPROOF");
3376
3377 /* This code for the default cost and rows should match functioncmds.c */
3378 if (proc->prolang == INTERNALlanguageId ||
3379 proc->prolang == ClanguageId)
3380 procost = 1;
3381 else
3382 procost = 100;
3383 if (proc->procost != procost)
3384 appendStringInfo(&buf, " COST %g", proc->procost);
3385
3386 if (proc->prorows > 0 && proc->prorows != 1000)
3387 appendStringInfo(&buf, " ROWS %g", proc->prorows);
3388
3389 if (proc->prosupport)
3390 {
3391 Oid argtypes[1];
3392
3393 /*
3394 * We should qualify the support function's name if it wouldn't be
3395 * resolved by lookup in the current search path.
3396 */
3397 argtypes[0] = INTERNALOID;
3398 appendStringInfo(&buf, " SUPPORT %s",
3399 generate_function_name(proc->prosupport, 1,
3400 NIL, argtypes,
3401 false, NULL, false));
3402 }
3403
3404 if (oldlen != buf.len)
3405 appendStringInfoChar(&buf, '\n');
3406
3407 /* Emit any proconfig options, one per line */
3409 if (!isnull)
3410 {
3412 int i;
3413
3415 Assert(ARR_NDIM(a) == 1);
3416 Assert(ARR_LBOUND(a)[0] == 1);
3417
3418 for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3419 {
3420 Datum d;
3421
3422 d = array_ref(a, 1, &i,
3423 -1 /* varlenarray */ ,
3424 -1 /* TEXT's typlen */ ,
3425 false /* TEXT's typbyval */ ,
3426 TYPALIGN_INT /* TEXT's typalign */ ,
3427 &isnull);
3428 if (!isnull)
3429 {
3431 char *pos;
3432
3433 pos = strchr(configitem, '=');
3434 if (pos == NULL)
3435 continue;
3436 *pos++ = '\0';
3437
3438 appendStringInfo(&buf, " SET %s TO ",
3440
3441 /*
3442 * Variables that are marked GUC_LIST_QUOTE were already fully
3443 * quoted by flatten_set_variable_args() before they were put
3444 * into the proconfig array. However, because the quoting
3445 * rules used there aren't exactly like SQL's, we have to
3446 * break the list value apart and then quote the elements as
3447 * string literals. (The elements may be double-quoted as-is,
3448 * but we can't just feed them to the SQL parser; it would do
3449 * the wrong thing with elements that are zero-length or
3450 * longer than NAMEDATALEN.) Also, we need a special case for
3451 * empty lists.
3452 *
3453 * Variables that are not so marked should just be emitted as
3454 * simple string literals. If the variable is not known to
3455 * guc.c, we'll do that; this makes it unsafe to use
3456 * GUC_LIST_QUOTE for extension variables.
3457 */
3459 {
3460 List *namelist;
3461 ListCell *lc;
3462
3463 /* Parse string into list of identifiers */
3464 if (!SplitGUCList(pos, ',', &namelist))
3465 {
3466 /* this shouldn't fail really */
3467 elog(ERROR, "invalid list syntax in proconfig item");
3468 }
3469 /* Special case: represent an empty list as NULL */
3470 if (namelist == NIL)
3471 appendStringInfoString(&buf, "NULL");
3472 foreach(lc, namelist)
3473 {
3474 char *curname = (char *) lfirst(lc);
3475
3477 if (lnext(namelist, lc))
3479 }
3480 }
3481 else
3483 appendStringInfoChar(&buf, '\n');
3484 }
3485 }
3486 }
3487
3488 /* And finally the function definition ... */
3490 if (proc->prolang == SQLlanguageId && !isnull)
3491 {
3493 }
3494 else
3495 {
3496 appendStringInfoString(&buf, "AS ");
3497
3499 if (!isnull)
3500 {
3502 appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3503 }
3504
3506 prosrc = TextDatumGetCString(tmp);
3507
3508 /*
3509 * We always use dollar quoting. Figure out a suitable delimiter.
3510 *
3511 * Since the user is likely to be editing the function body string, we
3512 * shouldn't use a short delimiter that he might easily create a
3513 * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3514 * if needed.
3515 */
3517 appendStringInfoChar(&dq, '$');
3518 appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3519 while (strstr(prosrc, dq.data) != NULL)
3520 appendStringInfoChar(&dq, 'x');
3521 appendStringInfoChar(&dq, '$');
3522
3523 appendBinaryStringInfo(&buf, dq.data, dq.len);
3524 appendStringInfoString(&buf, prosrc);
3525 appendBinaryStringInfo(&buf, dq.data, dq.len);
3526 }
3527
3528 appendStringInfoChar(&buf, '\n');
3529
3531
3533}
3534
3535/*
3536 * pg_get_function_arguments
3537 * Get a nicely-formatted list of arguments for a function.
3538 * This is everything that would go between the parentheses in
3539 * CREATE FUNCTION.
3540 */
3541Datum
3543{
3544 Oid funcid = PG_GETARG_OID(0);
3547
3551
3553
3554 (void) print_function_arguments(&buf, proctup, false, true);
3555
3557
3559}
3560
3561/*
3562 * pg_get_function_identity_arguments
3563 * Get a formatted list of arguments for a function.
3564 * This is everything that would go between the parentheses in
3565 * ALTER FUNCTION, etc. In particular, don't print defaults.
3566 */
3567Datum
3569{
3570 Oid funcid = PG_GETARG_OID(0);
3573
3577
3579
3580 (void) print_function_arguments(&buf, proctup, false, false);
3581
3583
3585}
3586
3587/*
3588 * pg_get_function_result
3589 * Get a nicely-formatted version of the result type of a function.
3590 * This is what would appear after RETURNS in CREATE FUNCTION.
3591 */
3592Datum
3594{
3595 Oid funcid = PG_GETARG_OID(0);
3598
3602
3603 if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
3604 {
3607 }
3608
3610
3612
3614
3616}
3617
3618/*
3619 * Guts of pg_get_function_result: append the function's return type
3620 * to the specified buffer.
3621 */
3622static void
3624{
3626 int ntabargs = 0;
3628
3630
3631 if (proc->proretset)
3632 {
3633 /* It might be a table function; try to print the arguments */
3634 appendStringInfoString(&rbuf, "TABLE(");
3636 if (ntabargs > 0)
3638 else
3640 }
3641
3642 if (ntabargs == 0)
3643 {
3644 /* Not a table function, so do the normal thing */
3645 if (proc->proretset)
3646 appendStringInfoString(&rbuf, "SETOF ");
3647 appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3648 }
3649
3650 appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
3651}
3652
3653/*
3654 * Common code for pg_get_function_arguments and pg_get_function_result:
3655 * append the desired subset of arguments to buf. We print only TABLE
3656 * arguments when print_table_args is true, and all the others when it's false.
3657 * We print argument defaults only if print_defaults is true.
3658 * Function return value is the number of arguments printed.
3659 */
3660static int
3663{
3665 int numargs;
3666 Oid *argtypes;
3667 char **argnames;
3668 char *argmodes;
3669 int insertorderbyat = -1;
3670 int argsprinted;
3671 int inputargno;
3672 int nlackdefaults;
3673 List *argdefaults = NIL;
3675 int i;
3676
3677 numargs = get_func_arg_info(proctup,
3678 &argtypes, &argnames, &argmodes);
3679
3680 nlackdefaults = numargs;
3681 if (print_defaults && proc->pronargdefaults > 0)
3682 {
3684 bool isnull;
3685
3688 &isnull);
3689 if (!isnull)
3690 {
3691 char *str;
3692
3695 pfree(str);
3697 /* nlackdefaults counts only *input* arguments lacking defaults */
3698 nlackdefaults = proc->pronargs - list_length(argdefaults);
3699 }
3700 }
3701
3702 /* Check for special treatment of ordered-set aggregates */
3703 if (proc->prokind == PROKIND_AGGREGATE)
3704 {
3707
3710 elog(ERROR, "cache lookup failed for aggregate %u",
3711 proc->oid);
3713 if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3714 insertorderbyat = agg->aggnumdirectargs;
3716 }
3717
3718 argsprinted = 0;
3719 inputargno = 0;
3720 for (i = 0; i < numargs; i++)
3721 {
3722 Oid argtype = argtypes[i];
3723 char *argname = argnames ? argnames[i] : NULL;
3725 const char *modename;
3726 bool isinput;
3727
3728 switch (argmode)
3729 {
3730 case PROARGMODE_IN:
3731
3732 /*
3733 * For procedures, explicitly mark all argument modes, so as
3734 * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3735 */
3736 if (proc->prokind == PROKIND_PROCEDURE)
3737 modename = "IN ";
3738 else
3739 modename = "";
3740 isinput = true;
3741 break;
3742 case PROARGMODE_INOUT:
3743 modename = "INOUT ";
3744 isinput = true;
3745 break;
3746 case PROARGMODE_OUT:
3747 modename = "OUT ";
3748 isinput = false;
3749 break;
3751 modename = "VARIADIC ";
3752 isinput = true;
3753 break;
3754 case PROARGMODE_TABLE:
3755 modename = "";
3756 isinput = false;
3757 break;
3758 default:
3759 elog(ERROR, "invalid parameter mode '%c'", argmode);
3760 modename = NULL; /* keep compiler quiet */
3761 isinput = false;
3762 break;
3763 }
3764 if (isinput)
3765 inputargno++; /* this is a 1-based counter */
3766
3768 continue;
3769
3771 {
3772 if (argsprinted)
3774 appendStringInfoString(buf, "ORDER BY ");
3775 }
3776 else if (argsprinted)
3778
3780 if (argname && argname[0])
3781 appendStringInfo(buf, "%s ", quote_identifier(argname));
3784 {
3785 Node *expr;
3786
3788 expr = (Node *) lfirst(nextargdefault);
3790
3791 appendStringInfo(buf, " DEFAULT %s",
3792 deparse_expression(expr, NIL, false, false));
3793 }
3794 argsprinted++;
3795
3796 /* nasty hack: print the last arg twice for variadic ordered-set agg */
3797 if (argsprinted == insertorderbyat && i == numargs - 1)
3798 {
3799 i--;
3800 /* aggs shouldn't have defaults anyway, but just to be sure ... */
3801 print_defaults = false;
3802 }
3803 }
3804
3805 return argsprinted;
3806}
3807
3808static bool
3809is_input_argument(int nth, const char *argmodes)
3810{
3811 return (!argmodes
3815}
3816
3817/*
3818 * Append used transformed types to specified buffer
3819 */
3820static void
3822{
3823 Oid *trftypes;
3824 int ntypes;
3825
3826 ntypes = get_func_trftypes(proctup, &trftypes);
3827 if (ntypes > 0)
3828 {
3829 int i;
3830
3831 appendStringInfoString(buf, " TRANSFORM ");
3832 for (i = 0; i < ntypes; i++)
3833 {
3834 if (i != 0)
3836 appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3837 }
3839 }
3840}
3841
3842/*
3843 * Get textual representation of a function argument's default value. The
3844 * second argument of this function is the argument number among all arguments
3845 * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
3846 * how information_schema.sql uses it.
3847 */
3848Datum
3850{
3851 Oid funcid = PG_GETARG_OID(0);
3854 Form_pg_proc proc;
3855 int numargs;
3856 Oid *argtypes;
3857 char **argnames;
3858 char *argmodes;
3859 int i;
3861 Node *node;
3862 char *str;
3863 int nth_inputarg;
3865 bool isnull;
3866 int nth_default;
3867
3871
3872 numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3874 {
3877 }
3878
3879 nth_inputarg = 0;
3880 for (i = 0; i < nth_arg; i++)
3882 nth_inputarg++;
3883
3886 &isnull);
3887 if (isnull)
3888 {
3891 }
3892
3895 pfree(str);
3896
3897 proc = (Form_pg_proc) GETSTRUCT(proctup);
3898
3899 /*
3900 * Calculate index into proargdefaults: proargdefaults corresponds to the
3901 * last N input arguments, where N = pronargdefaults.
3902 */
3903 nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3904
3906 {
3909 }
3911 str = deparse_expression(node, NIL, false, false);
3912
3914
3916}
3917
3918static void
3920{
3921 int numargs;
3922 Oid *argtypes;
3923 char **argnames;
3924 char *argmodes;
3925 deparse_namespace dpns = {0};
3926 Datum tmp;
3927 Node *n;
3928
3930 numargs = get_func_arg_info(proctup,
3931 &argtypes, &argnames, &argmodes);
3932 dpns.numargs = numargs;
3933 dpns.argnames = argnames;
3934
3937
3938 if (IsA(n, List))
3939 {
3940 List *stmts;
3941 ListCell *lc;
3942
3943 stmts = linitial(castNode(List, n));
3944
3945 appendStringInfoString(buf, "BEGIN ATOMIC\n");
3946
3947 foreach(lc, stmts)
3948 {
3949 Query *query = lfirst_node(Query, lc);
3950
3951 /* It seems advisable to get at least AccessShareLock on rels */
3952 AcquireRewriteLocks(query, false, false);
3953 get_query_def(query, buf, list_make1(&dpns), NULL, false,
3957 }
3958
3960 }
3961 else
3962 {
3963 Query *query = castNode(Query, n);
3964
3965 /* It seems advisable to get at least AccessShareLock on rels */
3966 AcquireRewriteLocks(query, false, false);
3967 get_query_def(query, buf, list_make1(&dpns), NULL, false,
3968 0, WRAP_COLUMN_DEFAULT, 0);
3969 }
3970}
3971
3972Datum
3974{
3975 Oid funcid = PG_GETARG_OID(0);
3978 bool isnull;
3979
3981
3982 /* Look up the function */
3986
3988 if (isnull)
3989 {
3992 }
3993
3995
3997
3999}
4000
4001
4002/*
4003 * deparse_expression - General utility for deparsing expressions
4004 *
4005 * calls deparse_expression_pretty with all prettyPrinting disabled
4006 */
4007char *
4009 bool forceprefix, bool showimplicit)
4010{
4012 showimplicit, 0, 0);
4013}
4014
4015/* ----------
4016 * deparse_expression_pretty - General utility for deparsing expressions
4017 *
4018 * expr is the node tree to be deparsed. It must be a transformed expression
4019 * tree (ie, not the raw output of gram.y).
4020 *
4021 * dpcontext is a list of deparse_namespace nodes representing the context
4022 * for interpreting Vars in the node tree. It can be NIL if no Vars are
4023 * expected.
4024 *
4025 * forceprefix is true to force all Vars to be prefixed with their table names.
4026 *
4027 * showimplicit is true to force all implicit casts to be shown explicitly.
4028 *
4029 * Tries to pretty up the output according to prettyFlags and startIndent.
4030 *
4031 * The result is a palloc'd string.
4032 * ----------
4033 */
4034static char *
4036 bool forceprefix, bool showimplicit,
4037 int prettyFlags, int startIndent)
4038{
4040 deparse_context context;
4041
4043 context.buf = &buf;
4044 context.namespaces = dpcontext;
4045 context.resultDesc = NULL;
4046 context.targetList = NIL;
4047 context.windowClause = NIL;
4048 context.varprefix = forceprefix;
4049 context.prettyFlags = prettyFlags;
4051 context.indentLevel = startIndent;
4052 context.colNamesVisible = true;
4053 context.inGroupBy = false;
4054 context.varInOrderBy = false;
4055 context.appendparents = NULL;
4056
4057 get_rule_expr(expr, &context, showimplicit);
4058
4059 return buf.data;
4060}
4061
4062/* ----------
4063 * deparse_context_for - Build deparse context for a single relation
4064 *
4065 * Given the reference name (alias) and OID of a relation, build deparsing
4066 * context for an expression referencing only that relation (as varno 1,
4067 * varlevelsup 0). This is sufficient for many uses of deparse_expression.
4068 * ----------
4069 */
4070List *
4071deparse_context_for(const char *aliasname, Oid relid)
4072{
4075
4077
4078 /* Build a minimal RTE for the rel */
4080 rte->rtekind = RTE_RELATION;
4081 rte->relid = relid;
4082 rte->relkind = RELKIND_RELATION; /* no need for exactness here */
4083 rte->rellockmode = AccessShareLock;
4084 rte->alias = makeAlias(aliasname, NIL);
4085 rte->eref = rte->alias;
4086 rte->lateral = false;
4087 rte->inh = false;
4088 rte->inFromCl = true;
4089
4090 /* Build one-element rtable */
4091 dpns->rtable = list_make1(rte);
4092 dpns->subplans = NIL;
4093 dpns->ctes = NIL;
4094 dpns->appendrels = NULL;
4097
4098 /* Return a one-deep namespace stack */
4099 return list_make1(dpns);
4100}
4101
4102/*
4103 * deparse_context_for_plan_tree - Build deparse context for a Plan tree
4104 *
4105 * When deparsing an expression in a Plan tree, we use the plan's rangetable
4106 * to resolve names of simple Vars. The initialization of column names for
4107 * this is rather expensive if the rangetable is large, and it'll be the same
4108 * for every expression in the Plan tree; so we do it just once and re-use
4109 * the result of this function for each expression. (Note that the result
4110 * is not usable until set_deparse_context_plan() is applied to it.)
4111 *
4112 * In addition to the PlannedStmt, pass the per-RTE alias names
4113 * assigned by a previous call to select_rtable_names_for_explain.
4114 */
4115List *
4116deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
4117{
4119
4121
4122 /* Initialize fields that stay the same across the whole plan tree */
4123 dpns->rtable = pstmt->rtable;
4124 dpns->rtable_names = rtable_names;
4125 dpns->subplans = pstmt->subplans;
4126 dpns->ctes = NIL;
4127 if (pstmt->appendRelations)
4128 {
4129 /* Set up the array, indexed by child relid */
4130 int ntables = list_length(dpns->rtable);
4131 ListCell *lc;
4132
4133 dpns->appendrels = (AppendRelInfo **)
4134 palloc0((ntables + 1) * sizeof(AppendRelInfo *));
4135 foreach(lc, pstmt->appendRelations)
4136 {
4138 Index crelid = appinfo->child_relid;
4139
4140 Assert(crelid > 0 && crelid <= ntables);
4141 Assert(dpns->appendrels[crelid] == NULL);
4142 dpns->appendrels[crelid] = appinfo;
4143 }
4144 }
4145 else
4146 dpns->appendrels = NULL; /* don't need it */
4147
4148 /*
4149 * Set up column name aliases, ignoring any join RTEs; they don't matter
4150 * because plan trees don't contain any join alias Vars.
4151 */
4153
4154 /* Return a one-deep namespace stack */
4155 return list_make1(dpns);
4156}
4157
4158/*
4159 * set_deparse_context_plan - Specify Plan node containing expression
4160 *
4161 * When deparsing an expression in a Plan tree, we might have to resolve
4162 * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
4163 * provide the parent Plan node. Then OUTER_VAR and INNER_VAR references
4164 * can be resolved by drilling down into the left and right child plans.
4165 * Similarly, INDEX_VAR references can be resolved by reference to the
4166 * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
4167 * ForeignScan and CustomScan nodes. (Note that we don't currently support
4168 * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
4169 * for those, we can only deparse the indexqualorig fields, which won't
4170 * contain INDEX_VAR Vars.)
4171 *
4172 * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
4173 * the most-closely-nested first. This is needed to resolve PARAM_EXEC
4174 * Params. Note we assume that all the Plan nodes share the same rtable.
4175 *
4176 * For a ModifyTable plan, we might also need to resolve references to OLD/NEW
4177 * variables in the RETURNING list, so we copy the alias names of the OLD and
4178 * NEW rows from the ModifyTable plan node.
4179 *
4180 * Once this function has been called, deparse_expression() can be called on
4181 * subsidiary expression(s) of the specified Plan node. To deparse
4182 * expressions of a different Plan node in the same Plan tree, re-call this
4183 * function to identify the new parent Plan node.
4184 *
4185 * The result is the same List passed in; this is a notational convenience.
4186 */
4187List *
4189{
4191
4192 /* Should always have one-entry namespace list for Plan deparsing */
4195
4196 /* Set our attention on the specific plan node passed in */
4197 dpns->ancestors = ancestors;
4199
4200 /* For ModifyTable, set aliases for OLD and NEW in RETURNING */
4201 if (IsA(plan, ModifyTable))
4202 {
4203 dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias;
4204 dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias;
4205 }
4206
4207 return dpcontext;
4208}
4209
4210/*
4211 * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
4212 *
4213 * Determine the relation aliases we'll use during an EXPLAIN operation.
4214 * This is just a frontend to set_rtable_names. We have to expose the aliases
4215 * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
4216 */
4217List *
4219{
4221
4222 memset(&dpns, 0, sizeof(dpns));
4223 dpns.rtable = rtable;
4224 dpns.subplans = NIL;
4225 dpns.ctes = NIL;
4226 dpns.appendrels = NULL;
4228 /* We needn't bother computing column aliases yet */
4229
4230 return dpns.rtable_names;
4231}
4232
4233/*
4234 * set_rtable_names: select RTE aliases to be used in printing a query
4235 *
4236 * We fill in dpns->rtable_names with a list of names that is one-for-one with
4237 * the already-filled dpns->rtable list. Each RTE name is unique among those
4238 * in the new namespace plus any ancestor namespaces listed in
4239 * parent_namespaces.
4240 *
4241 * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
4242 *
4243 * Note that this function is only concerned with relation names, not column
4244 * names.
4245 */
4246static void
4249{
4251 HTAB *names_hash;
4253 bool found;
4254 int rtindex;
4255 ListCell *lc;
4256
4257 dpns->rtable_names = NIL;
4258 /* nothing more to do if empty rtable */
4259 if (dpns->rtable == NIL)
4260 return;
4261
4262 /*
4263 * We use a hash table to hold known names, so that this process is O(N)
4264 * not O(N^2) for N names.
4265 */
4266 hash_ctl.keysize = NAMEDATALEN;
4267 hash_ctl.entrysize = sizeof(NameHashEntry);
4269 names_hash = hash_create("set_rtable_names names",
4270 list_length(dpns->rtable),
4271 &hash_ctl,
4273
4274 /* Preload the hash table with names appearing in parent_namespaces */
4275 foreach(lc, parent_namespaces)
4276 {
4278 ListCell *lc2;
4279
4280 foreach(lc2, olddpns->rtable_names)
4281 {
4282 char *oldname = (char *) lfirst(lc2);
4283
4284 if (oldname == NULL)
4285 continue;
4286 hentry = (NameHashEntry *) hash_search(names_hash,
4287 oldname,
4288 HASH_ENTER,
4289 &found);
4290 /* we do not complain about duplicate names in parent namespaces */
4291 hentry->counter = 0;
4292 }
4293 }
4294
4295 /* Now we can scan the rtable */
4296 rtindex = 1;
4297 foreach(lc, dpns->rtable)
4298 {
4300 char *refname;
4301
4302 /* Just in case this takes an unreasonable amount of time ... */
4304
4305 if (rels_used && !bms_is_member(rtindex, rels_used))
4306 {
4307 /* Ignore unreferenced RTE */
4308 refname = NULL;
4309 }
4310 else if (rte->alias)
4311 {
4312 /* If RTE has a user-defined alias, prefer that */
4313 refname = rte->alias->aliasname;
4314 }
4315 else if (rte->rtekind == RTE_RELATION)
4316 {
4317 /* Use the current actual name of the relation */
4318 refname = get_rel_name(rte->relid);
4319 }
4320 else if (rte->rtekind == RTE_JOIN)
4321 {
4322 /* Unnamed join has no refname */
4323 refname = NULL;
4324 }
4325 else
4326 {
4327 /* Otherwise use whatever the parser assigned */
4328 refname = rte->eref->aliasname;
4329 }
4330
4331 /*
4332 * If the selected name isn't unique, append digits to make it so, and
4333 * make a new hash entry for it once we've got a unique name. For a
4334 * very long input name, we might have to truncate to stay within
4335 * NAMEDATALEN.
4336 */
4337 if (refname)
4338 {
4339 hentry = (NameHashEntry *) hash_search(names_hash,
4340 refname,
4341 HASH_ENTER,
4342 &found);
4343 if (found)
4344 {
4345 /* Name already in use, must choose a new one */
4346 int refnamelen = strlen(refname);
4347 char *modname = (char *) palloc(refnamelen + 16);
4349
4350 do
4351 {
4352 hentry->counter++;
4353 for (;;)
4354 {
4355 memcpy(modname, refname, refnamelen);
4356 sprintf(modname + refnamelen, "_%d", hentry->counter);
4357 if (strlen(modname) < NAMEDATALEN)
4358 break;
4359 /* drop chars from refname to keep all the digits */
4361 refnamelen - 1);
4362 }
4363 hentry2 = (NameHashEntry *) hash_search(names_hash,
4364 modname,
4365 HASH_ENTER,
4366 &found);
4367 } while (found);
4368 hentry2->counter = 0; /* init new hash entry */
4369 refname = modname;
4370 }
4371 else
4372 {
4373 /* Name not previously used, need only initialize hentry */
4374 hentry->counter = 0;
4375 }
4376 }
4377
4378 dpns->rtable_names = lappend(dpns->rtable_names, refname);
4379 rtindex++;
4380 }
4381
4382 hash_destroy(names_hash);
4383}
4384
4385/*
4386 * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
4387 *
4388 * For convenience, this is defined to initialize the deparse_namespace struct
4389 * from scratch.
4390 */
4391static void
4394{
4395 ListCell *lc;
4396 ListCell *lc2;
4397
4398 /* Initialize *dpns and fill rtable/ctes links */
4399 memset(dpns, 0, sizeof(deparse_namespace));
4400 dpns->rtable = query->rtable;
4401 dpns->subplans = NIL;
4402 dpns->ctes = query->cteList;
4403 dpns->appendrels = NULL;
4404 dpns->ret_old_alias = query->returningOldAlias;
4405 dpns->ret_new_alias = query->returningNewAlias;
4406
4407 /* Assign a unique relation alias to each RTE */
4409
4410 /* Initialize dpns->rtable_columns to contain zeroed structs */
4411 dpns->rtable_columns = NIL;
4412 while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4413 dpns->rtable_columns = lappend(dpns->rtable_columns,
4414 palloc0(sizeof(deparse_columns)));
4415
4416 /* If it's a utility query, it won't have a jointree */
4417 if (query->jointree)
4418 {
4419 /* Detect whether global uniqueness of USING names is needed */
4420 dpns->unique_using =
4422
4423 /*
4424 * Select names for columns merged by USING, via a recursive pass over
4425 * the query jointree.
4426 */
4427 set_using_names(dpns, (Node *) query->jointree, NIL);
4428 }
4429
4430 /*
4431 * Now assign remaining column aliases for each RTE. We do this in a
4432 * linear scan of the rtable, so as to process RTEs whether or not they
4433 * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4434 * etc). JOIN RTEs must be processed after their children, but this is
4435 * okay because they appear later in the rtable list than their children
4436 * (cf Asserts in identify_join_columns()).
4437 */
4438 forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4439 {
4442
4443 if (rte->rtekind == RTE_JOIN)
4445 else
4447 }
4448}
4449
4450/*
4451 * set_simple_column_names: fill in column aliases for non-query situations
4452 *
4453 * This handles EXPLAIN and cases where we only have relation RTEs. Without
4454 * a join tree, we can't do anything smart about join RTEs, but we don't
4455 * need to, because EXPLAIN should never see join alias Vars anyway.
4456 * If we find a join RTE we'll just skip it, leaving its deparse_columns
4457 * struct all-zero. If somehow we try to deparse a join alias Var, we'll
4458 * error out cleanly because the struct's num_cols will be zero.
4459 */
4460static void
4462{
4463 ListCell *lc;
4464 ListCell *lc2;
4465
4466 /* Initialize dpns->rtable_columns to contain zeroed structs */
4467 dpns->rtable_columns = NIL;
4468 while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4469 dpns->rtable_columns = lappend(dpns->rtable_columns,
4470 palloc0(sizeof(deparse_columns)));
4471
4472 /* Assign unique column aliases within each non-join RTE */
4473 forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4474 {
4477
4478 if (rte->rtekind != RTE_JOIN)
4480 }
4481}
4482
4483/*
4484 * has_dangerous_join_using: search jointree for unnamed JOIN USING
4485 *
4486 * Merged columns of a JOIN USING may act differently from either of the input
4487 * columns, either because they are merged with COALESCE (in a FULL JOIN) or
4488 * because an implicit coercion of the underlying input column is required.
4489 * In such a case the column must be referenced as a column of the JOIN not as
4490 * a column of either input. And this is problematic if the join is unnamed
4491 * (alias-less): we cannot qualify the column's name with an RTE name, since
4492 * there is none. (Forcibly assigning an alias to the join is not a solution,
4493 * since that will prevent legal references to tables below the join.)
4494 * To ensure that every column in the query is unambiguously referenceable,
4495 * we must assign such merged columns names that are globally unique across
4496 * the whole query, aliasing other columns out of the way as necessary.
4497 *
4498 * Because the ensuing re-aliasing is fairly damaging to the readability of
4499 * the query, we don't do this unless we have to. So, we must pre-scan
4500 * the join tree to see if we have to, before starting set_using_names().
4501 */
4502static bool
4504{
4505 if (IsA(jtnode, RangeTblRef))
4506 {
4507 /* nothing to do here */
4508 }
4509 else if (IsA(jtnode, FromExpr))
4510 {
4511 FromExpr *f = (FromExpr *) jtnode;
4512 ListCell *lc;
4513
4514 foreach(lc, f->fromlist)
4515 {
4517 return true;
4518 }
4519 }
4520 else if (IsA(jtnode, JoinExpr))
4521 {
4522 JoinExpr *j = (JoinExpr *) jtnode;
4523
4524 /* Is it an unnamed JOIN with USING? */
4525 if (j->alias == NULL && j->usingClause)
4526 {
4527 /*
4528 * Yes, so check each join alias var to see if any of them are not
4529 * simple references to underlying columns. If so, we have a
4530 * dangerous situation and must pick unique aliases.
4531 */
4532 RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4533
4534 /* We need only examine the merged columns */
4535 for (int i = 0; i < jrte->joinmergedcols; i++)
4536 {
4537 Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4538
4539 if (!IsA(aliasvar, Var))
4540 return true;
4541 }
4542 }
4543
4544 /* Nope, but inspect children */
4545 if (has_dangerous_join_using(dpns, j->larg))
4546 return true;
4547 if (has_dangerous_join_using(dpns, j->rarg))
4548 return true;
4549 }
4550 else
4551 elog(ERROR, "unrecognized node type: %d",
4552 (int) nodeTag(jtnode));
4553 return false;
4554}
4555
4556/*
4557 * set_using_names: select column aliases to be used for merged USING columns
4558 *
4559 * We do this during a recursive descent of the query jointree.
4560 * dpns->unique_using must already be set to determine the global strategy.
4561 *
4562 * Column alias info is saved in the dpns->rtable_columns list, which is
4563 * assumed to be filled with pre-zeroed deparse_columns structs.
4564 *
4565 * parentUsing is a list of all USING aliases assigned in parent joins of
4566 * the current jointree node. (The passed-in list must not be modified.)
4567 *
4568 * Note that we do not use per-deparse_columns hash tables in this function.
4569 * The number of names that need to be assigned should be small enough that
4570 * we don't need to trouble with that.
4571 */
4572static void
4573set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
4574{
4575 if (IsA(jtnode, RangeTblRef))
4576 {
4577 /* nothing to do now */
4578 }
4579 else if (IsA(jtnode, FromExpr))
4580 {
4581 FromExpr *f = (FromExpr *) jtnode;
4582 ListCell *lc;
4583
4584 foreach(lc, f->fromlist)
4585 set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4586 }
4587 else if (IsA(jtnode, JoinExpr))
4588 {
4589 JoinExpr *j = (JoinExpr *) jtnode;
4590 RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4592 int *leftattnos;
4593 int *rightattnos;
4596 int i;
4597 ListCell *lc;
4598
4599 /* Get info about the shape of the join */
4601 leftattnos = colinfo->leftattnos;
4602 rightattnos = colinfo->rightattnos;
4603
4604 /* Look up the not-yet-filled-in child deparse_columns structs */
4607
4608 /*
4609 * If this join is unnamed, then we cannot substitute new aliases at
4610 * this level, so any name requirements pushed down to here must be
4611 * pushed down again to the children.
4612 */
4613 if (rte->alias == NULL)
4614 {
4615 for (i = 0; i < colinfo->num_cols; i++)
4616 {
4617 char *colname = colinfo->colnames[i];
4618
4619 if (colname == NULL)
4620 continue;
4621
4622 /* Push down to left column, unless it's a system column */
4623 if (leftattnos[i] > 0)
4624 {
4626 leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4627 }
4628
4629 /* Same on the righthand side */
4630 if (rightattnos[i] > 0)
4631 {
4633 rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4634 }
4635 }
4636 }
4637
4638 /*
4639 * If there's a USING clause, select the USING column names and push
4640 * those names down to the children. We have two strategies:
4641 *
4642 * If dpns->unique_using is true, we force all USING names to be
4643 * unique across the whole query level. In principle we'd only need
4644 * the names of dangerous USING columns to be globally unique, but to
4645 * safely assign all USING names in a single pass, we have to enforce
4646 * the same uniqueness rule for all of them. However, if a USING
4647 * column's name has been pushed down from the parent, we should use
4648 * it as-is rather than making a uniqueness adjustment. This is
4649 * necessary when we're at an unnamed join, and it creates no risk of
4650 * ambiguity. Also, if there's a user-written output alias for a
4651 * merged column, we prefer to use that rather than the input name;
4652 * this simplifies the logic and seems likely to lead to less aliasing
4653 * overall.
4654 *
4655 * If dpns->unique_using is false, we only need USING names to be
4656 * unique within their own join RTE. We still need to honor
4657 * pushed-down names, though.
4658 *
4659 * Though significantly different in results, these two strategies are
4660 * implemented by the same code, with only the difference of whether
4661 * to put assigned names into dpns->using_names.
4662 */
4663 if (j->usingClause)
4664 {
4665 /* Copy the input parentUsing list so we don't modify it */
4666 parentUsing = list_copy(parentUsing);
4667
4668 /* USING names must correspond to the first join output columns */
4670 i = 0;
4671 foreach(lc, j->usingClause)
4672 {
4673 char *colname = strVal(lfirst(lc));
4674
4675 /* Assert it's a merged column */
4676 Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4677
4678 /* Adopt passed-down name if any, else select unique name */
4679 if (colinfo->colnames[i] != NULL)
4680 colname = colinfo->colnames[i];
4681 else
4682 {
4683 /* Prefer user-written output alias if any */
4684 if (rte->alias && i < list_length(rte->alias->colnames))
4685 colname = strVal(list_nth(rte->alias->colnames, i));
4686 /* Make it appropriately unique */
4687 colname = make_colname_unique(colname, dpns, colinfo);
4688 if (dpns->unique_using)
4689 dpns->using_names = lappend(dpns->using_names,
4690 colname);
4691 /* Save it as output column name, too */
4692 colinfo->colnames[i] = colname;
4693 }
4694
4695 /* Remember selected names for use later */
4696 colinfo->usingNames = lappend(colinfo->usingNames, colname);
4697 parentUsing = lappend(parentUsing, colname);
4698
4699 /* Push down to left column, unless it's a system column */
4700 if (leftattnos[i] > 0)
4701 {
4703 leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4704 }
4705
4706 /* Same on the righthand side */
4707 if (rightattnos[i] > 0)
4708 {
4710 rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4711 }
4712
4713 i++;
4714 }
4715 }
4716
4717 /* Mark child deparse_columns structs with correct parentUsing info */
4718 leftcolinfo->parentUsing = parentUsing;
4719 rightcolinfo->parentUsing = parentUsing;
4720
4721 /* Now recursively assign USING column names in children */
4722 set_using_names(dpns, j->larg, parentUsing);
4723 set_using_names(dpns, j->rarg, parentUsing);
4724 }
4725 else
4726 elog(ERROR, "unrecognized node type: %d",
4727 (int) nodeTag(jtnode));
4728}
4729
4730/*
4731 * set_relation_column_names: select column aliases for a non-join RTE
4732 *
4733 * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4734 * If any colnames entries are already filled in, those override local
4735 * choices.
4736 */
4737static void
4740{
4741 int ncolumns;
4742 char **real_colnames;
4743 bool changed_any;
4744 int noldcolumns;
4745 int i;
4746 int j;
4747
4748 /*
4749 * Construct an array of the current "real" column names of the RTE.
4750 * real_colnames[] will be indexed by physical column number, with NULL
4751 * entries for dropped columns.
4752 */
4753 if (rte->rtekind == RTE_RELATION)
4754 {
4755 /* Relation --- look to the system catalogs for up-to-date info */
4756 Relation rel;
4757 TupleDesc tupdesc;
4758
4759 rel = relation_open(rte->relid, AccessShareLock);
4760 tupdesc = RelationGetDescr(rel);
4761
4762 ncolumns = tupdesc->natts;
4763 real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4764
4765 for (i = 0; i < ncolumns; i++)
4766 {
4767 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4768
4769 if (attr->attisdropped)
4770 real_colnames[i] = NULL;
4771 else
4772 real_colnames[i] = pstrdup(NameStr(attr->attname));
4773 }
4775 }
4776 else
4777 {
4778 /* Otherwise get the column names from eref or expandRTE() */
4779 List *colnames;
4780 ListCell *lc;
4781
4782 /*
4783 * Functions returning composites have the annoying property that some
4784 * of the composite type's columns might have been dropped since the
4785 * query was parsed. If possible, use expandRTE() to handle that
4786 * case, since it has the tedious logic needed to find out about
4787 * dropped columns. However, if we're explaining a plan, then we
4788 * don't have rte->functions because the planner thinks that won't be
4789 * needed later, and that breaks expandRTE(). So in that case we have
4790 * to rely on rte->eref, which may lead us to report a dropped
4791 * column's old name; that seems close enough for EXPLAIN's purposes.
4792 *
4793 * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4794 * which should be sufficiently up-to-date: no other RTE types can
4795 * have columns get dropped from under them after parsing.
4796 */
4797 if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4798 {
4799 /* Since we're not creating Vars, rtindex etc. don't matter */
4801 true /* include dropped */ , &colnames, NULL);
4802 }
4803 else
4804 colnames = rte->eref->colnames;
4805
4806 ncolumns = list_length(colnames);
4807 real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4808
4809 i = 0;
4810 foreach(lc, colnames)
4811 {
4812 /*
4813 * If the column name we find here is an empty string, then it's a
4814 * dropped column, so change to NULL.
4815 */
4816 char *cname = strVal(lfirst(lc));
4817
4818 if (cname[0] == '\0')
4819 cname = NULL;
4821 i++;
4822 }
4823 }
4824
4825 /*
4826 * Ensure colinfo->colnames has a slot for each column. (It could be long
4827 * enough already, if we pushed down a name for the last column.) Note:
4828 * it's possible that there are now more columns than there were when the
4829 * query was parsed, ie colnames could be longer than rte->eref->colnames.
4830 * We must assign unique aliases to the new columns too, else there could
4831 * be unresolved conflicts when the view/rule is reloaded.
4832 */
4834 Assert(colinfo->num_cols == ncolumns);
4835
4836 /*
4837 * Make sufficiently large new_colnames and is_new_col arrays, too.
4838 *
4839 * Note: because we leave colinfo->num_new_cols zero until after the loop,
4840 * colname_is_unique will not consult that array, which is fine because it
4841 * would only be duplicate effort.
4842 */
4843 colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4844 colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4845
4846 /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4848
4849 /*
4850 * Scan the columns, select a unique alias for each one, and store it in
4851 * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4852 * entries for dropped columns, the latter omits them. Also mark
4853 * new_colnames entries as to whether they are new since parse time; this
4854 * is the case for entries beyond the length of rte->eref->colnames.
4855 */
4856 noldcolumns = list_length(rte->eref->colnames);
4857 changed_any = false;
4858 j = 0;
4859 for (i = 0; i < ncolumns; i++)
4860 {
4861 char *real_colname = real_colnames[i];
4862 char *colname = colinfo->colnames[i];
4863
4864 /* Skip dropped columns */
4865 if (real_colname == NULL)
4866 {
4867 Assert(colname == NULL); /* colnames[i] is already NULL */
4868 continue;
4869 }
4870
4871 /* If alias already assigned, that's what to use */
4872 if (colname == NULL)
4873 {
4874 /* If user wrote an alias, prefer that over real column name */
4875 if (rte->alias && i < list_length(rte->alias->colnames))
4876 colname = strVal(list_nth(rte->alias->colnames, i));
4877 else
4878 colname = real_colname;
4879
4880 /* Unique-ify and insert into colinfo */
4881 colname = make_colname_unique(colname, dpns, colinfo);
4882
4883 colinfo->colnames[i] = colname;
4884 add_to_names_hash(colinfo, colname);
4885 }
4886
4887 /* Put names of non-dropped columns in new_colnames[] too */
4888 colinfo->new_colnames[j] = colname;
4889 /* And mark them as new or not */
4890 colinfo->is_new_col[j] = (i >= noldcolumns);
4891 j++;
4892
4893 /* Remember if any assigned aliases differ from "real" name */
4894 if (!changed_any && strcmp(colname, real_colname) != 0)
4895 changed_any = true;
4896 }
4897
4898 /* We're now done needing the colinfo's names_hash */
4900
4901 /*
4902 * Set correct length for new_colnames[] array. (Note: if columns have
4903 * been added, colinfo->num_cols includes them, which is not really quite
4904 * right but is harmless, since any new columns must be at the end where
4905 * they won't affect varattnos of pre-existing columns.)
4906 */
4907 colinfo->num_new_cols = j;
4908
4909 /*
4910 * For a relation RTE, we need only print the alias column names if any
4911 * are different from the underlying "real" names. For a function RTE,
4912 * always emit a complete column alias list; this is to protect against
4913 * possible instability of the default column names (eg, from altering
4914 * parameter names). For tablefunc RTEs, we never print aliases, because
4915 * the column names are part of the clause itself. For other RTE types,
4916 * print if we changed anything OR if there were user-written column
4917 * aliases (since the latter would be part of the underlying "reality").
4918 */
4919 if (rte->rtekind == RTE_RELATION)
4920 colinfo->printaliases = changed_any;
4921 else if (rte->rtekind == RTE_FUNCTION)
4922 colinfo->printaliases = true;
4923 else if (rte->rtekind == RTE_TABLEFUNC)
4924 colinfo->printaliases = false;
4925 else if (rte->alias && rte->alias->colnames != NIL)
4926 colinfo->printaliases = true;
4927 else
4928 colinfo->printaliases = changed_any;
4929}
4930
4931/*
4932 * set_join_column_names: select column aliases for a join RTE
4933 *
4934 * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
4935 * If any colnames entries are already filled in, those override local
4936 * choices. Also, names for USING columns were already chosen by
4937 * set_using_names(). We further expect that column alias selection has been
4938 * completed for both input RTEs.
4939 */
4940static void
4943{
4946 bool changed_any;
4947 int noldcolumns;
4948 int nnewcolumns;
4951 int i;
4952 int j;
4953 int ic;
4954 int jc;
4955
4956 /* Look up the previously-filled-in child deparse_columns structs */
4959
4960 /*
4961 * Ensure colinfo->colnames has a slot for each column. (It could be long
4962 * enough already, if we pushed down a name for the last column.) Note:
4963 * it's possible that one or both inputs now have more columns than there
4964 * were when the query was parsed, but we'll deal with that below. We
4965 * only need entries in colnames for pre-existing columns.
4966 */
4967 noldcolumns = list_length(rte->eref->colnames);
4969 Assert(colinfo->num_cols == noldcolumns);
4970
4971 /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4973
4974 /*
4975 * Scan the join output columns, select an alias for each one, and store
4976 * it in colinfo->colnames. If there are USING columns, set_using_names()
4977 * already selected their names, so we can start the loop at the first
4978 * non-merged column.
4979 */
4980 changed_any = false;
4981 for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4982 {
4983 char *colname = colinfo->colnames[i];
4984 char *real_colname;
4985
4986 /* Join column must refer to at least one input column */
4987 Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4988
4989 /* Get the child column name */
4990 if (colinfo->leftattnos[i] > 0)
4991 real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4992 else if (colinfo->rightattnos[i] > 0)
4993 real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4994 else
4995 {
4996 /* We're joining system columns --- use eref name */
4997 real_colname = strVal(list_nth(rte->eref->colnames, i));
4998 }
4999
5000 /* If child col has been dropped, no need to assign a join colname */
5001 if (real_colname == NULL)
5002 {
5003 colinfo->colnames[i] = NULL;
5004 continue;
5005 }
5006
5007 /* In an unnamed join, just report child column names as-is */
5008 if (rte->alias == NULL)
5009 {
5010 colinfo->colnames[i] = real_colname;
5012 continue;
5013 }
5014
5015 /* If alias already assigned, that's what to use */
5016 if (colname == NULL)
5017 {
5018 /* If user wrote an alias, prefer that over real column name */
5019 if (rte->alias && i < list_length(rte->alias->colnames))
5020 colname = strVal(list_nth(rte->alias->colnames, i));
5021 else
5022 colname = real_colname;
5023
5024 /* Unique-ify and insert into colinfo */
5025 colname = make_colname_unique(colname, dpns, colinfo);
5026
5027 colinfo->colnames[i] = colname;
5028 add_to_names_hash(colinfo, colname);
5029 }
5030
5031 /* Remember if any assigned aliases differ from "real" name */
5032 if (!changed_any && strcmp(colname, real_colname) != 0)
5033 changed_any = true;
5034 }
5035
5036 /*
5037 * Calculate number of columns the join would have if it were re-parsed
5038 * now, and create storage for the new_colnames and is_new_col arrays.
5039 *
5040 * Note: colname_is_unique will be consulting new_colnames[] during the
5041 * loops below, so its not-yet-filled entries must be zeroes.
5042 */
5043 nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
5044 list_length(colinfo->usingNames);
5045 colinfo->num_new_cols = nnewcolumns;
5046 colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
5047 colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
5048
5049 /*
5050 * Generating the new_colnames array is a bit tricky since any new columns
5051 * added since parse time must be inserted in the right places. This code
5052 * must match the parser, which will order a join's columns as merged
5053 * columns first (in USING-clause order), then non-merged columns from the
5054 * left input (in attnum order), then non-merged columns from the right
5055 * input (ditto). If one of the inputs is itself a join, its columns will
5056 * be ordered according to the same rule, which means newly-added columns
5057 * might not be at the end. We can figure out what's what by consulting
5058 * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
5059 *
5060 * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
5061 * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
5062 * meanings for the current child RTE.
5063 */
5064
5065 /* Handle merged columns; they are first and can't be new */
5066 i = j = 0;
5067 while (i < noldcolumns &&
5068 colinfo->leftattnos[i] != 0 &&
5069 colinfo->rightattnos[i] != 0)
5070 {
5071 /* column name is already determined and known unique */
5072 colinfo->new_colnames[j] = colinfo->colnames[i];
5073 colinfo->is_new_col[j] = false;
5074
5075 /* build bitmapsets of child attnums of merged columns */
5076 if (colinfo->leftattnos[i] > 0)
5077 leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
5078 if (colinfo->rightattnos[i] > 0)
5080
5081 i++, j++;
5082 }
5083
5084 /* Handle non-merged left-child columns */
5085 ic = 0;
5086 for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
5087 {
5088 char *child_colname = leftcolinfo->new_colnames[jc];
5089
5090 if (!leftcolinfo->is_new_col[jc])
5091 {
5092 /* Advance ic to next non-dropped old column of left child */
5093 while (ic < leftcolinfo->num_cols &&
5094 leftcolinfo->colnames[ic] == NULL)
5095 ic++;
5096 Assert(ic < leftcolinfo->num_cols);
5097 ic++;
5098 /* If it is a merged column, we already processed it */
5100 continue;
5101 /* Else, advance i to the corresponding existing join column */
5102 while (i < colinfo->num_cols &&
5103 colinfo->colnames[i] == NULL)
5104 i++;
5105 Assert(i < colinfo->num_cols);
5106 Assert(ic == colinfo->leftattnos[i]);
5107 /* Use the already-assigned name of this column */
5108 colinfo->new_colnames[j] = colinfo->colnames[i];
5109 i++;
5110 }
5111 else
5112 {
5113 /*
5114 * Unique-ify the new child column name and assign, unless we're
5115 * in an unnamed join, in which case just copy
5116 */
5117 if (rte->alias != NULL)
5118 {
5119 colinfo->new_colnames[j] =
5121 if (!changed_any &&
5122 strcmp(colinfo->new_colnames[j], child_colname) != 0)
5123 changed_any = true;
5124 }
5125 else
5126 colinfo->new_colnames[j] = child_colname;
5127 add_to_names_hash(colinfo, colinfo->new_colnames[j]);
5128 }
5129
5130 colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
5131 j++;
5132 }
5133
5134 /* Handle non-merged right-child columns in exactly the same way */
5135 ic = 0;
5136 for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
5137 {
5138 char *child_colname = rightcolinfo->new_colnames[jc];
5139
5140 if (!rightcolinfo->is_new_col[jc])
5141 {
5142 /* Advance ic to next non-dropped old column of right child */
5143 while (ic < rightcolinfo->num_cols &&
5144 rightcolinfo->colnames[ic] == NULL)
5145 ic++;
5146 Assert(ic < rightcolinfo->num_cols);
5147 ic++;
5148 /* If it is a merged column, we already processed it */
5150 continue;
5151 /* Else, advance i to the corresponding existing join column */
5152 while (i < colinfo->num_cols &&
5153 colinfo->colnames[i] == NULL)
5154 i++;
5155 Assert(i < colinfo->num_cols);
5156 Assert(ic == colinfo->rightattnos[i]);
5157 /* Use the already-assigned name of this column */
5158 colinfo->new_colnames[j] = colinfo->colnames[i];
5159 i++;
5160 }
5161 else
5162 {
5163 /*
5164 * Unique-ify the new child column name and assign, unless we're
5165 * in an unnamed join, in which case just copy
5166 */
5167 if (rte->alias != NULL)
5168 {
5169 colinfo->new_colnames[j] =
5171 if (!changed_any &&
5172 strcmp(colinfo->new_colnames[j], child_colname) != 0)
5173 changed_any = true;
5174 }
5175 else
5176 colinfo->new_colnames[j] = child_colname;
5177 add_to_names_hash(colinfo, colinfo->new_colnames[j]);
5178 }
5179
5180 colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
5181 j++;
5182 }
5183
5184 /* Assert we processed the right number of columns */
5185#ifdef USE_ASSERT_CHECKING
5186 while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
5187 i++;
5188 Assert(i == colinfo->num_cols);
5189 Assert(j == nnewcolumns);
5190#endif
5191
5192 /* We're now done needing the colinfo's names_hash */
5194
5195 /*
5196 * For a named join, print column aliases if we changed any from the child
5197 * names. Unnamed joins cannot print aliases.
5198 */
5199 if (rte->alias != NULL)
5200 colinfo->printaliases = changed_any;
5201 else
5202 colinfo->printaliases = false;
5203}
5204
5205/*
5206 * colname_is_unique: is colname distinct from already-chosen column names?
5207 *
5208 * dpns is query-wide info, colinfo is for the column's RTE
5209 */
5210static bool
5211colname_is_unique(const char *colname, deparse_namespace *dpns,
5213{
5214 int i;
5215 ListCell *lc;
5216
5217 /*
5218 * If we have a hash table, consult that instead of linearly scanning the
5219 * colinfo's strings.
5220 */
5221 if (colinfo->names_hash)
5222 {
5223 if (hash_search(colinfo->names_hash,
5224 colname,
5225 HASH_FIND,
5226 NULL) != NULL)
5227 return false;
5228 }
5229 else
5230 {
5231 /* Check against already-assigned column aliases within RTE */
5232 for (i = 0; i < colinfo->num_cols; i++)
5233 {
5234 char *oldname = colinfo->colnames[i];
5235
5236 if (oldname && strcmp(oldname, colname) == 0)
5237 return false;
5238 }
5239
5240 /*
5241 * If we're building a new_colnames array, check that too (this will
5242 * be partially but not completely redundant with the previous checks)
5243 */
5244 for (i = 0; i < colinfo->num_new_cols; i++)
5245 {
5246 char *oldname = colinfo->new_colnames[i];
5247
5248 if (oldname && strcmp(oldname, colname) == 0)
5249 return false;
5250 }
5251
5252 /*
5253 * Also check against names already assigned for parent-join USING
5254 * cols
5255 */
5256 foreach(lc, colinfo->parentUsing)
5257 {
5258 char *oldname = (char *) lfirst(lc);
5259
5260 if (strcmp(oldname, colname) == 0)
5261 return false;
5262 }
5263 }
5264
5265 /*
5266 * Also check against USING-column names that must be globally unique.
5267 * These are not hashed, but there should be few of them.
5268 */
5269 foreach(lc, dpns->using_names)
5270 {
5271 char *oldname = (char *) lfirst(lc);
5272
5273 if (strcmp(oldname, colname) == 0)
5274 return false;
5275 }
5276
5277 return true;
5278}
5279
5280/*
5281 * make_colname_unique: modify colname if necessary to make it unique
5282 *
5283 * dpns is query-wide info, colinfo is for the column's RTE
5284 */
5285static char *
5288{
5289 /*
5290 * If the selected name isn't unique, append digits to make it so. For a
5291 * very long input name, we might have to truncate to stay within
5292 * NAMEDATALEN.
5293 */
5294 if (!colname_is_unique(colname, dpns, colinfo))
5295 {
5296 int colnamelen = strlen(colname);
5297 char *modname = (char *) palloc(colnamelen + 16);
5298 int i = 0;
5299
5300 do
5301 {
5302 i++;
5303 for (;;)
5304 {
5305 memcpy(modname, colname, colnamelen);
5306 sprintf(modname + colnamelen, "_%d", i);
5307 if (strlen(modname) < NAMEDATALEN)
5308 break;
5309 /* drop chars from colname to keep all the digits */
5311 colnamelen - 1);
5312 }
5313 } while (!colname_is_unique(modname, dpns, colinfo));
5314 colname = modname;
5315 }
5316 return colname;
5317}
5318
5319/*
5320 * expand_colnames_array_to: make colinfo->colnames at least n items long
5321 *
5322 * Any added array entries are initialized to zero.
5323 */
5324static void
5326{
5327 if (n > colinfo->num_cols)
5328 {
5329 if (colinfo->colnames == NULL)
5330 colinfo->colnames = palloc0_array(char *, n);
5331 else
5332 colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
5333 colinfo->num_cols = n;
5334 }
5335}
5336
5337/*
5338 * build_colinfo_names_hash: optionally construct a hash table for colinfo
5339 */
5340static void
5342{
5344 int i;
5345 ListCell *lc;
5346
5347 /*
5348 * Use a hash table only for RTEs with at least 32 columns. (The cutoff
5349 * is somewhat arbitrary, but let's choose it so that this code does get
5350 * exercised in the regression tests.)
5351 */
5352 if (colinfo->num_cols < 32)
5353 return;
5354
5355 /*
5356 * Set up the hash table. The entries are just strings with no other
5357 * payload.
5358 */
5359 hash_ctl.keysize = NAMEDATALEN;
5360 hash_ctl.entrysize = NAMEDATALEN;
5362 colinfo->names_hash = hash_create("deparse_columns names",
5363 colinfo->num_cols + colinfo->num_new_cols,
5364 &hash_ctl,
5366
5367 /*
5368 * Preload the hash table with any names already present (these would have
5369 * come from set_using_names).
5370 */
5371 for (i = 0; i < colinfo->num_cols; i++)
5372 {
5373 char *oldname = colinfo->colnames[i];
5374
5375 if (oldname)
5377 }
5378
5379 for (i = 0; i < colinfo->num_new_cols; i++)
5380 {
5381 char *oldname = colinfo->new_colnames[i];
5382
5383 if (oldname)
5385 }
5386
5387 foreach(lc, colinfo->parentUsing)
5388 {
5389 char *oldname = (char *) lfirst(lc);
5390
5392 }
5393}
5394
5395/*
5396 * add_to_names_hash: add a string to the names_hash, if we're using one
5397 */
5398static void
5400{
5401 if (colinfo->names_hash)
5402 (void) hash_search(colinfo->names_hash,
5403 name,
5404 HASH_ENTER,
5405 NULL);
5406}
5407
5408/*
5409 * destroy_colinfo_names_hash: destroy hash table when done with it
5410 */
5411static void
5413{
5414 if (colinfo->names_hash)
5415 {
5416 hash_destroy(colinfo->names_hash);
5417 colinfo->names_hash = NULL;
5418 }
5419}
5420
5421/*
5422 * identify_join_columns: figure out where columns of a join come from
5423 *
5424 * Fills the join-specific fields of the colinfo struct, except for
5425 * usingNames which is filled later.
5426 */
5427static void
5430{
5431 int numjoincols;
5432 int jcolno;
5433 int rcolno;
5434 ListCell *lc;
5435
5436 /* Extract left/right child RT indexes */
5437 if (IsA(j->larg, RangeTblRef))
5438 colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
5439 else if (IsA(j->larg, JoinExpr))
5440 colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
5441 else
5442 elog(ERROR, "unrecognized node type in jointree: %d",
5443 (int) nodeTag(j->larg));
5444 if (IsA(j->rarg, RangeTblRef))
5445 colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
5446 else if (IsA(j->rarg, JoinExpr))
5447 colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
5448 else
5449 elog(ERROR, "unrecognized node type in jointree: %d",
5450 (int) nodeTag(j->rarg));
5451
5452 /* Assert children will be processed earlier than join in second pass */
5453 Assert(colinfo->leftrti < j->rtindex);
5454 Assert(colinfo->rightrti < j->rtindex);
5455
5456 /* Initialize result arrays with zeroes */
5457 numjoincols = list_length(jrte->joinaliasvars);
5458 Assert(numjoincols == list_length(jrte->eref->colnames));
5459 colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
5460 colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
5461
5462 /*
5463 * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
5464 * Recall that the column(s) merged due to USING are the first column(s)
5465 * of the join output. We need not do anything special while scanning
5466 * joinleftcols, but while scanning joinrightcols we must distinguish
5467 * merged from unmerged columns.
5468 */
5469 jcolno = 0;
5470 foreach(lc, jrte->joinleftcols)
5471 {
5472 int leftattno = lfirst_int(lc);
5473
5474 colinfo->leftattnos[jcolno++] = leftattno;
5475 }
5476 rcolno = 0;
5477 foreach(lc, jrte->joinrightcols)
5478 {
5479 int rightattno = lfirst_int(lc);
5480
5481 if (rcolno < jrte->joinmergedcols) /* merged column? */
5482 colinfo->rightattnos[rcolno] = rightattno;
5483 else
5484 colinfo->rightattnos[jcolno++] = rightattno;
5485 rcolno++;
5486 }
5488}
5489
5490/*
5491 * get_rtable_name: convenience function to get a previously assigned RTE alias
5492 *
5493 * The RTE must belong to the topmost namespace level in "context".
5494 */
5495static char *
5496get_rtable_name(int rtindex, deparse_context *context)
5497{
5499
5500 Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
5501 return (char *) list_nth(dpns->rtable_names, rtindex - 1);
5502}
5503
5504/*
5505 * set_deparse_plan: set up deparse_namespace to parse subexpressions
5506 * of a given Plan node
5507 *
5508 * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
5509 * and index_tlist fields. Caller must already have adjusted the ancestors
5510 * list if necessary. Note that the rtable, subplans, and ctes fields do
5511 * not need to change when shifting attention to different plan nodes in a
5512 * single plan tree.
5513 */
5514static void
5516{
5517 dpns->plan = plan;
5518
5519 /*
5520 * We special-case Append and MergeAppend to pretend that the first child
5521 * plan is the OUTER referent; we have to interpret OUTER Vars in their
5522 * tlists according to one of the children, and the first one is the most
5523 * natural choice.
5524 */
5525 if (IsA(plan, Append))
5526 dpns->outer_plan = linitial(((Append *) plan)->appendplans);
5527 else if (IsA(plan, MergeAppend))
5528 dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
5529 else
5530 dpns->outer_plan = outerPlan(plan);
5531
5532 if (dpns->outer_plan)
5533 dpns->outer_tlist = dpns->outer_plan->targetlist;
5534 else
5535 dpns->outer_tlist = NIL;
5536
5537 /*
5538 * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
5539 * use OUTER because that could someday conflict with the normal meaning.)
5540 * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
5541 * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
5542 * that as INNER referent.
5543 *
5544 * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
5545 * INNER referent. This is the join from the target relation to the data
5546 * source, and all INNER_VAR Vars in other parts of the query refer to its
5547 * targetlist.
5548 *
5549 * For ON CONFLICT DO SELECT/UPDATE we just need the inner tlist to point
5550 * to the excluded expression's tlist. (Similar to the SubqueryScan we
5551 * don't want to reuse OUTER, it's used for RETURNING in some modify table
5552 * cases, although not INSERT .. CONFLICT).
5553 */
5554 if (IsA(plan, SubqueryScan))
5555 dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5556 else if (IsA(plan, CteScan))
5557 dpns->inner_plan = list_nth(dpns->subplans,
5558 ((CteScan *) plan)->ctePlanId - 1);
5559 else if (IsA(plan, WorkTableScan))
5560 dpns->inner_plan = find_recursive_union(dpns,
5561 (WorkTableScan *) plan);
5562 else if (IsA(plan, ModifyTable))
5563 {
5564 if (((ModifyTable *) plan)->operation == CMD_MERGE)
5565 dpns->inner_plan = outerPlan(plan);
5566 else
5567 dpns->inner_plan = plan;
5568 }
5569 else
5570 dpns->inner_plan = innerPlan(plan);
5571
5573 dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
5574 else if (dpns->inner_plan)
5575 dpns->inner_tlist = dpns->inner_plan->targetlist;
5576 else
5577 dpns->inner_tlist = NIL;
5578
5579 /* Set up referent for INDEX_VAR Vars, if needed */
5580 if (IsA(plan, IndexOnlyScan))
5581 dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5582 else if (IsA(plan, ForeignScan))
5583 dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5584 else if (IsA(plan, CustomScan))
5585 dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5586 else
5587 dpns->index_tlist = NIL;
5588}
5589
5590/*
5591 * Locate the ancestor plan node that is the RecursiveUnion generating
5592 * the WorkTableScan's work table. We can match on wtParam, since that
5593 * should be unique within the plan tree.
5594 */
5595static Plan *
5597{
5598 ListCell *lc;
5599
5600 foreach(lc, dpns->ancestors)
5601 {
5602 Plan *ancestor = (Plan *) lfirst(lc);
5603
5604 if (IsA(ancestor, RecursiveUnion) &&
5605 ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5606 return ancestor;
5607 }
5608 elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5609 wtscan->wtParam);
5610 return NULL;
5611}
5612
5613/*
5614 * push_child_plan: temporarily transfer deparsing attention to a child plan
5615 *
5616 * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
5617 * deparse context in case the referenced expression itself uses
5618 * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
5619 * affecting levelsup issues (although in a Plan tree there really shouldn't
5620 * be any).
5621 *
5622 * Caller must provide a local deparse_namespace variable to save the
5623 * previous state for pop_child_plan.
5624 */
5625static void
5628{
5629 /* Save state for restoration later */
5630 *save_dpns = *dpns;
5631
5632 /* Link current plan node into ancestors list */
5633 dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5634
5635 /* Set attention on selected child */
5637}
5638
5639/*
5640 * pop_child_plan: undo the effects of push_child_plan
5641 */
5642static void
5644{
5645 List *ancestors;
5646
5647 /* Get rid of ancestors list cell added by push_child_plan */
5648 ancestors = list_delete_first(dpns->ancestors);
5649
5650 /* Restore fields changed by push_child_plan */
5651 *dpns = *save_dpns;
5652
5653 /* Make sure dpns->ancestors is right (may be unnecessary) */
5654 dpns->ancestors = ancestors;
5655}
5656
5657/*
5658 * push_ancestor_plan: temporarily transfer deparsing attention to an
5659 * ancestor plan
5660 *
5661 * When expanding a Param reference, we must adjust the deparse context
5662 * to match the plan node that contains the expression being printed;
5663 * otherwise we'd fail if that expression itself contains a Param or
5664 * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
5665 *
5666 * The target ancestor is conveniently identified by the ListCell holding it
5667 * in dpns->ancestors.
5668 *
5669 * Caller must provide a local deparse_namespace variable to save the
5670 * previous state for pop_ancestor_plan.
5671 */
5672static void
5675{
5677
5678 /* Save state for restoration later */
5679 *save_dpns = *dpns;
5680
5681 /* Build a new ancestor list with just this node's ancestors */
5682 dpns->ancestors =
5683 list_copy_tail(dpns->ancestors,
5684 list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5685
5686 /* Set attention on selected ancestor */
5688}
5689
5690/*
5691 * pop_ancestor_plan: undo the effects of push_ancestor_plan
5692 */
5693static void
5695{
5696 /* Free the ancestor list made in push_ancestor_plan */
5697 list_free(dpns->ancestors);
5698
5699 /* Restore fields changed by push_ancestor_plan */
5700 *dpns = *save_dpns;
5701}
5702
5703
5704/* ----------
5705 * make_ruledef - reconstruct the CREATE RULE command
5706 * for a given pg_rewrite tuple
5707 * ----------
5708 */
5709static void
5711 int prettyFlags)
5712{
5713 char *rulename;
5714 char ev_type;
5715 Oid ev_class;
5716 bool is_instead;
5717 char *ev_qual;
5718 char *ev_action;
5719 List *actions;
5722 int fno;
5723 Datum dat;
5724 bool isnull;
5725
5726 /*
5727 * Get the attribute values from the rules tuple
5728 */
5729 fno = SPI_fnumber(rulettc, "rulename");
5730 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5731 Assert(!isnull);
5732 rulename = NameStr(*(DatumGetName(dat)));
5733
5734 fno = SPI_fnumber(rulettc, "ev_type");
5735 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5736 Assert(!isnull);
5737 ev_type = DatumGetChar(dat);
5738
5739 fno = SPI_fnumber(rulettc, "ev_class");
5740 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5741 Assert(!isnull);
5743
5744 fno = SPI_fnumber(rulettc, "is_instead");
5745 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5746 Assert(!isnull);
5747 is_instead = DatumGetBool(dat);
5748
5749 fno = SPI_fnumber(rulettc, "ev_qual");
5751 Assert(ev_qual != NULL);
5752
5753 fno = SPI_fnumber(rulettc, "ev_action");
5755 Assert(ev_action != NULL);
5756 actions = (List *) stringToNode(ev_action);
5757 if (actions == NIL)
5758 elog(ERROR, "invalid empty ev_action list");
5759
5761
5762 /*
5763 * Build the rules definition text
5764 */
5765 appendStringInfo(buf, "CREATE RULE %s AS",
5766 quote_identifier(rulename));
5767
5768 if (prettyFlags & PRETTYFLAG_INDENT)
5769 appendStringInfoString(buf, "\n ON ");
5770 else
5771 appendStringInfoString(buf, " ON ");
5772
5773 /* The event the rule is fired for */
5774 switch (ev_type)
5775 {
5776 case '1':
5777 appendStringInfoString(buf, "SELECT");
5779 break;
5780
5781 case '2':
5782 appendStringInfoString(buf, "UPDATE");
5783 break;
5784
5785 case '3':
5786 appendStringInfoString(buf, "INSERT");
5787 break;
5788
5789 case '4':
5790 appendStringInfoString(buf, "DELETE");
5791 break;
5792
5793 default:
5794 ereport(ERROR,
5796 errmsg("rule \"%s\" has unsupported event type %d",
5797 rulename, ev_type)));
5798 break;
5799 }
5800
5801 /* The relation the rule is fired on */
5802 appendStringInfo(buf, " TO %s",
5803 (prettyFlags & PRETTYFLAG_SCHEMA) ?
5806
5807 /* If the rule has an event qualification, add it */
5808 if (strcmp(ev_qual, "<>") != 0)
5809 {
5810 Node *qual;
5811 Query *query;
5812 deparse_context context;
5814
5815 if (prettyFlags & PRETTYFLAG_INDENT)
5817 appendStringInfoString(buf, " WHERE ");
5818
5819 qual = stringToNode(ev_qual);
5820
5821 /*
5822 * We need to make a context for recognizing any Vars in the qual
5823 * (which can only be references to OLD and NEW). Use the rtable of
5824 * the first query in the action list for this purpose.
5825 */
5826 query = (Query *) linitial(actions);
5827
5828 /*
5829 * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5830 * into the SELECT, and that's what we need to look at. (Ugly kluge
5831 * ... try to fix this when we redesign querytrees.)
5832 */
5833 query = getInsertSelectQuery(query, NULL);
5834
5835 /* Must acquire locks right away; see notes in get_query_def() */
5836 AcquireRewriteLocks(query, false, false);
5837
5838 context.buf = buf;
5839 context.namespaces = list_make1(&dpns);
5840 context.resultDesc = NULL;
5841 context.targetList = NIL;
5842 context.windowClause = NIL;
5843 context.varprefix = (list_length(query->rtable) != 1);
5844 context.prettyFlags = prettyFlags;
5846 context.indentLevel = PRETTYINDENT_STD;
5847 context.colNamesVisible = true;
5848 context.inGroupBy = false;
5849 context.varInOrderBy = false;
5850 context.appendparents = NULL;
5851
5852 set_deparse_for_query(&dpns, query, NIL);
5853
5854 get_rule_expr(qual, &context, false);
5855 }
5856
5857 appendStringInfoString(buf, " DO ");
5858
5859 /* The INSTEAD keyword (if so) */
5860 if (is_instead)
5861 appendStringInfoString(buf, "INSTEAD ");
5862
5863 /* Finally the rules actions */
5864 if (list_length(actions) > 1)
5865 {
5867 Query *query;
5868
5870 foreach(action, actions)
5871 {
5872 query = (Query *) lfirst(action);
5873 get_query_def(query, buf, NIL, viewResultDesc, true,
5874 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5875 if (prettyFlags)
5877 else
5879 }
5881 }
5882 else
5883 {
5884 Query *query;
5885
5886 query = (Query *) linitial(actions);
5887 get_query_def(query, buf, NIL, viewResultDesc, true,
5888 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5890 }
5891
5893}
5894
5895
5896/* ----------
5897 * make_viewdef - reconstruct the SELECT part of a
5898 * view rewrite rule
5899 * ----------
5900 */
5901static void
5903 int prettyFlags, int wrapColumn)
5904{
5905 Query *query;
5906 char ev_type;
5907 Oid ev_class;
5908 bool is_instead;
5909 char *ev_qual;
5910 char *ev_action;
5911 List *actions;
5913 int fno;
5914 Datum dat;
5915 bool isnull;
5916
5917 /*
5918 * Get the attribute values from the rules tuple
5919 */
5920 fno = SPI_fnumber(rulettc, "ev_type");
5921 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5922 Assert(!isnull);
5923 ev_type = DatumGetChar(dat);
5924
5925 fno = SPI_fnumber(rulettc, "ev_class");
5926 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5927 Assert(!isnull);
5929
5930 fno = SPI_fnumber(rulettc, "is_instead");
5931 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5932 Assert(!isnull);
5933 is_instead = DatumGetBool(dat);
5934
5935 fno = SPI_fnumber(rulettc, "ev_qual");
5937 Assert(ev_qual != NULL);
5938
5939 fno = SPI_fnumber(rulettc, "ev_action");
5941 Assert(ev_action != NULL);
5942 actions = (List *) stringToNode(ev_action);
5943
5944 if (list_length(actions) != 1)
5945 {
5946 /* keep output buffer empty and leave */
5947 return;
5948 }
5949
5950 query = (Query *) linitial(actions);
5951
5952 if (ev_type != '1' || !is_instead ||
5953 strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5954 {
5955 /* keep output buffer empty and leave */
5956 return;
5957 }
5958
5960
5962 prettyFlags, wrapColumn, 0);
5964
5966}
5967
5968
5969/* ----------
5970 * get_query_def - Parse back one query parsetree
5971 *
5972 * query: parsetree to be displayed
5973 * buf: output text is appended to buf
5974 * parentnamespace: list (initially empty) of outer-level deparse_namespace's
5975 * resultDesc: if not NULL, the output tuple descriptor for the view
5976 * represented by a SELECT query. We use the column names from it
5977 * to label SELECT output columns, in preference to names in the query
5978 * colNamesVisible: true if the surrounding context cares about the output
5979 * column names at all (as, for example, an EXISTS() context does not);
5980 * when false, we can suppress dummy column labels such as "?column?"
5981 * prettyFlags: bitmask of PRETTYFLAG_XXX options
5982 * wrapColumn: maximum line length, or -1 to disable wrapping
5983 * startIndent: initial indentation amount
5984 * ----------
5985 */
5986static void
5988 TupleDesc resultDesc, bool colNamesVisible,
5989 int prettyFlags, int wrapColumn, int startIndent)
5990{
5991 deparse_context context;
5993 int rtable_size;
5994
5995 /* Guard against excessively long or deeply-nested queries */
5998
5999 rtable_size = query->hasGroupRTE ?
6000 list_length(query->rtable) - 1 :
6001 list_length(query->rtable);
6002
6003 /*
6004 * Replace any Vars in the query's targetlist and havingQual that
6005 * reference GROUP outputs with the underlying grouping expressions.
6006 *
6007 * We can safely pass NULL for the root here. Preserving varnullingrels
6008 * makes no difference to the deparsed source text.
6009 */
6010 if (query->hasGroupRTE)
6011 {
6012 query->targetList = (List *)
6013 flatten_group_exprs(NULL, query, (Node *) query->targetList);
6014 query->havingQual =
6015 flatten_group_exprs(NULL, query, query->havingQual);
6016 }
6017
6018 /*
6019 * Before we begin to examine the query, acquire locks on referenced
6020 * relations, and fix up deleted columns in JOIN RTEs. This ensures
6021 * consistent results. Note we assume it's OK to scribble on the passed
6022 * querytree!
6023 *
6024 * We are only deparsing the query (we are not about to execute it), so we
6025 * only need AccessShareLock on the relations it mentions.
6026 */
6027 AcquireRewriteLocks(query, false, false);
6028
6029 context.buf = buf;
6031 context.resultDesc = NULL;
6032 context.targetList = NIL;
6033 context.windowClause = NIL;
6034 context.varprefix = (parentnamespace != NIL ||
6035 rtable_size != 1);
6036 context.prettyFlags = prettyFlags;
6037 context.wrapColumn = wrapColumn;
6038 context.indentLevel = startIndent;
6039 context.colNamesVisible = colNamesVisible;
6040 context.inGroupBy = false;
6041 context.varInOrderBy = false;
6042 context.appendparents = NULL;
6043
6045
6046 switch (query->commandType)
6047 {
6048 case CMD_SELECT:
6049 /* We set context.resultDesc only if it's a SELECT */
6050 context.resultDesc = resultDesc;
6051 get_select_query_def(query, &context);
6052 break;
6053
6054 case CMD_UPDATE:
6055 get_update_query_def(query, &context);
6056 break;
6057
6058 case CMD_INSERT:
6059 get_insert_query_def(query, &context);
6060 break;
6061
6062 case CMD_DELETE:
6063 get_delete_query_def(query, &context);
6064 break;
6065
6066 case CMD_MERGE:
6067 get_merge_query_def(query, &context);
6068 break;
6069
6070 case CMD_NOTHING:
6071 appendStringInfoString(buf, "NOTHING");
6072 break;
6073
6074 case CMD_UTILITY:
6075 get_utility_query_def(query, &context);
6076 break;
6077
6078 default:
6079 elog(ERROR, "unrecognized query command type: %d",
6080 query->commandType);
6081 break;
6082 }
6083}
6084
6085/* ----------
6086 * get_values_def - Parse back a VALUES list
6087 * ----------
6088 */
6089static void
6090get_values_def(List *values_lists, deparse_context *context)
6091{
6092 StringInfo buf = context->buf;
6093 bool first_list = true;
6094 ListCell *vtl;
6095
6096 appendStringInfoString(buf, "VALUES ");
6097
6098 foreach(vtl, values_lists)
6099 {
6100 List *sublist = (List *) lfirst(vtl);
6101 bool first_col = true;
6102 ListCell *lc;
6103
6104 if (first_list)
6105 first_list = false;
6106 else
6108
6110 foreach(lc, sublist)
6111 {
6112 Node *col = (Node *) lfirst(lc);
6113
6114 if (first_col)
6115 first_col = false;
6116 else
6118
6119 /*
6120 * Print the value. Whole-row Vars need special treatment.
6121 */
6122 get_rule_expr_toplevel(col, context, false);
6123 }
6125 }
6126}
6127
6128/* ----------
6129 * get_with_clause - Parse back a WITH clause
6130 * ----------
6131 */
6132static void
6133get_with_clause(Query *query, deparse_context *context)
6134{
6135 StringInfo buf = context->buf;
6136 const char *sep;
6137 ListCell *l;
6138
6139 if (query->cteList == NIL)
6140 return;
6141
6142 if (PRETTY_INDENT(context))
6143 {
6144 context->indentLevel += PRETTYINDENT_STD;
6146 }
6147
6148 if (query->hasRecursive)
6149 sep = "WITH RECURSIVE ";
6150 else
6151 sep = "WITH ";
6152 foreach(l, query->cteList)
6153 {
6155
6158 if (cte->aliascolnames)
6159 {
6160 bool first = true;
6161 ListCell *col;
6162
6164 foreach(col, cte->aliascolnames)
6165 {
6166 if (first)
6167 first = false;
6168 else
6172 }
6174 }
6175 appendStringInfoString(buf, " AS ");
6176 switch (cte->ctematerialized)
6177 {
6179 break;
6181 appendStringInfoString(buf, "MATERIALIZED ");
6182 break;
6184 appendStringInfoString(buf, "NOT MATERIALIZED ");
6185 break;
6186 }
6188 if (PRETTY_INDENT(context))
6189 appendContextKeyword(context, "", 0, 0, 0);
6190 get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
6191 true,
6192 context->prettyFlags, context->wrapColumn,
6193 context->indentLevel);
6194 if (PRETTY_INDENT(context))
6195 appendContextKeyword(context, "", 0, 0, 0);
6197
6198 if (cte->search_clause)
6199 {
6200 bool first = true;
6201 ListCell *lc;
6202
6203 appendStringInfo(buf, " SEARCH %s FIRST BY ",
6204 cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
6205
6206 foreach(lc, cte->search_clause->search_col_list)
6207 {
6208 if (first)
6209 first = false;
6210 else
6214 }
6215
6216 appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
6217 }
6218
6219 if (cte->cycle_clause)
6220 {
6221 bool first = true;
6222 ListCell *lc;
6223
6224 appendStringInfoString(buf, " CYCLE ");
6225
6226 foreach(lc, cte->cycle_clause->cycle_col_list)
6227 {
6228 if (first)
6229 first = false;
6230 else
6234 }
6235
6236 appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
6237
6238 {
6239 Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
6240 Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
6241
6242 if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
6243 cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
6244 {
6245 appendStringInfoString(buf, " TO ");
6246 get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
6247 appendStringInfoString(buf, " DEFAULT ");
6248 get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
6249 }
6250 }
6251
6252 appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
6253 }
6254
6255 sep = ", ";
6256 }
6257
6258 if (PRETTY_INDENT(context))
6259 {
6260 context->indentLevel -= PRETTYINDENT_STD;
6261 appendContextKeyword(context, "", 0, 0, 0);
6262 }
6263 else
6265}
6266
6267/* ----------
6268 * get_select_query_def - Parse back a SELECT parsetree
6269 * ----------
6270 */
6271static void
6273{
6274 StringInfo buf = context->buf;
6275 bool force_colno;
6276 ListCell *l;
6277
6278 /* Insert the WITH clause if given */
6279 get_with_clause(query, context);
6280
6281 /* Subroutines may need to consult the SELECT targetlist and windowClause */
6282 context->targetList = query->targetList;
6283 context->windowClause = query->windowClause;
6284
6285 /*
6286 * If the Query node has a setOperations tree, then it's the top level of
6287 * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
6288 * fields are interesting in the top query itself.
6289 */
6290 if (query->setOperations)
6291 {
6292 get_setop_query(query->setOperations, query, context);
6293 /* ORDER BY clauses must be simple in this case */
6294 force_colno = true;
6295 }
6296 else
6297 {
6298 get_basic_select_query(query, context);
6299 force_colno = false;
6300 }
6301
6302 /* Add the ORDER BY clause if given */
6303 if (query->sortClause != NIL)
6304 {
6305 appendContextKeyword(context, " ORDER BY ",
6307 get_rule_orderby(query->sortClause, query->targetList,
6308 force_colno, context);
6309 }
6310
6311 /*
6312 * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
6313 * standard spelling of LIMIT.
6314 */
6315 if (query->limitOffset != NULL)
6316 {
6317 appendContextKeyword(context, " OFFSET ",
6319 get_rule_expr(query->limitOffset, context, false);
6320 }
6321 if (query->limitCount != NULL)
6322 {
6323 if (query->limitOption == LIMIT_OPTION_WITH_TIES)
6324 {
6325 /*
6326 * The limitCount arg is a c_expr, so it needs parens. Simple
6327 * literals and function expressions would not need parens, but
6328 * unfortunately it's hard to tell if the expression will be
6329 * printed as a simple literal like 123 or as a typecast
6330 * expression, like '-123'::int4. The grammar accepts the former
6331 * without quoting, but not the latter.
6332 */
6333 appendContextKeyword(context, " FETCH FIRST ",
6336 get_rule_expr(query->limitCount, context, false);
6338 appendStringInfoString(buf, " ROWS WITH TIES");
6339 }
6340 else
6341 {
6342 appendContextKeyword(context, " LIMIT ",
6344 if (IsA(query->limitCount, Const) &&
6345 ((Const *) query->limitCount)->constisnull)
6347 else
6348 get_rule_expr(query->limitCount, context, false);
6349 }
6350 }
6351
6352 /* Add FOR [KEY] UPDATE/SHARE clauses if present */
6353 if (query->hasForUpdate)
6354 {
6355 foreach(l, query->rowMarks)
6356 {
6357 RowMarkClause *rc = (RowMarkClause *) lfirst(l);
6358
6359 /* don't print implicit clauses */
6360 if (rc->pushedDown)
6361 continue;
6362
6363 appendContextKeyword(context,
6366
6367 appendStringInfo(buf, " OF %s",
6369 context)));
6370 if (rc->waitPolicy == LockWaitError)
6371 appendStringInfoString(buf, " NOWAIT");
6372 else if (rc->waitPolicy == LockWaitSkip)
6373 appendStringInfoString(buf, " SKIP LOCKED");
6374 }
6375 }
6376}
6377
6378static char *
6380{
6381 switch (strength)
6382 {
6383 case LCS_NONE:
6384 /* we intentionally throw an error for LCS_NONE */
6385 elog(ERROR, "unrecognized LockClauseStrength %d",
6386 (int) strength);
6387 break;
6388 case LCS_FORKEYSHARE:
6389 return " FOR KEY SHARE";
6390 case LCS_FORSHARE:
6391 return " FOR SHARE";
6392 case LCS_FORNOKEYUPDATE:
6393 return " FOR NO KEY UPDATE";
6394 case LCS_FORUPDATE:
6395 return " FOR UPDATE";
6396 }
6397 return NULL; /* keep compiler quiet */
6398}
6399
6400/*
6401 * Detect whether query looks like SELECT ... FROM VALUES(),
6402 * with no need to rename the output columns of the VALUES RTE.
6403 * If so, return the VALUES RTE. Otherwise return NULL.
6404 */
6405static RangeTblEntry *
6406get_simple_values_rte(Query *query, TupleDesc resultDesc)
6407{
6409 ListCell *lc;
6410
6411 /*
6412 * We want to detect a match even if the Query also contains OLD or NEW
6413 * rule RTEs. So the idea is to scan the rtable and see if there is only
6414 * one inFromCl RTE that is a VALUES RTE.
6415 */
6416 foreach(lc, query->rtable)
6417 {
6419
6420 if (rte->rtekind == RTE_VALUES && rte->inFromCl)
6421 {
6422 if (result)
6423 return NULL; /* multiple VALUES (probably not possible) */
6424 result = rte;
6425 }
6426 else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
6427 continue; /* ignore rule entries */
6428 else
6429 return NULL; /* something else -> not simple VALUES */
6430 }
6431
6432 /*
6433 * We don't need to check the targetlist in any great detail, because
6434 * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
6435 * appear inside auto-generated sub-queries with very restricted
6436 * structure. However, DefineView might have modified the tlist by
6437 * injecting new column aliases, or we might have some other column
6438 * aliases forced by a resultDesc. We can only simplify if the RTE's
6439 * column names match the names that get_target_list() would select.
6440 */
6441 if (result)
6442 {
6443 ListCell *lcn;
6444 int colno;
6445
6446 if (list_length(query->targetList) != list_length(result->eref->colnames))
6447 return NULL; /* this probably cannot happen */
6448 colno = 0;
6449 forboth(lc, query->targetList, lcn, result->eref->colnames)
6450 {
6452 char *cname = strVal(lfirst(lcn));
6453 char *colname;
6454
6455 if (tle->resjunk)
6456 return NULL; /* this probably cannot happen */
6457
6458 /* compute name that get_target_list would use for column */
6459 colno++;
6460 if (resultDesc && colno <= resultDesc->natts)
6461 colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6462 else
6463 colname = tle->resname;
6464
6465 /* does it match the VALUES RTE? */
6466 if (colname == NULL || strcmp(colname, cname) != 0)
6467 return NULL; /* column name has been changed */
6468 }
6469 }
6470
6471 return result;
6472}
6473
6474static void
6476{
6477 StringInfo buf = context->buf;
6479 char *sep;
6480 ListCell *l;
6481
6482 if (PRETTY_INDENT(context))
6483 {
6484 context->indentLevel += PRETTYINDENT_STD;
6486 }
6487
6488 /*
6489 * If the query looks like SELECT * FROM (VALUES ...), then print just the
6490 * VALUES part. This reverses what transformValuesClause() did at parse
6491 * time.
6492 */
6493 values_rte = get_simple_values_rte(query, context->resultDesc);
6494 if (values_rte)
6495 {
6496 get_values_def(values_rte->values_lists, context);
6497 return;
6498 }
6499
6500 /*
6501 * Build up the query string - first we say SELECT
6502 */
6503 if (query->isReturn)
6504 appendStringInfoString(buf, "RETURN");
6505 else
6506 appendStringInfoString(buf, "SELECT");
6507
6508 /* Add the DISTINCT clause if given */
6509 if (query->distinctClause != NIL)
6510 {
6511 if (query->hasDistinctOn)
6512 {
6513 appendStringInfoString(buf, " DISTINCT ON (");
6514 sep = "";
6515 foreach(l, query->distinctClause)
6516 {
6518
6520 get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6521 false, context);
6522 sep = ", ";
6523 }
6525 }
6526 else
6527 appendStringInfoString(buf, " DISTINCT");
6528 }
6529
6530 /* Then we tell what to select (the targetlist) */
6531 get_target_list(query->targetList, context);
6532
6533 /* Add the FROM clause if needed */
6534 get_from_clause(query, " FROM ", context);
6535
6536 /* Add the WHERE clause if given */
6537 if (query->jointree->quals != NULL)
6538 {
6539 appendContextKeyword(context, " WHERE ",
6541 get_rule_expr(query->jointree->quals, context, false);
6542 }
6543
6544 /* Add the GROUP BY clause if given */
6545 if (query->groupClause != NULL || query->groupingSets != NULL)
6546 {
6547 bool save_ingroupby;
6548
6549 appendContextKeyword(context, " GROUP BY ",
6551 if (query->groupDistinct)
6552 appendStringInfoString(buf, "DISTINCT ");
6553
6554 save_ingroupby = context->inGroupBy;
6555 context->inGroupBy = true;
6556
6557 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 {
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 if (IsA(ctor->func, Var))
12392 {
12393 /*
12394 * If the aggregate is computed by a lower plan node, setrefs.c will
12395 * have replaced the Aggref or WindowFunc with a Var referencing that
12396 * node's output. Chase the Var back to it so we can still print the
12397 * original JSON aggregate syntax. This only happens in EXPLAIN.
12398 */
12399 resolve_special_varno((Node *) ctor->func, context,
12401 }
12402 else
12403 elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
12404 nodeTag(ctor->func));
12405}
12406
12407/*
12408 * Deparse a JsonConstructorExpr whose aggregate is computed by a lower plan
12409 * node; resolve_special_varno has located the underlying Aggref/WindowFunc.
12410 */
12411static void
12413 void *callback_arg)
12414{
12416
12417 if (!IsA(node, Aggref) && !IsA(node, WindowFunc))
12418 elog(ERROR, "JSON aggregate constructor does not point to an Aggref or WindowFunc");
12419
12420 /* Flat copy suffices; we only replace func. */
12421 ctor = *(JsonConstructorExpr *) callback_arg;
12422 ctor.func = (Expr *) node;
12423 get_json_constructor(&ctor, context, false);
12424}
12425
12426/*
12427 * simple_quote_literal - Format a string as a SQL literal, append to buf
12428 */
12429static void
12431{
12432 const char *valptr;
12433
12434 /*
12435 * We always form the string literal according to standard SQL rules.
12436 */
12438 for (valptr = val; *valptr; valptr++)
12439 {
12440 char ch = *valptr;
12441
12442 if (SQL_STR_DOUBLE(ch, false))
12445 }
12447}
12448
12449
12450/* ----------
12451 * get_sublink_expr - Parse back a sublink
12452 * ----------
12453 */
12454static void
12456{
12457 StringInfo buf = context->buf;
12458 Query *query = (Query *) (sublink->subselect);
12459 char *opname = NULL;
12460 bool need_paren;
12461
12462 if (sublink->subLinkType == ARRAY_SUBLINK)
12463 appendStringInfoString(buf, "ARRAY(");
12464 else
12466
12467 /*
12468 * Note that we print the name of only the first operator, when there are
12469 * multiple combining operators. This is an approximation that could go
12470 * wrong in various scenarios (operators in different schemas, renamed
12471 * operators, etc) but there is not a whole lot we can do about it, since
12472 * the syntax allows only one operator to be shown.
12473 */
12474 if (sublink->testexpr)
12475 {
12476 if (IsA(sublink->testexpr, OpExpr))
12477 {
12478 /* single combining operator */
12479 OpExpr *opexpr = (OpExpr *) sublink->testexpr;
12480
12481 get_rule_expr(linitial(opexpr->args), context, true);
12482 opname = generate_operator_name(opexpr->opno,
12483 exprType(linitial(opexpr->args)),
12484 exprType(lsecond(opexpr->args)));
12485 }
12486 else if (IsA(sublink->testexpr, BoolExpr))
12487 {
12488 /* multiple combining operators, = or <> cases */
12489 char *sep;
12490 ListCell *l;
12491
12493 sep = "";
12494 foreach(l, ((BoolExpr *) sublink->testexpr)->args)
12495 {
12496 OpExpr *opexpr = lfirst_node(OpExpr, l);
12497
12499 get_rule_expr(linitial(opexpr->args), context, true);
12500 if (!opname)
12502 exprType(linitial(opexpr->args)),
12503 exprType(lsecond(opexpr->args)));
12504 sep = ", ";
12505 }
12507 }
12508 else if (IsA(sublink->testexpr, RowCompareExpr))
12509 {
12510 /* multiple combining operators, < <= > >= cases */
12512
12514 get_rule_expr((Node *) rcexpr->largs, context, true);
12516 exprType(linitial(rcexpr->largs)),
12517 exprType(linitial(rcexpr->rargs)));
12519 }
12520 else
12521 elog(ERROR, "unrecognized testexpr type: %d",
12522 (int) nodeTag(sublink->testexpr));
12523 }
12524
12525 need_paren = true;
12526
12527 switch (sublink->subLinkType)
12528 {
12529 case EXISTS_SUBLINK:
12530 appendStringInfoString(buf, "EXISTS ");
12531 break;
12532
12533 case ANY_SUBLINK:
12534 if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
12535 appendStringInfoString(buf, " IN ");
12536 else
12537 appendStringInfo(buf, " %s ANY ", opname);
12538 break;
12539
12540 case ALL_SUBLINK:
12541 appendStringInfo(buf, " %s ALL ", opname);
12542 break;
12543
12544 case ROWCOMPARE_SUBLINK:
12545 appendStringInfo(buf, " %s ", opname);
12546 break;
12547
12548 case EXPR_SUBLINK:
12549 case MULTIEXPR_SUBLINK:
12550 case ARRAY_SUBLINK:
12551 need_paren = false;
12552 break;
12553
12554 case CTE_SUBLINK: /* shouldn't occur in a SubLink */
12555 default:
12556 elog(ERROR, "unrecognized sublink type: %d",
12557 (int) sublink->subLinkType);
12558 break;
12559 }
12560
12561 if (need_paren)
12563
12564 get_query_def(query, buf, context->namespaces, NULL, false,
12565 context->prettyFlags, context->wrapColumn,
12566 context->indentLevel);
12567
12568 if (need_paren)
12570 else
12572}
12573
12574
12575/* ----------
12576 * get_xmltable - Parse back a XMLTABLE function
12577 * ----------
12578 */
12579static void
12581{
12582 StringInfo buf = context->buf;
12583
12584 appendStringInfoString(buf, "XMLTABLE(");
12585
12586 if (tf->ns_uris != NIL)
12587 {
12588 ListCell *lc1,
12589 *lc2;
12590 bool first = true;
12591
12592 appendStringInfoString(buf, "XMLNAMESPACES (");
12593 forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
12594 {
12595 Node *expr = (Node *) lfirst(lc1);
12597
12598 if (!first)
12600 else
12601 first = false;
12602
12603 if (ns_node != NULL)
12604 {
12605 get_rule_expr(expr, context, showimplicit);
12606 appendStringInfo(buf, " AS %s",
12608 }
12609 else
12610 {
12611 appendStringInfoString(buf, "DEFAULT ");
12612 get_rule_expr(expr, context, showimplicit);
12613 }
12614 }
12616 }
12617
12619 get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
12620 appendStringInfoString(buf, ") PASSING (");
12621 get_rule_expr((Node *) tf->docexpr, context, showimplicit);
12623
12624 if (tf->colexprs != NIL)
12625 {
12626 ListCell *l1;
12627 ListCell *l2;
12628 ListCell *l3;
12629 ListCell *l4;
12630 ListCell *l5;
12631 int colnum = 0;
12632
12633 appendStringInfoString(buf, " COLUMNS ");
12634 forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
12635 l4, tf->colexprs, l5, tf->coldefexprs)
12636 {
12637 char *colname = strVal(lfirst(l1));
12638 Oid typid = lfirst_oid(l2);
12639 int32 typmod = lfirst_int(l3);
12640 Node *colexpr = (Node *) lfirst(l4);
12641 Node *coldefexpr = (Node *) lfirst(l5);
12642 bool ordinality = (tf->ordinalitycol == colnum);
12643 bool notnull = bms_is_member(colnum, tf->notnulls);
12644
12645 if (colnum > 0)
12647 colnum++;
12648
12649 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12650 ordinality ? "FOR ORDINALITY" :
12651 format_type_with_typemod(typid, typmod));
12652 if (ordinality)
12653 continue;
12654
12655 if (coldefexpr != NULL)
12656 {
12657 appendStringInfoString(buf, " DEFAULT (");
12658 get_rule_expr((Node *) coldefexpr, context, showimplicit);
12660 }
12661 if (colexpr != NULL)
12662 {
12663 appendStringInfoString(buf, " PATH (");
12664 get_rule_expr((Node *) colexpr, context, showimplicit);
12666 }
12667 if (notnull)
12668 appendStringInfoString(buf, " NOT NULL");
12669 }
12670 }
12671
12673}
12674
12675/*
12676 * get_json_table_nested_columns - Parse back nested JSON_TABLE columns
12677 */
12678static void
12680 deparse_context *context, bool showimplicit,
12681 bool needcomma)
12682{
12684 {
12686
12687 if (needcomma)
12688 appendStringInfoChar(context->buf, ',');
12689
12690 appendStringInfoChar(context->buf, ' ');
12691 appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
12692 get_const_expr(scan->path->value, context, -1);
12693 appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
12694 get_json_table_columns(tf, scan, context, showimplicit);
12695 }
12696 else if (IsA(plan, JsonTableSiblingJoin))
12697 {
12699
12701 needcomma);
12703 true);
12704 }
12705}
12706
12707/*
12708 * json_table_plan_is_default - does this plan match the implicit default?
12709 *
12710 * When no PLAN clause is given, JSON_TABLE builds a default plan that joins
12711 * every nested path to its parent with OUTER and every set of sibling paths
12712 * with UNION (see transformJsonTableColumns()). Such a plan is fully implied
12713 * by the NESTED COLUMNS structure, so we need not (and, to match the input,
12714 * should not) print a PLAN clause for it; we only deparse a PLAN clause when
12715 * the plan deviates from the default, i.e. uses an INNER or CROSS join
12716 * somewhere. This follows the usual ruleutils convention of omitting a clause
12717 * that merely restates the default (cf. get_json_expr_options() for ON
12718 * EMPTY/ON ERROR, or the NULLS FIRST/LAST handling in get_rule_orderby()).
12719 */
12720static bool
12722{
12724 {
12726
12727 if (scan->child)
12728 {
12729 if (!scan->outerJoin)
12730 return false; /* INNER is not the default */
12731 return json_table_plan_is_default(scan->child);
12732 }
12733
12734 return true;
12735 }
12736 else
12737 {
12739
12740 if (join->cross)
12741 return false; /* CROSS is not the default */
12742 return json_table_plan_is_default(join->lplan) &&
12744 }
12745}
12746
12747/*
12748 * get_json_table_plan - Parse back a JSON_TABLE plan
12749 */
12750static void
12752 bool parenthesize)
12753{
12754 if (parenthesize)
12755 appendStringInfoChar(context->buf, '(');
12756
12758 {
12760
12762
12763 if (s->child)
12764 {
12765 appendStringInfoString(context->buf,
12766 s->outerJoin ? " OUTER " : " INNER ");
12767 get_json_table_plan(tf, s->child, context,
12769 castNode(JsonTablePathScan, s->child)->child);
12770 }
12771 }
12772 else if (IsA(plan, JsonTableSiblingJoin))
12773 {
12775
12776 get_json_table_plan(tf, j->lplan, context,
12777 IsA(j->lplan, JsonTableSiblingJoin) ||
12778 castNode(JsonTablePathScan, j->lplan)->child);
12779
12780 appendStringInfoString(context->buf, j->cross ? " CROSS " : " UNION ");
12781
12782 get_json_table_plan(tf, j->rplan, context,
12783 IsA(j->rplan, JsonTableSiblingJoin) ||
12784 castNode(JsonTablePathScan, j->rplan)->child);
12785 }
12786
12787 if (parenthesize)
12788 appendStringInfoChar(context->buf, ')');
12789}
12790
12791/*
12792 * get_json_table_columns - Parse back JSON_TABLE columns
12793 */
12794static void
12796 deparse_context *context,
12797 bool showimplicit)
12798{
12799 StringInfo buf = context->buf;
12804 int colnum = 0;
12805
12807 appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12808
12809 if (PRETTY_INDENT(context))
12810 context->indentLevel += PRETTYINDENT_VAR;
12811
12812 forfour(lc_colname, tf->colnames,
12813 lc_coltype, tf->coltypes,
12814 lc_coltypmod, tf->coltypmods,
12815 lc_colvalexpr, tf->colvalexprs)
12816 {
12817 char *colname = strVal(lfirst(lc_colname));
12818 JsonExpr *colexpr;
12819 Oid typid;
12820 int32 typmod;
12821 bool ordinality;
12823
12824 typid = lfirst_oid(lc_coltype);
12825 typmod = lfirst_int(lc_coltypmod);
12827
12828 /* Skip columns that don't belong to this scan. */
12829 if (scan->colMin < 0 || colnum < scan->colMin)
12830 {
12831 colnum++;
12832 continue;
12833 }
12834 if (colnum > scan->colMax)
12835 break;
12836
12837 if (colnum > scan->colMin)
12839
12840 colnum++;
12841
12842 ordinality = !colexpr;
12843
12844 appendContextKeyword(context, "", 0, 0, 0);
12845
12846 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12847 ordinality ? "FOR ORDINALITY" :
12848 format_type_with_typemod(typid, typmod));
12849 if (ordinality)
12850 continue;
12851
12852 /*
12853 * Set default_behavior to guide get_json_expr_options() on whether to
12854 * emit the ON ERROR / EMPTY clauses.
12855 */
12856 if (colexpr->op == JSON_EXISTS_OP)
12857 {
12858 appendStringInfoString(buf, " EXISTS");
12860 }
12861 else
12862 {
12863 if (colexpr->op == JSON_QUERY_OP)
12864 {
12865 char typcategory;
12866 bool typispreferred;
12867
12869
12872 colexpr->format->format_type == JS_FORMAT_JSONB ?
12873 " FORMAT JSONB" : " FORMAT JSON");
12874 }
12875
12877 }
12878
12879 appendStringInfoString(buf, " PATH ");
12880
12881 get_json_path_spec(colexpr->path_spec, context, showimplicit);
12882
12883 get_json_expr_options(colexpr, context, default_behavior);
12884 }
12885
12886 if (scan->child)
12888 scan->colMin >= 0);
12889
12890 if (PRETTY_INDENT(context))
12891 context->indentLevel -= PRETTYINDENT_VAR;
12892
12893 appendContextKeyword(context, ")", 0, 0, 0);
12894}
12895
12896/* ----------
12897 * get_json_table - Parse back a JSON_TABLE function
12898 * ----------
12899 */
12900static void
12902{
12903 StringInfo buf = context->buf;
12906
12907 appendStringInfoString(buf, "JSON_TABLE(");
12908
12909 if (PRETTY_INDENT(context))
12910 context->indentLevel += PRETTYINDENT_VAR;
12911
12912 appendContextKeyword(context, "", 0, 0, 0);
12913
12914 get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12915
12917
12918 get_const_expr(root->path->value, context, -1);
12919
12920 appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12921
12922 if (jexpr->passing_values)
12923 {
12924 ListCell *lc1,
12925 *lc2;
12926 bool needcomma = false;
12927
12929 appendContextKeyword(context, "PASSING ", 0, 0, 0);
12930
12931 if (PRETTY_INDENT(context))
12932 context->indentLevel += PRETTYINDENT_VAR;
12933
12934 forboth(lc1, jexpr->passing_names,
12935 lc2, jexpr->passing_values)
12936 {
12937 if (needcomma)
12939 needcomma = true;
12940
12941 appendContextKeyword(context, "", 0, 0, 0);
12942
12943 get_rule_expr((Node *) lfirst(lc2), context, false);
12944 appendStringInfo(buf, " AS %s",
12946 );
12947 }
12948
12949 if (PRETTY_INDENT(context))
12950 context->indentLevel -= PRETTYINDENT_VAR;
12951 }
12952
12953 get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12954 showimplicit);
12955
12956 /*
12957 * Deparse a PLAN clause only for a non-default plan; the default plan is
12958 * implied by the NESTED COLUMNS structure (see
12959 * json_table_plan_is_default).
12960 */
12962 {
12964 appendContextKeyword(context, "PLAN ", 0, 0, 0);
12965 get_json_table_plan(tf, (JsonTablePlan *) root, context, true);
12966 }
12967
12968 if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12969 get_json_behavior(jexpr->on_error, context, "ERROR");
12970
12971 if (PRETTY_INDENT(context))
12972 context->indentLevel -= PRETTYINDENT_VAR;
12973
12974 appendContextKeyword(context, ")", 0, 0, 0);
12975}
12976
12977/* ----------
12978 * get_tablefunc - Parse back a table function
12979 * ----------
12980 */
12981static void
12983{
12984 /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12985
12986 if (tf->functype == TFT_XMLTABLE)
12987 get_xmltable(tf, context, showimplicit);
12988 else if (tf->functype == TFT_JSON_TABLE)
12989 get_json_table(tf, context, showimplicit);
12990}
12991
12992/* ----------
12993 * get_from_clause - Parse back a FROM clause
12994 *
12995 * "prefix" is the keyword that denotes the start of the list of FROM
12996 * elements. It is FROM when used to parse back SELECT and UPDATE, but
12997 * is USING when parsing back DELETE.
12998 * ----------
12999 */
13000static void
13001get_from_clause(Query *query, const char *prefix, deparse_context *context)
13002{
13003 StringInfo buf = context->buf;
13004 bool first = true;
13005 ListCell *l;
13006
13007 /*
13008 * We use the query's jointree as a guide to what to print. However, we
13009 * must ignore auto-added RTEs that are marked not inFromCl. (These can
13010 * only appear at the top level of the jointree, so it's sufficient to
13011 * check here.) This check also ensures we ignore the rule pseudo-RTEs
13012 * for NEW and OLD.
13013 */
13014 foreach(l, query->jointree->fromlist)
13015 {
13016 Node *jtnode = (Node *) lfirst(l);
13017
13018 if (IsA(jtnode, RangeTblRef))
13019 {
13020 int varno = ((RangeTblRef *) jtnode)->rtindex;
13021 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
13022
13023 if (!rte->inFromCl)
13024 continue;
13025 }
13026
13027 if (first)
13028 {
13029 appendContextKeyword(context, prefix,
13031 first = false;
13032
13033 get_from_clause_item(jtnode, query, context);
13034 }
13035 else
13036 {
13038
13040
13041 /*
13042 * Put the new FROM item's text into itembuf so we can decide
13043 * after we've got it whether or not it needs to go on a new line.
13044 */
13046 context->buf = &itembuf;
13047
13048 get_from_clause_item(jtnode, query, context);
13049
13050 /* Restore context's output buffer */
13051 context->buf = buf;
13052
13053 /* Consider line-wrapping if enabled */
13054 if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
13055 {
13056 /* Does the new item start with a new line? */
13057 if (itembuf.len > 0 && itembuf.data[0] == '\n')
13058 {
13059 /* If so, we shouldn't add anything */
13060 /* instead, remove any trailing spaces currently in buf */
13062 }
13063 else
13064 {
13065 char *trailing_nl;
13066
13067 /* Locate the start of the current line in the buffer */
13068 trailing_nl = strrchr(buf->data, '\n');
13069 if (trailing_nl == NULL)
13070 trailing_nl = buf->data;
13071 else
13072 trailing_nl++;
13073
13074 /*
13075 * Add a newline, plus some indentation, if the new item
13076 * would cause an overflow.
13077 */
13078 if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
13082 }
13083 }
13084
13085 /* Add the new item */
13087
13088 /* clean up */
13089 pfree(itembuf.data);
13090 }
13091 }
13092}
13093
13094static void
13095get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
13096{
13097 StringInfo buf = context->buf;
13099
13100 if (IsA(jtnode, RangeTblRef))
13101 {
13102 int varno = ((RangeTblRef *) jtnode)->rtindex;
13103 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
13106
13107 if (rte->lateral)
13108 appendStringInfoString(buf, "LATERAL ");
13109
13110 /* Print the FROM item proper */
13111 switch (rte->rtekind)
13112 {
13113 case RTE_RELATION:
13114 /* Normal relation RTE */
13115 appendStringInfo(buf, "%s%s",
13118 context->namespaces));
13119 break;
13120 case RTE_SUBQUERY:
13121 /* Subquery RTE */
13123 get_query_def(rte->subquery, buf, context->namespaces, NULL,
13124 true,
13125 context->prettyFlags, context->wrapColumn,
13126 context->indentLevel);
13128 break;
13129 case RTE_FUNCTION:
13130 /* Function RTE */
13131 rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
13132
13133 /*
13134 * Omit ROWS FROM() syntax for just one function, unless it
13135 * has both a coldeflist and WITH ORDINALITY. If it has both,
13136 * we must use ROWS FROM() syntax to avoid ambiguity about
13137 * whether the coldeflist includes the ordinality column.
13138 */
13139 if (list_length(rte->functions) == 1 &&
13140 (rtfunc1->funccolnames == NIL || !rte->funcordinality))
13141 {
13142 get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
13143 /* we'll print the coldeflist below, if it has one */
13144 }
13145 else
13146 {
13147 bool all_unnest;
13148 ListCell *lc;
13149
13150 /*
13151 * If all the function calls in the list are to unnest,
13152 * and none need a coldeflist, then collapse the list back
13153 * down to UNNEST(args). (If we had more than one
13154 * built-in unnest function, this would get more
13155 * difficult.)
13156 *
13157 * XXX This is pretty ugly, since it makes not-terribly-
13158 * future-proof assumptions about what the parser would do
13159 * with the output; but the alternative is to emit our
13160 * nonstandard ROWS FROM() notation for what might have
13161 * been a perfectly spec-compliant multi-argument
13162 * UNNEST().
13163 */
13164 all_unnest = true;
13165 foreach(lc, rte->functions)
13166 {
13168
13169 if (!IsA(rtfunc->funcexpr, FuncExpr) ||
13170 ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
13171 rtfunc->funccolnames != NIL)
13172 {
13173 all_unnest = false;
13174 break;
13175 }
13176 }
13177
13178 if (all_unnest)
13179 {
13180 List *allargs = NIL;
13181
13182 foreach(lc, rte->functions)
13183 {
13185 List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
13186
13187 allargs = list_concat(allargs, args);
13188 }
13189
13190 appendStringInfoString(buf, "UNNEST(");
13191 get_rule_expr((Node *) allargs, context, true);
13193 }
13194 else
13195 {
13196 int funcno = 0;
13197
13198 appendStringInfoString(buf, "ROWS FROM(");
13199 foreach(lc, rte->functions)
13200 {
13202
13203 if (funcno > 0)
13205 get_rule_expr_funccall(rtfunc->funcexpr, context, true);
13206 if (rtfunc->funccolnames != NIL)
13207 {
13208 /* Reconstruct the column definition list */
13209 appendStringInfoString(buf, " AS ");
13211 NULL,
13212 context);
13213 }
13214 funcno++;
13215 }
13217 }
13218 /* prevent printing duplicate coldeflist below */
13219 rtfunc1 = NULL;
13220 }
13221 if (rte->funcordinality)
13222 appendStringInfoString(buf, " WITH ORDINALITY");
13223 break;
13224 case RTE_TABLEFUNC:
13225 get_tablefunc(rte->tablefunc, context, true);
13226 break;
13227 case RTE_GRAPH_TABLE:
13228 appendStringInfoString(buf, "GRAPH_TABLE (");
13230 appendStringInfoString(buf, " MATCH ");
13231 get_graph_pattern_def(rte->graph_pattern, context);
13232 appendStringInfoString(buf, " COLUMNS (");
13233 {
13234 bool first = true;
13235
13236 foreach_node(TargetEntry, te, rte->graph_table_columns)
13237 {
13238 if (!first)
13240 else
13241 first = false;
13242
13243 get_rule_expr((Node *) te->expr, context, false);
13244 appendStringInfoString(buf, " AS ");
13246 }
13247 }
13249 break;
13250 case RTE_VALUES:
13251 /* Values list RTE */
13253 get_values_def(rte->values_lists, context);
13255 break;
13256 case RTE_CTE:
13258 break;
13259 default:
13260 elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
13261 break;
13262 }
13263
13264 /* Print the relation alias, if needed */
13265 get_rte_alias(rte, varno, false, context);
13266
13267 /* Print the column definitions or aliases, if needed */
13268 if (rtfunc1 && rtfunc1->funccolnames != NIL)
13269 {
13270 /* Reconstruct the columndef list, which is also the aliases */
13272 }
13273 else
13274 {
13275 /* Else print column aliases as needed */
13277 }
13278
13279 /* Tablesample clause must go after any alias */
13280 if (rte->rtekind == RTE_RELATION && rte->tablesample)
13281 get_tablesample_def(rte->tablesample, context);
13282 }
13283 else if (IsA(jtnode, JoinExpr))
13284 {
13285 JoinExpr *j = (JoinExpr *) jtnode;
13288
13289 need_paren_on_right = PRETTY_PAREN(context) &&
13290 !IsA(j->rarg, RangeTblRef) &&
13291 !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
13292
13293 if (!PRETTY_PAREN(context) || j->alias != NULL)
13295
13296 get_from_clause_item(j->larg, query, context);
13297
13298 switch (j->jointype)
13299 {
13300 case JOIN_INNER:
13301 if (j->quals)
13302 appendContextKeyword(context, " JOIN ",
13306 else
13307 appendContextKeyword(context, " CROSS JOIN ",
13311 break;
13312 case JOIN_LEFT:
13313 appendContextKeyword(context, " LEFT JOIN ",
13317 break;
13318 case JOIN_FULL:
13319 appendContextKeyword(context, " FULL JOIN ",
13323 break;
13324 case JOIN_RIGHT:
13325 appendContextKeyword(context, " RIGHT JOIN ",
13329 break;
13330 default:
13331 elog(ERROR, "unrecognized join type: %d",
13332 (int) j->jointype);
13333 }
13334
13337 get_from_clause_item(j->rarg, query, context);
13340
13341 if (j->usingClause)
13342 {
13343 ListCell *lc;
13344 bool first = true;
13345
13346 appendStringInfoString(buf, " USING (");
13347 /* Use the assigned names, not what's in usingClause */
13348 foreach(lc, colinfo->usingNames)
13349 {
13350 char *colname = (char *) lfirst(lc);
13351
13352 if (first)
13353 first = false;
13354 else
13357 }
13359
13360 if (j->join_using_alias)
13361 appendStringInfo(buf, " AS %s",
13362 quote_identifier(j->join_using_alias->aliasname));
13363 }
13364 else if (j->quals)
13365 {
13366 appendStringInfoString(buf, " ON ");
13367 if (!PRETTY_PAREN(context))
13369 get_rule_expr(j->quals, context, false);
13370 if (!PRETTY_PAREN(context))
13372 }
13373 else if (j->jointype != JOIN_INNER)
13374 {
13375 /* If we didn't say CROSS JOIN above, we must provide an ON */
13376 appendStringInfoString(buf, " ON TRUE");
13377 }
13378
13379 if (!PRETTY_PAREN(context) || j->alias != NULL)
13381
13382 /* Yes, it's correct to put alias after the right paren ... */
13383 if (j->alias != NULL)
13384 {
13385 /*
13386 * Note that it's correct to emit an alias clause if and only if
13387 * there was one originally. Otherwise we'd be converting a named
13388 * join to unnamed or vice versa, which creates semantic
13389 * subtleties we don't want. However, we might print a different
13390 * alias name than was there originally.
13391 */
13392 appendStringInfo(buf, " %s",
13394 context)));
13396 }
13397 }
13398 else
13399 elog(ERROR, "unrecognized node type: %d",
13400 (int) nodeTag(jtnode));
13401}
13402
13403/*
13404 * get_rte_alias - print the relation's alias, if needed
13405 *
13406 * If printed, the alias is preceded by a space, or by " AS " if use_as is true.
13407 */
13408static void
13409get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
13410 deparse_context *context)
13411{
13413 char *refname = get_rtable_name(varno, context);
13415 bool printalias = false;
13416
13417 if (rte->alias != NULL)
13418 {
13419 /* Always print alias if user provided one */
13420 printalias = true;
13421 }
13422 else if (colinfo->printaliases)
13423 {
13424 /* Always print alias if we need to print column aliases */
13425 printalias = true;
13426 }
13427 else if (rte->rtekind == RTE_RELATION)
13428 {
13429 /*
13430 * No need to print alias if it's same as relation name (this would
13431 * normally be the case, but not if set_rtable_names had to resolve a
13432 * conflict).
13433 */
13434 if (strcmp(refname, get_relation_name(rte->relid)) != 0)
13435 printalias = true;
13436 }
13437 else if (rte->rtekind == RTE_FUNCTION)
13438 {
13439 /*
13440 * For a function RTE, always print alias. This covers possible
13441 * renaming of the function and/or instability of the FigureColname
13442 * rules for things that aren't simple functions. Note we'd need to
13443 * force it anyway for the columndef list case.
13444 */
13445 printalias = true;
13446 }
13447 else if (rte->rtekind == RTE_SUBQUERY ||
13448 rte->rtekind == RTE_VALUES)
13449 {
13450 /*
13451 * For a subquery, always print alias. This makes the output
13452 * SQL-spec-compliant, even though we allow such aliases to be omitted
13453 * on input.
13454 */
13455 printalias = true;
13456 }
13457 else if (rte->rtekind == RTE_CTE)
13458 {
13459 /*
13460 * No need to print alias if it's same as CTE name (this would
13461 * normally be the case, but not if set_rtable_names had to resolve a
13462 * conflict).
13463 */
13464 if (strcmp(refname, rte->ctename) != 0)
13465 printalias = true;
13466 }
13467
13468 if (printalias)
13469 appendStringInfo(context->buf, "%s%s",
13470 use_as ? " AS " : " ",
13471 quote_identifier(refname));
13472}
13473
13474/*
13475 * get_for_portion_of - print FOR PORTION OF if needed
13476 * XXX: Newlines would help here, at least when pretty-printing. But then the
13477 * alias and SET will be on their own line with a leading space.
13478 */
13479static void
13481{
13482 if (forPortionOf)
13483 {
13484 appendStringInfo(context->buf, " FOR PORTION OF %s",
13485 quote_identifier(forPortionOf->range_name));
13486
13487 /*
13488 * Try to write it as FROM ... TO ... if we received it that way,
13489 * otherwise (targetRange).
13490 */
13491 if (forPortionOf->targetFrom && forPortionOf->targetTo)
13492 {
13493 appendStringInfoString(context->buf, " FROM ");
13494 get_rule_expr(forPortionOf->targetFrom, context, false);
13495 appendStringInfoString(context->buf, " TO ");
13496 get_rule_expr(forPortionOf->targetTo, context, false);
13497 }
13498 else
13499 {
13500 appendStringInfoString(context->buf, " (");
13501 get_rule_expr(forPortionOf->targetRange, context, false);
13502 appendStringInfoChar(context->buf, ')');
13503 }
13504 }
13505}
13506
13507/*
13508 * get_column_alias_list - print column alias list for an RTE
13509 *
13510 * Caller must already have printed the relation's alias name.
13511 */
13512static void
13514{
13515 StringInfo buf = context->buf;
13516 int i;
13517 bool first = true;
13518
13519 /* Don't print aliases if not needed */
13520 if (!colinfo->printaliases)
13521 return;
13522
13523 for (i = 0; i < colinfo->num_new_cols; i++)
13524 {
13525 char *colname = colinfo->new_colnames[i];
13526
13527 if (first)
13528 {
13530 first = false;
13531 }
13532 else
13535 }
13536 if (!first)
13538}
13539
13540/*
13541 * get_from_clause_coldeflist - reproduce FROM clause coldeflist
13542 *
13543 * When printing a top-level coldeflist (which is syntactically also the
13544 * relation's column alias list), use column names from colinfo. But when
13545 * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
13546 * original coldeflist's names, which are available in rtfunc->funccolnames.
13547 * Pass NULL for colinfo to select the latter behavior.
13548 *
13549 * The coldeflist is appended immediately (no space) to buf. Caller is
13550 * responsible for ensuring that an alias or AS is present before it.
13551 */
13552static void
13555 deparse_context *context)
13556{
13557 StringInfo buf = context->buf;
13558 ListCell *l1;
13559 ListCell *l2;
13560 ListCell *l3;
13561 ListCell *l4;
13562 int i;
13563
13565
13566 i = 0;
13567 forfour(l1, rtfunc->funccoltypes,
13568 l2, rtfunc->funccoltypmods,
13569 l3, rtfunc->funccolcollations,
13570 l4, rtfunc->funccolnames)
13571 {
13572 Oid atttypid = lfirst_oid(l1);
13573 int32 atttypmod = lfirst_int(l2);
13574 Oid attcollation = lfirst_oid(l3);
13575 char *attname;
13576
13577 if (colinfo)
13578 attname = colinfo->colnames[i];
13579 else
13580 attname = strVal(lfirst(l4));
13581
13582 Assert(attname); /* shouldn't be any dropped columns here */
13583
13584 if (i > 0)
13586 appendStringInfo(buf, "%s %s",
13588 format_type_with_typemod(atttypid, atttypmod));
13589 if (OidIsValid(attcollation) &&
13590 attcollation != get_typcollation(atttypid))
13591 appendStringInfo(buf, " COLLATE %s",
13592 generate_collation_name(attcollation));
13593
13594 i++;
13595 }
13596
13598}
13599
13600/*
13601 * get_tablesample_def - print a TableSampleClause
13602 */
13603static void
13605{
13606 StringInfo buf = context->buf;
13607 Oid argtypes[1];
13608 int nargs;
13609 ListCell *l;
13610
13611 /*
13612 * We should qualify the handler's function name if it wouldn't be
13613 * resolved by lookup in the current search path.
13614 */
13615 argtypes[0] = INTERNALOID;
13616 appendStringInfo(buf, " TABLESAMPLE %s (",
13617 generate_function_name(tablesample->tsmhandler, 1,
13618 NIL, argtypes,
13619 false, NULL, false));
13620
13621 nargs = 0;
13622 foreach(l, tablesample->args)
13623 {
13624 if (nargs++ > 0)
13626 get_rule_expr((Node *) lfirst(l), context, false);
13627 }
13629
13630 if (tablesample->repeatable != NULL)
13631 {
13632 appendStringInfoString(buf, " REPEATABLE (");
13633 get_rule_expr((Node *) tablesample->repeatable, context, false);
13635 }
13636}
13637
13638/*
13639 * get_opclass_name - fetch name of an index operator class
13640 *
13641 * The opclass name is appended (after a space) to buf.
13642 *
13643 * Output is suppressed if the opclass is the default for the given
13644 * actual_datatype. (If you don't want this behavior, just pass
13645 * InvalidOid for actual_datatype.)
13646 */
13647static void
13650{
13653 char *opcname;
13654 char *nspname;
13655
13658 elog(ERROR, "cache lookup failed for opclass %u", opclass);
13660
13662 GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
13663 {
13664 /* Okay, we need the opclass name. Do we need to qualify it? */
13665 opcname = NameStr(opcrec->opcname);
13666 if (OpclassIsVisible(opclass))
13668 else
13669 {
13670 nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
13671 appendStringInfo(buf, " %s.%s",
13672 quote_identifier(nspname),
13674 }
13675 }
13677}
13678
13679/*
13680 * generate_opclass_name
13681 * Compute the name to display for an opclass specified by OID
13682 *
13683 * The result includes all necessary quoting and schema-prefixing.
13684 */
13685char *
13687{
13689
13691 get_opclass_name(opclass, InvalidOid, &buf);
13692
13693 return &buf.data[1]; /* get_opclass_name() prepends space */
13694}
13695
13696/*
13697 * processIndirection - take care of array and subfield assignment
13698 *
13699 * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
13700 * appear in the input, printing them as decoration for the base column
13701 * name (which we assume the caller just printed). We might also need to
13702 * strip CoerceToDomain nodes, but only ones that appear above assignment
13703 * nodes.
13704 *
13705 * Returns the subexpression that's to be assigned.
13706 */
13707static Node *
13709{
13710 StringInfo buf = context->buf;
13712
13713 for (;;)
13714 {
13715 if (node == NULL)
13716 break;
13717 if (IsA(node, FieldStore))
13718 {
13719 FieldStore *fstore = (FieldStore *) node;
13720 Oid typrelid;
13721 char *fieldname;
13722
13723 /* lookup tuple type */
13724 typrelid = get_typ_typrelid(fstore->resulttype);
13725 if (!OidIsValid(typrelid))
13726 elog(ERROR, "argument type %s of FieldStore is not a tuple type",
13727 format_type_be(fstore->resulttype));
13728
13729 /*
13730 * Print the field name. There should only be one target field in
13731 * stored rules. There could be more than that in executable
13732 * target lists, but this function cannot be used for that case.
13733 */
13734 Assert(list_length(fstore->fieldnums) == 1);
13735 fieldname = get_attname(typrelid,
13736 linitial_int(fstore->fieldnums), false);
13737 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
13738
13739 /*
13740 * We ignore arg since it should be an uninteresting reference to
13741 * the target column or subcolumn.
13742 */
13743 node = (Node *) linitial(fstore->newvals);
13744 }
13745 else if (IsA(node, SubscriptingRef))
13746 {
13747 SubscriptingRef *sbsref = (SubscriptingRef *) node;
13748
13749 if (sbsref->refassgnexpr == NULL)
13750 break;
13751
13752 printSubscripts(sbsref, context);
13753
13754 /*
13755 * We ignore refexpr since it should be an uninteresting reference
13756 * to the target column or subcolumn.
13757 */
13758 node = (Node *) sbsref->refassgnexpr;
13759 }
13760 else if (IsA(node, CoerceToDomain))
13761 {
13762 cdomain = (CoerceToDomain *) node;
13763 /* If it's an explicit domain coercion, we're done */
13764 if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
13765 break;
13766 /* Tentatively descend past the CoerceToDomain */
13767 node = (Node *) cdomain->arg;
13768 }
13769 else
13770 break;
13771 }
13772
13773 /*
13774 * If we descended past a CoerceToDomain whose argument turned out not to
13775 * be a FieldStore or array assignment, back up to the CoerceToDomain.
13776 * (This is not enough to be fully correct if there are nested implicit
13777 * CoerceToDomains, but such cases shouldn't ever occur.)
13778 */
13779 if (cdomain && node == (Node *) cdomain->arg)
13780 node = (Node *) cdomain;
13781
13782 return node;
13783}
13784
13785static void
13787{
13788 StringInfo buf = context->buf;
13791
13792 lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
13793 foreach(uplist_item, sbsref->refupperindexpr)
13794 {
13796 if (lowlist_item)
13797 {
13798 /* If subexpression is NULL, get_rule_expr prints nothing */
13799 get_rule_expr((Node *) lfirst(lowlist_item), context, false);
13802 }
13803 /* If subexpression is NULL, get_rule_expr prints nothing */
13804 get_rule_expr((Node *) lfirst(uplist_item), context, false);
13806 }
13807}
13808
13809/*
13810 * quote_identifier - Quote an identifier only if needed
13811 *
13812 * When quotes are needed, we palloc the required space; slightly
13813 * space-wasteful but well worth it for notational simplicity.
13814 */
13815const char *
13816quote_identifier(const char *ident)
13817{
13818 /*
13819 * Can avoid quoting if ident starts with a lowercase letter or underscore
13820 * and contains only lowercase letters, digits, and underscores, *and* is
13821 * not any SQL keyword. Otherwise, supply quotes.
13822 */
13823 int nquotes = 0;
13824 bool safe;
13825 const char *ptr;
13826 char *result;
13827 char *optr;
13828
13829 /*
13830 * would like to use <ctype.h> macros here, but they might yield unwanted
13831 * locale-specific results...
13832 */
13833 safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
13834
13835 for (ptr = ident; *ptr; ptr++)
13836 {
13837 char ch = *ptr;
13838
13839 if ((ch >= 'a' && ch <= 'z') ||
13840 (ch >= '0' && ch <= '9') ||
13841 (ch == '_'))
13842 {
13843 /* okay */
13844 }
13845 else
13846 {
13847 safe = false;
13848 if (ch == '"')
13849 nquotes++;
13850 }
13851 }
13852
13854 safe = false;
13855
13856 if (safe)
13857 {
13858 /*
13859 * Check for keyword. We quote keywords except for unreserved ones.
13860 * (In some cases we could avoid quoting a col_name or type_func_name
13861 * keyword, but it seems much harder than it's worth to tell that.)
13862 *
13863 * Note: ScanKeywordLookup() does case-insensitive comparison, but
13864 * that's fine, since we already know we have all-lower-case.
13865 */
13867
13869 safe = false;
13870 }
13871
13872 if (safe)
13873 return ident; /* no change needed */
13874
13875 result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13876
13877 optr = result;
13878 *optr++ = '"';
13879 for (ptr = ident; *ptr; ptr++)
13880 {
13881 char ch = *ptr;
13882
13883 if (ch == '"')
13884 *optr++ = '"';
13885 *optr++ = ch;
13886 }
13887 *optr++ = '"';
13888 *optr = '\0';
13889
13890 return result;
13891}
13892
13893/*
13894 * quote_qualified_identifier - Quote a possibly-qualified identifier
13895 *
13896 * Return a name of the form qualifier.ident, or just ident if qualifier
13897 * is NULL, quoting each component if necessary. The result is palloc'd.
13898 */
13899char *
13901 const char *ident)
13902{
13904
13906 if (qualifier)
13909 return buf.data;
13910}
13911
13912/*
13913 * get_relation_name
13914 * Get the unqualified name of a relation specified by OID
13915 *
13916 * This differs from the underlying get_rel_name() function in that it will
13917 * throw error instead of silently returning NULL if the OID is bad.
13918 */
13919static char *
13921{
13922 char *relname = get_rel_name(relid);
13923
13924 if (!relname)
13925 elog(ERROR, "cache lookup failed for relation %u", relid);
13926 return relname;
13927}
13928
13929/*
13930 * generate_relation_name
13931 * Compute the name to display for a relation specified by OID
13932 *
13933 * The result includes all necessary quoting and schema-prefixing.
13934 *
13935 * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
13936 * We will forcibly qualify the relation name if it equals any CTE name
13937 * visible in the namespace list.
13938 */
13939static char *
13940generate_relation_name(Oid relid, List *namespaces)
13941{
13942 HeapTuple tp;
13944 bool need_qual;
13946 char *relname;
13947 char *nspname;
13948 char *result;
13949
13951 if (!HeapTupleIsValid(tp))
13952 elog(ERROR, "cache lookup failed for relation %u", relid);
13954 relname = NameStr(reltup->relname);
13955
13956 /* Check for conflicting CTE name */
13957 need_qual = false;
13958 foreach(nslist, namespaces)
13959 {
13962
13963 foreach(ctlist, dpns->ctes)
13964 {
13966
13967 if (strcmp(cte->ctename, relname) == 0)
13968 {
13969 need_qual = true;
13970 break;
13971 }
13972 }
13973 if (need_qual)
13974 break;
13975 }
13976
13977 /* Otherwise, qualify the name if not visible in search path */
13978 if (!need_qual)
13979 need_qual = !RelationIsVisible(relid);
13980
13981 if (need_qual)
13982 nspname = get_namespace_name_or_temp(reltup->relnamespace);
13983 else
13984 nspname = NULL;
13985
13987
13988 ReleaseSysCache(tp);
13989
13990 return result;
13991}
13992
13993/*
13994 * generate_qualified_relation_name
13995 * Compute the name to display for a relation specified by OID
13996 *
13997 * As above, but unconditionally schema-qualify the name.
13998 */
13999static char *
14001{
14002 HeapTuple tp;
14004 char *result;
14005
14007 if (!HeapTupleIsValid(tp))
14008 elog(ERROR, "cache lookup failed for relation %u", relid);
14010
14011 result = get_qualified_objname(reltup->relnamespace,
14012 NameStr(reltup->relname));
14013 ReleaseSysCache(tp);
14014
14015 return result;
14016}
14017
14018/*
14019 * generate_function_name
14020 * Compute the name to display for a function specified by OID,
14021 * given that it is being called with the specified actual arg names and
14022 * types. (Those matter because of ambiguous-function resolution rules.)
14023 *
14024 * If we're dealing with a potentially variadic function (in practice, this
14025 * means a FuncExpr or Aggref, not some other way of calling a function), then
14026 * has_variadic must specify whether variadic arguments have been merged,
14027 * and *use_variadic_p will be set to indicate whether to print VARIADIC in
14028 * the output. For non-FuncExpr cases, has_variadic should be false and
14029 * use_variadic_p can be NULL.
14030 *
14031 * inGroupBy must be true if we're deparsing a GROUP BY clause.
14032 *
14033 * The result includes all necessary quoting and schema-prefixing.
14034 */
14035static char *
14036generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
14037 bool has_variadic, bool *use_variadic_p,
14038 bool inGroupBy)
14039{
14040 char *result;
14043 char *proname;
14044 bool use_variadic;
14045 char *nspname;
14047 int fgc_flags;
14048 Oid p_funcid;
14049 Oid p_rettype;
14050 bool p_retset;
14051 int p_nvargs;
14052 Oid p_vatype;
14054 bool force_qualify = false;
14055
14058 elog(ERROR, "cache lookup failed for function %u", funcid);
14060 proname = NameStr(procform->proname);
14061
14062 /*
14063 * Due to parser hacks to avoid needing to reserve CUBE, we need to force
14064 * qualification of some function names within GROUP BY.
14065 */
14066 if (inGroupBy)
14067 {
14068 if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
14069 force_qualify = true;
14070 }
14071
14072 /*
14073 * Determine whether VARIADIC should be printed. We must do this first
14074 * since it affects the lookup rules in func_get_detail().
14075 *
14076 * We always print VARIADIC if the function has a merged variadic-array
14077 * argument. Note that this is always the case for functions taking a
14078 * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
14079 * and printed the array elements as separate arguments, the call could
14080 * match a newer non-VARIADIC function.
14081 */
14082 if (use_variadic_p)
14083 {
14084 /* Parser should not have set funcvariadic unless fn is variadic */
14085 Assert(!has_variadic || OidIsValid(procform->provariadic));
14088 }
14089 else
14090 {
14092 use_variadic = false;
14093 }
14094
14095 /*
14096 * The idea here is to schema-qualify only if the parser would fail to
14097 * resolve the correct function given the unqualified func name with the
14098 * specified argtypes and VARIADIC flag. But if we already decided to
14099 * force qualification, then we can skip the lookup and pretend we didn't
14100 * find it.
14101 */
14102 if (!force_qualify)
14104 NIL, argnames, nargs, argtypes,
14105 !use_variadic, true, false,
14106 &fgc_flags,
14110 else
14111 {
14114 }
14115
14116 if ((p_result == FUNCDETAIL_NORMAL ||
14119 p_funcid == funcid)
14120 nspname = NULL;
14121 else
14122 nspname = get_namespace_name_or_temp(procform->pronamespace);
14123
14125
14127
14128 return result;
14129}
14130
14131/*
14132 * generate_operator_name
14133 * Compute the name to display for an operator specified by OID,
14134 * given that it is being called with the specified actual arg types.
14135 * (Arg types matter because of ambiguous-operator resolution rules.
14136 * Pass InvalidOid for unused arg of a unary operator.)
14137 *
14138 * The result includes all necessary quoting and schema-prefixing,
14139 * plus the OPERATOR() decoration needed to use a qualified operator name
14140 * in an expression.
14141 */
14142static char *
14144{
14148 char *oprname;
14149 char *nspname;
14151
14153
14156 elog(ERROR, "cache lookup failed for operator %u", operid);
14158 oprname = NameStr(operform->oprname);
14159
14160 /*
14161 * The idea here is to schema-qualify only if the parser would fail to
14162 * resolve the correct operator given the unqualified op name with the
14163 * specified argtypes.
14164 */
14165 switch (operform->oprkind)
14166 {
14167 case 'b':
14169 true, -1);
14170 break;
14171 case 'l':
14173 true, -1);
14174 break;
14175 default:
14176 elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
14177 p_result = NULL; /* keep compiler quiet */
14178 break;
14179 }
14180
14181 if (p_result != NULL && oprid(p_result) == operid)
14182 nspname = NULL;
14183 else
14184 {
14185 nspname = get_namespace_name_or_temp(operform->oprnamespace);
14186 appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
14187 }
14188
14189 appendStringInfoString(&buf, oprname);
14190
14191 if (nspname)
14193
14194 if (p_result != NULL)
14196
14198
14199 return buf.data;
14200}
14201
14202/*
14203 * generate_operator_clause --- generate a binary-operator WHERE clause
14204 *
14205 * This is used for internally-generated-and-executed SQL queries, where
14206 * precision is essential and readability is secondary. The basic
14207 * requirement is to append "leftop op rightop" to buf, where leftop and
14208 * rightop are given as strings and are assumed to yield types leftoptype
14209 * and rightoptype; the operator is identified by OID. The complexity
14210 * comes from needing to be sure that the parser will select the desired
14211 * operator when the query is parsed. We always name the operator using
14212 * OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
14213 * We have to emit casts too, if either input isn't already the input type
14214 * of the operator; else we are at the mercy of the parser's heuristics for
14215 * ambiguous-operator resolution. The caller must ensure that leftop and
14216 * rightop are suitable arguments for a cast operation; it's best to insert
14217 * parentheses if they aren't just variables or parameters.
14218 */
14219void
14221 const char *leftop, Oid leftoptype,
14222 Oid opoid,
14223 const char *rightop, Oid rightoptype)
14224{
14227 char *oprname;
14228 char *nspname;
14229
14232 elog(ERROR, "cache lookup failed for operator %u", opoid);
14234 Assert(operform->oprkind == 'b');
14235 oprname = NameStr(operform->oprname);
14236
14237 nspname = get_namespace_name(operform->oprnamespace);
14238
14240 if (leftoptype != operform->oprleft)
14241 add_cast_to(buf, operform->oprleft);
14242 appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
14243 appendStringInfoString(buf, oprname);
14244 appendStringInfo(buf, ") %s", rightop);
14245 if (rightoptype != operform->oprright)
14246 add_cast_to(buf, operform->oprright);
14247
14249}
14250
14251/*
14252 * Add a cast specification to buf. We spell out the type name the hard way,
14253 * intentionally not using format_type_be(). This is to avoid corner cases
14254 * for CHARACTER, BIT, and perhaps other types, where specifying the type
14255 * using SQL-standard syntax results in undesirable data truncation. By
14256 * doing it this way we can be certain that the cast will have default (-1)
14257 * target typmod.
14258 */
14259static void
14261{
14264 char *typname;
14265 char *nspname;
14266
14269 elog(ERROR, "cache lookup failed for type %u", typid);
14271
14272 typname = NameStr(typform->typname);
14273 nspname = get_namespace_name_or_temp(typform->typnamespace);
14274
14275 appendStringInfo(buf, "::%s.%s",
14277
14279}
14280
14281/*
14282 * generate_qualified_type_name
14283 * Compute the name to display for a type specified by OID
14284 *
14285 * This is different from format_type_be() in that we unconditionally
14286 * schema-qualify the name. That also means no special syntax for
14287 * SQL-standard type names ... although in current usage, this should
14288 * only get used for domains, so such cases wouldn't occur anyway.
14289 */
14290static char *
14292{
14293 HeapTuple tp;
14295 char *typname;
14296 char *nspname;
14297 char *result;
14298
14300 if (!HeapTupleIsValid(tp))
14301 elog(ERROR, "cache lookup failed for type %u", typid);
14303 typname = NameStr(typtup->typname);
14304
14305 nspname = get_namespace_name_or_temp(typtup->typnamespace);
14306 if (!nspname)
14307 elog(ERROR, "cache lookup failed for namespace %u",
14308 typtup->typnamespace);
14309
14311
14312 ReleaseSysCache(tp);
14313
14314 return result;
14315}
14316
14317/*
14318 * generate_collation_name
14319 * Compute the name to display for a collation specified by OID
14320 *
14321 * The result includes all necessary quoting and schema-prefixing.
14322 */
14323char *
14325{
14326 HeapTuple tp;
14328 char *collname;
14329 char *nspname;
14330 char *result;
14331
14333 if (!HeapTupleIsValid(tp))
14334 elog(ERROR, "cache lookup failed for collation %u", collid);
14336 collname = NameStr(colltup->collname);
14337
14339 nspname = get_namespace_name_or_temp(colltup->collnamespace);
14340 else
14341 nspname = NULL;
14342
14343 result = quote_qualified_identifier(nspname, collname);
14344
14345 ReleaseSysCache(tp);
14346
14347 return result;
14348}
14349
14350/*
14351 * Given a C string, produce a TEXT datum.
14352 *
14353 * We assume that the input was palloc'd and may be freed.
14354 */
14355static text *
14356string_to_text(char *str)
14357{
14358 text *result;
14359
14361 pfree(str);
14362 return result;
14363}
14364
14365/*
14366 * Generate a C string representing a relation options from text[] datum.
14367 */
14368void
14370{
14371 Datum *options;
14372 int noptions;
14373 int i;
14374
14376 &options, NULL, &noptions);
14377
14378 for (i = 0; i < noptions; i++)
14379 {
14381 char *name;
14382 char *separator;
14383 char *value;
14384
14385 /*
14386 * Each array element should have the form name=value. If the "=" is
14387 * missing for some reason, treat it like an empty value.
14388 */
14389 name = option;
14390 separator = strchr(option, '=');
14391 if (separator)
14392 {
14393 *separator = '\0';
14394 value = separator + 1;
14395 }
14396 else
14397 value = "";
14398
14399 if (i > 0)
14402
14403 /*
14404 * In general we need to quote the value; but to avoid unnecessary
14405 * clutter, do not quote if it is an identifier that would not need
14406 * quoting. (We could also allow numbers, but that is a bit trickier
14407 * than it looks --- for example, are leading zeroes significant? We
14408 * don't want to assume very much here about what custom reloptions
14409 * might mean.)
14410 */
14413 else
14415
14416 pfree(option);
14417 }
14418}
14419
14420/*
14421 * Generate a C string representing a relation's reloptions, or NULL if none.
14422 */
14423static char *
14425{
14426 char *result = NULL;
14427 HeapTuple tuple;
14428 Datum reloptions;
14429 bool isnull;
14430
14431 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14432 if (!HeapTupleIsValid(tuple))
14433 elog(ERROR, "cache lookup failed for relation %u", relid);
14434
14435 reloptions = SysCacheGetAttr(RELOID, tuple,
14436 Anum_pg_class_reloptions, &isnull);
14437 if (!isnull)
14438 {
14440
14442 get_reloptions(&buf, reloptions);
14443
14444 result = buf.data;
14445 }
14446
14447 ReleaseSysCache(tuple);
14448
14449 return result;
14450}
14451
14452/*
14453 * get_range_partbound_string
14454 * A C string representation of one range partition bound
14455 */
14456char *
14458{
14459 deparse_context context;
14461 ListCell *cell;
14462 char *sep;
14463
14465 memset(&context, 0, sizeof(deparse_context));
14466 context.buf = &buf;
14467
14469 sep = "";
14470 foreach(cell, bound_datums)
14471 {
14472 PartitionRangeDatum *datum =
14474
14477 appendStringInfoString(&buf, "MINVALUE");
14478 else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
14479 appendStringInfoString(&buf, "MAXVALUE");
14480 else
14481 {
14482 Const *val = castNode(Const, datum->value);
14483
14484 get_const_expr(val, &context, -1);
14485 }
14486 sep = ", ";
14487 }
14489
14490 return buf.data;
14491}
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:217
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:547
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:645
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:934
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:252
#define bms_is_empty(a)
Definition bitmapset.h:119
#define TextDatumGetCString(d)
Definition builtins.h:99
#define NameStr(name)
Definition c.h:894
NameData * Name
Definition c.h:892
#define Max(x, y)
Definition c.h:1125
#define Assert(condition)
Definition c.h:1002
int16_t int16
Definition c.h:678
#define SQL_STR_DOUBLE(ch, escape_backslash)
Definition c.h:1292
int32_t int32
Definition c.h:679
uint16_t uint16
Definition c.h:682
#define lengthof(array)
Definition c.h:932
unsigned int Index
Definition c.h:757
float float4
Definition c.h:772
#define pg_fallthrough
Definition c.h:220
#define OidIsValid(objectId)
Definition c.h:917
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:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
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:2381
long val
Definition informix.c:689
static struct @175 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:2242
char * get_propgraph_property_name(Oid propoid)
Definition lsyscache.c:4098
AttrNumber get_attnum(Oid relid, const char *attname)
Definition lsyscache.c:1084
Oid get_opclass_input_type(Oid opclass)
Definition lsyscache.c:1464
bool type_is_rowtype(Oid typid)
Definition lsyscache.c:2971
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition lsyscache.c:3223
Datum get_attoptions(Oid relid, int16 attnum)
Definition lsyscache.c:1196
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2317
Oid get_typcollation(Oid typid)
Definition lsyscache.c:3372
char * get_language_name(Oid langoid, bool missing_ok)
Definition lsyscache.c:1413
char * get_namespace_name_or_temp(Oid nspid)
Definition lsyscache.c:3706
char * get_propgraph_label_name(Oid labeloid)
Definition lsyscache.c:4080
char * get_constraint_name(Oid conoid)
Definition lsyscache.c:1307
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition lsyscache.c:1053
Oid get_rel_tablespace(Oid relid)
Definition lsyscache.c:2368
Oid get_typ_typrelid(Oid typid)
Definition lsyscache.c:3047
Oid get_base_element_type(Oid typid)
Definition lsyscache.c:3148
char * get_qualified_objname(Oid nspid, char *objname)
Definition lsyscache.c:3720
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3682
void get_type_category_preferred(Oid typid, char *typcategory, bool *typispreferred)
Definition lsyscache.c:3026
void get_atttypetypmodcoll(Oid relid, AttrNumber attnum, Oid *typid, int32 *typmod, Oid *collid)
Definition lsyscache.c:1169
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:394
#define IsA(nodeptr, _type_)
Definition nodes.h:162
#define nodeTag(nodeptr)
Definition nodes.h:137
#define DO_AGGSPLIT_COMBINE(as)
Definition nodes.h:393
@ ONCONFLICT_SELECT
Definition nodes.h:429
@ ONCONFLICT_UPDATE
Definition nodes.h:428
@ ONCONFLICT_NOTHING
Definition nodes.h:427
@ CMD_MERGE
Definition nodes.h:277
@ CMD_UTILITY
Definition nodes.h:278
@ CMD_INSERT
Definition nodes.h:275
@ CMD_DELETE
Definition nodes.h:276
@ CMD_UPDATE
Definition nodes.h:274
@ CMD_SELECT
Definition nodes.h:273
@ CMD_NOTHING
Definition nodes.h:280
@ LIMIT_OPTION_WITH_TIES
Definition nodes.h:441
#define makeNode(_type_)
Definition nodes.h:159
#define castNode(_type_, nodeptr)
Definition nodes.h:180
@ JOIN_FULL
Definition nodes.h:303
@ JOIN_INNER
Definition nodes.h:301
@ JOIN_RIGHT
Definition nodes.h:304
@ JOIN_LEFT
Definition nodes.h:302
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:623
#define FRAMEOPTION_END_OFFSET
Definition parsenodes.h:634
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition parsenodes.h:628
@ 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:625
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition parsenodes.h:618
#define GetCTETargetList(cte)
#define FKCONSTR_ACTION_SETDEFAULT
@ PARTITION_STRATEGY_HASH
Definition parsenodes.h:921
@ PARTITION_STRATEGY_LIST
Definition parsenodes.h:919
@ PARTITION_STRATEGY_RANGE
Definition parsenodes.h:920
#define FRAMEOPTION_START_CURRENT_ROW
Definition parsenodes.h:622
#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:632
@ PARTITION_RANGE_DATUM_MAXVALUE
Definition parsenodes.h:973
@ PARTITION_RANGE_DATUM_MINVALUE
Definition parsenodes.h:971
@ 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:627
#define FRAMEOPTION_EXCLUDE_TIES
Definition parsenodes.h:630
#define FRAMEOPTION_RANGE
Definition parsenodes.h:614
#define FRAMEOPTION_EXCLUDE_GROUP
Definition parsenodes.h:629
#define FKCONSTR_ACTION_CASCADE
#define FRAMEOPTION_GROUPS
Definition parsenodes.h:616
#define FRAMEOPTION_BETWEEN
Definition parsenodes.h:617
#define FKCONSTR_ACTION_SETNULL
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition parsenodes.h:621
#define FRAMEOPTION_START_OFFSET_PRECEDING
Definition parsenodes.h:624
#define FRAMEOPTION_START_OFFSET_FOLLOWING
Definition parsenodes.h:626
#define FRAMEOPTION_NONDEFAULT
Definition parsenodes.h:613
#define FKCONSTR_MATCH_FULL
#define FKCONSTR_ACTION_NOACTION
#define FRAMEOPTION_ROWS
Definition parsenodes.h:615
@ 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:1999
@ IS_NOT_FALSE
Definition primnodes.h:1999
@ IS_NOT_UNKNOWN
Definition primnodes.h:1999
@ IS_TRUE
Definition primnodes.h:1999
@ IS_UNKNOWN
Definition primnodes.h:1999
@ IS_FALSE
Definition primnodes.h:1999
@ ARRAY_SUBLINK
Definition primnodes.h:1017
@ ANY_SUBLINK
Definition primnodes.h:1013
@ MULTIEXPR_SUBLINK
Definition primnodes.h:1016
@ CTE_SUBLINK
Definition primnodes.h:1018
@ EXPR_SUBLINK
Definition primnodes.h:1015
@ ROWCOMPARE_SUBLINK
Definition primnodes.h:1014
@ ALL_SUBLINK
Definition primnodes.h:1012
@ EXISTS_SUBLINK
Definition primnodes.h:1011
@ JS_FORMAT_JSONB
Definition primnodes.h:1648
@ JS_FORMAT_DEFAULT
Definition primnodes.h:1646
@ JS_FORMAT_JSON
Definition primnodes.h:1647
@ IS_LEAST
Definition primnodes.h:1511
@ IS_GREATEST
Definition primnodes.h:1510
@ TFT_XMLTABLE
Definition primnodes.h:101
@ TFT_JSON_TABLE
Definition primnodes.h:102
BoolExprType
Definition primnodes.h:944
@ AND_EXPR
Definition primnodes.h:945
@ OR_EXPR
Definition primnodes.h:945
@ NOT_EXPR
Definition primnodes.h:945
@ JS_ENC_DEFAULT
Definition primnodes.h:1634
@ JS_ENC_UTF32
Definition primnodes.h:1637
@ JS_ENC_UTF16
Definition primnodes.h:1636
@ XMLOPTION_DOCUMENT
Definition primnodes.h:1600
@ SVFOP_CURRENT_CATALOG
Definition primnodes.h:1557
@ SVFOP_LOCALTIME_N
Definition primnodes.h:1550
@ SVFOP_CURRENT_TIMESTAMP
Definition primnodes.h:1547
@ SVFOP_LOCALTIME
Definition primnodes.h:1549
@ SVFOP_CURRENT_TIMESTAMP_N
Definition primnodes.h:1548
@ SVFOP_CURRENT_ROLE
Definition primnodes.h:1553
@ SVFOP_USER
Definition primnodes.h:1555
@ SVFOP_CURRENT_SCHEMA
Definition primnodes.h:1558
@ SVFOP_LOCALTIMESTAMP_N
Definition primnodes.h:1552
@ SVFOP_CURRENT_DATE
Definition primnodes.h:1544
@ SVFOP_CURRENT_TIME_N
Definition primnodes.h:1546
@ SVFOP_CURRENT_TIME
Definition primnodes.h:1545
@ SVFOP_LOCALTIMESTAMP
Definition primnodes.h:1551
@ SVFOP_CURRENT_USER
Definition primnodes.h:1554
@ SVFOP_SESSION_USER
Definition primnodes.h:1556
@ PARAM_MULTIEXPR
Definition primnodes.h:388
@ PARAM_EXTERN
Definition primnodes.h:385
@ PARAM_EXEC
Definition primnodes.h:386
@ JSW_UNCONDITIONAL
Definition primnodes.h:1774
@ JSW_CONDITIONAL
Definition primnodes.h:1773
@ JSW_UNSPEC
Definition primnodes.h:1771
@ JSW_NONE
Definition primnodes.h:1772
#define PARSER_IGNORE_NULLS
Definition primnodes.h:577
@ IS_DOCUMENT
Definition primnodes.h:1595
@ IS_XMLFOREST
Definition primnodes.h:1590
@ IS_XMLCONCAT
Definition primnodes.h:1588
@ IS_XMLPI
Definition primnodes.h:1592
@ IS_XMLPARSE
Definition primnodes.h:1591
@ IS_XMLSERIALIZE
Definition primnodes.h:1594
@ IS_XMLROOT
Definition primnodes.h:1593
@ IS_XMLELEMENT
Definition primnodes.h:1589
@ 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:1785
@ JSON_BEHAVIOR_DEFAULT
Definition primnodes.h:1794
@ JSON_BEHAVIOR_FALSE
Definition primnodes.h:1790
@ JSON_BEHAVIOR_NULL
Definition primnodes.h:1786
@ JSON_BEHAVIOR_EMPTY_ARRAY
Definition primnodes.h:1792
@ JSON_QUERY_OP
Definition primnodes.h:1824
@ JSON_EXISTS_OP
Definition primnodes.h:1823
@ JSON_VALUE_OP
Definition primnodes.h:1825
CoercionForm
Definition primnodes.h:756
@ COERCE_SQL_SYNTAX
Definition primnodes.h:760
@ COERCE_IMPLICIT_CAST
Definition primnodes.h:759
@ COERCE_EXPLICIT_CAST
Definition primnodes.h:758
@ COERCE_EXPLICIT_CALL
Definition primnodes.h:757
@ OVERRIDING_SYSTEM_VALUE
Definition primnodes.h:31
@ OVERRIDING_USER_VALUE
Definition primnodes.h:30
@ IS_NULL
Definition primnodes.h:1975
@ IS_NOT_NULL
Definition primnodes.h:1975
@ JS_TYPE_ARRAY
Definition primnodes.h:1744
@ JS_TYPE_OBJECT
Definition primnodes.h:1743
@ JS_TYPE_SCALAR
Definition primnodes.h:1745
@ MERGE_WHEN_NOT_MATCHED_BY_TARGET
Definition primnodes.h:2021
@ MERGE_WHEN_NOT_MATCHED_BY_SOURCE
Definition primnodes.h:2020
@ MERGE_WHEN_MATCHED
Definition primnodes.h:2019
#define OUTER_VAR
Definition primnodes.h:244
@ JSCTOR_JSON_SERIALIZE
Definition primnodes.h:1705
@ JSCTOR_JSON_ARRAYAGG
Definition primnodes.h:1702
@ JSCTOR_JSON_PARSE
Definition primnodes.h:1703
@ JSCTOR_JSON_OBJECT
Definition primnodes.h:1698
@ JSCTOR_JSON_SCALAR
Definition primnodes.h:1704
@ JSCTOR_JSON_ARRAY_QUERY
Definition primnodes.h:1700
@ JSCTOR_JSON_ARRAY
Definition primnodes.h:1699
@ JSCTOR_JSON_OBJECTAGG
Definition primnodes.h:1701
#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:898
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags, int wrapColumn)
Definition ruleutils.c:5903
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:2456
static char * get_rtable_name(int rtindex, deparse_context *context)
Definition ruleutils.c:5497
Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
Definition ruleutils.c:729
static int decompile_column_index_array(Datum column_index_array, Oid relId, bool withPeriod, StringInfo buf)
Definition ruleutils.c:2981
static void make_propgraphdef_labels(StringInfo buf, Oid elid, const char *elalias, Oid elrelid)
Definition ruleutils.c:1799
static void set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte, deparse_columns *colinfo)
Definition ruleutils.c:4739
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:4117
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition ruleutils.c:1987
Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS)
Definition ruleutils.c:1211
static void set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
Definition ruleutils.c:4574
static Plan * find_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)
Definition ruleutils.c:5597
static text * string_to_text(char *str)
static void get_values_def(List *values_lists, deparse_context *context)
Definition ruleutils.c:6091
#define PRETTYINDENT_LIMIT
Definition ruleutils.c:90
Datum pg_get_viewdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:691
static char * make_colname_unique(char *colname, deparse_namespace *dpns, deparse_columns *colinfo)
Definition ruleutils.c:5287
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:2506
static void set_deparse_for_query(deparse_namespace *dpns, Query *query, List *parent_namespaces)
Definition ruleutils.c:4393
static void print_function_trftypes(StringInfo buf, HeapTuple proctup)
Definition ruleutils.c:3822
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:3810
static void get_query_def(Query *query, StringInfo buf, List *parentnamespace, TupleDesc resultDesc, bool colNamesVisible, int prettyFlags, int wrapColumn, int startIndent)
Definition ruleutils.c:5988
static void get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
Datum pg_get_functiondef(PG_FUNCTION_ARGS)
Definition ruleutils.c:3287
Datum pg_get_function_result(PG_FUNCTION_ARGS)
Definition ruleutils.c:3594
Datum pg_get_indexdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:1191
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:6380
static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as, deparse_context *context)
#define only_marker(rte)
Definition ruleutils.c:563
Datum pg_get_function_arg_default(PG_FUNCTION_ARGS)
Definition ruleutils.c:3850
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:5342
Datum pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
Definition ruleutils.c:2198
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:573
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:1997
Datum pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:1969
static void add_to_names_hash(deparse_columns *colinfo, const char *name)
Definition ruleutils.c:5400
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:5212
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:1262
char * pg_get_partkeydef_columns(Oid relid, bool pretty)
Definition ruleutils.c:2284
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:4072
static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
Datum pg_get_partkeydef(PG_FUNCTION_ARGS)
Definition ruleutils.c:2269
static char * generate_qualified_relation_name(Oid relid)
static void set_simple_column_names(deparse_namespace *dpns)
Definition ruleutils.c:4462
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:1248
#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:1867
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:1283
static char * pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok)
Definition ruleutils.c:2553
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:5326
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:4189
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:2014
#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:5695
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:4036
Datum pg_get_ruledef_ext(PG_FUNCTION_ARGS)
Definition ruleutils.c:591
char * pg_get_indexdef_string(Oid indexrelid)
Definition ruleutils.c:1238
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:1601
static void print_function_sqlbody(StringInfo buf, HeapTuple proctup)
Definition ruleutils.c:3920
static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
Definition ruleutils.c:4504
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:802
static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell, deparse_namespace *save_dpns)
Definition ruleutils.c:5674
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:3070
static Node * processIndirection(Node *node, deparse_context *context)
static void get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
static bool json_table_plan_is_default(JsonTablePlan *plan)
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:884
List * select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
Definition ruleutils.c:4219
Datum pg_get_function_sqlbody(PG_FUNCTION_ARGS)
Definition ruleutils.c:3974
static void get_path_pattern_expr_def(List *path_pattern_expr, deparse_context *context)
Definition ruleutils.c:8031
static void get_json_agg_constructor_expr(Node *node, deparse_context *context, void *callback_arg)
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition ruleutils.c:3035
static char * generate_qualified_type_name(Oid typid)
static void get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
static void get_json_table_plan(TableFunc *tf, JsonTablePlan *plan, deparse_context *context, bool parenthesize)
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:3052
static int list_oid_str_pair_cmp_by_str(const ListCell *p1, const ListCell *p2)
Definition ruleutils.c:1786
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:774
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:5644
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:2544
char * pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
Definition ruleutils.c:2488
static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte, deparse_columns *colinfo)
Definition ruleutils.c:4942
Datum pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
Definition ruleutils.c:2523
static void make_propgraphdef_properties(StringInfo buf, Oid ellabelid, Oid elrelid)
Definition ruleutils.c:1883
static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces, Bitmapset *rels_used)
Definition ruleutils.c:4248
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:710
static char * pg_get_partkeydef_worker(Oid relid, int prettyFlags, bool attrsOnly, bool missing_ok)
Definition ruleutils.c:2297
static void get_oper_expr(OpExpr *expr, deparse_context *context)
Datum pg_get_propgraphdef(PG_FUNCTION_ARGS)
Definition ruleutils.c:1620
Datum pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
Definition ruleutils.c:3569
static char * pg_get_triggerdef_worker(Oid trigid, bool pretty)
Definition ruleutils.c:913
#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:4009
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:5711
static const char *const query_getviewrule
Definition ruleutils.c:341
static char * pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
Definition ruleutils.c:610
static int print_function_arguments(StringInfo buf, HeapTuple proctup, bool print_table_args, bool print_defaults)
Definition ruleutils.c:3662
static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte, deparse_columns *colinfo)
Definition ruleutils.c:5429
static void print_function_rettype(StringInfo buf, HeapTuple proctup)
Definition ruleutils.c:3624
char * generate_opclass_name(Oid opclass)
static void set_deparse_plan(deparse_namespace *dpns, Plan *plan)
Definition ruleutils.c:5516
static void make_propgraphdef_elements(StringInfo buf, Oid pgrelid, char pgekind)
Definition ruleutils.c:1662
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:6407
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:5627
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:6476
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:6134
#define PRETTYINDENT_VAR
Definition ruleutils.c:88
static void destroy_colinfo_names_hash(deparse_columns *colinfo)
Definition ruleutils.c:5413
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:3193
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:3543
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:6273
static void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf)
Datum pg_get_viewdef_name(PG_FUNCTION_ARGS)
Definition ruleutils.c:749
Datum pg_get_userbyid(PG_FUNCTION_ARGS)
Definition ruleutils.c:3155
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
int SPI_keepplan(SPIPlanPtr plan)
Definition spi.c:977
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
Definition spi.c:1221
SPIPlanPtr SPI_prepare(const char *src, int nargs, const Oid *argtypes)
Definition spi.c:861
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:96
#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:461
List * aggdistinct
Definition primnodes.h:491
List * aggdirectargs
Definition primnodes.h:482
List * args
Definition primnodes.h:485
Expr * aggfilter
Definition primnodes.h:494
List * aggorder
Definition primnodes.h:488
Index parent_relid
Definition pathnodes.h:3301
BoolExprType boolop
Definition primnodes.h:953
List * args
Definition primnodes.h:954
CTEMaterialize ctematerialized
bool attisdropped
Definition tupdesc.h:78
Oid consttype
Definition primnodes.h:333
char * cursor_name
Definition primnodes.h:2121
List * newvals
Definition primnodes.h:1176
Node * quals
Definition primnodes.h:2380
List * fromlist
Definition primnodes.h:2379
Oid funcid
Definition primnodes.h:770
List * args
Definition primnodes.h:788
Node * whereClause
List * path_pattern_list
JsonBehaviorType btype
Definition primnodes.h:1811
JsonBehavior * on_empty
Definition primnodes.h:1860
JsonFormat * format
Definition primnodes.h:1847
Node * path_spec
Definition primnodes.h:1850
JsonWrapper wrapper
Definition primnodes.h:1871
JsonExprOp op
Definition primnodes.h:1838
JsonBehavior * on_error
Definition primnodes.h:1861
bool omit_quotes
Definition primnodes.h:1874
JsonFormatType format_type
Definition primnodes.h:1659
JsonValueType item_type
Definition primnodes.h:1757
JsonFormat * format
Definition primnodes.h:1671
JsonTablePath * path
Definition primnodes.h:1919
JsonTablePlan * child
Definition primnodes.h:1928
Const * value
Definition primnodes.h:1892
JsonTablePlan * rplan
Definition primnodes.h:1950
JsonTablePlan * lplan
Definition primnodes.h:1949
Definition pg_list.h:54
Definition nodes.h:133
Oid opno
Definition primnodes.h:835
List * args
Definition primnodes.h:853
int paramid
Definition primnodes.h:397
ParamKind paramkind
Definition primnodes.h:396
PartitionRangeDatumKind kind
Definition parsenodes.h:980
List * appendRelations
Definition plannodes.h:124
List * subplans
Definition plannodes.h:129
List * rtable
Definition plannodes.h:107
List * rowMarks
Definition parsenodes.h:238
bool groupDistinct
Definition parsenodes.h:222
Node * mergeJoinCondition
Definition parsenodes.h:201
Node * limitCount
Definition parsenodes.h:235
FromExpr * jointree
Definition parsenodes.h:187
List * returningList
Definition parsenodes.h:219
Node * setOperations
Definition parsenodes.h:240
List * cteList
Definition parsenodes.h:178
OnConflictExpr * onConflict
Definition parsenodes.h:208
ForPortionOfExpr * forPortionOf
Definition parsenodes.h:153
List * groupClause
Definition parsenodes.h:221
Node * havingQual
Definition parsenodes.h:226
List * rtable
Definition parsenodes.h:180
Node * limitOffset
Definition parsenodes.h:234
CmdType commandType
Definition parsenodes.h:124
LimitOption limitOption
Definition parsenodes.h:236
Node * utilityStmt
Definition parsenodes.h:144
List * mergeActionList
Definition parsenodes.h:190
List * windowClause
Definition parsenodes.h:228
List * targetList
Definition parsenodes.h:203
List * groupingSets
Definition parsenodes.h:224
List * distinctClause
Definition parsenodes.h:230
List * sortClause
Definition parsenodes.h:232
List * args
Definition primnodes.h:1431
LockClauseStrength strength
LockWaitPolicy waitPolicy
TupleDesc tupdesc
Definition spi.h:25
HeapTuple * vals
Definition spi.h:26
SQLValueFunctionOp op
Definition primnodes.h:1564
SetOperation op
Definition value.h:64
char * plan_name
Definition primnodes.h:1086
List * args
Definition primnodes.h:1106
List * paramIds
Definition primnodes.h:1082
bool isInitPlan
Definition primnodes.h:1093
bool useHashTable
Definition primnodes.h:1094
Node * testexpr
Definition primnodes.h:1081
List * parParam
Definition primnodes.h:1105
List * setParam
Definition primnodes.h:1103
SubLinkType subLinkType
Definition primnodes.h:1079
Expr * refassgnexpr
Definition primnodes.h:726
List * refupperindexpr
Definition primnodes.h:716
List * reflowerindexpr
Definition primnodes.h:722
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:598
Index winref
Definition primnodes.h:604
Expr * aggfilter
Definition primnodes.h:600
int ignore_nulls
Definition primnodes.h:610
List * args
Definition primnodes.h:1616
bool indent
Definition primnodes.h:1620
List * named_args
Definition primnodes.h:1612
XmlExprOp op
Definition primnodes.h:1608
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:889
Definition c.h:874
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition c.h:881
Definition c.h:835
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:1949
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(uint_fast32_t val, char *buf)
Definition zic.c:2854

◆ GET_PRETTY_FLAGS

#define GET_PRETTY_FLAGS (   pretty)

◆ only_marker

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

Definition at line 563 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 14261 of file ruleutils.c.

14262{
14265 char *typname;
14266 char *nspname;
14267
14270 elog(ERROR, "cache lookup failed for type %u", typid);
14272
14273 typname = NameStr(typform->typname);
14274 nspname = get_namespace_name_or_temp(typform->typnamespace);
14275
14276 appendStringInfo(buf, "::%s.%s",
14278
14280}

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 5400 of file ruleutils.c.

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

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 5342 of file ruleutils.c.

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

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 5212 of file ruleutils.c.

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

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 2981 of file ruleutils.c.

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

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 4072 of file ruleutils.c.

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

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 4117 of file ruleutils.c.

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

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 5413 of file ruleutils.c.

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

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 5326 of file ruleutils.c.

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

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 5597 of file ruleutils.c.

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

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

Referenced by set_deparse_plan().

◆ flatten_reloptions()

static char * flatten_reloptions ( Oid  relid)
static

Definition at line 14425 of file ruleutils.c.

14426{
14427 char *result = NULL;
14428 HeapTuple tuple;
14429 Datum reloptions;
14430 bool isnull;
14431
14432 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14433 if (!HeapTupleIsValid(tuple))
14434 elog(ERROR, "cache lookup failed for relation %u", relid);
14435
14436 reloptions = SysCacheGetAttr(RELOID, tuple,
14437 Anum_pg_class_reloptions, &isnull);
14438 if (!isnull)
14439 {
14441
14443 get_reloptions(&buf, reloptions);
14444
14445 result = buf.data;
14446 }
14447
14448 ReleaseSysCache(tuple);
14449
14450 return result;
14451}

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 14325 of file ruleutils.c.

14326{
14327 HeapTuple tp;
14329 char *collname;
14330 char *nspname;
14331 char *result;
14332
14334 if (!HeapTupleIsValid(tp))
14335 elog(ERROR, "cache lookup failed for collation %u", collid);
14337 collname = NameStr(colltup->collname);
14338
14340 nspname = get_namespace_name_or_temp(colltup->collnamespace);
14341 else
14342 nspname = NULL;
14343
14344 result = quote_qualified_identifier(nspname, collname);
14345
14346 ReleaseSysCache(tp);
14347
14348 return result;
14349}

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 14037 of file ruleutils.c.

14040{
14041 char *result;
14044 char *proname;
14045 bool use_variadic;
14046 char *nspname;
14048 int fgc_flags;
14049 Oid p_funcid;
14050 Oid p_rettype;
14051 bool p_retset;
14052 int p_nvargs;
14053 Oid p_vatype;
14055 bool force_qualify = false;
14056
14059 elog(ERROR, "cache lookup failed for function %u", funcid);
14061 proname = NameStr(procform->proname);
14062
14063 /*
14064 * Due to parser hacks to avoid needing to reserve CUBE, we need to force
14065 * qualification of some function names within GROUP BY.
14066 */
14067 if (inGroupBy)
14068 {
14069 if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
14070 force_qualify = true;
14071 }
14072
14073 /*
14074 * Determine whether VARIADIC should be printed. We must do this first
14075 * since it affects the lookup rules in func_get_detail().
14076 *
14077 * We always print VARIADIC if the function has a merged variadic-array
14078 * argument. Note that this is always the case for functions taking a
14079 * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
14080 * and printed the array elements as separate arguments, the call could
14081 * match a newer non-VARIADIC function.
14082 */
14083 if (use_variadic_p)
14084 {
14085 /* Parser should not have set funcvariadic unless fn is variadic */
14086 Assert(!has_variadic || OidIsValid(procform->provariadic));
14089 }
14090 else
14091 {
14093 use_variadic = false;
14094 }
14095
14096 /*
14097 * The idea here is to schema-qualify only if the parser would fail to
14098 * resolve the correct function given the unqualified func name with the
14099 * specified argtypes and VARIADIC flag. But if we already decided to
14100 * force qualification, then we can skip the lookup and pretend we didn't
14101 * find it.
14102 */
14103 if (!force_qualify)
14105 NIL, argnames, nargs, argtypes,
14106 !use_variadic, true, false,
14107 &fgc_flags,
14111 else
14112 {
14115 }
14116
14117 if ((p_result == FUNCDETAIL_NORMAL ||
14120 p_funcid == funcid)
14121 nspname = NULL;
14122 else
14123 nspname = get_namespace_name_or_temp(procform->pronamespace);
14124
14126
14128
14129 return result;
14130}

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 13687 of file ruleutils.c.

13688{
13690
13692 get_opclass_name(opclass, InvalidOid, &buf);
13693
13694 return &buf.data[1]; /* get_opclass_name() prepends space */
13695}

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 14221 of file ruleutils.c.

14225{
14228 char *oprname;
14229 char *nspname;
14230
14233 elog(ERROR, "cache lookup failed for operator %u", opoid);
14235 Assert(operform->oprkind == 'b');
14236 oprname = NameStr(operform->oprname);
14237
14238 nspname = get_namespace_name(operform->oprnamespace);
14239
14241 if (leftoptype != operform->oprleft)
14242 add_cast_to(buf, operform->oprleft);
14243 appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
14244 appendStringInfoString(buf, oprname);
14245 appendStringInfo(buf, ") %s", rightop);
14246 if (rightoptype != operform->oprright)
14247 add_cast_to(buf, operform->oprright);
14248
14250}

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 14144 of file ruleutils.c.

14145{
14149 char *oprname;
14150 char *nspname;
14152
14154
14157 elog(ERROR, "cache lookup failed for operator %u", operid);
14159 oprname = NameStr(operform->oprname);
14160
14161 /*
14162 * The idea here is to schema-qualify only if the parser would fail to
14163 * resolve the correct operator given the unqualified op name with the
14164 * specified argtypes.
14165 */
14166 switch (operform->oprkind)
14167 {
14168 case 'b':
14170 true, -1);
14171 break;
14172 case 'l':
14174 true, -1);
14175 break;
14176 default:
14177 elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
14178 p_result = NULL; /* keep compiler quiet */
14179 break;
14180 }
14181
14182 if (p_result != NULL && oprid(p_result) == operid)
14183 nspname = NULL;
14184 else
14185 {
14186 nspname = get_namespace_name_or_temp(operform->oprnamespace);
14187 appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
14188 }
14189
14190 appendStringInfoString(&buf, oprname);
14191
14192 if (nspname)
14194
14195 if (p_result != NULL)
14197
14199
14200 return buf.data;
14201}

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 14001 of file ruleutils.c.

14002{
14003 HeapTuple tp;
14005 char *result;
14006
14008 if (!HeapTupleIsValid(tp))
14009 elog(ERROR, "cache lookup failed for relation %u", relid);
14011
14012 result = get_qualified_objname(reltup->relnamespace,
14013 NameStr(reltup->relname));
14014 ReleaseSysCache(tp);
14015
14016 return result;
14017}

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 14292 of file ruleutils.c.

14293{
14294 HeapTuple tp;
14296 char *typname;
14297 char *nspname;
14298 char *result;
14299
14301 if (!HeapTupleIsValid(tp))
14302 elog(ERROR, "cache lookup failed for type %u", typid);
14304 typname = NameStr(typtup->typname);
14305
14306 nspname = get_namespace_name_or_temp(typtup->typnamespace);
14307 if (!nspname)
14308 elog(ERROR, "cache lookup failed for namespace %u",
14309 typtup->typnamespace);
14310
14312
14313 ReleaseSysCache(tp);
14314
14315 return result;
14316}

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 13941 of file ruleutils.c.

13942{
13943 HeapTuple tp;
13945 bool need_qual;
13947 char *relname;
13948 char *nspname;
13949 char *result;
13950
13952 if (!HeapTupleIsValid(tp))
13953 elog(ERROR, "cache lookup failed for relation %u", relid);
13955 relname = NameStr(reltup->relname);
13956
13957 /* Check for conflicting CTE name */
13958 need_qual = false;
13959 foreach(nslist, namespaces)
13960 {
13963
13964 foreach(ctlist, dpns->ctes)
13965 {
13967
13968 if (strcmp(cte->ctename, relname) == 0)
13969 {
13970 need_qual = true;
13971 break;
13972 }
13973 }
13974 if (need_qual)
13975 break;
13976 }
13977
13978 /* Otherwise, qualify the name if not visible in search path */
13979 if (!need_qual)
13980 need_qual = !RelationIsVisible(relid);
13981
13982 if (need_qual)
13983 nspname = get_namespace_name_or_temp(reltup->relnamespace);
13984 else
13985 nspname = NULL;
13986
13988
13989 ReleaseSysCache(tp);
13990
13991 return result;
13992}

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 6476 of file ruleutils.c.

6477{
6478 StringInfo buf = context->buf;
6480 char *sep;
6481 ListCell *l;
6482
6483 if (PRETTY_INDENT(context))
6484 {
6485 context->indentLevel += PRETTYINDENT_STD;
6487 }
6488
6489 /*
6490 * If the query looks like SELECT * FROM (VALUES ...), then print just the
6491 * VALUES part. This reverses what transformValuesClause() did at parse
6492 * time.
6493 */
6494 values_rte = get_simple_values_rte(query, context->resultDesc);
6495 if (values_rte)
6496 {
6497 get_values_def(values_rte->values_lists, context);
6498 return;
6499 }
6500
6501 /*
6502 * Build up the query string - first we say SELECT
6503 */
6504 if (query->isReturn)
6505 appendStringInfoString(buf, "RETURN");
6506 else
6507 appendStringInfoString(buf, "SELECT");
6508
6509 /* Add the DISTINCT clause if given */
6510 if (query->distinctClause != NIL)
6511 {
6512 if (query->hasDistinctOn)
6513 {
6514 appendStringInfoString(buf, " DISTINCT ON (");
6515 sep = "";
6516 foreach(l, query->distinctClause)
6517 {
6519
6521 get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6522 false, context);
6523 sep = ", ";
6524 }
6526 }
6527 else
6528 appendStringInfoString(buf, " DISTINCT");
6529 }
6530
6531 /* Then we tell what to select (the targetlist) */
6532 get_target_list(query->targetList, context);
6533
6534 /* Add the FROM clause if needed */
6535 get_from_clause(query, " FROM ", context);
6536
6537 /* Add the WHERE clause if given */
6538 if (query->jointree->quals != NULL)
6539 {
6540 appendContextKeyword(context, " WHERE ",
6542 get_rule_expr(query->jointree->quals, context, false);
6543 }
6544
6545 /* Add the GROUP BY clause if given */
6546 if (query->groupClause != NULL || query->groupingSets != NULL)
6547 {
6548 bool save_ingroupby;
6549
6550 appendContextKeyword(context, " GROUP BY ",
6552 if (query->groupDistinct)
6553 appendStringInfoString(buf, "DISTINCT ");
6554
6555 save_ingroupby = context->inGroupBy;
6556 context->inGroupBy = true;
6557
6558 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::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 13514 of file ruleutils.c.

13515{
13516 StringInfo buf = context->buf;
13517 int i;
13518 bool first = true;
13519
13520 /* Don't print aliases if not needed */
13521 if (!colinfo->printaliases)
13522 return;
13523
13524 for (i = 0; i < colinfo->num_new_cols; i++)
13525 {
13526 char *colname = colinfo->new_colnames[i];
13527
13528 if (first)
13529 {
13531 first = false;
13532 }
13533 else
13536 }
13537 if (!first)
13539}

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 13481 of file ruleutils.c.

13482{
13483 if (forPortionOf)
13484 {
13485 appendStringInfo(context->buf, " FOR PORTION OF %s",
13486 quote_identifier(forPortionOf->range_name));
13487
13488 /*
13489 * Try to write it as FROM ... TO ... if we received it that way,
13490 * otherwise (targetRange).
13491 */
13492 if (forPortionOf->targetFrom && forPortionOf->targetTo)
13493 {
13494 appendStringInfoString(context->buf, " FROM ");
13495 get_rule_expr(forPortionOf->targetFrom, context, false);
13496 appendStringInfoString(context->buf, " TO ");
13497 get_rule_expr(forPortionOf->targetTo, context, false);
13498 }
13499 else
13500 {
13501 appendStringInfoString(context->buf, " (");
13502 get_rule_expr(forPortionOf->targetRange, context, false);
13503 appendStringInfoChar(context->buf, ')');
13504 }
13505 }
13506}

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 13002 of file ruleutils.c.

13003{
13004 StringInfo buf = context->buf;
13005 bool first = true;
13006 ListCell *l;
13007
13008 /*
13009 * We use the query's jointree as a guide to what to print. However, we
13010 * must ignore auto-added RTEs that are marked not inFromCl. (These can
13011 * only appear at the top level of the jointree, so it's sufficient to
13012 * check here.) This check also ensures we ignore the rule pseudo-RTEs
13013 * for NEW and OLD.
13014 */
13015 foreach(l, query->jointree->fromlist)
13016 {
13017 Node *jtnode = (Node *) lfirst(l);
13018
13019 if (IsA(jtnode, RangeTblRef))
13020 {
13021 int varno = ((RangeTblRef *) jtnode)->rtindex;
13022 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
13023
13024 if (!rte->inFromCl)
13025 continue;
13026 }
13027
13028 if (first)
13029 {
13030 appendContextKeyword(context, prefix,
13032 first = false;
13033
13034 get_from_clause_item(jtnode, query, context);
13035 }
13036 else
13037 {
13039
13041
13042 /*
13043 * Put the new FROM item's text into itembuf so we can decide
13044 * after we've got it whether or not it needs to go on a new line.
13045 */
13047 context->buf = &itembuf;
13048
13049 get_from_clause_item(jtnode, query, context);
13050
13051 /* Restore context's output buffer */
13052 context->buf = buf;
13053
13054 /* Consider line-wrapping if enabled */
13055 if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
13056 {
13057 /* Does the new item start with a new line? */
13058 if (itembuf.len > 0 && itembuf.data[0] == '\n')
13059 {
13060 /* If so, we shouldn't add anything */
13061 /* instead, remove any trailing spaces currently in buf */
13063 }
13064 else
13065 {
13066 char *trailing_nl;
13067
13068 /* Locate the start of the current line in the buffer */
13069 trailing_nl = strrchr(buf->data, '\n');
13070 if (trailing_nl == NULL)
13071 trailing_nl = buf->data;
13072 else
13073 trailing_nl++;
13074
13075 /*
13076 * Add a newline, plus some indentation, if the new item
13077 * would cause an overflow.
13078 */
13079 if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
13083 }
13084 }
13085
13086 /* Add the new item */
13088
13089 /* clean up */
13090 pfree(itembuf.data);
13091 }
13092 }
13093}

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 13554 of file ruleutils.c.

13557{
13558 StringInfo buf = context->buf;
13559 ListCell *l1;
13560 ListCell *l2;
13561 ListCell *l3;
13562 ListCell *l4;
13563 int i;
13564
13566
13567 i = 0;
13568 forfour(l1, rtfunc->funccoltypes,
13569 l2, rtfunc->funccoltypmods,
13570 l3, rtfunc->funccolcollations,
13571 l4, rtfunc->funccolnames)
13572 {
13573 Oid atttypid = lfirst_oid(l1);
13574 int32 atttypmod = lfirst_int(l2);
13575 Oid attcollation = lfirst_oid(l3);
13576 char *attname;
13577
13578 if (colinfo)
13579 attname = colinfo->colnames[i];
13580 else
13581 attname = strVal(lfirst(l4));
13582
13583 Assert(attname); /* shouldn't be any dropped columns here */
13584
13585 if (i > 0)
13587 appendStringInfo(buf, "%s %s",
13589 format_type_with_typemod(atttypid, atttypmod));
13590 if (OidIsValid(attcollation) &&
13591 attcollation != get_typcollation(atttypid))
13592 appendStringInfo(buf, " COLLATE %s",
13593 generate_collation_name(attcollation));
13594
13595 i++;
13596 }
13597
13599}

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 13096 of file ruleutils.c.

13097{
13098 StringInfo buf = context->buf;
13100
13101 if (IsA(jtnode, RangeTblRef))
13102 {
13103 int varno = ((RangeTblRef *) jtnode)->rtindex;
13104 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
13107
13108 if (rte->lateral)
13109 appendStringInfoString(buf, "LATERAL ");
13110
13111 /* Print the FROM item proper */
13112 switch (rte->rtekind)
13113 {
13114 case RTE_RELATION:
13115 /* Normal relation RTE */
13116 appendStringInfo(buf, "%s%s",
13119 context->namespaces));
13120 break;
13121 case RTE_SUBQUERY:
13122 /* Subquery RTE */
13124 get_query_def(rte->subquery, buf, context->namespaces, NULL,
13125 true,
13126 context->prettyFlags, context->wrapColumn,
13127 context->indentLevel);
13129 break;
13130 case RTE_FUNCTION:
13131 /* Function RTE */
13132 rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
13133
13134 /*
13135 * Omit ROWS FROM() syntax for just one function, unless it
13136 * has both a coldeflist and WITH ORDINALITY. If it has both,
13137 * we must use ROWS FROM() syntax to avoid ambiguity about
13138 * whether the coldeflist includes the ordinality column.
13139 */
13140 if (list_length(rte->functions) == 1 &&
13141 (rtfunc1->funccolnames == NIL || !rte->funcordinality))
13142 {
13143 get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
13144 /* we'll print the coldeflist below, if it has one */
13145 }
13146 else
13147 {
13148 bool all_unnest;
13149 ListCell *lc;
13150
13151 /*
13152 * If all the function calls in the list are to unnest,
13153 * and none need a coldeflist, then collapse the list back
13154 * down to UNNEST(args). (If we had more than one
13155 * built-in unnest function, this would get more
13156 * difficult.)
13157 *
13158 * XXX This is pretty ugly, since it makes not-terribly-
13159 * future-proof assumptions about what the parser would do
13160 * with the output; but the alternative is to emit our
13161 * nonstandard ROWS FROM() notation for what might have
13162 * been a perfectly spec-compliant multi-argument
13163 * UNNEST().
13164 */
13165 all_unnest = true;
13166 foreach(lc, rte->functions)
13167 {
13169
13170 if (!IsA(rtfunc->funcexpr, FuncExpr) ||
13171 ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
13172 rtfunc->funccolnames != NIL)
13173 {
13174 all_unnest = false;
13175 break;
13176 }
13177 }
13178
13179 if (all_unnest)
13180 {
13181 List *allargs = NIL;
13182
13183 foreach(lc, rte->functions)
13184 {
13186 List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
13187
13188 allargs = list_concat(allargs, args);
13189 }
13190
13191 appendStringInfoString(buf, "UNNEST(");
13192 get_rule_expr((Node *) allargs, context, true);
13194 }
13195 else
13196 {
13197 int funcno = 0;
13198
13199 appendStringInfoString(buf, "ROWS FROM(");
13200 foreach(lc, rte->functions)
13201 {
13203
13204 if (funcno > 0)
13206 get_rule_expr_funccall(rtfunc->funcexpr, context, true);
13207 if (rtfunc->funccolnames != NIL)
13208 {
13209 /* Reconstruct the column definition list */
13210 appendStringInfoString(buf, " AS ");
13212 NULL,
13213 context);
13214 }
13215 funcno++;
13216 }
13218 }
13219 /* prevent printing duplicate coldeflist below */
13220 rtfunc1 = NULL;
13221 }
13222 if (rte->funcordinality)
13223 appendStringInfoString(buf, " WITH ORDINALITY");
13224 break;
13225 case RTE_TABLEFUNC:
13226 get_tablefunc(rte->tablefunc, context, true);
13227 break;
13228 case RTE_GRAPH_TABLE:
13229 appendStringInfoString(buf, "GRAPH_TABLE (");
13231 appendStringInfoString(buf, " MATCH ");
13232 get_graph_pattern_def(rte->graph_pattern, context);
13233 appendStringInfoString(buf, " COLUMNS (");
13234 {
13235 bool first = true;
13236
13237 foreach_node(TargetEntry, te, rte->graph_table_columns)
13238 {
13239 if (!first)
13241 else
13242 first = false;
13243
13244 get_rule_expr((Node *) te->expr, context, false);
13245 appendStringInfoString(buf, " AS ");
13247 }
13248 }
13250 break;
13251 case RTE_VALUES:
13252 /* Values list RTE */
13254 get_values_def(rte->values_lists, context);
13256 break;
13257 case RTE_CTE:
13259 break;
13260 default:
13261 elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
13262 break;
13263 }
13264
13265 /* Print the relation alias, if needed */
13266 get_rte_alias(rte, varno, false, context);
13267
13268 /* Print the column definitions or aliases, if needed */
13269 if (rtfunc1 && rtfunc1->funccolnames != NIL)
13270 {
13271 /* Reconstruct the columndef list, which is also the aliases */
13273 }
13274 else
13275 {
13276 /* Else print column aliases as needed */
13278 }
13279
13280 /* Tablesample clause must go after any alias */
13281 if (rte->rtekind == RTE_RELATION && rte->tablesample)
13282 get_tablesample_def(rte->tablesample, context);
13283 }
13284 else if (IsA(jtnode, JoinExpr))
13285 {
13286 JoinExpr *j = (JoinExpr *) jtnode;
13289
13290 need_paren_on_right = PRETTY_PAREN(context) &&
13291 !IsA(j->rarg, RangeTblRef) &&
13292 !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
13293
13294 if (!PRETTY_PAREN(context) || j->alias != NULL)
13296
13297 get_from_clause_item(j->larg, query, context);
13298
13299 switch (j->jointype)
13300 {
13301 case JOIN_INNER:
13302 if (j->quals)
13303 appendContextKeyword(context, " JOIN ",
13307 else
13308 appendContextKeyword(context, " CROSS JOIN ",
13312 break;
13313 case JOIN_LEFT:
13314 appendContextKeyword(context, " LEFT JOIN ",
13318 break;
13319 case JOIN_FULL:
13320 appendContextKeyword(context, " FULL JOIN ",
13324 break;
13325 case JOIN_RIGHT:
13326 appendContextKeyword(context, " RIGHT JOIN ",
13330 break;
13331 default:
13332 elog(ERROR, "unrecognized join type: %d",
13333 (int) j->jointype);
13334 }
13335
13338 get_from_clause_item(j->rarg, query, context);
13341
13342 if (j->usingClause)
13343 {
13344 ListCell *lc;
13345 bool first = true;
13346
13347 appendStringInfoString(buf, " USING (");
13348 /* Use the assigned names, not what's in usingClause */
13349 foreach(lc, colinfo->usingNames)
13350 {
13351 char *colname = (char *) lfirst(lc);
13352
13353 if (first)
13354 first = false;
13355 else
13358 }
13360
13361 if (j->join_using_alias)
13362 appendStringInfo(buf, " AS %s",
13363 quote_identifier(j->join_using_alias->aliasname));
13364 }
13365 else if (j->quals)
13366 {
13367 appendStringInfoString(buf, " ON ");
13368 if (!PRETTY_PAREN(context))
13370 get_rule_expr(j->quals, context, false);
13371 if (!PRETTY_PAREN(context))
13373 }
13374 else if (j->jointype != JOIN_INNER)
13375 {
13376 /* If we didn't say CROSS JOIN above, we must provide an ON */
13377 appendStringInfoString(buf, " ON TRUE");
13378 }
13379
13380 if (!PRETTY_PAREN(context) || j->alias != NULL)
13382
13383 /* Yes, it's correct to put alias after the right paren ... */
13384 if (j->alias != NULL)
13385 {
13386 /*
13387 * Note that it's correct to emit an alias clause if and only if
13388 * there was one originally. Otherwise we'd be converting a named
13389 * join to unnamed or vice versa, which creates semantic
13390 * subtleties we don't want. However, we might print a different
13391 * alias name than was there originally.
13392 */
13393 appendStringInfo(buf, " %s",
13395 context)));
13397 }
13398 }
13399 else
13400 elog(ERROR, "unrecognized node type: %d",
13401 (int) nodeTag(jtnode));
13402}

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 if (IsA(ctor->func, Var))
12393 {
12394 /*
12395 * If the aggregate is computed by a lower plan node, setrefs.c will
12396 * have replaced the Aggref or WindowFunc with a Var referencing that
12397 * node's output. Chase the Var back to it so we can still print the
12398 * original JSON aggregate syntax. This only happens in EXPLAIN.
12399 */
12400 resolve_special_varno((Node *) ctor->func, context,
12402 }
12403 else
12404 elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
12405 nodeTag(ctor->func));
12406}

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

Referenced by get_json_constructor().

◆ get_json_agg_constructor_expr()

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

Definition at line 12413 of file ruleutils.c.

12415{
12417
12418 if (!IsA(node, Aggref) && !IsA(node, WindowFunc))
12419 elog(ERROR, "JSON aggregate constructor does not point to an Aggref or WindowFunc");
12420
12421 /* Flat copy suffices; we only replace func. */
12422 ctor = *(JsonConstructorExpr *) callback_arg;
12423 ctor.func = (Expr *) node;
12424 get_json_constructor(&ctor, context, false);
12425}

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

Referenced by get_json_agg_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_json_agg_constructor_expr(), and 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 12902 of file ruleutils.c.

12903{
12904 StringInfo buf = context->buf;
12907
12908 appendStringInfoString(buf, "JSON_TABLE(");
12909
12910 if (PRETTY_INDENT(context))
12911 context->indentLevel += PRETTYINDENT_VAR;
12912
12913 appendContextKeyword(context, "", 0, 0, 0);
12914
12915 get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12916
12918
12919 get_const_expr(root->path->value, context, -1);
12920
12921 appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12922
12923 if (jexpr->passing_values)
12924 {
12925 ListCell *lc1,
12926 *lc2;
12927 bool needcomma = false;
12928
12930 appendContextKeyword(context, "PASSING ", 0, 0, 0);
12931
12932 if (PRETTY_INDENT(context))
12933 context->indentLevel += PRETTYINDENT_VAR;
12934
12935 forboth(lc1, jexpr->passing_names,
12936 lc2, jexpr->passing_values)
12937 {
12938 if (needcomma)
12940 needcomma = true;
12941
12942 appendContextKeyword(context, "", 0, 0, 0);
12943
12944 get_rule_expr((Node *) lfirst(lc2), context, false);
12945 appendStringInfo(buf, " AS %s",
12947 );
12948 }
12949
12950 if (PRETTY_INDENT(context))
12951 context->indentLevel -= PRETTYINDENT_VAR;
12952 }
12953
12954 get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12955 showimplicit);
12956
12957 /*
12958 * Deparse a PLAN clause only for a non-default plan; the default plan is
12959 * implied by the NESTED COLUMNS structure (see
12960 * json_table_plan_is_default).
12961 */
12963 {
12965 appendContextKeyword(context, "PLAN ", 0, 0, 0);
12966 get_json_table_plan(tf, (JsonTablePlan *) root, context, true);
12967 }
12968
12969 if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12970 get_json_behavior(jexpr->on_error, context, "ERROR");
12971
12972 if (PRETTY_INDENT(context))
12973 context->indentLevel -= PRETTYINDENT_VAR;
12974
12975 appendContextKeyword(context, ")", 0, 0, 0);
12976}

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_json_table_plan(), get_rule_expr(), deparse_context::indentLevel, JSON_BEHAVIOR_EMPTY_ARRAY, json_table_plan_is_default(), 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 12796 of file ruleutils.c.

12799{
12800 StringInfo buf = context->buf;
12805 int colnum = 0;
12806
12808 appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12809
12810 if (PRETTY_INDENT(context))
12811 context->indentLevel += PRETTYINDENT_VAR;
12812
12813 forfour(lc_colname, tf->colnames,
12814 lc_coltype, tf->coltypes,
12815 lc_coltypmod, tf->coltypmods,
12816 lc_colvalexpr, tf->colvalexprs)
12817 {
12818 char *colname = strVal(lfirst(lc_colname));
12819 JsonExpr *colexpr;
12820 Oid typid;
12821 int32 typmod;
12822 bool ordinality;
12824
12825 typid = lfirst_oid(lc_coltype);
12826 typmod = lfirst_int(lc_coltypmod);
12828
12829 /* Skip columns that don't belong to this scan. */
12830 if (scan->colMin < 0 || colnum < scan->colMin)
12831 {
12832 colnum++;
12833 continue;
12834 }
12835 if (colnum > scan->colMax)
12836 break;
12837
12838 if (colnum > scan->colMin)
12840
12841 colnum++;
12842
12843 ordinality = !colexpr;
12844
12845 appendContextKeyword(context, "", 0, 0, 0);
12846
12847 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12848 ordinality ? "FOR ORDINALITY" :
12849 format_type_with_typemod(typid, typmod));
12850 if (ordinality)
12851 continue;
12852
12853 /*
12854 * Set default_behavior to guide get_json_expr_options() on whether to
12855 * emit the ON ERROR / EMPTY clauses.
12856 */
12857 if (colexpr->op == JSON_EXISTS_OP)
12858 {
12859 appendStringInfoString(buf, " EXISTS");
12861 }
12862 else
12863 {
12864 if (colexpr->op == JSON_QUERY_OP)
12865 {
12866 char typcategory;
12867 bool typispreferred;
12868
12870
12873 colexpr->format->format_type == JS_FORMAT_JSONB ?
12874 " FORMAT JSONB" : " FORMAT JSON");
12875 }
12876
12878 }
12879
12880 appendStringInfoString(buf, " PATH ");
12881
12882 get_json_path_spec(colexpr->path_spec, context, showimplicit);
12883
12884 get_json_expr_options(colexpr, context, default_behavior);
12885 }
12886
12887 if (scan->child)
12889 scan->colMin >= 0);
12890
12891 if (PRETTY_INDENT(context))
12892 context->indentLevel -= PRETTYINDENT_VAR;
12893
12894 appendContextKeyword(context, ")", 0, 0, 0);
12895}

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 12680 of file ruleutils.c.

12683{
12685 {
12687
12688 if (needcomma)
12689 appendStringInfoChar(context->buf, ',');
12690
12691 appendStringInfoChar(context->buf, ' ');
12692 appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
12693 get_const_expr(scan->path->value, context, -1);
12694 appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
12695 get_json_table_columns(tf, scan, context, showimplicit);
12696 }
12697 else if (IsA(plan, JsonTableSiblingJoin))
12698 {
12700
12702 needcomma);
12704 true);
12705 }
12706}

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_json_table_plan()

static void get_json_table_plan ( TableFunc tf,
JsonTablePlan plan,
deparse_context context,
bool  parenthesize 
)
static

Definition at line 12752 of file ruleutils.c.

12754{
12755 if (parenthesize)
12756 appendStringInfoChar(context->buf, '(');
12757
12759 {
12761
12763
12764 if (s->child)
12765 {
12766 appendStringInfoString(context->buf,
12767 s->outerJoin ? " OUTER " : " INNER ");
12768 get_json_table_plan(tf, s->child, context,
12770 castNode(JsonTablePathScan, s->child)->child);
12771 }
12772 }
12773 else if (IsA(plan, JsonTableSiblingJoin))
12774 {
12776
12777 get_json_table_plan(tf, j->lplan, context,
12778 IsA(j->lplan, JsonTableSiblingJoin) ||
12779 castNode(JsonTablePathScan, j->lplan)->child);
12780
12781 appendStringInfoString(context->buf, j->cross ? " CROSS " : " UNION ");
12782
12783 get_json_table_plan(tf, j->rplan, context,
12784 IsA(j->rplan, JsonTableSiblingJoin) ||
12785 castNode(JsonTablePathScan, j->rplan)->child);
12786 }
12787
12788 if (parenthesize)
12789 appendStringInfoChar(context->buf, ')');
12790}

References appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, castNode, JsonTablePathScan::child, fb(), get_json_table_plan(), IsA, j, JsonTablePath::name, JsonTablePathScan::outerJoin, JsonTablePathScan::path, plan, and quote_identifier().

Referenced by get_json_table(), and get_json_table_plan().

◆ get_lock_clause_strength()

static char * get_lock_clause_strength ( LockClauseStrength  strength)
static

Definition at line 6380 of file ruleutils.c.

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

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 13649 of file ruleutils.c.

13651{
13654 char *opcname;
13655 char *nspname;
13656
13659 elog(ERROR, "cache lookup failed for opclass %u", opclass);
13661
13663 GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
13664 {
13665 /* Okay, we need the opclass name. Do we need to qualify it? */
13666 opcname = NameStr(opcrec->opcname);
13667 if (OpclassIsVisible(opclass))
13669 else
13670 {
13671 nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
13672 appendStringInfo(buf, " %s.%s",
13673 quote_identifier(nspname),
13675 }
13676 }
13678}

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 5988 of file ruleutils.c.

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

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 14458 of file ruleutils.c.

14459{
14460 deparse_context context;
14462 ListCell *cell;
14463 char *sep;
14464
14466 memset(&context, 0, sizeof(deparse_context));
14467 context.buf = &buf;
14468
14470 sep = "";
14471 foreach(cell, bound_datums)
14472 {
14473 PartitionRangeDatum *datum =
14475
14478 appendStringInfoString(&buf, "MINVALUE");
14479 else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
14480 appendStringInfoString(&buf, "MAXVALUE");
14481 else
14482 {
14483 Const *val = castNode(Const, datum->value);
14484
14485 get_const_expr(val, &context, -1);
14486 }
14487 sep = ", ";
14488 }
14490
14491 return buf.data;
14492}

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 13921 of file ruleutils.c.

13922{
13923 char *relname = get_rel_name(relid);
13924
13925 if (!relname)
13926 elog(ERROR, "cache lookup failed for relation %u", relid);
13927 return relname;
13928}

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 14370 of file ruleutils.c.

14371{
14372 Datum *options;
14373 int noptions;
14374 int i;
14375
14377 &options, NULL, &noptions);
14378
14379 for (i = 0; i < noptions; i++)
14380 {
14382 char *name;
14383 char *separator;
14384 char *value;
14385
14386 /*
14387 * Each array element should have the form name=value. If the "=" is
14388 * missing for some reason, treat it like an empty value.
14389 */
14390 name = option;
14391 separator = strchr(option, '=');
14392 if (separator)
14393 {
14394 *separator = '\0';
14395 value = separator + 1;
14396 }
14397 else
14398 value = "";
14399
14400 if (i > 0)
14403
14404 /*
14405 * In general we need to quote the value; but to avoid unnecessary
14406 * clutter, do not quote if it is an identifier that would not need
14407 * quoting. (We could also allow numbers, but that is a bit trickier
14408 * than it looks --- for example, are leading zeroes significant? We
14409 * don't want to assume very much here about what custom reloptions
14410 * might mean.)
14411 */
14414 else
14416
14417 pfree(option);
14418 }
14419}

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 5497 of file ruleutils.c.

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

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 13410 of file ruleutils.c.

13412{
13414 char *refname = get_rtable_name(varno, context);
13416 bool printalias = false;
13417
13418 if (rte->alias != NULL)
13419 {
13420 /* Always print alias if user provided one */
13421 printalias = true;
13422 }
13423 else if (colinfo->printaliases)
13424 {
13425 /* Always print alias if we need to print column aliases */
13426 printalias = true;
13427 }
13428 else if (rte->rtekind == RTE_RELATION)
13429 {
13430 /*
13431 * No need to print alias if it's same as relation name (this would
13432 * normally be the case, but not if set_rtable_names had to resolve a
13433 * conflict).
13434 */
13435 if (strcmp(refname, get_relation_name(rte->relid)) != 0)
13436 printalias = true;
13437 }
13438 else if (rte->rtekind == RTE_FUNCTION)
13439 {
13440 /*
13441 * For a function RTE, always print alias. This covers possible
13442 * renaming of the function and/or instability of the FigureColname
13443 * rules for things that aren't simple functions. Note we'd need to
13444 * force it anyway for the columndef list case.
13445 */
13446 printalias = true;
13447 }
13448 else if (rte->rtekind == RTE_SUBQUERY ||
13449 rte->rtekind == RTE_VALUES)
13450 {
13451 /*
13452 * For a subquery, always print alias. This makes the output
13453 * SQL-spec-compliant, even though we allow such aliases to be omitted
13454 * on input.
13455 */
13456 printalias = true;
13457 }
13458 else if (rte->rtekind == RTE_CTE)
13459 {
13460 /*
13461 * No need to print alias if it's same as CTE name (this would
13462 * normally be the case, but not if set_rtable_names had to resolve a
13463 * conflict).
13464 */
13465 if (strcmp(refname, rte->ctename) != 0)
13466 printalias = true;
13467 }
13468
13469 if (printalias)
13470 appendStringInfo(context->buf, "%s%s",
13471 use_as ? " AS " : " ",
13472 quote_identifier(refname));
13473}

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 {
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 6273 of file ruleutils.c.

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

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 6407 of file ruleutils.c.

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

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 12456 of file ruleutils.c.

12457{
12458 StringInfo buf = context->buf;
12459 Query *query = (Query *) (sublink->subselect);
12460 char *opname = NULL;
12461 bool need_paren;
12462
12463 if (sublink->subLinkType == ARRAY_SUBLINK)
12464 appendStringInfoString(buf, "ARRAY(");
12465 else
12467
12468 /*
12469 * Note that we print the name of only the first operator, when there are
12470 * multiple combining operators. This is an approximation that could go
12471 * wrong in various scenarios (operators in different schemas, renamed
12472 * operators, etc) but there is not a whole lot we can do about it, since
12473 * the syntax allows only one operator to be shown.
12474 */
12475 if (sublink->testexpr)
12476 {
12477 if (IsA(sublink->testexpr, OpExpr))
12478 {
12479 /* single combining operator */
12480 OpExpr *opexpr = (OpExpr *) sublink->testexpr;
12481
12482 get_rule_expr(linitial(opexpr->args), context, true);
12483 opname = generate_operator_name(opexpr->opno,
12484 exprType(linitial(opexpr->args)),
12485 exprType(lsecond(opexpr->args)));
12486 }
12487 else if (IsA(sublink->testexpr, BoolExpr))
12488 {
12489 /* multiple combining operators, = or <> cases */
12490 char *sep;
12491 ListCell *l;
12492
12494 sep = "";
12495 foreach(l, ((BoolExpr *) sublink->testexpr)->args)
12496 {
12497 OpExpr *opexpr = lfirst_node(OpExpr, l);
12498
12500 get_rule_expr(linitial(opexpr->args), context, true);
12501 if (!opname)
12503 exprType(linitial(opexpr->args)),
12504 exprType(lsecond(opexpr->args)));
12505 sep = ", ";
12506 }
12508 }
12509 else if (IsA(sublink->testexpr, RowCompareExpr))
12510 {
12511 /* multiple combining operators, < <= > >= cases */
12513
12515 get_rule_expr((Node *) rcexpr->largs, context, true);
12517 exprType(linitial(rcexpr->largs)),
12518 exprType(linitial(rcexpr->rargs)));
12520 }
12521 else
12522 elog(ERROR, "unrecognized testexpr type: %d",
12523 (int) nodeTag(sublink->testexpr));
12524 }
12525
12526 need_paren = true;
12527
12528 switch (sublink->subLinkType)
12529 {
12530 case EXISTS_SUBLINK:
12531 appendStringInfoString(buf, "EXISTS ");
12532 break;
12533
12534 case ANY_SUBLINK:
12535 if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
12536 appendStringInfoString(buf, " IN ");
12537 else
12538 appendStringInfo(buf, " %s ANY ", opname);
12539 break;
12540
12541 case ALL_SUBLINK:
12542 appendStringInfo(buf, " %s ALL ", opname);
12543 break;
12544
12545 case ROWCOMPARE_SUBLINK:
12546 appendStringInfo(buf, " %s ", opname);
12547 break;
12548
12549 case EXPR_SUBLINK:
12550 case MULTIEXPR_SUBLINK:
12551 case ARRAY_SUBLINK:
12552 need_paren = false;
12553 break;
12554
12555 case CTE_SUBLINK: /* shouldn't occur in a SubLink */
12556 default:
12557 elog(ERROR, "unrecognized sublink type: %d",
12558 (int) sublink->subLinkType);
12559 break;
12560 }
12561
12562 if (need_paren)
12564
12565 get_query_def(query, buf, context->namespaces, NULL, false,
12566 context->prettyFlags, context->wrapColumn,
12567 context->indentLevel);
12568
12569 if (need_paren)
12571 else
12573}

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 12983 of file ruleutils.c.

12984{
12985 /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12986
12987 if (tf->functype == TFT_XMLTABLE)
12988 get_xmltable(tf, context, showimplicit);
12989 else if (tf->functype == TFT_JSON_TABLE)
12990 get_json_table(tf, context, showimplicit);
12991}

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 13605 of file ruleutils.c.

13606{
13607 StringInfo buf = context->buf;
13608 Oid argtypes[1];
13609 int nargs;
13610 ListCell *l;
13611
13612 /*
13613 * We should qualify the handler's function name if it wouldn't be
13614 * resolved by lookup in the current search path.
13615 */
13616 argtypes[0] = INTERNALOID;
13617 appendStringInfo(buf, " TABLESAMPLE %s (",
13618 generate_function_name(tablesample->tsmhandler, 1,
13619 NIL, argtypes,
13620 false, NULL, false));
13621
13622 nargs = 0;
13623 foreach(l, tablesample->args)
13624 {
13625 if (nargs++ > 0)
13627 get_rule_expr((Node *) lfirst(l), context, false);
13628 }
13630
13631 if (tablesample->repeatable != NULL)
13632 {
13633 appendStringInfoString(buf, " REPEATABLE (");
13634 get_rule_expr((Node *) tablesample->repeatable, context, false);
13636 }
13637}

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 6091 of file ruleutils.c.

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

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 6134 of file ruleutils.c.

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

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 12581 of file ruleutils.c.

12582{
12583 StringInfo buf = context->buf;
12584
12585 appendStringInfoString(buf, "XMLTABLE(");
12586
12587 if (tf->ns_uris != NIL)
12588 {
12589 ListCell *lc1,
12590 *lc2;
12591 bool first = true;
12592
12593 appendStringInfoString(buf, "XMLNAMESPACES (");
12594 forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
12595 {
12596 Node *expr = (Node *) lfirst(lc1);
12598
12599 if (!first)
12601 else
12602 first = false;
12603
12604 if (ns_node != NULL)
12605 {
12606 get_rule_expr(expr, context, showimplicit);
12607 appendStringInfo(buf, " AS %s",
12609 }
12610 else
12611 {
12612 appendStringInfoString(buf, "DEFAULT ");
12613 get_rule_expr(expr, context, showimplicit);
12614 }
12615 }
12617 }
12618
12620 get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
12621 appendStringInfoString(buf, ") PASSING (");
12622 get_rule_expr((Node *) tf->docexpr, context, showimplicit);
12624
12625 if (tf->colexprs != NIL)
12626 {
12627 ListCell *l1;
12628 ListCell *l2;
12629 ListCell *l3;
12630 ListCell *l4;
12631 ListCell *l5;
12632 int colnum = 0;
12633
12634 appendStringInfoString(buf, " COLUMNS ");
12635 forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
12636 l4, tf->colexprs, l5, tf->coldefexprs)
12637 {
12638 char *colname = strVal(lfirst(l1));
12639 Oid typid = lfirst_oid(l2);
12640 int32 typmod = lfirst_int(l3);
12641 Node *colexpr = (Node *) lfirst(l4);
12642 Node *coldefexpr = (Node *) lfirst(l5);
12643 bool ordinality = (tf->ordinalitycol == colnum);
12644 bool notnull = bms_is_member(colnum, tf->notnulls);
12645
12646 if (colnum > 0)
12648 colnum++;
12649
12650 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12651 ordinality ? "FOR ORDINALITY" :
12652 format_type_with_typemod(typid, typmod));
12653 if (ordinality)
12654 continue;
12655
12656 if (coldefexpr != NULL)
12657 {
12658 appendStringInfoString(buf, " DEFAULT (");
12659 get_rule_expr((Node *) coldefexpr, context, showimplicit);
12661 }
12662 if (colexpr != NULL)
12663 {
12664 appendStringInfoString(buf, " PATH (");
12665 get_rule_expr((Node *) colexpr, context, showimplicit);
12667 }
12668 if (notnull)
12669 appendStringInfoString(buf, " NOT NULL");
12670 }
12671 }
12672
12674}

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 4504 of file ruleutils.c.

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

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 5429 of file ruleutils.c.

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

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 3810 of file ruleutils.c.

3811{
3812 return (!argmodes
3816}

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().

◆ json_table_plan_is_default()

static bool json_table_plan_is_default ( JsonTablePlan plan)
static

Definition at line 12722 of file ruleutils.c.

12723{
12725 {
12727
12728 if (scan->child)
12729 {
12730 if (!scan->outerJoin)
12731 return false; /* INNER is not the default */
12732 return json_table_plan_is_default(scan->child);
12733 }
12734
12735 return true;
12736 }
12737 else
12738 {
12740
12741 if (join->cross)
12742 return false; /* CROSS is not the default */
12743 return json_table_plan_is_default(join->lplan) &&
12745 }
12746}

References castNode, JsonTablePathScan::child, JsonTableSiblingJoin::cross, IsA, json_table_plan_is_default(), JsonTableSiblingJoin::lplan, JsonTablePathScan::outerJoin, plan, and JsonTableSiblingJoin::rplan.

Referenced by get_json_table(), and json_table_plan_is_default().

◆ 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 1786 of file ruleutils.c.

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

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 5287 of file ruleutils.c.

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

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 1662 of file ruleutils.c.

1663{
1666 SysScanDesc scan;
1667 bool first;
1668 HeapTuple tup;
1669
1671
1672 ScanKeyInit(&scankey[0],
1676
1678
1679 first = true;
1680 while ((tup = systable_getnext(scan)))
1681 {
1683 char *relname;
1684 Datum datum;
1685 bool isnull;
1686
1687 if (pgeform->pgekind != pgekind)
1688 continue;
1689
1690 if (first)
1691 {
1692 appendStringInfo(buf, "\n %s TABLES (\n", pgekind == PGEKIND_VERTEX ? "VERTEX" : "EDGE");
1693 first = false;
1694 }
1695 else
1697
1698 relname = get_rel_name(pgeform->pgerelid);
1699 if (relname && strcmp(relname, NameStr(pgeform->pgealias)) == 0)
1700 appendStringInfo(buf, " %s",
1701 generate_relation_name(pgeform->pgerelid, NIL));
1702 else
1703 appendStringInfo(buf, " %s AS %s",
1705 quote_identifier(NameStr(pgeform->pgealias)));
1706
1708 if (!isnull)
1709 {
1710 appendStringInfoString(buf, " KEY (");
1711 decompile_column_index_array(datum, pgeform->pgerelid, false, buf);
1713 }
1714 else
1715 elog(ERROR, "null pgekey for element %u", pgeform->oid);
1716
1717 if (pgekind == PGEKIND_EDGE)
1718 {
1719 Datum srckey;
1720 Datum srcref;
1721 Datum destkey;
1722 Datum destref;
1725
1727 srckey = isnull ? 0 : datum;
1729 srcref = isnull ? 0 : datum;
1731 destkey = isnull ? 0 : datum;
1733 destref = isnull ? 0 : datum;
1734
1735 appendStringInfoString(buf, " SOURCE");
1737 if (!tup2)
1738 elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgesrcvertexid);
1740 if (srckey)
1741 {
1742 appendStringInfoString(buf, " KEY (");
1743 decompile_column_index_array(srckey, pgeform->pgerelid, false, buf);
1744 appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1745 decompile_column_index_array(srcref, pgeform2->pgerelid, false, buf);
1747 }
1748 else
1751
1752 appendStringInfoString(buf, " DESTINATION");
1754 if (!tup2)
1755 elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgedestvertexid);
1757 if (destkey)
1758 {
1759 appendStringInfoString(buf, " KEY (");
1760 decompile_column_index_array(destkey, pgeform->pgerelid, false, buf);
1761 appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
1762 decompile_column_index_array(destref, pgeform2->pgerelid, false, buf);
1764 }
1765 else
1768 }
1769
1770 make_propgraphdef_labels(buf, pgeform->oid, NameStr(pgeform->pgealias), pgeform->pgerelid);
1771 }
1772 if (!first)
1773 appendStringInfoString(buf, "\n )");
1774
1775 systable_endscan(scan);
1777}

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 1799 of file ruleutils.c.

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

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 1883 of file ruleutils.c.

1884{
1887 SysScanDesc scan;
1888 HeapTuple tup;
1889 List *outlist = NIL;
1890
1892
1893 ScanKeyInit(&scankey[0],
1897
1898 /*
1899 * We want to output the properties in a deterministic order. So we first
1900 * read all the data, then sort, then print it.
1901 */
1903
1904 while ((tup = systable_getnext(scan)))
1905 {
1908 bool isnull;
1909 char *tmp;
1910 Node *expr;
1911 char *propname;
1912
1914 Assert(!isnull);
1916 expr = stringToNode(tmp);
1917 pfree(tmp);
1918
1920
1921 outlist = lappend(outlist, list_make2(makeString(propname), expr));
1922 }
1923
1924 systable_endscan(scan);
1926
1928
1929 if (outlist)
1930 {
1931 List *context;
1932 ListCell *lc;
1933 bool first = true;
1934
1936
1937 appendStringInfoString(buf, " PROPERTIES (");
1938
1939 foreach(lc, outlist)
1940 {
1942 char *propname = strVal(linitial(data));
1943 Node *expr = lsecond(data);
1944
1945 if (first)
1946 first = false;
1947 else
1949
1950 if (IsA(expr, Var) && strcmp(propname, get_attname(elrelid, castNode(Var, expr)->varattno, false)) == 0)
1952 else
1953 appendStringInfo(buf, "%s AS %s",
1954 deparse_expression_pretty(expr, context, false, false, 0, 0),
1956 }
1957
1959 }
1960 else
1961 appendStringInfoString(buf, " NO PROPERTIES");
1962}

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 5711 of file ruleutils.c.

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

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 5903 of file ruleutils.c.

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

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 2506 of file ruleutils.c.

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

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 2544 of file ruleutils.c.

2545{
2546 return pg_get_constraintdef_worker(constraintId, true, 0, false);
2547}

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 2523 of file ruleutils.c.

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

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 2553 of file ruleutils.c.

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

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 3035 of file ruleutils.c.

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

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 3052 of file ruleutils.c.

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

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 3070 of file ruleutils.c.

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

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 3850 of file ruleutils.c.

3851{
3852 Oid funcid = PG_GETARG_OID(0);
3855 Form_pg_proc proc;
3856 int numargs;
3857 Oid *argtypes;
3858 char **argnames;
3859 char *argmodes;
3860 int i;
3862 Node *node;
3863 char *str;
3864 int nth_inputarg;
3866 bool isnull;
3867 int nth_default;
3868
3872
3873 numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3875 {
3878 }
3879
3880 nth_inputarg = 0;
3881 for (i = 0; i < nth_arg; i++)
3883 nth_inputarg++;
3884
3887 &isnull);
3888 if (isnull)
3889 {
3892 }
3893
3896 pfree(str);
3897
3898 proc = (Form_pg_proc) GETSTRUCT(proctup);
3899
3900 /*
3901 * Calculate index into proargdefaults: proargdefaults corresponds to the
3902 * last N input arguments, where N = pronargdefaults.
3903 */
3904 nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3905
3907 {
3910 }
3912 str = deparse_expression(node, NIL, false, false);
3913
3915
3917}

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 3974 of file ruleutils.c.

3975{
3976 Oid funcid = PG_GETARG_OID(0);
3979 bool isnull;
3980
3982
3983 /* Look up the function */
3987
3989 if (isnull)
3990 {
3993 }
3994
3996
3998
4000}

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 3287 of file ruleutils.c.

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

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 1191 of file ruleutils.c.

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

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 1248 of file ruleutils.c.

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

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 1262 of file ruleutils.c.

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

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 1211 of file ruleutils.c.

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

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 1238 of file ruleutils.c.

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

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 1283 of file ruleutils.c.

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

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 2488 of file ruleutils.c.

2489{
2491 List *context;
2492
2494 context = deparse_context_for(aliasname, partitionId);
2495
2496 return deparse_expression((Node *) constr_expr, context, true, false);
2497}

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 2456 of file ruleutils.c.

2457{
2460 int prettyFlags;
2461 List *context;
2462 char *consrc;
2463
2465
2466 /* Quick exit if no partition constraint */
2467 if (constr_expr == NULL)
2469
2470 /*
2471 * Deparse and return the constraint expression.
2472 */
2473 prettyFlags = PRETTYFLAG_INDENT;
2475 consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2476 false, prettyFlags, 0);
2477
2479}

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 2269 of file ruleutils.c.

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

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 2284 of file ruleutils.c.

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

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 2297 of file ruleutils.c.

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

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 1601 of file ruleutils.c.

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

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 573 of file ruleutils.c.

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

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 591 of file ruleutils.c.

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

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 610 of file ruleutils.c.

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

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 3193 of file ruleutils.c.

3194{
3195 text *tablename = PG_GETARG_TEXT_PP(0);
3198 Oid tableOid;
3199 char *column;
3203 ScanKeyData key[3];
3204 SysScanDesc scan;
3205 HeapTuple tup;
3206
3207 /* Look up table name. Can't lock it - we might not have privileges. */
3209 tableOid = RangeVarGetRelid(tablerv, NoLock, false);
3210
3211 /* Get the number of the column */
3213
3214 attnum = get_attnum(tableOid, column);
3216 ereport(ERROR,
3218 errmsg("column \"%s\" of relation \"%s\" does not exist",
3219 column, tablerv->relname)));
3220
3221 /* Search the dependency table for the dependent sequence */
3223
3224 ScanKeyInit(&key[0],
3228 ScanKeyInit(&key[1],
3231 ObjectIdGetDatum(tableOid));
3232 ScanKeyInit(&key[2],
3236
3238 NULL, 3, key);
3239
3240 while (HeapTupleIsValid(tup = systable_getnext(scan)))
3241 {
3243
3244 /*
3245 * Look for an auto dependency (serial column) or internal dependency
3246 * (identity column) of a sequence on a column. (We need the relkind
3247 * test because indexes can also have auto dependencies on columns.)
3248 */
3249 if (deprec->classid == RelationRelationId &&
3250 deprec->objsubid == 0 &&
3251 (deprec->deptype == DEPENDENCY_AUTO ||
3252 deprec->deptype == DEPENDENCY_INTERNAL) &&
3254 {
3255 sequenceId = deprec->objid;
3256 break;
3257 }
3258 }
3259
3260 systable_endscan(scan);
3262
3264 {
3265 char *result;
3266
3268
3270 }
3271
3273}

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 2014 of file ruleutils.c.

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

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 1969 of file ruleutils.c.

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

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 1997 of file ruleutils.c.

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

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 2198 of file ruleutils.c.

2199{
2203 Datum datum;
2204 List *context;
2205 ListCell *lc;
2206 List *exprs = NIL;
2207 bool has_exprs;
2208 char *tmp;
2209 ArrayBuildState *astate = NULL;
2210
2212
2215
2216 /* Does the stats object have expressions? */
2218
2219 /* no expressions? we're done */
2220 if (!has_exprs)
2221 {
2224 }
2225
2227
2228 /*
2229 * Get the statistics expressions, and deparse them into text values.
2230 */
2233 tmp = TextDatumGetCString(datum);
2234 exprs = (List *) stringToNode(tmp);
2235 pfree(tmp);
2236
2238 statextrec->stxrelid);
2239
2240 foreach(lc, exprs)
2241 {
2242 Node *expr = (Node *) lfirst(lc);
2243 char *str;
2244 int prettyFlags = PRETTYFLAG_INDENT;
2245
2246 str = deparse_expression_pretty(expr, context, false, false,
2247 prettyFlags, 0);
2248
2249 astate = accumArrayResult(astate,
2251 false,
2252 TEXTOID,
2254 }
2255
2257
2259}

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 1987 of file ruleutils.c.

1988{
1989 return pg_get_statisticsobj_worker(statextid, false, false);
1990}

References fb(), and pg_get_statisticsobj_worker().

Referenced by RememberStatisticsForRebuilding().

◆ pg_get_triggerdef()

Datum pg_get_triggerdef ( PG_FUNCTION_ARGS  )

Definition at line 884 of file ruleutils.c.

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

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 898 of file ruleutils.c.

899{
901 bool pretty = PG_GETARG_BOOL(1);
902 char *res;
903
905
906 if (res == NULL)
908
910}

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 913 of file ruleutils.c.

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

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 3155 of file ruleutils.c.

3156{
3157 Oid roleid = PG_GETARG_OID(0);
3158 Name result;
3161
3162 /*
3163 * Allocate space for the result
3164 */
3167
3168 /*
3169 * Get the pg_authid entry and print the result
3170 */
3173 {
3175 *result = role_rec->rolname;
3177 }
3178 else
3179 sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
3180
3182}

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 691 of file ruleutils.c.

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

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 710 of file ruleutils.c.

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

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 749 of file ruleutils.c.

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

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 774 of file ruleutils.c.

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

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 802 of file ruleutils.c.

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

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 729 of file ruleutils.c.

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

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 5695 of file ruleutils.c.

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

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 5644 of file ruleutils.c.

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

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 3662 of file ruleutils.c.

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

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 3624 of file ruleutils.c.

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

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 3920 of file ruleutils.c.

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

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 3822 of file ruleutils.c.

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

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 13787 of file ruleutils.c.

13788{
13789 StringInfo buf = context->buf;
13792
13793 lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
13794 foreach(uplist_item, sbsref->refupperindexpr)
13795 {
13797 if (lowlist_item)
13798 {
13799 /* If subexpression is NULL, get_rule_expr prints nothing */
13800 get_rule_expr((Node *) lfirst(lowlist_item), context, false);
13803 }
13804 /* If subexpression is NULL, get_rule_expr prints nothing */
13805 get_rule_expr((Node *) lfirst(uplist_item), context, false);
13807 }
13808}

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 13709 of file ruleutils.c.

13710{
13711 StringInfo buf = context->buf;
13713
13714 for (;;)
13715 {
13716 if (node == NULL)
13717 break;
13718 if (IsA(node, FieldStore))
13719 {
13720 FieldStore *fstore = (FieldStore *) node;
13721 Oid typrelid;
13722 char *fieldname;
13723
13724 /* lookup tuple type */
13725 typrelid = get_typ_typrelid(fstore->resulttype);
13726 if (!OidIsValid(typrelid))
13727 elog(ERROR, "argument type %s of FieldStore is not a tuple type",
13728 format_type_be(fstore->resulttype));
13729
13730 /*
13731 * Print the field name. There should only be one target field in
13732 * stored rules. There could be more than that in executable
13733 * target lists, but this function cannot be used for that case.
13734 */
13735 Assert(list_length(fstore->fieldnums) == 1);
13736 fieldname = get_attname(typrelid,
13737 linitial_int(fstore->fieldnums), false);
13738 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
13739
13740 /*
13741 * We ignore arg since it should be an uninteresting reference to
13742 * the target column or subcolumn.
13743 */
13744 node = (Node *) linitial(fstore->newvals);
13745 }
13746 else if (IsA(node, SubscriptingRef))
13747 {
13748 SubscriptingRef *sbsref = (SubscriptingRef *) node;
13749
13750 if (sbsref->refassgnexpr == NULL)
13751 break;
13752
13753 printSubscripts(sbsref, context);
13754
13755 /*
13756 * We ignore refexpr since it should be an uninteresting reference
13757 * to the target column or subcolumn.
13758 */
13759 node = (Node *) sbsref->refassgnexpr;
13760 }
13761 else if (IsA(node, CoerceToDomain))
13762 {
13763 cdomain = (CoerceToDomain *) node;
13764 /* If it's an explicit domain coercion, we're done */
13765 if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
13766 break;
13767 /* Tentatively descend past the CoerceToDomain */
13768 node = (Node *) cdomain->arg;
13769 }
13770 else
13771 break;
13772 }
13773
13774 /*
13775 * If we descended past a CoerceToDomain whose argument turned out not to
13776 * be a FieldStore or array assignment, back up to the CoerceToDomain.
13777 * (This is not enough to be fully correct if there are nested implicit
13778 * CoerceToDomains, but such cases shouldn't ever occur.)
13779 */
13780 if (cdomain && node == (Node *) cdomain->arg)
13781 node = (Node *) cdomain;
13782
13783 return node;
13784}

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 1867 of file ruleutils.c.

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

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 5674 of file ruleutils.c.

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

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 5627 of file ruleutils.c.

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

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 13817 of file ruleutils.c.

13818{
13819 /*
13820 * Can avoid quoting if ident starts with a lowercase letter or underscore
13821 * and contains only lowercase letters, digits, and underscores, *and* is
13822 * not any SQL keyword. Otherwise, supply quotes.
13823 */
13824 int nquotes = 0;
13825 bool safe;
13826 const char *ptr;
13827 char *result;
13828 char *optr;
13829
13830 /*
13831 * would like to use <ctype.h> macros here, but they might yield unwanted
13832 * locale-specific results...
13833 */
13834 safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
13835
13836 for (ptr = ident; *ptr; ptr++)
13837 {
13838 char ch = *ptr;
13839
13840 if ((ch >= 'a' && ch <= 'z') ||
13841 (ch >= '0' && ch <= '9') ||
13842 (ch == '_'))
13843 {
13844 /* okay */
13845 }
13846 else
13847 {
13848 safe = false;
13849 if (ch == '"')
13850 nquotes++;
13851 }
13852 }
13853
13855 safe = false;
13856
13857 if (safe)
13858 {
13859 /*
13860 * Check for keyword. We quote keywords except for unreserved ones.
13861 * (In some cases we could avoid quoting a col_name or type_func_name
13862 * keyword, but it seems much harder than it's worth to tell that.)
13863 *
13864 * Note: ScanKeywordLookup() does case-insensitive comparison, but
13865 * that's fine, since we already know we have all-lower-case.
13866 */
13868
13870 safe = false;
13871 }
13872
13873 if (safe)
13874 return ident; /* no change needed */
13875
13876 result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13877
13878 optr = result;
13879 *optr++ = '"';
13880 for (ptr = ident; *ptr; ptr++)
13881 {
13882 char ch = *ptr;
13883
13884 if (ch == '"')
13885 *optr++ = '"';
13886 *optr++ = ch;
13887 }
13888 *optr++ = '"';
13889 *optr = '\0';
13890
13891 return result;
13892}

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_json_table_plan(), 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(), make_propgraphdef_elements(), make_propgraphdef_labels(), make_propgraphdef_properties(), make_ruledef(), NameListToQuotedString(), 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(), 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_json_agg_constructor(), 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 4219 of file ruleutils.c.

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

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 4189 of file ruleutils.c.

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

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 4393 of file ruleutils.c.

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

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 5516 of file ruleutils.c.

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

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 4942 of file ruleutils.c.

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

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 4739 of file ruleutils.c.

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

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 4248 of file ruleutils.c.

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

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 4462 of file ruleutils.c.

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

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 4574 of file ruleutils.c.

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

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 12431 of file ruleutils.c.

12432{
12433 const char *valptr;
12434
12435 /*
12436 * We always form the string literal according to standard SQL rules.
12437 */
12439 for (valptr = val; *valptr; valptr++)
12440 {
12441 char ch = *valptr;
12442
12443 if (SQL_STR_DOUBLE(ch, false))
12446 }
12448}

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().