#include "postgres.h"
#include <unistd.h>
#include <fcntl.h>
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.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_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "parser/keywords.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.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/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
#include "utils/typcache.h"
#include "utils/xml.h"
Go to the source code of this file.
Data Structures | |
struct | deparse_context |
struct | deparse_namespace |
struct | deparse_columns |
Defines | |
#define | PRETTYINDENT_STD 8 |
#define | PRETTYINDENT_JOIN 13 |
#define | PRETTYINDENT_JOIN_ON (PRETTYINDENT_JOIN-PRETTYINDENT_STD) |
#define | PRETTYINDENT_VAR 4 |
#define | PRETTYFLAG_PAREN 1 |
#define | PRETTYFLAG_INDENT 2 |
#define | WRAP_COLUMN_DEFAULT 0 |
#define | PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN) |
#define | PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT) |
#define | deparse_columns_fetch(rangetable_index, dpns) ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1)) |
#define | only_marker(rte) ((rte)->inh ? "" : "ONLY ") |
Functions | |
static char * | deparse_expression_pretty (Node *expr, List *dpcontext, bool forceprefix, bool showimplicit, int prettyFlags, int startIndent) |
static char * | pg_get_viewdef_worker (Oid viewoid, int prettyFlags, int wrapColumn) |
static char * | pg_get_triggerdef_worker (Oid trigid, bool pretty) |
static void | decompile_column_index_array (Datum column_index_array, Oid relId, StringInfo buf) |
static char * | pg_get_ruledef_worker (Oid ruleoid, int prettyFlags) |
static char * | pg_get_indexdef_worker (Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags) |
static char * | pg_get_constraintdef_worker (Oid constraintId, bool fullCommand, int prettyFlags) |
static text * | pg_get_expr_worker (text *expr, Oid relid, const char *relname, 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 | set_rtable_names (deparse_namespace *dpns, List *parent_namespaces, Bitmapset *rels_used) |
static bool | refname_is_unique (char *refname, deparse_namespace *dpns, List *parent_namespaces) |
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_unnamed_full_join_using (Node *jtnode) |
static void | set_using_names (deparse_namespace *dpns, Node *jtnode) |
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 (char *colname, deparse_namespace *dpns, deparse_columns *colinfo) |
static char * | make_colname_unique (char *colname, deparse_namespace *dpns, deparse_columns *colinfo) |
static void | expand_colnames_array_to (deparse_columns *colinfo, int n) |
static void | identify_join_columns (JoinExpr *j, RangeTblEntry *jrte, deparse_columns *colinfo) |
static void | flatten_join_using_qual (Node *qual, List **leftvars, List **rightvars) |
static char * | get_rtable_name (int rtindex, deparse_context *context) |
static void | set_deparse_planstate (deparse_namespace *dpns, PlanState *ps) |
static void | push_child_plan (deparse_namespace *dpns, PlanState *ps, 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, 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, TupleDesc resultDesc) |
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_delete_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, TupleDesc resultDesc) |
static void | get_target_list (List *targetList, deparse_context *context, TupleDesc resultDesc) |
static void | get_setop_query (Node *setOp, Query *query, deparse_context *context, TupleDesc resultDesc) |
static Node * | get_rule_sortgroupclause (SortGroupClause *srt, List *tlist, bool force_colno, 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 char * | get_variable (Var *var, int levelsup, bool istoplevel, deparse_context *context) |
static Node * | find_param_referent (Param *param, deparse_context *context, deparse_namespace **dpns_p, ListCell **ancestor_cell_p) |
static void | get_parameter (Param *param, deparse_context *context) |
static const char * | get_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 | get_rule_expr (Node *node, deparse_context *context, bool showimplicit) |
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) |
static void | get_windowfunc_expr (WindowFunc *wfunc, 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 | simple_quote_literal (StringInfo buf, const char *val) |
static void | get_sublink_expr (SubLink *sublink, deparse_context *context) |
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_column_alias_list (deparse_columns *colinfo, deparse_context *context) |
static void | get_from_clause_coldeflist (deparse_columns *colinfo, List *types, List *typmods, List *collations, deparse_context *context) |
static void | get_opclass_name (Oid opclass, Oid actual_datatype, StringInfo buf) |
static Node * | processIndirection (Node *node, deparse_context *context, bool printit) |
static void | printSubscripts (ArrayRef *aref, deparse_context *context) |
static char * | get_relation_name (Oid relid) |
static char * | generate_relation_name (Oid relid, List *namespaces) |
static char * | generate_function_name (Oid funcid, int nargs, List *argnames, Oid *argtypes, bool was_variadic, bool *use_variadic_p) |
static char * | generate_operator_name (Oid operid, Oid arg1, Oid arg2) |
static text * | string_to_text (char *str) |
static char * | flatten_reloptions (Oid relid) |
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) |
char * | pg_get_indexdef_string (Oid indexrelid) |
char * | pg_get_indexdef_columns (Oid indexrelid, bool pretty) |
Datum | pg_get_constraintdef (PG_FUNCTION_ARGS) |
Datum | pg_get_constraintdef_ext (PG_FUNCTION_ARGS) |
char * | pg_get_constraintdef_string (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) |
char * | deparse_expression (Node *expr, List *dpcontext, bool forceprefix, bool showimplicit) |
List * | deparse_context_for (const char *aliasname, Oid relid) |
List * | deparse_context_for_planstate (Node *planstate, List *ancestors, List *rtable, List *rtable_names) |
List * | select_rtable_names_for_explain (List *rtable, Bitmapset *rels_used) |
static RangeTblEntry * | get_simple_values_rte (Query *query) |
static const char * | get_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) |
const char * | quote_identifier (const char *ident) |
char * | quote_qualified_identifier (const char *qualifier, const char *ident) |
char * | generate_collation_name (Oid collid) |
Variables | |
static SPIPlanPtr | plan_getrulebyoid = NULL |
static const char * | query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1" |
static SPIPlanPtr | plan_getviewrule = NULL |
static const char * | query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2" |
bool | quote_all_identifiers = false |
#define deparse_columns_fetch | ( | rangetable_index, | ||
dpns | ||||
) | ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1)) |
Definition at line 251 of file ruleutils.c.
Referenced by get_from_clause_item(), get_variable(), set_join_column_names(), and set_using_names().
#define only_marker | ( | rte | ) | ((rte)->inh ? "" : "ONLY ") |
Definition at line 406 of file ruleutils.c.
Referenced by get_delete_query_def(), get_from_clause_item(), and get_update_query_def().
#define PRETTY_INDENT | ( | context | ) | ((context)->prettyFlags & PRETTYFLAG_INDENT) |
Definition at line 82 of file ruleutils.c.
Referenced by appendContextKeyword(), get_basic_select_query(), get_delete_query_def(), get_from_clause(), get_insert_query_def(), get_rule_expr(), get_setop_query(), get_target_list(), get_update_query_def(), and get_with_clause().
#define PRETTY_PAREN | ( | context | ) | ((context)->prettyFlags & PRETTYFLAG_PAREN) |
Definition at line 81 of file ruleutils.c.
Referenced by get_coercion_expr(), get_from_clause_item(), get_oper_expr(), get_rule_expr(), and get_rule_expr_paren().
#define PRETTYFLAG_INDENT 2 |
Definition at line 75 of file ruleutils.c.
Referenced by make_ruledef(), pg_get_constraintdef_ext(), pg_get_expr_ext(), pg_get_indexdef_columns(), pg_get_indexdef_ext(), pg_get_ruledef_ext(), pg_get_triggerdef_worker(), pg_get_viewdef_ext(), and pg_get_viewdef_name_ext().
#define PRETTYFLAG_PAREN 1 |
Definition at line 74 of file ruleutils.c.
Referenced by isSimpleNode(), pg_get_constraintdef_ext(), pg_get_expr_ext(), pg_get_indexdef_columns(), pg_get_indexdef_ext(), pg_get_ruledef_ext(), pg_get_triggerdef_worker(), pg_get_viewdef_ext(), pg_get_viewdef_name_ext(), and pg_get_viewdef_wrap().
#define PRETTYINDENT_JOIN 13 |
Definition at line 69 of file ruleutils.c.
Referenced by get_from_clause_item().
#define PRETTYINDENT_JOIN_ON (PRETTYINDENT_JOIN-PRETTYINDENT_STD) |
Definition at line 70 of file ruleutils.c.
#define PRETTYINDENT_STD 8 |
Definition at line 68 of file ruleutils.c.
Referenced by get_basic_select_query(), get_delete_query_def(), get_from_clause(), get_insert_query_def(), get_rule_windowclause(), get_select_query_def(), get_setop_query(), get_target_list(), get_update_query_def(), and get_utility_query_def().
#define PRETTYINDENT_VAR 4 |
Definition at line 71 of file ruleutils.c.
Referenced by get_from_clause(), get_rule_expr(), and get_target_list().
#define WRAP_COLUMN_DEFAULT 0 |
Definition at line 78 of file ruleutils.c.
Referenced by make_ruledef(), pg_get_viewdef(), pg_get_viewdef_ext(), pg_get_viewdef_name(), and pg_get_viewdef_name_ext().
static void appendContextKeyword | ( | deparse_context * | context, | |
const char * | str, | |||
int | indentBefore, | |||
int | indentAfter, | |||
int | indentPlus | |||
) | [static] |
Definition at line 6227 of file ruleutils.c.
References appendStringInfoChar(), appendStringInfoSpaces(), appendStringInfoString(), deparse_context::buf, deparse_context::indentLevel, Max, and PRETTY_INDENT.
Referenced by get_basic_select_query(), get_delete_query_def(), get_from_clause(), get_from_clause_item(), get_insert_query_def(), 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().
{ if (PRETTY_INDENT(context)) { context->indentLevel += indentBefore; appendStringInfoChar(context->buf, '\n'); appendStringInfoSpaces(context->buf, Max(context->indentLevel, 0) + indentPlus); appendStringInfoString(context->buf, str); context->indentLevel += indentAfter; if (context->indentLevel < 0) context->indentLevel = 0; } else appendStringInfoString(context->buf, str); }
static bool colname_is_unique | ( | char * | colname, | |
deparse_namespace * | dpns, | |||
deparse_columns * | colinfo | |||
) | [static] |
Definition at line 3266 of file ruleutils.c.
References deparse_columns::colnames, i, lfirst, deparse_columns::new_colnames, deparse_columns::num_cols, deparse_columns::num_new_cols, and deparse_namespace::using_names.
Referenced by make_colname_unique().
{ int i; ListCell *lc; /* Check against already-assigned column aliases within RTE */ for (i = 0; i < colinfo->num_cols; i++) { char *oldname = colinfo->colnames[i]; if (oldname && strcmp(oldname, colname) == 0) return false; } /* * If we're building a new_colnames array, check that too (this will be * partially but not completely redundant with the previous checks) */ for (i = 0; i < colinfo->num_new_cols; i++) { char *oldname = colinfo->new_colnames[i]; if (oldname && strcmp(oldname, colname) == 0) return false; } /* Also check against USING-column names that must be globally unique */ foreach(lc, dpns->using_names) { char *oldname = (char *) lfirst(lc); if (strcmp(oldname, colname) == 0) return false; } return true; }
static void decompile_column_index_array | ( | Datum | column_index_array, | |
Oid | relId, | |||
StringInfo | buf | |||
) | [static] |
Definition at line 1586 of file ruleutils.c.
References appendStringInfo(), appendStringInfoString(), DatumGetArrayTypeP, DatumGetInt16, deconstruct_array(), get_relid_attribute_name(), INT2OID, NULL, and quote_identifier().
Referenced by pg_get_constraintdef_worker().
{ Datum *keys; int nKeys; int j; /* Extract data from array of int16 */ deconstruct_array(DatumGetArrayTypeP(column_index_array), INT2OID, 2, true, 's', &keys, NULL, &nKeys); for (j = 0; j < nKeys; j++) { char *colName; colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j])); if (j == 0) appendStringInfoString(buf, quote_identifier(colName)); else appendStringInfo(buf, ", %s", quote_identifier(colName)); } }
Definition at line 2333 of file ruleutils.c.
References RangeTblEntry::alias, deparse_namespace::ctes, RangeTblEntry::eref, RangeTblEntry::inFromCl, RangeTblEntry::inh, RangeTblEntry::lateral, list_make1, makeAlias(), makeNode, NIL, NULL, palloc0(), RangeTblEntry::relid, RangeTblEntry::relkind, deparse_namespace::rtable, RangeTblEntry::rtekind, set_rtable_names(), and set_simple_column_names().
Referenced by pg_get_constraintdef_worker(), pg_get_expr_worker(), pg_get_indexdef_worker(), StoreAttrDefault(), and StoreRelCheck().
{ deparse_namespace *dpns; RangeTblEntry *rte; dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace)); /* Build a minimal RTE for the rel */ rte = makeNode(RangeTblEntry); rte->rtekind = RTE_RELATION; rte->relid = relid; rte->relkind = RELKIND_RELATION; /* no need for exactness here */ rte->alias = makeAlias(aliasname, NIL); rte->eref = rte->alias; rte->lateral = false; rte->inh = false; rte->inFromCl = true; /* Build one-element rtable */ dpns->rtable = list_make1(rte); dpns->ctes = NIL; set_rtable_names(dpns, NIL, NULL); set_simple_column_names(dpns); /* Return a one-deep namespace stack */ return list_make1(dpns); }
List* deparse_context_for_planstate | ( | Node * | planstate, | |
List * | ancestors, | |||
List * | rtable, | |||
List * | rtable_names | |||
) |
Definition at line 2387 of file ruleutils.c.
References deparse_namespace::ancestors, deparse_namespace::ctes, list_make1, palloc0(), deparse_namespace::rtable, deparse_namespace::rtable_names, set_deparse_planstate(), and set_simple_column_names().
Referenced by show_expression(), show_plan_tlist(), and show_sort_keys_common().
{ deparse_namespace *dpns; dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace)); /* Initialize fields that stay the same across the whole plan tree */ dpns->rtable = rtable; dpns->rtable_names = rtable_names; dpns->ctes = NIL; /* * Set up column name aliases. We will get rather bogus results for join * RTEs, but that doesn't matter because plan trees don't contain any join * alias Vars. */ set_simple_column_names(dpns); /* Set our attention on the specific plan node passed in */ set_deparse_planstate(dpns, (PlanState *) planstate); dpns->ancestors = ancestors; /* Return a one-deep namespace stack */ return list_make1(dpns); }
Definition at line 2275 of file ruleutils.c.
References deparse_expression_pretty().
Referenced by AlterDomainDefault(), DefineDomain(), domainAddConstraint(), print_function_arguments(), show_expression(), show_plan_tlist(), show_sort_keys_common(), StoreAttrDefault(), and StoreRelCheck().
{ return deparse_expression_pretty(expr, dpcontext, forceprefix, showimplicit, 0, 0); }
static char * deparse_expression_pretty | ( | Node * | expr, | |
List * | dpcontext, | |||
bool | forceprefix, | |||
bool | showimplicit, | |||
int | prettyFlags, | |||
int | startIndent | |||
) | [static] |
Definition at line 2302 of file ruleutils.c.
References deparse_context::buf, buf, StringInfoData::data, get_rule_expr(), deparse_context::indentLevel, initStringInfo(), deparse_context::namespaces, deparse_context::prettyFlags, deparse_context::varprefix, deparse_context::windowClause, deparse_context::windowTList, and deparse_context::wrapColumn.
Referenced by deparse_expression(), pg_get_constraintdef_worker(), pg_get_expr_worker(), and pg_get_indexdef_worker().
{ StringInfoData buf; deparse_context context; initStringInfo(&buf); context.buf = &buf; context.namespaces = dpcontext; context.windowClause = NIL; context.windowTList = NIL; context.varprefix = forceprefix; context.prettyFlags = prettyFlags; context.wrapColumn = WRAP_COLUMN_DEFAULT; context.indentLevel = startIndent; get_rule_expr(expr, &context, showimplicit); return buf.data; }
static void expand_colnames_array_to | ( | deparse_columns * | colinfo, | |
int | n | |||
) | [static] |
Definition at line 3337 of file ruleutils.c.
References deparse_columns::colnames, NULL, deparse_columns::num_cols, palloc0(), and repalloc().
Referenced by set_join_column_names(), set_relation_column_names(), and set_using_names().
{ if (n > colinfo->num_cols) { if (colinfo->colnames == NULL) colinfo->colnames = (char **) palloc0(n * sizeof(char *)); else { colinfo->colnames = (char **) repalloc(colinfo->colnames, n * sizeof(char *)); memset(colinfo->colnames + colinfo->num_cols, 0, (n - colinfo->num_cols) * sizeof(char *)); } colinfo->num_cols = n; } }
static Node * find_param_referent | ( | Param * | param, | |
deparse_context * | context, | |||
deparse_namespace ** | dpns_p, | |||
ListCell ** | ancestor_cell_p | |||
) | [static] |
Definition at line 5803 of file ruleutils.c.
References deparse_namespace::ancestors, arg, SubPlan::args, Assert, ExprState::expr, forboth, PlanState::initPlan, innerPlanState, IsA, lfirst, lfirst_int, linitial, deparse_context::namespaces, NestLoop::nestParams, NIL, PARAM_EXEC, Param::paramid, Param::paramkind, NestLoopParam::paramno, NestLoopParam::paramval, SubPlan::parParam, PlanState::plan, SubPlanState::planstate, deparse_namespace::planstate, PlanState::subPlan, and SubPlanState::xprstate.
Referenced by get_name_for_var_field(), and get_parameter().
{ /* Initialize output parameters to prevent compiler warnings */ *dpns_p = NULL; *ancestor_cell_p = NULL; /* * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or * SubPlan argument. This will necessarily be in some ancestor of the * current expression's PlanState. */ if (param->paramkind == PARAM_EXEC) { deparse_namespace *dpns; PlanState *child_ps; bool in_same_plan_level; ListCell *lc; dpns = (deparse_namespace *) linitial(context->namespaces); child_ps = dpns->planstate; in_same_plan_level = true; foreach(lc, dpns->ancestors) { PlanState *ps = (PlanState *) lfirst(lc); ListCell *lc2; /* * NestLoops transmit params to their inner child only; also, once * we've crawled up out of a subplan, this couldn't possibly be * the right match. */ if (IsA(ps, NestLoopState) && child_ps == innerPlanState(ps) && in_same_plan_level) { NestLoop *nl = (NestLoop *) ps->plan; foreach(lc2, nl->nestParams) { NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2); if (nlp->paramno == param->paramid) { /* Found a match, so return it */ *dpns_p = dpns; *ancestor_cell_p = lc; return (Node *) nlp->paramval; } } } /* * Check to see if we're crawling up from a subplan. */ foreach(lc2, ps->subPlan) { SubPlanState *sstate = (SubPlanState *) lfirst(lc2); SubPlan *subplan = (SubPlan *) sstate->xprstate.expr; ListCell *lc3; ListCell *lc4; if (child_ps != sstate->planstate) continue; /* Matched subplan, so check its arguments */ forboth(lc3, subplan->parParam, lc4, subplan->args) { int paramid = lfirst_int(lc3); Node *arg = (Node *) lfirst(lc4); if (paramid == param->paramid) { /* Found a match, so return it */ *dpns_p = dpns; *ancestor_cell_p = lc; return arg; } } /* Keep looking, but we are emerging from a subplan. */ in_same_plan_level = false; break; } /* * Likewise check to see if we're emerging from an initplan. * Initplans never have any parParams, so no need to search that * list, but we need to know if we should reset * in_same_plan_level. */ foreach(lc2, ps->initPlan) { SubPlanState *sstate = (SubPlanState *) lfirst(lc2); if (child_ps != sstate->planstate) continue; /* No parameters to be had here. */ Assert(((SubPlan *) sstate->xprstate.expr)->parParam == NIL); /* Keep looking, but we are emerging from an initplan. */ in_same_plan_level = false; break; } /* No luck, crawl up to next ancestor */ child_ps = ps; } } /* No referent found */ return NULL; }
Definition at line 3488 of file ruleutils.c.
References AND_EXPR, OpExpr::args, BoolExpr::args, Assert, BoolExpr::boolop, elog, ERROR, IsA, lappend(), lfirst, linitial, list_length(), lsecond, nodeTag, and strip_implicit_coercions().
Referenced by identify_join_columns().
{ if (IsA(qual, BoolExpr)) { /* Handle AND nodes by recursion */ BoolExpr *b = (BoolExpr *) qual; ListCell *lc; Assert(b->boolop == AND_EXPR); foreach(lc, b->args) { flatten_join_using_qual((Node *) lfirst(lc), leftvars, rightvars); } } else if (IsA(qual, OpExpr)) { /* Otherwise we should have an equality operator */ OpExpr *op = (OpExpr *) qual; Var *var; if (list_length(op->args) != 2) elog(ERROR, "unexpected unary operator in JOIN/USING qual"); /* Arguments should be Vars with perhaps implicit coercions */ var = (Var *) strip_implicit_coercions((Node *) linitial(op->args)); if (!IsA(var, Var)) elog(ERROR, "unexpected node type in JOIN/USING qual: %d", (int) nodeTag(var)); *leftvars = lappend(*leftvars, var); var = (Var *) strip_implicit_coercions((Node *) lsecond(op->args)); if (!IsA(var, Var)) elog(ERROR, "unexpected node type in JOIN/USING qual: %d", (int) nodeTag(var)); *rightvars = lappend(*rightvars, var); } else { /* Perhaps we have an implicit coercion to boolean? */ Node *q = strip_implicit_coercions(qual); if (q != qual) flatten_join_using_qual(q, leftvars, rightvars); else elog(ERROR, "unexpected node type in JOIN/USING qual: %d", (int) nodeTag(qual)); } }
static char * flatten_reloptions | ( | Oid | relid | ) | [static] |
Definition at line 8761 of file ruleutils.c.
References Anum_pg_class_reloptions, CStringGetTextDatum, elog, ERROR, HeapTupleIsValid, ObjectIdGetDatum, OidFunctionCall2, ReleaseSysCache(), RELOID, SearchSysCache1, SysCacheGetAttr(), and TextDatumGetCString.
Referenced by pg_get_constraintdef_worker(), and pg_get_indexdef_worker().
{ char *result = NULL; HeapTuple tuple; Datum reloptions; bool isnull; tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", relid); reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, &isnull); if (!isnull) { Datum sep, txt; /* * We want to use array_to_text(reloptions, ', ') --- but * DirectFunctionCall2(array_to_text) does not work, because * array_to_text() relies on flinfo to be valid. So use * OidFunctionCall2. */ sep = CStringGetTextDatum(", "); txt = OidFunctionCall2(F_ARRAY_TO_TEXT, reloptions, sep); result = TextDatumGetCString(txt); } ReleaseSysCache(tuple); return result; }
char* generate_collation_name | ( | Oid | collid | ) |
Definition at line 8716 of file ruleutils.c.
References CollationIsVisible(), COLLOID, elog, ERROR, get_namespace_name(), 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(), and pg_get_indexdef_worker().
{ HeapTuple tp; Form_pg_collation colltup; char *collname; char *nspname; char *result; tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for collation %u", collid); colltup = (Form_pg_collation) GETSTRUCT(tp); collname = NameStr(colltup->collname); if (!CollationIsVisible(collid)) nspname = get_namespace_name(colltup->collnamespace); else nspname = NULL; result = quote_qualified_identifier(nspname, collname); ReleaseSysCache(tp); return result; }
static char * generate_function_name | ( | Oid | funcid, | |
int | nargs, | |||
List * | argnames, | |||
Oid * | argtypes, | |||
bool | was_variadic, | |||
bool * | use_variadic_p | |||
) | [static] |
Definition at line 8554 of file ruleutils.c.
References ANYOID, Assert, elog, ERROR, func_get_detail(), FUNCDETAIL_AGGREGATE, FUNCDETAIL_NORMAL, FUNCDETAIL_WINDOWFUNC, get_namespace_name(), GETSTRUCT, HeapTupleIsValid, list_make1, makeString(), NameStr, NIL, NULL, ObjectIdGetDatum, OidIsValid, PROCOID, quote_qualified_identifier(), ReleaseSysCache(), and SearchSysCache1.
Referenced by get_agg_expr(), get_func_expr(), get_windowfunc_expr(), and pg_get_triggerdef_worker().
{ char *result; HeapTuple proctup; Form_pg_proc procform; char *proname; bool use_variadic; char *nspname; FuncDetailCode p_result; Oid p_funcid; Oid p_rettype; bool p_retset; int p_nvargs; Oid *p_true_typeids; proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(proctup)) elog(ERROR, "cache lookup failed for function %u", funcid); procform = (Form_pg_proc) GETSTRUCT(proctup); proname = NameStr(procform->proname); /* * Determine whether VARIADIC should be printed. We must do this first * since it affects the lookup rules in func_get_detail(). * * Currently, we always print VARIADIC if the function is variadic and * takes a variadic type other than ANY. (In principle, if VARIADIC * wasn't originally specified and the array actual argument is * deconstructable, we could print the array elements separately and not * print VARIADIC, thus more nearly reproducing the original input. For * the moment that seems like too much complication for the benefit.) * However, if the function takes VARIADIC ANY, then the parser didn't * fold the arguments together into an array, so we must print VARIADIC if * and only if it was used originally. */ if (use_variadic_p) { if (OidIsValid(procform->provariadic)) { if (procform->provariadic != ANYOID) use_variadic = true; else use_variadic = was_variadic; } else use_variadic = false; *use_variadic_p = use_variadic; } else { Assert(!was_variadic); use_variadic = false; } /* * The idea here is to schema-qualify only if the parser would fail to * resolve the correct function given the unqualified func name with the * specified argtypes and VARIADIC flag. */ p_result = func_get_detail(list_make1(makeString(proname)), NIL, argnames, nargs, argtypes, !use_variadic, true, &p_funcid, &p_rettype, &p_retset, &p_nvargs, &p_true_typeids, NULL); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE || p_result == FUNCDETAIL_WINDOWFUNC) && p_funcid == funcid) nspname = NULL; else nspname = get_namespace_name(procform->pronamespace); result = quote_qualified_identifier(nspname, proname); ReleaseSysCache(proctup); return result; }
Definition at line 8646 of file ruleutils.c.
References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), buf, StringInfoData::data, elog, ERROR, get_namespace_name(), GETSTRUCT, HeapTupleIsValid, initStringInfo(), left_oper(), list_make1, makeString(), NameStr, NULL, ObjectIdGetDatum, oper(), OPEROID, oprid(), quote_identifier(), ReleaseSysCache(), right_oper(), 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().
{ StringInfoData buf; HeapTuple opertup; Form_pg_operator operform; char *oprname; char *nspname; Operator p_result; initStringInfo(&buf); opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid)); if (!HeapTupleIsValid(opertup)) elog(ERROR, "cache lookup failed for operator %u", operid); operform = (Form_pg_operator) GETSTRUCT(opertup); oprname = NameStr(operform->oprname); /* * The idea here is to schema-qualify only if the parser would fail to * resolve the correct operator given the unqualified op name with the * specified argtypes. */ switch (operform->oprkind) { case 'b': p_result = oper(NULL, list_make1(makeString(oprname)), arg1, arg2, true, -1); break; case 'l': p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2, true, -1); break; case 'r': p_result = right_oper(NULL, list_make1(makeString(oprname)), arg1, true, -1); break; default: elog(ERROR, "unrecognized oprkind: %d", operform->oprkind); p_result = NULL; /* keep compiler quiet */ break; } if (p_result != NULL && oprid(p_result) == operid) nspname = NULL; else { nspname = get_namespace_name(operform->oprnamespace); appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname)); } appendStringInfoString(&buf, oprname); if (nspname) appendStringInfoChar(&buf, ')'); if (p_result != NULL) ReleaseSysCache(p_result); ReleaseSysCache(opertup); return buf.data; }
Definition at line 8485 of file ruleutils.c.
References CommonTableExpr::ctename, deparse_namespace::ctes, elog, ERROR, get_namespace_name(), GETSTRUCT, HeapTupleIsValid, lfirst, NameStr, ObjectIdGetDatum, quote_qualified_identifier(), RelationIsVisible(), ReleaseSysCache(), RELOID, and SearchSysCache1.
Referenced by get_delete_query_def(), get_from_clause_item(), get_insert_query_def(), get_update_query_def(), make_ruledef(), pg_get_constraintdef_worker(), pg_get_indexdef_worker(), and pg_get_triggerdef_worker().
{ HeapTuple tp; Form_pg_class reltup; bool need_qual; ListCell *nslist; char *relname; char *nspname; char *result; tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for relation %u", relid); reltup = (Form_pg_class) GETSTRUCT(tp); relname = NameStr(reltup->relname); /* Check for conflicting CTE name */ need_qual = false; foreach(nslist, namespaces) { deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist); ListCell *ctlist; foreach(ctlist, dpns->ctes) { CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist); if (strcmp(cte->ctename, relname) == 0) { need_qual = true; break; } } if (need_qual) break; } /* Otherwise, qualify the name if not visible in search path */ if (!need_qual) need_qual = !RelationIsVisible(relid); if (need_qual) nspname = get_namespace_name(reltup->relnamespace); else nspname = NULL; result = quote_qualified_identifier(nspname, relname); ReleaseSysCache(tp); return result; }
static void get_agg_expr | ( | Aggref * | aggref, | |
deparse_context * | context | |||
) | [static] |
Definition at line 7379 of file ruleutils.c.
References Aggref::aggdistinct, Aggref::aggfnoid, Aggref::aggorder, Aggref::aggstar, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, Aggref::args, Assert, deparse_context::buf, buf, ereport, errcode(), errmsg(), ERROR, TargetEntry::expr, exprType(), FUNC_MAX_ARGS, generate_function_name(), get_rule_expr(), get_rule_orderby(), IsA, lappend(), lfirst, NIL, NULL, and TargetEntry::resjunk.
Referenced by get_rule_expr().
{ StringInfo buf = context->buf; Oid argtypes[FUNC_MAX_ARGS]; List *arglist; int nargs; ListCell *l; /* Extract the regular arguments, ignoring resjunk stuff for the moment */ arglist = NIL; nargs = 0; foreach(l, aggref->args) { TargetEntry *tle = (TargetEntry *) lfirst(l); Node *arg = (Node *) tle->expr; Assert(!IsA(arg, NamedArgExpr)); if (tle->resjunk) continue; if (nargs >= FUNC_MAX_ARGS) /* paranoia */ ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("too many arguments"))); argtypes[nargs] = exprType(arg); arglist = lappend(arglist, arg); nargs++; } appendStringInfo(buf, "%s(%s", generate_function_name(aggref->aggfnoid, nargs, NIL, argtypes, false, NULL), (aggref->aggdistinct != NIL) ? "DISTINCT " : ""); /* aggstar can be set only in zero-argument aggregates */ if (aggref->aggstar) appendStringInfoChar(buf, '*'); else get_rule_expr((Node *) arglist, context, true); if (aggref->aggorder != NIL) { appendStringInfoString(buf, " ORDER BY "); get_rule_orderby(aggref->aggorder, aggref->args, false, context); } appendStringInfoChar(buf, ')'); }
static void get_basic_select_query | ( | Query * | query, | |
deparse_context * | context, | |||
TupleDesc | resultDesc | |||
) | [static] |
Definition at line 4292 of file ruleutils.c.
References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, Query::distinctClause, get_from_clause(), get_rule_expr(), get_rule_sortgroupclause(), get_rule_windowclause(), get_simple_values_rte(), get_target_list(), get_values_def(), Query::groupClause, Query::hasDistinctOn, Query::havingQual, deparse_context::indentLevel, Query::jointree, lfirst, NIL, NULL, PRETTY_INDENT, PRETTYINDENT_STD, FromExpr::quals, Query::targetList, RangeTblEntry::values_lists, and Query::windowClause.
Referenced by get_select_query_def().
{ StringInfo buf = context->buf; RangeTblEntry *values_rte; char *sep; ListCell *l; if (PRETTY_INDENT(context)) { context->indentLevel += PRETTYINDENT_STD; appendStringInfoChar(buf, ' '); } /* * If the query looks like SELECT * FROM (VALUES ...), then print just the * VALUES part. This reverses what transformValuesClause() did at parse * time. */ values_rte = get_simple_values_rte(query); if (values_rte) { get_values_def(values_rte->values_lists, context); return; } /* * Build up the query string - first we say SELECT */ appendStringInfo(buf, "SELECT"); /* Add the DISTINCT clause if given */ if (query->distinctClause != NIL) { if (query->hasDistinctOn) { appendStringInfo(buf, " DISTINCT ON ("); sep = ""; foreach(l, query->distinctClause) { SortGroupClause *srt = (SortGroupClause *) lfirst(l); appendStringInfoString(buf, sep); get_rule_sortgroupclause(srt, query->targetList, false, context); sep = ", "; } appendStringInfo(buf, ")"); } else appendStringInfo(buf, " DISTINCT"); } /* Then we tell what to select (the targetlist) */ get_target_list(query->targetList, context, resultDesc); /* Add the FROM clause if needed */ get_from_clause(query, " FROM ", context); /* Add the WHERE clause if given */ if (query->jointree->quals != NULL) { appendContextKeyword(context, " WHERE ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); get_rule_expr(query->jointree->quals, context, false); } /* Add the GROUP BY clause if given */ if (query->groupClause != NULL) { appendContextKeyword(context, " GROUP BY ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); sep = ""; foreach(l, query->groupClause) { SortGroupClause *grp = (SortGroupClause *) lfirst(l); appendStringInfoString(buf, sep); get_rule_sortgroupclause(grp, query->targetList, false, context); sep = ", "; } } /* Add the HAVING clause if given */ if (query->havingQual != NULL) { appendContextKeyword(context, " HAVING ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); get_rule_expr(query->havingQual, context, false); } /* Add the WINDOW clause if needed */ if (query->windowClause != NIL) get_rule_windowclause(query, context); }
static void get_coercion_expr | ( | Node * | arg, | |
deparse_context * | context, | |||
Oid | resulttype, | |||
int32 | resulttypmod, | |||
Node * | parentNode | |||
) | [static] |
Definition at line 7495 of file ruleutils.c.
References appendStringInfo(), appendStringInfoChar(), deparse_context::buf, buf, format_type_with_typemod(), get_const_expr(), get_rule_expr_paren(), IsA, and PRETTY_PAREN.
Referenced by get_func_expr(), and get_rule_expr().
{ StringInfo buf = context->buf; /* * Since parse_coerce.c doesn't immediately collapse application of * length-coercion functions to constants, what we'll typically see in * such cases is a Const with typmod -1 and a length-coercion function * right above it. Avoid generating redundant output. However, beware of * suppressing casts when the user actually wrote something like * 'foo'::text::char(3). */ if (arg && IsA(arg, Const) && ((Const *) arg)->consttype == resulttype && ((Const *) arg)->consttypmod == -1) { /* Show the constant without normal ::typename decoration */ get_const_expr((Const *) arg, context, -1); } else { if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); get_rule_expr_paren(arg, context, false, parentNode); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); } appendStringInfo(buf, "::%s", format_type_with_typemod(resulttype, resulttypmod)); }
static void get_column_alias_list | ( | deparse_columns * | colinfo, | |
deparse_context * | context | |||
) | [static] |
Definition at line 8150 of file ruleutils.c.
References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, i, deparse_columns::new_colnames, deparse_columns::num_new_cols, deparse_columns::printaliases, and quote_identifier().
Referenced by get_from_clause_item().
{ StringInfo buf = context->buf; int i; bool first = true; /* Don't print aliases if not needed */ if (!colinfo->printaliases) return; for (i = 0; i < colinfo->num_new_cols; i++) { char *colname = colinfo->new_colnames[i]; if (first) { appendStringInfoChar(buf, '('); first = false; } else appendStringInfo(buf, ", "); appendStringInfoString(buf, quote_identifier(colname)); } if (!first) appendStringInfoChar(buf, ')'); }
static void get_const_collation | ( | Const * | constval, | |
deparse_context * | context | |||
) | [static] |
Definition at line 7674 of file ruleutils.c.
References appendStringInfo(), deparse_context::buf, buf, Const::constcollid, Const::consttype, generate_collation_name(), get_typcollation(), and OidIsValid.
Referenced by get_const_expr().
{ StringInfo buf = context->buf; if (OidIsValid(constval->constcollid)) { Oid typcollation = get_typcollation(constval->consttype); if (constval->constcollid != typcollation) { appendStringInfo(buf, " COLLATE %s", generate_collation_name(constval->constcollid)); } } }
static void get_const_expr | ( | Const * | constval, | |
deparse_context * | context, | |||
int | showtype | |||
) | [static] |
Definition at line 7543 of file ruleutils.c.
References appendStringInfo(), appendStringInfoString(), BITOID, BOOLOID, deparse_context::buf, buf, Const::constisnull, Const::consttype, Const::consttypmod, Const::constvalue, FLOAT4OID, FLOAT8OID, format_type_with_typemod(), get_const_collation(), getTypeOutputInfo(), INT2OID, INT4OID, INT8OID, NUMERICOID, OIDOID, OidOutputFunctionCall(), pfree(), simple_quote_literal(), UNKNOWNOID, and VARBITOID.
Referenced by get_coercion_expr(), get_rule_expr(), and get_rule_sortgroupclause().
{ StringInfo buf = context->buf; Oid typoutput; bool typIsVarlena; char *extval; bool isfloat = false; bool needlabel; if (constval->constisnull) { /* * Always label the type of a NULL constant to prevent misdecisions * about type when reparsing. */ appendStringInfo(buf, "NULL"); if (showtype >= 0) { appendStringInfo(buf, "::%s", format_type_with_typemod(constval->consttype, constval->consttypmod)); get_const_collation(constval, context); } return; } getTypeOutputInfo(constval->consttype, &typoutput, &typIsVarlena); extval = OidOutputFunctionCall(typoutput, constval->constvalue); switch (constval->consttype) { case INT2OID: case INT4OID: case INT8OID: case OIDOID: case FLOAT4OID: case FLOAT8OID: case NUMERICOID: { /* * These types are printed without quotes unless they contain * values that aren't accepted by the scanner unquoted (e.g., * 'NaN'). Note that strtod() and friends might accept NaN, * so we can't use that to test. * * In reality we only need to defend against infinity and NaN, * so we need not get too crazy about pattern matching here. * * There is a special-case gotcha: if the constant is signed, * we need to parenthesize it, else the parser might see a * leading plus/minus as binding less tightly than adjacent * operators --- particularly, the cast that we might attach * below. */ if (strspn(extval, "0123456789+-eE.") == strlen(extval)) { if (extval[0] == '+' || extval[0] == '-') appendStringInfo(buf, "(%s)", extval); else appendStringInfoString(buf, extval); if (strcspn(extval, "eE.") != strlen(extval)) isfloat = true; /* it looks like a float */ } else appendStringInfo(buf, "'%s'", extval); } break; case BITOID: case VARBITOID: appendStringInfo(buf, "B'%s'", extval); break; case BOOLOID: if (strcmp(extval, "t") == 0) appendStringInfo(buf, "true"); else appendStringInfo(buf, "false"); break; default: simple_quote_literal(buf, extval); break; } pfree(extval); if (showtype < 0) return; /* * For showtype == 0, append ::typename unless the constant will be * implicitly typed as the right type when it is read in. * * XXX this code has to be kept in sync with the behavior of the parser, * especially make_const. */ switch (constval->consttype) { case BOOLOID: case INT4OID: case UNKNOWNOID: /* These types can be left unlabeled */ needlabel = false; break; case NUMERICOID: /* * Float-looking constants will be typed as numeric, but if * there's a specific typmod we need to show it. */ needlabel = !isfloat || (constval->consttypmod >= 0); break; default: needlabel = true; break; } if (needlabel || showtype > 0) appendStringInfo(buf, "::%s", format_type_with_typemod(constval->consttype, constval->consttypmod)); get_const_collation(constval, context); }
static void get_delete_query_def | ( | Query * | query, | |
deparse_context * | context | |||
) | [static] |
Definition at line 5089 of file ruleutils.c.
References RangeTblEntry::alias, Alias::aliasname, appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), Assert, deparse_context::buf, buf, generate_relation_name(), get_from_clause(), get_rule_expr(), get_target_list(), get_with_clause(), deparse_context::indentLevel, Query::jointree, NIL, NULL, only_marker, PRETTY_INDENT, PRETTYINDENT_STD, FromExpr::quals, quote_identifier(), RangeTblEntry::relid, Query::resultRelation, Query::returningList, rt_fetch, Query::rtable, RTE_RELATION, and RangeTblEntry::rtekind.
Referenced by get_query_def().
{ StringInfo buf = context->buf; RangeTblEntry *rte; /* Insert the WITH clause if given */ get_with_clause(query, context); /* * Start the query with DELETE FROM relname */ rte = rt_fetch(query->resultRelation, query->rtable); Assert(rte->rtekind == RTE_RELATION); if (PRETTY_INDENT(context)) { appendStringInfoChar(buf, ' '); context->indentLevel += PRETTYINDENT_STD; } appendStringInfo(buf, "DELETE FROM %s%s", only_marker(rte), generate_relation_name(rte->relid, NIL)); if (rte->alias != NULL) appendStringInfo(buf, " %s", quote_identifier(rte->alias->aliasname)); /* Add the USING clause if given */ get_from_clause(query, " USING ", context); /* Add a WHERE clause if given */ if (query->jointree->quals != NULL) { appendContextKeyword(context, " WHERE ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); get_rule_expr(query->jointree->quals, context, false); } /* Add RETURNING if present */ if (query->returningList) { appendContextKeyword(context, " RETURNING", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); get_target_list(query->returningList, context, NULL); } }
static void get_from_clause | ( | Query * | query, | |
const char * | prefix, | |||
deparse_context * | context | |||
) | [static] |
Definition at line 7850 of file ruleutils.c.
References appendContextKeyword(), appendStringInfoString(), deparse_context::buf, buf, StringInfoData::data, FromExpr::fromlist, get_from_clause_item(), RangeTblEntry::inFromCl, initStringInfo(), IsA, Query::jointree, lfirst, NULL, pfree(), PRETTY_INDENT, PRETTYINDENT_STD, PRETTYINDENT_VAR, rt_fetch, Query::rtable, and deparse_context::wrapColumn.
Referenced by get_basic_select_query(), get_delete_query_def(), and get_update_query_def().
{ StringInfo buf = context->buf; bool first = true; ListCell *l; /* * We use the query's jointree as a guide to what to print. However, we * must ignore auto-added RTEs that are marked not inFromCl. (These can * only appear at the top level of the jointree, so it's sufficient to * check here.) This check also ensures we ignore the rule pseudo-RTEs * for NEW and OLD. */ foreach(l, query->jointree->fromlist) { Node *jtnode = (Node *) lfirst(l); if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef *) jtnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, query->rtable); if (!rte->inFromCl) continue; } if (first) { appendContextKeyword(context, prefix, -PRETTYINDENT_STD, PRETTYINDENT_STD, 2); first = false; get_from_clause_item(jtnode, query, context); } else { StringInfoData itembuf; appendStringInfoString(buf, ", "); /* * Put the new FROM item's text into itembuf so we can decide * after we've got it whether or not it needs to go on a new line. */ initStringInfo(&itembuf); context->buf = &itembuf; get_from_clause_item(jtnode, query, context); /* Restore context's output buffer */ context->buf = buf; /* Consider line-wrapping if enabled */ if (PRETTY_INDENT(context) && context->wrapColumn >= 0) { char *trailing_nl; /* Locate the start of the current line in the buffer */ trailing_nl = strrchr(buf->data, '\n'); if (trailing_nl == NULL) trailing_nl = buf->data; else trailing_nl++; /* * Add a newline, plus some indentation, if the new item would * cause an overflow. */ if (strlen(trailing_nl) + strlen(itembuf.data) > context->wrapColumn) appendContextKeyword(context, "", -PRETTYINDENT_STD, PRETTYINDENT_STD, PRETTYINDENT_VAR); } /* Add the new item */ appendStringInfoString(buf, itembuf.data); /* clean up */ pfree(itembuf.data); } } }
static void get_from_clause_coldeflist | ( | deparse_columns * | colinfo, | |
List * | types, | |||
List * | typmods, | |||
List * | collations, | |||
deparse_context * | context | |||
) | [static] |
Definition at line 8184 of file ruleutils.c.
References appendStringInfo(), appendStringInfoChar(), Assert, deparse_context::buf, buf, deparse_columns::colnames, format_type_with_typemod(), forthree, generate_collation_name(), get_typcollation(), i, lfirst_int, lfirst_oid, OidIsValid, and quote_identifier().
Referenced by get_from_clause_item().
{ StringInfo buf = context->buf; ListCell *l1; ListCell *l2; ListCell *l3; int i; appendStringInfoChar(buf, '('); i = 0; forthree(l1, types, l2, typmods, l3, collations) { char *attname = colinfo->colnames[i]; Oid atttypid = lfirst_oid(l1); int32 atttypmod = lfirst_int(l2); Oid attcollation = lfirst_oid(l3); Assert(attname); /* shouldn't be any dropped columns here */ if (i > 0) appendStringInfo(buf, ", "); appendStringInfo(buf, "%s %s", quote_identifier(attname), format_type_with_typemod(atttypid, atttypmod)); if (OidIsValid(attcollation) && attcollation != get_typcollation(atttypid)) appendStringInfo(buf, " COLLATE %s", generate_collation_name(attcollation)); i++; } appendStringInfoChar(buf, ')'); }
static void get_from_clause_item | ( | Node * | jtnode, | |
Query * | query, | |||
deparse_context * | context | |||
) | [static] |
Definition at line 7933 of file ruleutils.c.
References JoinExpr::alias, RangeTblEntry::alias, Alias::aliasname, appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, RangeTblEntry::ctename, deparse_columns_fetch, elog, ERROR, RangeTblEntry::funccolcollations, RangeTblEntry::funccoltypes, RangeTblEntry::funccoltypmods, RangeTblEntry::funcexpr, generate_relation_name(), get_column_alias_list(), get_from_clause_coldeflist(), get_query_def(), get_relation_name(), get_rtable_name(), get_rule_expr(), get_values_def(), deparse_context::indentLevel, IsA, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JoinExpr::jointype, JoinExpr::larg, RangeTblEntry::lateral, lfirst, linitial, deparse_context::namespaces, NIL, nodeTag, NULL, only_marker, PRETTY_PAREN, deparse_context::prettyFlags, PRETTYINDENT_JOIN, deparse_columns::printaliases, JoinExpr::quals, quote_identifier(), JoinExpr::rarg, RangeTblEntry::relid, rt_fetch, Query::rtable, RTE_CTE, RTE_FUNCTION, RTE_RELATION, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, JoinExpr::rtindex, RangeTblEntry::subquery, JoinExpr::usingClause, deparse_columns::usingNames, RangeTblEntry::values_lists, and deparse_context::wrapColumn.
Referenced by get_from_clause().
{ StringInfo buf = context->buf; deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces); if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef *) jtnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, query->rtable); char *refname = get_rtable_name(varno, context); deparse_columns *colinfo = deparse_columns_fetch(varno, dpns); bool printalias; if (rte->lateral) appendStringInfoString(buf, "LATERAL "); /* Print the FROM item proper */ switch (rte->rtekind) { case RTE_RELATION: /* Normal relation RTE */ appendStringInfo(buf, "%s%s", only_marker(rte), generate_relation_name(rte->relid, context->namespaces)); break; case RTE_SUBQUERY: /* Subquery RTE */ appendStringInfoChar(buf, '('); get_query_def(rte->subquery, buf, context->namespaces, NULL, context->prettyFlags, context->wrapColumn, context->indentLevel); appendStringInfoChar(buf, ')'); break; case RTE_FUNCTION: /* Function RTE */ get_rule_expr(rte->funcexpr, context, true); break; case RTE_VALUES: /* Values list RTE */ get_values_def(rte->values_lists, context); break; case RTE_CTE: appendStringInfoString(buf, quote_identifier(rte->ctename)); break; default: elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind); break; } /* Print the relation alias, if needed */ printalias = false; if (rte->alias != NULL) { /* Always print alias if user provided one */ printalias = true; } else if (colinfo->printaliases) { /* Always print alias if we need to print column aliases */ printalias = true; } else if (rte->rtekind == RTE_RELATION) { /* * No need to print alias if it's same as relation name (this * would normally be the case, but not if set_rtable_names had to * resolve a conflict). */ if (strcmp(refname, get_relation_name(rte->relid)) != 0) printalias = true; } else if (rte->rtekind == RTE_FUNCTION) { /* * For a function RTE, always print alias. This covers possible * renaming of the function and/or instability of the * FigureColname rules for things that aren't simple functions. * Also note we'd need to force it anyway for the RECORD case. */ printalias = true; } else if (rte->rtekind == RTE_CTE) { /* * No need to print alias if it's same as CTE name (this would * normally be the case, but not if set_rtable_names had to * resolve a conflict). */ if (strcmp(refname, rte->ctename) != 0) printalias = true; } if (printalias) appendStringInfo(buf, " %s", quote_identifier(refname)); /* Print the column definitions or aliases, if needed */ if (rte->rtekind == RTE_FUNCTION && rte->funccoltypes != NIL) { /* Function returning RECORD, reconstruct the columndefs */ get_from_clause_coldeflist(colinfo, rte->funccoltypes, rte->funccoltypmods, rte->funccolcollations, context); } else { /* Else print column aliases as needed */ get_column_alias_list(colinfo, context); } } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns); bool need_paren_on_right; need_paren_on_right = PRETTY_PAREN(context) && !IsA(j->rarg, RangeTblRef) && !(IsA(j->rarg, JoinExpr) &&((JoinExpr *) j->rarg)->alias != NULL); if (!PRETTY_PAREN(context) || j->alias != NULL) appendStringInfoChar(buf, '('); get_from_clause_item(j->larg, query, context); switch (j->jointype) { case JOIN_INNER: if (j->quals) appendContextKeyword(context, " JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); else appendContextKeyword(context, " CROSS JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 1); break; case JOIN_LEFT: appendContextKeyword(context, " LEFT JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); break; case JOIN_FULL: appendContextKeyword(context, " FULL JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); break; case JOIN_RIGHT: appendContextKeyword(context, " RIGHT JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); break; default: elog(ERROR, "unrecognized join type: %d", (int) j->jointype); } if (need_paren_on_right) appendStringInfoChar(buf, '('); get_from_clause_item(j->rarg, query, context); if (need_paren_on_right) appendStringInfoChar(buf, ')'); context->indentLevel -= PRETTYINDENT_JOIN_ON; if (j->usingClause) { ListCell *lc; bool first = true; appendStringInfo(buf, " USING ("); /* Use the assigned names, not what's in usingClause */ foreach(lc, colinfo->usingNames) { char *colname = (char *) lfirst(lc); if (first) first = false; else appendStringInfo(buf, ", "); appendStringInfoString(buf, quote_identifier(colname)); } appendStringInfoChar(buf, ')'); } else if (j->quals) { appendStringInfo(buf, " ON "); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); get_rule_expr(j->quals, context, false); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); } if (!PRETTY_PAREN(context) || j->alias != NULL) appendStringInfoChar(buf, ')'); /* Yes, it's correct to put alias after the right paren ... */ if (j->alias != NULL) { appendStringInfo(buf, " %s", quote_identifier(j->alias->aliasname)); get_column_alias_list(colinfo, context); } } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode)); }
static void get_func_expr | ( | FuncExpr * | expr, | |
deparse_context * | context, | |||
bool | showimplicit | |||
) | [static] |
Definition at line 7295 of file ruleutils.c.
References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, FuncExpr::args, deparse_context::buf, buf, COERCE_EXPLICIT_CAST, COERCE_IMPLICIT_CAST, ereport, errcode(), errmsg(), ERROR, exprIsLengthCoercion(), exprType(), FUNC_MAX_ARGS, FuncExpr::funcformat, FuncExpr::funcid, FuncExpr::funcresulttype, FuncExpr::funcvariadic, generate_function_name(), get_coercion_expr(), get_rule_expr(), get_rule_expr_paren(), IsA, lappend(), lfirst, linitial, list_length(), lnext, and NULL.
Referenced by get_rule_expr().
{ StringInfo buf = context->buf; Oid funcoid = expr->funcid; Oid argtypes[FUNC_MAX_ARGS]; int nargs; List *argnames; bool use_variadic; ListCell *l; /* * If the function call came from an implicit coercion, then just show the * first argument --- unless caller wants to see implicit coercions. */ if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit) { get_rule_expr_paren((Node *) linitial(expr->args), context, false, (Node *) expr); return; } /* * If the function call came from a cast, then show the first argument * plus an explicit cast operation. */ if (expr->funcformat == COERCE_EXPLICIT_CAST || expr->funcformat == COERCE_IMPLICIT_CAST) { Node *arg = linitial(expr->args); Oid rettype = expr->funcresulttype; int32 coercedTypmod; /* Get the typmod if this is a length-coercion function */ (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod); get_coercion_expr(arg, context, rettype, coercedTypmod, (Node *) expr); return; } /* * Normal function: display as proname(args). First we need to extract * the argument datatypes. */ if (list_length(expr->args) > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("too many arguments"))); nargs = 0; argnames = NIL; foreach(l, expr->args) { Node *arg = (Node *) lfirst(l); if (IsA(arg, NamedArgExpr)) argnames = lappend(argnames, ((NamedArgExpr *) arg)->name); argtypes[nargs] = exprType(arg); nargs++; } appendStringInfo(buf, "%s(", generate_function_name(funcoid, nargs, argnames, argtypes, expr->funcvariadic, &use_variadic)); nargs = 0; foreach(l, expr->args) { if (nargs++ > 0) appendStringInfoString(buf, ", "); if (use_variadic && lnext(l) == NULL) appendStringInfoString(buf, "VARIADIC "); get_rule_expr((Node *) lfirst(l), context, true); } appendStringInfoChar(buf, ')'); }
static void get_insert_query_def | ( | Query * | query, | |
deparse_context * | context | |||
) | [static] |
Definition at line 4854 of file ruleutils.c.
References appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, elog, ERROR, TargetEntry::expr, generate_relation_name(), get_query_def(), get_relid_attribute_name(), get_rule_expr(), get_target_list(), get_values_def(), get_with_clause(), deparse_context::indentLevel, lappend(), lfirst, linitial, list_head(), lnext, NIL, NULL, PRETTY_INDENT, deparse_context::prettyFlags, PRETTYINDENT_STD, processIndirection(), quote_identifier(), RangeTblEntry::relid, TargetEntry::resjunk, TargetEntry::resno, Query::resultRelation, Query::returningList, rt_fetch, Query::rtable, RTE_RELATION, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, RangeTblEntry::subquery, Query::targetList, RangeTblEntry::values_lists, and deparse_context::wrapColumn.
Referenced by get_query_def().
{ StringInfo buf = context->buf; RangeTblEntry *select_rte = NULL; RangeTblEntry *values_rte = NULL; RangeTblEntry *rte; char *sep; ListCell *values_cell; ListCell *l; List *strippedexprs; /* Insert the WITH clause if given */ get_with_clause(query, context); /* * If it's an INSERT ... SELECT or multi-row VALUES, there will be a * single RTE for the SELECT or VALUES. Plain VALUES has neither. */ foreach(l, query->rtable) { rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) { if (select_rte) elog(ERROR, "too many subquery RTEs in INSERT"); select_rte = rte; } if (rte->rtekind == RTE_VALUES) { if (values_rte) elog(ERROR, "too many values RTEs in INSERT"); values_rte = rte; } } if (select_rte && values_rte) elog(ERROR, "both subquery and values RTEs in INSERT"); /* * Start the query with INSERT INTO relname */ rte = rt_fetch(query->resultRelation, query->rtable); Assert(rte->rtekind == RTE_RELATION); if (PRETTY_INDENT(context)) { context->indentLevel += PRETTYINDENT_STD; appendStringInfoChar(buf, ' '); } appendStringInfo(buf, "INSERT INTO %s ", generate_relation_name(rte->relid, NIL)); /* * Add the insert-column-names list. To handle indirection properly, we * need to look for indirection nodes in the top targetlist (if it's * INSERT ... SELECT or INSERT ... single VALUES), or in the first * expression list of the VALUES RTE (if it's INSERT ... multi VALUES). We * assume that all the expression lists will have similar indirection in * the latter case. */ if (values_rte) values_cell = list_head((List *) linitial(values_rte->values_lists)); else values_cell = NULL; strippedexprs = NIL; sep = ""; if (query->targetList) appendStringInfoChar(buf, '('); foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); if (tle->resjunk) continue; /* ignore junk entries */ appendStringInfoString(buf, sep); sep = ", "; /* * Put out name of target column; look in the catalogs, not at * tle->resname, since resname will fail to track RENAME. */ appendStringInfoString(buf, quote_identifier(get_relid_attribute_name(rte->relid, tle->resno))); /* * Print any indirection needed (subfields or subscripts), and strip * off the top-level nodes representing the indirection assignments. */ if (values_cell) { /* we discard the stripped expression in this case */ processIndirection((Node *) lfirst(values_cell), context, true); values_cell = lnext(values_cell); } else { /* we keep a list of the stripped expressions in this case */ strippedexprs = lappend(strippedexprs, processIndirection((Node *) tle->expr, context, true)); } } if (query->targetList) appendStringInfo(buf, ") "); if (select_rte) { /* Add the SELECT */ get_query_def(select_rte->subquery, buf, NIL, NULL, context->prettyFlags, context->wrapColumn, context->indentLevel); } else if (values_rte) { /* Add the multi-VALUES expression lists */ get_values_def(values_rte->values_lists, context); } else if (strippedexprs) { /* Add the single-VALUES expression list */ appendContextKeyword(context, "VALUES (", -PRETTYINDENT_STD, PRETTYINDENT_STD, 2); get_rule_expr((Node *) strippedexprs, context, false); appendStringInfoChar(buf, ')'); } else { /* No expressions, so it must be DEFAULT VALUES */ appendStringInfo(buf, "DEFAULT VALUES"); } /* Add RETURNING if present */ if (query->returningList) { appendContextKeyword(context, " RETURNING", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); get_target_list(query->returningList, context, NULL); } }
static const char* get_name_for_var_field | ( | Var * | var, | |
int | fieldno, | |||
int | levelsup, | |||
deparse_context * | context | |||
) | [static] |
Definition at line 5425 of file ruleutils.c.
References Alias::aliasname, Assert, tupleDesc::attrs, RowExpr::colnames, RangeTblEntry::ctelevelsup, RangeTblEntry::ctename, CommonTableExpr::ctename, CommonTableExpr::ctequery, deparse_namespace::ctes, elog, RangeTblEntry::eref, ERROR, TargetEntry::expr, exprType(), exprTypmod(), find_param_referent(), get_expr_result_type(), get_rte_attribute_name(), get_tle_by_resno(), GetCTETargetList, deparse_namespace::index_tlist, INDEX_VAR, deparse_namespace::inner_planstate, deparse_namespace::inner_tlist, INNER_VAR, InvalidAttrNumber, IsA, RangeTblEntry::joinaliasvars, lcons(), lfirst, list_copy_tail(), list_delete_first(), list_length(), list_nth(), lookup_rowtype_tupdesc_copy(), deparse_context::namespaces, NameStr, NIL, NULL, deparse_namespace::outer_planstate, deparse_namespace::outer_tlist, OUTER_VAR, pop_ancestor_plan(), pop_child_plan(), push_ancestor_plan(), push_child_plan(), RECORDOID, TargetEntry::resjunk, rt_fetch, deparse_namespace::rtable, RTE_CTE, RTE_FUNCTION, RTE_JOIN, RTE_RELATION, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, set_deparse_for_query(), strVal, RangeTblEntry::subquery, Query::targetList, TYPEFUNC_COMPOSITE, Var::varattno, Var::varlevelsup, Var::varno, and Var::vartype.
Referenced by get_rule_expr().
{ RangeTblEntry *rte; AttrNumber attnum; int netlevelsup; deparse_namespace *dpns; TupleDesc tupleDesc; Node *expr; /* * If it's a RowExpr that was expanded from a whole-row Var, use the * column names attached to it. */ if (IsA(var, RowExpr)) { RowExpr *r = (RowExpr *) var; if (fieldno > 0 && fieldno <= list_length(r->colnames)) return strVal(list_nth(r->colnames, fieldno - 1)); } /* * If it's a Param of type RECORD, try to find what the Param refers to. */ if (IsA(var, Param)) { Param *param = (Param *) var; ListCell *ancestor_cell; expr = find_param_referent(param, context, &dpns, &ancestor_cell); if (expr) { /* Found a match, so recurse to decipher the field name */ deparse_namespace save_dpns; const char *result; push_ancestor_plan(dpns, ancestor_cell, &save_dpns); result = get_name_for_var_field((Var *) expr, fieldno, 0, context); pop_ancestor_plan(dpns, &save_dpns); return result; } } /* * If it's a Var of type RECORD, we have to find what the Var refers to; * if not, we can use get_expr_result_type. If that fails, we try * lookup_rowtype_tupdesc, which will probably fail too, but will ereport * an acceptable message. */ if (!IsA(var, Var) || var->vartype != RECORDOID) { if (get_expr_result_type((Node *) var, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) tupleDesc = lookup_rowtype_tupdesc_copy(exprType((Node *) var), exprTypmod((Node *) var)); Assert(tupleDesc); /* Got the tupdesc, so we can extract the field name */ Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); return NameStr(tupleDesc->attrs[fieldno - 1]->attname); } /* Find appropriate nesting depth */ netlevelsup = var->varlevelsup + levelsup; if (netlevelsup >= list_length(context->namespaces)) elog(ERROR, "bogus varlevelsup: %d offset %d", var->varlevelsup, levelsup); dpns = (deparse_namespace *) list_nth(context->namespaces, netlevelsup); /* * Try to find the relevant RTE in this rtable. In a plan tree, it's * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig * down into the subplans, or INDEX_VAR, which is resolved similarly. */ if (var->varno >= 1 && var->varno <= list_length(dpns->rtable)) { rte = rt_fetch(var->varno, dpns->rtable); attnum = var->varattno; } else if (var->varno == OUTER_VAR && dpns->outer_tlist) { TargetEntry *tle; deparse_namespace save_dpns; const char *result; tle = get_tle_by_resno(dpns->outer_tlist, var->varattno); if (!tle) elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno); Assert(netlevelsup == 0); push_child_plan(dpns, dpns->outer_planstate, &save_dpns); result = get_name_for_var_field((Var *) tle->expr, fieldno, levelsup, context); pop_child_plan(dpns, &save_dpns); return result; } else if (var->varno == INNER_VAR && dpns->inner_tlist) { TargetEntry *tle; deparse_namespace save_dpns; const char *result; tle = get_tle_by_resno(dpns->inner_tlist, var->varattno); if (!tle) elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno); Assert(netlevelsup == 0); push_child_plan(dpns, dpns->inner_planstate, &save_dpns); result = get_name_for_var_field((Var *) tle->expr, fieldno, levelsup, context); pop_child_plan(dpns, &save_dpns); return result; } else if (var->varno == INDEX_VAR && dpns->index_tlist) { TargetEntry *tle; const char *result; tle = get_tle_by_resno(dpns->index_tlist, var->varattno); if (!tle) elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno); Assert(netlevelsup == 0); result = get_name_for_var_field((Var *) tle->expr, fieldno, levelsup, context); return result; } else { elog(ERROR, "bogus varno: %d", var->varno); return NULL; /* keep compiler quiet */ } if (attnum == InvalidAttrNumber) { /* Var is whole-row reference to RTE, so select the right field */ return get_rte_attribute_name(rte, fieldno); } /* * This part has essentially the same logic as the parser's * expandRecordVariable() function, but we are dealing with a different * representation of the input context, and we only need one field name * not a TupleDesc. Also, we need special cases for finding subquery and * CTE subplans when deparsing Plan trees. */ expr = (Node *) var; /* default if we can't drill down */ switch (rte->rtekind) { case RTE_RELATION: case RTE_VALUES: /* * This case should not occur: a column of a table or values list * shouldn't have type RECORD. Fall through and fail (most * likely) at the bottom. */ break; case RTE_SUBQUERY: /* Subselect-in-FROM: examine sub-select's output expr */ { if (rte->subquery) { TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList, attnum); if (ste == NULL || ste->resjunk) elog(ERROR, "subquery %s does not have attribute %d", rte->eref->aliasname, attnum); expr = (Node *) ste->expr; if (IsA(expr, Var)) { /* * Recurse into the sub-select to see what its Var * refers to. We have to build an additional level of * namespace to keep in step with varlevelsup in the * subselect. */ deparse_namespace mydpns; const char *result; set_deparse_for_query(&mydpns, rte->subquery, context->namespaces); context->namespaces = lcons(&mydpns, context->namespaces); result = get_name_for_var_field((Var *) expr, fieldno, 0, context); context->namespaces = list_delete_first(context->namespaces); return result; } /* else fall through to inspect the expression */ } else { /* * We're deparsing a Plan tree so we don't have complete * RTE entries (in particular, rte->subquery is NULL). But * the only place we'd see a Var directly referencing a * SUBQUERY RTE is in a SubqueryScan plan node, and we can * look into the child plan's tlist instead. */ TargetEntry *tle; deparse_namespace save_dpns; const char *result; if (!dpns->inner_planstate) elog(ERROR, "failed to find plan for subquery %s", rte->eref->aliasname); tle = get_tle_by_resno(dpns->inner_tlist, attnum); if (!tle) elog(ERROR, "bogus varattno for subquery var: %d", attnum); Assert(netlevelsup == 0); push_child_plan(dpns, dpns->inner_planstate, &save_dpns); result = get_name_for_var_field((Var *) tle->expr, fieldno, levelsup, context); pop_child_plan(dpns, &save_dpns); return result; } } break; case RTE_JOIN: /* Join RTE --- recursively inspect the alias variable */ if (rte->joinaliasvars == NIL) elog(ERROR, "cannot decompile join alias var in plan tree"); Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1); if (IsA(expr, Var)) return get_name_for_var_field((Var *) expr, fieldno, var->varlevelsup + levelsup, context); /* else fall through to inspect the expression */ break; case RTE_FUNCTION: /* * We couldn't get here unless a function is declared with one of * its result columns as RECORD, which is not allowed. */ break; case RTE_CTE: /* CTE reference: examine subquery's output expr */ { CommonTableExpr *cte = NULL; Index ctelevelsup; ListCell *lc; /* * Try to find the referenced CTE using the namespace stack. */ ctelevelsup = rte->ctelevelsup + netlevelsup; if (ctelevelsup >= list_length(context->namespaces)) lc = NULL; else { deparse_namespace *ctedpns; ctedpns = (deparse_namespace *) list_nth(context->namespaces, ctelevelsup); foreach(lc, ctedpns->ctes) { cte = (CommonTableExpr *) lfirst(lc); if (strcmp(cte->ctename, rte->ctename) == 0) break; } } if (lc != NULL) { Query *ctequery = (Query *) cte->ctequery; TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte), attnum); if (ste == NULL || ste->resjunk) elog(ERROR, "subquery %s does not have attribute %d", rte->eref->aliasname, attnum); expr = (Node *) ste->expr; if (IsA(expr, Var)) { /* * Recurse into the CTE to see what its Var refers to. * We have to build an additional level of namespace * to keep in step with varlevelsup in the CTE. * Furthermore it could be an outer CTE, so we may * have to delete some levels of namespace. */ List *save_nslist = context->namespaces; List *new_nslist; deparse_namespace mydpns; const char *result; set_deparse_for_query(&mydpns, ctequery, context->namespaces); new_nslist = list_copy_tail(context->namespaces, ctelevelsup); context->namespaces = lcons(&mydpns, new_nslist); result = get_name_for_var_field((Var *) expr, fieldno, 0, context); context->namespaces = save_nslist; return result; } /* else fall through to inspect the expression */ } else { /* * We're deparsing a Plan tree so we don't have a CTE * list. But the only place we'd see a Var directly * referencing a CTE RTE is in a CteScan plan node, and we * can look into the subplan's tlist instead. */ TargetEntry *tle; deparse_namespace save_dpns; const char *result; if (!dpns->inner_planstate) elog(ERROR, "failed to find plan for CTE %s", rte->eref->aliasname); tle = get_tle_by_resno(dpns->inner_tlist, attnum); if (!tle) elog(ERROR, "bogus varattno for subquery var: %d", attnum); Assert(netlevelsup == 0); push_child_plan(dpns, dpns->inner_planstate, &save_dpns); result = get_name_for_var_field((Var *) tle->expr, fieldno, levelsup, context); pop_child_plan(dpns, &save_dpns); return result; } } break; } /* * We now have an expression we can't expand any more, so see if * get_expr_result_type() can do anything with it. If not, pass to * lookup_rowtype_tupdesc() which will probably fail, but will give an * appropriate error message while failing. */ if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr), exprTypmod(expr)); Assert(tupleDesc); /* Got the tupdesc, so we can extract the field name */ Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); return NameStr(tupleDesc->attrs[fieldno - 1]->attname); }
static void get_opclass_name | ( | Oid | opclass, | |
Oid | actual_datatype, | |||
StringInfo | buf | |||
) | [static] |
Definition at line 8231 of file ruleutils.c.
References appendStringInfo(), CLAOID, elog, ERROR, get_namespace_name(), GetDefaultOpClass(), GETSTRUCT, HeapTupleIsValid, NameStr, ObjectIdGetDatum, OidIsValid, OpclassIsVisible(), quote_identifier(), ReleaseSysCache(), and SearchSysCache1.
Referenced by pg_get_indexdef_worker().
{ HeapTuple ht_opc; Form_pg_opclass opcrec; char *opcname; char *nspname; ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass)); if (!HeapTupleIsValid(ht_opc)) elog(ERROR, "cache lookup failed for opclass %u", opclass); opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc); if (!OidIsValid(actual_datatype) || GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass) { /* Okay, we need the opclass name. Do we need to qualify it? */ opcname = NameStr(opcrec->opcname); if (OpclassIsVisible(opclass)) appendStringInfo(buf, " %s", quote_identifier(opcname)); else { nspname = get_namespace_name(opcrec->opcnamespace); appendStringInfo(buf, " %s.%s", quote_identifier(nspname), quote_identifier(opcname)); } } ReleaseSysCache(ht_opc); }
static void get_oper_expr | ( | OpExpr * | expr, | |
deparse_context * | context | |||
) | [static] |
Definition at line 7234 of file ruleutils.c.
References appendStringInfo(), appendStringInfoChar(), arg, OpExpr::args, deparse_context::buf, buf, elog, ERROR, exprType(), generate_operator_name(), get_rule_expr_paren(), GETSTRUCT, HeapTupleIsValid, InvalidOid, linitial, list_length(), lsecond, ObjectIdGetDatum, OPEROID, OpExpr::opno, PRETTY_PAREN, ReleaseSysCache(), and SearchSysCache1.
Referenced by get_rule_expr().
{ StringInfo buf = context->buf; Oid opno = expr->opno; List *args = expr->args; if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); if (list_length(args) == 2) { /* binary operator */ Node *arg1 = (Node *) linitial(args); Node *arg2 = (Node *) lsecond(args); get_rule_expr_paren(arg1, context, true, (Node *) expr); appendStringInfo(buf, " %s ", generate_operator_name(opno, exprType(arg1), exprType(arg2))); get_rule_expr_paren(arg2, context, true, (Node *) expr); } else { /* unary operator --- but which side? */ Node *arg = (Node *) linitial(args); HeapTuple tp; Form_pg_operator optup; tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for operator %u", opno); optup = (Form_pg_operator) GETSTRUCT(tp); switch (optup->oprkind) { case 'l': appendStringInfo(buf, "%s ", generate_operator_name(opno, InvalidOid, exprType(arg))); get_rule_expr_paren(arg, context, true, (Node *) expr); break; case 'r': get_rule_expr_paren(arg, context, true, (Node *) expr); appendStringInfo(buf, " %s", generate_operator_name(opno, exprType(arg), InvalidOid)); break; default: elog(ERROR, "bogus oprkind: %d", optup->oprkind); } ReleaseSysCache(tp); } if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); }
static void get_parameter | ( | Param * | param, | |
deparse_context * | context | |||
) | [static] |
Definition at line 5923 of file ruleutils.c.
References appendStringInfo(), appendStringInfoChar(), deparse_context::buf, find_param_referent(), get_rule_expr(), IsA, Param::paramid, pop_ancestor_plan(), push_ancestor_plan(), and deparse_context::varprefix.
Referenced by get_rule_expr().
{ Node *expr; deparse_namespace *dpns; ListCell *ancestor_cell; /* * If it's a PARAM_EXEC parameter, try to locate the expression from which * the parameter was computed. Note that failing to find a referent isn't * an error, since the Param might well be a subplan output rather than an * input. */ expr = find_param_referent(param, context, &dpns, &ancestor_cell); if (expr) { /* Found a match, so print it */ deparse_namespace save_dpns; bool save_varprefix; bool need_paren; /* Switch attention to the ancestor plan node */ push_ancestor_plan(dpns, ancestor_cell, &save_dpns); /* * Force prefixing of Vars, since they won't belong to the relation * being scanned in the original plan node. */ save_varprefix = context->varprefix; context->varprefix = true; /* * A Param's expansion is typically a Var, Aggref, or upper-level * Param, which wouldn't need extra parentheses. Otherwise, insert * parens to ensure the expression looks atomic. */ need_paren = !(IsA(expr, Var) || IsA(expr, Aggref) || IsA(expr, Param)); if (need_paren) appendStringInfoChar(context->buf, '('); get_rule_expr(expr, context, false); if (need_paren) appendStringInfoChar(context->buf, ')'); context->varprefix = save_varprefix; pop_ancestor_plan(dpns, &save_dpns); return; } /* * Not PARAM_EXEC, or couldn't find referent: just print $N. */ appendStringInfo(context->buf, "$%d", param->paramid); }
static void get_query_def | ( | Query * | query, | |
StringInfo | buf, | |||
List * | parentnamespace, | |||
TupleDesc | resultDesc, | |||
int | prettyFlags, | |||
int | wrapColumn, | |||
int | startIndent | |||
) | [static] |
Definition at line 3972 of file ruleutils.c.
References AcquireRewriteLocks(), appendStringInfo(), deparse_context::buf, CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_SELECT, CMD_UPDATE, CMD_UTILITY, Query::commandType, elog, ERROR, get_delete_query_def(), get_insert_query_def(), get_select_query_def(), get_update_query_def(), get_utility_query_def(), deparse_context::indentLevel, lcons(), list_copy(), list_length(), deparse_context::namespaces, NIL, deparse_context::prettyFlags, Query::rtable, set_deparse_for_query(), deparse_context::varprefix, deparse_context::windowClause, deparse_context::windowTList, 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(), and make_viewdef().
{ deparse_context context; deparse_namespace dpns; /* * Before we begin to examine the query, acquire locks on referenced * relations, and fix up deleted columns in JOIN RTEs. This ensures * consistent results. Note we assume it's OK to scribble on the passed * querytree! */ AcquireRewriteLocks(query, false); context.buf = buf; context.namespaces = lcons(&dpns, list_copy(parentnamespace)); context.windowClause = NIL; context.windowTList = NIL; context.varprefix = (parentnamespace != NIL || list_length(query->rtable) != 1); context.prettyFlags = prettyFlags; context.wrapColumn = wrapColumn; context.indentLevel = startIndent; set_deparse_for_query(&dpns, query, parentnamespace); switch (query->commandType) { case CMD_SELECT: get_select_query_def(query, &context, resultDesc); break; case CMD_UPDATE: get_update_query_def(query, &context); break; case CMD_INSERT: get_insert_query_def(query, &context); break; case CMD_DELETE: get_delete_query_def(query, &context); break; case CMD_NOTHING: appendStringInfo(buf, "NOTHING"); break; case CMD_UTILITY: get_utility_query_def(query, &context); break; default: elog(ERROR, "unrecognized query command type: %d", query->commandType); break; } }
static char * get_relation_name | ( | Oid | relid | ) | [static] |
Definition at line 8465 of file ruleutils.c.
References elog, ERROR, and get_rel_name().
Referenced by get_from_clause_item(), pg_get_constraintdef_worker(), and pg_get_indexdef_worker().
{ char *relname = get_rel_name(relid); if (!relname) elog(ERROR, "cache lookup failed for relation %u", relid); return relname; }
static char * get_rtable_name | ( | int | rtindex, | |
deparse_context * | context | |||
) | [static] |
Definition at line 3542 of file ruleutils.c.
References Assert, linitial, list_length(), list_nth(), deparse_context::namespaces, and deparse_namespace::rtable_names.
Referenced by get_from_clause_item(), and get_select_query_def().
{ deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces); Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names)); return (char *) list_nth(dpns->rtable_names, rtindex - 1); }
static void get_rule_expr | ( | Node * | node, | |
deparse_context * | context, | |||
bool | showimplicit | |||
) | [static] |
Definition at line 6291 of file ruleutils.c.
References AND_EXPR, appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), CoerceToDomain::arg, BooleanTest::arg, NullTest::arg, CaseExpr::arg, CollateExpr::arg, ConvertRowtypeExpr::arg, ArrayCoerceExpr::arg, CoerceViaIO::arg, RelabelType::arg, FieldSelect::arg, arg, NamedArgExpr::arg, XmlExpr::arg_names, XmlExpr::args, MinMaxExpr::args, CoalesceExpr::args, RowExpr::args, CaseExpr::args, BoolExpr::args, ScalarArrayOpExpr::args, OpExpr::args, ArrayExpr::array_typeid, Assert, tupleDesc::attrs, BoolExpr::boolop, BooleanTest::booltesttype, deparse_context::buf, buf, COERCE_EXPLICIT_CAST, COERCE_IMPLICIT_CAST, ArrayCoerceExpr::coerceformat, CoerceViaIO::coerceformat, CoerceToDomain::coercionformat, CollateExpr::collOid, Const::constisnull, Const::constvalue, convert(), ConvertRowtypeExpr::convertformat, CurrentOfExpr::cursor_name, CurrentOfExpr::cursor_param, DatumGetBool, DatumGetInt32, CaseExpr::defresult, ArrayExpr::elements, elog, ERROR, CaseWhen::expr, exprType(), FieldSelect::fieldnum, forboth, format_type_with_typemod(), generate_collation_name(), generate_operator_name(), get_agg_expr(), get_base_element_type(), get_coercion_expr(), get_const_expr(), get_func_expr(), get_name_for_var_field(), get_oper_expr(), get_parameter(), get_rule_expr_paren(), get_sublink_expr(), get_variable(), get_windowfunc_expr(), i, 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, RowCompareExpr::largs, lfirst, linitial, linitial_oid, list_head(), list_length(), lnext, lookup_rowtype_tupdesc(), lsecond, lthird, map_xml_name_to_sql_identifier(), XmlExpr::name, NamedArgExpr::name, XmlExpr::named_args, tupleDesc::natts, FieldStore::newvals, NIL, nodeTag, NOT_EXPR, NULL, NullTest::nulltesttype, XmlExpr::op, MinMaxExpr::op, ScalarArrayOpExpr::opno, RowCompareExpr::opnos, OR_EXPR, SubPlan::plan_name, PRETTY_INDENT, PRETTY_PAREN, PRETTYINDENT_VAR, printSubscripts(), processIndirection(), quote_identifier(), RowCompareExpr::rargs, RECORDOID, ArrayRef::refassgnexpr, ArrayRef::refexpr, RelabelType::relabelformat, ReleaseTupleDesc, CaseWhen::result, CoerceToDomain::resulttype, ConvertRowtypeExpr::resulttype, ArrayCoerceExpr::resulttype, CoerceViaIO::resulttype, RelabelType::resulttype, CoerceToDomain::resulttypmod, ArrayCoerceExpr::resulttypmod, RelabelType::resulttypmod, RowExpr::row_format, RowExpr::row_typeid, splan, strip_implicit_coercions(), strVal, AlternativeSubPlan::subplans, T_Aggref, T_AlternativeSubPlan, T_ArrayCoerceExpr, T_ArrayExpr, T_ArrayRef, T_BooleanTest, T_BoolExpr, T_CaseExpr, T_CaseTestExpr, T_CoalesceExpr, T_CoerceToDomain, T_CoerceToDomainValue, T_CoerceViaIO, T_CollateExpr, T_Const, T_ConvertRowtypeExpr, T_CurrentOfExpr, T_DistinctExpr, T_FieldSelect, T_FieldStore, T_FuncExpr, T_List, T_MinMaxExpr, T_NamedArgExpr, T_NullIfExpr, T_NullTest, T_OpExpr, T_Param, T_RelabelType, T_RowCompareExpr, T_RowExpr, T_ScalarArrayOpExpr, T_SetToDefault, T_SubLink, T_SubPlan, T_Var, T_WindowFunc, T_XmlExpr, XmlExpr::type, XmlExpr::typmod, SubPlan::useHashTable, ScalarArrayOpExpr::useOr, XML_STANDALONE_NO, XML_STANDALONE_NO_VALUE, XML_STANDALONE_YES, XmlExpr::xmloption, and XMLOPTION_DOCUMENT.
Referenced by deparse_expression_pretty(), get_agg_expr(), get_basic_select_query(), get_delete_query_def(), get_from_clause_item(), get_func_expr(), get_insert_query_def(), get_parameter(), get_rule_expr_paren(), get_rule_sortgroupclause(), get_rule_windowspec(), get_select_query_def(), get_sublink_expr(), get_target_list(), get_update_query_def(), get_values_def(), get_variable(), get_windowfunc_expr(), make_ruledef(), pg_get_triggerdef_worker(), and printSubscripts().
{ StringInfo buf = context->buf; if (node == NULL) return; /* * Each level of get_rule_expr must emit an indivisible term * (parenthesized if necessary) to ensure result is reparsed into the same * expression tree. The only exception is that when the input is a List, * we emit the component items comma-separated with no surrounding * decoration; this is convenient for most callers. */ switch (nodeTag(node)) { case T_Var: (void) get_variable((Var *) node, 0, false, context); break; case T_Const: get_const_expr((Const *) node, context, 0); break; case T_Param: get_parameter((Param *) node, context); break; case T_Aggref: get_agg_expr((Aggref *) node, context); break; case T_WindowFunc: get_windowfunc_expr((WindowFunc *) node, context); break; case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; bool need_parens; /* * If the argument is a CaseTestExpr, we must be inside a * FieldStore, ie, we are assigning to an element of an array * within a composite column. Since we already punted on * displaying the FieldStore's target information, just punt * here too, and display only the assignment source * expression. */ if (IsA(aref->refexpr, CaseTestExpr)) { Assert(aref->refassgnexpr); get_rule_expr((Node *) aref->refassgnexpr, context, showimplicit); break; } /* * Parenthesize the argument unless it's a simple Var or a * FieldSelect. (In particular, if it's another ArrayRef, we * *must* parenthesize to avoid confusion.) */ need_parens = !IsA(aref->refexpr, Var) && !IsA(aref->refexpr, FieldSelect); if (need_parens) appendStringInfoChar(buf, '('); get_rule_expr((Node *) aref->refexpr, context, showimplicit); if (need_parens) appendStringInfoChar(buf, ')'); /* * If there's a refassgnexpr, we want to print the node in the * format "array[subscripts] := refassgnexpr". This is not * legal SQL, so decompilation of INSERT or UPDATE statements * should always use processIndirection as part of the * statement-level syntax. We should only see this when * EXPLAIN tries to print the targetlist of a plan resulting * from such a statement. */ if (aref->refassgnexpr) { Node *refassgnexpr; /* * Use processIndirection to print this node's subscripts * as well as any additional field selections or * subscripting in immediate descendants. It returns the * RHS expr that is actually being "assigned". */ refassgnexpr = processIndirection(node, context, true); appendStringInfoString(buf, " := "); get_rule_expr(refassgnexpr, context, showimplicit); } else { /* Just an ordinary array fetch, so print subscripts */ printSubscripts(aref, context); } } break; case T_FuncExpr: get_func_expr((FuncExpr *) node, context, showimplicit); break; case T_NamedArgExpr: { NamedArgExpr *na = (NamedArgExpr *) node; appendStringInfo(buf, "%s := ", quote_identifier(na->name)); get_rule_expr((Node *) na->arg, context, showimplicit); } break; case T_OpExpr: get_oper_expr((OpExpr *) node, context); break; case T_DistinctExpr: { DistinctExpr *expr = (DistinctExpr *) node; List *args = expr->args; Node *arg1 = (Node *) linitial(args); Node *arg2 = (Node *) lsecond(args); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); get_rule_expr_paren(arg1, context, true, node); appendStringInfo(buf, " IS DISTINCT FROM "); get_rule_expr_paren(arg2, context, true, node); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); } break; case T_NullIfExpr: { NullIfExpr *nullifexpr = (NullIfExpr *) node; appendStringInfo(buf, "NULLIF("); get_rule_expr((Node *) nullifexpr->args, context, true); appendStringInfoChar(buf, ')'); } break; case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; List *args = expr->args; Node *arg1 = (Node *) linitial(args); Node *arg2 = (Node *) lsecond(args); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); get_rule_expr_paren(arg1, context, true, node); appendStringInfo(buf, " %s %s (", generate_operator_name(expr->opno, exprType(arg1), get_base_element_type(exprType(arg2))), expr->useOr ? "ANY" : "ALL"); get_rule_expr_paren(arg2, context, true, node); appendStringInfoChar(buf, ')'); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); } break; case T_BoolExpr: { BoolExpr *expr = (BoolExpr *) node; Node *first_arg = linitial(expr->args); ListCell *arg = lnext(list_head(expr->args)); switch (expr->boolop) { case AND_EXPR: if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); get_rule_expr_paren(first_arg, context, false, node); while (arg) { appendStringInfo(buf, " AND "); get_rule_expr_paren((Node *) lfirst(arg), context, false, node); arg = lnext(arg); } if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); break; case OR_EXPR: if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); get_rule_expr_paren(first_arg, context, false, node); while (arg) { appendStringInfo(buf, " OR "); get_rule_expr_paren((Node *) lfirst(arg), context, false, node); arg = lnext(arg); } if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); break; case NOT_EXPR: if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); appendStringInfo(buf, "NOT "); get_rule_expr_paren(first_arg, context, false, node); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); break; default: elog(ERROR, "unrecognized boolop: %d", (int) expr->boolop); } } break; case T_SubLink: get_sublink_expr((SubLink *) node, context); break; case T_SubPlan: { SubPlan *subplan = (SubPlan *) node; /* * We cannot see an already-planned subplan in rule deparsing, * only while EXPLAINing a query plan. We don't try to * reconstruct the original SQL, just reference the subplan * that appears elsewhere in EXPLAIN's result. */ if (subplan->useHashTable) appendStringInfo(buf, "(hashed %s)", subplan->plan_name); else appendStringInfo(buf, "(%s)", subplan->plan_name); } break; case T_AlternativeSubPlan: { AlternativeSubPlan *asplan = (AlternativeSubPlan *) node; ListCell *lc; /* As above, this can only happen during EXPLAIN */ appendStringInfo(buf, "(alternatives: "); foreach(lc, asplan->subplans) { SubPlan *splan = (SubPlan *) lfirst(lc); Assert(IsA(splan, SubPlan)); if (splan->useHashTable) appendStringInfo(buf, "hashed %s", splan->plan_name); else appendStringInfo(buf, "%s", splan->plan_name); if (lnext(lc)) appendStringInfo(buf, " or "); } appendStringInfo(buf, ")"); } break; case T_FieldSelect: { FieldSelect *fselect = (FieldSelect *) node; Node *arg = (Node *) fselect->arg; int fno = fselect->fieldnum; const char *fieldname; bool need_parens; /* * Parenthesize the argument unless it's an ArrayRef or * another FieldSelect. Note in particular that it would be * WRONG to not parenthesize a Var argument; simplicity is not * the issue here, having the right number of names is. */ need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect); if (need_parens) appendStringInfoChar(buf, '('); get_rule_expr(arg, context, true); if (need_parens) appendStringInfoChar(buf, ')'); /* * Get and print the field name. */ fieldname = get_name_for_var_field((Var *) arg, fno, 0, context); appendStringInfo(buf, ".%s", quote_identifier(fieldname)); } break; case T_FieldStore: { FieldStore *fstore = (FieldStore *) node; bool need_parens; /* * There is no good way to represent a FieldStore as real SQL, * so decompilation of INSERT or UPDATE statements should * always use processIndirection as part of the * statement-level syntax. We should only get here when * EXPLAIN tries to print the targetlist of a plan resulting * from such a statement. The plan case is even harder than * ordinary rules would be, because the planner tries to * collapse multiple assignments to the same field or subfield * into one FieldStore; so we can see a list of target fields * not just one, and the arguments could be FieldStores * themselves. We don't bother to try to print the target * field names; we just print the source arguments, with a * ROW() around them if there's more than one. This isn't * terribly complete, but it's probably good enough for * EXPLAIN's purposes; especially since anything more would be * either hopelessly confusing or an even poorer * representation of what the plan is actually doing. */ need_parens = (list_length(fstore->newvals) != 1); if (need_parens) appendStringInfoString(buf, "ROW("); get_rule_expr((Node *) fstore->newvals, context, showimplicit); if (need_parens) appendStringInfoChar(buf, ')'); } break; case T_RelabelType: { RelabelType *relabel = (RelabelType *) node; Node *arg = (Node *) relabel->arg; if (relabel->relabelformat == COERCE_IMPLICIT_CAST && !showimplicit) { /* don't show the implicit cast */ get_rule_expr_paren(arg, context, false, node); } else { get_coercion_expr(arg, context, relabel->resulttype, relabel->resulttypmod, node); } } break; case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; Node *arg = (Node *) iocoerce->arg; if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST && !showimplicit) { /* don't show the implicit cast */ get_rule_expr_paren(arg, context, false, node); } else { get_coercion_expr(arg, context, iocoerce->resulttype, -1, node); } } break; case T_ArrayCoerceExpr: { ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; Node *arg = (Node *) acoerce->arg; if (acoerce->coerceformat == COERCE_IMPLICIT_CAST && !showimplicit) { /* don't show the implicit cast */ get_rule_expr_paren(arg, context, false, node); } else { get_coercion_expr(arg, context, acoerce->resulttype, acoerce->resulttypmod, node); } } break; case T_ConvertRowtypeExpr: { ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node; Node *arg = (Node *) convert->arg; if (convert->convertformat == COERCE_IMPLICIT_CAST && !showimplicit) { /* don't show the implicit cast */ get_rule_expr_paren(arg, context, false, node); } else { get_coercion_expr(arg, context, convert->resulttype, -1, node); } } break; case T_CollateExpr: { CollateExpr *collate = (CollateExpr *) node; Node *arg = (Node *) collate->arg; if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); get_rule_expr_paren(arg, context, showimplicit, node); appendStringInfo(buf, " COLLATE %s", generate_collation_name(collate->collOid)); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); } break; case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; ListCell *temp; appendContextKeyword(context, "CASE", 0, PRETTYINDENT_VAR, 0); if (caseexpr->arg) { appendStringInfoChar(buf, ' '); get_rule_expr((Node *) caseexpr->arg, context, true); } foreach(temp, caseexpr->args) { CaseWhen *when = (CaseWhen *) lfirst(temp); Node *w = (Node *) when->expr; if (caseexpr->arg) { /* * The parser should have produced WHEN clauses of the * form "CaseTestExpr = RHS", possibly with an * implicit coercion inserted above the CaseTestExpr. * For accurate decompilation of rules it's essential * that we show just the RHS. However in an * expression that's been through the optimizer, the * WHEN clause could be almost anything (since the * equality operator could have been expanded into an * inline function). If we don't recognize the form * of the WHEN clause, just punt and display it as-is. */ if (IsA(w, OpExpr)) { List *args = ((OpExpr *) w)->args; if (list_length(args) == 2 && IsA(strip_implicit_coercions(linitial(args)), CaseTestExpr)) w = (Node *) lsecond(args); } } if (!PRETTY_INDENT(context)) appendStringInfoChar(buf, ' '); appendContextKeyword(context, "WHEN ", 0, 0, 0); get_rule_expr(w, context, false); appendStringInfo(buf, " THEN "); get_rule_expr((Node *) when->result, context, true); } if (!PRETTY_INDENT(context)) appendStringInfoChar(buf, ' '); appendContextKeyword(context, "ELSE ", 0, 0, 0); get_rule_expr((Node *) caseexpr->defresult, context, true); if (!PRETTY_INDENT(context)) appendStringInfoChar(buf, ' '); appendContextKeyword(context, "END", -PRETTYINDENT_VAR, 0, 0); } break; case T_CaseTestExpr: { /* * Normally we should never get here, since for expressions * that can contain this node type we attempt to avoid * recursing to it. But in an optimized expression we might * be unable to avoid that (see comments for CaseExpr). If we * do see one, print it as CASE_TEST_EXPR. */ appendStringInfo(buf, "CASE_TEST_EXPR"); } break; case T_ArrayExpr: { ArrayExpr *arrayexpr = (ArrayExpr *) node; appendStringInfo(buf, "ARRAY["); get_rule_expr((Node *) arrayexpr->elements, context, true); appendStringInfoChar(buf, ']'); /* * If the array isn't empty, we assume its elements are * coerced to the desired type. If it's empty, though, we * need an explicit coercion to the array type. */ if (arrayexpr->elements == NIL) appendStringInfo(buf, "::%s", format_type_with_typemod(arrayexpr->array_typeid, -1)); } break; case T_RowExpr: { RowExpr *rowexpr = (RowExpr *) node; TupleDesc tupdesc = NULL; ListCell *arg; int i; char *sep; /* * If it's a named type and not RECORD, we may have to skip * dropped columns and/or claim there are NULLs for added * columns. */ if (rowexpr->row_typeid != RECORDOID) { tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); Assert(list_length(rowexpr->args) <= tupdesc->natts); } /* * SQL99 allows "ROW" to be omitted when there is more than * one column, but for simplicity we always print it. */ appendStringInfo(buf, "ROW("); sep = ""; i = 0; foreach(arg, rowexpr->args) { Node *e = (Node *) lfirst(arg); if (tupdesc == NULL || !tupdesc->attrs[i]->attisdropped) { appendStringInfoString(buf, sep); get_rule_expr(e, context, true); sep = ", "; } i++; } if (tupdesc != NULL) { while (i < tupdesc->natts) { if (!tupdesc->attrs[i]->attisdropped) { appendStringInfoString(buf, sep); appendStringInfo(buf, "NULL"); sep = ", "; } i++; } ReleaseTupleDesc(tupdesc); } appendStringInfo(buf, ")"); if (rowexpr->row_format == COERCE_EXPLICIT_CAST) appendStringInfo(buf, "::%s", format_type_with_typemod(rowexpr->row_typeid, -1)); } break; case T_RowCompareExpr: { RowCompareExpr *rcexpr = (RowCompareExpr *) node; ListCell *arg; char *sep; /* * SQL99 allows "ROW" to be omitted when there is more than * one column, but for simplicity we always print it. */ appendStringInfo(buf, "(ROW("); sep = ""; foreach(arg, rcexpr->largs) { Node *e = (Node *) lfirst(arg); appendStringInfoString(buf, sep); get_rule_expr(e, context, true); sep = ", "; } /* * We assume that the name of the first-column operator will * do for all the rest too. This is definitely open to * failure, eg if some but not all operators were renamed * since the construct was parsed, but there seems no way to * be perfect. */ appendStringInfo(buf, ") %s ROW(", generate_operator_name(linitial_oid(rcexpr->opnos), exprType(linitial(rcexpr->largs)), exprType(linitial(rcexpr->rargs)))); sep = ""; foreach(arg, rcexpr->rargs) { Node *e = (Node *) lfirst(arg); appendStringInfoString(buf, sep); get_rule_expr(e, context, true); sep = ", "; } appendStringInfo(buf, "))"); } break; case T_CoalesceExpr: { CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; appendStringInfo(buf, "COALESCE("); get_rule_expr((Node *) coalesceexpr->args, context, true); appendStringInfoChar(buf, ')'); } break; case T_MinMaxExpr: { MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; switch (minmaxexpr->op) { case IS_GREATEST: appendStringInfo(buf, "GREATEST("); break; case IS_LEAST: appendStringInfo(buf, "LEAST("); break; } get_rule_expr((Node *) minmaxexpr->args, context, true); appendStringInfoChar(buf, ')'); } break; case T_XmlExpr: { XmlExpr *xexpr = (XmlExpr *) node; bool needcomma = false; ListCell *arg; ListCell *narg; Const *con; switch (xexpr->op) { case IS_XMLCONCAT: appendStringInfoString(buf, "XMLCONCAT("); break; case IS_XMLELEMENT: appendStringInfoString(buf, "XMLELEMENT("); break; case IS_XMLFOREST: appendStringInfoString(buf, "XMLFOREST("); break; case IS_XMLPARSE: appendStringInfoString(buf, "XMLPARSE("); break; case IS_XMLPI: appendStringInfoString(buf, "XMLPI("); break; case IS_XMLROOT: appendStringInfoString(buf, "XMLROOT("); break; case IS_XMLSERIALIZE: appendStringInfoString(buf, "XMLSERIALIZE("); break; case IS_DOCUMENT: break; } if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE) { if (xexpr->xmloption == XMLOPTION_DOCUMENT) appendStringInfoString(buf, "DOCUMENT "); else appendStringInfoString(buf, "CONTENT "); } if (xexpr->name) { appendStringInfo(buf, "NAME %s", quote_identifier(map_xml_name_to_sql_identifier(xexpr->name))); needcomma = true; } if (xexpr->named_args) { if (xexpr->op != IS_XMLFOREST) { if (needcomma) appendStringInfoString(buf, ", "); appendStringInfoString(buf, "XMLATTRIBUTES("); needcomma = false; } forboth(arg, xexpr->named_args, narg, xexpr->arg_names) { Node *e = (Node *) lfirst(arg); char *argname = strVal(lfirst(narg)); if (needcomma) appendStringInfoString(buf, ", "); get_rule_expr((Node *) e, context, true); appendStringInfo(buf, " AS %s", quote_identifier(map_xml_name_to_sql_identifier(argname))); needcomma = true; } if (xexpr->op != IS_XMLFOREST) appendStringInfoChar(buf, ')'); } if (xexpr->args) { if (needcomma) appendStringInfoString(buf, ", "); switch (xexpr->op) { case IS_XMLCONCAT: case IS_XMLELEMENT: case IS_XMLFOREST: case IS_XMLPI: case IS_XMLSERIALIZE: /* no extra decoration needed */ get_rule_expr((Node *) xexpr->args, context, true); break; case IS_XMLPARSE: Assert(list_length(xexpr->args) == 2); get_rule_expr((Node *) linitial(xexpr->args), context, true); con = (Const *) lsecond(xexpr->args); Assert(IsA(con, Const)); Assert(!con->constisnull); if (DatumGetBool(con->constvalue)) appendStringInfoString(buf, " PRESERVE WHITESPACE"); else appendStringInfoString(buf, " STRIP WHITESPACE"); break; case IS_XMLROOT: Assert(list_length(xexpr->args) == 3); get_rule_expr((Node *) linitial(xexpr->args), context, true); appendStringInfoString(buf, ", VERSION "); con = (Const *) lsecond(xexpr->args); if (IsA(con, Const) && con->constisnull) appendStringInfoString(buf, "NO VALUE"); else get_rule_expr((Node *) con, context, false); con = (Const *) lthird(xexpr->args); Assert(IsA(con, Const)); if (con->constisnull) /* suppress STANDALONE NO VALUE */ ; else { switch (DatumGetInt32(con->constvalue)) { case XML_STANDALONE_YES: appendStringInfoString(buf, ", STANDALONE YES"); break; case XML_STANDALONE_NO: appendStringInfoString(buf, ", STANDALONE NO"); break; case XML_STANDALONE_NO_VALUE: appendStringInfoString(buf, ", STANDALONE NO VALUE"); break; default: break; } } break; case IS_DOCUMENT: get_rule_expr_paren((Node *) xexpr->args, context, false, node); break; } } if (xexpr->op == IS_XMLSERIALIZE) appendStringInfo(buf, " AS %s", format_type_with_typemod(xexpr->type, xexpr->typmod)); if (xexpr->op == IS_DOCUMENT) appendStringInfoString(buf, " IS DOCUMENT"); else appendStringInfoChar(buf, ')'); } break; case T_NullTest: { NullTest *ntest = (NullTest *) node; if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); get_rule_expr_paren((Node *) ntest->arg, context, true, node); switch (ntest->nulltesttype) { case IS_NULL: appendStringInfo(buf, " IS NULL"); break; case IS_NOT_NULL: appendStringInfo(buf, " IS NOT NULL"); break; default: elog(ERROR, "unrecognized nulltesttype: %d", (int) ntest->nulltesttype); } if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); } break; case T_BooleanTest: { BooleanTest *btest = (BooleanTest *) node; if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); get_rule_expr_paren((Node *) btest->arg, context, false, node); switch (btest->booltesttype) { case IS_TRUE: appendStringInfo(buf, " IS TRUE"); break; case IS_NOT_TRUE: appendStringInfo(buf, " IS NOT TRUE"); break; case IS_FALSE: appendStringInfo(buf, " IS FALSE"); break; case IS_NOT_FALSE: appendStringInfo(buf, " IS NOT FALSE"); break; case IS_UNKNOWN: appendStringInfo(buf, " IS UNKNOWN"); break; case IS_NOT_UNKNOWN: appendStringInfo(buf, " IS NOT UNKNOWN"); break; default: elog(ERROR, "unrecognized booltesttype: %d", (int) btest->booltesttype); } if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); } break; case T_CoerceToDomain: { CoerceToDomain *ctest = (CoerceToDomain *) node; Node *arg = (Node *) ctest->arg; if (ctest->coercionformat == COERCE_IMPLICIT_CAST && !showimplicit) { /* don't show the implicit cast */ get_rule_expr(arg, context, false); } else { get_coercion_expr(arg, context, ctest->resulttype, ctest->resulttypmod, node); } } break; case T_CoerceToDomainValue: appendStringInfo(buf, "VALUE"); break; case T_SetToDefault: appendStringInfo(buf, "DEFAULT"); break; case T_CurrentOfExpr: { CurrentOfExpr *cexpr = (CurrentOfExpr *) node; if (cexpr->cursor_name) appendStringInfo(buf, "CURRENT OF %s", quote_identifier(cexpr->cursor_name)); else appendStringInfo(buf, "CURRENT OF $%d", cexpr->cursor_param); } break; case T_List: { char *sep; ListCell *l; sep = ""; foreach(l, (List *) node) { appendStringInfoString(buf, sep); get_rule_expr((Node *) lfirst(l), context, showimplicit); sep = ", "; } } break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); break; } }
static void get_rule_expr_paren | ( | Node * | node, | |
deparse_context * | context, | |||
bool | showimplicit, | |||
Node * | parentNode | |||
) | [static] |
Definition at line 6259 of file ruleutils.c.
References appendStringInfoChar(), deparse_context::buf, get_rule_expr(), isSimpleNode(), PRETTY_PAREN, and deparse_context::prettyFlags.
Referenced by get_coercion_expr(), get_func_expr(), get_oper_expr(), and get_rule_expr().
{ bool need_paren; need_paren = PRETTY_PAREN(context) && !isSimpleNode(node, parentNode, context->prettyFlags); if (need_paren) appendStringInfoChar(context->buf, '('); get_rule_expr(node, context, showimplicit); if (need_paren) appendStringInfoChar(context->buf, ')'); }
static void get_rule_orderby | ( | List * | orderList, | |
List * | targetList, | |||
bool | force_colno, | |||
deparse_context * | context | |||
) | [static] |
Definition at line 4663 of file ruleutils.c.
References appendStringInfo(), appendStringInfoString(), deparse_context::buf, buf, exprType(), generate_operator_name(), get_rule_sortgroupclause(), TypeCacheEntry::gt_opr, lfirst, lookup_type_cache(), TypeCacheEntry::lt_opr, SortGroupClause::nulls_first, SortGroupClause::sortop, TYPECACHE_GT_OPR, and TYPECACHE_LT_OPR.
Referenced by get_agg_expr(), get_rule_windowspec(), and get_select_query_def().
{ StringInfo buf = context->buf; const char *sep; ListCell *l; sep = ""; foreach(l, orderList) { SortGroupClause *srt = (SortGroupClause *) lfirst(l); Node *sortexpr; Oid sortcoltype; TypeCacheEntry *typentry; appendStringInfoString(buf, sep); sortexpr = get_rule_sortgroupclause(srt, targetList, force_colno, context); sortcoltype = exprType(sortexpr); /* See whether operator is default < or > for datatype */ typentry = lookup_type_cache(sortcoltype, TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); if (srt->sortop == typentry->lt_opr) { /* ASC is default, so emit nothing for it */ if (srt->nulls_first) appendStringInfo(buf, " NULLS FIRST"); } else if (srt->sortop == typentry->gt_opr) { appendStringInfo(buf, " DESC"); /* DESC defaults to NULLS FIRST */ if (!srt->nulls_first) appendStringInfo(buf, " NULLS LAST"); } else { appendStringInfo(buf, " USING %s", generate_operator_name(srt->sortop, sortcoltype, sortcoltype)); /* be specific to eliminate ambiguity */ if (srt->nulls_first) appendStringInfo(buf, " NULLS FIRST"); else appendStringInfo(buf, " NULLS LAST"); } sep = ", "; } }
static Node * get_rule_sortgroupclause | ( | SortGroupClause * | srt, | |
List * | tlist, | |||
bool | force_colno, | |||
deparse_context * | context | |||
) | [static] |
Definition at line 4628 of file ruleutils.c.
References appendStringInfo(), Assert, deparse_context::buf, buf, TargetEntry::expr, get_const_expr(), get_rule_expr(), get_sortgroupclause_tle(), IsA, TargetEntry::resjunk, and TargetEntry::resno.
Referenced by get_basic_select_query(), get_rule_orderby(), and get_rule_windowspec().
{ StringInfo buf = context->buf; TargetEntry *tle; Node *expr; tle = get_sortgroupclause_tle(srt, tlist); expr = (Node *) tle->expr; /* * Use column-number form if requested by caller. Otherwise, if * expression is a constant, force it to be dumped with an explicit cast * as decoration --- this is because a simple integer constant is * ambiguous (and will be misinterpreted by findTargetlistEntry()) if we * dump it without any decoration. Otherwise, just dump the expression * normally. */ if (force_colno) { Assert(!tle->resjunk); appendStringInfo(buf, "%d", tle->resno); } else if (expr && IsA(expr, Const)) get_const_expr((Const *) expr, context, 1); else get_rule_expr(expr, context, true); return expr; }
static void get_rule_windowclause | ( | Query * | query, | |
deparse_context * | context | |||
) | [static] |
Definition at line 4721 of file ruleutils.c.
References appendContextKeyword(), appendStringInfo(), appendStringInfoString(), deparse_context::buf, buf, get_rule_windowspec(), lfirst, WindowClause::name, NULL, PRETTYINDENT_STD, quote_identifier(), Query::targetList, and Query::windowClause.
Referenced by get_basic_select_query().
{ StringInfo buf = context->buf; const char *sep; ListCell *l; sep = NULL; foreach(l, query->windowClause) { WindowClause *wc = (WindowClause *) lfirst(l); if (wc->name == NULL) continue; /* ignore anonymous windows */ if (sep == NULL) appendContextKeyword(context, " WINDOW ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); else appendStringInfoString(buf, sep); appendStringInfo(buf, "%s AS ", quote_identifier(wc->name)); get_rule_windowspec(wc, query->targetList, context); sep = ", "; } }
static void get_rule_windowspec | ( | WindowClause * | wc, | |
List * | targetList, | |||
deparse_context * | context | |||
) | [static] |
Definition at line 4753 of file ruleutils.c.
References appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, WindowClause::copiedOrder, WindowClause::endOffset, FRAMEOPTION_BETWEEN, FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_UNBOUNDED_FOLLOWING, FRAMEOPTION_END_VALUE, FRAMEOPTION_END_VALUE_FOLLOWING, FRAMEOPTION_END_VALUE_PRECEDING, FRAMEOPTION_NONDEFAULT, FRAMEOPTION_RANGE, FRAMEOPTION_ROWS, FRAMEOPTION_START_CURRENT_ROW, FRAMEOPTION_START_UNBOUNDED_PRECEDING, FRAMEOPTION_START_VALUE, FRAMEOPTION_START_VALUE_FOLLOWING, FRAMEOPTION_START_VALUE_PRECEDING, WindowClause::frameOptions, get_rule_expr(), get_rule_orderby(), get_rule_sortgroupclause(), StringInfoData::len, lfirst, WindowClause::orderClause, WindowClause::partitionClause, quote_identifier(), WindowClause::refname, and WindowClause::startOffset.
Referenced by get_rule_windowclause(), and get_windowfunc_expr().
{ StringInfo buf = context->buf; bool needspace = false; const char *sep; ListCell *l; appendStringInfoChar(buf, '('); if (wc->refname) { appendStringInfoString(buf, quote_identifier(wc->refname)); needspace = true; } /* partition clauses are always inherited, so only print if no refname */ if (wc->partitionClause && !wc->refname) { if (needspace) appendStringInfoChar(buf, ' '); appendStringInfoString(buf, "PARTITION BY "); sep = ""; foreach(l, wc->partitionClause) { SortGroupClause *grp = (SortGroupClause *) lfirst(l); appendStringInfoString(buf, sep); get_rule_sortgroupclause(grp, targetList, false, context); sep = ", "; } needspace = true; } /* print ordering clause only if not inherited */ if (wc->orderClause && !wc->copiedOrder) { if (needspace) appendStringInfoChar(buf, ' '); appendStringInfoString(buf, "ORDER BY "); get_rule_orderby(wc->orderClause, targetList, false, context); needspace = true; } /* framing clause is never inherited, so print unless it's default */ if (wc->frameOptions & FRAMEOPTION_NONDEFAULT) { if (needspace) appendStringInfoChar(buf, ' '); if (wc->frameOptions & FRAMEOPTION_RANGE) appendStringInfoString(buf, "RANGE "); else if (wc->frameOptions & FRAMEOPTION_ROWS) appendStringInfoString(buf, "ROWS "); else Assert(false); if (wc->frameOptions & FRAMEOPTION_BETWEEN) appendStringInfoString(buf, "BETWEEN "); if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) appendStringInfoString(buf, "UNBOUNDED PRECEDING "); else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW) appendStringInfoString(buf, "CURRENT ROW "); else if (wc->frameOptions & FRAMEOPTION_START_VALUE) { get_rule_expr(wc->startOffset, context, false); if (wc->frameOptions & FRAMEOPTION_START_VALUE_PRECEDING) appendStringInfoString(buf, " PRECEDING "); else if (wc->frameOptions & FRAMEOPTION_START_VALUE_FOLLOWING) appendStringInfoString(buf, " FOLLOWING "); else Assert(false); } else Assert(false); if (wc->frameOptions & FRAMEOPTION_BETWEEN) { appendStringInfoString(buf, "AND "); if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING) appendStringInfoString(buf, "UNBOUNDED FOLLOWING "); else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW) appendStringInfoString(buf, "CURRENT ROW "); else if (wc->frameOptions & FRAMEOPTION_END_VALUE) { get_rule_expr(wc->endOffset, context, false); if (wc->frameOptions & FRAMEOPTION_END_VALUE_PRECEDING) appendStringInfoString(buf, " PRECEDING "); else if (wc->frameOptions & FRAMEOPTION_END_VALUE_FOLLOWING) appendStringInfoString(buf, " FOLLOWING "); else Assert(false); } else Assert(false); } /* we will now have a trailing space; remove it */ buf->len--; } appendStringInfoChar(buf, ')'); }
static void get_select_query_def | ( | Query * | query, | |
deparse_context * | context, | |||
TupleDesc | resultDesc | |||
) | [static] |
Definition at line 4150 of file ruleutils.c.
References appendContextKeyword(), appendStringInfo(), deparse_context::buf, buf, get_basic_select_query(), get_rtable_name(), get_rule_expr(), get_rule_orderby(), get_setop_query(), get_with_clause(), Query::hasForUpdate, IsA, LCS_FORKEYSHARE, LCS_FORNOKEYUPDATE, LCS_FORSHARE, LCS_FORUPDATE, lfirst, Query::limitCount, Query::limitOffset, NIL, RowMarkClause::noWait, NULL, PRETTYINDENT_STD, RowMarkClause::pushedDown, quote_identifier(), Query::rowMarks, RowMarkClause::rti, Query::setOperations, Query::sortClause, RowMarkClause::strength, Query::targetList, Query::windowClause, deparse_context::windowClause, and deparse_context::windowTList.
Referenced by get_query_def().
{ StringInfo buf = context->buf; List *save_windowclause; List *save_windowtlist; bool force_colno; ListCell *l; /* Insert the WITH clause if given */ get_with_clause(query, context); /* Set up context for possible window functions */ save_windowclause = context->windowClause; context->windowClause = query->windowClause; save_windowtlist = context->windowTList; context->windowTList = query->targetList; /* * If the Query node has a setOperations tree, then it's the top level of * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT * fields are interesting in the top query itself. */ if (query->setOperations) { get_setop_query(query->setOperations, query, context, resultDesc); /* ORDER BY clauses must be simple in this case */ force_colno = true; } else { get_basic_select_query(query, context, resultDesc); force_colno = false; } /* Add the ORDER BY clause if given */ if (query->sortClause != NIL) { appendContextKeyword(context, " ORDER BY ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); get_rule_orderby(query->sortClause, query->targetList, force_colno, context); } /* Add the LIMIT clause if given */ if (query->limitOffset != NULL) { appendContextKeyword(context, " OFFSET ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); get_rule_expr(query->limitOffset, context, false); } if (query->limitCount != NULL) { appendContextKeyword(context, " LIMIT ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); if (IsA(query->limitCount, Const) && ((Const *) query->limitCount)->constisnull) appendStringInfo(buf, "ALL"); else get_rule_expr(query->limitCount, context, false); } /* Add FOR [KEY] UPDATE/SHARE clauses if present */ if (query->hasForUpdate) { foreach(l, query->rowMarks) { RowMarkClause *rc = (RowMarkClause *) lfirst(l); /* don't print implicit clauses */ if (rc->pushedDown) continue; switch (rc->strength) { case LCS_FORKEYSHARE: appendContextKeyword(context, " FOR KEY SHARE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); break; case LCS_FORSHARE: appendContextKeyword(context, " FOR SHARE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); break; case LCS_FORNOKEYUPDATE: appendContextKeyword(context, " FOR NO KEY UPDATE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); break; case LCS_FORUPDATE: appendContextKeyword(context, " FOR UPDATE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); break; } appendStringInfo(buf, " OF %s", quote_identifier(get_rtable_name(rc->rti, context))); if (rc->noWait) appendStringInfo(buf, " NOWAIT"); } } context->windowClause = save_windowclause; context->windowTList = save_windowtlist; }
static void get_setop_query | ( | Node * | setOp, | |
Query * | query, | |||
deparse_context * | context, | |||
TupleDesc | resultDesc | |||
) | [static] |
Definition at line 4526 of file ruleutils.c.
References SetOperationStmt::all, appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoSpaces(), Assert, deparse_context::buf, buf, Query::cteList, elog, ERROR, get_query_def(), deparse_context::indentLevel, IsA, SetOperationStmt::larg, Query::limitCount, Query::limitOffset, deparse_context::namespaces, nodeTag, NULL, SetOperationStmt::op, PRETTY_INDENT, deparse_context::prettyFlags, PRETTYINDENT_STD, SetOperationStmt::rarg, Query::rowMarks, rt_fetch, Query::rtable, RangeTblRef::rtindex, SETOP_EXCEPT, SETOP_INTERSECT, SETOP_UNION, Query::setOperations, Query::sortClause, RangeTblEntry::subquery, and deparse_context::wrapColumn.
Referenced by get_select_query_def().
{ StringInfo buf = context->buf; bool need_paren; if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); Query *subquery = rte->subquery; Assert(subquery != NULL); Assert(subquery->setOperations == NULL); /* Need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y */ need_paren = (subquery->cteList || subquery->sortClause || subquery->rowMarks || subquery->limitOffset || subquery->limitCount); if (need_paren) appendStringInfoChar(buf, '('); get_query_def(subquery, buf, context->namespaces, resultDesc, context->prettyFlags, context->wrapColumn, context->indentLevel); if (need_paren) appendStringInfoChar(buf, ')'); } else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; if (PRETTY_INDENT(context)) { context->indentLevel += PRETTYINDENT_STD; appendStringInfoSpaces(buf, PRETTYINDENT_STD); } /* * We force parens whenever nesting two SetOperationStmts. There are * some cases in which parens are needed around a leaf query too, but * those are more easily handled at the next level down (see code * above). */ need_paren = !IsA(op->larg, RangeTblRef); if (need_paren) appendStringInfoChar(buf, '('); get_setop_query(op->larg, query, context, resultDesc); if (need_paren) appendStringInfoChar(buf, ')'); if (!PRETTY_INDENT(context)) appendStringInfoChar(buf, ' '); switch (op->op) { case SETOP_UNION: appendContextKeyword(context, "UNION ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); break; case SETOP_INTERSECT: appendContextKeyword(context, "INTERSECT ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); break; case SETOP_EXCEPT: appendContextKeyword(context, "EXCEPT ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); break; default: elog(ERROR, "unrecognized set op: %d", (int) op->op); } if (op->all) appendStringInfo(buf, "ALL "); if (PRETTY_INDENT(context)) appendContextKeyword(context, "", 0, 0, 0); need_paren = !IsA(op->rarg, RangeTblRef); if (need_paren) appendStringInfoChar(buf, '('); get_setop_query(op->rarg, query, context, resultDesc); if (need_paren) appendStringInfoChar(buf, ')'); if (PRETTY_INDENT(context)) context->indentLevel -= PRETTYINDENT_STD; } else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(setOp)); } }
static const char * get_simple_binary_op_name | ( | OpExpr * | expr | ) | [static] |
Definition at line 5989 of file ruleutils.c.
References OpExpr::args, exprType(), generate_operator_name(), linitial, list_length(), lsecond, and OpExpr::opno.
Referenced by isSimpleNode().
static RangeTblEntry* get_simple_values_rte | ( | Query * | query | ) | [static] |
Definition at line 4260 of file ruleutils.c.
References RangeTblEntry::inFromCl, lfirst, Query::rtable, RTE_RELATION, RTE_VALUES, and RangeTblEntry::rtekind.
Referenced by get_basic_select_query().
{ RangeTblEntry *result = NULL; ListCell *lc; /* * We want to return TRUE even if the Query also contains OLD or NEW rule * RTEs. So the idea is to scan the rtable and see if there is only one * inFromCl RTE that is a VALUES RTE. We don't look at the targetlist at * all. This is okay because parser/analyze.c will never generate a * "bare" VALUES RTE --- they only appear inside auto-generated * sub-queries with very restricted structure. */ foreach(lc, query->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); if (rte->rtekind == RTE_VALUES && rte->inFromCl) { if (result) return NULL; /* multiple VALUES (probably not possible) */ result = rte; } else if (rte->rtekind == RTE_RELATION && !rte->inFromCl) continue; /* ignore rule entries */ else return NULL; /* something else -> not simple VALUES */ } return result; }
static void get_sublink_expr | ( | SubLink * | sublink, | |
deparse_context * | context | |||
) | [static] |
Definition at line 7721 of file ruleutils.c.
References ALL_SUBLINK, ANY_SUBLINK, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), OpExpr::args, ARRAY_SUBLINK, Assert, deparse_context::buf, buf, CTE_SUBLINK, elog, ERROR, EXISTS_SUBLINK, EXPR_SUBLINK, exprType(), generate_operator_name(), get_query_def(), get_rule_expr(), deparse_context::indentLevel, IsA, RowCompareExpr::largs, lfirst, linitial, linitial_oid, lsecond, deparse_context::namespaces, nodeTag, NULL, OpExpr::opno, RowCompareExpr::opnos, deparse_context::prettyFlags, RowCompareExpr::rargs, ROWCOMPARE_SUBLINK, SubLink::subLinkType, SubLink::subselect, SubLink::testexpr, and deparse_context::wrapColumn.
Referenced by get_rule_expr().
{ StringInfo buf = context->buf; Query *query = (Query *) (sublink->subselect); char *opname = NULL; bool need_paren; if (sublink->subLinkType == ARRAY_SUBLINK) appendStringInfo(buf, "ARRAY("); else appendStringInfoChar(buf, '('); /* * Note that we print the name of only the first operator, when there are * multiple combining operators. This is an approximation that could go * wrong in various scenarios (operators in different schemas, renamed * operators, etc) but there is not a whole lot we can do about it, since * the syntax allows only one operator to be shown. */ if (sublink->testexpr) { if (IsA(sublink->testexpr, OpExpr)) { /* single combining operator */ OpExpr *opexpr = (OpExpr *) sublink->testexpr; get_rule_expr(linitial(opexpr->args), context, true); opname = generate_operator_name(opexpr->opno, exprType(linitial(opexpr->args)), exprType(lsecond(opexpr->args))); } else if (IsA(sublink->testexpr, BoolExpr)) { /* multiple combining operators, = or <> cases */ char *sep; ListCell *l; appendStringInfoChar(buf, '('); sep = ""; foreach(l, ((BoolExpr *) sublink->testexpr)->args) { OpExpr *opexpr = (OpExpr *) lfirst(l); Assert(IsA(opexpr, OpExpr)); appendStringInfoString(buf, sep); get_rule_expr(linitial(opexpr->args), context, true); if (!opname) opname = generate_operator_name(opexpr->opno, exprType(linitial(opexpr->args)), exprType(lsecond(opexpr->args))); sep = ", "; } appendStringInfoChar(buf, ')'); } else if (IsA(sublink->testexpr, RowCompareExpr)) { /* multiple combining operators, < <= > >= cases */ RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr; appendStringInfoChar(buf, '('); get_rule_expr((Node *) rcexpr->largs, context, true); opname = generate_operator_name(linitial_oid(rcexpr->opnos), exprType(linitial(rcexpr->largs)), exprType(linitial(rcexpr->rargs))); appendStringInfoChar(buf, ')'); } else elog(ERROR, "unrecognized testexpr type: %d", (int) nodeTag(sublink->testexpr)); } need_paren = true; switch (sublink->subLinkType) { case EXISTS_SUBLINK: appendStringInfo(buf, "EXISTS "); break; case ANY_SUBLINK: if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */ appendStringInfo(buf, " IN "); else appendStringInfo(buf, " %s ANY ", opname); break; case ALL_SUBLINK: appendStringInfo(buf, " %s ALL ", opname); break; case ROWCOMPARE_SUBLINK: appendStringInfo(buf, " %s ", opname); break; case EXPR_SUBLINK: case ARRAY_SUBLINK: need_paren = false; break; case CTE_SUBLINK: /* shouldn't occur in a SubLink */ default: elog(ERROR, "unrecognized sublink type: %d", (int) sublink->subLinkType); break; } if (need_paren) appendStringInfoChar(buf, '('); get_query_def(query, buf, context->namespaces, NULL, context->prettyFlags, context->wrapColumn, context->indentLevel); if (need_paren) appendStringInfo(buf, "))"); else appendStringInfoChar(buf, ')'); }
static void get_target_list | ( | List * | targetList, | |
deparse_context * | context, | |||
TupleDesc | resultDesc | |||
) | [static] |
Definition at line 4396 of file ruleutils.c.
References appendContextKeyword(), appendStringInfo(), appendStringInfoString(), tupleDesc::attrs, deparse_context::buf, buf, StringInfoData::data, TargetEntry::expr, get_rule_expr(), get_variable(), initStringInfo(), IsA, StringInfoData::len, lfirst, NameStr, NULL, pfree(), PRETTY_INDENT, PRETTYINDENT_STD, PRETTYINDENT_VAR, quote_identifier(), resetStringInfo(), TargetEntry::resjunk, TargetEntry::resname, and deparse_context::wrapColumn.
Referenced by get_basic_select_query(), get_delete_query_def(), get_insert_query_def(), and get_update_query_def().
{ StringInfo buf = context->buf; StringInfoData targetbuf; bool last_was_multiline = false; char *sep; int colno; ListCell *l; /* we use targetbuf to hold each TLE's text temporarily */ initStringInfo(&targetbuf); sep = " "; colno = 0; foreach(l, targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); char *colname; char *attname; if (tle->resjunk) continue; /* ignore junk entries */ appendStringInfoString(buf, sep); sep = ", "; colno++; /* * Put the new field text into targetbuf so we can decide after we've * got it whether or not it needs to go on a new line. */ resetStringInfo(&targetbuf); context->buf = &targetbuf; /* * We special-case Var nodes rather than using get_rule_expr. This is * needed because get_rule_expr will display a whole-row Var as * "foo.*", which is the preferred notation in most contexts, but at * the top level of a SELECT list it's not right (the parser will * expand that notation into multiple columns, yielding behavior * different from a whole-row Var). We need to call get_variable * directly so that we can tell it to do the right thing. */ if (tle->expr && IsA(tle->expr, Var)) { attname = get_variable((Var *) tle->expr, 0, true, context); } else { get_rule_expr((Node *) tle->expr, context, true); /* We'll show the AS name unless it's this: */ attname = "?column?"; } /* * Figure out what the result column should be called. In the context * of a view, use the view's tuple descriptor (so as to pick up the * effects of any column RENAME that's been done on the view). * Otherwise, just use what we can find in the TLE. */ if (resultDesc && colno <= resultDesc->natts) colname = NameStr(resultDesc->attrs[colno - 1]->attname); else colname = tle->resname; /* Show AS unless the column's name is correct as-is */ if (colname) /* resname could be NULL */ { if (attname == NULL || strcmp(attname, colname) != 0) appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname)); } /* Restore context's output buffer */ context->buf = buf; /* Consider line-wrapping if enabled */ if (PRETTY_INDENT(context) && context->wrapColumn >= 0) { int leading_nl_pos = -1; char *trailing_nl; int pos; /* Does the new field start with whitespace plus a new line? */ for (pos = 0; pos < targetbuf.len; pos++) { if (targetbuf.data[pos] == '\n') { leading_nl_pos = pos; break; } if (targetbuf.data[pos] != ' ') break; } /* Locate the start of the current line in the output buffer */ trailing_nl = strrchr(buf->data, '\n'); if (trailing_nl == NULL) trailing_nl = buf->data; else trailing_nl++; /* * If the field we're adding is the first in the list, or it * already has a leading newline, don't add anything. Otherwise, * add a newline, plus some indentation, if either the new field * would cause an overflow or the last field used more than one * line. */ if (colno > 1 && leading_nl_pos == -1 && ((strlen(trailing_nl) + strlen(targetbuf.data) > context->wrapColumn) || last_was_multiline)) appendContextKeyword(context, "", -PRETTYINDENT_STD, PRETTYINDENT_STD, PRETTYINDENT_VAR); /* Remember this field's multiline status for next iteration */ last_was_multiline = (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL); } /* Add the new field */ appendStringInfoString(buf, targetbuf.data); } /* clean up */ pfree(targetbuf.data); }
static void get_update_query_def | ( | Query * | query, | |
deparse_context * | context | |||
) | [static] |
Definition at line 5003 of file ruleutils.c.
References RangeTblEntry::alias, Alias::aliasname, appendContextKeyword(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, TargetEntry::expr, generate_relation_name(), get_from_clause(), get_relid_attribute_name(), get_rule_expr(), get_target_list(), get_with_clause(), deparse_context::indentLevel, Query::jointree, lfirst, NIL, NULL, only_marker, PRETTY_INDENT, PRETTYINDENT_STD, processIndirection(), FromExpr::quals, quote_identifier(), RangeTblEntry::relid, TargetEntry::resjunk, TargetEntry::resno, Query::resultRelation, Query::returningList, rt_fetch, Query::rtable, RTE_RELATION, RangeTblEntry::rtekind, and Query::targetList.
Referenced by get_query_def().
{ StringInfo buf = context->buf; char *sep; RangeTblEntry *rte; ListCell *l; /* Insert the WITH clause if given */ get_with_clause(query, context); /* * Start the query with UPDATE relname SET */ rte = rt_fetch(query->resultRelation, query->rtable); Assert(rte->rtekind == RTE_RELATION); if (PRETTY_INDENT(context)) { appendStringInfoChar(buf, ' '); context->indentLevel += PRETTYINDENT_STD; } appendStringInfo(buf, "UPDATE %s%s", only_marker(rte), generate_relation_name(rte->relid, NIL)); if (rte->alias != NULL) appendStringInfo(buf, " %s", quote_identifier(rte->alias->aliasname)); appendStringInfoString(buf, " SET "); /* Add the comma separated list of 'attname = value' */ sep = ""; foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); Node *expr; if (tle->resjunk) continue; /* ignore junk entries */ appendStringInfoString(buf, sep); sep = ", "; /* * Put out name of target column; look in the catalogs, not at * tle->resname, since resname will fail to track RENAME. */ appendStringInfoString(buf, quote_identifier(get_relid_attribute_name(rte->relid, tle->resno))); /* * Print any indirection needed (subfields or subscripts), and strip * off the top-level nodes representing the indirection assignments. */ expr = processIndirection((Node *) tle->expr, context, true); appendStringInfo(buf, " = "); get_rule_expr(expr, context, false); } /* Add the FROM clause if needed */ get_from_clause(query, " FROM ", context); /* Add a WHERE clause if given */ if (query->jointree->quals != NULL) { appendContextKeyword(context, " WHERE ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); get_rule_expr(query->jointree->quals, context, false); } /* Add RETURNING if present */ if (query->returningList) { appendContextKeyword(context, " RETURNING", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); get_target_list(query->returningList, context, NULL); } }
static void get_utility_query_def | ( | Query * | query, | |
deparse_context * | context | |||
) | [static] |
Definition at line 5140 of file ruleutils.c.
References appendContextKeyword(), appendStringInfo(), appendStringInfoString(), deparse_context::buf, buf, NotifyStmt::conditionname, elog, ERROR, IsA, NotifyStmt::payload, PRETTYINDENT_STD, quote_identifier(), simple_quote_literal(), and Query::utilityStmt.
Referenced by get_query_def().
{ StringInfo buf = context->buf; if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt)) { NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt; appendContextKeyword(context, "", 0, PRETTYINDENT_STD, 1); appendStringInfo(buf, "NOTIFY %s", quote_identifier(stmt->conditionname)); if (stmt->payload) { appendStringInfoString(buf, ", "); simple_quote_literal(buf, stmt->payload); } } else { /* Currently only NOTIFY utility commands can appear in rules */ elog(ERROR, "unexpected utility statement type"); } }
static void get_values_def | ( | List * | values_lists, | |
deparse_context * | context | |||
) | [static] |
Definition at line 4037 of file ruleutils.c.
References appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, get_rule_expr(), lfirst, and processIndirection().
Referenced by get_basic_select_query(), get_from_clause_item(), and get_insert_query_def().
{ StringInfo buf = context->buf; bool first_list = true; ListCell *vtl; appendStringInfoString(buf, "VALUES "); foreach(vtl, values_lists) { List *sublist = (List *) lfirst(vtl); bool first_col = true; ListCell *lc; if (first_list) first_list = false; else appendStringInfoString(buf, ", "); appendStringInfoChar(buf, '('); foreach(lc, sublist) { Node *col = (Node *) lfirst(lc); if (first_col) first_col = false; else appendStringInfoChar(buf, ','); /* * Strip any top-level nodes representing indirection assignments, * then print the result. */ get_rule_expr(processIndirection(col, context, false), context, false); } appendStringInfoChar(buf, ')'); } }
static char * get_variable | ( | Var * | var, | |
int | levelsup, | |||
bool | istoplevel, | |||
deparse_context * | context | |||
) | [static] |
Definition at line 5186 of file ruleutils.c.
References RangeTblEntry::alias, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, deparse_context::buf, buf, deparse_columns::colnames, Alias::colnames, deparse_columns_fetch, elog, RangeTblEntry::eref, ERROR, TargetEntry::expr, format_type_with_typemod(), get_rte_attribute_name(), get_rule_expr(), get_tle_by_resno(), deparse_namespace::index_tlist, INDEX_VAR, deparse_namespace::inner_planstate, deparse_namespace::inner_tlist, INNER_VAR, InvalidAttrNumber, IsA, RangeTblEntry::joinaliasvars, list_length(), list_nth(), deparse_context::namespaces, NIL, NULL, deparse_namespace::outer_planstate, deparse_namespace::outer_tlist, OUTER_VAR, pop_child_plan(), push_child_plan(), quote_identifier(), rt_fetch, deparse_namespace::rtable, deparse_namespace::rtable_names, RTE_CTE, RTE_JOIN, RTE_SUBQUERY, RangeTblEntry::rtekind, Var::varattno, Var::varlevelsup, Var::varno, deparse_context::varprefix, Var::vartype, and Var::vartypmod.
Referenced by get_rule_expr(), and get_target_list().
{ StringInfo buf = context->buf; RangeTblEntry *rte; AttrNumber attnum; int netlevelsup; deparse_namespace *dpns; deparse_columns *colinfo; char *refname; char *attname; /* Find appropriate nesting depth */ netlevelsup = var->varlevelsup + levelsup; if (netlevelsup >= list_length(context->namespaces)) elog(ERROR, "bogus varlevelsup: %d offset %d", var->varlevelsup, levelsup); dpns = (deparse_namespace *) list_nth(context->namespaces, netlevelsup); /* * Try to find the relevant RTE in this rtable. In a plan tree, it's * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig * down into the subplans, or INDEX_VAR, which is resolved similarly. Also * find the aliases previously assigned for this RTE. */ if (var->varno >= 1 && var->varno <= list_length(dpns->rtable)) { rte = rt_fetch(var->varno, dpns->rtable); refname = (char *) list_nth(dpns->rtable_names, var->varno - 1); colinfo = deparse_columns_fetch(var->varno, dpns); attnum = var->varattno; } else if (var->varno == OUTER_VAR && dpns->outer_tlist) { TargetEntry *tle; deparse_namespace save_dpns; tle = get_tle_by_resno(dpns->outer_tlist, var->varattno); if (!tle) elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno); Assert(netlevelsup == 0); push_child_plan(dpns, dpns->outer_planstate, &save_dpns); /* * Force parentheses because our caller probably assumed a Var is a * simple expression. */ if (!IsA(tle->expr, Var)) appendStringInfoChar(buf, '('); get_rule_expr((Node *) tle->expr, context, true); if (!IsA(tle->expr, Var)) appendStringInfoChar(buf, ')'); pop_child_plan(dpns, &save_dpns); return NULL; } else if (var->varno == INNER_VAR && dpns->inner_tlist) { TargetEntry *tle; deparse_namespace save_dpns; tle = get_tle_by_resno(dpns->inner_tlist, var->varattno); if (!tle) elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno); Assert(netlevelsup == 0); push_child_plan(dpns, dpns->inner_planstate, &save_dpns); /* * Force parentheses because our caller probably assumed a Var is a * simple expression. */ if (!IsA(tle->expr, Var)) appendStringInfoChar(buf, '('); get_rule_expr((Node *) tle->expr, context, true); if (!IsA(tle->expr, Var)) appendStringInfoChar(buf, ')'); pop_child_plan(dpns, &save_dpns); return NULL; } else if (var->varno == INDEX_VAR && dpns->index_tlist) { TargetEntry *tle; tle = get_tle_by_resno(dpns->index_tlist, var->varattno); if (!tle) elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno); Assert(netlevelsup == 0); /* * Force parentheses because our caller probably assumed a Var is a * simple expression. */ if (!IsA(tle->expr, Var)) appendStringInfoChar(buf, '('); get_rule_expr((Node *) tle->expr, context, true); if (!IsA(tle->expr, Var)) appendStringInfoChar(buf, ')'); return NULL; } else { elog(ERROR, "bogus varno: %d", var->varno); return NULL; /* keep compiler quiet */ } /* * The planner will sometimes emit Vars referencing resjunk elements of a * subquery's target list (this is currently only possible if it chooses * to generate a "physical tlist" for a SubqueryScan or CteScan node). * Although we prefer to print subquery-referencing Vars using the * subquery's alias, that's not possible for resjunk items since they have * no alias. So in that case, drill down to the subplan and print the * contents of the referenced tlist item. This works because in a plan * tree, such Vars can only occur in a SubqueryScan or CteScan node, and * we'll have set dpns->inner_planstate to reference the child plan node. */ if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) && attnum > list_length(rte->eref->colnames) && dpns->inner_planstate) { TargetEntry *tle; deparse_namespace save_dpns; tle = get_tle_by_resno(dpns->inner_tlist, var->varattno); if (!tle) elog(ERROR, "bogus varattno for subquery var: %d", var->varattno); Assert(netlevelsup == 0); push_child_plan(dpns, dpns->inner_planstate, &save_dpns); /* * Force parentheses because our caller probably assumed a Var is a * simple expression. */ if (!IsA(tle->expr, Var)) appendStringInfoChar(buf, '('); get_rule_expr((Node *) tle->expr, context, true); if (!IsA(tle->expr, Var)) appendStringInfoChar(buf, ')'); pop_child_plan(dpns, &save_dpns); return NULL; } /* * If it's an unnamed join, look at the expansion of the alias variable. * If it's a simple reference to one of the input vars, then recursively * print the name of that var instead. When it's not a simple reference, * we have to just print the unqualified join column name. (This can only * happen with columns that were merged by USING or NATURAL clauses in a * FULL JOIN; we took pains previously to make the unqualified column name * unique in such cases.) * * This wouldn't work in decompiling plan trees, because we don't store * joinaliasvars lists after planning; but a plan tree should never * contain a join alias variable. */ if (rte->rtekind == RTE_JOIN && rte->alias == NULL) { if (rte->joinaliasvars == NIL) elog(ERROR, "cannot decompile join alias var in plan tree"); if (attnum > 0) { Var *aliasvar; aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); if (IsA(aliasvar, Var)) { return get_variable(aliasvar, var->varlevelsup + levelsup, istoplevel, context); } } /* * Unnamed join has no refname. (Note: since it's unnamed, there is * no way the user could have referenced it to create a whole-row Var * for it. So we don't have to cover that case below.) */ Assert(refname == NULL); } if (attnum == InvalidAttrNumber) attname = NULL; else if (attnum > 0) { /* Get column name to use from the colinfo struct */ Assert(attnum <= colinfo->num_cols); attname = colinfo->colnames[attnum - 1]; Assert(attname != NULL); } else { /* System column - name is fixed, get it from the catalog */ attname = get_rte_attribute_name(rte, attnum); } if (refname && (context->varprefix || attname == NULL)) { appendStringInfoString(buf, quote_identifier(refname)); appendStringInfoChar(buf, '.'); } if (attname) appendStringInfoString(buf, quote_identifier(attname)); else { appendStringInfoChar(buf, '*'); if (istoplevel) appendStringInfo(buf, "::%s", format_type_with_typemod(var->vartype, var->vartypmod)); } return attname; }
static void get_windowfunc_expr | ( | WindowFunc * | wfunc, | |
deparse_context * | context | |||
) | [static] |
Definition at line 7429 of file ruleutils.c.
References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, WindowFunc::args, Assert, deparse_context::buf, buf, elog, ereport, errcode(), errmsg(), ERROR, exprType(), FUNC_MAX_ARGS, generate_function_name(), get_rule_expr(), get_rule_windowspec(), IsA, lfirst, list_length(), WindowClause::name, NIL, NULL, quote_identifier(), deparse_context::windowClause, deparse_context::windowTList, WindowFunc::winfnoid, WindowFunc::winref, WindowClause::winref, and WindowFunc::winstar.
Referenced by get_rule_expr().
{ StringInfo buf = context->buf; Oid argtypes[FUNC_MAX_ARGS]; int nargs; ListCell *l; if (list_length(wfunc->args) > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("too many arguments"))); nargs = 0; foreach(l, wfunc->args) { Node *arg = (Node *) lfirst(l); Assert(!IsA(arg, NamedArgExpr)); argtypes[nargs] = exprType(arg); nargs++; } appendStringInfo(buf, "%s(", generate_function_name(wfunc->winfnoid, nargs, NIL, argtypes, false, NULL)); /* winstar can be set only in zero-argument aggregates */ if (wfunc->winstar) appendStringInfoChar(buf, '*'); else get_rule_expr((Node *) wfunc->args, context, true); appendStringInfoString(buf, ") OVER "); foreach(l, context->windowClause) { WindowClause *wc = (WindowClause *) lfirst(l); if (wc->winref == wfunc->winref) { if (wc->name) appendStringInfoString(buf, quote_identifier(wc->name)); else get_rule_windowspec(wc, context->windowTList, context); break; } } if (l == NULL) { if (context->windowClause) elog(ERROR, "could not find window clause for winref %u", wfunc->winref); /* * In EXPLAIN, we don't have window context information available, so * we have to settle for this: */ appendStringInfoString(buf, "(?)"); } }
static void get_with_clause | ( | Query * | query, | |
deparse_context * | context | |||
) | [static] |
Definition at line 4082 of file ruleutils.c.
References CommonTableExpr::aliascolnames, appendContextKeyword(), appendStringInfoChar(), appendStringInfoString(), deparse_context::buf, buf, Query::cteList, CommonTableExpr::ctename, CommonTableExpr::ctequery, get_query_def(), Query::hasRecursive, deparse_context::indentLevel, lfirst, deparse_context::namespaces, NIL, NULL, PRETTY_INDENT, deparse_context::prettyFlags, quote_identifier(), strVal, and deparse_context::wrapColumn.
Referenced by get_delete_query_def(), get_insert_query_def(), get_select_query_def(), and get_update_query_def().
{ StringInfo buf = context->buf; const char *sep; ListCell *l; if (query->cteList == NIL) return; if (PRETTY_INDENT(context)) { context->indentLevel += PRETTYINDENT_STD; appendStringInfoChar(buf, ' '); } if (query->hasRecursive) sep = "WITH RECURSIVE "; else sep = "WITH "; foreach(l, query->cteList) { CommonTableExpr *cte = (CommonTableExpr *) lfirst(l); appendStringInfoString(buf, sep); appendStringInfoString(buf, quote_identifier(cte->ctename)); if (cte->aliascolnames) { bool first = true; ListCell *col; appendStringInfoChar(buf, '('); foreach(col, cte->aliascolnames) { if (first) first = false; else appendStringInfoString(buf, ", "); appendStringInfoString(buf, quote_identifier(strVal(lfirst(col)))); } appendStringInfoChar(buf, ')'); } appendStringInfoString(buf, " AS ("); if (PRETTY_INDENT(context)) appendContextKeyword(context, "", 0, 0, 0); get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL, context->prettyFlags, context->wrapColumn, context->indentLevel); if (PRETTY_INDENT(context)) appendContextKeyword(context, "", 0, 0, 0); appendStringInfoChar(buf, ')'); sep = ", "; } if (PRETTY_INDENT(context)) { context->indentLevel -= PRETTYINDENT_STD; appendContextKeyword(context, "", 0, 0, 0); } else appendStringInfoChar(buf, ' '); }
Definition at line 2645 of file ruleutils.c.
References JoinExpr::alias, elog, ERROR, FromExpr::fromlist, IsA, JOIN_FULL, JoinExpr::jointype, JoinExpr::larg, lfirst, nodeTag, NULL, JoinExpr::rarg, and JoinExpr::usingClause.
Referenced by set_deparse_for_query().
{ if (IsA(jtnode, RangeTblRef)) { /* nothing to do here */ } else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; ListCell *lc; foreach(lc, f->fromlist) { if (has_unnamed_full_join_using((Node *) lfirst(lc))) return true; } } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; /* Is it an unnamed FULL JOIN with USING? */ if (j->alias == NULL && j->jointype == JOIN_FULL && j->usingClause) return true; /* Nope, but inspect children */ if (has_unnamed_full_join_using(j->larg)) return true; if (has_unnamed_full_join_using(j->rarg)) return true; } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode)); return false; }
static void identify_join_columns | ( | JoinExpr * | j, | |
RangeTblEntry * | jrte, | |||
deparse_columns * | colinfo | |||
) | [static] |
Definition at line 3361 of file ruleutils.c.
References Assert, Alias::colnames, elog, RangeTblEntry::eref, ERROR, flatten_join_using_qual(), forboth, i, IsA, RangeTblEntry::joinaliasvars, JoinExpr::larg, deparse_columns::leftattnos, deparse_columns::leftrti, lfirst, list_length(), nodeTag, palloc0(), JoinExpr::quals, JoinExpr::rarg, deparse_columns::rightattnos, deparse_columns::rightrti, JoinExpr::rtindex, JoinExpr::usingClause, Var::varattno, Var::varlevelsup, and Var::varno.
Referenced by set_using_names().
{ int numjoincols; int i; ListCell *lc; /* Extract left/right child RT indexes */ if (IsA(j->larg, RangeTblRef)) colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex; else if (IsA(j->larg, JoinExpr)) colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex; else elog(ERROR, "unrecognized node type in jointree: %d", (int) nodeTag(j->larg)); if (IsA(j->rarg, RangeTblRef)) colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex; else if (IsA(j->rarg, JoinExpr)) colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex; else elog(ERROR, "unrecognized node type in jointree: %d", (int) nodeTag(j->rarg)); /* Assert children will be processed earlier than join in second pass */ Assert(colinfo->leftrti < j->rtindex); Assert(colinfo->rightrti < j->rtindex); /* Initialize result arrays with zeroes */ numjoincols = list_length(jrte->joinaliasvars); Assert(numjoincols == list_length(jrte->eref->colnames)); colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int)); colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int)); /* Scan the joinaliasvars list to identify simple column references */ i = 0; foreach(lc, jrte->joinaliasvars) { Var *aliasvar = (Var *) lfirst(lc); if (IsA(aliasvar, Var)) { Assert(aliasvar->varlevelsup == 0); Assert(aliasvar->varattno != 0); if (aliasvar->varno == colinfo->leftrti) colinfo->leftattnos[i] = aliasvar->varattno; else if (aliasvar->varno == colinfo->rightrti) colinfo->rightattnos[i] = aliasvar->varattno; else elog(ERROR, "unexpected varno %d in JOIN RTE", aliasvar->varno); } else if (IsA(aliasvar, CoalesceExpr)) { /* * It's a merged column in FULL JOIN USING. Ignore it for now and * let the code below identify the merged columns. */ } else { /* * Although NULL constants can appear in joinaliasvars lists * during planning, we shouldn't see any here, since the Query * tree hasn't been through AcquireRewriteLocks(). */ elog(ERROR, "unrecognized node type in join alias vars: %d", (int) nodeTag(aliasvar)); } i++; } /* * If there's a USING clause, deconstruct the join quals to identify the * merged columns. This is a tad painful but if we cannot rely on the * column names, there is no other representation of which columns were * joined by USING. (Unless the join type is FULL, we can't tell from the * joinaliasvars list which columns are merged.) Note: we assume that the * merged columns are the first output column(s) of the join. */ if (j->usingClause) { List *leftvars = NIL; List *rightvars = NIL; ListCell *lc2; /* Extract left- and right-side Vars from the qual expression */ flatten_join_using_qual(j->quals, &leftvars, &rightvars); Assert(list_length(leftvars) == list_length(j->usingClause)); Assert(list_length(rightvars) == list_length(j->usingClause)); /* Mark the output columns accordingly */ i = 0; forboth(lc, leftvars, lc2, rightvars) { Var *leftvar = (Var *) lfirst(lc); Var *rightvar = (Var *) lfirst(lc2); Assert(leftvar->varlevelsup == 0); Assert(leftvar->varattno != 0); if (leftvar->varno != colinfo->leftrti) elog(ERROR, "unexpected varno %d in JOIN USING qual", leftvar->varno); colinfo->leftattnos[i] = leftvar->varattno; Assert(rightvar->varlevelsup == 0); Assert(rightvar->varattno != 0); if (rightvar->varno != colinfo->rightrti) elog(ERROR, "unexpected varno %d in JOIN USING qual", rightvar->varno); colinfo->rightattnos[i] = rightvar->varattno; i++; } } }
Definition at line 6015 of file ruleutils.c.
References AND_EXPR, arg, COERCE_EXPLICIT_CAST, COERCE_IMPLICIT_CAST, get_simple_binary_op_name(), IsA, linitial, nodeTag, NOT_EXPR, OR_EXPR, PRETTYFLAG_PAREN, T_Aggref, T_ArrayCoerceExpr, T_ArrayExpr, T_ArrayRef, T_BooleanTest, T_BoolExpr, T_CaseExpr, T_CoalesceExpr, T_CoerceToDomain, T_CoerceToDomainValue, T_CoerceViaIO, T_Const, T_ConvertRowtypeExpr, T_CurrentOfExpr, T_DistinctExpr, T_FieldSelect, T_FieldStore, T_FuncExpr, T_MinMaxExpr, T_NullIfExpr, T_NullTest, T_OpExpr, T_Param, T_RelabelType, T_RowExpr, T_SetToDefault, T_SubLink, T_Var, T_WindowFunc, and T_XmlExpr.
Referenced by get_rule_expr_paren().
{ if (!node) return false; switch (nodeTag(node)) { case T_Var: case T_Const: case T_Param: case T_CoerceToDomainValue: case T_SetToDefault: case T_CurrentOfExpr: /* single words: always simple */ return true; case T_ArrayRef: case T_ArrayExpr: case T_RowExpr: case T_CoalesceExpr: case T_MinMaxExpr: case T_XmlExpr: case T_NullIfExpr: case T_Aggref: case T_WindowFunc: case T_FuncExpr: /* function-like: name(..) or name[..] */ return true; /* CASE keywords act as parentheses */ case T_CaseExpr: return true; case T_FieldSelect: /* * appears simple since . has top precedence, unless parent is * T_FieldSelect itself! */ return (IsA(parentNode, FieldSelect) ? false : true); case T_FieldStore: /* * treat like FieldSelect (probably doesn't matter) */ return (IsA(parentNode, FieldStore) ? false : true); case T_CoerceToDomain: /* maybe simple, check args */ return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg, node, prettyFlags); case T_RelabelType: return isSimpleNode((Node *) ((RelabelType *) node)->arg, node, prettyFlags); case T_CoerceViaIO: return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg, node, prettyFlags); case T_ArrayCoerceExpr: return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg, node, prettyFlags); case T_ConvertRowtypeExpr: return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg, node, prettyFlags); case T_OpExpr: { /* depends on parent node type; needs further checking */ if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr)) { const char *op; const char *parentOp; bool is_lopriop; bool is_hipriop; bool is_lopriparent; bool is_hipriparent; op = get_simple_binary_op_name((OpExpr *) node); if (!op) return false; /* We know only the basic operators + - and * / % */ is_lopriop = (strchr("+-", *op) != NULL); is_hipriop = (strchr("*/%", *op) != NULL); if (!(is_lopriop || is_hipriop)) return false; parentOp = get_simple_binary_op_name((OpExpr *) parentNode); if (!parentOp) return false; is_lopriparent = (strchr("+-", *parentOp) != NULL); is_hipriparent = (strchr("*/%", *parentOp) != NULL); if (!(is_lopriparent || is_hipriparent)) return false; if (is_hipriop && is_lopriparent) return true; /* op binds tighter than parent */ if (is_lopriop && is_hipriparent) return false; /* * Operators are same priority --- can skip parens only if * we have (a - b) - c, not a - (b - c). */ if (node == (Node *) linitial(((OpExpr *) parentNode)->args)) return true; return false; } /* else do the same stuff as for T_SubLink et al. */ /* FALL THROUGH */ } case T_SubLink: case T_NullTest: case T_BooleanTest: case T_DistinctExpr: switch (nodeTag(parentNode)) { case T_FuncExpr: { /* special handling for casts */ CoercionForm type = ((FuncExpr *) parentNode)->funcformat; if (type == COERCE_EXPLICIT_CAST || type == COERCE_IMPLICIT_CAST) return false; return true; /* own parentheses */ } case T_BoolExpr: /* lower precedence */ case T_ArrayRef: /* other separators */ case T_ArrayExpr: /* other separators */ case T_RowExpr: /* other separators */ case T_CoalesceExpr: /* own parentheses */ case T_MinMaxExpr: /* own parentheses */ case T_XmlExpr: /* own parentheses */ case T_NullIfExpr: /* other separators */ case T_Aggref: /* own parentheses */ case T_WindowFunc: /* own parentheses */ case T_CaseExpr: /* other separators */ return true; default: return false; } case T_BoolExpr: switch (nodeTag(parentNode)) { case T_BoolExpr: if (prettyFlags & PRETTYFLAG_PAREN) { BoolExprType type; BoolExprType parentType; type = ((BoolExpr *) node)->boolop; parentType = ((BoolExpr *) parentNode)->boolop; switch (type) { case NOT_EXPR: case AND_EXPR: if (parentType == AND_EXPR || parentType == OR_EXPR) return true; break; case OR_EXPR: if (parentType == OR_EXPR) return true; break; } } return false; case T_FuncExpr: { /* special handling for casts */ CoercionForm type = ((FuncExpr *) parentNode)->funcformat; if (type == COERCE_EXPLICIT_CAST || type == COERCE_IMPLICIT_CAST) return false; return true; /* own parentheses */ } case T_ArrayRef: /* other separators */ case T_ArrayExpr: /* other separators */ case T_RowExpr: /* other separators */ case T_CoalesceExpr: /* own parentheses */ case T_MinMaxExpr: /* own parentheses */ case T_XmlExpr: /* own parentheses */ case T_NullIfExpr: /* other separators */ case T_Aggref: /* own parentheses */ case T_WindowFunc: /* own parentheses */ case T_CaseExpr: /* other separators */ return true; default: return false; } default: break; } /* those we don't know: in dubio complexo */ return false; }
static char * make_colname_unique | ( | char * | colname, | |
deparse_namespace * | dpns, | |||
deparse_columns * | colinfo | |||
) | [static] |
Definition at line 3311 of file ruleutils.c.
References colname_is_unique(), i, and palloc().
Referenced by set_join_column_names(), set_relation_column_names(), and set_using_names().
{ /* * If the selected name isn't unique, append digits to make it so */ if (!colname_is_unique(colname, dpns, colinfo)) { char *modname = (char *) palloc(strlen(colname) + 32); int i = 0; do { sprintf(modname, "%s_%d", colname, ++i); } while (!colname_is_unique(modname, dpns, colinfo)); colname = modname; } return colname; }
static void make_ruledef | ( | StringInfo | buf, | |
HeapTuple | ruletup, | |||
TupleDesc | rulettc, | |||
int | prettyFlags | |||
) | [static] |
Definition at line 3710 of file ruleutils.c.
References AcquireRewriteLocks(), appendStringInfo(), appendStringInfoString(), Assert, deparse_context::buf, DatumGetBool, DatumGetChar, DatumGetInt16, DatumGetName, DatumGetObjectId, ereport, errcode(), errmsg(), ERROR, generate_relation_name(), get_query_def(), get_relid_attribute_name(), get_rule_expr(), getInsertSelectQuery(), deparse_context::indentLevel, lfirst, linitial, list_length(), list_make1, deparse_context::namespaces, NameStr, NIL, NULL, PRETTYFLAG_INDENT, deparse_context::prettyFlags, quote_identifier(), Query::rtable, set_deparse_for_query(), SPI_fnumber(), SPI_getbinval(), SPI_getvalue(), stringToNode(), deparse_context::varprefix, deparse_context::windowClause, deparse_context::windowTList, WRAP_COLUMN_DEFAULT, and deparse_context::wrapColumn.
Referenced by pg_get_ruledef_worker().
{ char *rulename; char ev_type; Oid ev_class; int16 ev_attr; bool is_instead; char *ev_qual; char *ev_action; List *actions = NIL; int fno; Datum dat; bool isnull; /* * Get the attribute values from the rules tuple */ fno = SPI_fnumber(rulettc, "rulename"); dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); Assert(!isnull); rulename = NameStr(*(DatumGetName(dat))); fno = SPI_fnumber(rulettc, "ev_type"); dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); Assert(!isnull); ev_type = DatumGetChar(dat); fno = SPI_fnumber(rulettc, "ev_class"); dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); Assert(!isnull); ev_class = DatumGetObjectId(dat); fno = SPI_fnumber(rulettc, "ev_attr"); dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); Assert(!isnull); ev_attr = DatumGetInt16(dat); fno = SPI_fnumber(rulettc, "is_instead"); dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); Assert(!isnull); is_instead = DatumGetBool(dat); /* these could be nulls */ fno = SPI_fnumber(rulettc, "ev_qual"); ev_qual = SPI_getvalue(ruletup, rulettc, fno); fno = SPI_fnumber(rulettc, "ev_action"); ev_action = SPI_getvalue(ruletup, rulettc, fno); if (ev_action != NULL) actions = (List *) stringToNode(ev_action); /* * Build the rules definition text */ appendStringInfo(buf, "CREATE RULE %s AS", quote_identifier(rulename)); if (prettyFlags & PRETTYFLAG_INDENT) appendStringInfoString(buf, "\n ON "); else appendStringInfoString(buf, " ON "); /* The event the rule is fired for */ switch (ev_type) { case '1': appendStringInfo(buf, "SELECT"); break; case '2': appendStringInfo(buf, "UPDATE"); break; case '3': appendStringInfo(buf, "INSERT"); break; case '4': appendStringInfo(buf, "DELETE"); break; default: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("rule \"%s\" has unsupported event type %d", rulename, ev_type))); break; } /* The relation the rule is fired on */ appendStringInfo(buf, " TO %s", generate_relation_name(ev_class, NIL)); if (ev_attr > 0) appendStringInfo(buf, ".%s", quote_identifier(get_relid_attribute_name(ev_class, ev_attr))); /* If the rule has an event qualification, add it */ if (ev_qual == NULL) ev_qual = ""; if (strlen(ev_qual) > 0 && strcmp(ev_qual, "<>") != 0) { Node *qual; Query *query; deparse_context context; deparse_namespace dpns; if (prettyFlags & PRETTYFLAG_INDENT) appendStringInfoString(buf, "\n "); appendStringInfo(buf, " WHERE "); qual = stringToNode(ev_qual); /* * We need to make a context for recognizing any Vars in the qual * (which can only be references to OLD and NEW). Use the rtable of * the first query in the action list for this purpose. */ query = (Query *) linitial(actions); /* * If the action is INSERT...SELECT, OLD/NEW have been pushed down * into the SELECT, and that's what we need to look at. (Ugly kluge * ... try to fix this when we redesign querytrees.) */ query = getInsertSelectQuery(query, NULL); /* Must acquire locks right away; see notes in get_query_def() */ AcquireRewriteLocks(query, false); context.buf = buf; context.namespaces = list_make1(&dpns); context.windowClause = NIL; context.windowTList = NIL; context.varprefix = (list_length(query->rtable) != 1); context.prettyFlags = prettyFlags; context.wrapColumn = WRAP_COLUMN_DEFAULT; context.indentLevel = PRETTYINDENT_STD; set_deparse_for_query(&dpns, query, NIL); get_rule_expr(qual, &context, false); } appendStringInfo(buf, " DO "); /* The INSTEAD keyword (if so) */ if (is_instead) appendStringInfo(buf, "INSTEAD "); /* Finally the rules actions */ if (list_length(actions) > 1) { ListCell *action; Query *query; appendStringInfo(buf, "("); foreach(action, actions) { query = (Query *) lfirst(action); get_query_def(query, buf, NIL, NULL, prettyFlags, WRAP_COLUMN_DEFAULT, 0); if (prettyFlags) appendStringInfo(buf, ";\n"); else appendStringInfo(buf, "; "); } appendStringInfo(buf, ");"); } else if (list_length(actions) == 0) { appendStringInfo(buf, "NOTHING;"); } else { Query *query; query = (Query *) linitial(actions); get_query_def(query, buf, NIL, NULL, prettyFlags, WRAP_COLUMN_DEFAULT, 0); appendStringInfo(buf, ";"); } }
static void make_viewdef | ( | StringInfo | buf, | |
HeapTuple | ruletup, | |||
TupleDesc | rulettc, | |||
int | prettyFlags, | |||
int | wrapColumn | |||
) | [static] |
Definition at line 3901 of file ruleutils.c.
References AccessShareLock, appendStringInfo(), Query::commandType, get_query_def(), heap_close, heap_open(), linitial, list_length(), NIL, NULL, RelationGetDescr, SPI_fnumber(), SPI_getbinval(), SPI_getvalue(), and stringToNode().
Referenced by pg_get_viewdef_worker().
{ Query *query; char ev_type; Oid ev_class; int16 ev_attr; bool is_instead; char *ev_qual; char *ev_action; List *actions = NIL; Relation ev_relation; int fno; bool isnull; /* * Get the attribute values from the rules tuple */ fno = SPI_fnumber(rulettc, "ev_type"); ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull); fno = SPI_fnumber(rulettc, "ev_class"); ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull); fno = SPI_fnumber(rulettc, "ev_attr"); ev_attr = (int16) SPI_getbinval(ruletup, rulettc, fno, &isnull); fno = SPI_fnumber(rulettc, "is_instead"); is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull); fno = SPI_fnumber(rulettc, "ev_qual"); ev_qual = SPI_getvalue(ruletup, rulettc, fno); fno = SPI_fnumber(rulettc, "ev_action"); ev_action = SPI_getvalue(ruletup, rulettc, fno); if (ev_action != NULL) actions = (List *) stringToNode(ev_action); if (list_length(actions) != 1) { appendStringInfo(buf, "Not a view"); return; } query = (Query *) linitial(actions); if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT) { appendStringInfo(buf, "Not a view"); return; } ev_relation = heap_open(ev_class, AccessShareLock); get_query_def(query, buf, NIL, RelationGetDescr(ev_relation), prettyFlags, wrapColumn, 0); appendStringInfo(buf, ";"); heap_close(ev_relation, AccessShareLock); }
Datum pg_get_constraintdef | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1253 of file ruleutils.c.
References pg_get_constraintdef_worker(), PG_GETARG_OID, PG_RETURN_TEXT_P, and string_to_text().
{ Oid constraintId = PG_GETARG_OID(0); int prettyFlags; prettyFlags = PRETTYFLAG_INDENT; PG_RETURN_TEXT_P(string_to_text(pg_get_constraintdef_worker(constraintId, false, prettyFlags))); }
Datum pg_get_constraintdef_ext | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1265 of file ruleutils.c.
References pg_get_constraintdef_worker(), PG_GETARG_BOOL, PG_GETARG_OID, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, PRETTYFLAG_PAREN, and string_to_text().
{ Oid constraintId = PG_GETARG_OID(0); bool pretty = PG_GETARG_BOOL(1); int prettyFlags; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT; PG_RETURN_TEXT_P(string_to_text(pg_get_constraintdef_worker(constraintId, false, prettyFlags))); }
char* pg_get_constraintdef_string | ( | Oid | constraintId | ) |
Definition at line 1279 of file ruleutils.c.
References pg_get_constraintdef_worker().
Referenced by ATExecAlterColumnType().
{ return pg_get_constraintdef_worker(constraintId, true, 0); }
static char * pg_get_constraintdef_worker | ( | Oid | constraintId, | |
bool | fullCommand, | |||
int | prettyFlags | |||
) | [static] |
Definition at line 1285 of file ruleutils.c.
References Anum_pg_constraint_conbin, Anum_pg_constraint_conexclop, Anum_pg_constraint_confkey, Anum_pg_constraint_conkey, appendStringInfo(), appendStringInfoString(), buf, CONSTRAINT_CHECK, CONSTRAINT_EXCLUSION, CONSTRAINT_FOREIGN, CONSTRAINT_PRIMARY, CONSTRAINT_TRIGGER, CONSTRAINT_UNIQUE, CONSTROID, StringInfoData::data, DatumGetArrayTypeP, DatumGetObjectId, decompile_column_index_array(), deconstruct_array(), deparse_context_for(), deparse_expression_pretty(), elog, ERROR, 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_relation_name(), get_constraint_index(), get_rel_tablespace(), get_relation_name(), get_tablespace_name(), GETSTRUCT, HeapTupleIsValid, i, initStringInfo(), InvalidOid, NameStr, NIL, NULL, ObjectIdGetDatum, OidIsValid, OIDOID, palloc(), pfree(), pg_get_indexdef_worker(), quote_identifier(), ReleaseSysCache(), SearchSysCache1, stringToNode(), SysCacheGetAttr(), TextDatumGetCString, and val.
Referenced by pg_get_constraintdef(), pg_get_constraintdef_ext(), and pg_get_constraintdef_string().
{ HeapTuple tup; Form_pg_constraint conForm; StringInfoData buf; tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintId)); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for constraint %u", constraintId); conForm = (Form_pg_constraint) GETSTRUCT(tup); initStringInfo(&buf); if (fullCommand && OidIsValid(conForm->conrelid)) { appendStringInfo(&buf, "ALTER TABLE ONLY %s ADD CONSTRAINT %s ", generate_relation_name(conForm->conrelid, NIL), quote_identifier(NameStr(conForm->conname))); } switch (conForm->contype) { case CONSTRAINT_FOREIGN: { Datum val; bool isnull; const char *string; /* Start off the constraint definition */ appendStringInfo(&buf, "FOREIGN KEY ("); /* Fetch and build referencing-column list */ val = SysCacheGetAttr(CONSTROID, tup, Anum_pg_constraint_conkey, &isnull); if (isnull) elog(ERROR, "null conkey for constraint %u", constraintId); decompile_column_index_array(val, conForm->conrelid, &buf); /* add foreign relation name */ appendStringInfo(&buf, ") REFERENCES %s(", generate_relation_name(conForm->confrelid, NIL)); /* Fetch and build referenced-column list */ val = SysCacheGetAttr(CONSTROID, tup, Anum_pg_constraint_confkey, &isnull); if (isnull) elog(ERROR, "null confkey for constraint %u", constraintId); decompile_column_index_array(val, conForm->confrelid, &buf); appendStringInfo(&buf, ")"); /* Add match type */ switch (conForm->confmatchtype) { case FKCONSTR_MATCH_FULL: string = " MATCH FULL"; break; case FKCONSTR_MATCH_PARTIAL: string = " MATCH PARTIAL"; break; case FKCONSTR_MATCH_SIMPLE: string = ""; break; default: elog(ERROR, "unrecognized confmatchtype: %d", conForm->confmatchtype); string = ""; /* keep compiler quiet */ break; } appendStringInfoString(&buf, string); /* Add ON UPDATE and ON DELETE clauses, if needed */ switch (conForm->confupdtype) { case FKCONSTR_ACTION_NOACTION: string = NULL; /* suppress default */ break; case FKCONSTR_ACTION_RESTRICT: string = "RESTRICT"; break; case FKCONSTR_ACTION_CASCADE: string = "CASCADE"; break; case FKCONSTR_ACTION_SETNULL: string = "SET NULL"; break; case FKCONSTR_ACTION_SETDEFAULT: string = "SET DEFAULT"; break; default: elog(ERROR, "unrecognized confupdtype: %d", conForm->confupdtype); string = NULL; /* keep compiler quiet */ break; } if (string) appendStringInfo(&buf, " ON UPDATE %s", string); switch (conForm->confdeltype) { case FKCONSTR_ACTION_NOACTION: string = NULL; /* suppress default */ break; case FKCONSTR_ACTION_RESTRICT: string = "RESTRICT"; break; case FKCONSTR_ACTION_CASCADE: string = "CASCADE"; break; case FKCONSTR_ACTION_SETNULL: string = "SET NULL"; break; case FKCONSTR_ACTION_SETDEFAULT: string = "SET DEFAULT"; break; default: elog(ERROR, "unrecognized confdeltype: %d", conForm->confdeltype); string = NULL; /* keep compiler quiet */ break; } if (string) appendStringInfo(&buf, " ON DELETE %s", string); break; } case CONSTRAINT_PRIMARY: case CONSTRAINT_UNIQUE: { Datum val; bool isnull; Oid indexId; /* Start off the constraint definition */ if (conForm->contype == CONSTRAINT_PRIMARY) appendStringInfo(&buf, "PRIMARY KEY ("); else appendStringInfo(&buf, "UNIQUE ("); /* Fetch and build target column list */ val = SysCacheGetAttr(CONSTROID, tup, Anum_pg_constraint_conkey, &isnull); if (isnull) elog(ERROR, "null conkey for constraint %u", constraintId); decompile_column_index_array(val, conForm->conrelid, &buf); appendStringInfo(&buf, ")"); indexId = get_constraint_index(constraintId); /* XXX why do we only print these bits if fullCommand? */ if (fullCommand && OidIsValid(indexId)) { char *options = flatten_reloptions(indexId); Oid tblspc; if (options) { appendStringInfo(&buf, " WITH (%s)", options); pfree(options); } tblspc = get_rel_tablespace(indexId); if (OidIsValid(tblspc)) appendStringInfo(&buf, " USING INDEX TABLESPACE %s", quote_identifier(get_tablespace_name(tblspc))); } break; } case CONSTRAINT_CHECK: { Datum val; bool isnull; char *conbin; char *consrc; Node *expr; List *context; /* Fetch constraint expression in parsetree form */ val = SysCacheGetAttr(CONSTROID, tup, Anum_pg_constraint_conbin, &isnull); if (isnull) elog(ERROR, "null conbin for constraint %u", constraintId); conbin = TextDatumGetCString(val); expr = stringToNode(conbin); /* Set up deparsing context for Var nodes in constraint */ if (conForm->conrelid != InvalidOid) { /* relation constraint */ context = deparse_context_for(get_relation_name(conForm->conrelid), conForm->conrelid); } else { /* domain constraint --- can't have Vars */ context = NIL; } consrc = deparse_expression_pretty(expr, context, false, false, prettyFlags, 0); /* * Now emit the constraint definition, adding NO INHERIT if * necessary. * * There are cases where the constraint expression will be * fully parenthesized and we don't need the outer parens ... * but there are other cases where we do need 'em. Be * conservative for now. * * Note that simply checking for leading '(' and trailing ')' * would NOT be good enough, consider "(x > 0) AND (y > 0)". */ appendStringInfo(&buf, "CHECK (%s)%s", consrc, conForm->connoinherit ? " NO INHERIT" : ""); break; } case CONSTRAINT_TRIGGER: /* * There isn't an ALTER TABLE syntax for creating a user-defined * constraint trigger, but it seems better to print something than * throw an error; if we throw error then this function couldn't * safely be applied to all rows of pg_constraint. */ appendStringInfo(&buf, "TRIGGER"); break; case CONSTRAINT_EXCLUSION: { Oid indexOid = conForm->conindid; Datum val; bool isnull; Datum *elems; int nElems; int i; Oid *operators; /* Extract operator OIDs from the pg_constraint tuple */ val = SysCacheGetAttr(CONSTROID, tup, Anum_pg_constraint_conexclop, &isnull); if (isnull) elog(ERROR, "null conexclop for constraint %u", constraintId); deconstruct_array(DatumGetArrayTypeP(val), OIDOID, sizeof(Oid), true, 'i', &elems, NULL, &nElems); operators = (Oid *) palloc(nElems * sizeof(Oid)); for (i = 0; i < nElems; i++) operators[i] = DatumGetObjectId(elems[i]); /* pg_get_indexdef_worker does the rest */ /* suppress tablespace because pg_dump wants it that way */ appendStringInfoString(&buf, pg_get_indexdef_worker(indexOid, 0, operators, false, false, prettyFlags)); break; } default: elog(ERROR, "invalid constraint type \"%c\"", conForm->contype); break; } if (conForm->condeferrable) appendStringInfo(&buf, " DEFERRABLE"); if (conForm->condeferred) appendStringInfo(&buf, " INITIALLY DEFERRED"); if (!conForm->convalidated) appendStringInfoString(&buf, " NOT VALID"); /* Cleanup */ ReleaseSysCache(tup); return buf.data; }
Datum pg_get_expr | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1626 of file ruleutils.c.
References get_rel_name(), NULL, OidIsValid, pg_get_expr_worker(), PG_GETARG_OID, PG_GETARG_TEXT_P, PG_RETURN_NULL, and PG_RETURN_TEXT_P.
Referenced by decompile_conbin().
{ text *expr = PG_GETARG_TEXT_P(0); Oid relid = PG_GETARG_OID(1); int prettyFlags; char *relname; prettyFlags = PRETTYFLAG_INDENT; if (OidIsValid(relid)) { /* Get the name for the relation */ relname = get_rel_name(relid); /* * If the OID isn't actually valid, don't throw an error, just return * NULL. This is a bit questionable, but it's what we've done * historically, and it can help avoid unwanted failures when * examining catalog entries for just-deleted relations. */ if (relname == NULL) PG_RETURN_NULL(); } else relname = NULL; PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, prettyFlags)); }
Datum pg_get_expr_ext | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1656 of file ruleutils.c.
References get_rel_name(), NULL, OidIsValid, pg_get_expr_worker(), PG_GETARG_BOOL, PG_GETARG_OID, PG_GETARG_TEXT_P, PG_RETURN_NULL, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, and PRETTYFLAG_PAREN.
{ text *expr = PG_GETARG_TEXT_P(0); Oid relid = PG_GETARG_OID(1); bool pretty = PG_GETARG_BOOL(2); int prettyFlags; char *relname; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT; if (OidIsValid(relid)) { /* Get the name for the relation */ relname = get_rel_name(relid); /* See notes above */ if (relname == NULL) PG_RETURN_NULL(); } else relname = NULL; PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, prettyFlags)); }
static text * pg_get_expr_worker | ( | text * | expr, | |
Oid | relid, | |||
const char * | relname, | |||
int | prettyFlags | |||
) | [static] |
Definition at line 1681 of file ruleutils.c.
References deparse_context_for(), deparse_expression_pretty(), OidIsValid, pfree(), string_to_text(), stringToNode(), and text_to_cstring().
Referenced by pg_get_expr(), and pg_get_expr_ext().
{ Node *node; List *context; char *exprstr; char *str; /* Convert input TEXT object to C string */ exprstr = text_to_cstring(expr); /* Convert expression to node tree */ node = (Node *) stringToNode(exprstr); pfree(exprstr); /* Prepare deparse context if needed */ if (OidIsValid(relid)) context = deparse_context_for(relname, relid); else context = NIL; /* Deparse */ str = deparse_expression_pretty(node, context, false, false, prettyFlags, 0); return string_to_text(str); }
Datum pg_get_function_arguments | ( | PG_FUNCTION_ARGS | ) |
Definition at line 2047 of file ruleutils.c.
References buf, StringInfoData::data, elog, ERROR, HeapTupleIsValid, initStringInfo(), ObjectIdGetDatum, PG_GETARG_OID, PG_RETURN_TEXT_P, print_function_arguments(), PROCOID, ReleaseSysCache(), SearchSysCache1, and string_to_text().
{ Oid funcid = PG_GETARG_OID(0); StringInfoData buf; HeapTuple proctup; initStringInfo(&buf); proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(proctup)) elog(ERROR, "cache lookup failed for function %u", funcid); (void) print_function_arguments(&buf, proctup, false, true); ReleaseSysCache(proctup); PG_RETURN_TEXT_P(string_to_text(buf.data)); }
Datum pg_get_function_identity_arguments | ( | PG_FUNCTION_ARGS | ) |
Definition at line 2073 of file ruleutils.c.
References buf, StringInfoData::data, elog, ERROR, HeapTupleIsValid, initStringInfo(), ObjectIdGetDatum, PG_GETARG_OID, PG_RETURN_TEXT_P, print_function_arguments(), PROCOID, ReleaseSysCache(), SearchSysCache1, and string_to_text().
{ Oid funcid = PG_GETARG_OID(0); StringInfoData buf; HeapTuple proctup; initStringInfo(&buf); proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(proctup)) elog(ERROR, "cache lookup failed for function %u", funcid); (void) print_function_arguments(&buf, proctup, false, false); ReleaseSysCache(proctup); PG_RETURN_TEXT_P(string_to_text(buf.data)); }
Datum pg_get_function_result | ( | PG_FUNCTION_ARGS | ) |
Definition at line 2098 of file ruleutils.c.
References buf, StringInfoData::data, elog, ERROR, HeapTupleIsValid, initStringInfo(), ObjectIdGetDatum, PG_GETARG_OID, PG_RETURN_TEXT_P, print_function_rettype(), PROCOID, ReleaseSysCache(), SearchSysCache1, and string_to_text().
{ Oid funcid = PG_GETARG_OID(0); StringInfoData buf; HeapTuple proctup; initStringInfo(&buf); proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(proctup)) elog(ERROR, "cache lookup failed for function %u", funcid); print_function_rettype(&buf, proctup); ReleaseSysCache(proctup); PG_RETURN_TEXT_P(string_to_text(buf.data)); }
Datum pg_get_functiondef | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1866 of file ruleutils.c.
References Anum_pg_proc_probin, Anum_pg_proc_proconfig, Anum_pg_proc_prosrc, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), ARR_DIMS, ARR_ELEMTYPE, ARR_LBOUND, ARR_NDIM, array_ref(), Assert, buf, ClanguageId, StringInfoData::data, DatumGetArrayTypeP, elog, ereport, errcode(), errmsg(), ERROR, get_namespace_name(), GETSTRUCT, HeapTupleIsValid, i, initStringInfo(), INTERNALlanguageId, LANGOID, StringInfoData::len, name, NameStr, NULL, ObjectIdGetDatum, PG_GETARG_OID, PG_RETURN_TEXT_P, pg_strcasecmp(), print_function_arguments(), print_function_rettype(), PROCOID, PROVOLATILE_IMMUTABLE, PROVOLATILE_STABLE, PROVOLATILE_VOLATILE, quote_identifier(), quote_qualified_identifier(), ReleaseSysCache(), SearchSysCache1, simple_quote_literal(), string_to_text(), SysCacheGetAttr(), TextDatumGetCString, and TEXTOID.
{ Oid funcid = PG_GETARG_OID(0); StringInfoData buf; StringInfoData dq; HeapTuple proctup; HeapTuple langtup; Form_pg_proc proc; Form_pg_language lang; Datum tmp; bool isnull; const char *prosrc; const char *name; const char *nsp; float4 procost; int oldlen; initStringInfo(&buf); /* Look up the function */ proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(proctup)) elog(ERROR, "cache lookup failed for function %u", funcid); proc = (Form_pg_proc) GETSTRUCT(proctup); name = NameStr(proc->proname); if (proc->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an aggregate function", name))); /* Need its pg_language tuple for the language name */ langtup = SearchSysCache1(LANGOID, ObjectIdGetDatum(proc->prolang)); if (!HeapTupleIsValid(langtup)) elog(ERROR, "cache lookup failed for language %u", proc->prolang); lang = (Form_pg_language) GETSTRUCT(langtup); /* * We always qualify the function name, to ensure the right function gets * replaced. */ nsp = get_namespace_name(proc->pronamespace); appendStringInfo(&buf, "CREATE OR REPLACE FUNCTION %s(", quote_qualified_identifier(nsp, name)); (void) print_function_arguments(&buf, proctup, false, true); appendStringInfoString(&buf, ")\n RETURNS "); print_function_rettype(&buf, proctup); appendStringInfo(&buf, "\n LANGUAGE %s\n", quote_identifier(NameStr(lang->lanname))); /* Emit some miscellaneous options on one line */ oldlen = buf.len; if (proc->proiswindow) appendStringInfoString(&buf, " WINDOW"); switch (proc->provolatile) { case PROVOLATILE_IMMUTABLE: appendStringInfoString(&buf, " IMMUTABLE"); break; case PROVOLATILE_STABLE: appendStringInfoString(&buf, " STABLE"); break; case PROVOLATILE_VOLATILE: break; } if (proc->proisstrict) appendStringInfoString(&buf, " STRICT"); if (proc->prosecdef) appendStringInfoString(&buf, " SECURITY DEFINER"); /* This code for the default cost and rows should match functioncmds.c */ if (proc->prolang == INTERNALlanguageId || proc->prolang == ClanguageId) procost = 1; else procost = 100; if (proc->procost != procost) appendStringInfo(&buf, " COST %g", proc->procost); if (proc->prorows > 0 && proc->prorows != 1000) appendStringInfo(&buf, " ROWS %g", proc->prorows); if (oldlen != buf.len) appendStringInfoChar(&buf, '\n'); /* Emit any proconfig options, one per line */ tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull); if (!isnull) { ArrayType *a = DatumGetArrayTypeP(tmp); int i; Assert(ARR_ELEMTYPE(a) == TEXTOID); Assert(ARR_NDIM(a) == 1); Assert(ARR_LBOUND(a)[0] == 1); for (i = 1; i <= ARR_DIMS(a)[0]; i++) { Datum d; d = array_ref(a, 1, &i, -1 /* varlenarray */ , -1 /* TEXT's typlen */ , false /* TEXT's typbyval */ , 'i' /* TEXT's typalign */ , &isnull); if (!isnull) { char *configitem = TextDatumGetCString(d); char *pos; pos = strchr(configitem, '='); if (pos == NULL) continue; *pos++ = '\0'; appendStringInfo(&buf, " SET %s TO ", quote_identifier(configitem)); /* * Some GUC variable names are 'LIST' type and hence must not * be quoted. */ if (pg_strcasecmp(configitem, "DateStyle") == 0 || pg_strcasecmp(configitem, "search_path") == 0) appendStringInfoString(&buf, pos); else simple_quote_literal(&buf, pos); appendStringInfoChar(&buf, '\n'); } } } /* And finally the function definition ... */ appendStringInfoString(&buf, "AS "); tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull); if (!isnull) { simple_quote_literal(&buf, TextDatumGetCString(tmp)); appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */ } tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); prosrc = TextDatumGetCString(tmp); /* * We always use dollar quoting. Figure out a suitable delimiter. * * Since the user is likely to be editing the function body string, we * shouldn't use a short delimiter that he might easily create a conflict * with. Hence prefer "$function$", but extend if needed. */ initStringInfo(&dq); appendStringInfoString(&dq, "$function"); while (strstr(prosrc, dq.data) != NULL) appendStringInfoChar(&dq, 'x'); appendStringInfoChar(&dq, '$'); appendStringInfoString(&buf, dq.data); appendStringInfoString(&buf, prosrc); appendStringInfoString(&buf, dq.data); appendStringInfoString(&buf, "\n"); ReleaseSysCache(langtup); ReleaseSysCache(proctup); PG_RETURN_TEXT_P(string_to_text(buf.data)); }
Datum pg_get_indexdef | ( | PG_FUNCTION_ARGS | ) |
Definition at line 916 of file ruleutils.c.
References NULL, pg_get_indexdef_worker(), PG_GETARG_OID, PG_RETURN_TEXT_P, and string_to_text().
{ Oid indexrelid = PG_GETARG_OID(0); int prettyFlags; prettyFlags = PRETTYFLAG_INDENT; PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, 0, NULL, false, false, prettyFlags))); }
Definition at line 953 of file ruleutils.c.
References NULL, pg_get_indexdef_worker(), PRETTYFLAG_INDENT, and PRETTYFLAG_PAREN.
Referenced by BuildIndexValueDescription().
{ int prettyFlags; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT; return pg_get_indexdef_worker(indexrelid, 0, NULL, true, false, prettyFlags); }
Datum pg_get_indexdef_ext | ( | PG_FUNCTION_ARGS | ) |
Definition at line 929 of file ruleutils.c.
References NULL, pg_get_indexdef_worker(), PG_GETARG_BOOL, PG_GETARG_INT32, PG_GETARG_OID, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, PRETTYFLAG_PAREN, and string_to_text().
{ Oid indexrelid = PG_GETARG_OID(0); int32 colno = PG_GETARG_INT32(1); bool pretty = PG_GETARG_BOOL(2); int prettyFlags; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT; PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno, NULL, colno != 0, false, prettyFlags))); }
char* pg_get_indexdef_string | ( | Oid | indexrelid | ) |
Definition at line 946 of file ruleutils.c.
References NULL, and pg_get_indexdef_worker().
Referenced by ATExecAlterColumnType().
{ return pg_get_indexdef_worker(indexrelid, 0, NULL, false, true, 0); }
static char * pg_get_indexdef_worker | ( | Oid | indexrelid, | |
int | colno, | |||
const Oid * | excludeOps, | |||
bool | attrsOnly, | |||
bool | showTblSpc, | |||
int | prettyFlags | |||
) | [static] |
Definition at line 968 of file ruleutils.c.
References AMOID, Anum_pg_index_indclass, Anum_pg_index_indcollation, Anum_pg_index_indexprs, Anum_pg_index_indoption, Anum_pg_index_indpred, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, buf, COERCE_EXPLICIT_CALL, StringInfoData::data, DatumGetPointer, deparse_context_for(), deparse_expression_pretty(), elog, ERROR, exprCollation(), exprType(), flatten_reloptions(), generate_collation_name(), generate_operator_name(), generate_relation_name(), get_atttypetypmodcoll(), get_opclass_name(), get_rel_tablespace(), get_relation_name(), get_relid_attribute_name(), get_tablespace_name(), GETSTRUCT, heap_attisnull(), HeapTupleIsValid, INDEXRELID, INDOPTION_DESC, INDOPTION_NULLS_FIRST, initStringInfo(), IsA, lfirst, list_head(), lnext, NameStr, NIL, NULL, ObjectIdGetDatum, OidIsValid, pfree(), quote_identifier(), ReleaseSysCache(), RELOID, SearchSysCache1, stringToNode(), SysCacheGetAttr(), TextDatumGetCString, oidvector::values, and int2vector::values.
Referenced by pg_get_constraintdef_worker(), pg_get_indexdef(), pg_get_indexdef_columns(), pg_get_indexdef_ext(), and pg_get_indexdef_string().
{ /* might want a separate isConstraint parameter later */ bool isConstraint = (excludeOps != NULL); HeapTuple ht_idx; HeapTuple ht_idxrel; HeapTuple ht_am; Form_pg_index idxrec; Form_pg_class idxrelrec; Form_pg_am amrec; List *indexprs; ListCell *indexpr_item; List *context; Oid indrelid; int keyno; Datum indcollDatum; Datum indclassDatum; Datum indoptionDatum; bool isnull; oidvector *indcollation; oidvector *indclass; int2vector *indoption; StringInfoData buf; char *str; char *sep; /* * Fetch the pg_index tuple by the Oid of the index */ ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid)); if (!HeapTupleIsValid(ht_idx)) elog(ERROR, "cache lookup failed for index %u", indexrelid); idxrec = (Form_pg_index) GETSTRUCT(ht_idx); indrelid = idxrec->indrelid; Assert(indexrelid == idxrec->indexrelid); /* Must get indcollation, indclass, and indoption the hard way */ indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indcollation, &isnull); Assert(!isnull); indcollation = (oidvector *) DatumGetPointer(indcollDatum); indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indclass, &isnull); Assert(!isnull); indclass = (oidvector *) DatumGetPointer(indclassDatum); indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indoption, &isnull); Assert(!isnull); indoption = (int2vector *) DatumGetPointer(indoptionDatum); /* * Fetch the pg_class tuple of the index relation */ ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid)); if (!HeapTupleIsValid(ht_idxrel)) elog(ERROR, "cache lookup failed for relation %u", indexrelid); idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel); /* * Fetch the pg_am tuple of the index' access method */ ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam)); if (!HeapTupleIsValid(ht_am)) elog(ERROR, "cache lookup failed for access method %u", idxrelrec->relam); amrec = (Form_pg_am) GETSTRUCT(ht_am); /* * Get the index expressions, if any. (NOTE: we do not use the relcache * versions of the expressions and predicate, because we want to display * non-const-folded expressions.) */ if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs)) { Datum exprsDatum; bool isnull; char *exprsString; exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indexprs, &isnull); Assert(!isnull); exprsString = TextDatumGetCString(exprsDatum); indexprs = (List *) stringToNode(exprsString); pfree(exprsString); } else indexprs = NIL; indexpr_item = list_head(indexprs); context = deparse_context_for(get_relation_name(indrelid), indrelid); /* * Start the index definition. Note that the index's name should never be * schema-qualified, but the indexed rel's name may be. */ initStringInfo(&buf); if (!attrsOnly) { if (!isConstraint) appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (", idxrec->indisunique ? "UNIQUE " : "", quote_identifier(NameStr(idxrelrec->relname)), generate_relation_name(indrelid, NIL), quote_identifier(NameStr(amrec->amname))); else /* currently, must be EXCLUDE constraint */ appendStringInfo(&buf, "EXCLUDE USING %s (", quote_identifier(NameStr(amrec->amname))); } /* * Report the indexed attributes */ sep = ""; for (keyno = 0; keyno < idxrec->indnatts; keyno++) { AttrNumber attnum = idxrec->indkey.values[keyno]; int16 opt = indoption->values[keyno]; Oid keycoltype; Oid keycolcollation; if (!colno) appendStringInfoString(&buf, sep); sep = ", "; if (attnum != 0) { /* Simple index column */ char *attname; int32 keycoltypmod; attname = get_relid_attribute_name(indrelid, attnum); if (!colno || colno == keyno + 1) appendStringInfoString(&buf, quote_identifier(attname)); get_atttypetypmodcoll(indrelid, attnum, &keycoltype, &keycoltypmod, &keycolcollation); } else { /* expressional index */ Node *indexkey; if (indexpr_item == NULL) elog(ERROR, "too few entries in indexprs list"); indexkey = (Node *) lfirst(indexpr_item); indexpr_item = lnext(indexpr_item); /* Deparse */ str = deparse_expression_pretty(indexkey, context, false, false, prettyFlags, 0); if (!colno || colno == keyno + 1) { /* Need parens if it's not a bare function call */ if (indexkey && IsA(indexkey, FuncExpr) && ((FuncExpr *) indexkey)->funcformat == COERCE_EXPLICIT_CALL) appendStringInfoString(&buf, str); else appendStringInfo(&buf, "(%s)", str); } keycoltype = exprType(indexkey); keycolcollation = exprCollation(indexkey); } if (!attrsOnly && (!colno || colno == keyno + 1)) { Oid indcoll; /* Add collation, if not default for column */ indcoll = indcollation->values[keyno]; if (OidIsValid(indcoll) && indcoll != keycolcollation) appendStringInfo(&buf, " COLLATE %s", generate_collation_name((indcoll))); /* Add the operator class name, if not default */ get_opclass_name(indclass->values[keyno], keycoltype, &buf); /* Add options if relevant */ if (amrec->amcanorder) { /* if it supports sort ordering, report DESC and NULLS opts */ if (opt & INDOPTION_DESC) { appendStringInfo(&buf, " DESC"); /* NULLS FIRST is the default in this case */ if (!(opt & INDOPTION_NULLS_FIRST)) appendStringInfo(&buf, " NULLS LAST"); } else { if (opt & INDOPTION_NULLS_FIRST) appendStringInfo(&buf, " NULLS FIRST"); } } /* Add the exclusion operator if relevant */ if (excludeOps != NULL) appendStringInfo(&buf, " WITH %s", generate_operator_name(excludeOps[keyno], keycoltype, keycoltype)); } } if (!attrsOnly) { appendStringInfoChar(&buf, ')'); /* * If it has options, append "WITH (options)" */ str = flatten_reloptions(indexrelid); if (str) { appendStringInfo(&buf, " WITH (%s)", str); pfree(str); } /* * If it's in a nondefault tablespace, say so, but only if requested */ if (showTblSpc) { Oid tblspc; tblspc = get_rel_tablespace(indexrelid); if (OidIsValid(tblspc)) { if (isConstraint) appendStringInfoString(&buf, " USING INDEX"); appendStringInfo(&buf, " TABLESPACE %s", quote_identifier(get_tablespace_name(tblspc))); } } /* * If it's a partial index, decompile and append the predicate */ if (!heap_attisnull(ht_idx, Anum_pg_index_indpred)) { Node *node; Datum predDatum; bool isnull; char *predString; /* Convert text string to node tree */ predDatum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indpred, &isnull); Assert(!isnull); predString = TextDatumGetCString(predDatum); node = (Node *) stringToNode(predString); pfree(predString); /* Deparse */ str = deparse_expression_pretty(node, context, false, false, prettyFlags, 0); if (isConstraint) appendStringInfo(&buf, " WHERE (%s)", str); else appendStringInfo(&buf, " WHERE %s", str); } } /* Clean up */ ReleaseSysCache(ht_idx); ReleaseSysCache(ht_idxrel); ReleaseSysCache(ht_am); return buf.data; }
Datum pg_get_ruledef | ( | PG_FUNCTION_ARGS | ) |
Definition at line 416 of file ruleutils.c.
References pg_get_ruledef_worker(), PG_GETARG_OID, PG_RETURN_TEXT_P, and string_to_text().
{ Oid ruleoid = PG_GETARG_OID(0); int prettyFlags; prettyFlags = PRETTYFLAG_INDENT; PG_RETURN_TEXT_P(string_to_text(pg_get_ruledef_worker(ruleoid, prettyFlags))); }
Datum pg_get_ruledef_ext | ( | PG_FUNCTION_ARGS | ) |
Definition at line 427 of file ruleutils.c.
References pg_get_ruledef_worker(), PG_GETARG_BOOL, PG_GETARG_OID, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, PRETTYFLAG_PAREN, and string_to_text().
{ Oid ruleoid = PG_GETARG_OID(0); bool pretty = PG_GETARG_BOOL(1); int prettyFlags; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT; PG_RETURN_TEXT_P(string_to_text(pg_get_ruledef_worker(ruleoid, prettyFlags))); }
static char * pg_get_ruledef_worker | ( | Oid | ruleoid, | |
int | prettyFlags | |||
) | [static] |
Definition at line 439 of file ruleutils.c.
References appendStringInfo(), buf, StringInfoData::data, elog, ERROR, initStringInfo(), make_ruledef(), NULL, ObjectIdGetDatum, query_getrulebyoid, SPI_connect(), SPI_execute_plan(), SPI_finish(), SPI_keepplan(), SPI_OK_CONNECT, 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().
{ Datum args[1]; char nulls[1]; int spirc; HeapTuple ruletup; TupleDesc rulettc; StringInfoData buf; /* * Do this first so that string is alloc'd in outer context not SPI's. */ initStringInfo(&buf); /* * Connect to SPI manager */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); /* * On the first call prepare the plan to lookup pg_rewrite. We read * pg_rewrite over the SPI manager instead of using the syscache to be * checked for read access on pg_rewrite. */ if (plan_getrulebyoid == NULL) { Oid argtypes[1]; SPIPlanPtr plan; argtypes[0] = OIDOID; plan = SPI_prepare(query_getrulebyoid, 1, argtypes); if (plan == NULL) elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid); SPI_keepplan(plan); plan_getrulebyoid = plan; } /* * Get the pg_rewrite tuple for this rule */ args[0] = ObjectIdGetDatum(ruleoid); nulls[0] = ' '; spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 1); if (spirc != SPI_OK_SELECT) elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid); if (SPI_processed != 1) appendStringInfo(&buf, "-"); else { /* * Get the rule's definition and put it into executor's memory */ ruletup = SPI_tuptable->vals[0]; rulettc = SPI_tuptable->tupdesc; make_ruledef(&buf, ruletup, rulettc, prettyFlags); } /* * Disconnect from SPI manager */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); return buf.data; }
Datum pg_get_serial_sequence | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1754 of file ruleutils.c.
References AccessShareLock, Anum_pg_depend_refclassid, Anum_pg_depend_refobjid, Anum_pg_depend_refobjsubid, BTEqualStrategyNumber, DEPENDENCY_AUTO, DependReferenceIndexId, DependRelationId, elog, ereport, errcode(), errmsg(), ERROR, get_attnum(), get_namespace_name(), get_rel_relkind(), GETSTRUCT, heap_close, heap_open(), HeapTupleIsValid, Int32GetDatum, InvalidAttrNumber, makeRangeVarFromNameList(), NameStr, NoLock, ObjectIdGetDatum, OidIsValid, PG_GETARG_TEXT_P, PG_GETARG_TEXT_PP, PG_RETURN_NULL, PG_RETURN_TEXT_P, quote_qualified_identifier(), RangeVarGetRelid, RelationRelationId, ReleaseSysCache(), RangeVar::relname, RELOID, ScanKeyInit(), SearchSysCache1, SnapshotNow, string_to_text(), systable_beginscan(), systable_endscan(), systable_getnext(), text_to_cstring(), and textToQualifiedNameList().
{ text *tablename = PG_GETARG_TEXT_P(0); text *columnname = PG_GETARG_TEXT_PP(1); RangeVar *tablerv; Oid tableOid; char *column; AttrNumber attnum; Oid sequenceId = InvalidOid; Relation depRel; ScanKeyData key[3]; SysScanDesc scan; HeapTuple tup; /* Look up table name. Can't lock it - we might not have privileges. */ tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename)); tableOid = RangeVarGetRelid(tablerv, NoLock, false); /* Get the number of the column */ column = text_to_cstring(columnname); attnum = get_attnum(tableOid, column); if (attnum == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", column, tablerv->relname))); /* Search the dependency table for the dependent sequence */ depRel = heap_open(DependRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationRelationId)); ScanKeyInit(&key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(tableOid)); ScanKeyInit(&key[2], Anum_pg_depend_refobjsubid, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(attnum)); scan = systable_beginscan(depRel, DependReferenceIndexId, true, SnapshotNow, 3, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup); /* * We assume any auto dependency of a sequence on a column must be * what we are looking for. (We need the relkind test because indexes * can also have auto dependencies on columns.) */ if (deprec->classid == RelationRelationId && deprec->objsubid == 0 && deprec->deptype == DEPENDENCY_AUTO && get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE) { sequenceId = deprec->objid; break; } } systable_endscan(scan); heap_close(depRel, AccessShareLock); if (OidIsValid(sequenceId)) { HeapTuple classtup; Form_pg_class classtuple; char *nspname; char *result; /* Get the sequence's pg_class entry */ classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(sequenceId)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for relation %u", sequenceId); classtuple = (Form_pg_class) GETSTRUCT(classtup); /* Get the namespace */ nspname = get_namespace_name(classtuple->relnamespace); if (!nspname) elog(ERROR, "cache lookup failed for namespace %u", classtuple->relnamespace); /* And construct the result string */ result = quote_qualified_identifier(nspname, NameStr(classtuple->relname)); ReleaseSysCache(classtup); PG_RETURN_TEXT_P(string_to_text(result)); } PG_RETURN_NULL(); }
Datum pg_get_triggerdef | ( | PG_FUNCTION_ARGS | ) |
Definition at line 666 of file ruleutils.c.
References pg_get_triggerdef_worker(), PG_GETARG_OID, PG_RETURN_TEXT_P, and string_to_text().
{ Oid trigid = PG_GETARG_OID(0); PG_RETURN_TEXT_P(string_to_text(pg_get_triggerdef_worker(trigid, false))); }
Datum pg_get_triggerdef_ext | ( | PG_FUNCTION_ARGS | ) |
Definition at line 674 of file ruleutils.c.
References pg_get_triggerdef_worker(), PG_GETARG_BOOL, PG_GETARG_OID, PG_RETURN_TEXT_P, and string_to_text().
{ Oid trigid = PG_GETARG_OID(0); bool pretty = PG_GETARG_BOOL(1); PG_RETURN_TEXT_P(string_to_text(pg_get_triggerdef_worker(trigid, pretty))); }
Definition at line 683 of file ruleutils.c.
References AccessShareLock, RangeTblEntry::alias, Anum_pg_trigger_tgargs, Anum_pg_trigger_tgqual, appendStringInfo(), appendStringInfoString(), BTEqualStrategyNumber, deparse_context::buf, buf, deparse_namespace::ctes, StringInfoData::data, DatumGetByteaP, elog, RangeTblEntry::eref, ERROR, fastgetattr, generate_function_name(), generate_relation_name(), get_rel_relkind(), get_relid_attribute_name(), get_rule_expr(), GETSTRUCT, heap_close, heap_open(), HeapTupleIsValid, i, deparse_context::indentLevel, RangeTblEntry::inFromCl, RangeTblEntry::inh, initStringInfo(), RangeTblEntry::lateral, list_make1, list_make2, makeAlias(), makeNode, deparse_context::namespaces, NameStr, NIL, NULL, ObjectIdAttributeNumber, ObjectIdGetDatum, OidIsValid, PRETTYFLAG_INDENT, PRETTYFLAG_PAREN, deparse_context::prettyFlags, quote_identifier(), RelationData::rd_att, RangeTblEntry::relid, RangeTblEntry::relkind, deparse_namespace::rtable, RangeTblEntry::rtekind, ScanKeyInit(), set_rtable_names(), set_simple_column_names(), simple_quote_literal(), SnapshotNow, stringToNode(), systable_beginscan(), systable_endscan(), systable_getnext(), TextDatumGetCString, TRIGGER_FOR_AFTER, TRIGGER_FOR_BEFORE, TRIGGER_FOR_DELETE, TRIGGER_FOR_INSERT, TRIGGER_FOR_INSTEAD, TRIGGER_FOR_ROW, TRIGGER_FOR_TRUNCATE, TRIGGER_FOR_UPDATE, TriggerOidIndexId, TriggerRelationId, value, VARDATA, deparse_context::varprefix, deparse_context::windowClause, deparse_context::windowTList, and deparse_context::wrapColumn.
Referenced by pg_get_triggerdef(), and pg_get_triggerdef_ext().
{ HeapTuple ht_trig; Form_pg_trigger trigrec; StringInfoData buf; Relation tgrel; ScanKeyData skey[1]; SysScanDesc tgscan; int findx = 0; char *tgname; Datum value; bool isnull; /* * Fetch the pg_trigger tuple by the Oid of the trigger */ tgrel = heap_open(TriggerRelationId, AccessShareLock); ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(trigid)); tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true, SnapshotNow, 1, skey); ht_trig = systable_getnext(tgscan); if (!HeapTupleIsValid(ht_trig)) elog(ERROR, "could not find tuple for trigger %u", trigid); trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig); /* * Start the trigger definition. Note that the trigger's name should never * be schema-qualified, but the trigger rel's name may be. */ initStringInfo(&buf); tgname = NameStr(trigrec->tgname); appendStringInfo(&buf, "CREATE %sTRIGGER %s ", OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "", quote_identifier(tgname)); if (TRIGGER_FOR_BEFORE(trigrec->tgtype)) appendStringInfo(&buf, "BEFORE"); else if (TRIGGER_FOR_AFTER(trigrec->tgtype)) appendStringInfo(&buf, "AFTER"); else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype)) appendStringInfo(&buf, "INSTEAD OF"); else elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype); if (TRIGGER_FOR_INSERT(trigrec->tgtype)) { appendStringInfo(&buf, " INSERT"); findx++; } if (TRIGGER_FOR_DELETE(trigrec->tgtype)) { if (findx > 0) appendStringInfo(&buf, " OR DELETE"); else appendStringInfo(&buf, " DELETE"); findx++; } if (TRIGGER_FOR_UPDATE(trigrec->tgtype)) { if (findx > 0) appendStringInfo(&buf, " OR UPDATE"); else appendStringInfo(&buf, " UPDATE"); findx++; /* tgattr is first var-width field, so OK to access directly */ if (trigrec->tgattr.dim1 > 0) { int i; appendStringInfoString(&buf, " OF "); for (i = 0; i < trigrec->tgattr.dim1; i++) { char *attname; if (i > 0) appendStringInfoString(&buf, ", "); attname = get_relid_attribute_name(trigrec->tgrelid, trigrec->tgattr.values[i]); appendStringInfoString(&buf, quote_identifier(attname)); } } } if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype)) { if (findx > 0) appendStringInfo(&buf, " OR TRUNCATE"); else appendStringInfo(&buf, " TRUNCATE"); findx++; } appendStringInfo(&buf, " ON %s ", generate_relation_name(trigrec->tgrelid, NIL)); if (OidIsValid(trigrec->tgconstraint)) { if (OidIsValid(trigrec->tgconstrrelid)) appendStringInfo(&buf, "FROM %s ", generate_relation_name(trigrec->tgconstrrelid, NIL)); if (!trigrec->tgdeferrable) appendStringInfo(&buf, "NOT "); appendStringInfo(&buf, "DEFERRABLE INITIALLY "); if (trigrec->tginitdeferred) appendStringInfo(&buf, "DEFERRED "); else appendStringInfo(&buf, "IMMEDIATE "); } if (TRIGGER_FOR_ROW(trigrec->tgtype)) appendStringInfo(&buf, "FOR EACH ROW "); else appendStringInfo(&buf, "FOR EACH STATEMENT "); /* If the trigger has a WHEN qualification, add that */ value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual, tgrel->rd_att, &isnull); if (!isnull) { Node *qual; char relkind; deparse_context context; deparse_namespace dpns; RangeTblEntry *oldrte; RangeTblEntry *newrte; appendStringInfoString(&buf, "WHEN ("); qual = stringToNode(TextDatumGetCString(value)); relkind = get_rel_relkind(trigrec->tgrelid); /* Build minimal OLD and NEW RTEs for the rel */ oldrte = makeNode(RangeTblEntry); oldrte->rtekind = RTE_RELATION; oldrte->relid = trigrec->tgrelid; oldrte->relkind = relkind; oldrte->alias = makeAlias("old", NIL); oldrte->eref = oldrte->alias; oldrte->lateral = false; oldrte->inh = false; oldrte->inFromCl = true; newrte = makeNode(RangeTblEntry); newrte->rtekind = RTE_RELATION; newrte->relid = trigrec->tgrelid; newrte->relkind = relkind; newrte->alias = makeAlias("new", NIL); newrte->eref = newrte->alias; newrte->lateral = false; newrte->inh = false; newrte->inFromCl = true; /* Build two-element rtable */ memset(&dpns, 0, sizeof(dpns)); dpns.rtable = list_make2(oldrte, newrte); dpns.ctes = NIL; set_rtable_names(&dpns, NIL, NULL); set_simple_column_names(&dpns); /* Set up context with one-deep namespace stack */ context.buf = &buf; context.namespaces = list_make1(&dpns); context.windowClause = NIL; context.windowTList = NIL; context.varprefix = true; context.prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT; context.wrapColumn = WRAP_COLUMN_DEFAULT; context.indentLevel = PRETTYINDENT_STD; get_rule_expr(qual, &context, false); appendStringInfo(&buf, ") "); } appendStringInfo(&buf, "EXECUTE PROCEDURE %s(", generate_function_name(trigrec->tgfoid, 0, NIL, NULL, false, NULL)); if (trigrec->tgnargs > 0) { char *p; int i; value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs, tgrel->rd_att, &isnull); if (isnull) elog(ERROR, "tgargs is null for trigger %u", trigid); p = (char *) VARDATA(DatumGetByteaP(value)); for (i = 0; i < trigrec->tgnargs; i++) { if (i > 0) appendStringInfo(&buf, ", "); simple_quote_literal(&buf, p); /* advance p to next string embedded in tgargs */ while (*p) p++; p++; } } /* We deliberately do not put semi-colon at end */ appendStringInfo(&buf, ")"); /* Clean up */ systable_endscan(tgscan); heap_close(tgrel, AccessShareLock); return buf.data; }
Datum pg_get_userbyid | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1716 of file ruleutils.c.
References AUTHOID, GETSTRUCT, HeapTupleIsValid, NAMEDATALEN, NameStr, ObjectIdGetDatum, palloc(), PG_GETARG_OID, PG_RETURN_NAME, ReleaseSysCache(), SearchSysCache1, and StrNCpy.
{ Oid roleid = PG_GETARG_OID(0); Name result; HeapTuple roletup; Form_pg_authid role_rec; /* * Allocate space for the result */ result = (Name) palloc(NAMEDATALEN); memset(NameStr(*result), 0, NAMEDATALEN); /* * Get the pg_authid entry and print the result */ roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); if (HeapTupleIsValid(roletup)) { role_rec = (Form_pg_authid) GETSTRUCT(roletup); StrNCpy(NameStr(*result), NameStr(role_rec->rolname), NAMEDATALEN); ReleaseSysCache(roletup); } else sprintf(NameStr(*result), "unknown (OID=%u)", roleid); PG_RETURN_NAME(result); }
Datum pg_get_viewdef | ( | PG_FUNCTION_ARGS | ) |
Definition at line 513 of file ruleutils.c.
References pg_get_viewdef_worker(), PG_GETARG_OID, PG_RETURN_TEXT_P, string_to_text(), and WRAP_COLUMN_DEFAULT.
{ /* By OID */ Oid viewoid = PG_GETARG_OID(0); int prettyFlags; prettyFlags = PRETTYFLAG_INDENT; PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT))); }
Datum pg_get_viewdef_ext | ( | PG_FUNCTION_ARGS | ) |
Definition at line 525 of file ruleutils.c.
References pg_get_viewdef_worker(), PG_GETARG_BOOL, PG_GETARG_OID, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, PRETTYFLAG_PAREN, string_to_text(), and WRAP_COLUMN_DEFAULT.
{ /* By OID */ Oid viewoid = PG_GETARG_OID(0); bool pretty = PG_GETARG_BOOL(1); int prettyFlags; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT; PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT))); }
Datum pg_get_viewdef_name | ( | PG_FUNCTION_ARGS | ) |
Definition at line 550 of file ruleutils.c.
References makeRangeVarFromNameList(), NoLock, pg_get_viewdef_worker(), PG_GETARG_TEXT_P, PG_RETURN_TEXT_P, RangeVarGetRelid, string_to_text(), textToQualifiedNameList(), and WRAP_COLUMN_DEFAULT.
{ /* By qualified name */ text *viewname = PG_GETARG_TEXT_P(0); int prettyFlags; RangeVar *viewrel; Oid viewoid; prettyFlags = PRETTYFLAG_INDENT; /* Look up view name. Can't lock it - we might not have privileges. */ viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname)); viewoid = RangeVarGetRelid(viewrel, NoLock, false); PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT))); }
Datum pg_get_viewdef_name_ext | ( | PG_FUNCTION_ARGS | ) |
Definition at line 569 of file ruleutils.c.
References makeRangeVarFromNameList(), NoLock, pg_get_viewdef_worker(), PG_GETARG_BOOL, PG_GETARG_TEXT_P, PG_RETURN_TEXT_P, PRETTYFLAG_INDENT, PRETTYFLAG_PAREN, RangeVarGetRelid, string_to_text(), textToQualifiedNameList(), and WRAP_COLUMN_DEFAULT.
{ /* By qualified name */ text *viewname = PG_GETARG_TEXT_P(0); bool pretty = PG_GETARG_BOOL(1); int prettyFlags; RangeVar *viewrel; Oid viewoid; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT; /* Look up view name. Can't lock it - we might not have privileges. */ viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname)); viewoid = RangeVarGetRelid(viewrel, NoLock, false); PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT))); }
static char * pg_get_viewdef_worker | ( | Oid | viewoid, | |
int | prettyFlags, | |||
int | wrapColumn | |||
) | [static] |
Definition at line 591 of file ruleutils.c.
References appendStringInfo(), buf, StringInfoData::data, elog, ERROR, initStringInfo(), make_viewdef(), NULL, ObjectIdGetDatum, PointerGetDatum, query_getviewrule, SPI_connect(), SPI_execute_plan(), SPI_finish(), SPI_keepplan(), SPI_OK_CONNECT, 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().
{ Datum args[2]; char nulls[2]; int spirc; HeapTuple ruletup; TupleDesc rulettc; StringInfoData buf; /* * Do this first so that string is alloc'd in outer context not SPI's. */ initStringInfo(&buf); /* * Connect to SPI manager */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); /* * On the first call prepare the plan to lookup pg_rewrite. We read * pg_rewrite over the SPI manager instead of using the syscache to be * checked for read access on pg_rewrite. */ if (plan_getviewrule == NULL) { Oid argtypes[2]; SPIPlanPtr plan; argtypes[0] = OIDOID; argtypes[1] = NAMEOID; plan = SPI_prepare(query_getviewrule, 2, argtypes); if (plan == NULL) elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule); SPI_keepplan(plan); plan_getviewrule = plan; } /* * Get the pg_rewrite tuple for the view's SELECT rule */ args[0] = ObjectIdGetDatum(viewoid); args[1] = PointerGetDatum(ViewSelectRuleName); nulls[0] = ' '; nulls[1] = ' '; spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 2); if (spirc != SPI_OK_SELECT) elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid); if (SPI_processed != 1) appendStringInfo(&buf, "Not a view"); else { /* * Get the rule's definition and put it into executor's memory */ ruletup = SPI_tuptable->vals[0]; rulettc = SPI_tuptable->tupdesc; make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn); } /* * Disconnect from SPI manager */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); return buf.data; }
Datum pg_get_viewdef_wrap | ( | PG_FUNCTION_ARGS | ) |
Definition at line 537 of file ruleutils.c.
References pg_get_viewdef_worker(), PG_GETARG_INT32, PG_GETARG_OID, PG_RETURN_TEXT_P, PRETTYFLAG_PAREN, and string_to_text().
{ /* By OID */ Oid viewoid = PG_GETARG_OID(0); int wrap = PG_GETARG_INT32(1); int prettyFlags; /* calling this implies we want pretty printing */ prettyFlags = PRETTYFLAG_PAREN | PRETTYFLAG_INDENT; PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags, wrap))); }
static void pop_ancestor_plan | ( | deparse_namespace * | dpns, | |
deparse_namespace * | save_dpns | |||
) | [static] |
Definition at line 3694 of file ruleutils.c.
References deparse_namespace::ancestors, and list_free().
Referenced by get_name_for_var_field(), and get_parameter().
static void pop_child_plan | ( | deparse_namespace * | dpns, | |
deparse_namespace * | save_dpns | |||
) | [static] |
Definition at line 3641 of file ruleutils.c.
References deparse_namespace::ancestors, and list_delete_first().
Referenced by get_name_for_var_field(), and get_variable().
{ List *ancestors; /* Get rid of ancestors list cell added by push_child_plan */ ancestors = list_delete_first(dpns->ancestors); /* Restore fields changed by push_child_plan */ *dpns = *save_dpns; /* Make sure dpns->ancestors is right (may be unnecessary) */ dpns->ancestors = ancestors; }
static int print_function_arguments | ( | StringInfo | buf, | |
HeapTuple | proctup, | |||
bool | print_table_args, | |||
bool | print_defaults | |||
) | [static] |
Definition at line 2160 of file ruleutils.c.
References Anum_pg_proc_proargdefaults, appendStringInfo(), appendStringInfoString(), Assert, deparse_expression(), elog, ERROR, format_type_be(), get_func_arg_info(), GETSTRUCT, i, IsA, lfirst, list_head(), list_length(), lnext, NIL, NULL, pfree(), PROARGMODE_IN, PROARGMODE_INOUT, PROARGMODE_OUT, PROARGMODE_TABLE, PROARGMODE_VARIADIC, PROCOID, quote_identifier(), stringToNode(), SysCacheGetAttr(), and TextDatumGetCString.
Referenced by pg_get_function_arguments(), pg_get_function_identity_arguments(), pg_get_functiondef(), and print_function_rettype().
{ Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup); int numargs; Oid *argtypes; char **argnames; char *argmodes; int argsprinted; int inputargno; int nlackdefaults; ListCell *nextargdefault = NULL; int i; numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes); nlackdefaults = numargs; if (print_defaults && proc->pronargdefaults > 0) { Datum proargdefaults; bool isnull; proargdefaults = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proargdefaults, &isnull); if (!isnull) { char *str; List *argdefaults; str = TextDatumGetCString(proargdefaults); argdefaults = (List *) stringToNode(str); Assert(IsA(argdefaults, List)); pfree(str); nextargdefault = list_head(argdefaults); /* nlackdefaults counts only *input* arguments lacking defaults */ nlackdefaults = proc->pronargs - list_length(argdefaults); } } argsprinted = 0; inputargno = 0; for (i = 0; i < numargs; i++) { Oid argtype = argtypes[i]; char *argname = argnames ? argnames[i] : NULL; char argmode = argmodes ? argmodes[i] : PROARGMODE_IN; const char *modename; bool isinput; switch (argmode) { case PROARGMODE_IN: modename = ""; isinput = true; break; case PROARGMODE_INOUT: modename = "INOUT "; isinput = true; break; case PROARGMODE_OUT: modename = "OUT "; isinput = false; break; case PROARGMODE_VARIADIC: modename = "VARIADIC "; isinput = true; break; case PROARGMODE_TABLE: modename = ""; isinput = false; break; default: elog(ERROR, "invalid parameter mode '%c'", argmode); modename = NULL; /* keep compiler quiet */ isinput = false; break; } if (isinput) inputargno++; /* this is a 1-based counter */ if (print_table_args != (argmode == PROARGMODE_TABLE)) continue; if (argsprinted) appendStringInfoString(buf, ", "); appendStringInfoString(buf, modename); if (argname && argname[0]) appendStringInfo(buf, "%s ", quote_identifier(argname)); appendStringInfoString(buf, format_type_be(argtype)); if (print_defaults && isinput && inputargno > nlackdefaults) { Node *expr; Assert(nextargdefault != NULL); expr = (Node *) lfirst(nextargdefault); nextargdefault = lnext(nextargdefault); appendStringInfo(buf, " DEFAULT %s", deparse_expression(expr, NIL, false, false)); } argsprinted++; } return argsprinted; }
static void print_function_rettype | ( | StringInfo | buf, | |
HeapTuple | proctup | |||
) | [static] |
Definition at line 2122 of file ruleutils.c.
References appendStringInfoString(), StringInfoData::data, format_type_be(), GETSTRUCT, initStringInfo(), print_function_arguments(), and resetStringInfo().
Referenced by pg_get_function_result(), and pg_get_functiondef().
{ Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup); int ntabargs = 0; StringInfoData rbuf; initStringInfo(&rbuf); if (proc->proretset) { /* It might be a table function; try to print the arguments */ appendStringInfoString(&rbuf, "TABLE("); ntabargs = print_function_arguments(&rbuf, proctup, true, false); if (ntabargs > 0) appendStringInfoString(&rbuf, ")"); else resetStringInfo(&rbuf); } if (ntabargs == 0) { /* Not a table function, so do the normal thing */ if (proc->proretset) appendStringInfoString(&rbuf, "SETOF "); appendStringInfoString(&rbuf, format_type_be(proc->prorettype)); } appendStringInfoString(buf, rbuf.data); }
static void printSubscripts | ( | ArrayRef * | aref, | |
deparse_context * | context | |||
) | [static] |
Definition at line 8331 of file ruleutils.c.
References appendStringInfoChar(), deparse_context::buf, buf, get_rule_expr(), lfirst, list_head(), lnext, ArrayRef::reflowerindexpr, and ArrayRef::refupperindexpr.
Referenced by get_rule_expr(), and processIndirection().
{ StringInfo buf = context->buf; ListCell *lowlist_item; ListCell *uplist_item; lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */ foreach(uplist_item, aref->refupperindexpr) { appendStringInfoChar(buf, '['); if (lowlist_item) { get_rule_expr((Node *) lfirst(lowlist_item), context, false); appendStringInfoChar(buf, ':'); lowlist_item = lnext(lowlist_item); } get_rule_expr((Node *) lfirst(uplist_item), context, false); appendStringInfoChar(buf, ']'); } }
static Node * processIndirection | ( | Node * | node, | |
deparse_context * | context, | |||
bool | printit | |||
) | [static] |
Definition at line 8271 of file ruleutils.c.
References appendStringInfo(), Assert, deparse_context::buf, buf, elog, ERROR, FieldStore::fieldnums, format_type_be(), get_relid_attribute_name(), get_typ_typrelid(), IsA, linitial, linitial_int, list_length(), FieldStore::newvals, NULL, OidIsValid, printSubscripts(), quote_identifier(), ArrayRef::refassgnexpr, and FieldStore::resulttype.
Referenced by get_insert_query_def(), get_rule_expr(), get_update_query_def(), and get_values_def().
{ StringInfo buf = context->buf; for (;;) { if (node == NULL) break; if (IsA(node, FieldStore)) { FieldStore *fstore = (FieldStore *) node; Oid typrelid; char *fieldname; /* lookup tuple type */ typrelid = get_typ_typrelid(fstore->resulttype); if (!OidIsValid(typrelid)) elog(ERROR, "argument type %s of FieldStore is not a tuple type", format_type_be(fstore->resulttype)); /* * Print the field name. There should only be one target field in * stored rules. There could be more than that in executable * target lists, but this function cannot be used for that case. */ Assert(list_length(fstore->fieldnums) == 1); fieldname = get_relid_attribute_name(typrelid, linitial_int(fstore->fieldnums)); if (printit) appendStringInfo(buf, ".%s", quote_identifier(fieldname)); /* * We ignore arg since it should be an uninteresting reference to * the target column or subcolumn. */ node = (Node *) linitial(fstore->newvals); } else if (IsA(node, ArrayRef)) { ArrayRef *aref = (ArrayRef *) node; if (aref->refassgnexpr == NULL) break; if (printit) printSubscripts(aref, context); /* * We ignore refexpr since it should be an uninteresting reference * to the target column or subcolumn. */ node = (Node *) aref->refassgnexpr; } else break; } return node; }
static void push_ancestor_plan | ( | deparse_namespace * | dpns, | |
ListCell * | ancestor_cell, | |||
deparse_namespace * | save_dpns | |||
) | [static] |
Definition at line 3671 of file ruleutils.c.
References deparse_namespace::ancestors, lappend(), lfirst, lnext, and set_deparse_planstate().
Referenced by get_name_for_var_field(), and get_parameter().
{ PlanState *ps = (PlanState *) lfirst(ancestor_cell); List *ancestors; /* Save state for restoration later */ *save_dpns = *dpns; /* Build a new ancestor list with just this node's ancestors */ ancestors = NIL; while ((ancestor_cell = lnext(ancestor_cell)) != NULL) ancestors = lappend(ancestors, lfirst(ancestor_cell)); dpns->ancestors = ancestors; /* Set attention on selected ancestor */ set_deparse_planstate(dpns, ps); }
static void push_child_plan | ( | deparse_namespace * | dpns, | |
PlanState * | ps, | |||
deparse_namespace * | save_dpns | |||
) | [static] |
Definition at line 3624 of file ruleutils.c.
References deparse_namespace::ancestors, lcons(), deparse_namespace::planstate, and set_deparse_planstate().
Referenced by get_name_for_var_field(), and get_variable().
{ /* Save state for restoration later */ *save_dpns = *dpns; /* Link current plan node into ancestors list */ dpns->ancestors = lcons(dpns->planstate, dpns->ancestors); /* Set attention on selected child */ set_deparse_planstate(dpns, ps); }
const char* quote_identifier | ( | const char * | ident | ) |
Definition at line 8359 of file ruleutils.c.
References ScanKeyword::category, NULL, NumScanKeywords, palloc(), pg_malloc(), quote_all_identifiers, ScanKeywordLookup(), ScanKeywords, and UNRESERVED_KEYWORD.
Referenced by ConvertTriggerToFK(), decompile_column_index_array(), deparseAnalyzeSql(), deparseColumnRef(), deparseFuncExpr(), deparseOperatorName(), deparseRelation(), execute_extension_script(), explain_get_index_name(), ExplainTargetRel(), flatten_set_variable_args(), format_operator_internal(), generate_operator_name(), get_column_alias_list(), get_delete_query_def(), get_from_clause_coldeflist(), get_from_clause_item(), get_insert_query_def(), get_opclass_name(), get_rule_expr(), get_rule_windowclause(), get_rule_windowspec(), get_select_query_def(), get_target_list(), get_update_query_def(), get_utility_query_def(), get_variable(), get_windowfunc_expr(), get_with_clause(), getObjectIdentity(), make_ruledef(), NameListToQuotedString(), new_9_0_populate_pg_largeobject_metadata(), old_8_3_create_sequence_script(), old_8_3_invalidate_bpchar_pattern_ops_indexes(), old_8_3_invalidate_hash_gin_indexes(), old_8_3_rebuild_tsvector_tables(), pg_get_constraintdef_worker(), pg_get_functiondef(), pg_get_indexdef_worker(), pg_get_triggerdef_worker(), pg_identify_object(), PLy_quote_ident(), print_function_arguments(), processIndirection(), quote_ident(), quote_object_name(), quote_qualified_identifier(), regoperout(), ri_add_cast_to(), ri_GenerateQual(), sepgsql_attribute_post_create(), sepgsql_database_post_create(), sepgsql_relation_post_create(), sepgsql_schema_post_create(), serialize_deflist(), text_format_string_conversion(), and worker_spi_main().
{ /* * Can avoid quoting if ident starts with a lowercase letter or underscore * and contains only lowercase letters, digits, and underscores, *and* is * not any SQL keyword. Otherwise, supply quotes. */ int nquotes = 0; bool safe; const char *ptr; char *result; char *optr; /* * would like to use <ctype.h> macros here, but they might yield unwanted * locale-specific results... */ safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_'); for (ptr = ident; *ptr; ptr++) { char ch = *ptr; if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '_')) { /* okay */ } else { safe = false; if (ch == '"') nquotes++; } } if (quote_all_identifiers) safe = false; if (safe) { /* * Check for keyword. We quote keywords except for unreserved ones. * (In some cases we could avoid quoting a col_name or type_func_name * keyword, but it seems much harder than it's worth to tell that.) * * Note: ScanKeywordLookup() does case-insensitive comparison, but * that's fine, since we already know we have all-lower-case. */ const ScanKeyword *keyword = ScanKeywordLookup(ident, ScanKeywords, NumScanKeywords); if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD) safe = false; } if (safe) return ident; /* no change needed */ result = (char *) palloc(strlen(ident) + nquotes + 2 + 1); optr = result; *optr++ = '"'; for (ptr = ident; *ptr; ptr++) { char ch = *ptr; if (ch == '"') *optr++ = '"'; *optr++ = ch; } *optr++ = '"'; *optr = '\0'; return result; }
char* quote_qualified_identifier | ( | const char * | qualifier, | |
const char * | ident | |||
) |
Definition at line 8445 of file ruleutils.c.
References appendStringInfo(), appendStringInfoString(), buf, StringInfoData::data, initStringInfo(), and quote_identifier().
Referenced by check_TSCurrentConfig(), format_procedure_internal(), format_type_internal(), generate_collation_name(), generate_function_name(), generate_relation_name(), getObjectDescription(), getObjectIdentity(), getOpFamilyDescription(), getOpFamilyIdentity(), getRelationDescription(), getRelationIdentity(), pg_get_functiondef(), pg_get_serial_sequence(), pltcl_init_load_unknown(), regclassout(), regconfigout(), regdictionaryout(), regprocout(), sepgsql_proc_post_create(), and transformColumnDefinition().
{ StringInfoData buf; initStringInfo(&buf); if (qualifier) appendStringInfo(&buf, "%s.", quote_identifier(qualifier)); appendStringInfoString(&buf, quote_identifier(ident)); return buf.data; }
static bool refname_is_unique | ( | char * | refname, | |
deparse_namespace * | dpns, | |||
List * | parent_namespaces | |||
) | [static] |
Definition at line 2512 of file ruleutils.c.
References lfirst, and deparse_namespace::rtable_names.
Referenced by set_rtable_names().
{ ListCell *lc; foreach(lc, dpns->rtable_names) { char *oldname = (char *) lfirst(lc); if (oldname && strcmp(oldname, refname) == 0) return false; } foreach(lc, parent_namespaces) { deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc); ListCell *lc2; foreach(lc2, olddpns->rtable_names) { char *oldname = (char *) lfirst(lc2); if (oldname && strcmp(oldname, refname) == 0) return false; } } return true; }
Definition at line 2422 of file ruleutils.c.
References deparse_namespace::ctes, NIL, deparse_namespace::rtable, deparse_namespace::rtable_names, and set_rtable_names().
Referenced by ExplainPrintPlan().
{ deparse_namespace dpns; memset(&dpns, 0, sizeof(dpns)); dpns.rtable = rtable; dpns.ctes = NIL; set_rtable_names(&dpns, NIL, rels_used); /* We needn't bother computing column aliases yet */ return dpns.rtable_names; }
static void set_deparse_for_query | ( | deparse_namespace * | dpns, | |
Query * | query, | |||
List * | parent_namespaces | |||
) | [static] |
Definition at line 2547 of file ruleutils.c.
References Query::cteList, deparse_namespace::ctes, forboth, has_unnamed_full_join_using(), Query::jointree, lappend(), lfirst, list_length(), NULL, palloc0(), Query::rtable, deparse_namespace::rtable, deparse_namespace::rtable_columns, RTE_JOIN, RangeTblEntry::rtekind, set_join_column_names(), set_relation_column_names(), set_rtable_names(), set_using_names(), and deparse_namespace::unique_using.
Referenced by get_name_for_var_field(), get_query_def(), and make_ruledef().
{ ListCell *lc; ListCell *lc2; /* Initialize *dpns and fill rtable/ctes links */ memset(dpns, 0, sizeof(deparse_namespace)); dpns->rtable = query->rtable; dpns->ctes = query->cteList; /* Assign a unique relation alias to each RTE */ set_rtable_names(dpns, parent_namespaces, NULL); /* Initialize dpns->rtable_columns to contain zeroed structs */ dpns->rtable_columns = NIL; while (list_length(dpns->rtable_columns) < list_length(dpns->rtable)) dpns->rtable_columns = lappend(dpns->rtable_columns, palloc0(sizeof(deparse_columns))); /* Detect whether global uniqueness of USING names is needed */ dpns->unique_using = has_unnamed_full_join_using((Node *) query->jointree); /* * Select names for columns merged by USING, via a recursive pass over the * query jointree. */ set_using_names(dpns, (Node *) query->jointree); /* * Now assign remaining column aliases for each RTE. We do this in a * linear scan of the rtable, so as to process RTEs whether or not they * are in the jointree (we mustn't miss NEW.*, INSERT target relations, * etc). JOIN RTEs must be processed after their children, but this is * okay because they appear later in the rtable list than their children * (cf Asserts in identify_join_columns()). */ forboth(lc, dpns->rtable, lc2, dpns->rtable_columns) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); deparse_columns *colinfo = (deparse_columns *) lfirst(lc2); if (rte->rtekind == RTE_JOIN) set_join_column_names(dpns, rte, colinfo); else set_relation_column_names(dpns, rte, colinfo); } }
static void set_deparse_planstate | ( | deparse_namespace * | dpns, | |
PlanState * | ps | |||
) | [static] |
Definition at line 3561 of file ruleutils.c.
References deparse_namespace::index_tlist, deparse_namespace::inner_planstate, deparse_namespace::inner_tlist, innerPlanState, IsA, deparse_namespace::outer_planstate, deparse_namespace::outer_tlist, outerPlanState, PlanState::plan, deparse_namespace::planstate, and Plan::targetlist.
Referenced by deparse_context_for_planstate(), push_ancestor_plan(), and push_child_plan().
{ dpns->planstate = ps; /* * We special-case Append and MergeAppend to pretend that the first child * plan is the OUTER referent; we have to interpret OUTER Vars in their * tlists according to one of the children, and the first one is the most * natural choice. Likewise special-case ModifyTable to pretend that the * first child plan is the OUTER referent; this is to support RETURNING * lists containing references to non-target relations. */ if (IsA(ps, AppendState)) dpns->outer_planstate = ((AppendState *) ps)->appendplans[0]; else if (IsA(ps, MergeAppendState)) dpns->outer_planstate = ((MergeAppendState *) ps)->mergeplans[0]; else if (IsA(ps, ModifyTableState)) dpns->outer_planstate = ((ModifyTableState *) ps)->mt_plans[0]; else dpns->outer_planstate = outerPlanState(ps); if (dpns->outer_planstate) dpns->outer_tlist = dpns->outer_planstate->plan->targetlist; else dpns->outer_tlist = NIL; /* * For a SubqueryScan, pretend the subplan is INNER referent. (We don't * use OUTER because that could someday conflict with the normal meaning.) * Likewise, for a CteScan, pretend the subquery's plan is INNER referent. */ if (IsA(ps, SubqueryScanState)) dpns->inner_planstate = ((SubqueryScanState *) ps)->subplan; else if (IsA(ps, CteScanState)) dpns->inner_planstate = ((CteScanState *) ps)->cteplanstate; else dpns->inner_planstate = innerPlanState(ps); if (dpns->inner_planstate) dpns->inner_tlist = dpns->inner_planstate->plan->targetlist; else dpns->inner_tlist = NIL; /* index_tlist is set only if it's an IndexOnlyScan */ if (IsA(ps->plan, IndexOnlyScan)) dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist; else dpns->index_tlist = NIL; }
static void set_join_column_names | ( | deparse_namespace * | dpns, | |
RangeTblEntry * | rte, | |||
deparse_columns * | colinfo | |||
) | [static] |
Definition at line 3009 of file ruleutils.c.
References RangeTblEntry::alias, Assert, bms_add_member(), bms_is_member(), deparse_columns::colnames, Alias::colnames, deparse_columns_fetch, RangeTblEntry::eref, expand_colnames_array_to(), i, deparse_columns::is_new_col, deparse_columns::leftattnos, deparse_columns::leftrti, list_length(), list_nth(), make_colname_unique(), deparse_columns::new_colnames, NULL, deparse_columns::num_cols, deparse_columns::num_new_cols, palloc0(), deparse_columns::printaliases, deparse_columns::rightattnos, deparse_columns::rightrti, strVal, and deparse_columns::usingNames.
Referenced by set_deparse_for_query().
{ deparse_columns *leftcolinfo; deparse_columns *rightcolinfo; bool changed_any; int noldcolumns; int nnewcolumns; Bitmapset *leftmerged = NULL; Bitmapset *rightmerged = NULL; int i; int j; int ic; int jc; /* Look up the previously-filled-in child deparse_columns structs */ leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns); rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns); /* * Ensure colinfo->colnames has a slot for each column. (It could be long * enough already, if we pushed down a name for the last column.) Note: * it's possible that one or both inputs now have more columns than there * were when the query was parsed, but we'll deal with that below. We * only need entries in colnames for pre-existing columns. */ noldcolumns = list_length(rte->eref->colnames); expand_colnames_array_to(colinfo, noldcolumns); Assert(colinfo->num_cols == noldcolumns); /* * Scan the join output columns, select an alias for each one, and store * it in colinfo->colnames. If there are USING columns, set_using_names() * already selected their names, so we can start the loop at the first * non-merged column. */ changed_any = false; for (i = list_length(colinfo->usingNames); i < noldcolumns; i++) { char *colname = colinfo->colnames[i]; char *real_colname; /* Get the child column name */ if (colinfo->leftattnos[i] > 0) real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1]; else if (colinfo->rightattnos[i] > 0) real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1]; else { /* We're joining system columns --- use eref name */ real_colname = (char *) list_nth(rte->eref->colnames, i); } /* Ignore dropped columns (only possible for non-merged column) */ if (real_colname == NULL) { Assert(colname == NULL); continue; } /* In an unnamed join, just report child column names as-is */ if (rte->alias == NULL) { colinfo->colnames[i] = real_colname; continue; } /* If alias already assigned, that's what to use */ if (colname == NULL) { /* If user wrote an alias, prefer that over real column name */ if (rte->alias && i < list_length(rte->alias->colnames)) colname = strVal(list_nth(rte->alias->colnames, i)); else colname = real_colname; /* Unique-ify and insert into colinfo */ colname = make_colname_unique(colname, dpns, colinfo); colinfo->colnames[i] = colname; } /* Remember if any assigned aliases differ from "real" name */ if (!changed_any && strcmp(colname, real_colname) != 0) changed_any = true; } /* * Calculate number of columns the join would have if it were re-parsed * now, and create storage for the new_colnames and is_new_col arrays. * * Note: colname_is_unique will be consulting new_colnames[] during the * loops below, so its not-yet-filled entries must be zeroes. */ nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols - list_length(colinfo->usingNames); colinfo->num_new_cols = nnewcolumns; colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *)); colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool)); /* * Generating the new_colnames array is a bit tricky since any new columns * added since parse time must be inserted in the right places. This code * must match the parser, which will order a join's columns as merged * columns first (in USING-clause order), then non-merged columns from the * left input (in attnum order), then non-merged columns from the right * input (ditto). If one of the inputs is itself a join, its columns will * be ordered according to the same rule, which means newly-added columns * might not be at the end. We can figure out what's what by consulting * the leftattnos and rightattnos arrays plus the input is_new_col arrays. * * In these loops, i indexes leftattnos/rightattnos (so it's join varattno * less one), j indexes new_colnames/is_new_col, and ic/jc have similar * meanings for the current child RTE. */ /* Handle merged columns; they are first and can't be new */ i = j = 0; while (i < noldcolumns && colinfo->leftattnos[i] != 0 && colinfo->rightattnos[i] != 0) { /* column name is already determined and known unique */ colinfo->new_colnames[j] = colinfo->colnames[i]; colinfo->is_new_col[j] = false; /* build bitmapsets of child attnums of merged columns */ if (colinfo->leftattnos[i] > 0) leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]); if (colinfo->rightattnos[i] > 0) rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]); i++, j++; } /* Handle non-merged left-child columns */ ic = 0; for (jc = 0; jc < leftcolinfo->num_new_cols; jc++) { char *child_colname = leftcolinfo->new_colnames[jc]; if (!leftcolinfo->is_new_col[jc]) { /* Advance ic to next non-dropped old column of left child */ while (ic < leftcolinfo->num_cols && leftcolinfo->colnames[ic] == NULL) ic++; Assert(ic < leftcolinfo->num_cols); ic++; /* If it is a merged column, we already processed it */ if (bms_is_member(ic, leftmerged)) continue; /* Else, advance i to the corresponding existing join column */ while (i < colinfo->num_cols && colinfo->colnames[i] == NULL) i++; Assert(i < colinfo->num_cols); Assert(ic == colinfo->leftattnos[i]); /* Use the already-assigned name of this column */ colinfo->new_colnames[j] = colinfo->colnames[i]; i++; } else { /* * Unique-ify the new child column name and assign, unless we're * in an unnamed join, in which case just copy */ if (rte->alias != NULL) { colinfo->new_colnames[j] = make_colname_unique(child_colname, dpns, colinfo); if (!changed_any && strcmp(colinfo->new_colnames[j], child_colname) != 0) changed_any = true; } else colinfo->new_colnames[j] = child_colname; } colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc]; j++; } /* Handle non-merged right-child columns in exactly the same way */ ic = 0; for (jc = 0; jc < rightcolinfo->num_new_cols; jc++) { char *child_colname = rightcolinfo->new_colnames[jc]; if (!rightcolinfo->is_new_col[jc]) { /* Advance ic to next non-dropped old column of right child */ while (ic < rightcolinfo->num_cols && rightcolinfo->colnames[ic] == NULL) ic++; Assert(ic < rightcolinfo->num_cols); ic++; /* If it is a merged column, we already processed it */ if (bms_is_member(ic, rightmerged)) continue; /* Else, advance i to the corresponding existing join column */ while (i < colinfo->num_cols && colinfo->colnames[i] == NULL) i++; Assert(i < colinfo->num_cols); Assert(ic == colinfo->rightattnos[i]); /* Use the already-assigned name of this column */ colinfo->new_colnames[j] = colinfo->colnames[i]; i++; } else { /* * Unique-ify the new child column name and assign, unless we're * in an unnamed join, in which case just copy */ if (rte->alias != NULL) { colinfo->new_colnames[j] = make_colname_unique(child_colname, dpns, colinfo); if (!changed_any && strcmp(colinfo->new_colnames[j], child_colname) != 0) changed_any = true; } else colinfo->new_colnames[j] = child_colname; } colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc]; j++; } /* Assert we processed the right number of columns */ #ifdef USE_ASSERT_CHECKING while (i < colinfo->num_cols && colinfo->colnames[i] == NULL) i++; Assert(i == colinfo->num_cols); Assert(j == nnewcolumns); #endif /* * For a named join, print column aliases if we changed any from the child * names. Unnamed joins cannot print aliases. */ if (rte->alias != NULL) colinfo->printaliases = changed_any; else colinfo->printaliases = false; }
static void set_relation_column_names | ( | deparse_namespace * | dpns, | |
RangeTblEntry * | rte, | |||
deparse_columns * | colinfo | |||
) | [static] |
Definition at line 2851 of file ruleutils.c.
References AccessShareLock, RangeTblEntry::alias, Assert, tupleDesc::attrs, deparse_columns::colnames, Alias::colnames, RangeTblEntry::eref, expand_colnames_array_to(), i, deparse_columns::is_new_col, lfirst, list_length(), list_nth(), make_colname_unique(), NameStr, tupleDesc::natts, deparse_columns::new_colnames, NIL, NULL, deparse_columns::num_cols, deparse_columns::num_new_cols, palloc(), deparse_columns::printaliases, pstrdup(), relation_close(), relation_open(), RelationGetDescr, RangeTblEntry::relid, RTE_FUNCTION, RTE_RELATION, RangeTblEntry::rtekind, and strVal.
Referenced by set_deparse_for_query(), and set_simple_column_names().
{ int ncolumns; char **real_colnames; bool changed_any; int noldcolumns; int i; int j; /* * Extract the RTE's "real" column names. This is comparable to * get_rte_attribute_name, except that it's important to disregard dropped * columns. We put NULL into the array for a dropped column. */ if (rte->rtekind == RTE_RELATION) { /* Relation --- look to the system catalogs for up-to-date info */ Relation rel; TupleDesc tupdesc; rel = relation_open(rte->relid, AccessShareLock); tupdesc = RelationGetDescr(rel); ncolumns = tupdesc->natts; real_colnames = (char **) palloc(ncolumns * sizeof(char *)); for (i = 0; i < ncolumns; i++) { if (tupdesc->attrs[i]->attisdropped) real_colnames[i] = NULL; else real_colnames[i] = pstrdup(NameStr(tupdesc->attrs[i]->attname)); } relation_close(rel, AccessShareLock); } else { /* Otherwise use the column names from eref */ ListCell *lc; ncolumns = list_length(rte->eref->colnames); real_colnames = (char **) palloc(ncolumns * sizeof(char *)); i = 0; foreach(lc, rte->eref->colnames) { real_colnames[i] = strVal(lfirst(lc)); i++; } } /* * Ensure colinfo->colnames has a slot for each column. (It could be long * enough already, if we pushed down a name for the last column.) Note: * it's possible that there are now more columns than there were when the * query was parsed, ie colnames could be longer than rte->eref->colnames. * We must assign unique aliases to the new columns too, else there could * be unresolved conflicts when the view/rule is reloaded. */ expand_colnames_array_to(colinfo, ncolumns); Assert(colinfo->num_cols == ncolumns); /* * Make sufficiently large new_colnames and is_new_col arrays, too. * * Note: because we leave colinfo->num_new_cols zero until after the loop, * colname_is_unique will not consult that array, which is fine because it * would only be duplicate effort. */ colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *)); colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool)); /* * Scan the columns, select a unique alias for each one, and store it in * colinfo->colnames and colinfo->new_colnames. The former array has NULL * entries for dropped columns, the latter omits them. Also mark * new_colnames entries as to whether they are new since parse time; this * is the case for entries beyond the length of rte->eref->colnames. */ noldcolumns = list_length(rte->eref->colnames); changed_any = false; j = 0; for (i = 0; i < ncolumns; i++) { char *real_colname = real_colnames[i]; char *colname = colinfo->colnames[i]; /* Skip dropped columns */ if (real_colname == NULL) { Assert(colname == NULL); /* colnames[i] is already NULL */ continue; } /* If alias already assigned, that's what to use */ if (colname == NULL) { /* If user wrote an alias, prefer that over real column name */ if (rte->alias && i < list_length(rte->alias->colnames)) colname = strVal(list_nth(rte->alias->colnames, i)); else colname = real_colname; /* Unique-ify and insert into colinfo */ colname = make_colname_unique(colname, dpns, colinfo); colinfo->colnames[i] = colname; } /* Put names of non-dropped columns in new_colnames[] too */ colinfo->new_colnames[j] = colname; /* And mark them as new or not */ colinfo->is_new_col[j] = (i >= noldcolumns); j++; /* Remember if any assigned aliases differ from "real" name */ if (!changed_any && strcmp(colname, real_colname) != 0) changed_any = true; } /* * Set correct length for new_colnames[] array. (Note: if columns have * been added, colinfo->num_cols includes them, which is not really quite * right but is harmless, since any new columns must be at the end where * they won't affect varattnos of pre-existing columns.) */ colinfo->num_new_cols = j; /* * For a relation RTE, we need only print the alias column names if any * are different from the underlying "real" names. For a function RTE, * always emit a complete column alias list; this is to protect against * possible instability of the default column names (eg, from altering * parameter names). For other RTE types, print if we changed anything OR * if there were user-written column aliases (since the latter would be * part of the underlying "reality"). */ if (rte->rtekind == RTE_RELATION) colinfo->printaliases = changed_any; else if (rte->rtekind == RTE_FUNCTION) colinfo->printaliases = true; else if (rte->alias && rte->alias->colnames != NIL) colinfo->printaliases = true; else colinfo->printaliases = changed_any; }
static void set_rtable_names | ( | deparse_namespace * | dpns, | |
List * | parent_namespaces, | |||
Bitmapset * | rels_used | |||
) | [static] |
Definition at line 2449 of file ruleutils.c.
References RangeTblEntry::alias, Alias::aliasname, bms_is_member(), RangeTblEntry::eref, get_rel_name(), i, lappend(), lfirst, palloc(), refname_is_unique(), RangeTblEntry::relid, deparse_namespace::rtable, deparse_namespace::rtable_names, RTE_JOIN, RTE_RELATION, and RangeTblEntry::rtekind.
Referenced by deparse_context_for(), pg_get_triggerdef_worker(), select_rtable_names_for_explain(), and set_deparse_for_query().
{ ListCell *lc; int rtindex = 1; dpns->rtable_names = NIL; foreach(lc, dpns->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); char *refname; if (rels_used && !bms_is_member(rtindex, rels_used)) { /* Ignore unreferenced RTE */ refname = NULL; } else if (rte->alias) { /* If RTE has a user-defined alias, prefer that */ refname = rte->alias->aliasname; } else if (rte->rtekind == RTE_RELATION) { /* Use the current actual name of the relation */ refname = get_rel_name(rte->relid); } else if (rte->rtekind == RTE_JOIN) { /* Unnamed join has no refname */ refname = NULL; } else { /* Otherwise use whatever the parser assigned */ refname = rte->eref->aliasname; } /* * If the selected name isn't unique, append digits to make it so */ if (refname && !refname_is_unique(refname, dpns, parent_namespaces)) { char *modname = (char *) palloc(strlen(refname) + 32); int i = 0; do { sprintf(modname, "%s_%d", refname, ++i); } while (!refname_is_unique(modname, dpns, parent_namespaces)); refname = modname; } dpns->rtable_names = lappend(dpns->rtable_names, refname); rtindex++; } }
static void set_simple_column_names | ( | deparse_namespace * | dpns | ) | [static] |
Definition at line 2605 of file ruleutils.c.
References forboth, lappend(), lfirst, list_length(), palloc0(), deparse_namespace::rtable, deparse_namespace::rtable_columns, and set_relation_column_names().
Referenced by deparse_context_for(), deparse_context_for_planstate(), and pg_get_triggerdef_worker().
{ ListCell *lc; ListCell *lc2; /* Initialize dpns->rtable_columns to contain zeroed structs */ dpns->rtable_columns = NIL; while (list_length(dpns->rtable_columns) < list_length(dpns->rtable)) dpns->rtable_columns = lappend(dpns->rtable_columns, palloc0(sizeof(deparse_columns))); /* Assign unique column aliases within each RTE */ forboth(lc, dpns->rtable, lc2, dpns->rtable_columns) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); deparse_columns *colinfo = (deparse_columns *) lfirst(lc2); set_relation_column_names(dpns, rte, colinfo); } }
static void set_using_names | ( | deparse_namespace * | dpns, | |
Node * | jtnode | |||
) | [static] |
Definition at line 2694 of file ruleutils.c.
References RangeTblEntry::alias, Assert, Alias::colnames, deparse_columns::colnames, deparse_columns_fetch, elog, ERROR, expand_colnames_array_to(), FromExpr::fromlist, i, identify_join_columns(), IsA, lappend(), JoinExpr::larg, deparse_columns::leftattnos, deparse_columns::leftrti, lfirst, list_length(), list_nth(), make_colname_unique(), nodeTag, NULL, deparse_columns::num_cols, JoinExpr::rarg, deparse_columns::rightattnos, deparse_columns::rightrti, rt_fetch, deparse_namespace::rtable, JoinExpr::rtindex, strVal, deparse_namespace::unique_using, deparse_namespace::using_names, JoinExpr::usingClause, and deparse_columns::usingNames.
Referenced by set_deparse_for_query().
{ if (IsA(jtnode, RangeTblRef)) { /* nothing to do now */ } else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; ListCell *lc; foreach(lc, f->fromlist) set_using_names(dpns, (Node *) lfirst(lc)); } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable); deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns); int *leftattnos; int *rightattnos; deparse_columns *leftcolinfo; deparse_columns *rightcolinfo; int i; ListCell *lc; /* Get info about the shape of the join */ identify_join_columns(j, rte, colinfo); leftattnos = colinfo->leftattnos; rightattnos = colinfo->rightattnos; /* Look up the not-yet-filled-in child deparse_columns structs */ leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns); rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns); /* * If this join is unnamed, then we cannot substitute new aliases at * this level, so any name requirements pushed down to here must be * pushed down again to the children. */ if (rte->alias == NULL) { for (i = 0; i < colinfo->num_cols; i++) { char *colname = colinfo->colnames[i]; if (colname == NULL) continue; /* Push down to left column, unless it's a system column */ if (leftattnos[i] > 0) { expand_colnames_array_to(leftcolinfo, leftattnos[i]); leftcolinfo->colnames[leftattnos[i] - 1] = colname; } /* Same on the righthand side */ if (rightattnos[i] > 0) { expand_colnames_array_to(rightcolinfo, rightattnos[i]); rightcolinfo->colnames[rightattnos[i] - 1] = colname; } } } /* * If there's a USING clause, select the USING column names and push * those names down to the children. We have two strategies: * * If dpns->unique_using is TRUE, we force all USING names to be * unique across the whole query level. In principle we'd only need * the names of USING columns in unnamed full joins to be globally * unique, but to safely assign all USING names in a single pass, we * have to enforce the same uniqueness rule for all of them. However, * if a USING column's name has been pushed down from the parent, we * should use it as-is rather than making a uniqueness adjustment. * This is necessary when we're at an unnamed join, and it creates no * risk of ambiguity. Also, if there's a user-written output alias * for a merged column, we prefer to use that rather than the input * name; this simplifies the logic and seems likely to lead to less * aliasing overall. * * If dpns->unique_using is FALSE, we only need USING names to be * unique within their own join RTE. We still need to honor * pushed-down names, though. * * Though significantly different in results, these two strategies are * implemented by the same code, with only the difference of whether * to put assigned names into dpns->using_names. */ if (j->usingClause) { /* USING names must correspond to the first join output columns */ expand_colnames_array_to(colinfo, list_length(j->usingClause)); i = 0; foreach(lc, j->usingClause) { char *colname = strVal(lfirst(lc)); /* Assert it's a merged column */ Assert(leftattnos[i] != 0 && rightattnos[i] != 0); /* Adopt passed-down name if any, else select unique name */ if (colinfo->colnames[i] != NULL) colname = colinfo->colnames[i]; else { /* Prefer user-written output alias if any */ if (rte->alias && i < list_length(rte->alias->colnames)) colname = strVal(list_nth(rte->alias->colnames, i)); /* Make it appropriately unique */ colname = make_colname_unique(colname, dpns, colinfo); if (dpns->unique_using) dpns->using_names = lappend(dpns->using_names, colname); /* Save it as output column name, too */ colinfo->colnames[i] = colname; } /* Remember selected names for use later */ colinfo->usingNames = lappend(colinfo->usingNames, colname); /* Push down to left column, unless it's a system column */ if (leftattnos[i] > 0) { expand_colnames_array_to(leftcolinfo, leftattnos[i]); leftcolinfo->colnames[leftattnos[i] - 1] = colname; } /* Same on the righthand side */ if (rightattnos[i] > 0) { expand_colnames_array_to(rightcolinfo, rightattnos[i]); rightcolinfo->colnames[rightattnos[i] - 1] = colname; } i++; } } /* Now recursively assign USING column names in children */ set_using_names(dpns, j->larg); set_using_names(dpns, j->rarg); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode)); }
static void simple_quote_literal | ( | StringInfo | buf, | |
const char * | val | |||
) | [static] |
Definition at line 7694 of file ruleutils.c.
References appendStringInfoChar(), SQL_STR_DOUBLE, and standard_conforming_strings.
Referenced by get_const_expr(), get_utility_query_def(), pg_get_functiondef(), and pg_get_triggerdef_worker().
{ const char *valptr; /* * We form the string literal according to the prevailing setting of * standard_conforming_strings; we never use E''. User is responsible for * making sure result is used correctly. */ appendStringInfoChar(buf, '\''); for (valptr = val; *valptr; valptr++) { char ch = *valptr; if (SQL_STR_DOUBLE(ch, !standard_conforming_strings)) appendStringInfoChar(buf, ch); appendStringInfoChar(buf, ch); } appendStringInfoChar(buf, '\''); }
static text * string_to_text | ( | char * | str | ) | [static] |
Definition at line 8748 of file ruleutils.c.
References cstring_to_text(), and pfree().
Referenced by pg_get_constraintdef(), pg_get_constraintdef_ext(), pg_get_expr_worker(), pg_get_function_arguments(), pg_get_function_identity_arguments(), pg_get_function_result(), pg_get_functiondef(), pg_get_indexdef(), pg_get_indexdef_ext(), pg_get_ruledef(), pg_get_ruledef_ext(), pg_get_serial_sequence(), pg_get_triggerdef(), pg_get_triggerdef_ext(), pg_get_viewdef(), pg_get_viewdef_ext(), pg_get_viewdef_name(), pg_get_viewdef_name_ext(), and pg_get_viewdef_wrap().
{ text *result; result = cstring_to_text(str); pfree(str); return result; }
SPIPlanPtr plan_getrulebyoid = NULL [static] |
Definition at line 259 of file ruleutils.c.
SPIPlanPtr plan_getviewrule = NULL [static] |
Definition at line 261 of file ruleutils.c.
const char* query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1" [static] |
Definition at line 260 of file ruleutils.c.
Referenced by pg_get_ruledef_worker().
const char* query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2" [static] |
Definition at line 262 of file ruleutils.c.
Referenced by pg_get_viewdef_worker().
bool quote_all_identifiers = false |
Definition at line 265 of file ruleutils.c.
Referenced by fmtId(), main(), quote_identifier(), and setup_connection().