Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Functions | Variables

ruleutils.c File Reference

#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"
Include dependency graph for ruleutils.c:

Go to the source code of this file.

Data Structures

struct  deparse_context
struct  deparse_namespace
struct  deparse_columns

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 textpg_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 Nodeget_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 Nodefind_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 NodeprocessIndirection (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 textstring_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)
Listdeparse_context_for (const char *aliasname, Oid relid)
Listdeparse_context_for_planstate (Node *planstate, List *ancestors, List *rtable, List *rtable_names)
Listselect_rtable_names_for_explain (List *rtable, Bitmapset *rels_used)
static RangeTblEntryget_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 Documentation

#define deparse_columns_fetch (   rangetable_index,
  dpns 
)    ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
#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)
#define PRETTY_PAREN (   context  )     ((context)->prettyFlags & PRETTYFLAG_PAREN)
#define PRETTYFLAG_INDENT   2
#define PRETTYFLAG_PAREN   1
#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
#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

Function Documentation

static void appendContextKeyword ( deparse_context context,
const char *  str,
int  indentBefore,
int  indentAfter,
int  indentPlus 
) [static]
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));
    }
}

List* deparse_context_for ( const char *  aliasname,
Oid  relid 
)

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);
}

char* deparse_expression ( Node expr,
List dpcontext,
bool  forceprefix,
bool  showimplicit 
)
static char * deparse_expression_pretty ( Node expr,
List dpcontext,
bool  forceprefix,
bool  showimplicit,
int  prettyFlags,
int  startIndent 
) [static]
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;
}

static void flatten_join_using_qual ( Node qual,
List **  leftvars,
List **  rightvars 
) [static]

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;
}

static char * generate_operator_name ( Oid  operid,
Oid  arg1,
Oid  arg2 
) [static]

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;
}

static char * generate_relation_name ( Oid  relid,
List namespaces 
) [static]

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

{
    List       *args = expr->args;

    if (list_length(args) == 2)
    {
        /* binary operator */
        Node       *arg1 = (Node *) linitial(args);
        Node       *arg2 = (Node *) lsecond(args);
        const char *op;

        op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
        if (strlen(op) == 1)
            return op;
    }
    return NULL;
}

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, ' ');
}

static bool has_unnamed_full_join_using ( Node jtnode  )  [static]

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++;
        }
    }
}

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

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   ) 
Datum pg_get_function_identity_arguments ( PG_FUNCTION_ARGS   ) 
Datum pg_get_function_result ( PG_FUNCTION_ARGS   ) 
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)));
}

char* pg_get_indexdef_columns ( Oid  indexrelid,
bool  pretty 
)

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   ) 
Datum pg_get_triggerdef_ext ( PG_FUNCTION_ARGS   ) 
static char * pg_get_triggerdef_worker ( Oid  trigid,
bool  pretty 
) [static]

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

{
    /* Free the ancestor list made in push_ancestor_plan */
    list_free(dpns->ancestors);

    /* Restore fields changed by push_ancestor_plan */
    *dpns = *save_dpns;
}

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 
)
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;
}

List* select_rtable_names_for_explain ( List rtable,
Bitmapset rels_used 
)

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]

Variable Documentation

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

Definition at line 265 of file ruleutils.c.

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