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

13513{
13516 char *typname;
13517 char *nspname;
13518
13521 elog(ERROR, "cache lookup failed for type %u", typid);
13523
13524 typname = NameStr(typform->typname);
13525 nspname = get_namespace_name_or_temp(typform->typnamespace);
13526
13527 appendStringInfo(buf, "::%s.%s",
13529
13531}

References appendStringInfo(), buf, elog, ERROR, fb(), 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 5039 of file ruleutils.c.

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

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

9097{
9098 StringInfo buf = context->buf;
9099
9100 if (PRETTY_INDENT(context))
9101 {
9102 int indentAmount;
9103
9104 context->indentLevel += indentBefore;
9105
9106 /* remove any trailing spaces currently in the buffer ... */
9108 /* ... then add a newline and some spaces */
9110
9111 if (context->indentLevel < PRETTYINDENT_LIMIT)
9112 indentAmount = Max(context->indentLevel, 0) + indentPlus;
9113 else
9114 {
9115 /*
9116 * If we're indented more than PRETTYINDENT_LIMIT characters, try
9117 * to conserve horizontal space by reducing the per-level
9118 * indentation. For best results the scale factor here should
9119 * divide all the indent amounts that get added to indentLevel
9120 * (PRETTYINDENT_STD, etc). It's important that the indentation
9121 * not grow unboundedly, else deeply-nested trees use O(N^2)
9122 * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
9123 */
9125 (context->indentLevel - PRETTYINDENT_LIMIT) /
9126 (PRETTYINDENT_STD / 2);
9128 /* scale/wrap logic affects indentLevel, but not indentPlus */
9130 }
9132
9134
9135 context->indentLevel += indentAfter;
9136 if (context->indentLevel < 0)
9137 context->indentLevel = 0;
9138 }
9139 else
9141}

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

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}

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

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}

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

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}

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

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}

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

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}

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

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

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

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}

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

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

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

8672{
8673 foreach_node(SubPlan, subplan, plan->initPlan)
8674 {
8675 foreach_int(paramid, subplan->setParam)
8676 {
8677 if (paramid == param->paramid)
8678 {
8679 /* Found a match, so return it. */
8680 *column_p = foreach_current_index(paramid);
8681 return subplan;
8682 }
8683 }
8684 }
8685 return NULL;
8686}

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

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

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

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}

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

Referenced by set_deparse_plan().

◆ flatten_reloptions()

static char * flatten_reloptions ( Oid  relid)
static

Definition at line 13676 of file ruleutils.c.

13677{
13678 char *result = NULL;
13679 HeapTuple tuple;
13680 Datum reloptions;
13681 bool isnull;
13682
13683 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13684 if (!HeapTupleIsValid(tuple))
13685 elog(ERROR, "cache lookup failed for relation %u", relid);
13686
13687 reloptions = SysCacheGetAttr(RELOID, tuple,
13688 Anum_pg_class_reloptions, &isnull);
13689 if (!isnull)
13690 {
13692
13694 get_reloptions(&buf, reloptions);
13695
13696 result = buf.data;
13697 }
13698
13699 ReleaseSysCache(tuple);
13700
13701 return result;
13702}

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

13577{
13578 HeapTuple tp;
13580 char *collname;
13581 char *nspname;
13582 char *result;
13583
13585 if (!HeapTupleIsValid(tp))
13586 elog(ERROR, "cache lookup failed for collation %u", collid);
13588 collname = NameStr(colltup->collname);
13589
13591 nspname = get_namespace_name_or_temp(colltup->collnamespace);
13592 else
13593 nspname = NULL;
13594
13595 result = quote_qualified_identifier(nspname, collname);
13596
13597 ReleaseSysCache(tp);
13598
13599 return result;
13600}

References CollationIsVisible(), collid, elog, ERROR, fb(), 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 13288 of file ruleutils.c.

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

References Assert, elog, ERROR, fb(), 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 12930 of file ruleutils.c.

12931{
12933
12935 get_opclass_name(opclass, InvalidOid, &buf);
12936
12937 return &buf.data[1]; /* get_opclass_name() prepends space */
12938}

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

13476{
13479 char *oprname;
13480 char *nspname;
13481
13484 elog(ERROR, "cache lookup failed for operator %u", opoid);
13486 Assert(operform->oprkind == 'b');
13487 oprname = NameStr(operform->oprname);
13488
13489 nspname = get_namespace_name(operform->oprnamespace);
13490
13492 if (leftoptype != operform->oprleft)
13493 add_cast_to(buf, operform->oprleft);
13494 appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
13495 appendStringInfoString(buf, oprname);
13496 appendStringInfo(buf, ") %s", rightop);
13497 if (rightoptype != operform->oprright)
13498 add_cast_to(buf, operform->oprright);
13499
13501}

References add_cast_to(), appendStringInfo(), appendStringInfoString(), Assert, buf, elog, ERROR, fb(), 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 13395 of file ruleutils.c.

13396{
13400 char *oprname;
13401 char *nspname;
13403
13405
13408 elog(ERROR, "cache lookup failed for operator %u", operid);
13410 oprname = NameStr(operform->oprname);
13411
13412 /*
13413 * The idea here is to schema-qualify only if the parser would fail to
13414 * resolve the correct operator given the unqualified op name with the
13415 * specified argtypes.
13416 */
13417 switch (operform->oprkind)
13418 {
13419 case 'b':
13421 true, -1);
13422 break;
13423 case 'l':
13425 true, -1);
13426 break;
13427 default:
13428 elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
13429 p_result = NULL; /* keep compiler quiet */
13430 break;
13431 }
13432
13433 if (p_result != NULL && oprid(p_result) == operid)
13434 nspname = NULL;
13435 else
13436 {
13437 nspname = get_namespace_name_or_temp(operform->oprnamespace);
13438 appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
13439 }
13440
13441 appendStringInfoString(&buf, oprname);
13442
13443 if (nspname)
13445
13446 if (p_result != NULL)
13448
13450
13451 return buf.data;
13452}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), buf, elog, ERROR, fb(), 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 13244 of file ruleutils.c.

13245{
13246 HeapTuple tp;
13248 char *relname;
13249 char *nspname;
13250 char *result;
13251
13253 if (!HeapTupleIsValid(tp))
13254 elog(ERROR, "cache lookup failed for relation %u", relid);
13256 relname = NameStr(reltup->relname);
13257
13258 nspname = get_namespace_name_or_temp(reltup->relnamespace);
13259 if (!nspname)
13260 elog(ERROR, "cache lookup failed for namespace %u",
13261 reltup->relnamespace);
13262
13263 result = quote_qualified_identifier(nspname, relname);
13264
13265 ReleaseSysCache(tp);
13266
13267 return result;
13268}

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

13544{
13545 HeapTuple tp;
13547 char *typname;
13548 char *nspname;
13549 char *result;
13550
13552 if (!HeapTupleIsValid(tp))
13553 elog(ERROR, "cache lookup failed for type %u", typid);
13555 typname = NameStr(typtup->typname);
13556
13557 nspname = get_namespace_name_or_temp(typtup->typnamespace);
13558 if (!nspname)
13559 elog(ERROR, "cache lookup failed for namespace %u",
13560 typtup->typnamespace);
13561
13562 result = quote_qualified_identifier(nspname, typname);
13563
13564 ReleaseSysCache(tp);
13565
13566 return result;
13567}

References elog, ERROR, fb(), 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 13184 of file ruleutils.c.

13185{
13186 HeapTuple tp;
13188 bool need_qual;
13190 char *relname;
13191 char *nspname;
13192 char *result;
13193
13195 if (!HeapTupleIsValid(tp))
13196 elog(ERROR, "cache lookup failed for relation %u", relid);
13198 relname = NameStr(reltup->relname);
13199
13200 /* Check for conflicting CTE name */
13201 need_qual = false;
13202 foreach(nslist, namespaces)
13203 {
13206
13207 foreach(ctlist, dpns->ctes)
13208 {
13210
13211 if (strcmp(cte->ctename, relname) == 0)
13212 {
13213 need_qual = true;
13214 break;
13215 }
13216 }
13217 if (need_qual)
13218 break;
13219 }
13220
13221 /* Otherwise, qualify the name if not visible in search path */
13222 if (!need_qual)
13223 need_qual = !RelationIsVisible(relid);
13224
13225 if (need_qual)
13226 nspname = get_namespace_name_or_temp(reltup->relnamespace);
13227 else
13228 nspname = NULL;
13229
13230 result = quote_qualified_identifier(nspname, relname);
13231
13232 ReleaseSysCache(tp);
13233
13234 return result;
13235}

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

11038{
11039 Aggref *aggref;
11040 Aggref *original_aggref = callback_arg;
11041
11042 if (!IsA(node, Aggref))
11043 elog(ERROR, "combining Aggref does not point to an Aggref");
11044
11045 aggref = (Aggref *) node;
11046 get_agg_expr(aggref, context, original_aggref);
11047}

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

10901{
10902 get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
10903 false);
10904}

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

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

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

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

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

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

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

12758{
12759 StringInfo buf = context->buf;
12760 int i;
12761 bool first = true;
12762
12763 /* Don't print aliases if not needed */
12764 if (!colinfo->printaliases)
12765 return;
12766
12767 for (i = 0; i < colinfo->num_new_cols; i++)
12768 {
12769 char *colname = colinfo->new_colnames[i];
12770
12771 if (first)
12772 {
12774 first = false;
12775 }
12776 else
12779 }
12780 if (!first)
12782}

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

11629{
11630 StringInfo buf = context->buf;
11631
11632 if (OidIsValid(constval->constcollid))
11633 {
11634 Oid typcollation = get_typcollation(constval->consttype);
11635
11636 if (constval->constcollid != typcollation)
11637 {
11638 appendStringInfo(buf, " COLLATE %s",
11639 generate_collation_name(constval->constcollid));
11640 }
11641 }
11642}

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

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

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

7361{
7362 StringInfo buf = context->buf;
7364
7365 /* Insert the WITH clause if given */
7366 get_with_clause(query, context);
7367
7368 /*
7369 * Start the query with DELETE FROM relname
7370 */
7371 rte = rt_fetch(query->resultRelation, query->rtable);
7372 Assert(rte->rtekind == RTE_RELATION);
7373 if (PRETTY_INDENT(context))
7374 {
7376 context->indentLevel += PRETTYINDENT_STD;
7377 }
7378 appendStringInfo(buf, "DELETE FROM %s%s",
7380 generate_relation_name(rte->relid, NIL));
7381
7382 /* Print the relation alias, if needed */
7383 get_rte_alias(rte, query->resultRelation, false, context);
7384
7385 /* Add the USING clause if given */
7386 get_from_clause(query, " USING ", context);
7387
7388 /* Add a WHERE clause if given */
7389 if (query->jointree->quals != NULL)
7390 {
7391 appendContextKeyword(context, " WHERE ",
7393 get_rule_expr(query->jointree->quals, context, false);
7394 }
7395
7396 /* Add RETURNING if present */
7397 if (query->returningList)
7398 get_returning_clause(query, context);
7399}

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

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

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

12800{
12801 StringInfo buf = context->buf;
12802 ListCell *l1;
12803 ListCell *l2;
12804 ListCell *l3;
12805 ListCell *l4;
12806 int i;
12807
12809
12810 i = 0;
12811 forfour(l1, rtfunc->funccoltypes,
12812 l2, rtfunc->funccoltypmods,
12813 l3, rtfunc->funccolcollations,
12814 l4, rtfunc->funccolnames)
12815 {
12816 Oid atttypid = lfirst_oid(l1);
12817 int32 atttypmod = lfirst_int(l2);
12818 Oid attcollation = lfirst_oid(l3);
12819 char *attname;
12820
12821 if (colinfo)
12822 attname = colinfo->colnames[i];
12823 else
12824 attname = strVal(lfirst(l4));
12825
12826 Assert(attname); /* shouldn't be any dropped columns here */
12827
12828 if (i > 0)
12830 appendStringInfo(buf, "%s %s",
12833 if (OidIsValid(attcollation) &&
12834 attcollation != get_typcollation(atttypid))
12835 appendStringInfo(buf, " COLLATE %s",
12836 generate_collation_name(attcollation));
12837
12838 i++;
12839 }
12840
12842}

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

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

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

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

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

11183{
11184 StringInfo buf = context->buf;
11185 Oid funcoid = expr->funcid;
11186
11187 switch (funcoid)
11188 {
11195 /* AT TIME ZONE ... note reversed argument order */
11197 get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
11198 (Node *) expr);
11199 appendStringInfoString(buf, " AT TIME ZONE ");
11200 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11201 (Node *) expr);
11203 return true;
11204
11207 case F_TIMEZONE_TIMETZ:
11208 /* AT LOCAL */
11210 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11211 (Node *) expr);
11212 appendStringInfoString(buf, " AT LOCAL)");
11213 return true;
11214
11228 /* (x1, x2) OVERLAPS (y1, y2) */
11230 get_rule_expr((Node *) linitial(expr->args), context, false);
11232 get_rule_expr((Node *) lsecond(expr->args), context, false);
11233 appendStringInfoString(buf, ") OVERLAPS (");
11234 get_rule_expr((Node *) lthird(expr->args), context, false);
11236 get_rule_expr((Node *) lfourth(expr->args), context, false);
11238 return true;
11239
11246 /* EXTRACT (x FROM y) */
11247 appendStringInfoString(buf, "EXTRACT(");
11248 {
11249 Const *con = (Const *) linitial(expr->args);
11250
11251 Assert(IsA(con, Const) &&
11252 con->consttype == TEXTOID &&
11253 !con->constisnull);
11255 }
11256 appendStringInfoString(buf, " FROM ");
11257 get_rule_expr((Node *) lsecond(expr->args), context, false);
11259 return true;
11260
11261 case F_IS_NORMALIZED:
11262 /* IS xxx NORMALIZED */
11264 get_rule_expr_paren((Node *) linitial(expr->args), context, false,
11265 (Node *) expr);
11267 if (list_length(expr->args) == 2)
11268 {
11269 Const *con = (Const *) lsecond(expr->args);
11270
11271 Assert(IsA(con, Const) &&
11272 con->consttype == TEXTOID &&
11273 !con->constisnull);
11274 appendStringInfo(buf, " %s",
11275 TextDatumGetCString(con->constvalue));
11276 }
11277 appendStringInfoString(buf, " NORMALIZED)");
11278 return true;
11279
11280 case F_PG_COLLATION_FOR:
11281 /* COLLATION FOR */
11282 appendStringInfoString(buf, "COLLATION FOR (");
11283 get_rule_expr((Node *) linitial(expr->args), context, false);
11285 return true;
11286
11287 case F_NORMALIZE:
11288 /* NORMALIZE() */
11289 appendStringInfoString(buf, "NORMALIZE(");
11290 get_rule_expr((Node *) linitial(expr->args), context, false);
11291 if (list_length(expr->args) == 2)
11292 {
11293 Const *con = (Const *) lsecond(expr->args);
11294
11295 Assert(IsA(con, Const) &&
11296 con->consttype == TEXTOID &&
11297 !con->constisnull);
11298 appendStringInfo(buf, ", %s",
11299 TextDatumGetCString(con->constvalue));
11300 }
11302 return true;
11303
11310 /* OVERLAY() */
11311 appendStringInfoString(buf, "OVERLAY(");
11312 get_rule_expr((Node *) linitial(expr->args), context, false);
11313 appendStringInfoString(buf, " PLACING ");
11314 get_rule_expr((Node *) lsecond(expr->args), context, false);
11315 appendStringInfoString(buf, " FROM ");
11316 get_rule_expr((Node *) lthird(expr->args), context, false);
11317 if (list_length(expr->args) == 4)
11318 {
11319 appendStringInfoString(buf, " FOR ");
11320 get_rule_expr((Node *) lfourth(expr->args), context, false);
11321 }
11323 return true;
11324
11325 case F_POSITION_BIT_BIT:
11328 /* POSITION() ... extra parens since args are b_expr not a_expr */
11329 appendStringInfoString(buf, "POSITION((");
11330 get_rule_expr((Node *) lsecond(expr->args), context, false);
11331 appendStringInfoString(buf, ") IN (");
11332 get_rule_expr((Node *) linitial(expr->args), context, false);
11334 return true;
11335
11342 /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
11343 appendStringInfoString(buf, "SUBSTRING(");
11344 get_rule_expr((Node *) linitial(expr->args), context, false);
11345 appendStringInfoString(buf, " FROM ");
11346 get_rule_expr((Node *) lsecond(expr->args), context, false);
11347 if (list_length(expr->args) == 3)
11348 {
11349 appendStringInfoString(buf, " FOR ");
11350 get_rule_expr((Node *) lthird(expr->args), context, false);
11351 }
11353 return true;
11354
11356 /* SUBSTRING SIMILAR/ESCAPE */
11357 appendStringInfoString(buf, "SUBSTRING(");
11358 get_rule_expr((Node *) linitial(expr->args), context, false);
11359 appendStringInfoString(buf, " SIMILAR ");
11360 get_rule_expr((Node *) lsecond(expr->args), context, false);
11361 appendStringInfoString(buf, " ESCAPE ");
11362 get_rule_expr((Node *) lthird(expr->args), context, false);
11364 return true;
11365
11367 case F_BTRIM_TEXT:
11368 case F_BTRIM_TEXT_TEXT:
11369 /* TRIM() */
11370 appendStringInfoString(buf, "TRIM(BOTH");
11371 if (list_length(expr->args) == 2)
11372 {
11374 get_rule_expr((Node *) lsecond(expr->args), context, false);
11375 }
11376 appendStringInfoString(buf, " FROM ");
11377 get_rule_expr((Node *) linitial(expr->args), context, false);
11379 return true;
11380
11382 case F_LTRIM_TEXT:
11383 case F_LTRIM_TEXT_TEXT:
11384 /* TRIM() */
11385 appendStringInfoString(buf, "TRIM(LEADING");
11386 if (list_length(expr->args) == 2)
11387 {
11389 get_rule_expr((Node *) lsecond(expr->args), context, false);
11390 }
11391 appendStringInfoString(buf, " FROM ");
11392 get_rule_expr((Node *) linitial(expr->args), context, false);
11394 return true;
11395
11397 case F_RTRIM_TEXT:
11398 case F_RTRIM_TEXT_TEXT:
11399 /* TRIM() */
11400 appendStringInfoString(buf, "TRIM(TRAILING");
11401 if (list_length(expr->args) == 2)
11402 {
11404 get_rule_expr((Node *) lsecond(expr->args), context, false);
11405 }
11406 appendStringInfoString(buf, " FROM ");
11407 get_rule_expr((Node *) linitial(expr->args), context, false);
11409 return true;
11410
11411 case F_SYSTEM_USER:
11412 appendStringInfoString(buf, "SYSTEM_USER");
11413 return true;
11414
11415 case F_XMLEXISTS:
11416 /* XMLEXISTS ... extra parens because args are c_expr */
11417 appendStringInfoString(buf, "XMLEXISTS((");
11418 get_rule_expr((Node *) linitial(expr->args), context, false);
11419 appendStringInfoString(buf, ") PASSING (");
11420 get_rule_expr((Node *) lsecond(expr->args), context, false);
11422 return true;
11423 }
11424 return false;
11425}

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

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

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, elog, ERROR, fb(), generate_relation_name(), get_attname(), get_constraint_name(), 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(), lfirst, deparse_context::namespaces, NIL, OidIsValid, Query::onConflict, ONCONFLICT_NOTHING, 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 11801 of file ruleutils.c.

11803{
11805
11808
11809 if (IsA(ctor->func, Aggref))
11810 get_agg_expr_helper((Aggref *) ctor->func, context,
11811 (Aggref *) ctor->func,
11813 else if (IsA(ctor->func, WindowFunc))
11814 get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
11815 funcname, options.data,
11817 else
11818 elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
11819 nodeTag(ctor->func));
11820}

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

9188{
9189 /*
9190 * The order of array elements must correspond to the order of
9191 * JsonBehaviorType members.
9192 */
9193 const char *behavior_names[] =
9194 {
9195 " NULL",
9196 " ERROR",
9197 " EMPTY",
9198 " TRUE",
9199 " FALSE",
9200 " UNKNOWN",
9201 " EMPTY ARRAY",
9202 " EMPTY OBJECT",
9203 " DEFAULT "
9204 };
9205
9206 if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
9207 elog(ERROR, "invalid json behavior type: %d", behavior->btype);
9208
9209 appendStringInfoString(context->buf, behavior_names[behavior->btype]);
9210
9211 if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
9212 get_rule_expr(behavior->expr, context, false);
9213
9214 appendStringInfo(context->buf, " ON %s", on);
9215}

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

11707{
11708 StringInfo buf = context->buf;
11709 const char *funcname;
11710 bool is_json_object;
11711 int curridx;
11712 ListCell *lc;
11713
11714 if (ctor->type == JSCTOR_JSON_OBJECTAGG)
11715 {
11716 get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
11717 return;
11718 }
11719 else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
11720 {
11721 get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
11722 return;
11723 }
11724
11725 switch (ctor->type)
11726 {
11727 case JSCTOR_JSON_OBJECT:
11728 funcname = "JSON_OBJECT";
11729 break;
11730 case JSCTOR_JSON_ARRAY:
11731 funcname = "JSON_ARRAY";
11732 break;
11733 case JSCTOR_JSON_PARSE:
11734 funcname = "JSON";
11735 break;
11736 case JSCTOR_JSON_SCALAR:
11737 funcname = "JSON_SCALAR";
11738 break;
11740 funcname = "JSON_SERIALIZE";
11741 break;
11742 default:
11743 elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
11744 }
11745
11746 appendStringInfo(buf, "%s(", funcname);
11747
11749 foreach(lc, ctor->args)
11750 {
11752 if (curridx > 0)
11753 {
11754 const char *sep;
11755
11756 sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
11758 }
11759
11760 get_rule_expr((Node *) lfirst(lc), context, true);
11761 }
11762
11765}

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

11772{
11773 if (ctor->absent_on_null)
11774 {
11775 if (ctor->type == JSCTOR_JSON_OBJECT ||
11776 ctor->type == JSCTOR_JSON_OBJECTAGG)
11777 appendStringInfoString(buf, " ABSENT ON NULL");
11778 }
11779 else
11780 {
11781 if (ctor->type == JSCTOR_JSON_ARRAY ||
11782 ctor->type == JSCTOR_JSON_ARRAYAGG)
11783 appendStringInfoString(buf, " NULL ON NULL");
11784 }
11785
11786 if (ctor->unique)
11787 appendStringInfoString(buf, " WITH UNIQUE KEYS");
11788
11789 /*
11790 * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
11791 * support one.
11792 */
11793 if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
11794 get_json_returning(ctor->returning, buf, true);
11795}

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

9226{
9227 if (jsexpr->op == JSON_QUERY_OP)
9228 {
9229 if (jsexpr->wrapper == JSW_CONDITIONAL)
9230 appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
9231 else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
9232 appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
9233 /* The default */
9234 else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
9235 appendStringInfoString(context->buf, " WITHOUT WRAPPER");
9236
9237 if (jsexpr->omit_quotes)
9238 appendStringInfoString(context->buf, " OMIT QUOTES");
9239 /* The default */
9240 else
9241 appendStringInfoString(context->buf, " KEEP QUOTES");
9242 }
9243
9244 if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
9245 get_json_behavior(jsexpr->on_empty, context, "EMPTY");
9246
9247 if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
9248 get_json_behavior(jsexpr->on_error, context, "ERROR");
9249}

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

11661{
11662 if (format->format_type == JS_FORMAT_DEFAULT)
11663 return;
11664
11666 format->format_type == JS_FORMAT_JSONB ?
11667 " FORMAT JSONB" : " FORMAT JSON");
11668
11669 if (format->encoding != JS_ENC_DEFAULT)
11670 {
11671 const char *encoding;
11672
11673 encoding =
11674 format->encoding == JS_ENC_UTF16 ? "UTF16" :
11675 format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
11676
11677 appendStringInfo(buf, " ENCODING %s", encoding);
11678 }
11679}

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

11649{
11650 if (IsA(path_spec, Const))
11651 get_const_expr((Const *) path_spec, context, -1);
11652 else
11653 get_rule_expr(path_spec, context, showimplicit);
11654}

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

11687{
11688 if (!OidIsValid(returning->typid))
11689 return;
11690
11691 appendStringInfo(buf, " RETURNING %s",
11693 returning->typmod));
11694
11696 returning->format->format_type !=
11697 (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
11698 get_json_format(returning->format, buf);
11699}

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

12214{
12215 StringInfo buf = context->buf;
12218
12219 appendStringInfoString(buf, "JSON_TABLE(");
12220
12221 if (PRETTY_INDENT(context))
12222 context->indentLevel += PRETTYINDENT_VAR;
12223
12224 appendContextKeyword(context, "", 0, 0, 0);
12225
12226 get_rule_expr(jexpr->formatted_expr, context, showimplicit);
12227
12229
12230 get_const_expr(root->path->value, context, -1);
12231
12232 appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
12233
12234 if (jexpr->passing_values)
12235 {
12236 ListCell *lc1,
12237 *lc2;
12238 bool needcomma = false;
12239
12241 appendContextKeyword(context, "PASSING ", 0, 0, 0);
12242
12243 if (PRETTY_INDENT(context))
12244 context->indentLevel += PRETTYINDENT_VAR;
12245
12246 forboth(lc1, jexpr->passing_names,
12247 lc2, jexpr->passing_values)
12248 {
12249 if (needcomma)
12251 needcomma = true;
12252
12253 appendContextKeyword(context, "", 0, 0, 0);
12254
12255 get_rule_expr((Node *) lfirst(lc2), context, false);
12256 appendStringInfo(buf, " AS %s",
12258 );
12259 }
12260
12261 if (PRETTY_INDENT(context))
12262 context->indentLevel -= PRETTYINDENT_VAR;
12263 }
12264
12265 get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
12266 showimplicit);
12267
12268 if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
12269 get_json_behavior(jexpr->on_error, context, "ERROR");
12270
12271 if (PRETTY_INDENT(context))
12272 context->indentLevel -= PRETTYINDENT_VAR;
12273
12274 appendContextKeyword(context, ")", 0, 0, 0);
12275}

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

12110{
12111 StringInfo buf = context->buf;
12116 int colnum = 0;
12117
12119 appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
12120
12121 if (PRETTY_INDENT(context))
12122 context->indentLevel += PRETTYINDENT_VAR;
12123
12124 forfour(lc_colname, tf->colnames,
12125 lc_coltype, tf->coltypes,
12126 lc_coltypmod, tf->coltypmods,
12127 lc_colvalexpr, tf->colvalexprs)
12128 {
12129 char *colname = strVal(lfirst(lc_colname));
12130 JsonExpr *colexpr;
12131 Oid typid;
12132 int32 typmod;
12133 bool ordinality;
12135
12136 typid = lfirst_oid(lc_coltype);
12137 typmod = lfirst_int(lc_coltypmod);
12139
12140 /* Skip columns that don't belong to this scan. */
12141 if (scan->colMin < 0 || colnum < scan->colMin)
12142 {
12143 colnum++;
12144 continue;
12145 }
12146 if (colnum > scan->colMax)
12147 break;
12148
12149 if (colnum > scan->colMin)
12151
12152 colnum++;
12153
12154 ordinality = !colexpr;
12155
12156 appendContextKeyword(context, "", 0, 0, 0);
12157
12158 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12159 ordinality ? "FOR ORDINALITY" :
12160 format_type_with_typemod(typid, typmod));
12161 if (ordinality)
12162 continue;
12163
12164 /*
12165 * Set default_behavior to guide get_json_expr_options() on whether to
12166 * emit the ON ERROR / EMPTY clauses.
12167 */
12168 if (colexpr->op == JSON_EXISTS_OP)
12169 {
12170 appendStringInfoString(buf, " EXISTS");
12172 }
12173 else
12174 {
12175 if (colexpr->op == JSON_QUERY_OP)
12176 {
12177 char typcategory;
12178 bool typispreferred;
12179
12181
12184 colexpr->format->format_type == JS_FORMAT_JSONB ?
12185 " FORMAT JSONB" : " FORMAT JSON");
12186 }
12187
12189 }
12190
12191 appendStringInfoString(buf, " PATH ");
12192
12193 get_json_path_spec(colexpr->path_spec, context, showimplicit);
12194
12195 get_json_expr_options(colexpr, context, default_behavior);
12196 }
12197
12198 if (scan->child)
12200 scan->colMin >= 0);
12201
12202 if (PRETTY_INDENT(context))
12203 context->indentLevel -= PRETTYINDENT_VAR;
12204
12205 appendContextKeyword(context, ")", 0, 0, 0);
12206}

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

12078{
12080 {
12082
12083 if (needcomma)
12084 appendStringInfoChar(context->buf, ',');
12085
12086 appendStringInfoChar(context->buf, ' ');
12087 appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
12088 get_const_expr(scan->path->value, context, -1);
12089 appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
12090 get_json_table_columns(tf, scan, context, showimplicit);
12091 }
12092 else if (IsA(plan, JsonTableSiblingJoin))
12093 {
12095
12097 needcomma);
12099 true);
12100 }
12101}

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

static void get_merge_query_def ( Query query,
deparse_context context 
)
static

Definition at line 7407 of file ruleutils.c.

7408{
7409 StringInfo buf = context->buf;
7411 ListCell *lc;
7413
7414 /* Insert the WITH clause if given */
7415 get_with_clause(query, context);
7416
7417 /*
7418 * Start the query with MERGE INTO relname
7419 */
7420 rte = rt_fetch(query->resultRelation, query->rtable);
7421 Assert(rte->rtekind == RTE_RELATION);
7422 if (PRETTY_INDENT(context))
7423 {
7425 context->indentLevel += PRETTYINDENT_STD;
7426 }
7427 appendStringInfo(buf, "MERGE INTO %s%s",
7429 generate_relation_name(rte->relid, NIL));
7430
7431 /* Print the relation alias, if needed */
7432 get_rte_alias(rte, query->resultRelation, false, context);
7433
7434 /* Print the source relation and join clause */
7435 get_from_clause(query, " USING ", context);
7436 appendContextKeyword(context, " ON ",
7438 get_rule_expr(query->mergeJoinCondition, context, false);
7439
7440 /*
7441 * Test for any NOT MATCHED BY SOURCE actions. If there are none, then
7442 * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
7443 * SQL standard. Otherwise, we have a non-SQL-standard query, so output
7444 * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
7445 * more explicit.
7446 */
7447 haveNotMatchedBySource = false;
7448 foreach(lc, query->mergeActionList)
7449 {
7451
7452 if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
7453 {
7455 break;
7456 }
7457 }
7458
7459 /* Print each merge action */
7460 foreach(lc, query->mergeActionList)
7461 {
7463
7464 appendContextKeyword(context, " WHEN ",
7466 switch (action->matchKind)
7467 {
7468 case MERGE_WHEN_MATCHED:
7469 appendStringInfoString(buf, "MATCHED");
7470 break;
7472 appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
7473 break;
7476 appendStringInfoString(buf, "NOT MATCHED BY TARGET");
7477 else
7478 appendStringInfoString(buf, "NOT MATCHED");
7479 break;
7480 default:
7481 elog(ERROR, "unrecognized matchKind: %d",
7482 (int) action->matchKind);
7483 }
7484
7485 if (action->qual)
7486 {
7487 appendContextKeyword(context, " AND ",
7489 get_rule_expr(action->qual, context, false);
7490 }
7491 appendContextKeyword(context, " THEN ",
7493
7494 if (action->commandType == CMD_INSERT)
7495 {
7496 /* This generally matches get_insert_query_def() */
7498 const char *sep = "";
7499 ListCell *lc2;
7500
7501 appendStringInfoString(buf, "INSERT");
7502
7503 if (action->targetList)
7505 foreach(lc2, action->targetList)
7506 {
7508
7509 Assert(!tle->resjunk);
7510
7512 sep = ", ";
7513
7516 tle->resno,
7517 false)));
7519 processIndirection((Node *) tle->expr,
7520 context));
7521 }
7522 if (action->targetList)
7524
7525 if (action->override)
7526 {
7527 if (action->override == OVERRIDING_SYSTEM_VALUE)
7528 appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
7529 else if (action->override == OVERRIDING_USER_VALUE)
7530 appendStringInfoString(buf, " OVERRIDING USER VALUE");
7531 }
7532
7533 if (strippedexprs)
7534 {
7535 appendContextKeyword(context, " VALUES (",
7537 get_rule_list_toplevel(strippedexprs, context, false);
7539 }
7540 else
7541 appendStringInfoString(buf, " DEFAULT VALUES");
7542 }
7543 else if (action->commandType == CMD_UPDATE)
7544 {
7545 appendStringInfoString(buf, "UPDATE SET ");
7546 get_update_query_targetlist_def(query, action->targetList,
7547 context, rte);
7548 }
7549 else if (action->commandType == CMD_DELETE)
7550 appendStringInfoString(buf, "DELETE");
7551 else if (action->commandType == CMD_NOTHING)
7552 appendStringInfoString(buf, "DO NOTHING");
7553 }
7554
7555 /* Add RETURNING if present */
7556 if (query->returningList)
7557 get_returning_clause(query, context);
7558}

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

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

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

12894{
12897 char *opcname;
12898 char *nspname;
12899
12902 elog(ERROR, "cache lookup failed for opclass %u", opclass);
12904
12906 GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
12907 {
12908 /* Okay, we need the opclass name. Do we need to qualify it? */
12909 opcname = NameStr(opcrec->opcname);
12910 if (OpclassIsVisible(opclass))
12912 else
12913 {
12914 nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
12915 appendStringInfo(buf, " %s.%s",
12916 quote_identifier(nspname),
12918 }
12919 }
12921}

References appendStringInfo(), buf, elog, ERROR, fb(), 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 10763 of file ruleutils.c.

10764{
10765 StringInfo buf = context->buf;
10766 Oid opno = expr->opno;
10767 List *args = expr->args;
10768
10769 if (!PRETTY_PAREN(context))
10771 if (list_length(args) == 2)
10772 {
10773 /* binary operator */
10774 Node *arg1 = (Node *) linitial(args);
10775 Node *arg2 = (Node *) lsecond(args);
10776
10777 get_rule_expr_paren(arg1, context, true, (Node *) expr);
10778 appendStringInfo(buf, " %s ",
10780 exprType(arg1),
10781 exprType(arg2)));
10782 get_rule_expr_paren(arg2, context, true, (Node *) expr);
10783 }
10784 else
10785 {
10786 /* prefix operator */
10787 Node *arg = (Node *) linitial(args);
10788
10789 appendStringInfo(buf, "%s ",
10791 InvalidOid,
10792 exprType(arg)));
10793 get_rule_expr_paren(arg, context, true, (Node *) expr);
10794 }
10795 if (!PRETTY_PAREN(context))
10797}

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

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

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

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}

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

13710{
13711 deparse_context context;
13713 ListCell *cell;
13714 char *sep;
13715
13717 memset(&context, 0, sizeof(deparse_context));
13718 context.buf = &buf;
13719
13721 sep = "";
13722 foreach(cell, bound_datums)
13723 {
13724 PartitionRangeDatum *datum =
13726
13729 appendStringInfoString(&buf, "MINVALUE");
13730 else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
13731 appendStringInfoString(&buf, "MAXVALUE");
13732 else
13733 {
13734 Const *val = castNode(Const, datum->value);
13735
13736 get_const_expr(val, &context, -1);
13737 }
13738 sep = ", ";
13739 }
13741
13742 return buf.data;
13743}

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

13165{
13166 char *relname = get_rel_name(relid);
13167
13168 if (!relname)
13169 elog(ERROR, "cache lookup failed for relation %u", relid);
13170 return relname;
13171}

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

13622{
13623 Datum *options;
13624 int noptions;
13625 int i;
13626
13628 &options, NULL, &noptions);
13629
13630 for (i = 0; i < noptions; i++)
13631 {
13633 char *name;
13634 char *separator;
13635 char *value;
13636
13637 /*
13638 * Each array element should have the form name=value. If the "=" is
13639 * missing for some reason, treat it like an empty value.
13640 */
13641 name = option;
13642 separator = strchr(option, '=');
13643 if (separator)
13644 {
13645 *separator = '\0';
13646 value = separator + 1;
13647 }
13648 else
13649 value = "";
13650
13651 if (i > 0)
13654
13655 /*
13656 * In general we need to quote the value; but to avoid unnecessary
13657 * clutter, do not quote if it is an identifier that would not need
13658 * quoting. (We could also allow numbers, but that is a bit trickier
13659 * than it looks --- for example, are leading zeroes significant? We
13660 * don't want to assume very much here about what custom reloptions
13661 * might mean.)
13662 */
13665 else
13667
13668 pfree(option);
13669 }
13670}

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

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

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

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

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

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

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

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

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

10713{
10714 if (looks_like_function(node))
10715 get_rule_expr(node, context, showimplicit);
10716 else
10717 {
10718 StringInfo buf = context->buf;
10719
10720 appendStringInfoString(buf, "CAST(");
10721 /* no point in showing any top-level implicit cast */
10722 get_rule_expr(node, context, false);
10723 appendStringInfo(buf, " AS %s)",
10725 exprTypmod(node)));
10726 }
10727}

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

9170{
9171 bool need_paren;
9172
9173 need_paren = PRETTY_PAREN(context) &&
9174 !isSimpleNode(node, parentNode, context->prettyFlags);
9175
9176 if (need_paren)
9177 appendStringInfoChar(context->buf, '(');
9178
9179 get_rule_expr(node, context, showimplicit);
9180
9181 if (need_paren)
9182 appendStringInfoChar(context->buf, ')');
9183}

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

10665{
10666 if (node && IsA(node, Var))
10667 (void) get_variable((Var *) node, 0, true, context);
10668 else
10669 get_rule_expr(node, context, showimplicit);
10670}

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

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

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

10683{
10684 const char *sep;
10685 ListCell *lc;
10686
10687 sep = "";
10688 foreach(lc, lst)
10689 {
10690 Node *e = (Node *) lfirst(lc);
10691
10692 appendStringInfoString(context->buf, sep);
10694 sep = ", ";
10695 }
10696}

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

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

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

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

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

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

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

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

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

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 switch (rc->strength)
6001 {
6002 case LCS_NONE:
6003 /* we intentionally throw an error for LCS_NONE */
6004 elog(ERROR, "unrecognized LockClauseStrength %d",
6005 (int) rc->strength);
6006 break;
6007 case LCS_FORKEYSHARE:
6008 appendContextKeyword(context, " FOR KEY SHARE",
6010 break;
6011 case LCS_FORSHARE:
6012 appendContextKeyword(context, " FOR SHARE",
6014 break;
6015 case LCS_FORNOKEYUPDATE:
6016 appendContextKeyword(context, " FOR NO KEY UPDATE",
6018 break;
6019 case LCS_FORUPDATE:
6020 appendContextKeyword(context, " FOR UPDATE",
6022 break;
6023 }
6024
6025 appendStringInfo(buf, " OF %s",
6027 context)));
6028 if (rc->waitPolicy == LockWaitError)
6029 appendStringInfoString(buf, " NOWAIT");
6030 else if (rc->waitPolicy == LockWaitSkip)
6031 appendStringInfoString(buf, " SKIP LOCKED");
6032 }
6033 }
6034}

References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, elog, ERROR, fb(), get_basic_select_query(), get_rtable_name(), get_rule_expr(), get_rule_orderby(), get_setop_query(), get_with_clause(), IsA, LCS_FORKEYSHARE, LCS_FORNOKEYUPDATE, LCS_FORSHARE, LCS_FORUPDATE, LCS_NONE, 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 6419 of file ruleutils.c.

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

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

8838{
8839 List *args = expr->args;
8840
8841 if (list_length(args) == 2)
8842 {
8843 /* binary operator */
8844 Node *arg1 = (Node *) linitial(args);
8845 Node *arg2 = (Node *) lsecond(args);
8846 const char *op;
8847
8849 if (strlen(op) == 1)
8850 return op;
8851 }
8852 return NULL;
8853}

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

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

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

7894{
7895 StringInfo buf = context->buf;
7896
7897 /*
7898 * For a non-Var referent, force parentheses because our caller probably
7899 * assumed a Var is a simple expression.
7900 */
7901 if (!IsA(node, Var))
7903 get_rule_expr(node, context, true);
7904 if (!IsA(node, Var))
7906}

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

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

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

12283{
12284 /* XMLTABLE and JSON_TABLE are the only existing implementations. */
12285
12286 if (tf->functype == TFT_XMLTABLE)
12287 get_xmltable(tf, context, showimplicit);
12288 else if (tf->functype == TFT_JSON_TABLE)
12289 get_json_table(tf, context, showimplicit);
12290}

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

12849{
12850 StringInfo buf = context->buf;
12851 Oid argtypes[1];
12852 int nargs;
12853 ListCell *l;
12854
12855 /*
12856 * We should qualify the handler's function name if it wouldn't be
12857 * resolved by lookup in the current search path.
12858 */
12859 argtypes[0] = INTERNALOID;
12860 appendStringInfo(buf, " TABLESAMPLE %s (",
12861 generate_function_name(tablesample->tsmhandler, 1,
12862 NIL, argtypes,
12863 false, NULL, false));
12864
12865 nargs = 0;
12866 foreach(l, tablesample->args)
12867 {
12868 if (nargs++ > 0)
12870 get_rule_expr((Node *) lfirst(l), context, false);
12871 }
12873
12874 if (tablesample->repeatable != NULL)
12875 {
12876 appendStringInfoString(buf, " REPEATABLE (");
12877 get_rule_expr((Node *) tablesample->repeatable, context, false);
12879 }
12880}

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

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

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

7157{
7158 StringInfo buf = context->buf;
7160
7161 /* Insert the WITH clause if given */
7162 get_with_clause(query, context);
7163
7164 /*
7165 * Start the query with UPDATE relname SET
7166 */
7167 rte = rt_fetch(query->resultRelation, query->rtable);
7168 Assert(rte->rtekind == RTE_RELATION);
7169 if (PRETTY_INDENT(context))
7170 {
7172 context->indentLevel += PRETTYINDENT_STD;
7173 }
7174 appendStringInfo(buf, "UPDATE %s%s",
7176 generate_relation_name(rte->relid, NIL));
7177
7178 /* Print the relation alias, if needed */
7179 get_rte_alias(rte, query->resultRelation, false, context);
7180
7181 appendStringInfoString(buf, " SET ");
7182
7183 /* Deparse targetlist */
7184 get_update_query_targetlist_def(query, query->targetList, context, rte);
7185
7186 /* Add the FROM clause if needed */
7187 get_from_clause(query, " FROM ", context);
7188
7189 /* Add a WHERE clause if given */
7190 if (query->jointree->quals != NULL)
7191 {
7192 appendContextKeyword(context, " WHERE ",
7194 get_rule_expr(query->jointree->quals, context, false);
7195 }
7196
7197 /* Add RETURNING if present */
7198 if (query->returningList)
7199 get_returning_clause(query, context);
7200}

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

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

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

7567{
7568 StringInfo buf = context->buf;
7569
7570 if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
7571 {
7572 NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
7573
7574 appendContextKeyword(context, "",
7575 0, PRETTYINDENT_STD, 1);
7576 appendStringInfo(buf, "NOTIFY %s",
7577 quote_identifier(stmt->conditionname));
7578 if (stmt->payload)
7579 {
7581 simple_quote_literal(buf, stmt->payload);
7582 }
7583 }
7584 else
7585 {
7586 /* Currently only NOTIFY utility commands can appear in rules */
7587 elog(ERROR, "unexpected utility statement type");
7588 }
7589}

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

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}

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

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

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

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

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

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

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

11054{
11055 get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
11056}

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

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

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

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}

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

11977{
11978 StringInfo buf = context->buf;
11979
11980 appendStringInfoString(buf, "XMLTABLE(");
11981
11982 if (tf->ns_uris != NIL)
11983 {
11984 ListCell *lc1,
11985 *lc2;
11986 bool first = true;
11987
11988 appendStringInfoString(buf, "XMLNAMESPACES (");
11989 forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
11990 {
11991 Node *expr = (Node *) lfirst(lc1);
11993
11994 if (!first)
11996 else
11997 first = false;
11998
11999 if (ns_node != NULL)
12000 {
12001 get_rule_expr(expr, context, showimplicit);
12002 appendStringInfo(buf, " AS %s",
12004 }
12005 else
12006 {
12007 appendStringInfoString(buf, "DEFAULT ");
12008 get_rule_expr(expr, context, showimplicit);
12009 }
12010 }
12012 }
12013
12015 get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
12016 appendStringInfoString(buf, ") PASSING (");
12017 get_rule_expr((Node *) tf->docexpr, context, showimplicit);
12019
12020 if (tf->colexprs != NIL)
12021 {
12022 ListCell *l1;
12023 ListCell *l2;
12024 ListCell *l3;
12025 ListCell *l4;
12026 ListCell *l5;
12027 int colnum = 0;
12028
12029 appendStringInfoString(buf, " COLUMNS ");
12030 forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
12031 l4, tf->colexprs, l5, tf->coldefexprs)
12032 {
12033 char *colname = strVal(lfirst(l1));
12034 Oid typid = lfirst_oid(l2);
12035 int32 typmod = lfirst_int(l3);
12036 Node *colexpr = (Node *) lfirst(l4);
12037 Node *coldefexpr = (Node *) lfirst(l5);
12038 bool ordinality = (tf->ordinalitycol == colnum);
12039 bool notnull = bms_is_member(colnum, tf->notnulls);
12040
12041 if (colnum > 0)
12043 colnum++;
12044
12045 appendStringInfo(buf, "%s %s", quote_identifier(colname),
12046 ordinality ? "FOR ORDINALITY" :
12047 format_type_with_typemod(typid, typmod));
12048 if (ordinality)
12049 continue;
12050
12051 if (coldefexpr != NULL)
12052 {
12053 appendStringInfoString(buf, " DEFAULT (");
12054 get_rule_expr((Node *) coldefexpr, context, showimplicit);
12056 }
12057 if (colexpr != NULL)
12058 {
12059 appendStringInfoString(buf, " PATH (");
12060 get_rule_expr((Node *) colexpr, context, showimplicit);
12062 }
12063 if (notnull)
12064 appendStringInfoString(buf, " NOT NULL");
12065 }
12066 }
12067
12069}

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

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}

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

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}

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

3450{
3451 return (!argmodes
3455}

References fb().

Referenced by pg_get_function_arg_default().

◆ isSimpleNode()

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

Definition at line 8863 of file ruleutils.c.

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

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

10735{
10736 if (node == NULL)
10737 return false; /* probably shouldn't happen */
10738 switch (nodeTag(node))
10739 {
10740 case T_FuncExpr:
10741 /* OK, unless it's going to deparse as a cast */
10742 return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
10743 ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
10744 case T_NullIfExpr:
10745 case T_CoalesceExpr:
10746 case T_MinMaxExpr:
10747 case T_SQLValueFunction:
10748 case T_XmlExpr:
10749 case T_JsonExpr:
10750 /* these are all accepted by func_expr_common_subexpr */
10751 return true;
10752 default:
10753 break;
10754 }
10755 return false;
10756}

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

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}

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

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}

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

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}

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

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}

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

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

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

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}

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

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}

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

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}

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

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}

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

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}

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

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}

References castNode, deparse_expression(), fb(), 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 3613 of file ruleutils.c.

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}

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

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}

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

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}

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

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}

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

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}

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

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}

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

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

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

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}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, attname, attnum, buf, DatumGetPointer(), deparse_context_for(), deparse_expression_pretty(), elog, ERROR, exprCollation(), exprType(), fb(), flatten_reloptions(), 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 2127 of file ruleutils.c.

2128{
2130 List *context;
2131
2133 context = deparse_context_for(aliasname, partitionId);
2134
2135 return deparse_expression((Node *) constr_expr, context, true, false);
2136}

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

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}

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

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}

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

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

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

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}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, attname, attnum, buf, DatumGetPointer(), deparse_context_for(), deparse_expression_pretty(), elog, ERROR, exprCollation(), exprType(), fb(), 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 1588 of file ruleutils.c.

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}

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

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}

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

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}

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

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}

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

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}

References AccessShareLock, attnum, BTEqualStrategyNumber, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, ereport, errcode(), errmsg(), ERROR, fb(), 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 1653 of file ruleutils.c.

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}

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

1609{
1611 char *res;
1612
1613 res = pg_get_statisticsobj_worker(statextid, false, true);
1614
1615 if (res == NULL)
1617
1619}

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

1637{
1639 char *res;
1640
1641 res = pg_get_statisticsobj_worker(statextid, true, true);
1642
1643 if (res == NULL)
1645
1647}

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

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}

References accumArrayResult(), cstring_to_text(), CurrentMemoryContext, deparse_context_for(), deparse_expression_pretty(), fb(), 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 1626 of file ruleutils.c.

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

References fb(), and pg_get_statisticsobj_worker().

Referenced by RememberStatisticsForRebuilding().

◆ pg_get_triggerdef()

Datum pg_get_triggerdef ( PG_FUNCTION_ARGS  )

Definition at line 871 of file ruleutils.c.

872{
874 char *res;
875
876 res = pg_get_triggerdef_worker(trigid, false);
877
878 if (res == NULL)
880
882}

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

886{
888 bool pretty = PG_GETARG_BOOL(1);
889 char *res;
890
892
893 if (res == NULL)
895
897}

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

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}

References AccessShareLock, deparse_context::appendparents, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), attname, BTEqualStrategyNumber, deparse_context::buf, buf, deparse_context::colNamesVisible, DatumGetByteaPP, DatumGetName(), elog, ERROR, fastgetattr(), fb(), 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 2794 of file ruleutils.c.

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}

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

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}

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

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}

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

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}

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

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}

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

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}

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

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}

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

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}

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

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}

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

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}

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, buf, castNode, deparse_expression(), elog, ERROR, fb(), 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 3263 of file ruleutils.c.

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}

References appendBinaryStringInfo(), appendStringInfoChar(), appendStringInfoString(), buf, fb(), 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 3559 of file ruleutils.c.

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}

References AcquireRewriteLocks(), appendStringInfoChar(), appendStringInfoString(), buf, castNode, fb(), 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 3461 of file ruleutils.c.

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}

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

13031{
13032 StringInfo buf = context->buf;
13035
13036 lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
13037 foreach(uplist_item, sbsref->refupperindexpr)
13038 {
13040 if (lowlist_item)
13041 {
13042 /* If subexpression is NULL, get_rule_expr prints nothing */
13043 get_rule_expr((Node *) lfirst(lowlist_item), context, false);
13046 }
13047 /* If subexpression is NULL, get_rule_expr prints nothing */
13048 get_rule_expr((Node *) lfirst(uplist_item), context, false);
13050 }
13051}

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

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

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

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}

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

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}

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

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

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

9150{
9151 while (str->len > 0 && str->data[str->len - 1] == ' ')
9152 str->data[--(str->len)] = '\0';
9153}

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

7916{
7917 Var *var;
7919
7920 /* This function is recursive, so let's be paranoid. */
7922
7923 /* If it's not a Var, invoke the callback. */
7924 if (!IsA(node, Var))
7925 {
7926 (*callback) (node, context, callback_arg);
7927 return;
7928 }
7929
7930 /* Find appropriate nesting depth */
7931 var = (Var *) node;
7933 var->varlevelsup);
7934
7935 /*
7936 * If varno is special, recurse. (Don't worry about varnosyn; if we're
7937 * here, we already decided not to use that.)
7938 */
7939 if (var->varno == OUTER_VAR && dpns->outer_tlist)
7940 {
7944
7945 tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
7946 if (!tle)
7947 elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
7948
7949 /*
7950 * If we're descending to the first child of an Append or MergeAppend,
7951 * update appendparents. This will affect deparsing of all Vars
7952 * appearing within the eventually-resolved subexpression.
7953 */
7955
7956 if (IsA(dpns->plan, Append))
7957 context->appendparents = bms_union(context->appendparents,
7958 ((Append *) dpns->plan)->apprelids);
7959 else if (IsA(dpns->plan, MergeAppend))
7960 context->appendparents = bms_union(context->appendparents,
7961 ((MergeAppend *) dpns->plan)->apprelids);
7962
7963 push_child_plan(dpns, dpns->outer_plan, &save_dpns);
7964 resolve_special_varno((Node *) tle->expr, context,
7965 callback, callback_arg);
7968 return;
7969 }
7970 else if (var->varno == INNER_VAR && dpns->inner_tlist)
7971 {
7974
7975 tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
7976 if (!tle)
7977 elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
7978
7979 push_child_plan(dpns, dpns->inner_plan, &save_dpns);
7980 resolve_special_varno((Node *) tle->expr, context,
7981 callback, callback_arg);
7983 return;
7984 }
7985 else if (var->varno == INDEX_VAR && dpns->index_tlist)
7986 {
7988
7989 tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
7990 if (!tle)
7991 elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
7992
7993 resolve_special_varno((Node *) tle->expr, context,
7994 callback, callback_arg);
7995 return;
7996 }
7997 else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
7998 elog(ERROR, "bogus varno: %d", var->varno);
7999
8000 /* Not special. Just invoke the callback. */
8001 (*callback) (node, context, callback_arg);
8002}

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

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}

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

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}

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

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}

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

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 .. UPDATE we just need the inner tlist to point to the
5190 * excluded expression's tlist. (Similar to the SubqueryScan we don't want
5191 * to reuse OUTER, it's used for RETURNING in some modify table cases,
5192 * although not INSERT .. CONFLICT).
5193 */
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}

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

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}

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

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}

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

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}

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

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}

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

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}

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

11827{
11828 const char *valptr;
11829
11830 /*
11831 * We always form the string literal according to standard SQL rules.
11832 */
11834 for (valptr = val; *valptr; valptr++)
11835 {
11836 char ch = *valptr;
11837
11838 if (SQL_STR_DOUBLE(ch, false))
11841 }
11843}

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