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