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_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
 

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 charpg_get_statisticsobj_worker (Oid statextid, bool columns_only, bool missing_ok)
 
static charpg_get_partkeydef_worker (Oid relid, int prettyFlags, bool attrsOnly, bool missing_ok)
 
static charpg_get_constraintdef_worker (Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok)
 
static textpg_get_expr_worker (text *expr, Oid relid, int prettyFlags)
 
static int print_function_arguments (StringInfo buf, HeapTuple proctup, bool print_table_args, bool print_defaults)
 
static void print_function_rettype (StringInfo buf, HeapTuple proctup)
 
static void print_function_trftypes (StringInfo buf, HeapTuple proctup)
 
static void print_function_sqlbody (StringInfo buf, HeapTuple proctup)
 
static void set_rtable_names (deparse_namespace *dpns, List *parent_namespaces, Bitmapset *rels_used)
 
static void set_deparse_for_query (deparse_namespace *dpns, Query *query, List *parent_namespaces)
 
static void set_simple_column_names (deparse_namespace *dpns)
 
static bool has_dangerous_join_using (deparse_namespace *dpns, Node *jtnode)
 
static void set_using_names (deparse_namespace *dpns, Node *jtnode, List *parentUsing)
 
static void set_relation_column_names (deparse_namespace *dpns, RangeTblEntry *rte, deparse_columns *colinfo)
 
static void set_join_column_names (deparse_namespace *dpns, RangeTblEntry *rte, deparse_columns *colinfo)
 
static bool colname_is_unique (const char *colname, deparse_namespace *dpns, deparse_columns *colinfo)
 
static charmake_colname_unique (char *colname, deparse_namespace *dpns, deparse_columns *colinfo)
 
static void expand_colnames_array_to (deparse_columns *colinfo, int n)
 
static void build_colinfo_names_hash (deparse_columns *colinfo)
 
static void add_to_names_hash (deparse_columns *colinfo, const char *name)
 
static void destroy_colinfo_names_hash (deparse_columns *colinfo)
 
static void identify_join_columns (JoinExpr *j, RangeTblEntry *jrte, deparse_columns *colinfo)
 
static charget_rtable_name (int rtindex, deparse_context *context)
 
static void set_deparse_plan (deparse_namespace *dpns, Plan *plan)
 
static Planfind_recursive_union (deparse_namespace *dpns, WorkTableScan *wtscan)
 
static void push_child_plan (deparse_namespace *dpns, Plan *plan, deparse_namespace *save_dpns)
 
static void pop_child_plan (deparse_namespace *dpns, deparse_namespace *save_dpns)
 
static void push_ancestor_plan (deparse_namespace *dpns, ListCell *ancestor_cell, deparse_namespace *save_dpns)
 
static void pop_ancestor_plan (deparse_namespace *dpns, deparse_namespace *save_dpns)
 
static void make_ruledef (StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags)
 
static void make_viewdef (StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags, int wrapColumn)
 
static void get_query_def (Query *query, StringInfo buf, List *parentnamespace, TupleDesc resultDesc, bool colNamesVisible, int prettyFlags, int wrapColumn, int startIndent)
 
static void get_values_def (List *values_lists, deparse_context *context)
 
static void get_with_clause (Query *query, deparse_context *context)
 
static void get_select_query_def (Query *query, deparse_context *context)
 
static void get_insert_query_def (Query *query, deparse_context *context)
 
static void get_update_query_def (Query *query, deparse_context *context)
 
static void get_update_query_targetlist_def (Query *query, List *targetList, deparse_context *context, RangeTblEntry *rte)
 
static void get_delete_query_def (Query *query, deparse_context *context)
 
static void get_merge_query_def (Query *query, deparse_context *context)
 
static void get_utility_query_def (Query *query, deparse_context *context)
 
static charget_lock_clause_strength (LockClauseStrength strength)
 
static void get_basic_select_query (Query *query, deparse_context *context)
 
static void get_target_list (List *targetList, deparse_context *context)
 
static void get_returning_clause (Query *query, deparse_context *context)
 
static void get_setop_query (Node *setOp, Query *query, deparse_context *context)
 
static Nodeget_rule_sortgroupclause (Index ref, List *tlist, bool force_colno, deparse_context *context)
 
static void get_rule_groupingset (GroupingSet *gset, List *targetlist, bool omit_parens, deparse_context *context)
 
static void get_rule_orderby (List *orderList, List *targetList, bool force_colno, deparse_context *context)
 
static void get_rule_windowclause (Query *query, deparse_context *context)
 
static void get_rule_windowspec (WindowClause *wc, List *targetList, deparse_context *context)
 
static void get_window_frame_options (int frameOptions, Node *startOffset, Node *endOffset, deparse_context *context)
 
static charget_variable (Var *var, int levelsup, bool istoplevel, deparse_context *context)
 
static void get_special_variable (Node *node, deparse_context *context, void *callback_arg)
 
static void resolve_special_varno (Node *node, deparse_context *context, rsv_callback callback, void *callback_arg)
 
static Nodefind_param_referent (Param *param, deparse_context *context, deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
 
static SubPlanfind_param_generator (Param *param, deparse_context *context, int *column_p)
 
static SubPlanfind_param_generator_initplan (Param *param, Plan *plan, int *column_p)
 
static void get_parameter (Param *param, deparse_context *context)
 
static const charget_simple_binary_op_name (OpExpr *expr)
 
static bool isSimpleNode (Node *node, Node *parentNode, int prettyFlags)
 
static void appendContextKeyword (deparse_context *context, const char *str, int indentBefore, int indentAfter, int indentPlus)
 
static void removeStringInfoSpaces (StringInfo str)
 
static void get_rule_expr (Node *node, deparse_context *context, bool showimplicit)
 
static void get_rule_expr_toplevel (Node *node, deparse_context *context, bool showimplicit)
 
static void get_rule_list_toplevel (List *lst, deparse_context *context, bool showimplicit)
 
static void get_rule_expr_funccall (Node *node, deparse_context *context, bool showimplicit)
 
static bool looks_like_function (Node *node)
 
static void get_oper_expr (OpExpr *expr, deparse_context *context)
 
static void get_func_expr (FuncExpr *expr, deparse_context *context, bool showimplicit)
 
static void get_agg_expr (Aggref *aggref, deparse_context *context, Aggref *original_aggref)
 
static void get_agg_expr_helper (Aggref *aggref, deparse_context *context, Aggref *original_aggref, const char *funcname, const char *options, bool is_json_objectagg)
 
static void get_agg_combine_expr (Node *node, deparse_context *context, void *callback_arg)
 
static void get_windowfunc_expr (WindowFunc *wfunc, deparse_context *context)
 
static void get_windowfunc_expr_helper (WindowFunc *wfunc, deparse_context *context, const char *funcname, const char *options, bool is_json_objectagg)
 
static bool get_func_sql_syntax (FuncExpr *expr, deparse_context *context)
 
static void get_coercion_expr (Node *arg, deparse_context *context, Oid resulttype, int32 resulttypmod, Node *parentNode)
 
static void get_const_expr (Const *constval, deparse_context *context, int showtype)
 
static void get_const_collation (Const *constval, deparse_context *context)
 
static void get_json_format (JsonFormat *format, StringInfo buf)
 
static void get_json_returning (JsonReturning *returning, StringInfo buf, bool json_format_by_default)
 
static void get_json_constructor (JsonConstructorExpr *ctor, deparse_context *context, bool showimplicit)
 
static void get_json_constructor_options (JsonConstructorExpr *ctor, StringInfo buf)
 
static void get_json_agg_constructor (JsonConstructorExpr *ctor, deparse_context *context, const char *funcname, bool is_json_objectagg)
 
static void simple_quote_literal (StringInfo buf, const char *val)
 
static void get_sublink_expr (SubLink *sublink, deparse_context *context)
 
static void get_tablefunc (TableFunc *tf, deparse_context *context, bool showimplicit)
 
static void get_from_clause (Query *query, const char *prefix, deparse_context *context)
 
static void get_from_clause_item (Node *jtnode, Query *query, deparse_context *context)
 
static void get_rte_alias (RangeTblEntry *rte, int varno, bool use_as, deparse_context *context)
 
static void get_column_alias_list (deparse_columns *colinfo, deparse_context *context)
 
static void get_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)
 
static 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, bits16 flags)
 
charpg_get_querydef (Query *query, bool pretty)
 
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 const charget_name_for_var_field (Var *var, int fieldno, int levelsup, deparse_context *context)
 
static void get_rule_expr_paren (Node *node, deparse_context *context, bool showimplicit, Node *parentNode)
 
static void get_json_behavior (JsonBehavior *behavior, deparse_context *context, const char *on)
 
static void get_json_expr_options (JsonExpr *jsexpr, deparse_context *context, JsonBehaviorType default_behavior)
 
static void get_xmltable (TableFunc *tf, deparse_context *context, bool showimplicit)
 
static void get_json_table (TableFunc *tf, deparse_context *context, bool showimplicit)
 
chargenerate_opclass_name (Oid opclass)
 
const charquote_identifier (const char *ident)
 
charquote_qualified_identifier (const char *qualifier, const char *ident)
 
void generate_operator_clause (StringInfo buf, const char *leftop, Oid leftoptype, Oid opoid, const char *rightop, Oid rightoptype)
 
chargenerate_collation_name (Oid collid)
 
charget_range_partbound_string (List *bound_datums)
 

Variables

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

Macro Definition Documentation

◆ deparse_columns_fetch

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

Definition at line 312 of file ruleutils.c.

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

◆ GET_PRETTY_FLAGS

#define GET_PRETTY_FLAGS (   pretty)

◆ only_marker

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

Definition at line 551 of file ruleutils.c.

◆ PRETTY_INDENT

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

Definition at line 102 of file ruleutils.c.

◆ PRETTY_PAREN

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

Definition at line 101 of file ruleutils.c.

◆ PRETTY_SCHEMA

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

Definition at line 103 of file ruleutils.c.

◆ PRETTYFLAG_INDENT

#define PRETTYFLAG_INDENT   0x0002

Definition at line 89 of file ruleutils.c.

◆ PRETTYFLAG_PAREN

#define PRETTYFLAG_PAREN   0x0001

Definition at line 88 of file ruleutils.c.

◆ PRETTYFLAG_SCHEMA

#define PRETTYFLAG_SCHEMA   0x0004

Definition at line 90 of file ruleutils.c.

◆ PRETTYINDENT_JOIN

#define PRETTYINDENT_JOIN   4

Definition at line 82 of file ruleutils.c.

◆ PRETTYINDENT_LIMIT

#define PRETTYINDENT_LIMIT   40 /* wrap limit */

Definition at line 85 of file ruleutils.c.

◆ PRETTYINDENT_STD

#define PRETTYINDENT_STD   8

Definition at line 81 of file ruleutils.c.

◆ PRETTYINDENT_VAR

#define PRETTYINDENT_VAR   4

Definition at line 83 of file ruleutils.c.

◆ WRAP_COLUMN_DEFAULT

#define WRAP_COLUMN_DEFAULT   0

Definition at line 98 of file ruleutils.c.

Typedef Documentation

◆ rsv_callback

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

Definition at line 325 of file ruleutils.c.

Function Documentation

◆ add_cast_to()

static void add_cast_to ( StringInfo  buf,
Oid  typid 
)
static

Definition at line 13531 of file ruleutils.c.

13532{
13535 char *typname;
13536 char *nspname;
13537
13540 elog(ERROR, "cache lookup failed for type %u", typid);
13542
13543 typname = NameStr(typform->typname);
13544 nspname = get_namespace_name_or_temp(typform->typnamespace);
13545
13546 appendStringInfo(buf, "::%s.%s",
13548
13550}

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

5041{
5042 if (colinfo->names_hash)
5043 (void) hash_search(colinfo->names_hash,
5044 name,
5045 HASH_ENTER,
5046 NULL);
5047}

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

9116{
9117 StringInfo buf = context->buf;
9118
9119 if (PRETTY_INDENT(context))
9120 {
9121 int indentAmount;
9122
9123 context->indentLevel += indentBefore;
9124
9125 /* remove any trailing spaces currently in the buffer ... */
9127 /* ... then add a newline and some spaces */
9129
9130 if (context->indentLevel < PRETTYINDENT_LIMIT)
9131 indentAmount = Max(context->indentLevel, 0) + indentPlus;
9132 else
9133 {
9134 /*
9135 * If we're indented more than PRETTYINDENT_LIMIT characters, try
9136 * to conserve horizontal space by reducing the per-level
9137 * indentation. For best results the scale factor here should
9138 * divide all the indent amounts that get added to indentLevel
9139 * (PRETTYINDENT_STD, etc). It's important that the indentation
9140 * not grow unboundedly, else deeply-nested trees use O(N^2)
9141 * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
9142 */
9144 (context->indentLevel - PRETTYINDENT_LIMIT) /
9145 (PRETTYINDENT_STD / 2);
9147 /* scale/wrap logic affects indentLevel, but not indentPlus */
9149 }
9151
9153
9154 context->indentLevel += indentAfter;
9155 if (context->indentLevel < 0)
9156 context->indentLevel = 0;
9157 }
9158 else
9160}

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

4983{
4985 int i;
4986 ListCell *lc;
4987
4988 /*
4989 * Use a hash table only for RTEs with at least 32 columns. (The cutoff
4990 * is somewhat arbitrary, but let's choose it so that this code does get
4991 * exercised in the regression tests.)
4992 */
4993 if (colinfo->num_cols < 32)
4994 return;
4995
4996 /*
4997 * Set up the hash table. The entries are just strings with no other
4998 * payload.
4999 */
5000 hash_ctl.keysize = NAMEDATALEN;
5001 hash_ctl.entrysize = NAMEDATALEN;
5003 colinfo->names_hash = hash_create("deparse_columns names",
5004 colinfo->num_cols + colinfo->num_new_cols,
5005 &hash_ctl,
5007
5008 /*
5009 * Preload the hash table with any names already present (these would have
5010 * come from set_using_names).
5011 */
5012 for (i = 0; i < colinfo->num_cols; i++)
5013 {
5014 char *oldname = colinfo->colnames[i];
5015
5016 if (oldname)
5018 }
5019
5020 for (i = 0; i < colinfo->num_new_cols; i++)
5021 {
5022 char *oldname = colinfo->new_colnames[i];
5023
5024 if (oldname)
5026 }
5027
5028 foreach(lc, colinfo->parentUsing)
5029 {
5030 char *oldname = (char *) lfirst(lc);
5031
5033 }
5034}

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

4854{
4855 int i;
4856 ListCell *lc;
4857
4858 /*
4859 * If we have a hash table, consult that instead of linearly scanning the
4860 * colinfo's strings.
4861 */
4862 if (colinfo->names_hash)
4863 {
4864 if (hash_search(colinfo->names_hash,
4865 colname,
4866 HASH_FIND,
4867 NULL) != NULL)
4868 return false;
4869 }
4870 else
4871 {
4872 /* Check against already-assigned column aliases within RTE */
4873 for (i = 0; i < colinfo->num_cols; i++)
4874 {
4875 char *oldname = colinfo->colnames[i];
4876
4877 if (oldname && strcmp(oldname, colname) == 0)
4878 return false;
4879 }
4880
4881 /*
4882 * If we're building a new_colnames array, check that too (this will
4883 * be partially but not completely redundant with the previous checks)
4884 */
4885 for (i = 0; i < colinfo->num_new_cols; i++)
4886 {
4887 char *oldname = colinfo->new_colnames[i];
4888
4889 if (oldname && strcmp(oldname, colname) == 0)
4890 return false;
4891 }
4892
4893 /*
4894 * Also check against names already assigned for parent-join USING
4895 * cols
4896 */
4897 foreach(lc, colinfo->parentUsing)
4898 {
4899 char *oldname = (char *) lfirst(lc);
4900
4901 if (strcmp(oldname, colname) == 0)
4902 return false;
4903 }
4904 }
4905
4906 /*
4907 * Also check against USING-column names that must be globally unique.
4908 * These are not hashed, but there should be few of them.
4909 */
4910 foreach(lc, dpns->using_names)
4911 {
4912 char *oldname = (char *) lfirst(lc);
4913
4914 if (strcmp(oldname, colname) == 0)
4915 return false;
4916 }
4917
4918 return true;
4919}

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

2623{
2624 Datum *keys;
2625 int nKeys;
2626 int j;
2627
2628 /* Extract data from array of int16 */
2630 &keys, NULL, &nKeys);
2631
2632 for (j = 0; j < nKeys; j++)
2633 {
2634 char *colName;
2635
2636 colName = get_attname(relId, DatumGetInt16(keys[j]), false);
2637
2638 if (j == 0)
2640 else
2641 appendStringInfo(buf, ", %s%s",
2642 (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
2644 }
2645
2646 return nKeys;
2647}

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

Referenced by pg_get_constraintdef_worker().

◆ deparse_context_for()

List * deparse_context_for ( const char aliasname,
Oid  relid 
)

Definition at line 3712 of file ruleutils.c.

3713{
3716
3718
3719 /* Build a minimal RTE for the rel */
3721 rte->rtekind = RTE_RELATION;
3722 rte->relid = relid;
3723 rte->relkind = RELKIND_RELATION; /* no need for exactness here */
3724 rte->rellockmode = AccessShareLock;
3725 rte->alias = makeAlias(aliasname, NIL);
3726 rte->eref = rte->alias;
3727 rte->lateral = false;
3728 rte->inh = false;
3729 rte->inFromCl = true;
3730
3731 /* Build one-element rtable */
3732 dpns->rtable = list_make1(rte);
3733 dpns->subplans = NIL;
3734 dpns->ctes = NIL;
3735 dpns->appendrels = NULL;
3738
3739 /* Return a one-deep namespace stack */
3740 return list_make1(dpns);
3741}

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

Referenced by 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 3757 of file ruleutils.c.

3758{
3760
3762
3763 /* Initialize fields that stay the same across the whole plan tree */
3764 dpns->rtable = pstmt->rtable;
3765 dpns->rtable_names = rtable_names;
3766 dpns->subplans = pstmt->subplans;
3767 dpns->ctes = NIL;
3768 if (pstmt->appendRelations)
3769 {
3770 /* Set up the array, indexed by child relid */
3771 int ntables = list_length(dpns->rtable);
3772 ListCell *lc;
3773
3774 dpns->appendrels = (AppendRelInfo **)
3775 palloc0((ntables + 1) * sizeof(AppendRelInfo *));
3776 foreach(lc, pstmt->appendRelations)
3777 {
3779 Index crelid = appinfo->child_relid;
3780
3781 Assert(crelid > 0 && crelid <= ntables);
3782 Assert(dpns->appendrels[crelid] == NULL);
3783 dpns->appendrels[crelid] = appinfo;
3784 }
3785 }
3786 else
3787 dpns->appendrels = NULL; /* don't need it */
3788
3789 /*
3790 * Set up column name aliases, ignoring any join RTEs; they don't matter
3791 * because plan trees don't contain any join alias Vars.
3792 */
3794
3795 /* Return a one-deep namespace stack */
3796 return list_make1(dpns);
3797}

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

5054{
5055 if (colinfo->names_hash)
5056 {
5057 hash_destroy(colinfo->names_hash);
5058 colinfo->names_hash = NULL;
5059 }
5060}

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

4967{
4968 if (n > colinfo->num_cols)
4969 {
4970 if (colinfo->colnames == NULL)
4971 colinfo->colnames = palloc0_array(char *, n);
4972 else
4973 colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
4974 colinfo->num_cols = n;
4975 }
4976}

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

8594{
8595 /* Initialize output parameter to prevent compiler warnings */
8596 *column_p = 0;
8597
8598 /*
8599 * If it's a PARAM_EXEC parameter, search the current plan node as well as
8600 * ancestor nodes looking for a subplan or initplan that emits the value
8601 * for the Param. It could appear in the setParams of an initplan or
8602 * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
8603 */
8604 if (param->paramkind == PARAM_EXEC)
8605 {
8606 SubPlan *result;
8608 ListCell *lc;
8609
8610 dpns = (deparse_namespace *) linitial(context->namespaces);
8611
8612 /* First check the innermost plan node's initplans */
8613 result = find_param_generator_initplan(param, dpns->plan, column_p);
8614 if (result)
8615 return result;
8616
8617 /*
8618 * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
8619 * which can be referenced by Params elsewhere in the targetlist.
8620 * (Such Params should always be in the same targetlist, so there's no
8621 * need to do this work at upper plan nodes.)
8622 */
8623 foreach_node(TargetEntry, tle, dpns->plan->targetlist)
8624 {
8625 if (tle->expr && IsA(tle->expr, SubPlan))
8626 {
8627 SubPlan *subplan = (SubPlan *) tle->expr;
8628
8629 if (subplan->subLinkType == MULTIEXPR_SUBLINK)
8630 {
8631 foreach_int(paramid, subplan->setParam)
8632 {
8633 if (paramid == param->paramid)
8634 {
8635 /* Found a match, so return it. */
8636 *column_p = foreach_current_index(paramid);
8637 return subplan;
8638 }
8639 }
8640 }
8641 }
8642 }
8643
8644 /* No luck, so check the ancestor nodes */
8645 foreach(lc, dpns->ancestors)
8646 {
8647 Node *ancestor = (Node *) lfirst(lc);
8648
8649 /*
8650 * If ancestor is a SubPlan, check the paramIds it provides.
8651 */
8652 if (IsA(ancestor, SubPlan))
8653 {
8654 SubPlan *subplan = (SubPlan *) ancestor;
8655
8656 foreach_int(paramid, subplan->paramIds)
8657 {
8658 if (paramid == param->paramid)
8659 {
8660 /* Found a match, so return it. */
8661 *column_p = foreach_current_index(paramid);
8662 return subplan;
8663 }
8664 }
8665
8666 /* SubPlan isn't a kind of Plan, so skip the rest */
8667 continue;
8668 }
8669
8670 /*
8671 * Otherwise, it's some kind of Plan node, so check its initplans.
8672 */
8673 result = find_param_generator_initplan(param, (Plan *) ancestor,
8674 column_p);
8675 if (result)
8676 return result;
8677
8678 /* No luck, crawl up to next ancestor */
8679 }
8680 }
8681
8682 /* No generator found */
8683 return NULL;
8684}

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

8691{
8692 foreach_node(SubPlan, subplan, plan->initPlan)
8693 {
8694 foreach_int(paramid, subplan->setParam)
8695 {
8696 if (paramid == param->paramid)
8697 {
8698 /* Found a match, so return it. */
8699 *column_p = foreach_current_index(paramid);
8700 return subplan;
8701 }
8702 }
8703 }
8704 return NULL;
8705}

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

8481{
8482 /* Initialize output parameters to prevent compiler warnings */
8483 *dpns_p = NULL;
8485
8486 /*
8487 * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
8488 * SubPlan argument. This will necessarily be in some ancestor of the
8489 * current expression's Plan node.
8490 */
8491 if (param->paramkind == PARAM_EXEC)
8492 {
8495 ListCell *lc;
8496
8497 dpns = (deparse_namespace *) linitial(context->namespaces);
8498 child_plan = dpns->plan;
8499
8500 foreach(lc, dpns->ancestors)
8501 {
8502 Node *ancestor = (Node *) lfirst(lc);
8503 ListCell *lc2;
8504
8505 /*
8506 * NestLoops transmit params to their inner child only.
8507 */
8508 if (IsA(ancestor, NestLoop) &&
8510 {
8512
8513 foreach(lc2, nl->nestParams)
8514 {
8516
8517 if (nlp->paramno == param->paramid)
8518 {
8519 /* Found a match, so return it */
8520 *dpns_p = dpns;
8522 return (Node *) nlp->paramval;
8523 }
8524 }
8525 }
8526
8527 /*
8528 * If ancestor is a SubPlan, check the arguments it provides.
8529 */
8530 if (IsA(ancestor, SubPlan))
8531 {
8532 SubPlan *subplan = (SubPlan *) ancestor;
8533 ListCell *lc3;
8534 ListCell *lc4;
8535
8536 forboth(lc3, subplan->parParam, lc4, subplan->args)
8537 {
8538 int paramid = lfirst_int(lc3);
8539 Node *arg = (Node *) lfirst(lc4);
8540
8541 if (paramid == param->paramid)
8542 {
8543 /*
8544 * Found a match, so return it. But, since Vars in
8545 * the arg are to be evaluated in the surrounding
8546 * context, we have to point to the next ancestor item
8547 * that is *not* a SubPlan.
8548 */
8549 ListCell *rest;
8550
8551 for_each_cell(rest, dpns->ancestors,
8552 lnext(dpns->ancestors, lc))
8553 {
8554 Node *ancestor2 = (Node *) lfirst(rest);
8555
8556 if (!IsA(ancestor2, SubPlan))
8557 {
8558 *dpns_p = dpns;
8560 return arg;
8561 }
8562 }
8563 elog(ERROR, "SubPlan cannot be outermost ancestor");
8564 }
8565 }
8566
8567 /* SubPlan isn't a kind of Plan, so skip the rest */
8568 continue;
8569 }
8570
8571 /*
8572 * We need not consider the ancestor's initPlan list, since
8573 * initplans never have any parParams.
8574 */
8575
8576 /* No luck, crawl up to next ancestor */
8577 child_plan = (Plan *) ancestor;
8578 }
8579 }
8580
8581 /* No referent found */
8582 return NULL;
8583}

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

5238{
5239 ListCell *lc;
5240
5241 foreach(lc, dpns->ancestors)
5242 {
5243 Plan *ancestor = (Plan *) lfirst(lc);
5244
5245 if (IsA(ancestor, RecursiveUnion) &&
5246 ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
5247 return ancestor;
5248 }
5249 elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
5250 wtscan->wtParam);
5251 return NULL;
5252}

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

Referenced by set_deparse_plan().

◆ flatten_reloptions()

static char * flatten_reloptions ( Oid  relid)
static

Definition at line 13695 of file ruleutils.c.

13696{
13697 char *result = NULL;
13698 HeapTuple tuple;
13699 Datum reloptions;
13700 bool isnull;
13701
13702 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13703 if (!HeapTupleIsValid(tuple))
13704 elog(ERROR, "cache lookup failed for relation %u", relid);
13705
13706 reloptions = SysCacheGetAttr(RELOID, tuple,
13707 Anum_pg_class_reloptions, &isnull);
13708 if (!isnull)
13709 {
13711
13713 get_reloptions(&buf, reloptions);
13714
13715 result = buf.data;
13716 }
13717
13718 ReleaseSysCache(tuple);
13719
13720 return result;
13721}

References buf, elog, ERROR, fb(), get_reloptions(), HeapTupleIsValid, initStringInfo(), ObjectIdGetDatum(), ReleaseSysCache(), 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 13595 of file ruleutils.c.

13596{
13597 HeapTuple tp;
13599 char *collname;
13600 char *nspname;
13601 char *result;
13602
13604 if (!HeapTupleIsValid(tp))
13605 elog(ERROR, "cache lookup failed for collation %u", collid);
13607 collname = NameStr(colltup->collname);
13608
13610 nspname = get_namespace_name_or_temp(colltup->collnamespace);
13611 else
13612 nspname = NULL;
13613
13614 result = quote_qualified_identifier(nspname, collname);
13615
13616 ReleaseSysCache(tp);
13617
13618 return result;
13619}

References CollationIsVisible(), collid, elog, ERROR, fb(), Form_pg_collation, get_namespace_name_or_temp(), GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), quote_qualified_identifier(), ReleaseSysCache(), 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 13307 of file ruleutils.c.

13310{
13311 char *result;
13314 char *proname;
13315 bool use_variadic;
13316 char *nspname;
13318 int fgc_flags;
13319 Oid p_funcid;
13320 Oid p_rettype;
13321 bool p_retset;
13322 int p_nvargs;
13323 Oid p_vatype;
13325 bool force_qualify = false;
13326
13329 elog(ERROR, "cache lookup failed for function %u", funcid);
13331 proname = NameStr(procform->proname);
13332
13333 /*
13334 * Due to parser hacks to avoid needing to reserve CUBE, we need to force
13335 * qualification of some function names within GROUP BY.
13336 */
13337 if (inGroupBy)
13338 {
13339 if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
13340 force_qualify = true;
13341 }
13342
13343 /*
13344 * Determine whether VARIADIC should be printed. We must do this first
13345 * since it affects the lookup rules in func_get_detail().
13346 *
13347 * We always print VARIADIC if the function has a merged variadic-array
13348 * argument. Note that this is always the case for functions taking a
13349 * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC
13350 * and printed the array elements as separate arguments, the call could
13351 * match a newer non-VARIADIC function.
13352 */
13353 if (use_variadic_p)
13354 {
13355 /* Parser should not have set funcvariadic unless fn is variadic */
13356 Assert(!has_variadic || OidIsValid(procform->provariadic));
13359 }
13360 else
13361 {
13363 use_variadic = false;
13364 }
13365
13366 /*
13367 * The idea here is to schema-qualify only if the parser would fail to
13368 * resolve the correct function given the unqualified func name with the
13369 * specified argtypes and VARIADIC flag. But if we already decided to
13370 * force qualification, then we can skip the lookup and pretend we didn't
13371 * find it.
13372 */
13373 if (!force_qualify)
13375 NIL, argnames, nargs, argtypes,
13376 !use_variadic, true, false,
13377 &fgc_flags,
13381 else
13382 {
13385 }
13386
13387 if ((p_result == FUNCDETAIL_NORMAL ||
13390 p_funcid == funcid)
13391 nspname = NULL;
13392 else
13393 nspname = get_namespace_name_or_temp(procform->pronamespace);
13394
13395 result = quote_qualified_identifier(nspname, proname);
13396
13398
13399 return result;
13400}

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(), 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 12949 of file ruleutils.c.

12950{
12952
12954 get_opclass_name(opclass, InvalidOid, &buf);
12955
12956 return &buf.data[1]; /* get_opclass_name() prepends space */
12957}

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

13495{
13498 char *oprname;
13499 char *nspname;
13500
13503 elog(ERROR, "cache lookup failed for operator %u", opoid);
13505 Assert(operform->oprkind == 'b');
13506 oprname = NameStr(operform->oprname);
13507
13508 nspname = get_namespace_name(operform->oprnamespace);
13509
13511 if (leftoptype != operform->oprleft)
13512 add_cast_to(buf, operform->oprleft);
13513 appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
13514 appendStringInfoString(buf, oprname);
13515 appendStringInfo(buf, ") %s", rightop);
13516 if (rightoptype != operform->oprright)
13517 add_cast_to(buf, operform->oprright);
13518
13520}

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

13415{
13419 char *oprname;
13420 char *nspname;
13422
13424
13427 elog(ERROR, "cache lookup failed for operator %u", operid);
13429 oprname = NameStr(operform->oprname);
13430
13431 /*
13432 * The idea here is to schema-qualify only if the parser would fail to
13433 * resolve the correct operator given the unqualified op name with the
13434 * specified argtypes.
13435 */
13436 switch (operform->oprkind)
13437 {
13438 case 'b':
13440 true, -1);
13441 break;
13442 case 'l':
13444 true, -1);
13445 break;
13446 default:
13447 elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
13448 p_result = NULL; /* keep compiler quiet */
13449 break;
13450 }
13451
13452 if (p_result != NULL && oprid(p_result) == operid)
13453 nspname = NULL;
13454 else
13455 {
13456 nspname = get_namespace_name_or_temp(operform->oprnamespace);
13457 appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
13458 }
13459
13460 appendStringInfoString(&buf, oprname);
13461
13462 if (nspname)
13464
13465 if (p_result != NULL)
13467
13469
13470 return buf.data;
13471}

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

13264{
13265 HeapTuple tp;
13267 char *relname;
13268 char *nspname;
13269 char *result;
13270
13272 if (!HeapTupleIsValid(tp))
13273 elog(ERROR, "cache lookup failed for relation %u", relid);
13275 relname = NameStr(reltup->relname);
13276
13277 nspname = get_namespace_name_or_temp(reltup->relnamespace);
13278 if (!nspname)
13279 elog(ERROR, "cache lookup failed for namespace %u",
13280 reltup->relnamespace);
13281
13282 result = quote_qualified_identifier(nspname, relname);
13283
13284 ReleaseSysCache(tp);
13285
13286 return result;
13287}

References elog, ERROR, fb(), get_namespace_name_or_temp(), GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), quote_qualified_identifier(), ReleaseSysCache(), relname, 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 13562 of file ruleutils.c.

13563{
13564 HeapTuple tp;
13566 char *typname;
13567 char *nspname;
13568 char *result;
13569
13571 if (!HeapTupleIsValid(tp))
13572 elog(ERROR, "cache lookup failed for type %u", typid);
13574 typname = NameStr(typtup->typname);
13575
13576 nspname = get_namespace_name_or_temp(typtup->typnamespace);
13577 if (!nspname)
13578 elog(ERROR, "cache lookup failed for namespace %u",
13579 typtup->typnamespace);
13580
13581 result = quote_qualified_identifier(nspname, typname);
13582
13583 ReleaseSysCache(tp);
13584
13585 return result;
13586}

References elog, ERROR, fb(), Form_pg_type, get_namespace_name_or_temp(), GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), quote_qualified_identifier(), ReleaseSysCache(), 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 13203 of file ruleutils.c.

13204{
13205 HeapTuple tp;
13207 bool need_qual;
13209 char *relname;
13210 char *nspname;
13211 char *result;
13212
13214 if (!HeapTupleIsValid(tp))
13215 elog(ERROR, "cache lookup failed for relation %u", relid);
13217 relname = NameStr(reltup->relname);
13218
13219 /* Check for conflicting CTE name */
13220 need_qual = false;
13221 foreach(nslist, namespaces)
13222 {
13225
13226 foreach(ctlist, dpns->ctes)
13227 {
13229
13230 if (strcmp(cte->ctename, relname) == 0)
13231 {
13232 need_qual = true;
13233 break;
13234 }
13235 }
13236 if (need_qual)
13237 break;
13238 }
13239
13240 /* Otherwise, qualify the name if not visible in search path */
13241 if (!need_qual)
13242 need_qual = !RelationIsVisible(relid);
13243
13244 if (need_qual)
13245 nspname = get_namespace_name_or_temp(reltup->relnamespace);
13246 else
13247 nspname = NULL;
13248
13249 result = quote_qualified_identifier(nspname, relname);
13250
13251 ReleaseSysCache(tp);
13252
13253 return result;
13254}

References CommonTableExpr::ctename, elog, ERROR, fb(), get_namespace_name_or_temp(), GETSTRUCT(), HeapTupleIsValid, lfirst, NameStr, ObjectIdGetDatum(), quote_qualified_identifier(), RelationIsVisible(), ReleaseSysCache(), relname, 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_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 11056 of file ruleutils.c.

11057{
11058 Aggref *aggref;
11059 Aggref *original_aggref = callback_arg;
11060
11061 if (!IsA(node, Aggref))
11062 elog(ERROR, "combining Aggref does not point to an Aggref");
11063
11064 aggref = (Aggref *) node;
11065 get_agg_expr(aggref, context, original_aggref);
11066}

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

10920{
10921 get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
10922 false);
10923}

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

10933{
10934 StringInfo buf = context->buf;
10935 Oid argtypes[FUNC_MAX_ARGS];
10936 int nargs;
10937 bool use_variadic = false;
10938
10939 /*
10940 * For a combining aggregate, we look up and deparse the corresponding
10941 * partial aggregate instead. This is necessary because our input
10942 * argument list has been replaced; the new argument list always has just
10943 * one element, which will point to a partial Aggref that supplies us with
10944 * transition states to combine.
10945 */
10946 if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
10947 {
10949
10950 Assert(list_length(aggref->args) == 1);
10951 tle = linitial_node(TargetEntry, aggref->args);
10952 resolve_special_varno((Node *) tle->expr, context,
10954 return;
10955 }
10956
10957 /*
10958 * Mark as PARTIAL, if appropriate. We look to the original aggref so as
10959 * to avoid printing this when recursing from the code just above.
10960 */
10962 appendStringInfoString(buf, "PARTIAL ");
10963
10964 /* Extract the argument types as seen by the parser */
10965 nargs = get_aggregate_argtypes(aggref, argtypes);
10966
10967 if (!funcname)
10968 funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
10969 argtypes, aggref->aggvariadic,
10970 &use_variadic,
10971 context->inGroupBy);
10972
10973 /* Print the aggregate name, schema-qualified if needed */
10974 appendStringInfo(buf, "%s(%s", funcname,
10975 (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
10976
10977 if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
10978 {
10979 /*
10980 * Ordered-set aggregates do not use "*" syntax. Also, we needn't
10981 * worry about inserting VARIADIC. So we can just dump the direct
10982 * args as-is.
10983 */
10984 Assert(!aggref->aggvariadic);
10985 get_rule_expr((Node *) aggref->aggdirectargs, context, true);
10986 Assert(aggref->aggorder != NIL);
10987 appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
10988 get_rule_orderby(aggref->aggorder, aggref->args, false, context);
10989 }
10990 else
10991 {
10992 /* aggstar can be set only in zero-argument aggregates */
10993 if (aggref->aggstar)
10995 else
10996 {
10997 ListCell *l;
10998 int i;
10999
11000 i = 0;
11001 foreach(l, aggref->args)
11002 {
11004 Node *arg = (Node *) tle->expr;
11005
11007 if (tle->resjunk)
11008 continue;
11009 if (i++ > 0)
11010 {
11012 {
11013 /*
11014 * the ABSENT ON NULL and WITH UNIQUE args are printed
11015 * separately, so ignore them here
11016 */
11017 if (i > 2)
11018 break;
11019
11021 }
11022 else
11024 }
11025 if (use_variadic && i == nargs)
11026 appendStringInfoString(buf, "VARIADIC ");
11027 get_rule_expr(arg, context, true);
11028 }
11029 }
11030
11031 if (aggref->aggorder != NIL)
11032 {
11033 appendStringInfoString(buf, " ORDER BY ");
11034 get_rule_orderby(aggref->aggorder, aggref->args, false, context);
11035 }
11036 }
11037
11038 if (options)
11040
11041 if (aggref->aggfilter != NULL)
11042 {
11043 appendStringInfoString(buf, ") FILTER (WHERE ");
11044 get_rule_expr((Node *) aggref->aggfilter, context, false);
11045 }
11046
11048}

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

6114{
6115 StringInfo buf = context->buf;
6117 char *sep;
6118 ListCell *l;
6119
6120 if (PRETTY_INDENT(context))
6121 {
6122 context->indentLevel += PRETTYINDENT_STD;
6124 }
6125
6126 /*
6127 * If the query looks like SELECT * FROM (VALUES ...), then print just the
6128 * VALUES part. This reverses what transformValuesClause() did at parse
6129 * time.
6130 */
6131 values_rte = get_simple_values_rte(query, context->resultDesc);
6132 if (values_rte)
6133 {
6134 get_values_def(values_rte->values_lists, context);
6135 return;
6136 }
6137
6138 /*
6139 * Build up the query string - first we say SELECT
6140 */
6141 if (query->isReturn)
6142 appendStringInfoString(buf, "RETURN");
6143 else
6144 appendStringInfoString(buf, "SELECT");
6145
6146 /* Add the DISTINCT clause if given */
6147 if (query->distinctClause != NIL)
6148 {
6149 if (query->hasDistinctOn)
6150 {
6151 appendStringInfoString(buf, " DISTINCT ON (");
6152 sep = "";
6153 foreach(l, query->distinctClause)
6154 {
6156
6158 get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
6159 false, context);
6160 sep = ", ";
6161 }
6163 }
6164 else
6165 appendStringInfoString(buf, " DISTINCT");
6166 }
6167
6168 /* Then we tell what to select (the targetlist) */
6169 get_target_list(query->targetList, context);
6170
6171 /* Add the FROM clause if needed */
6172 get_from_clause(query, " FROM ", context);
6173
6174 /* Add the WHERE clause if given */
6175 if (query->jointree->quals != NULL)
6176 {
6177 appendContextKeyword(context, " WHERE ",
6179 get_rule_expr(query->jointree->quals, context, false);
6180 }
6181
6182 /* Add the GROUP BY clause if given */
6183 if (query->groupClause != NULL || query->groupingSets != NULL)
6184 {
6185 bool save_ingroupby;
6186
6187 appendContextKeyword(context, " GROUP BY ",
6189 if (query->groupDistinct)
6190 appendStringInfoString(buf, "DISTINCT ");
6191
6192 save_ingroupby = context->inGroupBy;
6193 context->inGroupBy = true;
6194
6195 if (query->groupByAll)
6197 else if (query->groupingSets == NIL)
6198 {
6199 sep = "";
6200 foreach(l, query->groupClause)
6201 {
6203
6205 get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
6206 false, context);
6207 sep = ", ";
6208 }
6209 }
6210 else
6211 {
6212 sep = "";
6213 foreach(l, query->groupingSets)
6214 {
6215 GroupingSet *grp = lfirst(l);
6216
6218 get_rule_groupingset(grp, query->targetList, true, context);
6219 sep = ", ";
6220 }
6221 }
6222
6223 context->inGroupBy = save_ingroupby;
6224 }
6225
6226 /* Add the HAVING clause if given */
6227 if (query->havingQual != NULL)
6228 {
6229 appendContextKeyword(context, " HAVING ",
6231 get_rule_expr(query->havingQual, context, false);
6232 }
6233
6234 /* Add the WINDOW clause if needed */
6235 if (query->windowClause != NIL)
6236 get_rule_windowclause(query, context);
6237}

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

Referenced by get_select_query_def().

◆ get_coercion_expr()

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

Definition at line 11453 of file ruleutils.c.

11456{
11457 StringInfo buf = context->buf;
11458
11459 /*
11460 * Since parse_coerce.c doesn't immediately collapse application of
11461 * length-coercion functions to constants, what we'll typically see in
11462 * such cases is a Const with typmod -1 and a length-coercion function
11463 * right above it. Avoid generating redundant output. However, beware of
11464 * suppressing casts when the user actually wrote something like
11465 * 'foo'::text::char(3).
11466 *
11467 * Note: it might seem that we are missing the possibility of needing to
11468 * print a COLLATE clause for such a Const. However, a Const could only
11469 * have nondefault collation in a post-constant-folding tree, in which the
11470 * length coercion would have been folded too. See also the special
11471 * handling of CollateExpr in coerce_to_target_type(): any collation
11472 * marking will be above the coercion node, not below it.
11473 */
11474 if (arg && IsA(arg, Const) &&
11475 ((Const *) arg)->consttype == resulttype &&
11476 ((Const *) arg)->consttypmod == -1)
11477 {
11478 /* Show the constant without normal ::typename decoration */
11479 get_const_expr((Const *) arg, context, -1);
11480 }
11481 else
11482 {
11483 if (!PRETTY_PAREN(context))
11485 get_rule_expr_paren(arg, context, false, parentNode);
11486 if (!PRETTY_PAREN(context))
11488 }
11489
11490 /*
11491 * Never emit resulttype(arg) functional notation. A pg_proc entry could
11492 * take precedence, and a resulttype in pg_temp would require schema
11493 * qualification that format_type_with_typemod() would usually omit. We've
11494 * standardized on arg::resulttype, but CAST(arg AS resulttype) notation
11495 * would work fine.
11496 */
11497 appendStringInfo(buf, "::%s",
11499}

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

12777{
12778 StringInfo buf = context->buf;
12779 int i;
12780 bool first = true;
12781
12782 /* Don't print aliases if not needed */
12783 if (!colinfo->printaliases)
12784 return;
12785
12786 for (i = 0; i < colinfo->num_new_cols; i++)
12787 {
12788 char *colname = colinfo->new_colnames[i];
12789
12790 if (first)
12791 {
12793 first = false;
12794 }
12795 else
12798 }
12799 if (!first)
12801}

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

11648{
11649 StringInfo buf = context->buf;
11650
11651 if (OidIsValid(constval->constcollid))
11652 {
11653 Oid typcollation = get_typcollation(constval->consttype);
11654
11655 if (constval->constcollid != typcollation)
11656 {
11657 appendStringInfo(buf, " COLLATE %s",
11658 generate_collation_name(constval->constcollid));
11659 }
11660 }
11661}

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

11518{
11519 StringInfo buf = context->buf;
11520 Oid typoutput;
11521 bool typIsVarlena;
11522 char *extval;
11523 bool needlabel = false;
11524
11525 if (constval->constisnull)
11526 {
11527 /*
11528 * Always label the type of a NULL constant to prevent misdecisions
11529 * about type when reparsing.
11530 */
11531 appendStringInfoString(buf, "NULL");
11532 if (showtype >= 0)
11533 {
11534 appendStringInfo(buf, "::%s",
11536 constval->consttypmod));
11537 get_const_collation(constval, context);
11538 }
11539 return;
11540 }
11541
11542 getTypeOutputInfo(constval->consttype,
11543 &typoutput, &typIsVarlena);
11544
11545 extval = OidOutputFunctionCall(typoutput, constval->constvalue);
11546
11547 switch (constval->consttype)
11548 {
11549 case INT4OID:
11550
11551 /*
11552 * INT4 can be printed without any decoration, unless it is
11553 * negative; in that case print it as '-nnn'::integer to ensure
11554 * that the output will re-parse as a constant, not as a constant
11555 * plus operator. In most cases we could get away with printing
11556 * (-nnn) instead, because of the way that gram.y handles negative
11557 * literals; but that doesn't work for INT_MIN, and it doesn't
11558 * seem that much prettier anyway.
11559 */
11560 if (extval[0] != '-')
11562 else
11563 {
11564 appendStringInfo(buf, "'%s'", extval);
11565 needlabel = true; /* we must attach a cast */
11566 }
11567 break;
11568
11569 case NUMERICOID:
11570
11571 /*
11572 * NUMERIC can be printed without quotes if it looks like a float
11573 * constant (not an integer, and not Infinity or NaN) and doesn't
11574 * have a leading sign (for the same reason as for INT4).
11575 */
11576 if (isdigit((unsigned char) extval[0]) &&
11577 strcspn(extval, "eE.") != strlen(extval))
11578 {
11580 }
11581 else
11582 {
11583 appendStringInfo(buf, "'%s'", extval);
11584 needlabel = true; /* we must attach a cast */
11585 }
11586 break;
11587
11588 case BOOLOID:
11589 if (strcmp(extval, "t") == 0)
11590 appendStringInfoString(buf, "true");
11591 else
11592 appendStringInfoString(buf, "false");
11593 break;
11594
11595 default:
11597 break;
11598 }
11599
11600 pfree(extval);
11601
11602 if (showtype < 0)
11603 return;
11604
11605 /*
11606 * For showtype == 0, append ::typename unless the constant will be
11607 * implicitly typed as the right type when it is read in.
11608 *
11609 * XXX this code has to be kept in sync with the behavior of the parser,
11610 * especially make_const.
11611 */
11612 switch (constval->consttype)
11613 {
11614 case BOOLOID:
11615 case UNKNOWNOID:
11616 /* These types can be left unlabeled */
11617 needlabel = false;
11618 break;
11619 case INT4OID:
11620 /* We determined above whether a label is needed */
11621 break;
11622 case NUMERICOID:
11623
11624 /*
11625 * Float-looking constants will be typed as numeric, which we
11626 * checked above; but if there's a nondefault typmod we need to
11627 * show it.
11628 */
11629 needlabel |= (constval->consttypmod >= 0);
11630 break;
11631 default:
11632 needlabel = true;
11633 break;
11634 }
11635 if (needlabel || showtype > 0)
11636 appendStringInfo(buf, "::%s",
11638 constval->consttypmod));
11639
11640 get_const_collation(constval, context);
11641}

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

7380{
7381 StringInfo buf = context->buf;
7383
7384 /* Insert the WITH clause if given */
7385 get_with_clause(query, context);
7386
7387 /*
7388 * Start the query with DELETE FROM relname
7389 */
7390 rte = rt_fetch(query->resultRelation, query->rtable);
7391 Assert(rte->rtekind == RTE_RELATION);
7392 if (PRETTY_INDENT(context))
7393 {
7395 context->indentLevel += PRETTYINDENT_STD;
7396 }
7397 appendStringInfo(buf, "DELETE FROM %s%s",
7399 generate_relation_name(rte->relid, NIL));
7400
7401 /* Print the relation alias, if needed */
7402 get_rte_alias(rte, query->resultRelation, false, context);
7403
7404 /* Add the USING clause if given */
7405 get_from_clause(query, " USING ", context);
7406
7407 /* Add a WHERE clause if given */
7408 if (query->jointree->quals != NULL)
7409 {
7410 appendContextKeyword(context, " WHERE ",
7412 get_rule_expr(query->jointree->quals, context, false);
7413 }
7414
7415 /* Add RETURNING if present */
7416 if (query->returningList)
7417 get_returning_clause(query, context);
7418}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), Assert, deparse_context::buf, buf, fb(), generate_relation_name(), 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_from_clause()

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

Definition at line 12320 of file ruleutils.c.

12321{
12322 StringInfo buf = context->buf;
12323 bool first = true;
12324 ListCell *l;
12325
12326 /*
12327 * We use the query's jointree as a guide to what to print. However, we
12328 * must ignore auto-added RTEs that are marked not inFromCl. (These can
12329 * only appear at the top level of the jointree, so it's sufficient to
12330 * check here.) This check also ensures we ignore the rule pseudo-RTEs
12331 * for NEW and OLD.
12332 */
12333 foreach(l, query->jointree->fromlist)
12334 {
12335 Node *jtnode = (Node *) lfirst(l);
12336
12337 if (IsA(jtnode, RangeTblRef))
12338 {
12339 int varno = ((RangeTblRef *) jtnode)->rtindex;
12340 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12341
12342 if (!rte->inFromCl)
12343 continue;
12344 }
12345
12346 if (first)
12347 {
12348 appendContextKeyword(context, prefix,
12350 first = false;
12351
12352 get_from_clause_item(jtnode, query, context);
12353 }
12354 else
12355 {
12357
12359
12360 /*
12361 * Put the new FROM item's text into itembuf so we can decide
12362 * after we've got it whether or not it needs to go on a new line.
12363 */
12365 context->buf = &itembuf;
12366
12367 get_from_clause_item(jtnode, query, context);
12368
12369 /* Restore context's output buffer */
12370 context->buf = buf;
12371
12372 /* Consider line-wrapping if enabled */
12373 if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
12374 {
12375 /* Does the new item start with a new line? */
12376 if (itembuf.len > 0 && itembuf.data[0] == '\n')
12377 {
12378 /* If so, we shouldn't add anything */
12379 /* instead, remove any trailing spaces currently in buf */
12381 }
12382 else
12383 {
12384 char *trailing_nl;
12385
12386 /* Locate the start of the current line in the buffer */
12387 trailing_nl = strrchr(buf->data, '\n');
12388 if (trailing_nl == NULL)
12389 trailing_nl = buf->data;
12390 else
12391 trailing_nl++;
12392
12393 /*
12394 * Add a newline, plus some indentation, if the new item
12395 * would cause an overflow.
12396 */
12397 if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
12401 }
12402 }
12403
12404 /* Add the new item */
12406
12407 /* clean up */
12408 pfree(itembuf.data);
12409 }
12410 }
12411}

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

12819{
12820 StringInfo buf = context->buf;
12821 ListCell *l1;
12822 ListCell *l2;
12823 ListCell *l3;
12824 ListCell *l4;
12825 int i;
12826
12828
12829 i = 0;
12830 forfour(l1, rtfunc->funccoltypes,
12831 l2, rtfunc->funccoltypmods,
12832 l3, rtfunc->funccolcollations,
12833 l4, rtfunc->funccolnames)
12834 {
12835 Oid atttypid = lfirst_oid(l1);
12836 int32 atttypmod = lfirst_int(l2);
12837 Oid attcollation = lfirst_oid(l3);
12838 char *attname;
12839
12840 if (colinfo)
12841 attname = colinfo->colnames[i];
12842 else
12843 attname = strVal(lfirst(l4));
12844
12845 Assert(attname); /* shouldn't be any dropped columns here */
12846
12847 if (i > 0)
12849 appendStringInfo(buf, "%s %s",
12852 if (OidIsValid(attcollation) &&
12853 attcollation != get_typcollation(atttypid))
12854 appendStringInfo(buf, " COLLATE %s",
12855 generate_collation_name(attcollation));
12856
12857 i++;
12858 }
12859
12861}

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

12415{
12416 StringInfo buf = context->buf;
12418
12419 if (IsA(jtnode, RangeTblRef))
12420 {
12421 int varno = ((RangeTblRef *) jtnode)->rtindex;
12422 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
12425
12426 if (rte->lateral)
12427 appendStringInfoString(buf, "LATERAL ");
12428
12429 /* Print the FROM item proper */
12430 switch (rte->rtekind)
12431 {
12432 case RTE_RELATION:
12433 /* Normal relation RTE */
12434 appendStringInfo(buf, "%s%s",
12437 context->namespaces));
12438 break;
12439 case RTE_SUBQUERY:
12440 /* Subquery RTE */
12442 get_query_def(rte->subquery, buf, context->namespaces, NULL,
12443 true,
12444 context->prettyFlags, context->wrapColumn,
12445 context->indentLevel);
12447 break;
12448 case RTE_FUNCTION:
12449 /* Function RTE */
12450 rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
12451
12452 /*
12453 * Omit ROWS FROM() syntax for just one function, unless it
12454 * has both a coldeflist and WITH ORDINALITY. If it has both,
12455 * we must use ROWS FROM() syntax to avoid ambiguity about
12456 * whether the coldeflist includes the ordinality column.
12457 */
12458 if (list_length(rte->functions) == 1 &&
12459 (rtfunc1->funccolnames == NIL || !rte->funcordinality))
12460 {
12461 get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
12462 /* we'll print the coldeflist below, if it has one */
12463 }
12464 else
12465 {
12466 bool all_unnest;
12467 ListCell *lc;
12468
12469 /*
12470 * If all the function calls in the list are to unnest,
12471 * and none need a coldeflist, then collapse the list back
12472 * down to UNNEST(args). (If we had more than one
12473 * built-in unnest function, this would get more
12474 * difficult.)
12475 *
12476 * XXX This is pretty ugly, since it makes not-terribly-
12477 * future-proof assumptions about what the parser would do
12478 * with the output; but the alternative is to emit our
12479 * nonstandard ROWS FROM() notation for what might have
12480 * been a perfectly spec-compliant multi-argument
12481 * UNNEST().
12482 */
12483 all_unnest = true;
12484 foreach(lc, rte->functions)
12485 {
12487
12488 if (!IsA(rtfunc->funcexpr, FuncExpr) ||
12489 ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
12490 rtfunc->funccolnames != NIL)
12491 {
12492 all_unnest = false;
12493 break;
12494 }
12495 }
12496
12497 if (all_unnest)
12498 {
12499 List *allargs = NIL;
12500
12501 foreach(lc, rte->functions)
12502 {
12504 List *args = ((FuncExpr *) rtfunc->funcexpr)->args;
12505
12506 allargs = list_concat(allargs, args);
12507 }
12508
12509 appendStringInfoString(buf, "UNNEST(");
12510 get_rule_expr((Node *) allargs, context, true);
12512 }
12513 else
12514 {
12515 int funcno = 0;
12516
12517 appendStringInfoString(buf, "ROWS FROM(");
12518 foreach(lc, rte->functions)
12519 {
12521
12522 if (funcno > 0)
12524 get_rule_expr_funccall(rtfunc->funcexpr, context, true);
12525 if (rtfunc->funccolnames != NIL)
12526 {
12527 /* Reconstruct the column definition list */
12528 appendStringInfoString(buf, " AS ");
12530 NULL,
12531 context);
12532 }
12533 funcno++;
12534 }
12536 }
12537 /* prevent printing duplicate coldeflist below */
12538 rtfunc1 = NULL;
12539 }
12540 if (rte->funcordinality)
12541 appendStringInfoString(buf, " WITH ORDINALITY");
12542 break;
12543 case RTE_TABLEFUNC:
12544 get_tablefunc(rte->tablefunc, context, true);
12545 break;
12546 case RTE_VALUES:
12547 /* Values list RTE */
12549 get_values_def(rte->values_lists, context);
12551 break;
12552 case RTE_CTE:
12554 break;
12555 default:
12556 elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
12557 break;
12558 }
12559
12560 /* Print the relation alias, if needed */
12561 get_rte_alias(rte, varno, false, context);
12562
12563 /* Print the column definitions or aliases, if needed */
12564 if (rtfunc1 && rtfunc1->funccolnames != NIL)
12565 {
12566 /* Reconstruct the columndef list, which is also the aliases */
12568 }
12569 else
12570 {
12571 /* Else print column aliases as needed */
12573 }
12574
12575 /* Tablesample clause must go after any alias */
12576 if (rte->rtekind == RTE_RELATION && rte->tablesample)
12577 get_tablesample_def(rte->tablesample, context);
12578 }
12579 else if (IsA(jtnode, JoinExpr))
12580 {
12581 JoinExpr *j = (JoinExpr *) jtnode;
12584
12585 need_paren_on_right = PRETTY_PAREN(context) &&
12586 !IsA(j->rarg, RangeTblRef) &&
12587 !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
12588
12589 if (!PRETTY_PAREN(context) || j->alias != NULL)
12591
12592 get_from_clause_item(j->larg, query, context);
12593
12594 switch (j->jointype)
12595 {
12596 case JOIN_INNER:
12597 if (j->quals)
12598 appendContextKeyword(context, " JOIN ",
12602 else
12603 appendContextKeyword(context, " CROSS JOIN ",
12607 break;
12608 case JOIN_LEFT:
12609 appendContextKeyword(context, " LEFT JOIN ",
12613 break;
12614 case JOIN_FULL:
12615 appendContextKeyword(context, " FULL JOIN ",
12619 break;
12620 case JOIN_RIGHT:
12621 appendContextKeyword(context, " RIGHT JOIN ",
12625 break;
12626 default:
12627 elog(ERROR, "unrecognized join type: %d",
12628 (int) j->jointype);
12629 }
12630
12633 get_from_clause_item(j->rarg, query, context);
12636
12637 if (j->usingClause)
12638 {
12639 ListCell *lc;
12640 bool first = true;
12641
12642 appendStringInfoString(buf, " USING (");
12643 /* Use the assigned names, not what's in usingClause */
12644 foreach(lc, colinfo->usingNames)
12645 {
12646 char *colname = (char *) lfirst(lc);
12647
12648 if (first)
12649 first = false;
12650 else
12653 }
12655
12656 if (j->join_using_alias)
12657 appendStringInfo(buf, " AS %s",
12658 quote_identifier(j->join_using_alias->aliasname));
12659 }
12660 else if (j->quals)
12661 {
12662 appendStringInfoString(buf, " ON ");
12663 if (!PRETTY_PAREN(context))
12665 get_rule_expr(j->quals, context, false);
12666 if (!PRETTY_PAREN(context))
12668 }
12669 else if (j->jointype != JOIN_INNER)
12670 {
12671 /* If we didn't say CROSS JOIN above, we must provide an ON */
12672 appendStringInfoString(buf, " ON TRUE");
12673 }
12674
12675 if (!PRETTY_PAREN(context) || j->alias != NULL)
12677
12678 /* Yes, it's correct to put alias after the right paren ... */
12679 if (j->alias != NULL)
12680 {
12681 /*
12682 * Note that it's correct to emit an alias clause if and only if
12683 * there was one originally. Otherwise we'd be converting a named
12684 * join to unnamed or vice versa, which creates semantic
12685 * subtleties we don't want. However, we might print a different
12686 * alias name than was there originally.
12687 */
12688 appendStringInfo(buf, " %s",
12690 context)));
12692 }
12693 }
12694 else
12695 elog(ERROR, "unrecognized node type: %d",
12696 (int) nodeTag(jtnode));
12697}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, deparse_columns_fetch, elog, ERROR, fb(), RangeTblFunction::funcexpr, generate_relation_name(), get_column_alias_list(), get_from_clause_coldeflist(), get_from_clause_item(), 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_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 10822 of file ruleutils.c.

10824{
10825 StringInfo buf = context->buf;
10826 Oid funcoid = expr->funcid;
10827 Oid argtypes[FUNC_MAX_ARGS];
10828 int nargs;
10829 List *argnames;
10830 bool use_variadic;
10831 ListCell *l;
10832
10833 /*
10834 * If the function call came from an implicit coercion, then just show the
10835 * first argument --- unless caller wants to see implicit coercions.
10836 */
10837 if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
10838 {
10839 get_rule_expr_paren((Node *) linitial(expr->args), context,
10840 false, (Node *) expr);
10841 return;
10842 }
10843
10844 /*
10845 * If the function call came from a cast, then show the first argument
10846 * plus an explicit cast operation.
10847 */
10848 if (expr->funcformat == COERCE_EXPLICIT_CAST ||
10849 expr->funcformat == COERCE_IMPLICIT_CAST)
10850 {
10851 Node *arg = linitial(expr->args);
10852 Oid rettype = expr->funcresulttype;
10854
10855 /* Get the typmod if this is a length-coercion function */
10857
10858 get_coercion_expr(arg, context,
10859 rettype, coercedTypmod,
10860 (Node *) expr);
10861
10862 return;
10863 }
10864
10865 /*
10866 * If the function was called using one of the SQL spec's random special
10867 * syntaxes, try to reproduce that. If we don't recognize the function,
10868 * fall through.
10869 */
10870 if (expr->funcformat == COERCE_SQL_SYNTAX)
10871 {
10872 if (get_func_sql_syntax(expr, context))
10873 return;
10874 }
10875
10876 /*
10877 * Normal function: display as proname(args). First we need to extract
10878 * the argument datatypes.
10879 */
10880 if (list_length(expr->args) > FUNC_MAX_ARGS)
10881 ereport(ERROR,
10883 errmsg("too many arguments")));
10884 nargs = 0;
10885 argnames = NIL;
10886 foreach(l, expr->args)
10887 {
10888 Node *arg = (Node *) lfirst(l);
10889
10890 if (IsA(arg, NamedArgExpr))
10891 argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
10892 argtypes[nargs] = exprType(arg);
10893 nargs++;
10894 }
10895
10896 appendStringInfo(buf, "%s(",
10898 argnames, argtypes,
10899 expr->funcvariadic,
10900 &use_variadic,
10901 context->inGroupBy));
10902 nargs = 0;
10903 foreach(l, expr->args)
10904 {
10905 if (nargs++ > 0)
10907 if (use_variadic && lnext(expr->args, l) == NULL)
10908 appendStringInfoString(buf, "VARIADIC ");
10909 get_rule_expr((Node *) lfirst(l), context, true);
10910 }
10912}

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

11202{
11203 StringInfo buf = context->buf;
11204 Oid funcoid = expr->funcid;
11205
11206 switch (funcoid)
11207 {
11214 /* AT TIME ZONE ... note reversed argument order */
11216 get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
11217 (Node *) expr);
11218 appendStringInfoString(buf, " AT TIME ZONE ");
11219 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11220 (Node *) expr);
11222 return true;
11223
11226 case F_TIMEZONE_TIMETZ:
11227 /* AT LOCAL */
11229 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11230 (Node *) expr);
11231 appendStringInfoString(buf, " AT LOCAL)");
11232 return true;
11233
11247 /* (x1, x2) OVERLAPS (y1, y2) */
11249 get_rule_expr((Node *) linitial(expr->args), context, false);
11251 get_rule_expr((Node *) lsecond(expr->args), context, false);
11252 appendStringInfoString(buf, ") OVERLAPS (");
11253 get_rule_expr((Node *) lthird(expr->args), context, false);
11255 get_rule_expr((Node *) lfourth(expr->args), context, false);
11257 return true;
11258
11265 /* EXTRACT (x FROM y) */
11266 appendStringInfoString(buf, "EXTRACT(");
11267 {
11268 Const *con = (Const *) linitial(expr->args);
11269
11270 Assert(IsA(con, Const) &&
11271 con->consttype == TEXTOID &&
11272 !con->constisnull);
11274 }
11275 appendStringInfoString(buf, " FROM ");
11276 get_rule_expr((Node *) lsecond(expr->args), context, false);
11278 return true;
11279
11280 case F_IS_NORMALIZED:
11281 /* IS xxx NORMALIZED */
11283 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11284 (Node *) expr);
11286 if (list_length(expr->args) == 2)
11287 {
11288 Const *con = (Const *) lsecond(expr->args);
11289
11290 Assert(IsA(con, Const) &&
11291 con->consttype == TEXTOID &&
11292 !con->constisnull);
11293 appendStringInfo(buf, " %s",
11294 TextDatumGetCString(con->constvalue));
11295 }
11296 appendStringInfoString(buf, " NORMALIZED)");
11297 return true;
11298
11299 case F_PG_COLLATION_FOR:
11300 /* COLLATION FOR */
11301 appendStringInfoString(buf, "COLLATION FOR (");
11302 get_rule_expr((Node *) linitial(expr->args), context, false);
11304 return true;
11305
11306 case F_NORMALIZE:
11307 /* NORMALIZE() */
11308 appendStringInfoString(buf, "NORMALIZE(");
11309 get_rule_expr((Node *) linitial(expr->args), context, false);
11310 if (list_length(expr->args) == 2)
11311 {
11312 Const *con = (Const *) lsecond(expr->args);
11313
11314 Assert(IsA(con, Const) &&
11315 con->consttype == TEXTOID &&
11316 !con->constisnull);
11317 appendStringInfo(buf, ", %s",
11318 TextDatumGetCString(con->constvalue));
11319 }
11321 return true;
11322
11329 /* OVERLAY() */
11330 appendStringInfoString(buf, "OVERLAY(");
11331 get_rule_expr((Node *) linitial(expr->args), context, false);
11332 appendStringInfoString(buf, " PLACING ");
11333 get_rule_expr((Node *) lsecond(expr->args), context, false);
11334 appendStringInfoString(buf, " FROM ");
11335 get_rule_expr((Node *) lthird(expr->args), context, false);
11336 if (list_length(expr->args) == 4)
11337 {
11338 appendStringInfoString(buf, " FOR ");
11339 get_rule_expr((Node *) lfourth(expr->args), context, false);
11340 }
11342 return true;
11343
11344 case F_POSITION_BIT_BIT:
11347 /* POSITION() ... extra parens since args are b_expr not a_expr */
11348 appendStringInfoString(buf, "POSITION((");
11349 get_rule_expr((Node *) lsecond(expr->args), context, false);
11350 appendStringInfoString(buf, ") IN (");
11351 get_rule_expr((Node *) linitial(expr->args), context, false);
11353 return true;
11354
11361 /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
11362 appendStringInfoString(buf, "SUBSTRING(");
11363 get_rule_expr((Node *) linitial(expr->args), context, false);
11364 appendStringInfoString(buf, " FROM ");
11365 get_rule_expr((Node *) lsecond(expr->args), context, false);
11366 if (list_length(expr->args) == 3)
11367 {
11368 appendStringInfoString(buf, " FOR ");
11369 get_rule_expr((Node *) lthird(expr->args), context, false);
11370 }
11372 return true;
11373
11375 /* SUBSTRING SIMILAR/ESCAPE */
11376 appendStringInfoString(buf, "SUBSTRING(");
11377 get_rule_expr((Node *) linitial(expr->args), context, false);
11378 appendStringInfoString(buf, " SIMILAR ");
11379 get_rule_expr((Node *) lsecond(expr->args), context, false);
11380 appendStringInfoString(buf, " ESCAPE ");
11381 get_rule_expr((Node *) lthird(expr->args), context, false);
11383 return true;
11384
11386 case F_BTRIM_TEXT:
11387 case F_BTRIM_TEXT_TEXT:
11388 /* TRIM() */
11389 appendStringInfoString(buf, "TRIM(BOTH");
11390 if (list_length(expr->args) == 2)
11391 {
11393 get_rule_expr((Node *) lsecond(expr->args), context, false);
11394 }
11395 appendStringInfoString(buf, " FROM ");
11396 get_rule_expr((Node *) linitial(expr->args), context, false);
11398 return true;
11399
11401 case F_LTRIM_TEXT:
11402 case F_LTRIM_TEXT_TEXT:
11403 /* TRIM() */
11404 appendStringInfoString(buf, "TRIM(LEADING");
11405 if (list_length(expr->args) == 2)
11406 {
11408 get_rule_expr((Node *) lsecond(expr->args), context, false);
11409 }
11410 appendStringInfoString(buf, " FROM ");
11411 get_rule_expr((Node *) linitial(expr->args), context, false);
11413 return true;
11414
11416 case F_RTRIM_TEXT:
11417 case F_RTRIM_TEXT_TEXT:
11418 /* TRIM() */
11419 appendStringInfoString(buf, "TRIM(TRAILING");
11420 if (list_length(expr->args) == 2)
11421 {
11423 get_rule_expr((Node *) lsecond(expr->args), context, false);
11424 }
11425 appendStringInfoString(buf, " FROM ");
11426 get_rule_expr((Node *) linitial(expr->args), context, false);
11428 return true;
11429
11430 case F_SYSTEM_USER:
11431 appendStringInfoString(buf, "SYSTEM_USER");
11432 return true;
11433
11434 case F_XMLEXISTS:
11435 /* XMLEXISTS ... extra parens because args are c_expr */
11436 appendStringInfoString(buf, "XMLEXISTS((");
11437 get_rule_expr((Node *) linitial(expr->args), context, false);
11438 appendStringInfoString(buf, ") PASSING (");
11439 get_rule_expr((Node *) lsecond(expr->args), context, false);
11441 return true;
11442 }
11443 return false;
11444}

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

static void get_insert_query_def ( Query query,
deparse_context context 
)
static

Definition at line 6946 of file ruleutils.c.

6947{
6948 StringInfo buf = context->buf;
6952 char *sep;
6953 ListCell *l;
6955
6956 /* Insert the WITH clause if given */
6957 get_with_clause(query, context);
6958
6959 /*
6960 * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
6961 * single RTE for the SELECT or VALUES. Plain VALUES has neither.
6962 */
6963 foreach(l, query->rtable)
6964 {
6965 rte = (RangeTblEntry *) lfirst(l);
6966
6967 if (rte->rtekind == RTE_SUBQUERY)
6968 {
6969 if (select_rte)
6970 elog(ERROR, "too many subquery RTEs in INSERT");
6971 select_rte = rte;
6972 }
6973
6974 if (rte->rtekind == RTE_VALUES)
6975 {
6976 if (values_rte)
6977 elog(ERROR, "too many values RTEs in INSERT");
6978 values_rte = rte;
6979 }
6980 }
6981 if (select_rte && values_rte)
6982 elog(ERROR, "both subquery and values RTEs in INSERT");
6983
6984 /*
6985 * Start the query with INSERT INTO relname
6986 */
6987 rte = rt_fetch(query->resultRelation, query->rtable);
6988 Assert(rte->rtekind == RTE_RELATION);
6989
6990 if (PRETTY_INDENT(context))
6991 {
6992 context->indentLevel += PRETTYINDENT_STD;
6994 }
6995 appendStringInfo(buf, "INSERT INTO %s",
6996 generate_relation_name(rte->relid, NIL));
6997
6998 /* Print the relation alias, if needed; INSERT requires explicit AS */
6999 get_rte_alias(rte, query->resultRelation, true, context);
7000
7001 /* always want a space here */
7003
7004 /*
7005 * Add the insert-column-names list. Any indirection decoration needed on
7006 * the column names can be inferred from the top targetlist.
7007 */
7009 sep = "";
7010 if (query->targetList)
7012 foreach(l, query->targetList)
7013 {
7015
7016 if (tle->resjunk)
7017 continue; /* ignore junk entries */
7018
7020 sep = ", ";
7021
7022 /*
7023 * Put out name of target column; look in the catalogs, not at
7024 * tle->resname, since resname will fail to track RENAME.
7025 */
7028 tle->resno,
7029 false)));
7030
7031 /*
7032 * Print any indirection needed (subfields or subscripts), and strip
7033 * off the top-level nodes representing the indirection assignments.
7034 * Add the stripped expressions to strippedexprs. (If it's a
7035 * single-VALUES statement, the stripped expressions are the VALUES to
7036 * print below. Otherwise they're just Vars and not really
7037 * interesting.)
7038 */
7040 processIndirection((Node *) tle->expr,
7041 context));
7042 }
7043 if (query->targetList)
7045
7046 if (query->override)
7047 {
7048 if (query->override == OVERRIDING_SYSTEM_VALUE)
7049 appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
7050 else if (query->override == OVERRIDING_USER_VALUE)
7051 appendStringInfoString(buf, "OVERRIDING USER VALUE ");
7052 }
7053
7054 if (select_rte)
7055 {
7056 /* Add the SELECT */
7057 get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
7058 false,
7059 context->prettyFlags, context->wrapColumn,
7060 context->indentLevel);
7061 }
7062 else if (values_rte)
7063 {
7064 /* Add the multi-VALUES expression lists */
7065 get_values_def(values_rte->values_lists, context);
7066 }
7067 else if (strippedexprs)
7068 {
7069 /* Add the single-VALUES expression list */
7070 appendContextKeyword(context, "VALUES (",
7072 get_rule_list_toplevel(strippedexprs, context, false);
7074 }
7075 else
7076 {
7077 /* No expressions, so it must be DEFAULT VALUES */
7078 appendStringInfoString(buf, "DEFAULT VALUES");
7079 }
7080
7081 /* Add ON CONFLICT if present */
7082 if (query->onConflict)
7083 {
7085
7086 appendStringInfoString(buf, " ON CONFLICT");
7087
7088 if (confl->arbiterElems)
7089 {
7090 /* Add the single-VALUES expression list */
7092 get_rule_expr((Node *) confl->arbiterElems, context, false);
7094
7095 /* Add a WHERE clause (for partial indexes) if given */
7096 if (confl->arbiterWhere != NULL)
7097 {
7098 bool save_varprefix;
7099
7100 /*
7101 * Force non-prefixing of Vars, since parser assumes that they
7102 * belong to target relation. WHERE clause does not use
7103 * InferenceElem, so this is separately required.
7104 */
7105 save_varprefix = context->varprefix;
7106 context->varprefix = false;
7107
7108 appendContextKeyword(context, " WHERE ",
7110 get_rule_expr(confl->arbiterWhere, context, false);
7111
7112 context->varprefix = save_varprefix;
7113 }
7114 }
7115 else if (OidIsValid(confl->constraint))
7116 {
7117 char *constraint = get_constraint_name(confl->constraint);
7118
7119 if (!constraint)
7120 elog(ERROR, "cache lookup failed for constraint %u",
7121 confl->constraint);
7122 appendStringInfo(buf, " ON CONSTRAINT %s",
7123 quote_identifier(constraint));
7124 }
7125
7126 if (confl->action == ONCONFLICT_NOTHING)
7127 {
7128 appendStringInfoString(buf, " DO NOTHING");
7129 }
7130 else if (confl->action == ONCONFLICT_UPDATE)
7131 {
7132 appendStringInfoString(buf, " DO UPDATE SET ");
7133 /* Deparse targetlist */
7134 get_update_query_targetlist_def(query, confl->onConflictSet,
7135 context, rte);
7136
7137 /* Add a WHERE clause if given */
7138 if (confl->onConflictWhere != NULL)
7139 {
7140 appendContextKeyword(context, " WHERE ",
7142 get_rule_expr(confl->onConflictWhere, context, false);
7143 }
7144 }
7145 else
7146 {
7147 Assert(confl->action == ONCONFLICT_SELECT);
7148 appendStringInfoString(buf, " DO SELECT");
7149
7150 /* Add FOR [KEY] UPDATE/SHARE clause if present */
7151 if (confl->lockStrength != LCS_NONE)
7153
7154 /* Add a WHERE clause if given */
7155 if (confl->onConflictWhere != NULL)
7156 {
7157 appendContextKeyword(context, " WHERE ",
7159 get_rule_expr(confl->onConflictWhere, context, false);
7160 }
7161 }
7162 }
7163
7164 /* Add RETURNING if present */
7165 if (query->returningList)
7166 get_returning_clause(query, context);
7167}

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

11822{
11824
11827
11828 if (IsA(ctor->func, Aggref))
11829 get_agg_expr_helper((Aggref *) ctor->func, context,
11830 (Aggref *) ctor->func,
11832 else if (IsA(ctor->func, WindowFunc))
11833 get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
11834 funcname, options.data,
11836 else
11837 elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
11838 nodeTag(ctor->func));
11839}

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

Referenced by get_json_constructor().

◆ get_json_behavior()

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

Definition at line 9205 of file ruleutils.c.

9207{
9208 /*
9209 * The order of array elements must correspond to the order of
9210 * JsonBehaviorType members.
9211 */
9212 const char *behavior_names[] =
9213 {
9214 " NULL",
9215 " ERROR",
9216 " EMPTY",
9217 " TRUE",
9218 " FALSE",
9219 " UNKNOWN",
9220 " EMPTY ARRAY",
9221 " EMPTY OBJECT",
9222 " DEFAULT "
9223 };
9224
9225 if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
9226 elog(ERROR, "invalid json behavior type: %d", behavior->btype);
9227
9228 appendStringInfoString(context->buf, behavior_names[behavior->btype]);
9229
9230 if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
9231 get_rule_expr(behavior->expr, context, false);
9232
9233 appendStringInfo(context->buf, " ON %s", on);
9234}

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

11726{
11727 StringInfo buf = context->buf;
11728 const char *funcname;
11729 bool is_json_object;
11730 int curridx;
11731 ListCell *lc;
11732
11733 if (ctor->type == JSCTOR_JSON_OBJECTAGG)
11734 {
11735 get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
11736 return;
11737 }
11738 else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
11739 {
11740 get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
11741 return;
11742 }
11743
11744 switch (ctor->type)
11745 {
11746 case JSCTOR_JSON_OBJECT:
11747 funcname = "JSON_OBJECT";
11748 break;
11749 case JSCTOR_JSON_ARRAY:
11750 funcname = "JSON_ARRAY";
11751 break;
11752 case JSCTOR_JSON_PARSE:
11753 funcname = "JSON";
11754 break;
11755 case JSCTOR_JSON_SCALAR:
11756 funcname = "JSON_SCALAR";
11757 break;
11759 funcname = "JSON_SERIALIZE";
11760 break;
11761 default:
11762 elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
11763 }
11764
11765 appendStringInfo(buf, "%s(", funcname);
11766
11768 foreach(lc, ctor->args)
11769 {
11771 if (curridx > 0)
11772 {
11773 const char *sep;
11774
11775 sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
11777 }
11778
11779 get_rule_expr((Node *) lfirst(lc), context, true);
11780 }
11781
11784}

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

Referenced by get_rule_expr().

◆ get_json_constructor_options()

static void get_json_constructor_options ( JsonConstructorExpr ctor,
StringInfo  buf 
)
static

Definition at line 11790 of file ruleutils.c.

11791{
11792 if (ctor->absent_on_null)
11793 {
11794 if (ctor->type == JSCTOR_JSON_OBJECT ||
11795 ctor->type == JSCTOR_JSON_OBJECTAGG)
11796 appendStringInfoString(buf, " ABSENT ON NULL");
11797 }
11798 else
11799 {
11800 if (ctor->type == JSCTOR_JSON_ARRAY ||
11801 ctor->type == JSCTOR_JSON_ARRAYAGG)
11802 appendStringInfoString(buf, " NULL ON NULL");
11803 }
11804
11805 if (ctor->unique)
11806 appendStringInfoString(buf, " WITH UNIQUE KEYS");
11807
11808 /*
11809 * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
11810 * support one.
11811 */
11812 if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
11813 get_json_returning(ctor->returning, buf, true);
11814}

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

9245{
9246 if (jsexpr->op == JSON_QUERY_OP)
9247 {
9248 if (jsexpr->wrapper == JSW_CONDITIONAL)
9249 appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
9250 else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
9251 appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
9252 /* The default */
9253 else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
9254 appendStringInfoString(context->buf, " WITHOUT WRAPPER");
9255
9256 if (jsexpr->omit_quotes)
9257 appendStringInfoString(context->buf, " OMIT QUOTES");
9258 /* The default */
9259 else
9260 appendStringInfoString(context->buf, " KEEP QUOTES");
9261 }
9262
9263 if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
9264 get_json_behavior(jsexpr->on_empty, context, "EMPTY");
9265
9266 if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
9267 get_json_behavior(jsexpr->on_error, context, "ERROR");
9268}

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

11680{
11681 if (format->format_type == JS_FORMAT_DEFAULT)
11682 return;
11683
11685 format->format_type == JS_FORMAT_JSONB ?
11686 " FORMAT JSONB" : " FORMAT JSON");
11687
11688 if (format->encoding != JS_ENC_DEFAULT)
11689 {
11690 const char *encoding;
11691
11692 encoding =
11693 format->encoding == JS_ENC_UTF16 ? "UTF16" :
11694 format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
11695
11696 appendStringInfo(buf, " ENCODING %s", encoding);
11697 }
11698}

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

11668{
11669 if (IsA(path_spec, Const))
11670 get_const_expr((Const *) path_spec, context, -1);
11671 else
11672 get_rule_expr(path_spec, context, showimplicit);
11673}

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

11706{
11707 if (!OidIsValid(returning->typid))
11708 return;
11709
11710 appendStringInfo(buf, " RETURNING %s",
11712 returning->typmod));
11713
11715 returning->format->format_type !=
11716 (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
11717 get_json_format(returning->format, buf);
11718}

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

12233{
12234 StringInfo buf = context->buf;
12237
12238 appendStringInfoString(buf, "JSON_TABLE(");
12239
12240 if (PRETTY_INDENT(context))
12241 context->indentLevel += PRETTYINDENT_VAR;
12242
12243 appendContextKeyword(context, "", 0, 0, 0);
12244
12245 get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12246
12248
12249 get_const_expr(root->path->value, context, -1);
12250
12251 appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12252
12253 if (jexpr->passing_values)
12254 {
12255 ListCell *lc1,
12256 *lc2;
12257 bool needcomma = false;
12258
12260 appendContextKeyword(context, "PASSING ", 0, 0, 0);
12261
12262 if (PRETTY_INDENT(context))
12263 context->indentLevel += PRETTYINDENT_VAR;
12264
12265 forboth(lc1, jexpr->passing_names,
12266 lc2, jexpr->passing_values)
12267 {
12268 if (needcomma)
12270 needcomma = true;
12271
12272 appendContextKeyword(context, "", 0, 0, 0);
12273
12274 get_rule_expr((Node *) lfirst(lc2), context, false);
12275 appendStringInfo(buf, " AS %s",
12277 );
12278 }
12279
12280 if (PRETTY_INDENT(context))
12281 context->indentLevel -= PRETTYINDENT_VAR;
12282 }
12283
12284 get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12285 showimplicit);
12286
12287 if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12288 get_json_behavior(jexpr->on_error, context, "ERROR");
12289
12290 if (PRETTY_INDENT(context))
12291 context->indentLevel -= PRETTYINDENT_VAR;
12292
12293 appendContextKeyword(context, ")", 0, 0, 0);
12294}

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

Referenced by get_tablefunc().

◆ get_json_table_columns()

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

Definition at line 12126 of file ruleutils.c.

12129{
12130 StringInfo buf = context->buf;
12135 int colnum = 0;
12136
12138 appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12139
12140 if (PRETTY_INDENT(context))
12141 context->indentLevel += PRETTYINDENT_VAR;
12142
12143 forfour(lc_colname, tf->colnames,
12144 lc_coltype, tf->coltypes,
12145 lc_coltypmod, tf->coltypmods,
12146 lc_colvalexpr, tf->colvalexprs)
12147 {
12148 char *colname = strVal(lfirst(lc_colname));
12149 JsonExpr *colexpr;
12150 Oid typid;
12151 int32 typmod;
12152 bool ordinality;
12154
12155 typid = lfirst_oid(lc_coltype);
12156 typmod = lfirst_int(lc_coltypmod);
12158
12159 /* Skip columns that don't belong to this scan. */
12160 if (scan->colMin < 0 || colnum < scan->colMin)
12161 {
12162 colnum++;
12163 continue;
12164 }
12165 if (colnum > scan->colMax)
12166 break;
12167
12168 if (colnum > scan->colMin)
12170
12171 colnum++;
12172
12173 ordinality = !colexpr;
12174
12175 appendContextKeyword(context, "", 0, 0, 0);
12176
12177 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12178 ordinality ? "FOR ORDINALITY" :
12179 format_type_with_typemod(typid, typmod));
12180 if (ordinality)
12181 continue;
12182
12183 /*
12184 * Set default_behavior to guide get_json_expr_options() on whether to
12185 * emit the ON ERROR / EMPTY clauses.
12186 */
12187 if (colexpr->op == JSON_EXISTS_OP)
12188 {
12189 appendStringInfoString(buf, " EXISTS");
12191 }
12192 else
12193 {
12194 if (colexpr->op == JSON_QUERY_OP)
12195 {
12196 char typcategory;
12197 bool typispreferred;
12198
12200
12203 colexpr->format->format_type == JS_FORMAT_JSONB ?
12204 " FORMAT JSONB" : " FORMAT JSON");
12205 }
12206
12208 }
12209
12210 appendStringInfoString(buf, " PATH ");
12211
12212 get_json_path_spec(colexpr->path_spec, context, showimplicit);
12213
12214 get_json_expr_options(colexpr, context, default_behavior);
12215 }
12216
12217 if (scan->child)
12219 scan->colMin >= 0);
12220
12221 if (PRETTY_INDENT(context))
12222 context->indentLevel -= PRETTYINDENT_VAR;
12223
12224 appendContextKeyword(context, ")", 0, 0, 0);
12225}

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

12097{
12099 {
12101
12102 if (needcomma)
12103 appendStringInfoChar(context->buf, ',');
12104
12105 appendStringInfoChar(context->buf, ' ');
12106 appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
12107 get_const_expr(scan->path->value, context, -1);
12108 appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
12109 get_json_table_columns(tf, scan, context, showimplicit);
12110 }
12111 else if (IsA(plan, JsonTableSiblingJoin))
12112 {
12114
12116 needcomma);
12118 true);
12119 }
12120}

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

Referenced by get_json_table_columns(), and get_json_table_nested_columns().

◆ get_lock_clause_strength()

static char * get_lock_clause_strength ( LockClauseStrength  strength)
static

Definition at line 6017 of file ruleutils.c.

6018{
6019 switch (strength)
6020 {
6021 case LCS_NONE:
6022 /* we intentionally throw an error for LCS_NONE */
6023 elog(ERROR, "unrecognized LockClauseStrength %d",
6024 (int) strength);
6025 break;
6026 case LCS_FORKEYSHARE:
6027 return " FOR KEY SHARE";
6028 case LCS_FORSHARE:
6029 return " FOR SHARE";
6030 case LCS_FORNOKEYUPDATE:
6031 return " FOR NO KEY UPDATE";
6032 case LCS_FORUPDATE:
6033 return " FOR UPDATE";
6034 }
6035 return NULL; /* keep compiler quiet */
6036}

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

7427{
7428 StringInfo buf = context->buf;
7430 ListCell *lc;
7432
7433 /* Insert the WITH clause if given */
7434 get_with_clause(query, context);
7435
7436 /*
7437 * Start the query with MERGE INTO relname
7438 */
7439 rte = rt_fetch(query->resultRelation, query->rtable);
7440 Assert(rte->rtekind == RTE_RELATION);
7441 if (PRETTY_INDENT(context))
7442 {
7444 context->indentLevel += PRETTYINDENT_STD;
7445 }
7446 appendStringInfo(buf, "MERGE INTO %s%s",
7448 generate_relation_name(rte->relid, NIL));
7449
7450 /* Print the relation alias, if needed */
7451 get_rte_alias(rte, query->resultRelation, false, context);
7452
7453 /* Print the source relation and join clause */
7454 get_from_clause(query, " USING ", context);
7455 appendContextKeyword(context, " ON ",
7457 get_rule_expr(query->mergeJoinCondition, context, false);
7458
7459 /*
7460 * Test for any NOT MATCHED BY SOURCE actions. If there are none, then
7461 * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
7462 * SQL standard. Otherwise, we have a non-SQL-standard query, so output
7463 * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
7464 * more explicit.
7465 */
7466 haveNotMatchedBySource = false;
7467 foreach(lc, query->mergeActionList)
7468 {
7470
7471 if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
7472 {
7474 break;
7475 }
7476 }
7477
7478 /* Print each merge action */
7479 foreach(lc, query->mergeActionList)
7480 {
7482
7483 appendContextKeyword(context, " WHEN ",
7485 switch (action->matchKind)
7486 {
7487 case MERGE_WHEN_MATCHED:
7488 appendStringInfoString(buf, "MATCHED");
7489 break;
7491 appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
7492 break;
7495 appendStringInfoString(buf, "NOT MATCHED BY TARGET");
7496 else
7497 appendStringInfoString(buf, "NOT MATCHED");
7498 break;
7499 default:
7500 elog(ERROR, "unrecognized matchKind: %d",
7501 (int) action->matchKind);
7502 }
7503
7504 if (action->qual)
7505 {
7506 appendContextKeyword(context, " AND ",
7508 get_rule_expr(action->qual, context, false);
7509 }
7510 appendContextKeyword(context, " THEN ",
7512
7513 if (action->commandType == CMD_INSERT)
7514 {
7515 /* This generally matches get_insert_query_def() */
7517 const char *sep = "";
7518 ListCell *lc2;
7519
7520 appendStringInfoString(buf, "INSERT");
7521
7522 if (action->targetList)
7524 foreach(lc2, action->targetList)
7525 {
7527
7528 Assert(!tle->resjunk);
7529
7531 sep = ", ";
7532
7535 tle->resno,
7536 false)));
7538 processIndirection((Node *) tle->expr,
7539 context));
7540 }
7541 if (action->targetList)
7543
7544 if (action->override)
7545 {
7546 if (action->override == OVERRIDING_SYSTEM_VALUE)
7547 appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
7548 else if (action->override == OVERRIDING_USER_VALUE)
7549 appendStringInfoString(buf, " OVERRIDING USER VALUE");
7550 }
7551
7552 if (strippedexprs)
7553 {
7554 appendContextKeyword(context, " VALUES (",
7556 get_rule_list_toplevel(strippedexprs, context, false);
7558 }
7559 else
7560 appendStringInfoString(buf, " DEFAULT VALUES");
7561 }
7562 else if (action->commandType == CMD_UPDATE)
7563 {
7564 appendStringInfoString(buf, "UPDATE SET ");
7565 get_update_query_targetlist_def(query, action->targetList,
7566 context, rte);
7567 }
7568 else if (action->commandType == CMD_DELETE)
7569 appendStringInfoString(buf, "DELETE");
7570 else if (action->commandType == CMD_NOTHING)
7571 appendStringInfoString(buf, "DO NOTHING");
7572 }
7573
7574 /* Add RETURNING if present */
7575 if (query->returningList)
7576 get_returning_clause(query, context);
7577}

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

8043{
8046 int netlevelsup;
8048 int varno;
8049 AttrNumber varattno;
8051 Node *expr;
8052
8053 /*
8054 * If it's a RowExpr that was expanded from a whole-row Var, use the
8055 * column names attached to it. (We could let get_expr_result_tupdesc()
8056 * handle this, but it's much cheaper to just pull out the name we need.)
8057 */
8058 if (IsA(var, RowExpr))
8059 {
8060 RowExpr *r = (RowExpr *) var;
8061
8062 if (fieldno > 0 && fieldno <= list_length(r->colnames))
8063 return strVal(list_nth(r->colnames, fieldno - 1));
8064 }
8065
8066 /*
8067 * If it's a Param of type RECORD, try to find what the Param refers to.
8068 */
8069 if (IsA(var, Param))
8070 {
8071 Param *param = (Param *) var;
8073
8074 expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8075 if (expr)
8076 {
8077 /* Found a match, so recurse to decipher the field name */
8079 const char *result;
8080
8082 result = get_name_for_var_field((Var *) expr, fieldno,
8083 0, context);
8085 return result;
8086 }
8087 }
8088
8089 /*
8090 * If it's a Var of type RECORD, we have to find what the Var refers to;
8091 * if not, we can use get_expr_result_tupdesc().
8092 */
8093 if (!IsA(var, Var) ||
8094 var->vartype != RECORDOID)
8095 {
8096 tupleDesc = get_expr_result_tupdesc((Node *) var, false);
8097 /* Got the tupdesc, so we can extract the field name */
8100 }
8101
8102 /* Find appropriate nesting depth */
8103 netlevelsup = var->varlevelsup + levelsup;
8104 if (netlevelsup >= list_length(context->namespaces))
8105 elog(ERROR, "bogus varlevelsup: %d offset %d",
8106 var->varlevelsup, levelsup);
8108 netlevelsup);
8109
8110 /*
8111 * If we have a syntactic referent for the Var, and we're working from a
8112 * parse tree, prefer to use the syntactic referent. Otherwise, fall back
8113 * on the semantic referent. (See comments in get_variable().)
8114 */
8115 if (var->varnosyn > 0 && dpns->plan == NULL)
8116 {
8117 varno = var->varnosyn;
8118 varattno = var->varattnosyn;
8119 }
8120 else
8121 {
8122 varno = var->varno;
8123 varattno = var->varattno;
8124 }
8125
8126 /*
8127 * Try to find the relevant RTE in this rtable. In a plan tree, it's
8128 * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
8129 * down into the subplans, or INDEX_VAR, which is resolved similarly.
8130 *
8131 * Note: unlike get_variable and resolve_special_varno, we need not worry
8132 * about inheritance mapping: a child Var should have the same datatype as
8133 * its parent, and here we're really only interested in the Var's type.
8134 */
8135 if (varno >= 1 && varno <= list_length(dpns->rtable))
8136 {
8137 rte = rt_fetch(varno, dpns->rtable);
8138 attnum = varattno;
8139 }
8140 else if (varno == OUTER_VAR && dpns->outer_tlist)
8141 {
8144 const char *result;
8145
8146 tle = get_tle_by_resno(dpns->outer_tlist, varattno);
8147 if (!tle)
8148 elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
8149
8150 Assert(netlevelsup == 0);
8151 push_child_plan(dpns, dpns->outer_plan, &save_dpns);
8152
8153 result = get_name_for_var_field((Var *) tle->expr, fieldno,
8154 levelsup, context);
8155
8157 return result;
8158 }
8159 else if (varno == INNER_VAR && dpns->inner_tlist)
8160 {
8163 const char *result;
8164
8165 tle = get_tle_by_resno(dpns->inner_tlist, varattno);
8166 if (!tle)
8167 elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
8168
8169 Assert(netlevelsup == 0);
8170 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8171
8172 result = get_name_for_var_field((Var *) tle->expr, fieldno,
8173 levelsup, context);
8174
8176 return result;
8177 }
8178 else if (varno == INDEX_VAR && dpns->index_tlist)
8179 {
8181 const char *result;
8182
8183 tle = get_tle_by_resno(dpns->index_tlist, varattno);
8184 if (!tle)
8185 elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
8186
8187 Assert(netlevelsup == 0);
8188
8189 result = get_name_for_var_field((Var *) tle->expr, fieldno,
8190 levelsup, context);
8191
8192 return result;
8193 }
8194 else
8195 {
8196 elog(ERROR, "bogus varno: %d", varno);
8197 return NULL; /* keep compiler quiet */
8198 }
8199
8201 {
8202 /* Var is whole-row reference to RTE, so select the right field */
8204 }
8205
8206 /*
8207 * This part has essentially the same logic as the parser's
8208 * expandRecordVariable() function, but we are dealing with a different
8209 * representation of the input context, and we only need one field name
8210 * not a TupleDesc. Also, we need special cases for finding subquery and
8211 * CTE subplans when deparsing Plan trees.
8212 */
8213 expr = (Node *) var; /* default if we can't drill down */
8214
8215 switch (rte->rtekind)
8216 {
8217 case RTE_RELATION:
8218 case RTE_VALUES:
8220 case RTE_RESULT:
8221
8222 /*
8223 * This case should not occur: a column of a table, values list,
8224 * or ENR shouldn't have type RECORD. Fall through and fail (most
8225 * likely) at the bottom.
8226 */
8227 break;
8228 case RTE_SUBQUERY:
8229 /* Subselect-in-FROM: examine sub-select's output expr */
8230 {
8231 if (rte->subquery)
8232 {
8233 TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
8234 attnum);
8235
8236 if (ste == NULL || ste->resjunk)
8237 elog(ERROR, "subquery %s does not have attribute %d",
8238 rte->eref->aliasname, attnum);
8239 expr = (Node *) ste->expr;
8240 if (IsA(expr, Var))
8241 {
8242 /*
8243 * Recurse into the sub-select to see what its Var
8244 * refers to. We have to build an additional level of
8245 * namespace to keep in step with varlevelsup in the
8246 * subselect; furthermore, the subquery RTE might be
8247 * from an outer query level, in which case the
8248 * namespace for the subselect must have that outer
8249 * level as parent namespace.
8250 */
8251 List *save_nslist = context->namespaces;
8254 const char *result;
8255
8257 netlevelsup);
8258
8259 set_deparse_for_query(&mydpns, rte->subquery,
8261
8263
8264 result = get_name_for_var_field((Var *) expr, fieldno,
8265 0, context);
8266
8267 context->namespaces = save_nslist;
8268
8269 return result;
8270 }
8271 /* else fall through to inspect the expression */
8272 }
8273 else
8274 {
8275 /*
8276 * We're deparsing a Plan tree so we don't have complete
8277 * RTE entries (in particular, rte->subquery is NULL). But
8278 * the only place we'd normally see a Var directly
8279 * referencing a SUBQUERY RTE is in a SubqueryScan plan
8280 * node, and we can look into the child plan's tlist
8281 * instead. An exception occurs if the subquery was
8282 * proven empty and optimized away: then we'd find such a
8283 * Var in a childless Result node, and there's nothing in
8284 * the plan tree that would let us figure out what it had
8285 * originally referenced. In that case, fall back on
8286 * printing "fN", analogously to the default column names
8287 * for RowExprs.
8288 */
8291 const char *result;
8292
8293 if (!dpns->inner_plan)
8294 {
8295 char *dummy_name = palloc(32);
8296
8297 Assert(dpns->plan && IsA(dpns->plan, Result));
8298 snprintf(dummy_name, 32, "f%d", fieldno);
8299 return dummy_name;
8300 }
8301 Assert(dpns->plan && IsA(dpns->plan, SubqueryScan));
8302
8303 tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8304 if (!tle)
8305 elog(ERROR, "bogus varattno for subquery var: %d",
8306 attnum);
8307 Assert(netlevelsup == 0);
8308 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8309
8310 result = get_name_for_var_field((Var *) tle->expr, fieldno,
8311 levelsup, context);
8312
8314 return result;
8315 }
8316 }
8317 break;
8318 case RTE_JOIN:
8319 /* Join RTE --- recursively inspect the alias variable */
8320 if (rte->joinaliasvars == NIL)
8321 elog(ERROR, "cannot decompile join alias var in plan tree");
8322 Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
8323 expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
8324 Assert(expr != NULL);
8325 /* we intentionally don't strip implicit coercions here */
8326 if (IsA(expr, Var))
8327 return get_name_for_var_field((Var *) expr, fieldno,
8328 var->varlevelsup + levelsup,
8329 context);
8330 /* else fall through to inspect the expression */
8331 break;
8332 case RTE_FUNCTION:
8333 case RTE_TABLEFUNC:
8334
8335 /*
8336 * We couldn't get here unless a function is declared with one of
8337 * its result columns as RECORD, which is not allowed.
8338 */
8339 break;
8340 case RTE_CTE:
8341 /* CTE reference: examine subquery's output expr */
8342 {
8343 CommonTableExpr *cte = NULL;
8344 Index ctelevelsup;
8345 ListCell *lc;
8346
8347 /*
8348 * Try to find the referenced CTE using the namespace stack.
8349 */
8350 ctelevelsup = rte->ctelevelsup + netlevelsup;
8351 if (ctelevelsup >= list_length(context->namespaces))
8352 lc = NULL;
8353 else
8354 {
8356
8358 list_nth(context->namespaces, ctelevelsup);
8359 foreach(lc, ctedpns->ctes)
8360 {
8361 cte = (CommonTableExpr *) lfirst(lc);
8362 if (strcmp(cte->ctename, rte->ctename) == 0)
8363 break;
8364 }
8365 }
8366 if (lc != NULL)
8367 {
8368 Query *ctequery = (Query *) cte->ctequery;
8370 attnum);
8371
8372 if (ste == NULL || ste->resjunk)
8373 elog(ERROR, "CTE %s does not have attribute %d",
8374 rte->eref->aliasname, attnum);
8375 expr = (Node *) ste->expr;
8376 if (IsA(expr, Var))
8377 {
8378 /*
8379 * Recurse into the CTE to see what its Var refers to.
8380 * We have to build an additional level of namespace
8381 * to keep in step with varlevelsup in the CTE;
8382 * furthermore it could be an outer CTE (compare
8383 * SUBQUERY case above).
8384 */
8385 List *save_nslist = context->namespaces;
8388 const char *result;
8389
8391 ctelevelsup);
8392
8393 set_deparse_for_query(&mydpns, ctequery,
8395
8397
8398 result = get_name_for_var_field((Var *) expr, fieldno,
8399 0, context);
8400
8401 context->namespaces = save_nslist;
8402
8403 return result;
8404 }
8405 /* else fall through to inspect the expression */
8406 }
8407 else
8408 {
8409 /*
8410 * We're deparsing a Plan tree so we don't have a CTE
8411 * list. But the only places we'd normally see a Var
8412 * directly referencing a CTE RTE are in CteScan or
8413 * WorkTableScan plan nodes. For those cases,
8414 * set_deparse_plan arranged for dpns->inner_plan to be
8415 * the plan node that emits the CTE or RecursiveUnion
8416 * result, and we can look at its tlist instead. As
8417 * above, this can fail if the CTE has been proven empty,
8418 * in which case fall back to "fN".
8419 */
8422 const char *result;
8423
8424 if (!dpns->inner_plan)
8425 {
8426 char *dummy_name = palloc(32);
8427
8428 Assert(dpns->plan && IsA(dpns->plan, Result));
8429 snprintf(dummy_name, 32, "f%d", fieldno);
8430 return dummy_name;
8431 }
8432 Assert(dpns->plan && (IsA(dpns->plan, CteScan) ||
8433 IsA(dpns->plan, WorkTableScan)));
8434
8435 tle = get_tle_by_resno(dpns->inner_tlist, attnum);
8436 if (!tle)
8437 elog(ERROR, "bogus varattno for subquery var: %d",
8438 attnum);
8439 Assert(netlevelsup == 0);
8440 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
8441
8442 result = get_name_for_var_field((Var *) tle->expr, fieldno,
8443 levelsup, context);
8444
8446 return result;
8447 }
8448 }
8449 break;
8450 case RTE_GROUP:
8451
8452 /*
8453 * We couldn't get here: any Vars that reference the RTE_GROUP RTE
8454 * should have been replaced with the underlying grouping
8455 * expressions.
8456 */
8457 break;
8458 }
8459
8460 /*
8461 * We now have an expression we can't expand any more, so see if
8462 * get_expr_result_tupdesc() can do anything with it.
8463 */
8464 tupleDesc = get_expr_result_tupdesc(expr, false);
8465 /* Got the tupdesc, so we can extract the field name */
8468}

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(), rt_fetch, RTE_CTE, RTE_FUNCTION, 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 12911 of file ruleutils.c.

12913{
12916 char *opcname;
12917 char *nspname;
12918
12921 elog(ERROR, "cache lookup failed for opclass %u", opclass);
12923
12925 GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
12926 {
12927 /* Okay, we need the opclass name. Do we need to qualify it? */
12928 opcname = NameStr(opcrec->opcname);
12929 if (OpclassIsVisible(opclass))
12931 else
12932 {
12933 nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
12934 appendStringInfo(buf, " %s.%s",
12935 quote_identifier(nspname),
12937 }
12938 }
12940}

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

10783{
10784 StringInfo buf = context->buf;
10785 Oid opno = expr->opno;
10786 List *args = expr->args;
10787
10788 if (!PRETTY_PAREN(context))
10790 if (list_length(args) == 2)
10791 {
10792 /* binary operator */
10793 Node *arg1 = (Node *) linitial(args);
10794 Node *arg2 = (Node *) lsecond(args);
10795
10796 get_rule_expr_paren(arg1, context, true, (Node *) expr);
10797 appendStringInfo(buf, " %s ",
10799 exprType(arg1),
10800 exprType(arg2)));
10801 get_rule_expr_paren(arg2, context, true, (Node *) expr);
10802 }
10803 else
10804 {
10805 /* prefix operator */
10806 Node *arg = (Node *) linitial(args);
10807
10808 appendStringInfo(buf, "%s ",
10810 InvalidOid,
10811 exprType(arg)));
10812 get_rule_expr_paren(arg, context, true, (Node *) expr);
10813 }
10814 if (!PRETTY_PAREN(context))
10816}

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

8712{
8713 Node *expr;
8716 SubPlan *subplan;
8717 int column;
8718
8719 /*
8720 * If it's a PARAM_EXEC parameter, try to locate the expression from which
8721 * the parameter was computed. This stanza handles only cases in which
8722 * the Param represents an input to the subplan we are currently in.
8723 */
8724 expr = find_param_referent(param, context, &dpns, &ancestor_cell);
8725 if (expr)
8726 {
8727 /* Found a match, so print it */
8729 bool save_varprefix;
8730 bool need_paren;
8731
8732 /* Switch attention to the ancestor plan node */
8734
8735 /*
8736 * Force prefixing of Vars, since they won't belong to the relation
8737 * being scanned in the original plan node.
8738 */
8739 save_varprefix = context->varprefix;
8740 context->varprefix = true;
8741
8742 /*
8743 * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
8744 * upper-level Param, which wouldn't need extra parentheses.
8745 * Otherwise, insert parens to ensure the expression looks atomic.
8746 */
8747 need_paren = !(IsA(expr, Var) ||
8748 IsA(expr, Aggref) ||
8749 IsA(expr, GroupingFunc) ||
8750 IsA(expr, Param));
8751 if (need_paren)
8752 appendStringInfoChar(context->buf, '(');
8753
8754 get_rule_expr(expr, context, false);
8755
8756 if (need_paren)
8757 appendStringInfoChar(context->buf, ')');
8758
8759 context->varprefix = save_varprefix;
8760
8762
8763 return;
8764 }
8765
8766 /*
8767 * Alternatively, maybe it's a subplan output, which we print as a
8768 * reference to the subplan. (We could drill down into the subplan and
8769 * print the relevant targetlist expression, but that has been deemed too
8770 * confusing since it would violate normal SQL scope rules. Also, we're
8771 * relying on this reference to show that the testexpr containing the
8772 * Param has anything to do with that subplan at all.)
8773 */
8774 subplan = find_param_generator(param, context, &column);
8775 if (subplan)
8776 {
8777 const char *nameprefix;
8778
8779 if (subplan->isInitPlan)
8780 nameprefix = "InitPlan ";
8781 else
8782 nameprefix = "SubPlan ";
8783
8784 appendStringInfo(context->buf, "(%s%s%s).col%d",
8785 subplan->useHashTable ? "hashed " : "",
8786 nameprefix,
8787 subplan->plan_name, column + 1);
8788
8789 return;
8790 }
8791
8792 /*
8793 * If it's an external parameter, see if the outermost namespace provides
8794 * function argument names.
8795 */
8796 if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
8797 {
8798 dpns = llast(context->namespaces);
8799 if (dpns->argnames &&
8800 param->paramid > 0 &&
8801 param->paramid <= dpns->numargs)
8802 {
8803 char *argname = dpns->argnames[param->paramid - 1];
8804
8805 if (argname)
8806 {
8807 bool should_qualify = false;
8808 ListCell *lc;
8809
8810 /*
8811 * Qualify the parameter name if there are any other deparse
8812 * namespaces with range tables. This avoids qualifying in
8813 * trivial cases like "RETURN a + b", but makes it safe in all
8814 * other cases.
8815 */
8816 foreach(lc, context->namespaces)
8817 {
8819
8820 if (depns->rtable_names != NIL)
8821 {
8822 should_qualify = true;
8823 break;
8824 }
8825 }
8826 if (should_qualify)
8827 {
8828 appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
8829 appendStringInfoChar(context->buf, '.');
8830 }
8831
8832 appendStringInfoString(context->buf, quote_identifier(argname));
8833 return;
8834 }
8835 }
8836 }
8837
8838 /*
8839 * Not PARAM_EXEC, or couldn't find referent: just print $N.
8840 *
8841 * It's a bug if we get here for anything except PARAM_EXTERN Params, but
8842 * in production builds printing $N seems more useful than failing.
8843 */
8844 Assert(param->paramkind == PARAM_EXTERN);
8845
8846 appendStringInfo(context->buf, "$%d", param->paramid);
8847}

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

5631{
5632 deparse_context context;
5634 int rtable_size;
5635
5636 /* Guard against excessively long or deeply-nested queries */
5639
5640 rtable_size = query->hasGroupRTE ?
5641 list_length(query->rtable) - 1 :
5642 list_length(query->rtable);
5643
5644 /*
5645 * Replace any Vars in the query's targetlist and havingQual that
5646 * reference GROUP outputs with the underlying grouping expressions.
5647 */
5648 if (query->hasGroupRTE)
5649 {
5650 query->targetList = (List *)
5651 flatten_group_exprs(NULL, query, (Node *) query->targetList);
5652 query->havingQual =
5653 flatten_group_exprs(NULL, query, query->havingQual);
5654 }
5655
5656 /*
5657 * Before we begin to examine the query, acquire locks on referenced
5658 * relations, and fix up deleted columns in JOIN RTEs. This ensures
5659 * consistent results. Note we assume it's OK to scribble on the passed
5660 * querytree!
5661 *
5662 * We are only deparsing the query (we are not about to execute it), so we
5663 * only need AccessShareLock on the relations it mentions.
5664 */
5665 AcquireRewriteLocks(query, false, false);
5666
5667 context.buf = buf;
5669 context.resultDesc = NULL;
5670 context.targetList = NIL;
5671 context.windowClause = NIL;
5672 context.varprefix = (parentnamespace != NIL ||
5673 rtable_size != 1);
5674 context.prettyFlags = prettyFlags;
5675 context.wrapColumn = wrapColumn;
5676 context.indentLevel = startIndent;
5677 context.colNamesVisible = colNamesVisible;
5678 context.inGroupBy = false;
5679 context.varInOrderBy = false;
5680 context.appendparents = NULL;
5681
5683
5684 switch (query->commandType)
5685 {
5686 case CMD_SELECT:
5687 /* We set context.resultDesc only if it's a SELECT */
5688 context.resultDesc = resultDesc;
5689 get_select_query_def(query, &context);
5690 break;
5691
5692 case CMD_UPDATE:
5693 get_update_query_def(query, &context);
5694 break;
5695
5696 case CMD_INSERT:
5697 get_insert_query_def(query, &context);
5698 break;
5699
5700 case CMD_DELETE:
5701 get_delete_query_def(query, &context);
5702 break;
5703
5704 case CMD_MERGE:
5705 get_merge_query_def(query, &context);
5706 break;
5707
5708 case CMD_NOTHING:
5709 appendStringInfoString(buf, "NOTHING");
5710 break;
5711
5712 case CMD_UTILITY:
5713 get_utility_query_def(query, &context);
5714 break;
5715
5716 default:
5717 elog(ERROR, "unrecognized query command type: %d",
5718 query->commandType);
5719 break;
5720 }
5721}

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

13729{
13730 deparse_context context;
13732 ListCell *cell;
13733 char *sep;
13734
13736 memset(&context, 0, sizeof(deparse_context));
13737 context.buf = &buf;
13738
13740 sep = "";
13741 foreach(cell, bound_datums)
13742 {
13743 PartitionRangeDatum *datum =
13745
13748 appendStringInfoString(&buf, "MINVALUE");
13749 else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
13750 appendStringInfoString(&buf, "MAXVALUE");
13751 else
13752 {
13753 Const *val = castNode(Const, datum->value);
13754
13755 get_const_expr(val, &context, -1);
13756 }
13757 sep = ", ";
13758 }
13760
13761 return buf.data;
13762}

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

13184{
13185 char *relname = get_rel_name(relid);
13186
13187 if (!relname)
13188 elog(ERROR, "cache lookup failed for relation %u", relid);
13189 return relname;
13190}

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

Referenced by get_rte_alias(), 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()

static void get_reloptions ( StringInfo  buf,
Datum  reloptions 
)
static

Definition at line 13640 of file ruleutils.c.

13641{
13642 Datum *options;
13643 int noptions;
13644 int i;
13645
13647 &options, NULL, &noptions);
13648
13649 for (i = 0; i < noptions; i++)
13650 {
13652 char *name;
13653 char *separator;
13654 char *value;
13655
13656 /*
13657 * Each array element should have the form name=value. If the "=" is
13658 * missing for some reason, treat it like an empty value.
13659 */
13660 name = option;
13661 separator = strchr(option, '=');
13662 if (separator)
13663 {
13664 *separator = '\0';
13665 value = separator + 1;
13666 }
13667 else
13668 value = "";
13669
13670 if (i > 0)
13673
13674 /*
13675 * In general we need to quote the value; but to avoid unnecessary
13676 * clutter, do not quote if it is an identifier that would not need
13677 * quoting. (We could also allow numbers, but that is a bit trickier
13678 * than it looks --- for example, are leading zeroes significant? We
13679 * don't want to assume very much here about what custom reloptions
13680 * might mean.)
13681 */
13684 else
13686
13687 pfree(option);
13688 }
13689}

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(), and pg_get_indexdef_worker().

◆ get_returning_clause()

static void get_returning_clause ( Query query,
deparse_context context 
)
static

Definition at line 6382 of file ruleutils.c.

6383{
6384 StringInfo buf = context->buf;
6385
6386 if (query->returningList)
6387 {
6388 bool have_with = false;
6389
6390 appendContextKeyword(context, " RETURNING",
6392
6393 /* Add WITH (OLD/NEW) options, if they're not the defaults */
6394 if (query->returningOldAlias && strcmp(query->returningOldAlias, "old") != 0)
6395 {
6396 appendStringInfo(buf, " WITH (OLD AS %s",
6397 quote_identifier(query->returningOldAlias));
6398 have_with = true;
6399 }
6400 if (query->returningNewAlias && strcmp(query->returningNewAlias, "new") != 0)
6401 {
6402 if (have_with)
6403 appendStringInfo(buf, ", NEW AS %s",
6404 quote_identifier(query->returningNewAlias));
6405 else
6406 {
6407 appendStringInfo(buf, " WITH (NEW AS %s",
6408 quote_identifier(query->returningNewAlias));
6409 have_with = true;
6410 }
6411 }
6412 if (have_with)
6414
6415 /* Add the returning expressions themselves */
6416 get_target_list(query->returningList, context);
6417 }
6418}

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

5138{
5140
5141 Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
5142 return (char *) list_nth(dpns->rtable_names, rtindex - 1);
5143}

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

12707{
12709 char *refname = get_rtable_name(varno, context);
12711 bool printalias = false;
12712
12713 if (rte->alias != NULL)
12714 {
12715 /* Always print alias if user provided one */
12716 printalias = true;
12717 }
12718 else if (colinfo->printaliases)
12719 {
12720 /* Always print alias if we need to print column aliases */
12721 printalias = true;
12722 }
12723 else if (rte->rtekind == RTE_RELATION)
12724 {
12725 /*
12726 * No need to print alias if it's same as relation name (this would
12727 * normally be the case, but not if set_rtable_names had to resolve a
12728 * conflict).
12729 */
12730 if (strcmp(refname, get_relation_name(rte->relid)) != 0)
12731 printalias = true;
12732 }
12733 else if (rte->rtekind == RTE_FUNCTION)
12734 {
12735 /*
12736 * For a function RTE, always print alias. This covers possible
12737 * renaming of the function and/or instability of the FigureColname
12738 * rules for things that aren't simple functions. Note we'd need to
12739 * force it anyway for the columndef list case.
12740 */
12741 printalias = true;
12742 }
12743 else if (rte->rtekind == RTE_SUBQUERY ||
12744 rte->rtekind == RTE_VALUES)
12745 {
12746 /*
12747 * For a subquery, always print alias. This makes the output
12748 * SQL-spec-compliant, even though we allow such aliases to be omitted
12749 * on input.
12750 */
12751 printalias = true;
12752 }
12753 else if (rte->rtekind == RTE_CTE)
12754 {
12755 /*
12756 * No need to print alias if it's same as CTE name (this would
12757 * normally be the case, but not if set_rtable_names had to resolve a
12758 * conflict).
12759 */
12760 if (strcmp(refname, rte->ctename) != 0)
12761 printalias = true;
12762 }
12763
12764 if (printalias)
12765 appendStringInfo(context->buf, "%s%s",
12766 use_as ? " AS " : " ",
12767 quote_identifier(refname));
12768}

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

9286{
9287 StringInfo buf = context->buf;
9288
9289 if (node == NULL)
9290 return;
9291
9292 /* Guard against excessively long or deeply-nested queries */
9295
9296 /*
9297 * Each level of get_rule_expr must emit an indivisible term
9298 * (parenthesized if necessary) to ensure result is reparsed into the same
9299 * expression tree. The only exception is that when the input is a List,
9300 * we emit the component items comma-separated with no surrounding
9301 * decoration; this is convenient for most callers.
9302 */
9303 switch (nodeTag(node))
9304 {
9305 case T_Var:
9306 (void) get_variable((Var *) node, 0, false, context);
9307 break;
9308
9309 case T_Const:
9310 get_const_expr((Const *) node, context, 0);
9311 break;
9312
9313 case T_Param:
9314 get_parameter((Param *) node, context);
9315 break;
9316
9317 case T_Aggref:
9318 get_agg_expr((Aggref *) node, context, (Aggref *) node);
9319 break;
9320
9321 case T_GroupingFunc:
9322 {
9323 GroupingFunc *gexpr = (GroupingFunc *) node;
9324
9325 appendStringInfoString(buf, "GROUPING(");
9326 get_rule_expr((Node *) gexpr->args, context, true);
9328 }
9329 break;
9330
9331 case T_WindowFunc:
9332 get_windowfunc_expr((WindowFunc *) node, context);
9333 break;
9334
9335 case T_MergeSupportFunc:
9336 appendStringInfoString(buf, "MERGE_ACTION()");
9337 break;
9338
9339 case T_SubscriptingRef:
9340 {
9341 SubscriptingRef *sbsref = (SubscriptingRef *) node;
9342 bool need_parens;
9343
9344 /*
9345 * If the argument is a CaseTestExpr, we must be inside a
9346 * FieldStore, ie, we are assigning to an element of an array
9347 * within a composite column. Since we already punted on
9348 * displaying the FieldStore's target information, just punt
9349 * here too, and display only the assignment source
9350 * expression.
9351 */
9352 if (IsA(sbsref->refexpr, CaseTestExpr))
9353 {
9354 Assert(sbsref->refassgnexpr);
9355 get_rule_expr((Node *) sbsref->refassgnexpr,
9356 context, showimplicit);
9357 break;
9358 }
9359
9360 /*
9361 * Parenthesize the argument unless it's a simple Var or a
9362 * FieldSelect. (In particular, if it's another
9363 * SubscriptingRef, we *must* parenthesize to avoid
9364 * confusion.)
9365 */
9366 need_parens = !IsA(sbsref->refexpr, Var) &&
9367 !IsA(sbsref->refexpr, FieldSelect);
9368 if (need_parens)
9370 get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
9371 if (need_parens)
9373
9374 /*
9375 * If there's a refassgnexpr, we want to print the node in the
9376 * format "container[subscripts] := refassgnexpr". This is
9377 * not legal SQL, so decompilation of INSERT or UPDATE
9378 * statements should always use processIndirection as part of
9379 * the statement-level syntax. We should only see this when
9380 * EXPLAIN tries to print the targetlist of a plan resulting
9381 * from such a statement.
9382 */
9383 if (sbsref->refassgnexpr)
9384 {
9385 Node *refassgnexpr;
9386
9387 /*
9388 * Use processIndirection to print this node's subscripts
9389 * as well as any additional field selections or
9390 * subscripting in immediate descendants. It returns the
9391 * RHS expr that is actually being "assigned".
9392 */
9393 refassgnexpr = processIndirection(node, context);
9394 appendStringInfoString(buf, " := ");
9395 get_rule_expr(refassgnexpr, context, showimplicit);
9396 }
9397 else
9398 {
9399 /* Just an ordinary container fetch, so print subscripts */
9400 printSubscripts(sbsref, context);
9401 }
9402 }
9403 break;
9404
9405 case T_FuncExpr:
9406 get_func_expr((FuncExpr *) node, context, showimplicit);
9407 break;
9408
9409 case T_NamedArgExpr:
9410 {
9411 NamedArgExpr *na = (NamedArgExpr *) node;
9412
9413 appendStringInfo(buf, "%s => ", quote_identifier(na->name));
9414 get_rule_expr((Node *) na->arg, context, showimplicit);
9415 }
9416 break;
9417
9418 case T_OpExpr:
9419 get_oper_expr((OpExpr *) node, context);
9420 break;
9421
9422 case T_DistinctExpr:
9423 {
9424 DistinctExpr *expr = (DistinctExpr *) node;
9425 List *args = expr->args;
9426 Node *arg1 = (Node *) linitial(args);
9427 Node *arg2 = (Node *) lsecond(args);
9428
9429 if (!PRETTY_PAREN(context))
9431 get_rule_expr_paren(arg1, context, true, node);
9432 appendStringInfoString(buf, " IS DISTINCT FROM ");
9433 get_rule_expr_paren(arg2, context, true, node);
9434 if (!PRETTY_PAREN(context))
9436 }
9437 break;
9438
9439 case T_NullIfExpr:
9440 {
9441 NullIfExpr *nullifexpr = (NullIfExpr *) node;
9442
9443 appendStringInfoString(buf, "NULLIF(");
9444 get_rule_expr((Node *) nullifexpr->args, context, true);
9446 }
9447 break;
9448
9450 {
9451 ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
9452 List *args = expr->args;
9453 Node *arg1 = (Node *) linitial(args);
9454 Node *arg2 = (Node *) lsecond(args);
9455
9456 if (!PRETTY_PAREN(context))
9458 get_rule_expr_paren(arg1, context, true, node);
9459 appendStringInfo(buf, " %s %s (",
9461 exprType(arg1),
9463 expr->useOr ? "ANY" : "ALL");
9464 get_rule_expr_paren(arg2, context, true, node);
9465
9466 /*
9467 * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
9468 * a bare sub-SELECT. Since we're here, the sub-SELECT must
9469 * be meant as a scalar sub-SELECT yielding an array value to
9470 * be used in ScalarArrayOpExpr; but the grammar will
9471 * preferentially interpret such a construct as an ANY/ALL
9472 * SubLink. To prevent misparsing the output that way, insert
9473 * a dummy coercion (which will be stripped by parse analysis,
9474 * so no inefficiency is added in dump and reload). This is
9475 * indeed most likely what the user wrote to get the construct
9476 * accepted in the first place.
9477 */
9478 if (IsA(arg2, SubLink) &&
9479 ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
9480 appendStringInfo(buf, "::%s",
9482 exprTypmod(arg2)));
9484 if (!PRETTY_PAREN(context))
9486 }
9487 break;
9488
9489 case T_BoolExpr:
9490 {
9491 BoolExpr *expr = (BoolExpr *) node;
9492 Node *first_arg = linitial(expr->args);
9493 ListCell *arg;
9494
9495 switch (expr->boolop)
9496 {
9497 case AND_EXPR:
9498 if (!PRETTY_PAREN(context))
9501 false, node);
9502 for_each_from(arg, expr->args, 1)
9503 {
9504 appendStringInfoString(buf, " AND ");
9505 get_rule_expr_paren((Node *) lfirst(arg), context,
9506 false, node);
9507 }
9508 if (!PRETTY_PAREN(context))
9510 break;
9511
9512 case OR_EXPR:
9513 if (!PRETTY_PAREN(context))
9516 false, node);
9517 for_each_from(arg, expr->args, 1)
9518 {
9519 appendStringInfoString(buf, " OR ");
9520 get_rule_expr_paren((Node *) lfirst(arg), context,
9521 false, node);
9522 }
9523 if (!PRETTY_PAREN(context))
9525 break;
9526
9527 case NOT_EXPR:
9528 if (!PRETTY_PAREN(context))
9530 appendStringInfoString(buf, "NOT ");
9532 false, node);
9533 if (!PRETTY_PAREN(context))
9535 break;
9536
9537 default:
9538 elog(ERROR, "unrecognized boolop: %d",
9539 (int) expr->boolop);
9540 }
9541 }
9542 break;
9543
9544 case T_SubLink:
9545 get_sublink_expr((SubLink *) node, context);
9546 break;
9547
9548 case T_SubPlan:
9549 {
9550 SubPlan *subplan = (SubPlan *) node;
9551
9552 /*
9553 * We cannot see an already-planned subplan in rule deparsing,
9554 * only while EXPLAINing a query plan. We don't try to
9555 * reconstruct the original SQL, just reference the subplan
9556 * that appears elsewhere in EXPLAIN's result. It does seem
9557 * useful to show the subLinkType and testexpr (if any), and
9558 * we also note whether the subplan will be hashed.
9559 */
9560 switch (subplan->subLinkType)
9561 {
9562 case EXISTS_SUBLINK:
9563 appendStringInfoString(buf, "EXISTS(");
9564 Assert(subplan->testexpr == NULL);
9565 break;
9566 case ALL_SUBLINK:
9567 appendStringInfoString(buf, "(ALL ");
9568 Assert(subplan->testexpr != NULL);
9569 break;
9570 case ANY_SUBLINK:
9571 appendStringInfoString(buf, "(ANY ");
9572 Assert(subplan->testexpr != NULL);
9573 break;
9574 case ROWCOMPARE_SUBLINK:
9575 /* Parenthesizing the testexpr seems sufficient */
9577 Assert(subplan->testexpr != NULL);
9578 break;
9579 case EXPR_SUBLINK:
9580 /* No need to decorate these subplan references */
9582 Assert(subplan->testexpr == NULL);
9583 break;
9584 case MULTIEXPR_SUBLINK:
9585 /* MULTIEXPR isn't executed in the normal way */
9586 appendStringInfoString(buf, "(rescan ");
9587 Assert(subplan->testexpr == NULL);
9588 break;
9589 case ARRAY_SUBLINK:
9590 appendStringInfoString(buf, "ARRAY(");
9591 Assert(subplan->testexpr == NULL);
9592 break;
9593 case CTE_SUBLINK:
9594 /* This case is unreachable within expressions */
9595 appendStringInfoString(buf, "CTE(");
9596 Assert(subplan->testexpr == NULL);
9597 break;
9598 }
9599
9600 if (subplan->testexpr != NULL)
9601 {
9603
9604 /*
9605 * Push SubPlan into ancestors list while deparsing
9606 * testexpr, so that we can handle PARAM_EXEC references
9607 * to the SubPlan's paramIds. (This makes it look like
9608 * the SubPlan is an "ancestor" of the current plan node,
9609 * which is a little weird, but it does no harm.) In this
9610 * path, we don't need to mention the SubPlan explicitly,
9611 * because the referencing Params will show its existence.
9612 */
9613 dpns = (deparse_namespace *) linitial(context->namespaces);
9614 dpns->ancestors = lcons(subplan, dpns->ancestors);
9615
9616 get_rule_expr(subplan->testexpr, context, showimplicit);
9618
9619 dpns->ancestors = list_delete_first(dpns->ancestors);
9620 }
9621 else
9622 {
9623 const char *nameprefix;
9624
9625 /* No referencing Params, so show the SubPlan's name */
9626 if (subplan->isInitPlan)
9627 nameprefix = "InitPlan ";
9628 else
9629 nameprefix = "SubPlan ";
9630 if (subplan->useHashTable)
9631 appendStringInfo(buf, "hashed %s%s)",
9632 nameprefix, subplan->plan_name);
9633 else
9634 appendStringInfo(buf, "%s%s)",
9635 nameprefix, subplan->plan_name);
9636 }
9637 }
9638 break;
9639
9641 {
9643 ListCell *lc;
9644
9645 /*
9646 * This case cannot be reached in normal usage, since no
9647 * AlternativeSubPlan can appear either in parsetrees or
9648 * finished plan trees. We keep it just in case somebody
9649 * wants to use this code to print planner data structures.
9650 */
9651 appendStringInfoString(buf, "(alternatives: ");
9652 foreach(lc, asplan->subplans)
9653 {
9654 SubPlan *splan = lfirst_node(SubPlan, lc);
9655 const char *nameprefix;
9656
9657 if (splan->isInitPlan)
9658 nameprefix = "InitPlan ";
9659 else
9660 nameprefix = "SubPlan ";
9661 if (splan->useHashTable)
9662 appendStringInfo(buf, "hashed %s%s", nameprefix,
9663 splan->plan_name);
9664 else
9666 splan->plan_name);
9667 if (lnext(asplan->subplans, lc))
9668 appendStringInfoString(buf, " or ");
9669 }
9671 }
9672 break;
9673
9674 case T_FieldSelect:
9675 {
9676 FieldSelect *fselect = (FieldSelect *) node;
9677 Node *arg = (Node *) fselect->arg;
9678 int fno = fselect->fieldnum;
9679 const char *fieldname;
9680 bool need_parens;
9681
9682 /*
9683 * Parenthesize the argument unless it's a SubscriptingRef or
9684 * another FieldSelect. Note in particular that it would be
9685 * WRONG to not parenthesize a Var argument; simplicity is not
9686 * the issue here, having the right number of names is.
9687 */
9689 !IsA(arg, FieldSelect);
9690 if (need_parens)
9692 get_rule_expr(arg, context, true);
9693 if (need_parens)
9695
9696 /*
9697 * Get and print the field name.
9698 */
9699 fieldname = get_name_for_var_field((Var *) arg, fno,
9700 0, context);
9701 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
9702 }
9703 break;
9704
9705 case T_FieldStore:
9706 {
9707 FieldStore *fstore = (FieldStore *) node;
9708 bool need_parens;
9709
9710 /*
9711 * There is no good way to represent a FieldStore as real SQL,
9712 * so decompilation of INSERT or UPDATE statements should
9713 * always use processIndirection as part of the
9714 * statement-level syntax. We should only get here when
9715 * EXPLAIN tries to print the targetlist of a plan resulting
9716 * from such a statement. The plan case is even harder than
9717 * ordinary rules would be, because the planner tries to
9718 * collapse multiple assignments to the same field or subfield
9719 * into one FieldStore; so we can see a list of target fields
9720 * not just one, and the arguments could be FieldStores
9721 * themselves. We don't bother to try to print the target
9722 * field names; we just print the source arguments, with a
9723 * ROW() around them if there's more than one. This isn't
9724 * terribly complete, but it's probably good enough for
9725 * EXPLAIN's purposes; especially since anything more would be
9726 * either hopelessly confusing or an even poorer
9727 * representation of what the plan is actually doing.
9728 */
9729 need_parens = (list_length(fstore->newvals) != 1);
9730 if (need_parens)
9731 appendStringInfoString(buf, "ROW(");
9732 get_rule_expr((Node *) fstore->newvals, context, showimplicit);
9733 if (need_parens)
9735 }
9736 break;
9737
9738 case T_RelabelType:
9739 {
9740 RelabelType *relabel = (RelabelType *) node;
9741 Node *arg = (Node *) relabel->arg;
9742
9743 if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
9744 !showimplicit)
9745 {
9746 /* don't show the implicit cast */
9747 get_rule_expr_paren(arg, context, false, node);
9748 }
9749 else
9750 {
9751 get_coercion_expr(arg, context,
9752 relabel->resulttype,
9753 relabel->resulttypmod,
9754 node);
9755 }
9756 }
9757 break;
9758
9759 case T_CoerceViaIO:
9760 {
9761 CoerceViaIO *iocoerce = (CoerceViaIO *) node;
9762 Node *arg = (Node *) iocoerce->arg;
9763
9764 if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
9765 !showimplicit)
9766 {
9767 /* don't show the implicit cast */
9768 get_rule_expr_paren(arg, context, false, node);
9769 }
9770 else
9771 {
9772 get_coercion_expr(arg, context,
9773 iocoerce->resulttype,
9774 -1,
9775 node);
9776 }
9777 }
9778 break;
9779
9780 case T_ArrayCoerceExpr:
9781 {
9783 Node *arg = (Node *) acoerce->arg;
9784
9785 if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
9786 !showimplicit)
9787 {
9788 /* don't show the implicit cast */
9789 get_rule_expr_paren(arg, context, false, node);
9790 }
9791 else
9792 {
9793 get_coercion_expr(arg, context,
9794 acoerce->resulttype,
9795 acoerce->resulttypmod,
9796 node);
9797 }
9798 }
9799 break;
9800
9802 {
9804 Node *arg = (Node *) convert->arg;
9805
9806 if (convert->convertformat == COERCE_IMPLICIT_CAST &&
9807 !showimplicit)
9808 {
9809 /* don't show the implicit cast */
9810 get_rule_expr_paren(arg, context, false, node);
9811 }
9812 else
9813 {
9814 get_coercion_expr(arg, context,
9815 convert->resulttype, -1,
9816 node);
9817 }
9818 }
9819 break;
9820
9821 case T_CollateExpr:
9822 {
9823 CollateExpr *collate = (CollateExpr *) node;
9824 Node *arg = (Node *) collate->arg;
9825
9826 if (!PRETTY_PAREN(context))
9828 get_rule_expr_paren(arg, context, showimplicit, node);
9829 appendStringInfo(buf, " COLLATE %s",
9830 generate_collation_name(collate->collOid));
9831 if (!PRETTY_PAREN(context))
9833 }
9834 break;
9835
9836 case T_CaseExpr:
9837 {
9838 CaseExpr *caseexpr = (CaseExpr *) node;
9839 ListCell *temp;
9840
9841 appendContextKeyword(context, "CASE",
9842 0, PRETTYINDENT_VAR, 0);
9843 if (caseexpr->arg)
9844 {
9846 get_rule_expr((Node *) caseexpr->arg, context, true);
9847 }
9848 foreach(temp, caseexpr->args)
9849 {
9851 Node *w = (Node *) when->expr;
9852
9853 if (caseexpr->arg)
9854 {
9855 /*
9856 * The parser should have produced WHEN clauses of the
9857 * form "CaseTestExpr = RHS", possibly with an
9858 * implicit coercion inserted above the CaseTestExpr.
9859 * For accurate decompilation of rules it's essential
9860 * that we show just the RHS. However in an
9861 * expression that's been through the optimizer, the
9862 * WHEN clause could be almost anything (since the
9863 * equality operator could have been expanded into an
9864 * inline function). If we don't recognize the form
9865 * of the WHEN clause, just punt and display it as-is.
9866 */
9867 if (IsA(w, OpExpr))
9868 {
9869 List *args = ((OpExpr *) w)->args;
9870
9871 if (list_length(args) == 2 &&
9873 CaseTestExpr))
9874 w = (Node *) lsecond(args);
9875 }
9876 }
9877
9878 if (!PRETTY_INDENT(context))
9880 appendContextKeyword(context, "WHEN ",
9881 0, 0, 0);
9882 get_rule_expr(w, context, false);
9883 appendStringInfoString(buf, " THEN ");
9884 get_rule_expr((Node *) when->result, context, true);
9885 }
9886 if (!PRETTY_INDENT(context))
9888 appendContextKeyword(context, "ELSE ",
9889 0, 0, 0);
9890 get_rule_expr((Node *) caseexpr->defresult, context, true);
9891 if (!PRETTY_INDENT(context))
9893 appendContextKeyword(context, "END",
9894 -PRETTYINDENT_VAR, 0, 0);
9895 }
9896 break;
9897
9898 case T_CaseTestExpr:
9899 {
9900 /*
9901 * Normally we should never get here, since for expressions
9902 * that can contain this node type we attempt to avoid
9903 * recursing to it. But in an optimized expression we might
9904 * be unable to avoid that (see comments for CaseExpr). If we
9905 * do see one, print it as CASE_TEST_EXPR.
9906 */
9907 appendStringInfoString(buf, "CASE_TEST_EXPR");
9908 }
9909 break;
9910
9911 case T_ArrayExpr:
9912 {
9913 ArrayExpr *arrayexpr = (ArrayExpr *) node;
9914
9915 appendStringInfoString(buf, "ARRAY[");
9916 get_rule_expr((Node *) arrayexpr->elements, context, true);
9918
9919 /*
9920 * If the array isn't empty, we assume its elements are
9921 * coerced to the desired type. If it's empty, though, we
9922 * need an explicit coercion to the array type.
9923 */
9924 if (arrayexpr->elements == NIL)
9925 appendStringInfo(buf, "::%s",
9926 format_type_with_typemod(arrayexpr->array_typeid, -1));
9927 }
9928 break;
9929
9930 case T_RowExpr:
9931 {
9932 RowExpr *rowexpr = (RowExpr *) node;
9933 TupleDesc tupdesc = NULL;
9934 ListCell *arg;
9935 int i;
9936 char *sep;
9937
9938 /*
9939 * If it's a named type and not RECORD, we may have to skip
9940 * dropped columns and/or claim there are NULLs for added
9941 * columns.
9942 */
9943 if (rowexpr->row_typeid != RECORDOID)
9944 {
9945 tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
9946 Assert(list_length(rowexpr->args) <= tupdesc->natts);
9947 }
9948
9949 /*
9950 * SQL99 allows "ROW" to be omitted when there is more than
9951 * one column, but for simplicity we always print it.
9952 */
9953 appendStringInfoString(buf, "ROW(");
9954 sep = "";
9955 i = 0;
9956 foreach(arg, rowexpr->args)
9957 {
9958 Node *e = (Node *) lfirst(arg);
9959
9960 if (tupdesc == NULL ||
9962 {
9964 /* Whole-row Vars need special treatment here */
9965 get_rule_expr_toplevel(e, context, true);
9966 sep = ", ";
9967 }
9968 i++;
9969 }
9970 if (tupdesc != NULL)
9971 {
9972 while (i < tupdesc->natts)
9973 {
9974 if (!TupleDescCompactAttr(tupdesc, i)->attisdropped)
9975 {
9977 appendStringInfoString(buf, "NULL");
9978 sep = ", ";
9979 }
9980 i++;
9981 }
9982
9983 ReleaseTupleDesc(tupdesc);
9984 }
9986 if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
9987 appendStringInfo(buf, "::%s",
9988 format_type_with_typemod(rowexpr->row_typeid, -1));
9989 }
9990 break;
9991
9992 case T_RowCompareExpr:
9993 {
9995
9996 /*
9997 * SQL99 allows "ROW" to be omitted when there is more than
9998 * one column, but for simplicity we always print it. Within
9999 * a ROW expression, whole-row Vars need special treatment, so
10000 * use get_rule_list_toplevel.
10001 */
10002 appendStringInfoString(buf, "(ROW(");
10003 get_rule_list_toplevel(rcexpr->largs, context, true);
10004
10005 /*
10006 * We assume that the name of the first-column operator will
10007 * do for all the rest too. This is definitely open to
10008 * failure, eg if some but not all operators were renamed
10009 * since the construct was parsed, but there seems no way to
10010 * be perfect.
10011 */
10012 appendStringInfo(buf, ") %s ROW(",
10014 exprType(linitial(rcexpr->largs)),
10015 exprType(linitial(rcexpr->rargs))));
10016 get_rule_list_toplevel(rcexpr->rargs, context, true);
10018 }
10019 break;
10020
10021 case T_CoalesceExpr:
10022 {
10024
10025 appendStringInfoString(buf, "COALESCE(");
10026 get_rule_expr((Node *) coalesceexpr->args, context, true);
10028 }
10029 break;
10030
10031 case T_MinMaxExpr:
10032 {
10033 MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
10034
10035 switch (minmaxexpr->op)
10036 {
10037 case IS_GREATEST:
10038 appendStringInfoString(buf, "GREATEST(");
10039 break;
10040 case IS_LEAST:
10041 appendStringInfoString(buf, "LEAST(");
10042 break;
10043 }
10044 get_rule_expr((Node *) minmaxexpr->args, context, true);
10046 }
10047 break;
10048
10049 case T_SQLValueFunction:
10050 {
10051 SQLValueFunction *svf = (SQLValueFunction *) node;
10052
10053 /*
10054 * Note: this code knows that typmod for time, timestamp, and
10055 * timestamptz just prints as integer.
10056 */
10057 switch (svf->op)
10058 {
10059 case SVFOP_CURRENT_DATE:
10060 appendStringInfoString(buf, "CURRENT_DATE");
10061 break;
10062 case SVFOP_CURRENT_TIME:
10063 appendStringInfoString(buf, "CURRENT_TIME");
10064 break;
10066 appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
10067 break;
10069 appendStringInfoString(buf, "CURRENT_TIMESTAMP");
10070 break;
10072 appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
10073 svf->typmod);
10074 break;
10075 case SVFOP_LOCALTIME:
10076 appendStringInfoString(buf, "LOCALTIME");
10077 break;
10078 case SVFOP_LOCALTIME_N:
10079 appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
10080 break;
10082 appendStringInfoString(buf, "LOCALTIMESTAMP");
10083 break;
10085 appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
10086 svf->typmod);
10087 break;
10088 case SVFOP_CURRENT_ROLE:
10089 appendStringInfoString(buf, "CURRENT_ROLE");
10090 break;
10091 case SVFOP_CURRENT_USER:
10092 appendStringInfoString(buf, "CURRENT_USER");
10093 break;
10094 case SVFOP_USER:
10095 appendStringInfoString(buf, "USER");
10096 break;
10097 case SVFOP_SESSION_USER:
10098 appendStringInfoString(buf, "SESSION_USER");
10099 break;
10101 appendStringInfoString(buf, "CURRENT_CATALOG");
10102 break;
10104 appendStringInfoString(buf, "CURRENT_SCHEMA");
10105 break;
10106 }
10107 }
10108 break;
10109
10110 case T_XmlExpr:
10111 {
10112 XmlExpr *xexpr = (XmlExpr *) node;
10113 bool needcomma = false;
10114 ListCell *arg;
10115 ListCell *narg;
10116 Const *con;
10117
10118 switch (xexpr->op)
10119 {
10120 case IS_XMLCONCAT:
10121 appendStringInfoString(buf, "XMLCONCAT(");
10122 break;
10123 case IS_XMLELEMENT:
10124 appendStringInfoString(buf, "XMLELEMENT(");
10125 break;
10126 case IS_XMLFOREST:
10127 appendStringInfoString(buf, "XMLFOREST(");
10128 break;
10129 case IS_XMLPARSE:
10130 appendStringInfoString(buf, "XMLPARSE(");
10131 break;
10132 case IS_XMLPI:
10133 appendStringInfoString(buf, "XMLPI(");
10134 break;
10135 case IS_XMLROOT:
10136 appendStringInfoString(buf, "XMLROOT(");
10137 break;
10138 case IS_XMLSERIALIZE:
10139 appendStringInfoString(buf, "XMLSERIALIZE(");
10140 break;
10141 case IS_DOCUMENT:
10142 break;
10143 }
10144 if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
10145 {
10146 if (xexpr->xmloption == XMLOPTION_DOCUMENT)
10147 appendStringInfoString(buf, "DOCUMENT ");
10148 else
10149 appendStringInfoString(buf, "CONTENT ");
10150 }
10151 if (xexpr->name)
10152 {
10153 appendStringInfo(buf, "NAME %s",
10155 needcomma = true;
10156 }
10157 if (xexpr->named_args)
10158 {
10159 if (xexpr->op != IS_XMLFOREST)
10160 {
10161 if (needcomma)
10163 appendStringInfoString(buf, "XMLATTRIBUTES(");
10164 needcomma = false;
10165 }
10166 forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
10167 {
10168 Node *e = (Node *) lfirst(arg);
10169 char *argname = strVal(lfirst(narg));
10170
10171 if (needcomma)
10173 get_rule_expr(e, context, true);
10174 appendStringInfo(buf, " AS %s",
10176 needcomma = true;
10177 }
10178 if (xexpr->op != IS_XMLFOREST)
10180 }
10181 if (xexpr->args)
10182 {
10183 if (needcomma)
10185 switch (xexpr->op)
10186 {
10187 case IS_XMLCONCAT:
10188 case IS_XMLELEMENT:
10189 case IS_XMLFOREST:
10190 case IS_XMLPI:
10191 case IS_XMLSERIALIZE:
10192 /* no extra decoration needed */
10193 get_rule_expr((Node *) xexpr->args, context, true);
10194 break;
10195 case IS_XMLPARSE:
10196 Assert(list_length(xexpr->args) == 2);
10197
10198 get_rule_expr((Node *) linitial(xexpr->args),
10199 context, true);
10200
10201 con = lsecond_node(Const, xexpr->args);
10202 Assert(!con->constisnull);
10203 if (DatumGetBool(con->constvalue))
10205 " PRESERVE WHITESPACE");
10206 else
10208 " STRIP WHITESPACE");
10209 break;
10210 case IS_XMLROOT:
10211 Assert(list_length(xexpr->args) == 3);
10212
10213 get_rule_expr((Node *) linitial(xexpr->args),
10214 context, true);
10215
10216 appendStringInfoString(buf, ", VERSION ");
10217 con = (Const *) lsecond(xexpr->args);
10218 if (IsA(con, Const) &&
10219 con->constisnull)
10220 appendStringInfoString(buf, "NO VALUE");
10221 else
10222 get_rule_expr((Node *) con, context, false);
10223
10224 con = lthird_node(Const, xexpr->args);
10225 if (con->constisnull)
10226 /* suppress STANDALONE NO VALUE */ ;
10227 else
10228 {
10229 switch (DatumGetInt32(con->constvalue))
10230 {
10231 case XML_STANDALONE_YES:
10233 ", STANDALONE YES");
10234 break;
10235 case XML_STANDALONE_NO:
10237 ", STANDALONE NO");
10238 break;
10241 ", STANDALONE NO VALUE");
10242 break;
10243 default:
10244 break;
10245 }
10246 }
10247 break;
10248 case IS_DOCUMENT:
10249 get_rule_expr_paren((Node *) xexpr->args, context, false, node);
10250 break;
10251 }
10252 }
10253 if (xexpr->op == IS_XMLSERIALIZE)
10254 {
10255 appendStringInfo(buf, " AS %s",
10256 format_type_with_typemod(xexpr->type,
10257 xexpr->typmod));
10258 if (xexpr->indent)
10259 appendStringInfoString(buf, " INDENT");
10260 else
10261 appendStringInfoString(buf, " NO INDENT");
10262 }
10263
10264 if (xexpr->op == IS_DOCUMENT)
10265 appendStringInfoString(buf, " IS DOCUMENT");
10266 else
10268 }
10269 break;
10270
10271 case T_NullTest:
10272 {
10273 NullTest *ntest = (NullTest *) node;
10274
10275 if (!PRETTY_PAREN(context))
10277 get_rule_expr_paren((Node *) ntest->arg, context, true, node);
10278
10279 /*
10280 * For scalar inputs, we prefer to print as IS [NOT] NULL,
10281 * which is shorter and traditional. If it's a rowtype input
10282 * but we're applying a scalar test, must print IS [NOT]
10283 * DISTINCT FROM NULL to be semantically correct.
10284 */
10285 if (ntest->argisrow ||
10286 !type_is_rowtype(exprType((Node *) ntest->arg)))
10287 {
10288 switch (ntest->nulltesttype)
10289 {
10290 case IS_NULL:
10291 appendStringInfoString(buf, " IS NULL");
10292 break;
10293 case IS_NOT_NULL:
10294 appendStringInfoString(buf, " IS NOT NULL");
10295 break;
10296 default:
10297 elog(ERROR, "unrecognized nulltesttype: %d",
10298 (int) ntest->nulltesttype);
10299 }
10300 }
10301 else
10302 {
10303 switch (ntest->nulltesttype)
10304 {
10305 case IS_NULL:
10306 appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
10307 break;
10308 case IS_NOT_NULL:
10309 appendStringInfoString(buf, " IS DISTINCT FROM NULL");
10310 break;
10311 default:
10312 elog(ERROR, "unrecognized nulltesttype: %d",
10313 (int) ntest->nulltesttype);
10314 }
10315 }
10316 if (!PRETTY_PAREN(context))
10318 }
10319 break;
10320
10321 case T_BooleanTest:
10322 {
10323 BooleanTest *btest = (BooleanTest *) node;
10324
10325 if (!PRETTY_PAREN(context))
10327 get_rule_expr_paren((Node *) btest->arg, context, false, node);
10328 switch (btest->booltesttype)
10329 {
10330 case IS_TRUE:
10331 appendStringInfoString(buf, " IS TRUE");
10332 break;
10333 case IS_NOT_TRUE:
10334 appendStringInfoString(buf, " IS NOT TRUE");
10335 break;
10336 case IS_FALSE:
10337 appendStringInfoString(buf, " IS FALSE");
10338 break;
10339 case IS_NOT_FALSE:
10340 appendStringInfoString(buf, " IS NOT FALSE");
10341 break;
10342 case IS_UNKNOWN:
10343 appendStringInfoString(buf, " IS UNKNOWN");
10344 break;
10345 case IS_NOT_UNKNOWN:
10346 appendStringInfoString(buf, " IS NOT UNKNOWN");
10347 break;
10348 default:
10349 elog(ERROR, "unrecognized booltesttype: %d",
10350 (int) btest->booltesttype);
10351 }
10352 if (!PRETTY_PAREN(context))
10354 }
10355 break;
10356
10357 case T_CoerceToDomain:
10358 {
10360 Node *arg = (Node *) ctest->arg;
10361
10362 if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
10363 !showimplicit)
10364 {
10365 /* don't show the implicit cast */
10366 get_rule_expr(arg, context, false);
10367 }
10368 else
10369 {
10370 get_coercion_expr(arg, context,
10371 ctest->resulttype,
10372 ctest->resulttypmod,
10373 node);
10374 }
10375 }
10376 break;
10377
10379 appendStringInfoString(buf, "VALUE");
10380 break;
10381
10382 case T_SetToDefault:
10383 appendStringInfoString(buf, "DEFAULT");
10384 break;
10385
10386 case T_CurrentOfExpr:
10387 {
10388 CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
10389
10390 if (cexpr->cursor_name)
10391 appendStringInfo(buf, "CURRENT OF %s",
10393 else
10394 appendStringInfo(buf, "CURRENT OF $%d",
10395 cexpr->cursor_param);
10396 }
10397 break;
10398
10399 case T_NextValueExpr:
10400 {
10402
10403 /*
10404 * This isn't exactly nextval(), but that seems close enough
10405 * for EXPLAIN's purposes.
10406 */
10407 appendStringInfoString(buf, "nextval(");
10410 NIL));
10412 }
10413 break;
10414
10415 case T_InferenceElem:
10416 {
10417 InferenceElem *iexpr = (InferenceElem *) node;
10418 bool save_varprefix;
10419 bool need_parens;
10420
10421 /*
10422 * InferenceElem can only refer to target relation, so a
10423 * prefix is not useful, and indeed would cause parse errors.
10424 */
10425 save_varprefix = context->varprefix;
10426 context->varprefix = false;
10427
10428 /*
10429 * Parenthesize the element unless it's a simple Var or a bare
10430 * function call. Follows pg_get_indexdef_worker().
10431 */
10432 need_parens = !IsA(iexpr->expr, Var);
10433 if (IsA(iexpr->expr, FuncExpr) &&
10434 ((FuncExpr *) iexpr->expr)->funcformat ==
10436 need_parens = false;
10437
10438 if (need_parens)
10440 get_rule_expr((Node *) iexpr->expr,
10441 context, false);
10442 if (need_parens)
10444
10445 context->varprefix = save_varprefix;
10446
10447 if (iexpr->infercollid)
10448 appendStringInfo(buf, " COLLATE %s",
10449 generate_collation_name(iexpr->infercollid));
10450
10451 /* Add the operator class name, if not default */
10452 if (iexpr->inferopclass)
10453 {
10454 Oid inferopclass = iexpr->inferopclass;
10456
10457 get_opclass_name(inferopclass, inferopcinputtype, buf);
10458 }
10459 }
10460 break;
10461
10462 case T_ReturningExpr:
10463 {
10465
10466 /*
10467 * We cannot see a ReturningExpr in rule deparsing, only while
10468 * EXPLAINing a query plan (ReturningExpr nodes are only ever
10469 * adding during query rewriting). Just display the expression
10470 * returned (an expanded view column).
10471 */
10472 get_rule_expr((Node *) retExpr->retexpr, context, showimplicit);
10473 }
10474 break;
10475
10477 {
10479 ListCell *cell;
10480 char *sep;
10481
10482 if (spec->is_default)
10483 {
10484 appendStringInfoString(buf, "DEFAULT");
10485 break;
10486 }
10487
10488 switch (spec->strategy)
10489 {
10491 Assert(spec->modulus > 0 && spec->remainder >= 0);
10492 Assert(spec->modulus > spec->remainder);
10493
10494 appendStringInfoString(buf, "FOR VALUES");
10495 appendStringInfo(buf, " WITH (modulus %d, remainder %d)",
10496 spec->modulus, spec->remainder);
10497 break;
10498
10500 Assert(spec->listdatums != NIL);
10501
10502 appendStringInfoString(buf, "FOR VALUES IN (");
10503 sep = "";
10504 foreach(cell, spec->listdatums)
10505 {
10506 Const *val = lfirst_node(Const, cell);
10507
10509 get_const_expr(val, context, -1);
10510 sep = ", ";
10511 }
10512
10514 break;
10515
10517 Assert(spec->lowerdatums != NIL &&
10518 spec->upperdatums != NIL &&
10519 list_length(spec->lowerdatums) ==
10520 list_length(spec->upperdatums));
10521
10522 appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
10523 get_range_partbound_string(spec->lowerdatums),
10524 get_range_partbound_string(spec->upperdatums));
10525 break;
10526
10527 default:
10528 elog(ERROR, "unrecognized partition strategy: %d",
10529 (int) spec->strategy);
10530 break;
10531 }
10532 }
10533 break;
10534
10535 case T_JsonValueExpr:
10536 {
10537 JsonValueExpr *jve = (JsonValueExpr *) node;
10538
10539 get_rule_expr((Node *) jve->raw_expr, context, false);
10540 get_json_format(jve->format, context->buf);
10541 }
10542 break;
10543
10545 get_json_constructor((JsonConstructorExpr *) node, context, false);
10546 break;
10547
10548 case T_JsonIsPredicate:
10549 {
10550 JsonIsPredicate *pred = (JsonIsPredicate *) node;
10551
10552 if (!PRETTY_PAREN(context))
10553 appendStringInfoChar(context->buf, '(');
10554
10555 get_rule_expr_paren(pred->expr, context, true, node);
10556
10557 appendStringInfoString(context->buf, " IS JSON");
10558
10559 /* TODO: handle FORMAT clause */
10560
10561 switch (pred->item_type)
10562 {
10563 case JS_TYPE_SCALAR:
10564 appendStringInfoString(context->buf, " SCALAR");
10565 break;
10566 case JS_TYPE_ARRAY:
10567 appendStringInfoString(context->buf, " ARRAY");
10568 break;
10569 case JS_TYPE_OBJECT:
10570 appendStringInfoString(context->buf, " OBJECT");
10571 break;
10572 default:
10573 break;
10574 }
10575
10576 if (pred->unique_keys)
10577 appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
10578
10579 if (!PRETTY_PAREN(context))
10580 appendStringInfoChar(context->buf, ')');
10581 }
10582 break;
10583
10584 case T_JsonExpr:
10585 {
10586 JsonExpr *jexpr = (JsonExpr *) node;
10587
10588 switch (jexpr->op)
10589 {
10590 case JSON_EXISTS_OP:
10591 appendStringInfoString(buf, "JSON_EXISTS(");
10592 break;
10593 case JSON_QUERY_OP:
10594 appendStringInfoString(buf, "JSON_QUERY(");
10595 break;
10596 case JSON_VALUE_OP:
10597 appendStringInfoString(buf, "JSON_VALUE(");
10598 break;
10599 default:
10600 elog(ERROR, "unrecognized JsonExpr op: %d",
10601 (int) jexpr->op);
10602 }
10603
10604 get_rule_expr(jexpr->formatted_expr, context, showimplicit);
10605
10607
10608 get_json_path_spec(jexpr->path_spec, context, showimplicit);
10609
10610 if (jexpr->passing_values)
10611 {
10612 ListCell *lc1,
10613 *lc2;
10614 bool needcomma = false;
10615
10616 appendStringInfoString(buf, " PASSING ");
10617
10618 forboth(lc1, jexpr->passing_names,
10619 lc2, jexpr->passing_values)
10620 {
10621 if (needcomma)
10623 needcomma = true;
10624
10625 get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
10626 appendStringInfo(buf, " AS %s",
10628 }
10629 }
10630
10631 if (jexpr->op != JSON_EXISTS_OP ||
10632 jexpr->returning->typid != BOOLOID)
10633 get_json_returning(jexpr->returning, context->buf,
10634 jexpr->op == JSON_QUERY_OP);
10635
10637 jexpr->op != JSON_EXISTS_OP ?
10640
10642 }
10643 break;
10644
10645 case T_List:
10646 {
10647 char *sep;
10648 ListCell *l;
10649
10650 sep = "";
10651 foreach(l, (List *) node)
10652 {
10654 get_rule_expr((Node *) lfirst(l), context, showimplicit);
10655 sep = ", ";
10656 }
10657 }
10658 break;
10659
10660 case T_TableFunc:
10661 get_tablefunc((TableFunc *) node, context, showimplicit);
10662 break;
10663
10664 default:
10665 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
10666 break;
10667 }
10668}

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_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_from_clause_item(), get_func_expr(), get_func_sql_syntax(), get_insert_query_def(), get_json_behavior(), get_json_constructor(), get_json_path_spec(), get_json_table(), get_merge_query_def(), get_parameter(), 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 10730 of file ruleutils.c.

10732{
10733 if (looks_like_function(node))
10734 get_rule_expr(node, context, showimplicit);
10735 else
10736 {
10737 StringInfo buf = context->buf;
10738
10739 appendStringInfoString(buf, "CAST(");
10740 /* no point in showing any top-level implicit cast */
10741 get_rule_expr(node, context, false);
10742 appendStringInfo(buf, " AS %s)",
10744 exprTypmod(node)));
10745 }
10746}

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

9189{
9190 bool need_paren;
9191
9192 need_paren = PRETTY_PAREN(context) &&
9193 !isSimpleNode(node, parentNode, context->prettyFlags);
9194
9195 if (need_paren)
9196 appendStringInfoChar(context->buf, '(');
9197
9198 get_rule_expr(node, context, showimplicit);
9199
9200 if (need_paren)
9201 appendStringInfoChar(context->buf, ')');
9202}

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

10684{
10685 if (node && IsA(node, Var))
10686 (void) get_variable((Var *) node, 0, true, context);
10687 else
10688 get_rule_expr(node, context, showimplicit);
10689}

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

6640{
6641 ListCell *l;
6642 StringInfo buf = context->buf;
6643 bool omit_child_parens = true;
6644 char *sep = "";
6645
6646 switch (gset->kind)
6647 {
6648 case GROUPING_SET_EMPTY:
6650 return;
6651
6653 {
6654 if (!omit_parens || list_length(gset->content) != 1)
6656
6657 foreach(l, gset->content)
6658 {
6659 Index ref = lfirst_int(l);
6660
6662 get_rule_sortgroupclause(ref, targetlist,
6663 false, context);
6664 sep = ", ";
6665 }
6666
6667 if (!omit_parens || list_length(gset->content) != 1)
6669 }
6670 return;
6671
6673 appendStringInfoString(buf, "ROLLUP(");
6674 break;
6675 case GROUPING_SET_CUBE:
6676 appendStringInfoString(buf, "CUBE(");
6677 break;
6678 case GROUPING_SET_SETS:
6679 appendStringInfoString(buf, "GROUPING SETS (");
6680 omit_child_parens = false;
6681 break;
6682 }
6683
6684 foreach(l, gset->content)
6685 {
6687 get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
6688 sep = ", ";
6689 }
6690
6692}

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

10702{
10703 const char *sep;
10704 ListCell *lc;
10705
10706 sep = "";
10707 foreach(lc, lst)
10708 {
10709 Node *e = (Node *) lfirst(lc);
10710
10711 appendStringInfoString(context->buf, sep);
10713 sep = ", ";
10714 }
10715}

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

6700{
6701 StringInfo buf = context->buf;
6702 const char *sep;
6703 ListCell *l;
6704
6705 sep = "";
6706 foreach(l, orderList)
6707 {
6709 Node *sortexpr;
6711 TypeCacheEntry *typentry;
6712
6714 sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
6715 force_colno, context);
6717 /* See whether operator is default < or > for datatype */
6718 typentry = lookup_type_cache(sortcoltype,
6720 if (srt->sortop == typentry->lt_opr)
6721 {
6722 /* ASC is default, so emit nothing for it */
6723 if (srt->nulls_first)
6724 appendStringInfoString(buf, " NULLS FIRST");
6725 }
6726 else if (srt->sortop == typentry->gt_opr)
6727 {
6728 appendStringInfoString(buf, " DESC");
6729 /* DESC defaults to NULLS FIRST */
6730 if (!srt->nulls_first)
6731 appendStringInfoString(buf, " NULLS LAST");
6732 }
6733 else
6734 {
6735 appendStringInfo(buf, " USING %s",
6738 sortcoltype));
6739 /* be specific to eliminate ambiguity */
6740 if (srt->nulls_first)
6741 appendStringInfoString(buf, " NULLS FIRST");
6742 else
6743 appendStringInfoString(buf, " NULLS LAST");
6744 }
6745 sep = ", ";
6746 }
6747}

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

6571{
6572 StringInfo buf = context->buf;
6574 Node *expr;
6575
6576 tle = get_sortgroupref_tle(ref, tlist);
6577 expr = (Node *) tle->expr;
6578
6579 /*
6580 * Use column-number form if requested by caller. Otherwise, if
6581 * expression is a constant, force it to be dumped with an explicit cast
6582 * as decoration --- this is because a simple integer constant is
6583 * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
6584 * we dump it without any decoration. Similarly, if it's just a Var,
6585 * there is risk of misinterpretation if the column name is reassigned in
6586 * the SELECT list, so we may need to force table qualification. And, if
6587 * it's anything more complex than a simple Var, then force extra parens
6588 * around it, to ensure it can't be misinterpreted as a cube() or rollup()
6589 * construct.
6590 */
6591 if (force_colno)
6592 {
6593 Assert(!tle->resjunk);
6594 appendStringInfo(buf, "%d", tle->resno);
6595 }
6596 else if (!expr)
6597 /* do nothing, probably can't happen */ ;
6598 else if (IsA(expr, Const))
6599 get_const_expr((Const *) expr, context, 1);
6600 else if (IsA(expr, Var))
6601 {
6602 /* Tell get_variable to check for name conflict */
6603 bool save_varinorderby = context->varInOrderBy;
6604
6605 context->varInOrderBy = true;
6606 (void) get_variable((Var *) expr, 0, false, context);
6608 }
6609 else
6610 {
6611 /*
6612 * We must force parens for function-like expressions even if
6613 * PRETTY_PAREN is off, since those are the ones in danger of
6614 * misparsing. For other expressions we need to force them only if
6615 * PRETTY_PAREN is on, since otherwise the expression will output them
6616 * itself. (We can't skip the parens.)
6617 */
6618 bool need_paren = (PRETTY_PAREN(context)
6619 || IsA(expr, FuncExpr)
6620 || IsA(expr, Aggref)
6621 || IsA(expr, WindowFunc)
6622 || IsA(expr, JsonConstructorExpr));
6623
6624 if (need_paren)
6625 appendStringInfoChar(context->buf, '(');
6626 get_rule_expr(expr, context, true);
6627 if (need_paren)
6628 appendStringInfoChar(context->buf, ')');
6629 }
6630
6631 return expr;
6632}

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

6757{
6758 StringInfo buf = context->buf;
6759 const char *sep;
6760 ListCell *l;
6761
6762 sep = NULL;
6763 foreach(l, query->windowClause)
6764 {
6765 WindowClause *wc = (WindowClause *) lfirst(l);
6766
6767 if (wc->name == NULL)
6768 continue; /* ignore anonymous windows */
6769
6770 if (sep == NULL)
6771 appendContextKeyword(context, " WINDOW ",
6773 else
6775
6776 appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
6777
6778 get_rule_windowspec(wc, query->targetList, context);
6779
6780 sep = ", ";
6781 }
6782}

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

6790{
6791 StringInfo buf = context->buf;
6792 bool needspace = false;
6793 const char *sep;
6794 ListCell *l;
6795
6797 if (wc->refname)
6798 {
6800 needspace = true;
6801 }
6802 /* partition clauses are always inherited, so only print if no refname */
6803 if (wc->partitionClause && !wc->refname)
6804 {
6805 if (needspace)
6807 appendStringInfoString(buf, "PARTITION BY ");
6808 sep = "";
6809 foreach(l, wc->partitionClause)
6810 {
6812
6814 get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
6815 false, context);
6816 sep = ", ";
6817 }
6818 needspace = true;
6819 }
6820 /* print ordering clause only if not inherited */
6821 if (wc->orderClause && !wc->copiedOrder)
6822 {
6823 if (needspace)
6825 appendStringInfoString(buf, "ORDER BY ");
6826 get_rule_orderby(wc->orderClause, targetList, false, context);
6827 needspace = true;
6828 }
6829 /* framing clause is never inherited, so print unless it's default */
6831 {
6832 if (needspace)
6835 wc->startOffset, wc->endOffset,
6836 context);
6837 }
6839}

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

5911{
5912 StringInfo buf = context->buf;
5913 bool force_colno;
5914 ListCell *l;
5915
5916 /* Insert the WITH clause if given */
5917 get_with_clause(query, context);
5918
5919 /* Subroutines may need to consult the SELECT targetlist and windowClause */
5920 context->targetList = query->targetList;
5921 context->windowClause = query->windowClause;
5922
5923 /*
5924 * If the Query node has a setOperations tree, then it's the top level of
5925 * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
5926 * fields are interesting in the top query itself.
5927 */
5928 if (query->setOperations)
5929 {
5930 get_setop_query(query->setOperations, query, context);
5931 /* ORDER BY clauses must be simple in this case */
5932 force_colno = true;
5933 }
5934 else
5935 {
5936 get_basic_select_query(query, context);
5937 force_colno = false;
5938 }
5939
5940 /* Add the ORDER BY clause if given */
5941 if (query->sortClause != NIL)
5942 {
5943 appendContextKeyword(context, " ORDER BY ",
5945 get_rule_orderby(query->sortClause, query->targetList,
5946 force_colno, context);
5947 }
5948
5949 /*
5950 * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
5951 * standard spelling of LIMIT.
5952 */
5953 if (query->limitOffset != NULL)
5954 {
5955 appendContextKeyword(context, " OFFSET ",
5957 get_rule_expr(query->limitOffset, context, false);
5958 }
5959 if (query->limitCount != NULL)
5960 {
5961 if (query->limitOption == LIMIT_OPTION_WITH_TIES)
5962 {
5963 /*
5964 * The limitCount arg is a c_expr, so it needs parens. Simple
5965 * literals and function expressions would not need parens, but
5966 * unfortunately it's hard to tell if the expression will be
5967 * printed as a simple literal like 123 or as a typecast
5968 * expression, like '-123'::int4. The grammar accepts the former
5969 * without quoting, but not the latter.
5970 */
5971 appendContextKeyword(context, " FETCH FIRST ",
5974 get_rule_expr(query->limitCount, context, false);
5976 appendStringInfoString(buf, " ROWS WITH TIES");
5977 }
5978 else
5979 {
5980 appendContextKeyword(context, " LIMIT ",
5982 if (IsA(query->limitCount, Const) &&
5983 ((Const *) query->limitCount)->constisnull)
5985 else
5986 get_rule_expr(query->limitCount, context, false);
5987 }
5988 }
5989
5990 /* Add FOR [KEY] UPDATE/SHARE clauses if present */
5991 if (query->hasForUpdate)
5992 {
5993 foreach(l, query->rowMarks)
5994 {
5995 RowMarkClause *rc = (RowMarkClause *) lfirst(l);
5996
5997 /* don't print implicit clauses */
5998 if (rc->pushedDown)
5999 continue;
6000
6001 appendContextKeyword(context,
6004
6005 appendStringInfo(buf, " OF %s",
6007 context)));
6008 if (rc->waitPolicy == LockWaitError)
6009 appendStringInfoString(buf, " NOWAIT");
6010 else if (rc->waitPolicy == LockWaitSkip)
6011 appendStringInfoString(buf, " SKIP LOCKED");
6012 }
6013 }
6014}

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

6422{
6423 StringInfo buf = context->buf;
6424 bool need_paren;
6425
6426 /* Guard against excessively long or deeply-nested queries */
6429
6430 if (IsA(setOp, RangeTblRef))
6431 {
6433 RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
6434 Query *subquery = rte->subquery;
6435
6436 Assert(subquery != NULL);
6437
6438 /*
6439 * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.
6440 * Also add parens if the leaf query contains its own set operations.
6441 * (That shouldn't happen unless one of the other clauses is also
6442 * present, see transformSetOperationTree; but let's be safe.)
6443 */
6444 need_paren = (subquery->cteList ||
6445 subquery->sortClause ||
6446 subquery->rowMarks ||
6447 subquery->limitOffset ||
6448 subquery->limitCount ||
6449 subquery->setOperations);
6450 if (need_paren)
6452 get_query_def(subquery, buf, context->namespaces,
6453 context->resultDesc, context->colNamesVisible,
6454 context->prettyFlags, context->wrapColumn,
6455 context->indentLevel);
6456 if (need_paren)
6458 }
6459 else if (IsA(setOp, SetOperationStmt))
6460 {
6462 int subindent;
6464
6465 /*
6466 * We force parens when nesting two SetOperationStmts, except when the
6467 * lefthand input is another setop of the same kind. Syntactically,
6468 * we could omit parens in rather more cases, but it seems best to use
6469 * parens to flag cases where the setop operator changes. If we use
6470 * parens, we also increase the indentation level for the child query.
6471 *
6472 * There are some cases in which parens are needed around a leaf query
6473 * too, but those are more easily handled at the next level down (see
6474 * code above).
6475 */
6476 if (IsA(op->larg, SetOperationStmt))
6477 {
6479
6480 if (op->op == lop->op && op->all == lop->all)
6481 need_paren = false;
6482 else
6483 need_paren = true;
6484 }
6485 else
6486 need_paren = false;
6487
6488 if (need_paren)
6489 {
6492 appendContextKeyword(context, "", subindent, 0, 0);
6493 }
6494 else
6495 subindent = 0;
6496
6497 get_setop_query(op->larg, query, context);
6498
6499 if (need_paren)
6500 appendContextKeyword(context, ") ", -subindent, 0, 0);
6501 else if (PRETTY_INDENT(context))
6502 appendContextKeyword(context, "", -subindent, 0, 0);
6503 else
6505
6506 switch (op->op)
6507 {
6508 case SETOP_UNION:
6509 appendStringInfoString(buf, "UNION ");
6510 break;
6511 case SETOP_INTERSECT:
6512 appendStringInfoString(buf, "INTERSECT ");
6513 break;
6514 case SETOP_EXCEPT:
6515 appendStringInfoString(buf, "EXCEPT ");
6516 break;
6517 default:
6518 elog(ERROR, "unrecognized set op: %d",
6519 (int) op->op);
6520 }
6521 if (op->all)
6522 appendStringInfoString(buf, "ALL ");
6523
6524 /* Always parenthesize if RHS is another setop */
6526
6527 /*
6528 * The indentation code here is deliberately a bit different from that
6529 * for the lefthand input, because we want the line breaks in
6530 * different places.
6531 */
6532 if (need_paren)
6533 {
6536 }
6537 else
6538 subindent = 0;
6539 appendContextKeyword(context, "", subindent, 0, 0);
6540
6541 /*
6542 * The output column names of the RHS sub-select don't matter.
6543 */
6545 context->colNamesVisible = false;
6546
6547 get_setop_query(op->rarg, query, context);
6548
6550
6551 if (PRETTY_INDENT(context))
6552 context->indentLevel -= subindent;
6553 if (need_paren)
6554 appendContextKeyword(context, ")", 0, 0, 0);
6555 }
6556 else
6557 {
6558 elog(ERROR, "unrecognized node type: %d",
6559 (int) nodeTag(setOp));
6560 }
6561}

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

8857{
8858 List *args = expr->args;
8859
8860 if (list_length(args) == 2)
8861 {
8862 /* binary operator */
8863 Node *arg1 = (Node *) linitial(args);
8864 Node *arg2 = (Node *) lsecond(args);
8865 const char *op;
8866
8868 if (strlen(op) == 1)
8869 return op;
8870 }
8871 return NULL;
8872}

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

6045{
6046 RangeTblEntry *result = NULL;
6047 ListCell *lc;
6048
6049 /*
6050 * We want to detect a match even if the Query also contains OLD or NEW
6051 * rule RTEs. So the idea is to scan the rtable and see if there is only
6052 * one inFromCl RTE that is a VALUES RTE.
6053 */
6054 foreach(lc, query->rtable)
6055 {
6057
6058 if (rte->rtekind == RTE_VALUES && rte->inFromCl)
6059 {
6060 if (result)
6061 return NULL; /* multiple VALUES (probably not possible) */
6062 result = rte;
6063 }
6064 else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
6065 continue; /* ignore rule entries */
6066 else
6067 return NULL; /* something else -> not simple VALUES */
6068 }
6069
6070 /*
6071 * We don't need to check the targetlist in any great detail, because
6072 * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
6073 * appear inside auto-generated sub-queries with very restricted
6074 * structure. However, DefineView might have modified the tlist by
6075 * injecting new column aliases, or we might have some other column
6076 * aliases forced by a resultDesc. We can only simplify if the RTE's
6077 * column names match the names that get_target_list() would select.
6078 */
6079 if (result)
6080 {
6081 ListCell *lcn;
6082 int colno;
6083
6084 if (list_length(query->targetList) != list_length(result->eref->colnames))
6085 return NULL; /* this probably cannot happen */
6086 colno = 0;
6087 forboth(lc, query->targetList, lcn, result->eref->colnames)
6088 {
6090 char *cname = strVal(lfirst(lcn));
6091 char *colname;
6092
6093 if (tle->resjunk)
6094 return NULL; /* this probably cannot happen */
6095
6096 /* compute name that get_target_list would use for column */
6097 colno++;
6098 if (resultDesc && colno <= resultDesc->natts)
6099 colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
6100 else
6101 colname = tle->resname;
6102
6103 /* does it match the VALUES RTE? */
6104 if (colname == NULL || strcmp(colname, cname) != 0)
6105 return NULL; /* column name has been changed */
6106 }
6107 }
6108
6109 return result;
6110}

References attname, fb(), forboth, lfirst, list_length(), NameStr, 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 7912 of file ruleutils.c.

7913{
7914 StringInfo buf = context->buf;
7915
7916 /*
7917 * For a non-Var referent, force parentheses because our caller probably
7918 * assumed a Var is a simple expression.
7919 */
7920 if (!IsA(node, Var))
7922 get_rule_expr(node, context, true);
7923 if (!IsA(node, Var))
7925}

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

11871{
11872 StringInfo buf = context->buf;
11873 Query *query = (Query *) (sublink->subselect);
11874 char *opname = NULL;
11875 bool need_paren;
11876
11877 if (sublink->subLinkType == ARRAY_SUBLINK)
11878 appendStringInfoString(buf, "ARRAY(");
11879 else
11881
11882 /*
11883 * Note that we print the name of only the first operator, when there are
11884 * multiple combining operators. This is an approximation that could go
11885 * wrong in various scenarios (operators in different schemas, renamed
11886 * operators, etc) but there is not a whole lot we can do about it, since
11887 * the syntax allows only one operator to be shown.
11888 */
11889 if (sublink->testexpr)
11890 {
11891 if (IsA(sublink->testexpr, OpExpr))
11892 {
11893 /* single combining operator */
11894 OpExpr *opexpr = (OpExpr *) sublink->testexpr;
11895
11896 get_rule_expr(linitial(opexpr->args), context, true);
11897 opname = generate_operator_name(opexpr->opno,
11898 exprType(linitial(opexpr->args)),
11899 exprType(lsecond(opexpr->args)));
11900 }
11901 else if (IsA(sublink->testexpr, BoolExpr))
11902 {
11903 /* multiple combining operators, = or <> cases */
11904 char *sep;
11905 ListCell *l;
11906
11908 sep = "";
11909 foreach(l, ((BoolExpr *) sublink->testexpr)->args)
11910 {
11911 OpExpr *opexpr = lfirst_node(OpExpr, l);
11912
11914 get_rule_expr(linitial(opexpr->args), context, true);
11915 if (!opname)
11917 exprType(linitial(opexpr->args)),
11918 exprType(lsecond(opexpr->args)));
11919 sep = ", ";
11920 }
11922 }
11923 else if (IsA(sublink->testexpr, RowCompareExpr))
11924 {
11925 /* multiple combining operators, < <= > >= cases */
11927
11929 get_rule_expr((Node *) rcexpr->largs, context, true);
11931 exprType(linitial(rcexpr->largs)),
11932 exprType(linitial(rcexpr->rargs)));
11934 }
11935 else
11936 elog(ERROR, "unrecognized testexpr type: %d",
11937 (int) nodeTag(sublink->testexpr));
11938 }
11939
11940 need_paren = true;
11941
11942 switch (sublink->subLinkType)
11943 {
11944 case EXISTS_SUBLINK:
11945 appendStringInfoString(buf, "EXISTS ");
11946 break;
11947
11948 case ANY_SUBLINK:
11949 if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
11950 appendStringInfoString(buf, " IN ");
11951 else
11952 appendStringInfo(buf, " %s ANY ", opname);
11953 break;
11954
11955 case ALL_SUBLINK:
11956 appendStringInfo(buf, " %s ALL ", opname);
11957 break;
11958
11959 case ROWCOMPARE_SUBLINK:
11960 appendStringInfo(buf, " %s ", opname);
11961 break;
11962
11963 case EXPR_SUBLINK:
11964 case MULTIEXPR_SUBLINK:
11965 case ARRAY_SUBLINK:
11966 need_paren = false;
11967 break;
11968
11969 case CTE_SUBLINK: /* shouldn't occur in a SubLink */
11970 default:
11971 elog(ERROR, "unrecognized sublink type: %d",
11972 (int) sublink->subLinkType);
11973 break;
11974 }
11975
11976 if (need_paren)
11978
11979 get_query_def(query, buf, context->namespaces, NULL, false,
11980 context->prettyFlags, context->wrapColumn,
11981 context->indentLevel);
11982
11983 if (need_paren)
11985 else
11987}

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

12302{
12303 /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12304
12305 if (tf->functype == TFT_XMLTABLE)
12306 get_xmltable(tf, context, showimplicit);
12307 else if (tf->functype == TFT_JSON_TABLE)
12308 get_json_table(tf, context, showimplicit);
12309}

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

12868{
12869 StringInfo buf = context->buf;
12870 Oid argtypes[1];
12871 int nargs;
12872 ListCell *l;
12873
12874 /*
12875 * We should qualify the handler's function name if it wouldn't be
12876 * resolved by lookup in the current search path.
12877 */
12878 argtypes[0] = INTERNALOID;
12879 appendStringInfo(buf, " TABLESAMPLE %s (",
12880 generate_function_name(tablesample->tsmhandler, 1,
12881 NIL, argtypes,
12882 false, NULL, false));
12883
12884 nargs = 0;
12885 foreach(l, tablesample->args)
12886 {
12887 if (nargs++ > 0)
12889 get_rule_expr((Node *) lfirst(l), context, false);
12890 }
12892
12893 if (tablesample->repeatable != NULL)
12894 {
12895 appendStringInfoString(buf, " REPEATABLE (");
12896 get_rule_expr((Node *) tablesample->repeatable, context, false);
12898 }
12899}

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

6247{
6248 StringInfo buf = context->buf;
6250 bool last_was_multiline = false;
6251 char *sep;
6252 int colno;
6253 ListCell *l;
6254
6255 /* we use targetbuf to hold each TLE's text temporarily */
6257
6258 sep = " ";
6259 colno = 0;
6260 foreach(l, targetList)
6261 {
6263 char *colname;
6264 char *attname;
6265
6266 if (tle->resjunk)
6267 continue; /* ignore junk entries */
6268
6270 sep = ", ";
6271 colno++;
6272
6273 /*
6274 * Put the new field text into targetbuf so we can decide after we've
6275 * got it whether or not it needs to go on a new line.
6276 */
6278 context->buf = &targetbuf;
6279
6280 /*
6281 * We special-case Var nodes rather than using get_rule_expr. This is
6282 * needed because get_rule_expr will display a whole-row Var as
6283 * "foo.*", which is the preferred notation in most contexts, but at
6284 * the top level of a SELECT list it's not right (the parser will
6285 * expand that notation into multiple columns, yielding behavior
6286 * different from a whole-row Var). We need to call get_variable
6287 * directly so that we can tell it to do the right thing, and so that
6288 * we can get the attribute name which is the default AS label.
6289 */
6290 if (tle->expr && (IsA(tle->expr, Var)))
6291 {
6292 attname = get_variable((Var *) tle->expr, 0, true, context);
6293 }
6294 else
6295 {
6296 get_rule_expr((Node *) tle->expr, context, true);
6297
6298 /*
6299 * When colNamesVisible is true, we should always show the
6300 * assigned column name explicitly. Otherwise, show it only if
6301 * it's not FigureColname's fallback.
6302 */
6303 attname = context->colNamesVisible ? NULL : "?column?";
6304 }
6305
6306 /*
6307 * Figure out what the result column should be called. In the context
6308 * of a view, use the view's tuple descriptor (so as to pick up the
6309 * effects of any column RENAME that's been done on the view).
6310 * Otherwise, just use what we can find in the TLE.
6311 */
6312 if (context->resultDesc && colno <= context->resultDesc->natts)
6313 colname = NameStr(TupleDescAttr(context->resultDesc,
6314 colno - 1)->attname);
6315 else
6316 colname = tle->resname;
6317
6318 /* Show AS unless the column's name is correct as-is */
6319 if (colname) /* resname could be NULL */
6320 {
6321 if (attname == NULL || strcmp(attname, colname) != 0)
6322 appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
6323 }
6324
6325 /* Restore context's output buffer */
6326 context->buf = buf;
6327
6328 /* Consider line-wrapping if enabled */
6329 if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
6330 {
6331 int leading_nl_pos;
6332
6333 /* Does the new field start with a new line? */
6334 if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
6335 leading_nl_pos = 0;
6336 else
6337 leading_nl_pos = -1;
6338
6339 /* If so, we shouldn't add anything */
6340 if (leading_nl_pos >= 0)
6341 {
6342 /* instead, remove any trailing spaces currently in buf */
6344 }
6345 else
6346 {
6347 char *trailing_nl;
6348
6349 /* Locate the start of the current line in the output buffer */
6350 trailing_nl = strrchr(buf->data, '\n');
6351 if (trailing_nl == NULL)
6352 trailing_nl = buf->data;
6353 else
6354 trailing_nl++;
6355
6356 /*
6357 * Add a newline, plus some indentation, if the new field is
6358 * not the first and either the new field would cause an
6359 * overflow or the last field used more than one line.
6360 */
6361 if (colno > 1 &&
6362 ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
6366 }
6367
6368 /* Remember this field's multiline status for next iteration */
6370 (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
6371 }
6372
6373 /* Add the new field */
6375 }
6376
6377 /* clean up */
6378 pfree(targetbuf.data);
6379}

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

7176{
7177 StringInfo buf = context->buf;
7179
7180 /* Insert the WITH clause if given */
7181 get_with_clause(query, context);
7182
7183 /*
7184 * Start the query with UPDATE relname SET
7185 */
7186 rte = rt_fetch(query->resultRelation, query->rtable);
7187 Assert(rte->rtekind == RTE_RELATION);
7188 if (PRETTY_INDENT(context))
7189 {
7191 context->indentLevel += PRETTYINDENT_STD;
7192 }
7193 appendStringInfo(buf, "UPDATE %s%s",
7195 generate_relation_name(rte->relid, NIL));
7196
7197 /* Print the relation alias, if needed */
7198 get_rte_alias(rte, query->resultRelation, false, context);
7199
7200 appendStringInfoString(buf, " SET ");
7201
7202 /* Deparse targetlist */
7203 get_update_query_targetlist_def(query, query->targetList, context, rte);
7204
7205 /* Add the FROM clause if needed */
7206 get_from_clause(query, " FROM ", context);
7207
7208 /* Add a WHERE clause if given */
7209 if (query->jointree->quals != NULL)
7210 {
7211 appendContextKeyword(context, " WHERE ",
7213 get_rule_expr(query->jointree->quals, context, false);
7214 }
7215
7216 /* Add RETURNING if present */
7217 if (query->returningList)
7218 get_returning_clause(query, context);
7219}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, fb(), generate_relation_name(), 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 7227 of file ruleutils.c.

7229{
7230 StringInfo buf = context->buf;
7231 ListCell *l;
7234 const char *sep;
7237
7238 /*
7239 * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
7240 * into a list. We expect them to appear, in ID order, in resjunk tlist
7241 * entries.
7242 */
7243 ma_sublinks = NIL;
7244 if (query->hasSubLinks) /* else there can't be any */
7245 {
7246 foreach(l, targetList)
7247 {
7249
7250 if (tle->resjunk && IsA(tle->expr, SubLink))
7251 {
7252 SubLink *sl = (SubLink *) tle->expr;
7253
7254 if (sl->subLinkType == MULTIEXPR_SUBLINK)
7255 {
7257 Assert(sl->subLinkId == list_length(ma_sublinks));
7258 }
7259 }
7260 }
7261 }
7265
7266 /* Add the comma separated list of 'attname = value' */
7267 sep = "";
7268 foreach(l, targetList)
7269 {
7271 Node *expr;
7272
7273 if (tle->resjunk)
7274 continue; /* ignore junk entries */
7275
7276 /* Emit separator (OK whether we're in multiassignment or not) */
7278 sep = ", ";
7279
7280 /*
7281 * Check to see if we're starting a multiassignment group: if so,
7282 * output a left paren.
7283 */
7284 if (next_ma_cell != NULL && cur_ma_sublink == NULL)
7285 {
7286 /*
7287 * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
7288 * Param. That could be buried under FieldStores and
7289 * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
7290 * and underneath those there could be an implicit type coercion.
7291 * Because we would ignore implicit type coercions anyway, we
7292 * don't need to be as careful as processIndirection() is about
7293 * descending past implicit CoerceToDomains.
7294 */
7295 expr = (Node *) tle->expr;
7296 while (expr)
7297 {
7298 if (IsA(expr, FieldStore))
7299 {
7300 FieldStore *fstore = (FieldStore *) expr;
7301
7302 expr = (Node *) linitial(fstore->newvals);
7303 }
7304 else if (IsA(expr, SubscriptingRef))
7305 {
7306 SubscriptingRef *sbsref = (SubscriptingRef *) expr;
7307
7308 if (sbsref->refassgnexpr == NULL)
7309 break;
7310
7311 expr = (Node *) sbsref->refassgnexpr;
7312 }
7313 else if (IsA(expr, CoerceToDomain))
7314 {
7316
7317 if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
7318 break;
7319 expr = (Node *) cdomain->arg;
7320 }
7321 else
7322 break;
7323 }
7324 expr = strip_implicit_coercions(expr);
7325
7326 if (expr && IsA(expr, Param) &&
7327 ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
7328 {
7332 Assert(((Param *) expr)->paramid ==
7333 ((cur_ma_sublink->subLinkId << 16) | 1));
7335 }
7336 }
7337
7338 /*
7339 * Put out name of target column; look in the catalogs, not at
7340 * tle->resname, since resname will fail to track RENAME.
7341 */
7344 tle->resno,
7345 false)));
7346
7347 /*
7348 * Print any indirection needed (subfields or subscripts), and strip
7349 * off the top-level nodes representing the indirection assignments.
7350 */
7351 expr = processIndirection((Node *) tle->expr, context);
7352
7353 /*
7354 * If we're in a multiassignment, skip printing anything more, unless
7355 * this is the last column; in which case, what we print should be the
7356 * sublink, not the Param.
7357 */
7358 if (cur_ma_sublink != NULL)
7359 {
7360 if (--remaining_ma_columns > 0)
7361 continue; /* not the last column of multiassignment */
7363 expr = (Node *) cur_ma_sublink;
7365 }
7366
7368
7369 get_rule_expr(expr, context, false);
7370 }
7371}

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

7586{
7587 StringInfo buf = context->buf;
7588
7589 if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7590 {
7591 NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7592
7593 appendContextKeyword(context, "",
7594 0, PRETTYINDENT_STD, 1);
7595 appendStringInfo(buf, "NOTIFY %s",
7596 quote_identifier(stmt->conditionname));
7597 if (stmt->payload)
7598 {
7600 simple_quote_literal(buf, stmt->payload);
7601 }
7602 }
7603 else
7604 {
7605 /* Currently only NOTIFY utility commands can appear in rules */
7606 elog(ERROR, "unexpected utility statement type");
7607 }
7608}

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

5729{
5730 StringInfo buf = context->buf;
5731 bool first_list = true;
5732 ListCell *vtl;
5733
5734 appendStringInfoString(buf, "VALUES ");
5735
5736 foreach(vtl, values_lists)
5737 {
5738 List *sublist = (List *) lfirst(vtl);
5739 bool first_col = true;
5740 ListCell *lc;
5741
5742 if (first_list)
5743 first_list = false;
5744 else
5746
5748 foreach(lc, sublist)
5749 {
5750 Node *col = (Node *) lfirst(lc);
5751
5752 if (first_col)
5753 first_col = false;
5754 else
5756
5757 /*
5758 * Print the value. Whole-row Vars need special treatment.
5759 */
5760 get_rule_expr_toplevel(col, context, false);
5761 }
5763 }
5764}

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

7631{
7632 StringInfo buf = context->buf;
7635 int netlevelsup;
7637 int varno;
7638 AttrNumber varattno;
7640 char *refname;
7641 char *attname;
7642 bool need_prefix;
7643
7644 /* Find appropriate nesting depth */
7645 netlevelsup = var->varlevelsup + levelsup;
7646 if (netlevelsup >= list_length(context->namespaces))
7647 elog(ERROR, "bogus varlevelsup: %d offset %d",
7648 var->varlevelsup, levelsup);
7650 netlevelsup);
7651
7652 /*
7653 * If we have a syntactic referent for the Var, and we're working from a
7654 * parse tree, prefer to use the syntactic referent. Otherwise, fall back
7655 * on the semantic referent. (Forcing use of the semantic referent when
7656 * printing plan trees is a design choice that's perhaps more motivated by
7657 * backwards compatibility than anything else. But it does have the
7658 * advantage of making plans more explicit.)
7659 */
7660 if (var->varnosyn > 0 && dpns->plan == NULL)
7661 {
7662 varno = var->varnosyn;
7663 varattno = var->varattnosyn;
7664 }
7665 else
7666 {
7667 varno = var->varno;
7668 varattno = var->varattno;
7669 }
7670
7671 /*
7672 * Try to find the relevant RTE in this rtable. In a plan tree, it's
7673 * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
7674 * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
7675 * find the aliases previously assigned for this RTE.
7676 */
7677 if (varno >= 1 && varno <= list_length(dpns->rtable))
7678 {
7679 /*
7680 * We might have been asked to map child Vars to some parent relation.
7681 */
7682 if (context->appendparents && dpns->appendrels)
7683 {
7684 int pvarno = varno;
7685 AttrNumber pvarattno = varattno;
7686 AppendRelInfo *appinfo = dpns->appendrels[pvarno];
7687 bool found = false;
7688
7689 /* Only map up to inheritance parents, not UNION ALL appendrels */
7690 while (appinfo &&
7691 rt_fetch(appinfo->parent_relid,
7692 dpns->rtable)->rtekind == RTE_RELATION)
7693 {
7694 found = false;
7695 if (pvarattno > 0) /* system columns stay as-is */
7696 {
7697 if (pvarattno > appinfo->num_child_cols)
7698 break; /* safety check */
7699 pvarattno = appinfo->parent_colnos[pvarattno - 1];
7700 if (pvarattno == 0)
7701 break; /* Var is local to child */
7702 }
7703
7705 found = true;
7706
7707 /* If the parent is itself a child, continue up. */
7708 Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
7709 appinfo = dpns->appendrels[pvarno];
7710 }
7711
7712 /*
7713 * If we found an ancestral rel, and that rel is included in
7714 * appendparents, print that column not the original one.
7715 */
7716 if (found && bms_is_member(pvarno, context->appendparents))
7717 {
7718 varno = pvarno;
7719 varattno = pvarattno;
7720 }
7721 }
7722
7723 rte = rt_fetch(varno, dpns->rtable);
7724
7725 /* might be returning old/new column value */
7727 refname = dpns->ret_old_alias;
7728 else if (var->varreturningtype == VAR_RETURNING_NEW)
7729 refname = dpns->ret_new_alias;
7730 else
7731 refname = (char *) list_nth(dpns->rtable_names, varno - 1);
7732
7734 attnum = varattno;
7735 }
7736 else
7737 {
7738 resolve_special_varno((Node *) var, context,
7740 return NULL;
7741 }
7742
7743 /*
7744 * The planner will sometimes emit Vars referencing resjunk elements of a
7745 * subquery's target list (this is currently only possible if it chooses
7746 * to generate a "physical tlist" for a SubqueryScan or CteScan node).
7747 * Although we prefer to print subquery-referencing Vars using the
7748 * subquery's alias, that's not possible for resjunk items since they have
7749 * no alias. So in that case, drill down to the subplan and print the
7750 * contents of the referenced tlist item. This works because in a plan
7751 * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
7752 * we'll have set dpns->inner_plan to reference the child plan node.
7753 */
7754 if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
7755 attnum > list_length(rte->eref->colnames) &&
7756 dpns->inner_plan)
7757 {
7760
7761 tle = get_tle_by_resno(dpns->inner_tlist, attnum);
7762 if (!tle)
7763 elog(ERROR, "invalid attnum %d for relation \"%s\"",
7764 attnum, rte->eref->aliasname);
7765
7766 Assert(netlevelsup == 0);
7767 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7768
7769 /*
7770 * Force parentheses because our caller probably assumed a Var is a
7771 * simple expression.
7772 */
7773 if (!IsA(tle->expr, Var))
7775 get_rule_expr((Node *) tle->expr, context, true);
7776 if (!IsA(tle->expr, Var))
7778
7780 return NULL;
7781 }
7782
7783 /*
7784 * If it's an unnamed join, look at the expansion of the alias variable.
7785 * If it's a simple reference to one of the input vars, then recursively
7786 * print the name of that var instead. When it's not a simple reference,
7787 * we have to just print the unqualified join column name. (This can only
7788 * happen with "dangerous" merged columns in a JOIN USING; we took pains
7789 * previously to make the unqualified column name unique in such cases.)
7790 *
7791 * This wouldn't work in decompiling plan trees, because we don't store
7792 * joinaliasvars lists after planning; but a plan tree should never
7793 * contain a join alias variable.
7794 */
7795 if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
7796 {
7797 if (rte->joinaliasvars == NIL)
7798 elog(ERROR, "cannot decompile join alias var in plan tree");
7799 if (attnum > 0)
7800 {
7801 Var *aliasvar;
7802
7803 aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
7804 /* we intentionally don't strip implicit coercions here */
7805 if (aliasvar && IsA(aliasvar, Var))
7806 {
7807 return get_variable(aliasvar, var->varlevelsup + levelsup,
7808 istoplevel, context);
7809 }
7810 }
7811
7812 /*
7813 * Unnamed join has no refname. (Note: since it's unnamed, there is
7814 * no way the user could have referenced it to create a whole-row Var
7815 * for it. So we don't have to cover that case below.)
7816 */
7817 Assert(refname == NULL);
7818 }
7819
7821 attname = NULL;
7822 else if (attnum > 0)
7823 {
7824 /* Get column name to use from the colinfo struct */
7825 if (attnum > colinfo->num_cols)
7826 elog(ERROR, "invalid attnum %d for relation \"%s\"",
7827 attnum, rte->eref->aliasname);
7828 attname = colinfo->colnames[attnum - 1];
7829
7830 /*
7831 * If we find a Var referencing a dropped column, it seems better to
7832 * print something (anything) than to fail. In general this should
7833 * not happen, but it used to be possible for some cases involving
7834 * functions returning named composite types, and perhaps there are
7835 * still bugs out there.
7836 */
7837 if (attname == NULL)
7838 attname = "?dropped?column?";
7839 }
7840 else
7841 {
7842 /* System column - name is fixed, get it from the catalog */
7844 }
7845
7846 need_prefix = (context->varprefix || attname == NULL ||
7848
7849 /*
7850 * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
7851 * clause, we may need to add a table-name prefix to prevent
7852 * findTargetlistEntrySQL92 from misinterpreting the name as an
7853 * output-column name. To avoid cluttering the output with unnecessary
7854 * prefixes, do so only if there is a name match to a SELECT tlist item
7855 * that is different from the Var.
7856 */
7857 if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
7858 {
7859 int colno = 0;
7860
7862 {
7863 char *colname;
7864
7865 if (tle->resjunk)
7866 continue; /* ignore junk entries */
7867 colno++;
7868
7869 /* This must match colname-choosing logic in get_target_list() */
7870 if (context->resultDesc && colno <= context->resultDesc->natts)
7871 colname = NameStr(TupleDescAttr(context->resultDesc,
7872 colno - 1)->attname);
7873 else
7874 colname = tle->resname;
7875
7876 if (colname && strcmp(colname, attname) == 0 &&
7877 !equal(var, tle->expr))
7878 {
7879 need_prefix = true;
7880 break;
7881 }
7882 }
7883 }
7884
7885 if (refname && need_prefix)
7886 {
7889 }
7890 if (attname)
7892 else
7893 {
7895 if (istoplevel)
7896 appendStringInfo(buf, "::%s",
7897 format_type_with_typemod(var->vartype,
7898 var->vartypmod));
7899 }
7900
7901 return attname;
7902}

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

6848{
6849 StringInfo buf = context->buf;
6850
6851 if (frameOptions & FRAMEOPTION_NONDEFAULT)
6852 {
6853 if (frameOptions & FRAMEOPTION_RANGE)
6854 appendStringInfoString(buf, "RANGE ");
6855 else if (frameOptions & FRAMEOPTION_ROWS)
6856 appendStringInfoString(buf, "ROWS ");
6857 else if (frameOptions & FRAMEOPTION_GROUPS)
6858 appendStringInfoString(buf, "GROUPS ");
6859 else
6860 Assert(false);
6861 if (frameOptions & FRAMEOPTION_BETWEEN)
6862 appendStringInfoString(buf, "BETWEEN ");
6863 if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
6864 appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
6865 else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
6866 appendStringInfoString(buf, "CURRENT ROW ");
6867 else if (frameOptions & FRAMEOPTION_START_OFFSET)
6868 {
6869 get_rule_expr(startOffset, context, false);
6870 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
6871 appendStringInfoString(buf, " PRECEDING ");
6872 else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
6873 appendStringInfoString(buf, " FOLLOWING ");
6874 else
6875 Assert(false);
6876 }
6877 else
6878 Assert(false);
6879 if (frameOptions & FRAMEOPTION_BETWEEN)
6880 {
6881 appendStringInfoString(buf, "AND ");
6882 if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
6883 appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
6884 else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
6885 appendStringInfoString(buf, "CURRENT ROW ");
6886 else if (frameOptions & FRAMEOPTION_END_OFFSET)
6887 {
6888 get_rule_expr(endOffset, context, false);
6889 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
6890 appendStringInfoString(buf, " PRECEDING ");
6891 else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
6892 appendStringInfoString(buf, " FOLLOWING ");
6893 else
6894 Assert(false);
6895 }
6896 else
6897 Assert(false);
6898 }
6899 if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
6900 appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
6901 else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
6902 appendStringInfoString(buf, "EXCLUDE GROUP ");
6903 else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
6904 appendStringInfoString(buf, "EXCLUDE TIES ");
6905 /* we will now have a trailing space; remove it */
6906 buf->data[--(buf->len)] = '\0';
6907 }
6908}

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

6917{
6919 deparse_context context;
6920
6922 context.buf = &buf;
6923 context.namespaces = dpcontext;
6924 context.resultDesc = NULL;
6925 context.targetList = NIL;
6926 context.windowClause = NIL;
6927 context.varprefix = forceprefix;
6928 context.prettyFlags = 0;
6930 context.indentLevel = 0;
6931 context.colNamesVisible = true;
6932 context.inGroupBy = false;
6933 context.varInOrderBy = false;
6934 context.appendparents = NULL;
6935
6936 get_window_frame_options(frameOptions, startOffset, endOffset, &context);
6937
6938 return buf.data;
6939}

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

11073{
11074 get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
11075}

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

11086{
11087 StringInfo buf = context->buf;
11088 Oid argtypes[FUNC_MAX_ARGS];
11089 int nargs;
11090 List *argnames;
11091 ListCell *l;
11092
11093 if (list_length(wfunc->args) > FUNC_MAX_ARGS)
11094 ereport(ERROR,
11096 errmsg("too many arguments")));
11097 nargs = 0;
11098 argnames = NIL;
11099 foreach(l, wfunc->args)
11100 {
11101 Node *arg = (Node *) lfirst(l);
11102
11103 if (IsA(arg, NamedArgExpr))
11104 argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
11105 argtypes[nargs] = exprType(arg);
11106 nargs++;
11107 }
11108
11109 if (!funcname)
11110 funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
11111 argtypes, false, NULL,
11112 context->inGroupBy);
11113
11114 appendStringInfo(buf, "%s(", funcname);
11115
11116 /* winstar can be set only in zero-argument aggregates */
11117 if (wfunc->winstar)
11119 else
11120 {
11122 {
11123 get_rule_expr((Node *) linitial(wfunc->args), context, false);
11125 get_rule_expr((Node *) lsecond(wfunc->args), context, false);
11126 }
11127 else
11128 get_rule_expr((Node *) wfunc->args, context, true);
11129 }
11130
11131 if (options)
11133
11134 if (wfunc->aggfilter != NULL)
11135 {
11136 appendStringInfoString(buf, ") FILTER (WHERE ");
11137 get_rule_expr((Node *) wfunc->aggfilter, context, false);
11138 }
11139
11141
11142 if (wfunc->ignore_nulls == PARSER_IGNORE_NULLS)
11143 appendStringInfoString(buf, "IGNORE NULLS ");
11144
11145 appendStringInfoString(buf, "OVER ");
11146
11147 if (context->windowClause)
11148 {
11149 /* Query-decompilation case: search the windowClause list */
11150 foreach(l, context->windowClause)
11151 {
11152 WindowClause *wc = (WindowClause *) lfirst(l);
11153
11154 if (wc->winref == wfunc->winref)
11155 {
11156 if (wc->name)
11158 else
11159 get_rule_windowspec(wc, context->targetList, context);
11160 break;
11161 }
11162 }
11163 if (l == NULL)
11164 elog(ERROR, "could not find window clause for winref %u",
11165 wfunc->winref);
11166 }
11167 else
11168 {
11169 /*
11170 * In EXPLAIN, search the namespace stack for a matching WindowAgg
11171 * node (probably it's always the first entry), and print winname.
11172 */
11173 foreach(l, context->namespaces)
11174 {
11176
11177 if (dpns->plan && IsA(dpns->plan, WindowAgg))
11178 {
11180
11181 if (wagg->winref == wfunc->winref)
11182 {
11184 break;
11185 }
11186 }
11187 }
11188 if (l == NULL)
11189 elog(ERROR, "could not find window clause for winref %u",
11190 wfunc->winref);
11191 }
11192}

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

5772{
5773 StringInfo buf = context->buf;
5774 const char *sep;
5775 ListCell *l;
5776
5777 if (query->cteList == NIL)
5778 return;
5779
5780 if (PRETTY_INDENT(context))
5781 {
5782 context->indentLevel += PRETTYINDENT_STD;
5784 }
5785
5786 if (query->hasRecursive)
5787 sep = "WITH RECURSIVE ";
5788 else
5789 sep = "WITH ";
5790 foreach(l, query->cteList)
5791 {
5793
5796 if (cte->aliascolnames)
5797 {
5798 bool first = true;
5799 ListCell *col;
5800
5802 foreach(col, cte->aliascolnames)
5803 {
5804 if (first)
5805 first = false;
5806 else
5810 }
5812 }
5813 appendStringInfoString(buf, " AS ");
5814 switch (cte->ctematerialized)
5815 {
5817 break;
5819 appendStringInfoString(buf, "MATERIALIZED ");
5820 break;
5822 appendStringInfoString(buf, "NOT MATERIALIZED ");
5823 break;
5824 }
5826 if (PRETTY_INDENT(context))
5827 appendContextKeyword(context, "", 0, 0, 0);
5828 get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
5829 true,
5830 context->prettyFlags, context->wrapColumn,
5831 context->indentLevel);
5832 if (PRETTY_INDENT(context))
5833 appendContextKeyword(context, "", 0, 0, 0);
5835
5836 if (cte->search_clause)
5837 {
5838 bool first = true;
5839 ListCell *lc;
5840
5841 appendStringInfo(buf, " SEARCH %s FIRST BY ",
5842 cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
5843
5844 foreach(lc, cte->search_clause->search_col_list)
5845 {
5846 if (first)
5847 first = false;
5848 else
5852 }
5853
5854 appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
5855 }
5856
5857 if (cte->cycle_clause)
5858 {
5859 bool first = true;
5860 ListCell *lc;
5861
5862 appendStringInfoString(buf, " CYCLE ");
5863
5864 foreach(lc, cte->cycle_clause->cycle_col_list)
5865 {
5866 if (first)
5867 first = false;
5868 else
5872 }
5873
5874 appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
5875
5876 {
5877 Const *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
5878 Const *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
5879
5880 if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
5881 cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
5882 {
5883 appendStringInfoString(buf, " TO ");
5884 get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
5885 appendStringInfoString(buf, " DEFAULT ");
5886 get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
5887 }
5888 }
5889
5890 appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
5891 }
5892
5893 sep = ", ";
5894 }
5895
5896 if (PRETTY_INDENT(context))
5897 {
5898 context->indentLevel -= PRETTYINDENT_STD;
5899 appendContextKeyword(context, "", 0, 0, 0);
5900 }
5901 else
5903}

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

11996{
11997 StringInfo buf = context->buf;
11998
11999 appendStringInfoString(buf, "XMLTABLE(");
12000
12001 if (tf->ns_uris != NIL)
12002 {
12003 ListCell *lc1,
12004 *lc2;
12005 bool first = true;
12006
12007 appendStringInfoString(buf, "XMLNAMESPACES (");
12008 forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
12009 {
12010 Node *expr = (Node *) lfirst(lc1);
12012
12013 if (!first)
12015 else
12016 first = false;
12017
12018 if (ns_node != NULL)
12019 {
12020 get_rule_expr(expr, context, showimplicit);
12021 appendStringInfo(buf, " AS %s",
12023 }
12024 else
12025 {
12026 appendStringInfoString(buf, "DEFAULT ");
12027 get_rule_expr(expr, context, showimplicit);
12028 }
12029 }
12031 }
12032
12034 get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
12035 appendStringInfoString(buf, ") PASSING (");
12036 get_rule_expr((Node *) tf->docexpr, context, showimplicit);
12038
12039 if (tf->colexprs != NIL)
12040 {
12041 ListCell *l1;
12042 ListCell *l2;
12043 ListCell *l3;
12044 ListCell *l4;
12045 ListCell *l5;
12046 int colnum = 0;
12047
12048 appendStringInfoString(buf, " COLUMNS ");
12049 forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
12050 l4, tf->colexprs, l5, tf->coldefexprs)
12051 {
12052 char *colname = strVal(lfirst(l1));
12053 Oid typid = lfirst_oid(l2);
12054 int32 typmod = lfirst_int(l3);
12055 Node *colexpr = (Node *) lfirst(l4);
12056 Node *coldefexpr = (Node *) lfirst(l5);
12057 bool ordinality = (tf->ordinalitycol == colnum);
12058 bool notnull = bms_is_member(colnum, tf->notnulls);
12059
12060 if (colnum > 0)
12062 colnum++;
12063
12064 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12065 ordinality ? "FOR ORDINALITY" :
12066 format_type_with_typemod(typid, typmod));
12067 if (ordinality)
12068 continue;
12069
12070 if (coldefexpr != NULL)
12071 {
12072 appendStringInfoString(buf, " DEFAULT (");
12073 get_rule_expr((Node *) coldefexpr, context, showimplicit);
12075 }
12076 if (colexpr != NULL)
12077 {
12078 appendStringInfoString(buf, " PATH (");
12079 get_rule_expr((Node *) colexpr, context, showimplicit);
12081 }
12082 if (notnull)
12083 appendStringInfoString(buf, " NOT NULL");
12084 }
12085 }
12086
12088}

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

4145{
4146 if (IsA(jtnode, RangeTblRef))
4147 {
4148 /* nothing to do here */
4149 }
4150 else if (IsA(jtnode, FromExpr))
4151 {
4152 FromExpr *f = (FromExpr *) jtnode;
4153 ListCell *lc;
4154
4155 foreach(lc, f->fromlist)
4156 {
4158 return true;
4159 }
4160 }
4161 else if (IsA(jtnode, JoinExpr))
4162 {
4163 JoinExpr *j = (JoinExpr *) jtnode;
4164
4165 /* Is it an unnamed JOIN with USING? */
4166 if (j->alias == NULL && j->usingClause)
4167 {
4168 /*
4169 * Yes, so check each join alias var to see if any of them are not
4170 * simple references to underlying columns. If so, we have a
4171 * dangerous situation and must pick unique aliases.
4172 */
4173 RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
4174
4175 /* We need only examine the merged columns */
4176 for (int i = 0; i < jrte->joinmergedcols; i++)
4177 {
4178 Node *aliasvar = list_nth(jrte->joinaliasvars, i);
4179
4180 if (!IsA(aliasvar, Var))
4181 return true;
4182 }
4183 }
4184
4185 /* Nope, but inspect children */
4186 if (has_dangerous_join_using(dpns, j->larg))
4187 return true;
4188 if (has_dangerous_join_using(dpns, j->rarg))
4189 return true;
4190 }
4191 else
4192 elog(ERROR, "unrecognized node type: %d",
4193 (int) nodeTag(jtnode));
4194 return false;
4195}

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

5071{
5072 int numjoincols;
5073 int jcolno;
5074 int rcolno;
5075 ListCell *lc;
5076
5077 /* Extract left/right child RT indexes */
5078 if (IsA(j->larg, RangeTblRef))
5079 colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
5080 else if (IsA(j->larg, JoinExpr))
5081 colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
5082 else
5083 elog(ERROR, "unrecognized node type in jointree: %d",
5084 (int) nodeTag(j->larg));
5085 if (IsA(j->rarg, RangeTblRef))
5086 colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
5087 else if (IsA(j->rarg, JoinExpr))
5088 colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
5089 else
5090 elog(ERROR, "unrecognized node type in jointree: %d",
5091 (int) nodeTag(j->rarg));
5092
5093 /* Assert children will be processed earlier than join in second pass */
5094 Assert(colinfo->leftrti < j->rtindex);
5095 Assert(colinfo->rightrti < j->rtindex);
5096
5097 /* Initialize result arrays with zeroes */
5098 numjoincols = list_length(jrte->joinaliasvars);
5099 Assert(numjoincols == list_length(jrte->eref->colnames));
5100 colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
5101 colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
5102
5103 /*
5104 * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
5105 * Recall that the column(s) merged due to USING are the first column(s)
5106 * of the join output. We need not do anything special while scanning
5107 * joinleftcols, but while scanning joinrightcols we must distinguish
5108 * merged from unmerged columns.
5109 */
5110 jcolno = 0;
5111 foreach(lc, jrte->joinleftcols)
5112 {
5113 int leftattno = lfirst_int(lc);
5114
5115 colinfo->leftattnos[jcolno++] = leftattno;
5116 }
5117 rcolno = 0;
5118 foreach(lc, jrte->joinrightcols)
5119 {
5120 int rightattno = lfirst_int(lc);
5121
5122 if (rcolno < jrte->joinmergedcols) /* merged column? */
5123 colinfo->rightattnos[rcolno] = rightattno;
5124 else
5125 colinfo->rightattnos[jcolno++] = rightattno;
5126 rcolno++;
5127 }
5129}

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

3451{
3452 return (!argmodes
3456}

References fb().

Referenced by pg_get_function_arg_default().

◆ isSimpleNode()

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

Definition at line 8882 of file ruleutils.c.

8883{
8884 if (!node)
8885 return false;
8886
8887 switch (nodeTag(node))
8888 {
8889 case T_Var:
8890 case T_Const:
8891 case T_Param:
8893 case T_SetToDefault:
8894 case T_CurrentOfExpr:
8895 /* single words: always simple */
8896 return true;
8897
8898 case T_SubscriptingRef:
8899 case T_ArrayExpr:
8900 case T_RowExpr:
8901 case T_CoalesceExpr:
8902 case T_MinMaxExpr:
8903 case T_SQLValueFunction:
8904 case T_XmlExpr:
8905 case T_NextValueExpr:
8906 case T_NullIfExpr:
8907 case T_Aggref:
8908 case T_GroupingFunc:
8909 case T_WindowFunc:
8910 case T_MergeSupportFunc:
8911 case T_FuncExpr:
8913 case T_JsonExpr:
8914 /* function-like: name(..) or name[..] */
8915 return true;
8916
8917 /* CASE keywords act as parentheses */
8918 case T_CaseExpr:
8919 return true;
8920
8921 case T_FieldSelect:
8922
8923 /*
8924 * appears simple since . has top precedence, unless parent is
8925 * T_FieldSelect itself!
8926 */
8927 return !IsA(parentNode, FieldSelect);
8928
8929 case T_FieldStore:
8930
8931 /*
8932 * treat like FieldSelect (probably doesn't matter)
8933 */
8934 return !IsA(parentNode, FieldStore);
8935
8936 case T_CoerceToDomain:
8937 /* maybe simple, check args */
8938 return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
8939 node, prettyFlags);
8940 case T_RelabelType:
8941 return isSimpleNode((Node *) ((RelabelType *) node)->arg,
8942 node, prettyFlags);
8943 case T_CoerceViaIO:
8944 return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
8945 node, prettyFlags);
8946 case T_ArrayCoerceExpr:
8947 return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
8948 node, prettyFlags);
8950 return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
8951 node, prettyFlags);
8952 case T_ReturningExpr:
8953 return isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr,
8954 node, prettyFlags);
8955
8956 case T_OpExpr:
8957 {
8958 /* depends on parent node type; needs further checking */
8959 if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
8960 {
8961 const char *op;
8962 const char *parentOp;
8963 bool is_lopriop;
8964 bool is_hipriop;
8965 bool is_lopriparent;
8966 bool is_hipriparent;
8967
8968 op = get_simple_binary_op_name((OpExpr *) node);
8969 if (!op)
8970 return false;
8971
8972 /* We know only the basic operators + - and * / % */
8973 is_lopriop = (strchr("+-", *op) != NULL);
8974 is_hipriop = (strchr("*/%", *op) != NULL);
8975 if (!(is_lopriop || is_hipriop))
8976 return false;
8977
8979 if (!parentOp)
8980 return false;
8981
8982 is_lopriparent = (strchr("+-", *parentOp) != NULL);
8983 is_hipriparent = (strchr("*/%", *parentOp) != NULL);
8985 return false;
8986
8988 return true; /* op binds tighter than parent */
8989
8991 return false;
8992
8993 /*
8994 * Operators are same priority --- can skip parens only if
8995 * we have (a - b) - c, not a - (b - c).
8996 */
8997 if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
8998 return true;
8999
9000 return false;
9001 }
9002 /* else do the same stuff as for T_SubLink et al. */
9003 }
9005
9006 case T_SubLink:
9007 case T_NullTest:
9008 case T_BooleanTest:
9009 case T_DistinctExpr:
9010 case T_JsonIsPredicate:
9011 switch (nodeTag(parentNode))
9012 {
9013 case T_FuncExpr:
9014 {
9015 /* special handling for casts and COERCE_SQL_SYNTAX */
9016 CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9017
9018 if (type == COERCE_EXPLICIT_CAST ||
9021 return false;
9022 return true; /* own parentheses */
9023 }
9024 case T_BoolExpr: /* lower precedence */
9025 case T_SubscriptingRef: /* other separators */
9026 case T_ArrayExpr: /* other separators */
9027 case T_RowExpr: /* other separators */
9028 case T_CoalesceExpr: /* own parentheses */
9029 case T_MinMaxExpr: /* own parentheses */
9030 case T_XmlExpr: /* own parentheses */
9031 case T_NullIfExpr: /* other separators */
9032 case T_Aggref: /* own parentheses */
9033 case T_GroupingFunc: /* own parentheses */
9034 case T_WindowFunc: /* own parentheses */
9035 case T_CaseExpr: /* other separators */
9036 return true;
9037 default:
9038 return false;
9039 }
9040
9041 case T_BoolExpr:
9042 switch (nodeTag(parentNode))
9043 {
9044 case T_BoolExpr:
9045 if (prettyFlags & PRETTYFLAG_PAREN)
9046 {
9049
9050 type = ((BoolExpr *) node)->boolop;
9051 parentType = ((BoolExpr *) parentNode)->boolop;
9052 switch (type)
9053 {
9054 case NOT_EXPR:
9055 case AND_EXPR:
9057 return true;
9058 break;
9059 case OR_EXPR:
9060 if (parentType == OR_EXPR)
9061 return true;
9062 break;
9063 }
9064 }
9065 return false;
9066 case T_FuncExpr:
9067 {
9068 /* special handling for casts and COERCE_SQL_SYNTAX */
9069 CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
9070
9071 if (type == COERCE_EXPLICIT_CAST ||
9074 return false;
9075 return true; /* own parentheses */
9076 }
9077 case T_SubscriptingRef: /* other separators */
9078 case T_ArrayExpr: /* other separators */
9079 case T_RowExpr: /* other separators */
9080 case T_CoalesceExpr: /* own parentheses */
9081 case T_MinMaxExpr: /* own parentheses */
9082 case T_XmlExpr: /* own parentheses */
9083 case T_NullIfExpr: /* other separators */
9084 case T_Aggref: /* own parentheses */
9085 case T_GroupingFunc: /* own parentheses */
9086 case T_WindowFunc: /* own parentheses */
9087 case T_CaseExpr: /* other separators */
9088 case T_JsonExpr: /* own parentheses */
9089 return true;
9090 default:
9091 return false;
9092 }
9093
9094 case T_JsonValueExpr:
9095 /* maybe simple, check args */
9096 return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
9097 node, prettyFlags);
9098
9099 default:
9100 break;
9101 }
9102 /* those we don't know: in dubio complexo */
9103 return false;
9104}

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

◆ looks_like_function()

static bool looks_like_function ( Node node)
static

Definition at line 10753 of file ruleutils.c.

10754{
10755 if (node == NULL)
10756 return false; /* probably shouldn't happen */
10757 switch (nodeTag(node))
10758 {
10759 case T_FuncExpr:
10760 /* OK, unless it's going to deparse as a cast */
10761 return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
10762 ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
10763 case T_NullIfExpr:
10764 case T_CoalesceExpr:
10765 case T_MinMaxExpr:
10766 case T_SQLValueFunction:
10767 case T_XmlExpr:
10768 case T_JsonExpr:
10769 /* these are all accepted by func_expr_common_subexpr */
10770 return true;
10771 default:
10772 break;
10773 }
10774 return false;
10775}

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

4929{
4930 /*
4931 * If the selected name isn't unique, append digits to make it so. For a
4932 * very long input name, we might have to truncate to stay within
4933 * NAMEDATALEN.
4934 */
4935 if (!colname_is_unique(colname, dpns, colinfo))
4936 {
4937 int colnamelen = strlen(colname);
4938 char *modname = (char *) palloc(colnamelen + 16);
4939 int i = 0;
4940
4941 do
4942 {
4943 i++;
4944 for (;;)
4945 {
4946 memcpy(modname, colname, colnamelen);
4947 sprintf(modname + colnamelen, "_%d", i);
4948 if (strlen(modname) < NAMEDATALEN)
4949 break;
4950 /* drop chars from colname to keep all the digits */
4952 colnamelen - 1);
4953 }
4954 } while (!colname_is_unique(modname, dpns, colinfo));
4955 colname = modname;
4956 }
4957 return colname;
4958}

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

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

◆ make_ruledef()

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

Definition at line 5351 of file ruleutils.c.

5353{
5354 char *rulename;
5355 char ev_type;
5356 Oid ev_class;
5357 bool is_instead;
5358 char *ev_qual;
5359 char *ev_action;
5360 List *actions;
5363 int fno;
5364 Datum dat;
5365 bool isnull;
5366
5367 /*
5368 * Get the attribute values from the rules tuple
5369 */
5370 fno = SPI_fnumber(rulettc, "rulename");
5371 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5372 Assert(!isnull);
5373 rulename = NameStr(*(DatumGetName(dat)));
5374
5375 fno = SPI_fnumber(rulettc, "ev_type");
5376 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5377 Assert(!isnull);
5378 ev_type = DatumGetChar(dat);
5379
5380 fno = SPI_fnumber(rulettc, "ev_class");
5381 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5382 Assert(!isnull);
5384
5385 fno = SPI_fnumber(rulettc, "is_instead");
5386 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5387 Assert(!isnull);
5388 is_instead = DatumGetBool(dat);
5389
5390 fno = SPI_fnumber(rulettc, "ev_qual");
5392 Assert(ev_qual != NULL);
5393
5394 fno = SPI_fnumber(rulettc, "ev_action");
5396 Assert(ev_action != NULL);
5397 actions = (List *) stringToNode(ev_action);
5398 if (actions == NIL)
5399 elog(ERROR, "invalid empty ev_action list");
5400
5402
5403 /*
5404 * Build the rules definition text
5405 */
5406 appendStringInfo(buf, "CREATE RULE %s AS",
5407 quote_identifier(rulename));
5408
5409 if (prettyFlags & PRETTYFLAG_INDENT)
5410 appendStringInfoString(buf, "\n ON ");
5411 else
5412 appendStringInfoString(buf, " ON ");
5413
5414 /* The event the rule is fired for */
5415 switch (ev_type)
5416 {
5417 case '1':
5418 appendStringInfoString(buf, "SELECT");
5420 break;
5421
5422 case '2':
5423 appendStringInfoString(buf, "UPDATE");
5424 break;
5425
5426 case '3':
5427 appendStringInfoString(buf, "INSERT");
5428 break;
5429
5430 case '4':
5431 appendStringInfoString(buf, "DELETE");
5432 break;
5433
5434 default:
5435 ereport(ERROR,
5437 errmsg("rule \"%s\" has unsupported event type %d",
5438 rulename, ev_type)));
5439 break;
5440 }
5441
5442 /* The relation the rule is fired on */
5443 appendStringInfo(buf, " TO %s",
5444 (prettyFlags & PRETTYFLAG_SCHEMA) ?
5447
5448 /* If the rule has an event qualification, add it */
5449 if (strcmp(ev_qual, "<>") != 0)
5450 {
5451 Node *qual;
5452 Query *query;
5453 deparse_context context;
5455
5456 if (prettyFlags & PRETTYFLAG_INDENT)
5458 appendStringInfoString(buf, " WHERE ");
5459
5460 qual = stringToNode(ev_qual);
5461
5462 /*
5463 * We need to make a context for recognizing any Vars in the qual
5464 * (which can only be references to OLD and NEW). Use the rtable of
5465 * the first query in the action list for this purpose.
5466 */
5467 query = (Query *) linitial(actions);
5468
5469 /*
5470 * If the action is INSERT...SELECT, OLD/NEW have been pushed down
5471 * into the SELECT, and that's what we need to look at. (Ugly kluge
5472 * ... try to fix this when we redesign querytrees.)
5473 */
5474 query = getInsertSelectQuery(query, NULL);
5475
5476 /* Must acquire locks right away; see notes in get_query_def() */
5477 AcquireRewriteLocks(query, false, false);
5478
5479 context.buf = buf;
5480 context.namespaces = list_make1(&dpns);
5481 context.resultDesc = NULL;
5482 context.targetList = NIL;
5483 context.windowClause = NIL;
5484 context.varprefix = (list_length(query->rtable) != 1);
5485 context.prettyFlags = prettyFlags;
5487 context.indentLevel = PRETTYINDENT_STD;
5488 context.colNamesVisible = true;
5489 context.inGroupBy = false;
5490 context.varInOrderBy = false;
5491 context.appendparents = NULL;
5492
5493 set_deparse_for_query(&dpns, query, NIL);
5494
5495 get_rule_expr(qual, &context, false);
5496 }
5497
5498 appendStringInfoString(buf, " DO ");
5499
5500 /* The INSTEAD keyword (if so) */
5501 if (is_instead)
5502 appendStringInfoString(buf, "INSTEAD ");
5503
5504 /* Finally the rules actions */
5505 if (list_length(actions) > 1)
5506 {
5508 Query *query;
5509
5511 foreach(action, actions)
5512 {
5513 query = (Query *) lfirst(action);
5514 get_query_def(query, buf, NIL, viewResultDesc, true,
5515 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5516 if (prettyFlags)
5518 else
5520 }
5522 }
5523 else
5524 {
5525 Query *query;
5526
5527 query = (Query *) linitial(actions);
5528 get_query_def(query, buf, NIL, viewResultDesc, true,
5529 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
5531 }
5532
5534}

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

5545{
5546 Query *query;
5547 char ev_type;
5548 Oid ev_class;
5549 bool is_instead;
5550 char *ev_qual;
5551 char *ev_action;
5552 List *actions;
5554 int fno;
5555 Datum dat;
5556 bool isnull;
5557
5558 /*
5559 * Get the attribute values from the rules tuple
5560 */
5561 fno = SPI_fnumber(rulettc, "ev_type");
5562 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5563 Assert(!isnull);
5564 ev_type = DatumGetChar(dat);
5565
5566 fno = SPI_fnumber(rulettc, "ev_class");
5567 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5568 Assert(!isnull);
5570
5571 fno = SPI_fnumber(rulettc, "is_instead");
5572 dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
5573 Assert(!isnull);
5574 is_instead = DatumGetBool(dat);
5575
5576 fno = SPI_fnumber(rulettc, "ev_qual");
5578 Assert(ev_qual != NULL);
5579
5580 fno = SPI_fnumber(rulettc, "ev_action");
5582 Assert(ev_action != NULL);
5583 actions = (List *) stringToNode(ev_action);
5584
5585 if (list_length(actions) != 1)
5586 {
5587 /* keep output buffer empty and leave */
5588 return;
5589 }
5590
5591 query = (Query *) linitial(actions);
5592
5593 if (ev_type != '1' || !is_instead ||
5594 strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
5595 {
5596 /* keep output buffer empty and leave */
5597 return;
5598 }
5599
5601
5603 prettyFlags, wrapColumn, 0);
5605
5607}

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

2147{
2149 int prettyFlags;
2150 char *res;
2151
2152 prettyFlags = PRETTYFLAG_INDENT;
2153
2154 res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2155
2156 if (res == NULL)
2158
2160}

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

2185{
2186 return pg_get_constraintdef_worker(constraintId, true, 0, false);
2187}

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

2164{
2166 bool pretty = PG_GETARG_BOOL(1);
2167 int prettyFlags;
2168 char *res;
2169
2170 prettyFlags = GET_PRETTY_FLAGS(pretty);
2171
2172 res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
2173
2174 if (res == NULL)
2176
2178}

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

2195{
2196 HeapTuple tup;
2199 SysScanDesc scandesc;
2203
2204 ScanKeyInit(&scankey[0],
2208
2209 scandesc = systable_beginscan(relation,
2211 true,
2212 snapshot,
2213 1,
2214 scankey);
2215
2216 /*
2217 * We later use the tuple with SysCacheGetAttr() as if we had obtained it
2218 * via SearchSysCache, which works fine.
2219 */
2220 tup = systable_getnext(scandesc);
2221
2222 UnregisterSnapshot(snapshot);
2223
2224 if (!HeapTupleIsValid(tup))
2225 {
2226 if (missing_ok)
2227 {
2228 systable_endscan(scandesc);
2229 table_close(relation, AccessShareLock);
2230 return NULL;
2231 }
2232 elog(ERROR, "could not find tuple for constraint %u", constraintId);
2233 }
2234
2236
2238
2239 if (fullCommand)
2240 {
2241 if (OidIsValid(conForm->conrelid))
2242 {
2243 /*
2244 * Currently, callers want ALTER TABLE (without ONLY) for CHECK
2245 * constraints, and other types of constraints don't inherit
2246 * anyway so it doesn't matter whether we say ONLY or not. Someday
2247 * we might need to let callers specify whether to put ONLY in the
2248 * command.
2249 */
2250 appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
2252 quote_identifier(NameStr(conForm->conname)));
2253 }
2254 else
2255 {
2256 /* Must be a domain constraint */
2257 Assert(OidIsValid(conForm->contypid));
2258 appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
2260 quote_identifier(NameStr(conForm->conname)));
2261 }
2262 }
2263
2264 switch (conForm->contype)
2265 {
2266 case CONSTRAINT_FOREIGN:
2267 {
2268 Datum val;
2269 bool isnull;
2270 const char *string;
2271
2272 /* Start off the constraint definition */
2273 appendStringInfoString(&buf, "FOREIGN KEY (");
2274
2275 /* Fetch and build referencing-column list */
2278
2279 /* If it is a temporal foreign key then it uses PERIOD. */
2280 decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
2281
2282 /* add foreign relation name */
2283 appendStringInfo(&buf, ") REFERENCES %s(",
2285 NIL));
2286
2287 /* Fetch and build referenced-column list */
2290
2291 decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
2292
2294
2295 /* Add match type */
2296 switch (conForm->confmatchtype)
2297 {
2299 string = " MATCH FULL";
2300 break;
2302 string = " MATCH PARTIAL";
2303 break;
2305 string = "";
2306 break;
2307 default:
2308 elog(ERROR, "unrecognized confmatchtype: %d",
2309 conForm->confmatchtype);
2310 string = ""; /* keep compiler quiet */
2311 break;
2312 }
2313 appendStringInfoString(&buf, string);
2314
2315 /* Add ON UPDATE and ON DELETE clauses, if needed */
2316 switch (conForm->confupdtype)
2317 {
2319 string = NULL; /* suppress default */
2320 break;
2322 string = "RESTRICT";
2323 break;
2325 string = "CASCADE";
2326 break;
2328 string = "SET NULL";
2329 break;
2331 string = "SET DEFAULT";
2332 break;
2333 default:
2334 elog(ERROR, "unrecognized confupdtype: %d",
2335 conForm->confupdtype);
2336 string = NULL; /* keep compiler quiet */
2337 break;
2338 }
2339 if (string)
2340 appendStringInfo(&buf, " ON UPDATE %s", string);
2341
2342 switch (conForm->confdeltype)
2343 {
2345 string = NULL; /* suppress default */
2346 break;
2348 string = "RESTRICT";
2349 break;
2351 string = "CASCADE";
2352 break;
2354 string = "SET NULL";
2355 break;
2357 string = "SET DEFAULT";
2358 break;
2359 default:
2360 elog(ERROR, "unrecognized confdeltype: %d",
2361 conForm->confdeltype);
2362 string = NULL; /* keep compiler quiet */
2363 break;
2364 }
2365 if (string)
2366 appendStringInfo(&buf, " ON DELETE %s", string);
2367
2368 /*
2369 * Add columns specified to SET NULL or SET DEFAULT if
2370 * provided.
2371 */
2374 if (!isnull)
2375 {
2377 decompile_column_index_array(val, conForm->conrelid, false, &buf);
2379 }
2380
2381 break;
2382 }
2383 case CONSTRAINT_PRIMARY:
2384 case CONSTRAINT_UNIQUE:
2385 {
2386 Datum val;
2387 Oid indexId;
2388 int keyatts;
2390
2391 /* Start off the constraint definition */
2392 if (conForm->contype == CONSTRAINT_PRIMARY)
2393 appendStringInfoString(&buf, "PRIMARY KEY ");
2394 else
2395 appendStringInfoString(&buf, "UNIQUE ");
2396
2397 indexId = conForm->conindid;
2398
2401 elog(ERROR, "cache lookup failed for index %u", indexId);
2402 if (conForm->contype == CONSTRAINT_UNIQUE &&
2403 ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
2404 appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
2405
2407
2408 /* Fetch and build target column list */
2411
2412 keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
2413 if (conForm->conperiod)
2414 appendStringInfoString(&buf, " WITHOUT OVERLAPS");
2415
2417
2418 /* Build including column list (from pg_index.indkeys) */
2421 if (DatumGetInt32(val) > keyatts)
2422 {
2423 Datum cols;
2424 Datum *keys;
2425 int nKeys;
2426 int j;
2427
2428 appendStringInfoString(&buf, " INCLUDE (");
2429
2432
2434 &keys, NULL, &nKeys);
2435
2436 for (j = keyatts; j < nKeys; j++)
2437 {
2438 char *colName;
2439
2440 colName = get_attname(conForm->conrelid,
2441 DatumGetInt16(keys[j]), false);
2442 if (j > keyatts)
2445 }
2446
2448 }
2450
2451 /* XXX why do we only print these bits if fullCommand? */
2453 {
2455 Oid tblspc;
2456
2457 if (options)
2458 {
2459 appendStringInfo(&buf, " WITH (%s)", options);
2460 pfree(options);
2461 }
2462
2463 /*
2464 * Print the tablespace, unless it's the database default.
2465 * This is to help ALTER TABLE usage of this facility,
2466 * which needs this behavior to recreate exact catalog
2467 * state.
2468 */
2470 if (OidIsValid(tblspc))
2471 appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
2473 }
2474
2475 break;
2476 }
2477 case CONSTRAINT_CHECK:
2478 {
2479 Datum val;
2480 char *conbin;
2481 char *consrc;
2482 Node *expr;
2483 List *context;
2484
2485 /* Fetch constraint expression in parsetree form */
2488
2490 expr = stringToNode(conbin);
2491
2492 /* Set up deparsing context for Var nodes in constraint */
2493 if (conForm->conrelid != InvalidOid)
2494 {
2495 /* relation constraint */
2496 context = deparse_context_for(get_relation_name(conForm->conrelid),
2497 conForm->conrelid);
2498 }
2499 else
2500 {
2501 /* domain constraint --- can't have Vars */
2502 context = NIL;
2503 }
2504
2505 consrc = deparse_expression_pretty(expr, context, false, false,
2506 prettyFlags, 0);
2507
2508 /*
2509 * Now emit the constraint definition, adding NO INHERIT if
2510 * necessary.
2511 *
2512 * There are cases where the constraint expression will be
2513 * fully parenthesized and we don't need the outer parens ...
2514 * but there are other cases where we do need 'em. Be
2515 * conservative for now.
2516 *
2517 * Note that simply checking for leading '(' and trailing ')'
2518 * would NOT be good enough, consider "(x > 0) AND (y > 0)".
2519 */
2520 appendStringInfo(&buf, "CHECK (%s)%s",
2521 consrc,
2522 conForm->connoinherit ? " NO INHERIT" : "");
2523 break;
2524 }
2525 case CONSTRAINT_NOTNULL:
2526 {
2527 if (conForm->conrelid)
2528 {
2530
2532
2533 appendStringInfo(&buf, "NOT NULL %s",
2535 attnum, false)));
2537 appendStringInfoString(&buf, " NO INHERIT");
2538 }
2539 else if (conForm->contypid)
2540 {
2541 /* conkey is null for domain not-null constraints */
2542 appendStringInfoString(&buf, "NOT NULL");
2543 }
2544 break;
2545 }
2546
2547 case CONSTRAINT_TRIGGER:
2548
2549 /*
2550 * There isn't an ALTER TABLE syntax for creating a user-defined
2551 * constraint trigger, but it seems better to print something than
2552 * throw an error; if we throw error then this function couldn't
2553 * safely be applied to all rows of pg_constraint.
2554 */
2555 appendStringInfoString(&buf, "TRIGGER");
2556 break;
2558 {
2559 Oid indexOid = conForm->conindid;
2560 Datum val;
2561 Datum *elems;
2562 int nElems;
2563 int i;
2564 Oid *operators;
2565
2566 /* Extract operator OIDs from the pg_constraint tuple */
2569
2571 &elems, NULL, &nElems);
2572
2573 operators = (Oid *) palloc(nElems * sizeof(Oid));
2574 for (i = 0; i < nElems; i++)
2575 operators[i] = DatumGetObjectId(elems[i]);
2576
2577 /* pg_get_indexdef_worker does the rest */
2578 /* suppress tablespace because pg_dump wants it that way */
2580 pg_get_indexdef_worker(indexOid,
2581 0,
2582 operators,
2583 false,
2584 false,
2585 false,
2586 false,
2587 prettyFlags,
2588 false));
2589 break;
2590 }
2591 default:
2592 elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
2593 break;
2594 }
2595
2596 if (conForm->condeferrable)
2597 appendStringInfoString(&buf, " DEFERRABLE");
2598 if (conForm->condeferred)
2599 appendStringInfoString(&buf, " INITIALLY DEFERRED");
2600
2601 /* Validated status is irrelevant when the constraint is NOT ENFORCED. */
2602 if (!conForm->conenforced)
2603 appendStringInfoString(&buf, " NOT ENFORCED");
2604 else if (!conForm->convalidated)
2605 appendStringInfoString(&buf, " NOT VALID");
2606
2607 /* Cleanup */
2608 systable_endscan(scandesc);
2609 table_close(relation, AccessShareLock);
2610
2611 return buf.data;
2612}

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

2676{
2677 text *expr = PG_GETARG_TEXT_PP(0);
2678 Oid relid = PG_GETARG_OID(1);
2679 text *result;
2680 int prettyFlags;
2681
2682 prettyFlags = PRETTYFLAG_INDENT;
2683
2684 result = pg_get_expr_worker(expr, relid, prettyFlags);
2685 if (result)
2686 PG_RETURN_TEXT_P(result);
2687 else
2689}

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

Referenced by decompile_conbin().

◆ pg_get_expr_ext()

Datum pg_get_expr_ext ( PG_FUNCTION_ARGS  )

Definition at line 2692 of file ruleutils.c.

2693{
2694 text *expr = PG_GETARG_TEXT_PP(0);
2695 Oid relid = PG_GETARG_OID(1);
2696 bool pretty = PG_GETARG_BOOL(2);
2697 text *result;
2698 int prettyFlags;
2699
2700 prettyFlags = GET_PRETTY_FLAGS(pretty);
2701
2702 result = pg_get_expr_worker(expr, relid, prettyFlags);
2703 if (result)
2704 PG_RETURN_TEXT_P(result);
2705 else
2707}

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

◆ pg_get_expr_worker()

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

Definition at line 2710 of file ruleutils.c.

2711{
2712 Node *node;
2713 Node *tst;
2714 Relids relids;
2715 List *context;
2716 char *exprstr;
2717 Relation rel = NULL;
2718 char *str;
2719
2720 /* Convert input pg_node_tree (really TEXT) object to C string */
2721 exprstr = text_to_cstring(expr);
2722
2723 /* Convert expression to node tree */
2724 node = (Node *) stringToNode(exprstr);
2725
2726 pfree(exprstr);
2727
2728 /*
2729 * Throw error if the input is a querytree rather than an expression tree.
2730 * While we could support queries here, there seems no very good reason
2731 * to. In most such catalog columns, we'll see a List of Query nodes, or
2732 * even nested Lists, so drill down to a non-List node before checking.
2733 */
2734 tst = node;
2735 while (tst && IsA(tst, List))
2736 tst = linitial((List *) tst);
2737 if (tst && IsA(tst, Query))
2738 ereport(ERROR,
2740 errmsg("input is a query, not an expression")));
2741
2742 /*
2743 * Throw error if the expression contains Vars we won't be able to
2744 * deparse.
2745 */
2746 relids = pull_varnos(NULL, node);
2747 if (OidIsValid(relid))
2748 {
2749 if (!bms_is_subset(relids, bms_make_singleton(1)))
2750 ereport(ERROR,
2752 errmsg("expression contains variables of more than one relation")));
2753 }
2754 else
2755 {
2756 if (!bms_is_empty(relids))
2757 ereport(ERROR,
2759 errmsg("expression contains variables")));
2760 }
2761
2762 /*
2763 * Prepare deparse context if needed. If we are deparsing with a relid,
2764 * we need to transiently open and lock the rel, to make sure it won't go
2765 * away underneath us. (set_relation_column_names would lock it anyway,
2766 * so this isn't really introducing any new behavior.)
2767 */
2768 if (OidIsValid(relid))
2769 {
2770 rel = try_relation_open(relid, AccessShareLock);
2771 if (rel == NULL)
2772 return NULL;
2773 context = deparse_context_for(RelationGetRelationName(rel), relid);
2774 }
2775 else
2776 context = NIL;
2777
2778 /* Deparse */
2779 str = deparse_expression_pretty(node, context, false, false,
2780 prettyFlags, 0);
2781
2782 if (rel != NULL)
2784
2785 return string_to_text(str);
2786}

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

3491{
3492 Oid funcid = PG_GETARG_OID(0);
3495 Form_pg_proc proc;
3496 int numargs;
3497 Oid *argtypes;
3498 char **argnames;
3499 char *argmodes;
3500 int i;
3502 Node *node;
3503 char *str;
3504 int nth_inputarg;
3506 bool isnull;
3507 int nth_default;
3508
3512
3513 numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
3515 {
3518 }
3519
3520 nth_inputarg = 0;
3521 for (i = 0; i < nth_arg; i++)
3523 nth_inputarg++;
3524
3527 &isnull);
3528 if (isnull)
3529 {
3532 }
3533
3536 pfree(str);
3537
3538 proc = (Form_pg_proc) GETSTRUCT(proctup);
3539
3540 /*
3541 * Calculate index into proargdefaults: proargdefaults corresponds to the
3542 * last N input arguments, where N = pronargdefaults.
3543 */
3544 nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
3545
3547 {
3550 }
3552 str = deparse_expression(node, NIL, false, false);
3553
3555
3557}

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

3615{
3616 Oid funcid = PG_GETARG_OID(0);
3619 bool isnull;
3620
3622
3623 /* Look up the function */
3627
3629 if (isnull)
3630 {
3633 }
3634
3636
3638
3640}

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

2928{
2929 Oid funcid = PG_GETARG_OID(0);
2933 Form_pg_proc proc;
2934 bool isfunction;
2935 Datum tmp;
2936 bool isnull;
2937 const char *prosrc;
2938 const char *name;
2939 const char *nsp;
2941 int oldlen;
2942
2944
2945 /* Look up the function */
2949
2950 proc = (Form_pg_proc) GETSTRUCT(proctup);
2951 name = NameStr(proc->proname);
2952
2953 if (proc->prokind == PROKIND_AGGREGATE)
2954 ereport(ERROR,
2956 errmsg("\"%s\" is an aggregate function", name)));
2957
2958 isfunction = (proc->prokind != PROKIND_PROCEDURE);
2959
2960 /*
2961 * We always qualify the function name, to ensure the right function gets
2962 * replaced.
2963 */
2964 nsp = get_namespace_name_or_temp(proc->pronamespace);
2965 appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
2966 isfunction ? "FUNCTION" : "PROCEDURE",
2968 (void) print_function_arguments(&buf, proctup, false, true);
2969 appendStringInfoString(&buf, ")\n");
2970 if (isfunction)
2971 {
2972 appendStringInfoString(&buf, " RETURNS ");
2974 appendStringInfoChar(&buf, '\n');
2975 }
2976
2978
2979 appendStringInfo(&buf, " LANGUAGE %s\n",
2980 quote_identifier(get_language_name(proc->prolang, false)));
2981
2982 /* Emit some miscellaneous options on one line */
2983 oldlen = buf.len;
2984
2985 if (proc->prokind == PROKIND_WINDOW)
2986 appendStringInfoString(&buf, " WINDOW");
2987 switch (proc->provolatile)
2988 {
2990 appendStringInfoString(&buf, " IMMUTABLE");
2991 break;
2992 case PROVOLATILE_STABLE:
2993 appendStringInfoString(&buf, " STABLE");
2994 break;
2996 break;
2997 }
2998
2999 switch (proc->proparallel)
3000 {
3001 case PROPARALLEL_SAFE:
3002 appendStringInfoString(&buf, " PARALLEL SAFE");
3003 break;
3005 appendStringInfoString(&buf, " PARALLEL RESTRICTED");
3006 break;
3007 case PROPARALLEL_UNSAFE:
3008 break;
3009 }
3010
3011 if (proc->proisstrict)
3012 appendStringInfoString(&buf, " STRICT");
3013 if (proc->prosecdef)
3014 appendStringInfoString(&buf, " SECURITY DEFINER");
3015 if (proc->proleakproof)
3016 appendStringInfoString(&buf, " LEAKPROOF");
3017
3018 /* This code for the default cost and rows should match functioncmds.c */
3019 if (proc->prolang == INTERNALlanguageId ||
3020 proc->prolang == ClanguageId)
3021 procost = 1;
3022 else
3023 procost = 100;
3024 if (proc->procost != procost)
3025 appendStringInfo(&buf, " COST %g", proc->procost);
3026
3027 if (proc->prorows > 0 && proc->prorows != 1000)
3028 appendStringInfo(&buf, " ROWS %g", proc->prorows);
3029
3030 if (proc->prosupport)
3031 {
3032 Oid argtypes[1];
3033
3034 /*
3035 * We should qualify the support function's name if it wouldn't be
3036 * resolved by lookup in the current search path.
3037 */
3038 argtypes[0] = INTERNALOID;
3039 appendStringInfo(&buf, " SUPPORT %s",
3040 generate_function_name(proc->prosupport, 1,
3041 NIL, argtypes,
3042 false, NULL, false));
3043 }
3044
3045 if (oldlen != buf.len)
3046 appendStringInfoChar(&buf, '\n');
3047
3048 /* Emit any proconfig options, one per line */
3050 if (!isnull)
3051 {
3053 int i;
3054
3056 Assert(ARR_NDIM(a) == 1);
3057 Assert(ARR_LBOUND(a)[0] == 1);
3058
3059 for (i = 1; i <= ARR_DIMS(a)[0]; i++)
3060 {
3061 Datum d;
3062
3063 d = array_ref(a, 1, &i,
3064 -1 /* varlenarray */ ,
3065 -1 /* TEXT's typlen */ ,
3066 false /* TEXT's typbyval */ ,
3067 TYPALIGN_INT /* TEXT's typalign */ ,
3068 &isnull);
3069 if (!isnull)
3070 {
3072 char *pos;
3073
3074 pos = strchr(configitem, '=');
3075 if (pos == NULL)
3076 continue;
3077 *pos++ = '\0';
3078
3079 appendStringInfo(&buf, " SET %s TO ",
3081
3082 /*
3083 * Variables that are marked GUC_LIST_QUOTE were already fully
3084 * quoted by flatten_set_variable_args() before they were put
3085 * into the proconfig array. However, because the quoting
3086 * rules used there aren't exactly like SQL's, we have to
3087 * break the list value apart and then quote the elements as
3088 * string literals. (The elements may be double-quoted as-is,
3089 * but we can't just feed them to the SQL parser; it would do
3090 * the wrong thing with elements that are zero-length or
3091 * longer than NAMEDATALEN.) Also, we need a special case for
3092 * empty lists.
3093 *
3094 * Variables that are not so marked should just be emitted as
3095 * simple string literals. If the variable is not known to
3096 * guc.c, we'll do that; this makes it unsafe to use
3097 * GUC_LIST_QUOTE for extension variables.
3098 */
3100 {
3101 List *namelist;
3102 ListCell *lc;
3103
3104 /* Parse string into list of identifiers */
3105 if (!SplitGUCList(pos, ',', &namelist))
3106 {
3107 /* this shouldn't fail really */
3108 elog(ERROR, "invalid list syntax in proconfig item");
3109 }
3110 /* Special case: represent an empty list as NULL */
3111 if (namelist == NIL)
3112 appendStringInfoString(&buf, "NULL");
3113 foreach(lc, namelist)
3114 {
3115 char *curname = (char *) lfirst(lc);
3116
3118 if (lnext(namelist, lc))
3120 }
3121 }
3122 else
3124 appendStringInfoChar(&buf, '\n');
3125 }
3126 }
3127 }
3128
3129 /* And finally the function definition ... */
3131 if (proc->prolang == SQLlanguageId && !isnull)
3132 {
3134 }
3135 else
3136 {
3137 appendStringInfoString(&buf, "AS ");
3138
3140 if (!isnull)
3141 {
3143 appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
3144 }
3145
3147 prosrc = TextDatumGetCString(tmp);
3148
3149 /*
3150 * We always use dollar quoting. Figure out a suitable delimiter.
3151 *
3152 * Since the user is likely to be editing the function body string, we
3153 * shouldn't use a short delimiter that he might easily create a
3154 * conflict with. Hence prefer "$function$"/"$procedure$", but extend
3155 * if needed.
3156 */
3158 appendStringInfoChar(&dq, '$');
3159 appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
3160 while (strstr(prosrc, dq.data) != NULL)
3161 appendStringInfoChar(&dq, 'x');
3162 appendStringInfoChar(&dq, '$');
3163
3164 appendBinaryStringInfo(&buf, dq.data, dq.len);
3165 appendStringInfoString(&buf, prosrc);
3166 appendBinaryStringInfo(&buf, dq.data, dq.len);
3167 }
3168
3169 appendStringInfoChar(&buf, '\n');
3170
3172
3174}

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

1180{
1181 Oid indexrelid = PG_GETARG_OID(0);
1182 int prettyFlags;
1183 char *res;
1184
1185 prettyFlags = PRETTYFLAG_INDENT;
1186
1187 res = pg_get_indexdef_worker(indexrelid, 0, NULL,
1188 false, false,
1189 false, false,
1190 prettyFlags, true);
1191
1192 if (res == NULL)
1194
1196}

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

1237{
1238 int prettyFlags;
1239
1240 prettyFlags = GET_PRETTY_FLAGS(pretty);
1241
1242 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1243 true, true,
1244 false, false,
1245 prettyFlags, false);
1246}

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,
bits16  flags 
)

Definition at line 1250 of file ruleutils.c.

1251{
1252 bool pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
1253 bool keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
1254 int prettyFlags;
1255
1256 prettyFlags = GET_PRETTY_FLAGS(pretty);
1257
1258 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1259 true, keys_only,
1260 false, false,
1261 prettyFlags, false);
1262}

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

1200{
1201 Oid indexrelid = PG_GETARG_OID(0);
1202 int32 colno = PG_GETARG_INT32(1);
1203 bool pretty = PG_GETARG_BOOL(2);
1204 int prettyFlags;
1205 char *res;
1206
1207 prettyFlags = GET_PRETTY_FLAGS(pretty);
1208
1209 res = pg_get_indexdef_worker(indexrelid, colno, NULL,
1210 colno != 0, false,
1211 false, false,
1212 prettyFlags, true);
1213
1214 if (res == NULL)
1216
1218}

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

1227{
1228 return pg_get_indexdef_worker(indexrelid, 0, NULL,
1229 false, false,
1230 true, true,
1231 0, false);
1232}

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

1276{
1277 /* might want a separate isConstraint parameter later */
1278 bool isConstraint = (excludeOps != NULL);
1286 List *indexprs;
1288 List *context;
1289 Oid indrelid;
1290 int keyno;
1298 char *str;
1299 char *sep;
1300
1301 /*
1302 * Fetch the pg_index tuple by the Oid of the index
1303 */
1306 {
1307 if (missing_ok)
1308 return NULL;
1309 elog(ERROR, "cache lookup failed for index %u", indexrelid);
1310 }
1312
1313 indrelid = idxrec->indrelid;
1314 Assert(indexrelid == idxrec->indexrelid);
1315
1316 /* Must get indcollation, indclass, and indoption the hard way */
1320
1324
1328
1329 /*
1330 * Fetch the pg_class tuple of the index relation
1331 */
1334 elog(ERROR, "cache lookup failed for relation %u", indexrelid);
1336
1337 /*
1338 * Fetch the pg_am tuple of the index' access method
1339 */
1341 if (!HeapTupleIsValid(ht_am))
1342 elog(ERROR, "cache lookup failed for access method %u",
1343 idxrelrec->relam);
1345
1346 /* Fetch the index AM's API struct */
1347 amroutine = GetIndexAmRoutine(amrec->amhandler);
1348
1349 /*
1350 * Get the index expressions, if any. (NOTE: we do not use the relcache
1351 * versions of the expressions and predicate, because we want to display
1352 * non-const-folded expressions.)
1353 */
1355 {
1357 char *exprsString;
1358
1364 }
1365 else
1366 indexprs = NIL;
1367
1369
1371
1372 /*
1373 * Start the index definition. Note that the index's name should never be
1374 * schema-qualified, but the indexed rel's name may be.
1375 */
1377
1378 if (!attrsOnly)
1379 {
1380 if (!isConstraint)
1381 appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
1382 idxrec->indisunique ? "UNIQUE " : "",
1385 && !inherits ? "ONLY " : "",
1386 (prettyFlags & PRETTYFLAG_SCHEMA) ?
1389 quote_identifier(NameStr(amrec->amname)));
1390 else /* currently, must be EXCLUDE constraint */
1391 appendStringInfo(&buf, "EXCLUDE USING %s (",
1392 quote_identifier(NameStr(amrec->amname)));
1393 }
1394
1395 /*
1396 * Report the indexed attributes
1397 */
1398 sep = "";
1399 for (keyno = 0; keyno < idxrec->indnatts; keyno++)
1400 {
1401 AttrNumber attnum = idxrec->indkey.values[keyno];
1404
1405 /*
1406 * Ignore non-key attributes if told to.
1407 */
1408 if (keysOnly && keyno >= idxrec->indnkeyatts)
1409 break;
1410
1411 /* Otherwise, print INCLUDE to divide key and non-key attrs. */
1412 if (!colno && keyno == idxrec->indnkeyatts)
1413 {
1414 appendStringInfoString(&buf, ") INCLUDE (");
1415 sep = "";
1416 }
1417
1418 if (!colno)
1420 sep = ", ";
1421
1422 if (attnum != 0)
1423 {
1424 /* Simple index column */
1425 char *attname;
1427
1429 if (!colno || colno == keyno + 1)
1434 }
1435 else
1436 {
1437 /* expressional index */
1438 Node *indexkey;
1439
1440 if (indexpr_item == NULL)
1441 elog(ERROR, "too few entries in indexprs list");
1444 /* Deparse */
1445 str = deparse_expression_pretty(indexkey, context, false, false,
1446 prettyFlags, 0);
1447 if (!colno || colno == keyno + 1)
1448 {
1449 /* Need parens if it's not a bare function call */
1452 else
1453 appendStringInfo(&buf, "(%s)", str);
1454 }
1457 }
1458
1459 /* Print additional decoration for (selected) key columns */
1461 (!colno || colno == keyno + 1))
1462 {
1463 int16 opt = indoption->values[keyno];
1464 Oid indcoll = indcollation->values[keyno];
1465 Datum attoptions = get_attoptions(indexrelid, keyno + 1);
1466 bool has_options = attoptions != (Datum) 0;
1467
1468 /* Add collation, if not default for column */
1470 appendStringInfo(&buf, " COLLATE %s",
1472
1473 /* Add the operator class name, if not default */
1474 get_opclass_name(indclass->values[keyno],
1476
1477 if (has_options)
1478 {
1480 get_reloptions(&buf, attoptions);
1482 }
1483
1484 /* Add options if relevant */
1485 if (amroutine->amcanorder)
1486 {
1487 /* if it supports sort ordering, report DESC and NULLS opts */
1488 if (opt & INDOPTION_DESC)
1489 {
1490 appendStringInfoString(&buf, " DESC");
1491 /* NULLS FIRST is the default in this case */
1492 if (!(opt & INDOPTION_NULLS_FIRST))
1493 appendStringInfoString(&buf, " NULLS LAST");
1494 }
1495 else
1496 {
1497 if (opt & INDOPTION_NULLS_FIRST)
1498 appendStringInfoString(&buf, " NULLS FIRST");
1499 }
1500 }
1501
1502 /* Add the exclusion operator if relevant */
1503 if (excludeOps != NULL)
1504 appendStringInfo(&buf, " WITH %s",
1506 keycoltype,
1507 keycoltype));
1508 }
1509 }
1510
1511 if (!attrsOnly)
1512 {
1514
1515 if (idxrec->indnullsnotdistinct)
1516 appendStringInfoString(&buf, " NULLS NOT DISTINCT");
1517
1518 /*
1519 * If it has options, append "WITH (options)"
1520 */
1521 str = flatten_reloptions(indexrelid);
1522 if (str)
1523 {
1524 appendStringInfo(&buf, " WITH (%s)", str);
1525 pfree(str);
1526 }
1527
1528 /*
1529 * Print tablespace, but only if requested
1530 */
1531 if (showTblSpc)
1532 {
1533 Oid tblspc;
1534
1535 tblspc = get_rel_tablespace(indexrelid);
1536 if (OidIsValid(tblspc))
1537 {
1538 if (isConstraint)
1539 appendStringInfoString(&buf, " USING INDEX");
1540 appendStringInfo(&buf, " TABLESPACE %s",
1542 }
1543 }
1544
1545 /*
1546 * If it's a partial index, decompile and append the predicate
1547 */
1549 {
1550 Node *node;
1552 char *predString;
1553
1554 /* Convert text string to node tree */
1558 node = (Node *) stringToNode(predString);
1560
1561 /* Deparse */
1562 str = deparse_expression_pretty(node, context, false, false,
1563 prettyFlags, 0);
1564 if (isConstraint)
1565 appendStringInfo(&buf, " WHERE (%s)", str);
1566 else
1567 appendStringInfo(&buf, " WHERE %s", str);
1568 }
1569 }
1570
1571 /* Clean up */
1575
1576 return buf.data;
1577}

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

2129{
2131 List *context;
2132
2134 context = deparse_context_for(aliasname, partitionId);
2135
2136 return deparse_expression((Node *) constr_expr, context, true, false);
2137}

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

2097{
2100 int prettyFlags;
2101 List *context;
2102 char *consrc;
2103
2105
2106 /* Quick exit if no partition constraint */
2107 if (constr_expr == NULL)
2109
2110 /*
2111 * Deparse and return the constraint expression.
2112 */
2113 prettyFlags = PRETTYFLAG_INDENT;
2115 consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
2116 false, prettyFlags, 0);
2117
2119}

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

1910{
1911 Oid relid = PG_GETARG_OID(0);
1912 char *res;
1913
1914 res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
1915
1916 if (res == NULL)
1918
1920}

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

1925{
1926 int prettyFlags;
1927
1928 prettyFlags = GET_PRETTY_FLAGS(pretty);
1929
1930 return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
1931}

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

1939{
1941 HeapTuple tuple;
1943 oidvector *partcollation;
1944 List *partexprs;
1946 List *context;
1947 Datum datum;
1949 int keyno;
1950 char *str;
1951 char *sep;
1952
1954 if (!HeapTupleIsValid(tuple))
1955 {
1956 if (missing_ok)
1957 return NULL;
1958 elog(ERROR, "cache lookup failed for partition key of %u", relid);
1959 }
1960
1962
1963 Assert(form->partrelid == relid);
1964
1965 /* Must get partclass and partcollation the hard way */
1966 datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1968 partclass = (oidvector *) DatumGetPointer(datum);
1969
1970 datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
1972 partcollation = (oidvector *) DatumGetPointer(datum);
1973
1974
1975 /*
1976 * Get the expressions, if any. (NOTE: we do not use the relcache
1977 * versions of the expressions, because we want to display
1978 * non-const-folded expressions.)
1979 */
1981 {
1983 char *exprsString;
1984
1988 partexprs = (List *) stringToNode(exprsString);
1989
1990 if (!IsA(partexprs, List))
1991 elog(ERROR, "unexpected node type found in partexprs: %d",
1992 (int) nodeTag(partexprs));
1993
1995 }
1996 else
1997 partexprs = NIL;
1998
1999 partexpr_item = list_head(partexprs);
2000 context = deparse_context_for(get_relation_name(relid), relid);
2001
2003
2004 switch (form->partstrat)
2005 {
2007 if (!attrsOnly)
2008 appendStringInfoString(&buf, "HASH");
2009 break;
2011 if (!attrsOnly)
2012 appendStringInfoString(&buf, "LIST");
2013 break;
2015 if (!attrsOnly)
2016 appendStringInfoString(&buf, "RANGE");
2017 break;
2018 default:
2019 elog(ERROR, "unexpected partition strategy: %d",
2020 (int) form->partstrat);
2021 }
2022
2023 if (!attrsOnly)
2025 sep = "";
2026 for (keyno = 0; keyno < form->partnatts; keyno++)
2027 {
2028 AttrNumber attnum = form->partattrs.values[keyno];
2031 Oid partcoll;
2032
2034 sep = ", ";
2035 if (attnum != 0)
2036 {
2037 /* Simple attribute reference */
2038 char *attname;
2040
2041 attname = get_attname(relid, attnum, false);
2046 }
2047 else
2048 {
2049 /* Expression */
2050 Node *partkey;
2051
2052 if (partexpr_item == NULL)
2053 elog(ERROR, "too few entries in partexprs list");
2055 partexpr_item = lnext(partexprs, partexpr_item);
2056
2057 /* Deparse */
2058 str = deparse_expression_pretty(partkey, context, false, false,
2059 prettyFlags, 0);
2060 /* Need parens if it's not a bare function call */
2063 else
2064 appendStringInfo(&buf, "(%s)", str);
2065
2068 }
2069
2070 /* Add collation, if not default for column */
2071 partcoll = partcollation->values[keyno];
2073 appendStringInfo(&buf, " COLLATE %s",
2075
2076 /* Add the operator class name, if not default */
2077 if (!attrsOnly)
2078 get_opclass_name(partclass->values[keyno], keycoltype, &buf);
2079 }
2080
2081 if (!attrsOnly)
2083
2084 /* Clean up */
2085 ReleaseSysCache(tuple);
2086
2087 return buf.data;
2088}

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

char * pg_get_querydef ( Query query,
bool  pretty 
)

Definition at line 1589 of file ruleutils.c.

1590{
1592 int prettyFlags;
1593
1594 prettyFlags = GET_PRETTY_FLAGS(pretty);
1595
1597
1598 get_query_def(query, &buf, NIL, NULL, true,
1599 prettyFlags, WRAP_COLUMN_DEFAULT, 0);
1600
1601 return buf.data;
1602}

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

562{
564 int prettyFlags;
565 char *res;
566
567 prettyFlags = PRETTYFLAG_INDENT;
568
569 res = pg_get_ruledef_worker(ruleoid, prettyFlags);
570
571 if (res == NULL)
573
575}

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

580{
582 bool pretty = PG_GETARG_BOOL(1);
583 int prettyFlags;
584 char *res;
585
586 prettyFlags = GET_PRETTY_FLAGS(pretty);
587
588 res = pg_get_ruledef_worker(ruleoid, prettyFlags);
589
590 if (res == NULL)
592
594}

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

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

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

2834{
2835 text *tablename = PG_GETARG_TEXT_PP(0);
2838 Oid tableOid;
2839 char *column;
2843 ScanKeyData key[3];
2844 SysScanDesc scan;
2845 HeapTuple tup;
2846
2847 /* Look up table name. Can't lock it - we might not have privileges. */
2849 tableOid = RangeVarGetRelid(tablerv, NoLock, false);
2850
2851 /* Get the number of the column */
2853
2854 attnum = get_attnum(tableOid, column);
2856 ereport(ERROR,
2858 errmsg("column \"%s\" of relation \"%s\" does not exist",
2859 column, tablerv->relname)));
2860
2861 /* Search the dependency table for the dependent sequence */
2863
2864 ScanKeyInit(&key[0],
2868 ScanKeyInit(&key[1],
2871 ObjectIdGetDatum(tableOid));
2872 ScanKeyInit(&key[2],
2876
2878 NULL, 3, key);
2879
2880 while (HeapTupleIsValid(tup = systable_getnext(scan)))
2881 {
2883
2884 /*
2885 * Look for an auto dependency (serial column) or internal dependency
2886 * (identity column) of a sequence on a column. (We need the relkind
2887 * test because indexes can also have auto dependencies on columns.)
2888 */
2889 if (deprec->classid == RelationRelationId &&
2890 deprec->objsubid == 0 &&
2891 (deprec->deptype == DEPENDENCY_AUTO ||
2892 deprec->deptype == DEPENDENCY_INTERNAL) &&
2894 {
2895 sequenceId = deprec->objid;
2896 break;
2897 }
2898 }
2899
2900 systable_endscan(scan);
2902
2904 {
2905 char *result;
2906
2908
2910 }
2911
2913}

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

1655{
1659 int colno;
1660 char *nsp;
1661 ArrayType *arr;
1662 char *enabled;
1663 Datum datum;
1664 bool ndistinct_enabled;
1666 bool mcv_enabled;
1667 int i;
1668 List *context;
1669 ListCell *lc;
1670 List *exprs = NIL;
1671 bool has_exprs;
1672 int ncolumns;
1673
1675
1677 {
1678 if (missing_ok)
1679 return NULL;
1680 elog(ERROR, "cache lookup failed for statistics object %u", statextid);
1681 }
1682
1683 /* has the statistics expressions? */
1685
1687
1688 /*
1689 * Get the statistics expressions, if any. (NOTE: we do not use the
1690 * relcache versions of the expressions, because we want to display
1691 * non-const-folded expressions.)
1692 */
1693 if (has_exprs)
1694 {
1696 char *exprsString;
1697
1701 exprs = (List *) stringToNode(exprsString);
1703 }
1704 else
1705 exprs = NIL;
1706
1707 /* count the number of columns (attributes and expressions) */
1708 ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
1709
1711
1712 if (!columns_only)
1713 {
1714 nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
1715 appendStringInfo(&buf, "CREATE STATISTICS %s",
1717 NameStr(statextrec->stxname)));
1718
1719 /*
1720 * Decode the stxkind column so that we know which stats types to
1721 * print.
1722 */
1725 arr = DatumGetArrayTypeP(datum);
1726 if (ARR_NDIM(arr) != 1 ||
1727 ARR_HASNULL(arr) ||
1728 ARR_ELEMTYPE(arr) != CHAROID)
1729 elog(ERROR, "stxkind is not a 1-D char array");
1730 enabled = (char *) ARR_DATA_PTR(arr);
1731
1732 ndistinct_enabled = false;
1733 dependencies_enabled = false;
1734 mcv_enabled = false;
1735
1736 for (i = 0; i < ARR_DIMS(arr)[0]; i++)
1737 {
1738 if (enabled[i] == STATS_EXT_NDISTINCT)
1739 ndistinct_enabled = true;
1740 else if (enabled[i] == STATS_EXT_DEPENDENCIES)
1741 dependencies_enabled = true;
1742 else if (enabled[i] == STATS_EXT_MCV)
1743 mcv_enabled = true;
1744
1745 /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
1746 }
1747
1748 /*
1749 * If any option is disabled, then we'll need to append the types
1750 * clause to show which options are enabled. We omit the types clause
1751 * on purpose when all options are enabled, so a pg_dump/pg_restore
1752 * will create all statistics types on a newer postgres version, if
1753 * the statistics had all options enabled on the original version.
1754 *
1755 * But if the statistics is defined on just a single column, it has to
1756 * be an expression statistics. In that case we don't need to specify
1757 * kinds.
1758 */
1760 (ncolumns > 1))
1761 {
1762 bool gotone = false;
1763
1765
1767 {
1768 appendStringInfoString(&buf, "ndistinct");
1769 gotone = true;
1770 }
1771
1773 {
1774 appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
1775 gotone = true;
1776 }
1777
1778 if (mcv_enabled)
1779 appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
1780
1782 }
1783
1784 appendStringInfoString(&buf, " ON ");
1785 }
1786
1787 /* decode simple column references */
1788 for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
1789 {
1790 AttrNumber attnum = statextrec->stxkeys.values[colno];
1791 char *attname;
1792
1793 if (colno > 0)
1795
1796 attname = get_attname(statextrec->stxrelid, attnum, false);
1797
1799 }
1800
1802 statextrec->stxrelid);
1803
1804 foreach(lc, exprs)
1805 {
1806 Node *expr = (Node *) lfirst(lc);
1807 char *str;
1808 int prettyFlags = PRETTYFLAG_PAREN;
1809
1810 str = deparse_expression_pretty(expr, context, false, false,
1811 prettyFlags, 0);
1812
1813 if (colno > 0)
1815
1816 /* Need parens if it's not a bare function call */
1817 if (looks_like_function(expr))
1819 else
1820 appendStringInfo(&buf, "(%s)", str);
1821
1822 colno++;
1823 }
1824
1825 if (!columns_only)
1826 appendStringInfo(&buf, " FROM %s",
1828
1830
1831 return buf.data;
1832}

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

1610{
1612 char *res;
1613
1614 res = pg_get_statisticsobj_worker(statextid, false, true);
1615
1616 if (res == NULL)
1618
1620}

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

1638{
1640 char *res;
1641
1642 res = pg_get_statisticsobj_worker(statextid, true, true);
1643
1644 if (res == NULL)
1646
1648}

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

1839{
1843 Datum datum;
1844 List *context;
1845 ListCell *lc;
1846 List *exprs = NIL;
1847 bool has_exprs;
1848 char *tmp;
1849 ArrayBuildState *astate = NULL;
1850
1852
1855
1856 /* Does the stats object have expressions? */
1858
1859 /* no expressions? we're done */
1860 if (!has_exprs)
1861 {
1864 }
1865
1867
1868 /*
1869 * Get the statistics expressions, and deparse them into text values.
1870 */
1873 tmp = TextDatumGetCString(datum);
1874 exprs = (List *) stringToNode(tmp);
1875 pfree(tmp);
1876
1878 statextrec->stxrelid);
1879
1880 foreach(lc, exprs)
1881 {
1882 Node *expr = (Node *) lfirst(lc);
1883 char *str;
1884 int prettyFlags = PRETTYFLAG_INDENT;
1885
1886 str = deparse_expression_pretty(expr, context, false, false,
1887 prettyFlags, 0);
1888
1889 astate = accumArrayResult(astate,
1891 false,
1892 TEXTOID,
1894 }
1895
1897
1899}

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

1628{
1629 return pg_get_statisticsobj_worker(statextid, false, false);
1630}

References fb(), and pg_get_statisticsobj_worker().

Referenced by RememberStatisticsForRebuilding().

◆ pg_get_triggerdef()

Datum pg_get_triggerdef ( PG_FUNCTION_ARGS  )

Definition at line 872 of file ruleutils.c.

873{
875 char *res;
876
877 res = pg_get_triggerdef_worker(trigid, false);
878
879 if (res == NULL)
881
883}

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

887{
889 bool pretty = PG_GETARG_BOOL(1);
890 char *res;
891
893
894 if (res == NULL)
896
898}

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

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

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

2796{
2797 Oid roleid = PG_GETARG_OID(0);
2798 Name result;
2801
2802 /*
2803 * Allocate space for the result
2804 */
2805 result = (Name) palloc(NAMEDATALEN);
2806 memset(NameStr(*result), 0, NAMEDATALEN);
2807
2808 /*
2809 * Get the pg_authid entry and print the result
2810 */
2813 {
2815 *result = role_rec->rolname;
2817 }
2818 else
2819 sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
2820
2821 PG_RETURN_NAME(result);
2822}

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

◆ pg_get_viewdef()

Datum pg_get_viewdef ( PG_FUNCTION_ARGS  )

Definition at line 679 of file ruleutils.c.

680{
681 /* By OID */
682 Oid viewoid = PG_GETARG_OID(0);
683 int prettyFlags;
684 char *res;
685
686 prettyFlags = PRETTYFLAG_INDENT;
687
688 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
689
690 if (res == NULL)
692
694}

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

699{
700 /* By OID */
701 Oid viewoid = PG_GETARG_OID(0);
702 bool pretty = PG_GETARG_BOOL(1);
703 int prettyFlags;
704 char *res;
705
706 prettyFlags = GET_PRETTY_FLAGS(pretty);
707
708 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
709
710 if (res == NULL)
712
714}

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

738{
739 /* By qualified name */
741 int prettyFlags;
743 Oid viewoid;
744 char *res;
745
746 prettyFlags = PRETTYFLAG_INDENT;
747
748 /* Look up view name. Can't lock it - we might not have privileges. */
750 viewoid = RangeVarGetRelid(viewrel, NoLock, false);
751
752 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
753
754 if (res == NULL)
756
758}

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

763{
764 /* By qualified name */
766 bool pretty = PG_GETARG_BOOL(1);
767 int prettyFlags;
769 Oid viewoid;
770 char *res;
771
772 prettyFlags = GET_PRETTY_FLAGS(pretty);
773
774 /* Look up view name. Can't lock it - we might not have privileges. */
776 viewoid = RangeVarGetRelid(viewrel, NoLock, false);
777
778 res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
779
780 if (res == NULL)
782
784}

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

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

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

718{
719 /* By OID */
720 Oid viewoid = PG_GETARG_OID(0);
721 int wrap = PG_GETARG_INT32(1);
722 int prettyFlags;
723 char *res;
724
725 /* calling this implies we want pretty printing */
726 prettyFlags = GET_PRETTY_FLAGS(true);
727
728 res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
729
730 if (res == NULL)
732
734}

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

5336{
5337 /* Free the ancestor list made in push_ancestor_plan */
5338 list_free(dpns->ancestors);
5339
5340 /* Restore fields changed by push_ancestor_plan */
5341 *dpns = *save_dpns;
5342}

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

5285{
5286 List *ancestors;
5287
5288 /* Get rid of ancestors list cell added by push_child_plan */
5289 ancestors = list_delete_first(dpns->ancestors);
5290
5291 /* Restore fields changed by push_child_plan */
5292 *dpns = *save_dpns;
5293
5294 /* Make sure dpns->ancestors is right (may be unnecessary) */
5295 dpns->ancestors = ancestors;
5296}

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

3304{
3306 int numargs;
3307 Oid *argtypes;
3308 char **argnames;
3309 char *argmodes;
3310 int insertorderbyat = -1;
3311 int argsprinted;
3312 int inputargno;
3313 int nlackdefaults;
3314 List *argdefaults = NIL;
3316 int i;
3317
3318 numargs = get_func_arg_info(proctup,
3319 &argtypes, &argnames, &argmodes);
3320
3321 nlackdefaults = numargs;
3322 if (print_defaults && proc->pronargdefaults > 0)
3323 {
3325 bool isnull;
3326
3329 &isnull);
3330 if (!isnull)
3331 {
3332 char *str;
3333
3336 pfree(str);
3338 /* nlackdefaults counts only *input* arguments lacking defaults */
3339 nlackdefaults = proc->pronargs - list_length(argdefaults);
3340 }
3341 }
3342
3343 /* Check for special treatment of ordered-set aggregates */
3344 if (proc->prokind == PROKIND_AGGREGATE)
3345 {
3348
3351 elog(ERROR, "cache lookup failed for aggregate %u",
3352 proc->oid);
3354 if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
3355 insertorderbyat = agg->aggnumdirectargs;
3357 }
3358
3359 argsprinted = 0;
3360 inputargno = 0;
3361 for (i = 0; i < numargs; i++)
3362 {
3363 Oid argtype = argtypes[i];
3364 char *argname = argnames ? argnames[i] : NULL;
3366 const char *modename;
3367 bool isinput;
3368
3369 switch (argmode)
3370 {
3371 case PROARGMODE_IN:
3372
3373 /*
3374 * For procedures, explicitly mark all argument modes, so as
3375 * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
3376 */
3377 if (proc->prokind == PROKIND_PROCEDURE)
3378 modename = "IN ";
3379 else
3380 modename = "";
3381 isinput = true;
3382 break;
3383 case PROARGMODE_INOUT:
3384 modename = "INOUT ";
3385 isinput = true;
3386 break;
3387 case PROARGMODE_OUT:
3388 modename = "OUT ";
3389 isinput = false;
3390 break;
3392 modename = "VARIADIC ";
3393 isinput = true;
3394 break;
3395 case PROARGMODE_TABLE:
3396 modename = "";
3397 isinput = false;
3398 break;
3399 default:
3400 elog(ERROR, "invalid parameter mode '%c'", argmode);
3401 modename = NULL; /* keep compiler quiet */
3402 isinput = false;
3403 break;
3404 }
3405 if (isinput)
3406 inputargno++; /* this is a 1-based counter */
3407
3409 continue;
3410
3412 {
3413 if (argsprinted)
3415 appendStringInfoString(buf, "ORDER BY ");
3416 }
3417 else if (argsprinted)
3419
3421 if (argname && argname[0])
3422 appendStringInfo(buf, "%s ", quote_identifier(argname));
3425 {
3426 Node *expr;
3427
3429 expr = (Node *) lfirst(nextargdefault);
3431
3432 appendStringInfo(buf, " DEFAULT %s",
3433 deparse_expression(expr, NIL, false, false));
3434 }
3435 argsprinted++;
3436
3437 /* nasty hack: print the last arg twice for variadic ordered-set agg */
3438 if (argsprinted == insertorderbyat && i == numargs - 1)
3439 {
3440 i--;
3441 /* aggs shouldn't have defaults anyway, but just to be sure ... */
3442 print_defaults = false;
3443 }
3444 }
3445
3446 return argsprinted;
3447}

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

3265{
3267 int ntabargs = 0;
3269
3271
3272 if (proc->proretset)
3273 {
3274 /* It might be a table function; try to print the arguments */
3275 appendStringInfoString(&rbuf, "TABLE(");
3277 if (ntabargs > 0)
3279 else
3281 }
3282
3283 if (ntabargs == 0)
3284 {
3285 /* Not a table function, so do the normal thing */
3286 if (proc->proretset)
3287 appendStringInfoString(&rbuf, "SETOF ");
3288 appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
3289 }
3290
3291 appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
3292}

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

3561{
3562 int numargs;
3563 Oid *argtypes;
3564 char **argnames;
3565 char *argmodes;
3566 deparse_namespace dpns = {0};
3567 Datum tmp;
3568 Node *n;
3569
3571 numargs = get_func_arg_info(proctup,
3572 &argtypes, &argnames, &argmodes);
3573 dpns.numargs = numargs;
3574 dpns.argnames = argnames;
3575
3578
3579 if (IsA(n, List))
3580 {
3581 List *stmts;
3582 ListCell *lc;
3583
3584 stmts = linitial(castNode(List, n));
3585
3586 appendStringInfoString(buf, "BEGIN ATOMIC\n");
3587
3588 foreach(lc, stmts)
3589 {
3590 Query *query = lfirst_node(Query, lc);
3591
3592 /* It seems advisable to get at least AccessShareLock on rels */
3593 AcquireRewriteLocks(query, false, false);
3594 get_query_def(query, buf, list_make1(&dpns), NULL, false,
3598 }
3599
3601 }
3602 else
3603 {
3604 Query *query = castNode(Query, n);
3605
3606 /* It seems advisable to get at least AccessShareLock on rels */
3607 AcquireRewriteLocks(query, false, false);
3608 get_query_def(query, buf, list_make1(&dpns), NULL, false,
3609 0, WRAP_COLUMN_DEFAULT, 0);
3610 }
3611}

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

3463{
3464 Oid *trftypes;
3465 int ntypes;
3466
3467 ntypes = get_func_trftypes(proctup, &trftypes);
3468 if (ntypes > 0)
3469 {
3470 int i;
3471
3472 appendStringInfoString(buf, " TRANSFORM ");
3473 for (i = 0; i < ntypes; i++)
3474 {
3475 if (i != 0)
3477 appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
3478 }
3480 }
3481}

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

13050{
13051 StringInfo buf = context->buf;
13054
13055 lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
13056 foreach(uplist_item, sbsref->refupperindexpr)
13057 {
13059 if (lowlist_item)
13060 {
13061 /* If subexpression is NULL, get_rule_expr prints nothing */
13062 get_rule_expr((Node *) lfirst(lowlist_item), context, false);
13065 }
13066 /* If subexpression is NULL, get_rule_expr prints nothing */
13067 get_rule_expr((Node *) lfirst(uplist_item), context, false);
13069 }
13070}

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

12972{
12973 StringInfo buf = context->buf;
12975
12976 for (;;)
12977 {
12978 if (node == NULL)
12979 break;
12980 if (IsA(node, FieldStore))
12981 {
12982 FieldStore *fstore = (FieldStore *) node;
12983 Oid typrelid;
12984 char *fieldname;
12985
12986 /* lookup tuple type */
12987 typrelid = get_typ_typrelid(fstore->resulttype);
12988 if (!OidIsValid(typrelid))
12989 elog(ERROR, "argument type %s of FieldStore is not a tuple type",
12990 format_type_be(fstore->resulttype));
12991
12992 /*
12993 * Print the field name. There should only be one target field in
12994 * stored rules. There could be more than that in executable
12995 * target lists, but this function cannot be used for that case.
12996 */
12997 Assert(list_length(fstore->fieldnums) == 1);
12998 fieldname = get_attname(typrelid,
12999 linitial_int(fstore->fieldnums), false);
13000 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
13001
13002 /*
13003 * We ignore arg since it should be an uninteresting reference to
13004 * the target column or subcolumn.
13005 */
13006 node = (Node *) linitial(fstore->newvals);
13007 }
13008 else if (IsA(node, SubscriptingRef))
13009 {
13010 SubscriptingRef *sbsref = (SubscriptingRef *) node;
13011
13012 if (sbsref->refassgnexpr == NULL)
13013 break;
13014
13015 printSubscripts(sbsref, context);
13016
13017 /*
13018 * We ignore refexpr since it should be an uninteresting reference
13019 * to the target column or subcolumn.
13020 */
13021 node = (Node *) sbsref->refassgnexpr;
13022 }
13023 else if (IsA(node, CoerceToDomain))
13024 {
13025 cdomain = (CoerceToDomain *) node;
13026 /* If it's an explicit domain coercion, we're done */
13027 if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
13028 break;
13029 /* Tentatively descend past the CoerceToDomain */
13030 node = (Node *) cdomain->arg;
13031 }
13032 else
13033 break;
13034 }
13035
13036 /*
13037 * If we descended past a CoerceToDomain whose argument turned out not to
13038 * be a FieldStore or array assignment, back up to the CoerceToDomain.
13039 * (This is not enough to be fully correct if there are nested implicit
13040 * CoerceToDomains, but such cases shouldn't ever occur.)
13041 */
13042 if (cdomain && node == (Node *) cdomain->arg)
13043 node = (Node *) cdomain;
13044
13045 return node;
13046}

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

◆ push_ancestor_plan()

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

Definition at line 5314 of file ruleutils.c.

5316{
5318
5319 /* Save state for restoration later */
5320 *save_dpns = *dpns;
5321
5322 /* Build a new ancestor list with just this node's ancestors */
5323 dpns->ancestors =
5324 list_copy_tail(dpns->ancestors,
5325 list_cell_number(dpns->ancestors, ancestor_cell) + 1);
5326
5327 /* Set attention on selected ancestor */
5329}

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

5269{
5270 /* Save state for restoration later */
5271 *save_dpns = *dpns;
5272
5273 /* Link current plan node into ancestors list */
5274 dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
5275
5276 /* Set attention on selected child */
5278}

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

13080{
13081 /*
13082 * Can avoid quoting if ident starts with a lowercase letter or underscore
13083 * and contains only lowercase letters, digits, and underscores, *and* is
13084 * not any SQL keyword. Otherwise, supply quotes.
13085 */
13086 int nquotes = 0;
13087 bool safe;
13088 const char *ptr;
13089 char *result;
13090 char *optr;
13091
13092 /*
13093 * would like to use <ctype.h> macros here, but they might yield unwanted
13094 * locale-specific results...
13095 */
13096 safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
13097
13098 for (ptr = ident; *ptr; ptr++)
13099 {
13100 char ch = *ptr;
13101
13102 if ((ch >= 'a' && ch <= 'z') ||
13103 (ch >= '0' && ch <= '9') ||
13104 (ch == '_'))
13105 {
13106 /* okay */
13107 }
13108 else
13109 {
13110 safe = false;
13111 if (ch == '"')
13112 nquotes++;
13113 }
13114 }
13115
13117 safe = false;
13118
13119 if (safe)
13120 {
13121 /*
13122 * Check for keyword. We quote keywords except for unreserved ones.
13123 * (In some cases we could avoid quoting a col_name or type_func_name
13124 * keyword, but it seems much harder than it's worth to tell that.)
13125 *
13126 * Note: ScanKeywordLookup() does case-insensitive comparison, but
13127 * that's fine, since we already know we have all-lower-case.
13128 */
13130
13132 safe = false;
13133 }
13134
13135 if (safe)
13136 return ident; /* no change needed */
13137
13138 result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13139
13140 optr = result;
13141 *optr++ = '"';
13142 for (ptr = ident; *ptr; ptr++)
13143 {
13144 char ch = *ptr;
13145
13146 if (ch == '"')
13147 *optr++ = '"';
13148 *optr++ = ch;
13149 }
13150 *optr++ = '"';
13151 *optr = '\0';
13152
13153 return result;
13154}

References fb(), ident, palloc(), quote_all_identifiers, 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_from_clause_coldeflist(), 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_opclass_name(), get_parameter(), get_reloptions(), get_returning_clause(), get_rte_alias(), get_rule_expr(), get_rule_windowclause(), get_rule_windowspec(), get_select_query_def(), get_target_list(), get_update_query_targetlist_def(), get_utility_query_def(), get_variable(), get_windowfunc_expr_helper(), get_with_clause(), get_xmltable(), getObjectIdentityParts(), libpqrcv_alter_slot(), make_ruledef(), NameListToQuotedString(), old_9_6_invalidate_hash_indexes(), overexplain_alias(), overexplain_range_table(), pg_get_constraintdef_worker(), pg_get_functiondef(), pg_get_indexdef_worker(), pg_get_partkeydef_worker(), pg_get_statisticsobj_worker(), pg_get_triggerdef_worker(), pg_identify_object(), PLy_quote_ident(), postgresExplainForeignScan(), postgresImportForeignSchema(), print_function_arguments(), process_extension_updates(), processIndirection(), quote_ident(), quote_object_name(), quote_qualified_identifier(), regdatabaseout(), regnamespaceout(), regoperout(), regroleout(), ReplicationSlotDropAtPubNode(), sepgsql_attribute_post_create(), sepgsql_database_post_create(), sepgsql_relation_post_create(), sepgsql_schema_post_create(), serialize_deflist(), set_frozenxids(), show_sortorder_options(), show_window_def(), test_inline_in_from_support_func(), text_format_string_conversion(), tuple_to_stringinfo(), and worker_spi_main().

◆ quote_qualified_identifier()

◆ removeStringInfoSpaces()

static void removeStringInfoSpaces ( StringInfo  str)
static

Definition at line 9168 of file ruleutils.c.

9169{
9170 while (str->len > 0 && str->data[str->len - 1] == ' ')
9171 str->data[--(str->len)] = '\0';
9172}

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

7935{
7936 Var *var;
7938
7939 /* This function is recursive, so let's be paranoid. */
7941
7942 /* If it's not a Var, invoke the callback. */
7943 if (!IsA(node, Var))
7944 {
7945 (*callback) (node, context, callback_arg);
7946 return;
7947 }
7948
7949 /* Find appropriate nesting depth */
7950 var = (Var *) node;
7952 var->varlevelsup);
7953
7954 /*
7955 * If varno is special, recurse. (Don't worry about varnosyn; if we're
7956 * here, we already decided not to use that.)
7957 */
7958 if (var->varno == OUTER_VAR && dpns->outer_tlist)
7959 {
7963
7964 tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
7965 if (!tle)
7966 elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
7967
7968 /*
7969 * If we're descending to the first child of an Append or MergeAppend,
7970 * update appendparents. This will affect deparsing of all Vars
7971 * appearing within the eventually-resolved subexpression.
7972 */
7974
7975 if (IsA(dpns->plan, Append))
7976 context->appendparents = bms_union(context->appendparents,
7977 ((Append *) dpns->plan)->apprelids);
7978 else if (IsA(dpns->plan, MergeAppend))
7979 context->appendparents = bms_union(context->appendparents,
7980 ((MergeAppend *) dpns->plan)->apprelids);
7981
7982 push_child_plan(dpns, dpns->outer_plan, &save_dpns);
7983 resolve_special_varno((Node *) tle->expr, context,
7984 callback, callback_arg);
7987 return;
7988 }
7989 else if (var->varno == INNER_VAR && dpns->inner_tlist)
7990 {
7993
7994 tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
7995 if (!tle)
7996 elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
7997
7998 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7999 resolve_special_varno((Node *) tle->expr, context,
8000 callback, callback_arg);
8002 return;
8003 }
8004 else if (var->varno == INDEX_VAR && dpns->index_tlist)
8005 {
8007
8008 tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
8009 if (!tle)
8010 elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
8011
8012 resolve_special_varno((Node *) tle->expr, context,
8013 callback, callback_arg);
8014 return;
8015 }
8016 else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
8017 elog(ERROR, "bogus varno: %d", var->varno);
8018
8019 /* Not special. Just invoke the callback. */
8020 (*callback) (node, context, callback_arg);
8021}

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

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

◆ select_rtable_names_for_explain()

List * select_rtable_names_for_explain ( List rtable,
Bitmapset rels_used 
)

Definition at line 3859 of file ruleutils.c.

3860{
3862
3863 memset(&dpns, 0, sizeof(dpns));
3864 dpns.rtable = rtable;
3865 dpns.subplans = NIL;
3866 dpns.ctes = NIL;
3867 dpns.appendrels = NULL;
3869 /* We needn't bother computing column aliases yet */
3870
3871 return dpns.rtable_names;
3872}

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

3830{
3832
3833 /* Should always have one-entry namespace list for Plan deparsing */
3836
3837 /* Set our attention on the specific plan node passed in */
3838 dpns->ancestors = ancestors;
3840
3841 /* For ModifyTable, set aliases for OLD and NEW in RETURNING */
3842 if (IsA(plan, ModifyTable))
3843 {
3844 dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias;
3845 dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias;
3846 }
3847
3848 return dpcontext;
3849}

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

4035{
4036 ListCell *lc;
4037 ListCell *lc2;
4038
4039 /* Initialize *dpns and fill rtable/ctes links */
4040 memset(dpns, 0, sizeof(deparse_namespace));
4041 dpns->rtable = query->rtable;
4042 dpns->subplans = NIL;
4043 dpns->ctes = query->cteList;
4044 dpns->appendrels = NULL;
4045 dpns->ret_old_alias = query->returningOldAlias;
4046 dpns->ret_new_alias = query->returningNewAlias;
4047
4048 /* Assign a unique relation alias to each RTE */
4050
4051 /* Initialize dpns->rtable_columns to contain zeroed structs */
4052 dpns->rtable_columns = NIL;
4053 while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4054 dpns->rtable_columns = lappend(dpns->rtable_columns,
4055 palloc0(sizeof(deparse_columns)));
4056
4057 /* If it's a utility query, it won't have a jointree */
4058 if (query->jointree)
4059 {
4060 /* Detect whether global uniqueness of USING names is needed */
4061 dpns->unique_using =
4063
4064 /*
4065 * Select names for columns merged by USING, via a recursive pass over
4066 * the query jointree.
4067 */
4068 set_using_names(dpns, (Node *) query->jointree, NIL);
4069 }
4070
4071 /*
4072 * Now assign remaining column aliases for each RTE. We do this in a
4073 * linear scan of the rtable, so as to process RTEs whether or not they
4074 * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
4075 * etc). JOIN RTEs must be processed after their children, but this is
4076 * okay because they appear later in the rtable list than their children
4077 * (cf Asserts in identify_join_columns()).
4078 */
4079 forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4080 {
4083
4084 if (rte->rtekind == RTE_JOIN)
4086 else
4088 }
4089}

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

5157{
5158 dpns->plan = plan;
5159
5160 /*
5161 * We special-case Append and MergeAppend to pretend that the first child
5162 * plan is the OUTER referent; we have to interpret OUTER Vars in their
5163 * tlists according to one of the children, and the first one is the most
5164 * natural choice.
5165 */
5166 if (IsA(plan, Append))
5167 dpns->outer_plan = linitial(((Append *) plan)->appendplans);
5168 else if (IsA(plan, MergeAppend))
5169 dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
5170 else
5171 dpns->outer_plan = outerPlan(plan);
5172
5173 if (dpns->outer_plan)
5174 dpns->outer_tlist = dpns->outer_plan->targetlist;
5175 else
5176 dpns->outer_tlist = NIL;
5177
5178 /*
5179 * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
5180 * use OUTER because that could someday conflict with the normal meaning.)
5181 * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
5182 * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
5183 * that as INNER referent.
5184 *
5185 * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
5186 * INNER referent. This is the join from the target relation to the data
5187 * source, and all INNER_VAR Vars in other parts of the query refer to its
5188 * targetlist.
5189 *
5190 * For ON CONFLICT DO SELECT/UPDATE we just need the inner tlist to point
5191 * to the excluded expression's tlist. (Similar to the SubqueryScan we
5192 * don't want to reuse OUTER, it's used for RETURNING in some modify table
5193 * cases, although not INSERT .. CONFLICT).
5194 */
5195 if (IsA(plan, SubqueryScan))
5196 dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
5197 else if (IsA(plan, CteScan))
5198 dpns->inner_plan = list_nth(dpns->subplans,
5199 ((CteScan *) plan)->ctePlanId - 1);
5200 else if (IsA(plan, WorkTableScan))
5201 dpns->inner_plan = find_recursive_union(dpns,
5202 (WorkTableScan *) plan);
5203 else if (IsA(plan, ModifyTable))
5204 {
5205 if (((ModifyTable *) plan)->operation == CMD_MERGE)
5206 dpns->inner_plan = outerPlan(plan);
5207 else
5208 dpns->inner_plan = plan;
5209 }
5210 else
5211 dpns->inner_plan = innerPlan(plan);
5212
5213 if (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)
5214 dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
5215 else if (dpns->inner_plan)
5216 dpns->inner_tlist = dpns->inner_plan->targetlist;
5217 else
5218 dpns->inner_tlist = NIL;
5219
5220 /* Set up referent for INDEX_VAR Vars, if needed */
5221 if (IsA(plan, IndexOnlyScan))
5222 dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
5223 else if (IsA(plan, ForeignScan))
5224 dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
5225 else if (IsA(plan, CustomScan))
5226 dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
5227 else
5228 dpns->index_tlist = NIL;
5229}

References CMD_INSERT, CMD_MERGE, fb(), find_recursive_union(), innerPlan, IsA, linitial, list_nth(), NIL, 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 4582 of file ruleutils.c.

4584{
4587 bool changed_any;
4588 int noldcolumns;
4589 int nnewcolumns;
4592 int i;
4593 int j;
4594 int ic;
4595 int jc;
4596
4597 /* Look up the previously-filled-in child deparse_columns structs */
4600
4601 /*
4602 * Ensure colinfo->colnames has a slot for each column. (It could be long
4603 * enough already, if we pushed down a name for the last column.) Note:
4604 * it's possible that one or both inputs now have more columns than there
4605 * were when the query was parsed, but we'll deal with that below. We
4606 * only need entries in colnames for pre-existing columns.
4607 */
4608 noldcolumns = list_length(rte->eref->colnames);
4610 Assert(colinfo->num_cols == noldcolumns);
4611
4612 /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4614
4615 /*
4616 * Scan the join output columns, select an alias for each one, and store
4617 * it in colinfo->colnames. If there are USING columns, set_using_names()
4618 * already selected their names, so we can start the loop at the first
4619 * non-merged column.
4620 */
4621 changed_any = false;
4622 for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
4623 {
4624 char *colname = colinfo->colnames[i];
4625 char *real_colname;
4626
4627 /* Join column must refer to at least one input column */
4628 Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
4629
4630 /* Get the child column name */
4631 if (colinfo->leftattnos[i] > 0)
4632 real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
4633 else if (colinfo->rightattnos[i] > 0)
4634 real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
4635 else
4636 {
4637 /* We're joining system columns --- use eref name */
4638 real_colname = strVal(list_nth(rte->eref->colnames, i));
4639 }
4640
4641 /* If child col has been dropped, no need to assign a join colname */
4642 if (real_colname == NULL)
4643 {
4644 colinfo->colnames[i] = NULL;
4645 continue;
4646 }
4647
4648 /* In an unnamed join, just report child column names as-is */
4649 if (rte->alias == NULL)
4650 {
4651 colinfo->colnames[i] = real_colname;
4653 continue;
4654 }
4655
4656 /* If alias already assigned, that's what to use */
4657 if (colname == NULL)
4658 {
4659 /* If user wrote an alias, prefer that over real column name */
4660 if (rte->alias && i < list_length(rte->alias->colnames))
4661 colname = strVal(list_nth(rte->alias->colnames, i));
4662 else
4663 colname = real_colname;
4664
4665 /* Unique-ify and insert into colinfo */
4666 colname = make_colname_unique(colname, dpns, colinfo);
4667
4668 colinfo->colnames[i] = colname;
4669 add_to_names_hash(colinfo, colname);
4670 }
4671
4672 /* Remember if any assigned aliases differ from "real" name */
4673 if (!changed_any && strcmp(colname, real_colname) != 0)
4674 changed_any = true;
4675 }
4676
4677 /*
4678 * Calculate number of columns the join would have if it were re-parsed
4679 * now, and create storage for the new_colnames and is_new_col arrays.
4680 *
4681 * Note: colname_is_unique will be consulting new_colnames[] during the
4682 * loops below, so its not-yet-filled entries must be zeroes.
4683 */
4684 nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
4685 list_length(colinfo->usingNames);
4686 colinfo->num_new_cols = nnewcolumns;
4687 colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
4688 colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
4689
4690 /*
4691 * Generating the new_colnames array is a bit tricky since any new columns
4692 * added since parse time must be inserted in the right places. This code
4693 * must match the parser, which will order a join's columns as merged
4694 * columns first (in USING-clause order), then non-merged columns from the
4695 * left input (in attnum order), then non-merged columns from the right
4696 * input (ditto). If one of the inputs is itself a join, its columns will
4697 * be ordered according to the same rule, which means newly-added columns
4698 * might not be at the end. We can figure out what's what by consulting
4699 * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
4700 *
4701 * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
4702 * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
4703 * meanings for the current child RTE.
4704 */
4705
4706 /* Handle merged columns; they are first and can't be new */
4707 i = j = 0;
4708 while (i < noldcolumns &&
4709 colinfo->leftattnos[i] != 0 &&
4710 colinfo->rightattnos[i] != 0)
4711 {
4712 /* column name is already determined and known unique */
4713 colinfo->new_colnames[j] = colinfo->colnames[i];
4714 colinfo->is_new_col[j] = false;
4715
4716 /* build bitmapsets of child attnums of merged columns */
4717 if (colinfo->leftattnos[i] > 0)
4718 leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
4719 if (colinfo->rightattnos[i] > 0)
4721
4722 i++, j++;
4723 }
4724
4725 /* Handle non-merged left-child columns */
4726 ic = 0;
4727 for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
4728 {
4729 char *child_colname = leftcolinfo->new_colnames[jc];
4730
4731 if (!leftcolinfo->is_new_col[jc])
4732 {
4733 /* Advance ic to next non-dropped old column of left child */
4734 while (ic < leftcolinfo->num_cols &&
4735 leftcolinfo->colnames[ic] == NULL)
4736 ic++;
4737 Assert(ic < leftcolinfo->num_cols);
4738 ic++;
4739 /* If it is a merged column, we already processed it */
4741 continue;
4742 /* Else, advance i to the corresponding existing join column */
4743 while (i < colinfo->num_cols &&
4744 colinfo->colnames[i] == NULL)
4745 i++;
4746 Assert(i < colinfo->num_cols);
4747 Assert(ic == colinfo->leftattnos[i]);
4748 /* Use the already-assigned name of this column */
4749 colinfo->new_colnames[j] = colinfo->colnames[i];
4750 i++;
4751 }
4752 else
4753 {
4754 /*
4755 * Unique-ify the new child column name and assign, unless we're
4756 * in an unnamed join, in which case just copy
4757 */
4758 if (rte->alias != NULL)
4759 {
4760 colinfo->new_colnames[j] =
4762 if (!changed_any &&
4763 strcmp(colinfo->new_colnames[j], child_colname) != 0)
4764 changed_any = true;
4765 }
4766 else
4767 colinfo->new_colnames[j] = child_colname;
4768 add_to_names_hash(colinfo, colinfo->new_colnames[j]);
4769 }
4770
4771 colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
4772 j++;
4773 }
4774
4775 /* Handle non-merged right-child columns in exactly the same way */
4776 ic = 0;
4777 for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
4778 {
4779 char *child_colname = rightcolinfo->new_colnames[jc];
4780
4781 if (!rightcolinfo->is_new_col[jc])
4782 {
4783 /* Advance ic to next non-dropped old column of right child */
4784 while (ic < rightcolinfo->num_cols &&
4785 rightcolinfo->colnames[ic] == NULL)
4786 ic++;
4787 Assert(ic < rightcolinfo->num_cols);
4788 ic++;
4789 /* If it is a merged column, we already processed it */
4791 continue;
4792 /* Else, advance i to the corresponding existing join column */
4793 while (i < colinfo->num_cols &&
4794 colinfo->colnames[i] == NULL)
4795 i++;
4796 Assert(i < colinfo->num_cols);
4797 Assert(ic == colinfo->rightattnos[i]);
4798 /* Use the already-assigned name of this column */
4799 colinfo->new_colnames[j] = colinfo->colnames[i];
4800 i++;
4801 }
4802 else
4803 {
4804 /*
4805 * Unique-ify the new child column name and assign, unless we're
4806 * in an unnamed join, in which case just copy
4807 */
4808 if (rte->alias != NULL)
4809 {
4810 colinfo->new_colnames[j] =
4812 if (!changed_any &&
4813 strcmp(colinfo->new_colnames[j], child_colname) != 0)
4814 changed_any = true;
4815 }
4816 else
4817 colinfo->new_colnames[j] = child_colname;
4818 add_to_names_hash(colinfo, colinfo->new_colnames[j]);
4819 }
4820
4821 colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
4822 j++;
4823 }
4824
4825 /* Assert we processed the right number of columns */
4826#ifdef USE_ASSERT_CHECKING
4827 while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
4828 i++;
4829 Assert(i == colinfo->num_cols);
4830 Assert(j == nnewcolumns);
4831#endif
4832
4833 /* We're now done needing the colinfo's names_hash */
4835
4836 /*
4837 * For a named join, print column aliases if we changed any from the child
4838 * names. Unnamed joins cannot print aliases.
4839 */
4840 if (rte->alias != NULL)
4841 colinfo->printaliases = changed_any;
4842 else
4843 colinfo->printaliases = false;
4844}

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

4381{
4382 int ncolumns;
4383 char **real_colnames;
4384 bool changed_any;
4385 int noldcolumns;
4386 int i;
4387 int j;
4388
4389 /*
4390 * Construct an array of the current "real" column names of the RTE.
4391 * real_colnames[] will be indexed by physical column number, with NULL
4392 * entries for dropped columns.
4393 */
4394 if (rte->rtekind == RTE_RELATION)
4395 {
4396 /* Relation --- look to the system catalogs for up-to-date info */
4397 Relation rel;
4398 TupleDesc tupdesc;
4399
4400 rel = relation_open(rte->relid, AccessShareLock);
4401 tupdesc = RelationGetDescr(rel);
4402
4403 ncolumns = tupdesc->natts;
4404 real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4405
4406 for (i = 0; i < ncolumns; i++)
4407 {
4408 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
4409
4410 if (attr->attisdropped)
4411 real_colnames[i] = NULL;
4412 else
4413 real_colnames[i] = pstrdup(NameStr(attr->attname));
4414 }
4416 }
4417 else
4418 {
4419 /* Otherwise get the column names from eref or expandRTE() */
4420 List *colnames;
4421 ListCell *lc;
4422
4423 /*
4424 * Functions returning composites have the annoying property that some
4425 * of the composite type's columns might have been dropped since the
4426 * query was parsed. If possible, use expandRTE() to handle that
4427 * case, since it has the tedious logic needed to find out about
4428 * dropped columns. However, if we're explaining a plan, then we
4429 * don't have rte->functions because the planner thinks that won't be
4430 * needed later, and that breaks expandRTE(). So in that case we have
4431 * to rely on rte->eref, which may lead us to report a dropped
4432 * column's old name; that seems close enough for EXPLAIN's purposes.
4433 *
4434 * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
4435 * which should be sufficiently up-to-date: no other RTE types can
4436 * have columns get dropped from under them after parsing.
4437 */
4438 if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
4439 {
4440 /* Since we're not creating Vars, rtindex etc. don't matter */
4442 true /* include dropped */ , &colnames, NULL);
4443 }
4444 else
4445 colnames = rte->eref->colnames;
4446
4447 ncolumns = list_length(colnames);
4448 real_colnames = (char **) palloc(ncolumns * sizeof(char *));
4449
4450 i = 0;
4451 foreach(lc, colnames)
4452 {
4453 /*
4454 * If the column name we find here is an empty string, then it's a
4455 * dropped column, so change to NULL.
4456 */
4457 char *cname = strVal(lfirst(lc));
4458
4459 if (cname[0] == '\0')
4460 cname = NULL;
4462 i++;
4463 }
4464 }
4465
4466 /*
4467 * Ensure colinfo->colnames has a slot for each column. (It could be long
4468 * enough already, if we pushed down a name for the last column.) Note:
4469 * it's possible that there are now more columns than there were when the
4470 * query was parsed, ie colnames could be longer than rte->eref->colnames.
4471 * We must assign unique aliases to the new columns too, else there could
4472 * be unresolved conflicts when the view/rule is reloaded.
4473 */
4475 Assert(colinfo->num_cols == ncolumns);
4476
4477 /*
4478 * Make sufficiently large new_colnames and is_new_col arrays, too.
4479 *
4480 * Note: because we leave colinfo->num_new_cols zero until after the loop,
4481 * colname_is_unique will not consult that array, which is fine because it
4482 * would only be duplicate effort.
4483 */
4484 colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
4485 colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
4486
4487 /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
4489
4490 /*
4491 * Scan the columns, select a unique alias for each one, and store it in
4492 * colinfo->colnames and colinfo->new_colnames. The former array has NULL
4493 * entries for dropped columns, the latter omits them. Also mark
4494 * new_colnames entries as to whether they are new since parse time; this
4495 * is the case for entries beyond the length of rte->eref->colnames.
4496 */
4497 noldcolumns = list_length(rte->eref->colnames);
4498 changed_any = false;
4499 j = 0;
4500 for (i = 0; i < ncolumns; i++)
4501 {
4502 char *real_colname = real_colnames[i];
4503 char *colname = colinfo->colnames[i];
4504
4505 /* Skip dropped columns */
4506 if (real_colname == NULL)
4507 {
4508 Assert(colname == NULL); /* colnames[i] is already NULL */
4509 continue;
4510 }
4511
4512 /* If alias already assigned, that's what to use */
4513 if (colname == NULL)
4514 {
4515 /* If user wrote an alias, prefer that over real column name */
4516 if (rte->alias && i < list_length(rte->alias->colnames))
4517 colname = strVal(list_nth(rte->alias->colnames, i));
4518 else
4519 colname = real_colname;
4520
4521 /* Unique-ify and insert into colinfo */
4522 colname = make_colname_unique(colname, dpns, colinfo);
4523
4524 colinfo->colnames[i] = colname;
4525 add_to_names_hash(colinfo, colname);
4526 }
4527
4528 /* Put names of non-dropped columns in new_colnames[] too */
4529 colinfo->new_colnames[j] = colname;
4530 /* And mark them as new or not */
4531 colinfo->is_new_col[j] = (i >= noldcolumns);
4532 j++;
4533
4534 /* Remember if any assigned aliases differ from "real" name */
4535 if (!changed_any && strcmp(colname, real_colname) != 0)
4536 changed_any = true;
4537 }
4538
4539 /* We're now done needing the colinfo's names_hash */
4541
4542 /*
4543 * Set correct length for new_colnames[] array. (Note: if columns have
4544 * been added, colinfo->num_cols includes them, which is not really quite
4545 * right but is harmless, since any new columns must be at the end where
4546 * they won't affect varattnos of pre-existing columns.)
4547 */
4548 colinfo->num_new_cols = j;
4549
4550 /*
4551 * For a relation RTE, we need only print the alias column names if any
4552 * are different from the underlying "real" names. For a function RTE,
4553 * always emit a complete column alias list; this is to protect against
4554 * possible instability of the default column names (eg, from altering
4555 * parameter names). For tablefunc RTEs, we never print aliases, because
4556 * the column names are part of the clause itself. For other RTE types,
4557 * print if we changed anything OR if there were user-written column
4558 * aliases (since the latter would be part of the underlying "reality").
4559 */
4560 if (rte->rtekind == RTE_RELATION)
4561 colinfo->printaliases = changed_any;
4562 else if (rte->rtekind == RTE_FUNCTION)
4563 colinfo->printaliases = true;
4564 else if (rte->rtekind == RTE_TABLEFUNC)
4565 colinfo->printaliases = false;
4566 else if (rte->alias && rte->alias->colnames != NIL)
4567 colinfo->printaliases = true;
4568 else
4569 colinfo->printaliases = changed_any;
4570}

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

3890{
3892 HTAB *names_hash;
3894 bool found;
3895 int rtindex;
3896 ListCell *lc;
3897
3898 dpns->rtable_names = NIL;
3899 /* nothing more to do if empty rtable */
3900 if (dpns->rtable == NIL)
3901 return;
3902
3903 /*
3904 * We use a hash table to hold known names, so that this process is O(N)
3905 * not O(N^2) for N names.
3906 */
3907 hash_ctl.keysize = NAMEDATALEN;
3908 hash_ctl.entrysize = sizeof(NameHashEntry);
3910 names_hash = hash_create("set_rtable_names names",
3911 list_length(dpns->rtable),
3912 &hash_ctl,
3914
3915 /* Preload the hash table with names appearing in parent_namespaces */
3916 foreach(lc, parent_namespaces)
3917 {
3919 ListCell *lc2;
3920
3921 foreach(lc2, olddpns->rtable_names)
3922 {
3923 char *oldname = (char *) lfirst(lc2);
3924
3925 if (oldname == NULL)
3926 continue;
3927 hentry = (NameHashEntry *) hash_search(names_hash,
3928 oldname,
3929 HASH_ENTER,
3930 &found);
3931 /* we do not complain about duplicate names in parent namespaces */
3932 hentry->counter = 0;
3933 }
3934 }
3935
3936 /* Now we can scan the rtable */
3937 rtindex = 1;
3938 foreach(lc, dpns->rtable)
3939 {
3941 char *refname;
3942
3943 /* Just in case this takes an unreasonable amount of time ... */
3945
3946 if (rels_used && !bms_is_member(rtindex, rels_used))
3947 {
3948 /* Ignore unreferenced RTE */
3949 refname = NULL;
3950 }
3951 else if (rte->alias)
3952 {
3953 /* If RTE has a user-defined alias, prefer that */
3954 refname = rte->alias->aliasname;
3955 }
3956 else if (rte->rtekind == RTE_RELATION)
3957 {
3958 /* Use the current actual name of the relation */
3959 refname = get_rel_name(rte->relid);
3960 }
3961 else if (rte->rtekind == RTE_JOIN)
3962 {
3963 /* Unnamed join has no refname */
3964 refname = NULL;
3965 }
3966 else
3967 {
3968 /* Otherwise use whatever the parser assigned */
3969 refname = rte->eref->aliasname;
3970 }
3971
3972 /*
3973 * If the selected name isn't unique, append digits to make it so, and
3974 * make a new hash entry for it once we've got a unique name. For a
3975 * very long input name, we might have to truncate to stay within
3976 * NAMEDATALEN.
3977 */
3978 if (refname)
3979 {
3980 hentry = (NameHashEntry *) hash_search(names_hash,
3981 refname,
3982 HASH_ENTER,
3983 &found);
3984 if (found)
3985 {
3986 /* Name already in use, must choose a new one */
3987 int refnamelen = strlen(refname);
3988 char *modname = (char *) palloc(refnamelen + 16);
3990
3991 do
3992 {
3993 hentry->counter++;
3994 for (;;)
3995 {
3996 memcpy(modname, refname, refnamelen);
3997 sprintf(modname + refnamelen, "_%d", hentry->counter);
3998 if (strlen(modname) < NAMEDATALEN)
3999 break;
4000 /* drop chars from refname to keep all the digits */
4002 refnamelen - 1);
4003 }
4004 hentry2 = (NameHashEntry *) hash_search(names_hash,
4005 modname,
4006 HASH_ENTER,
4007 &found);
4008 } while (found);
4009 hentry2->counter = 0; /* init new hash entry */
4010 refname = modname;
4011 }
4012 else
4013 {
4014 /* Name not previously used, need only initialize hentry */
4015 hentry->counter = 0;
4016 }
4017 }
4018
4019 dpns->rtable_names = lappend(dpns->rtable_names, refname);
4020 rtindex++;
4021 }
4022
4023 hash_destroy(names_hash);
4024}

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(), 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 4102 of file ruleutils.c.

4103{
4104 ListCell *lc;
4105 ListCell *lc2;
4106
4107 /* Initialize dpns->rtable_columns to contain zeroed structs */
4108 dpns->rtable_columns = NIL;
4109 while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
4110 dpns->rtable_columns = lappend(dpns->rtable_columns,
4111 palloc0(sizeof(deparse_columns)));
4112
4113 /* Assign unique column aliases within each non-join RTE */
4114 forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
4115 {
4118
4119 if (rte->rtekind != RTE_JOIN)
4121 }
4122}

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

4215{
4216 if (IsA(jtnode, RangeTblRef))
4217 {
4218 /* nothing to do now */
4219 }
4220 else if (IsA(jtnode, FromExpr))
4221 {
4222 FromExpr *f = (FromExpr *) jtnode;
4223 ListCell *lc;
4224
4225 foreach(lc, f->fromlist)
4226 set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
4227 }
4228 else if (IsA(jtnode, JoinExpr))
4229 {
4230 JoinExpr *j = (JoinExpr *) jtnode;
4231 RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
4233 int *leftattnos;
4234 int *rightattnos;
4237 int i;
4238 ListCell *lc;
4239
4240 /* Get info about the shape of the join */
4242 leftattnos = colinfo->leftattnos;
4243 rightattnos = colinfo->rightattnos;
4244
4245 /* Look up the not-yet-filled-in child deparse_columns structs */
4248
4249 /*
4250 * If this join is unnamed, then we cannot substitute new aliases at
4251 * this level, so any name requirements pushed down to here must be
4252 * pushed down again to the children.
4253 */
4254 if (rte->alias == NULL)
4255 {
4256 for (i = 0; i < colinfo->num_cols; i++)
4257 {
4258 char *colname = colinfo->colnames[i];
4259
4260 if (colname == NULL)
4261 continue;
4262
4263 /* Push down to left column, unless it's a system column */
4264 if (leftattnos[i] > 0)
4265 {
4267 leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4268 }
4269
4270 /* Same on the righthand side */
4271 if (rightattnos[i] > 0)
4272 {
4274 rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4275 }
4276 }
4277 }
4278
4279 /*
4280 * If there's a USING clause, select the USING column names and push
4281 * those names down to the children. We have two strategies:
4282 *
4283 * If dpns->unique_using is true, we force all USING names to be
4284 * unique across the whole query level. In principle we'd only need
4285 * the names of dangerous USING columns to be globally unique, but to
4286 * safely assign all USING names in a single pass, we have to enforce
4287 * the same uniqueness rule for all of them. However, if a USING
4288 * column's name has been pushed down from the parent, we should use
4289 * it as-is rather than making a uniqueness adjustment. This is
4290 * necessary when we're at an unnamed join, and it creates no risk of
4291 * ambiguity. Also, if there's a user-written output alias for a
4292 * merged column, we prefer to use that rather than the input name;
4293 * this simplifies the logic and seems likely to lead to less aliasing
4294 * overall.
4295 *
4296 * If dpns->unique_using is false, we only need USING names to be
4297 * unique within their own join RTE. We still need to honor
4298 * pushed-down names, though.
4299 *
4300 * Though significantly different in results, these two strategies are
4301 * implemented by the same code, with only the difference of whether
4302 * to put assigned names into dpns->using_names.
4303 */
4304 if (j->usingClause)
4305 {
4306 /* Copy the input parentUsing list so we don't modify it */
4307 parentUsing = list_copy(parentUsing);
4308
4309 /* USING names must correspond to the first join output columns */
4311 i = 0;
4312 foreach(lc, j->usingClause)
4313 {
4314 char *colname = strVal(lfirst(lc));
4315
4316 /* Assert it's a merged column */
4317 Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
4318
4319 /* Adopt passed-down name if any, else select unique name */
4320 if (colinfo->colnames[i] != NULL)
4321 colname = colinfo->colnames[i];
4322 else
4323 {
4324 /* Prefer user-written output alias if any */
4325 if (rte->alias && i < list_length(rte->alias->colnames))
4326 colname = strVal(list_nth(rte->alias->colnames, i));
4327 /* Make it appropriately unique */
4328 colname = make_colname_unique(colname, dpns, colinfo);
4329 if (dpns->unique_using)
4330 dpns->using_names = lappend(dpns->using_names,
4331 colname);
4332 /* Save it as output column name, too */
4333 colinfo->colnames[i] = colname;
4334 }
4335
4336 /* Remember selected names for use later */
4337 colinfo->usingNames = lappend(colinfo->usingNames, colname);
4338 parentUsing = lappend(parentUsing, colname);
4339
4340 /* Push down to left column, unless it's a system column */
4341 if (leftattnos[i] > 0)
4342 {
4344 leftcolinfo->colnames[leftattnos[i] - 1] = colname;
4345 }
4346
4347 /* Same on the righthand side */
4348 if (rightattnos[i] > 0)
4349 {
4351 rightcolinfo->colnames[rightattnos[i] - 1] = colname;
4352 }
4353
4354 i++;
4355 }
4356 }
4357
4358 /* Mark child deparse_columns structs with correct parentUsing info */
4359 leftcolinfo->parentUsing = parentUsing;
4360 rightcolinfo->parentUsing = parentUsing;
4361
4362 /* Now recursively assign USING column names in children */
4363 set_using_names(dpns, j->larg, parentUsing);
4364 set_using_names(dpns, j->rarg, parentUsing);
4365 }
4366 else
4367 elog(ERROR, "unrecognized node type: %d",
4368 (int) nodeTag(jtnode));
4369}

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

11846{
11847 const char *valptr;
11848
11849 /*
11850 * We always form the string literal according to standard SQL rules.
11851 */
11853 for (valptr = val; *valptr; valptr++)
11854 {
11855 char ch = *valptr;
11856
11857 if (SQL_STR_DOUBLE(ch, false))
11860 }
11862}

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

Referenced by pg_get_ruledef_worker().

◆ plan_getviewrule

SPIPlanPtr plan_getviewrule = NULL
static

Definition at line 335 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 334 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 336 of file ruleutils.c.

Referenced by pg_get_viewdef_worker().

◆ quote_all_identifiers

bool quote_all_identifiers = false

Definition at line 339 of file ruleutils.c.

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