diff options
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 2847 |
1 files changed, 0 insertions, 2847 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c deleted file mode 100644 index 2c15bf916b9..00000000000 --- a/src/backend/utils/adt/ruleutils.c +++ /dev/null @@ -1,2847 +0,0 @@ -/********************************************************************** - * ruleutils.c - Functions to convert stored expressions/querytrees - * back to source text - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.108 2002/06/13 03:40:49 tgl Exp $ - * - * This software is copyrighted by Jan Wieck - Hamburg. - * - * The author hereby grants permission to use, copy, modify, - * distribute, and license this software and its documentation - * for any purpose, provided that existing copyright notices are - * retained in all copies and that this notice is included - * verbatim in any distributions. No written agreement, license, - * or royalty fee is required for any of the authorized uses. - * Modifications to this software may be copyrighted by their - * author and need not follow the licensing terms described - * here, provided that the new terms are clearly indicated on - * the first page of each file where they apply. - * - * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY - * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR - * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS - * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN - * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON - * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO - * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, - * ENHANCEMENTS, OR MODIFICATIONS. - * - **********************************************************************/ - -#include "postgres.h" - -#include <unistd.h> -#include <fcntl.h> - -#include "catalog/heap.h" -#include "catalog/index.h" -#include "catalog/namespace.h" -#include "catalog/pg_index.h" -#include "catalog/pg_opclass.h" -#include "catalog/pg_operator.h" -#include "catalog/pg_shadow.h" -#include "executor/spi.h" -#include "lib/stringinfo.h" -#include "nodes/makefuncs.h" -#include "optimizer/clauses.h" -#include "optimizer/tlist.h" -#include "parser/keywords.h" -#include "parser/parse_expr.h" -#include "parser/parse_func.h" -#include "parser/parse_oper.h" -#include "parser/parsetree.h" -#include "rewrite/rewriteManip.h" -#include "rewrite/rewriteSupport.h" -#include "utils/lsyscache.h" - - -/* ---------- - * Local data types - * ---------- - */ - -/* Context info needed for invoking a recursive querytree display routine */ -typedef struct -{ - StringInfo buf; /* output buffer to append to */ - List *namespaces; /* List of deparse_namespace nodes */ - bool varprefix; /* TRUE to print prefixes on Vars */ -} deparse_context; - -/* - * Each level of query context around a subtree needs a level of Var namespace. - * A Var having varlevelsup=N refers to the N'th item (counting from 0) in - * the current context's namespaces list. - * - * The rangetable is the list of actual RTEs from the query tree. - * - * For deparsing plan trees, we allow two special RTE entries that are not - * part of the rtable list (mainly because they don't have consecutively - * allocated varnos). - */ -typedef struct -{ - List *rtable; /* List of RangeTblEntry nodes */ - int outer_varno; /* varno for outer_rte */ - RangeTblEntry *outer_rte; /* special RangeTblEntry, or NULL */ - int inner_varno; /* varno for inner_rte */ - RangeTblEntry *inner_rte; /* special RangeTblEntry, or NULL */ -} deparse_namespace; - - -/* ---------- - * Global data - * ---------- - */ -static void *plan_getrulebyoid = NULL; -static char *query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1"; -static void *plan_getviewrule = NULL; -static char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2"; - - -/* ---------- - * Local functions - * - * Most of these functions used to use fixed-size buffers to build their - * results. Now, they take an (already initialized) StringInfo object - * as a parameter, and append their text output to its contents. - * ---------- - */ -static text *pg_do_getviewdef(Oid viewoid); -static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc); -static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc); -static void get_query_def(Query *query, StringInfo buf, List *parentnamespace); -static void get_select_query_def(Query *query, deparse_context *context); -static void get_insert_query_def(Query *query, deparse_context *context); -static void get_update_query_def(Query *query, deparse_context *context); -static void get_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); -static void get_setop_query(Node *setOp, Query *query, - deparse_context *context); -static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist, - bool force_colno, - deparse_context *context); -static void get_names_for_var(Var *var, deparse_context *context, - char **refname, char **attname); -static void get_rule_expr(Node *node, deparse_context *context); -static void get_oper_expr(Expr *expr, deparse_context *context); -static void get_func_expr(Expr *expr, deparse_context *context); -static void get_agg_expr(Aggref *aggref, deparse_context *context); -static Node *strip_type_coercion(Node *expr, Oid resultType); -static void get_tle_expr(TargetEntry *tle, deparse_context *context); -static void get_const_expr(Const *constval, deparse_context *context); -static void get_sublink_expr(Node *node, deparse_context *context); -static void get_from_clause(Query *query, deparse_context *context); -static void get_from_clause_item(Node *jtnode, Query *query, - deparse_context *context); -static void get_opclass_name(Oid opclass, Oid actual_datatype, - StringInfo buf); -static bool tleIsArrayAssign(TargetEntry *tle); -static char *generate_relation_name(Oid relid); -static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes); -static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2); -static char *get_relid_attribute_name(Oid relid, AttrNumber attnum); - -#define only_marker(rte) ((rte)->inh ? "" : "ONLY ") - - -/* ---------- - * get_ruledef - Do it all and return a text - * that could be used as a statement - * to recreate the rule - * ---------- - */ -Datum -pg_get_ruledef(PG_FUNCTION_ARGS) -{ - Oid ruleoid = PG_GETARG_OID(0); - text *ruledef; - Datum args[1]; - char nulls[1]; - int spirc; - HeapTuple ruletup; - TupleDesc rulettc; - StringInfoData buf; - int len; - - /* - * Connect to SPI manager - */ - if (SPI_connect() != SPI_OK_CONNECT) - elog(ERROR, "get_ruledef: cannot connect to SPI manager"); - - /* - * 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]; - void *plan; - - argtypes[0] = OIDOID; - plan = SPI_prepare(query_getrulebyoid, 1, argtypes); - if (plan == NULL) - elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getrulebyoid); - plan_getrulebyoid = SPI_saveplan(plan); - } - - /* - * Get the pg_rewrite tuple for this rule - */ - args[0] = ObjectIdGetDatum(ruleoid); - nulls[0] = ' '; - spirc = SPI_execp(plan_getrulebyoid, args, nulls, 1); - if (spirc != SPI_OK_SELECT) - elog(ERROR, "failed to get pg_rewrite tuple for %u", ruleoid); - if (SPI_processed != 1) - { - if (SPI_finish() != SPI_OK_FINISH) - elog(ERROR, "get_ruledef: SPI_finish() failed"); - ruledef = palloc(VARHDRSZ + 1); - VARATT_SIZEP(ruledef) = VARHDRSZ + 1; - VARDATA(ruledef)[0] = '-'; - PG_RETURN_TEXT_P(ruledef); - } - - ruletup = SPI_tuptable->vals[0]; - rulettc = SPI_tuptable->tupdesc; - - /* - * Get the rules definition and put it into executors memory - */ - initStringInfo(&buf); - make_ruledef(&buf, ruletup, rulettc); - len = buf.len + VARHDRSZ; - ruledef = SPI_palloc(len); - VARATT_SIZEP(ruledef) = len; - memcpy(VARDATA(ruledef), buf.data, buf.len); - pfree(buf.data); - - /* - * Disconnect from SPI manager - */ - if (SPI_finish() != SPI_OK_FINISH) - elog(ERROR, "get_ruledef: SPI_finish() failed"); - - /* - * Easy - isn't it? - */ - PG_RETURN_TEXT_P(ruledef); -} - - -/* ---------- - * get_viewdef - Mainly the same thing, but we - * only return the SELECT part of a view - * ---------- - */ -Datum -pg_get_viewdef(PG_FUNCTION_ARGS) -{ - /* By OID */ - Oid viewoid = PG_GETARG_OID(0); - text *ruledef; - - ruledef = pg_do_getviewdef(viewoid); - PG_RETURN_TEXT_P(ruledef); -} - -Datum -pg_get_viewdef_name(PG_FUNCTION_ARGS) -{ - /* By qualified name */ - text *viewname = PG_GETARG_TEXT_P(0); - RangeVar *viewrel; - Oid viewoid; - text *ruledef; - - viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname, - "get_viewdef")); - viewoid = RangeVarGetRelid(viewrel, false); - - ruledef = pg_do_getviewdef(viewoid); - PG_RETURN_TEXT_P(ruledef); -} - -/* - * Common code for by-OID and by-name variants of pg_get_viewdef - */ -static text * -pg_do_getviewdef(Oid viewoid) -{ - text *ruledef; - Datum args[2]; - char nulls[2]; - int spirc; - HeapTuple ruletup; - TupleDesc rulettc; - StringInfoData buf; - int len; - - /* - * Connect to SPI manager - */ - if (SPI_connect() != SPI_OK_CONNECT) - elog(ERROR, "get_viewdef: cannot connect to SPI manager"); - - /* - * 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]; - void *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); - plan_getviewrule = SPI_saveplan(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_execp(plan_getviewrule, args, nulls, 2); - if (spirc != SPI_OK_SELECT) - elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid); - initStringInfo(&buf); - if (SPI_processed != 1) - appendStringInfo(&buf, "Not a view"); - else - { - /* - * Get the rules definition and put it into executors memory - */ - ruletup = SPI_tuptable->vals[0]; - rulettc = SPI_tuptable->tupdesc; - make_viewdef(&buf, ruletup, rulettc); - } - len = buf.len + VARHDRSZ; - ruledef = SPI_palloc(len); - VARATT_SIZEP(ruledef) = len; - memcpy(VARDATA(ruledef), buf.data, buf.len); - pfree(buf.data); - - /* - * Disconnect from SPI manager - */ - if (SPI_finish() != SPI_OK_FINISH) - elog(ERROR, "get_viewdef: SPI_finish() failed"); - - return ruledef; -} - - -/* ---------- - * get_indexdef - Get the definition of an index - * ---------- - */ -Datum -pg_get_indexdef(PG_FUNCTION_ARGS) -{ - Oid indexrelid = PG_GETARG_OID(0); - text *indexdef; - HeapTuple ht_idx; - HeapTuple ht_idxrel; - HeapTuple ht_am; - Form_pg_index idxrec; - Form_pg_class idxrelrec; - Form_pg_am amrec; - Oid indrelid; - int len; - int keyno; - Oid keycoltypes[INDEX_MAX_KEYS]; - StringInfoData buf; - StringInfoData keybuf; - char *sep; - - /* - * Fetch the pg_index tuple by the Oid of the index - */ - ht_idx = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(indexrelid), - 0, 0, 0); - if (!HeapTupleIsValid(ht_idx)) - elog(ERROR, "syscache lookup for index %u failed", indexrelid); - idxrec = (Form_pg_index) GETSTRUCT(ht_idx); - - indrelid = idxrec->indrelid; - Assert(indexrelid == idxrec->indexrelid); - - /* - * Fetch the pg_class tuple of the index relation - */ - ht_idxrel = SearchSysCache(RELOID, - ObjectIdGetDatum(indexrelid), - 0, 0, 0); - if (!HeapTupleIsValid(ht_idxrel)) - elog(ERROR, "syscache lookup for relid %u failed", indexrelid); - idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel); - - /* - * Fetch the pg_am tuple of the index' access method - */ - ht_am = SearchSysCache(AMOID, - ObjectIdGetDatum(idxrelrec->relam), - 0, 0, 0); - if (!HeapTupleIsValid(ht_am)) - elog(ERROR, "syscache lookup for AM %u failed", idxrelrec->relam); - amrec = (Form_pg_am) GETSTRUCT(ht_am); - - /* - * 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); - appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (", - idxrec->indisunique ? "UNIQUE " : "", - quote_identifier(NameStr(idxrelrec->relname)), - generate_relation_name(indrelid), - quote_identifier(NameStr(amrec->amname))); - - /* - * Collect the indexed attributes in keybuf - */ - initStringInfo(&keybuf); - sep = ""; - for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++) - { - AttrNumber attnum = idxrec->indkey[keyno]; - char *attname; - - if (attnum == InvalidAttrNumber) - break; - - attname = get_relid_attribute_name(indrelid, attnum); - keycoltypes[keyno] = get_atttype(indrelid, attnum); - - appendStringInfo(&keybuf, sep); - sep = ", "; - - /* - * Add the indexed field name - */ - appendStringInfo(&keybuf, "%s", quote_identifier(attname)); - - /* - * If not a functional index, add the operator class name - */ - if (idxrec->indproc == InvalidOid) - get_opclass_name(idxrec->indclass[keyno], - keycoltypes[keyno], - &keybuf); - } - - if (idxrec->indproc != InvalidOid) - { - /* - * For functional index say 'func (attrs) opclass' - */ - appendStringInfo(&buf, "%s(%s)", - generate_function_name(idxrec->indproc, - keyno, keycoltypes), - keybuf.data); - get_opclass_name(idxrec->indclass[0], - get_func_rettype(idxrec->indproc), - &buf); - } - else - { - /* - * Otherwise say 'attr opclass [, ...]' - */ - appendStringInfo(&buf, "%s", keybuf.data); - } - - appendStringInfoChar(&buf, ')'); - - /* - * If it's a partial index, decompile and append the predicate - */ - if (VARSIZE(&idxrec->indpred) > VARHDRSZ) - { - Node *node; - List *context; - char *exprstr; - char *str; - - /* Convert TEXT object to C string */ - exprstr = DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(&idxrec->indpred))); - /* Convert expression to node tree */ - node = (Node *) stringToNode(exprstr); - - /* - * If top level is a List, assume it is an implicit-AND structure, - * and convert to explicit AND. This is needed for partial index - * predicates. - */ - if (node && IsA(node, List)) - node = (Node *) make_ands_explicit((List *) node); - /* Deparse */ - context = deparse_context_for(get_rel_name(indrelid), indrelid); - str = deparse_expression(node, context, false); - appendStringInfo(&buf, " WHERE %s", str); - } - - /* - * Create the result as a TEXT datum, and free working data - */ - len = buf.len + VARHDRSZ; - indexdef = (text *) palloc(len); - VARATT_SIZEP(indexdef) = len; - memcpy(VARDATA(indexdef), buf.data, buf.len); - - pfree(buf.data); - pfree(keybuf.data); - - ReleaseSysCache(ht_idx); - ReleaseSysCache(ht_idxrel); - ReleaseSysCache(ht_am); - - PG_RETURN_TEXT_P(indexdef); -} - - -/* ---------- - * get_expr - Decompile an expression tree - * - * Input: an expression tree in nodeToString form, and a relation OID - * - * Output: reverse-listed expression - * - * Currently, the expression can only refer to a single relation, namely - * the one specified by the second parameter. This is sufficient for - * partial indexes, column default expressions, etc. - * ---------- - */ -Datum -pg_get_expr(PG_FUNCTION_ARGS) -{ - text *expr = PG_GETARG_TEXT_P(0); - Oid relid = PG_GETARG_OID(1); - text *result; - Node *node; - List *context; - char *exprstr; - char *relname; - char *str; - - /* Get the name for the relation */ - relname = get_rel_name(relid); - if (relname == NULL) - PG_RETURN_NULL(); /* should we raise an error? */ - - /* Convert input TEXT object to C string */ - exprstr = DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(expr))); - - /* Convert expression to node tree */ - node = (Node *) stringToNode(exprstr); - - /* - * If top level is a List, assume it is an implicit-AND structure, and - * convert to explicit AND. This is needed for partial index - * predicates. - */ - if (node && IsA(node, List)) - node = (Node *) make_ands_explicit((List *) node); - - /* Deparse */ - context = deparse_context_for(relname, relid); - str = deparse_expression(node, context, false); - - /* Pass the result back as TEXT */ - result = DatumGetTextP(DirectFunctionCall1(textin, - CStringGetDatum(str))); - - PG_RETURN_TEXT_P(result); -} - - -/* ---------- - * get_userbyid - Get a user name by usesysid and - * fallback to 'unknown (UID=n)' - * ---------- - */ -Datum -pg_get_userbyid(PG_FUNCTION_ARGS) -{ - int32 uid = PG_GETARG_INT32(0); - Name result; - HeapTuple usertup; - Form_pg_shadow user_rec; - - /* - * Allocate space for the result - */ - result = (Name) palloc(NAMEDATALEN); - memset(NameStr(*result), 0, NAMEDATALEN); - - /* - * Get the pg_shadow entry and print the result - */ - usertup = SearchSysCache(SHADOWSYSID, - ObjectIdGetDatum(uid), - 0, 0, 0); - if (HeapTupleIsValid(usertup)) - { - user_rec = (Form_pg_shadow) GETSTRUCT(usertup); - StrNCpy(NameStr(*result), NameStr(user_rec->usename), NAMEDATALEN); - ReleaseSysCache(usertup); - } - else - sprintf(NameStr(*result), "unknown (UID=%d)", uid); - - PG_RETURN_NAME(result); -} - -/* ---------- - * deparse_expression - General utility for deparsing expressions - * - * expr is the node tree to be deparsed. It must be a transformed expression - * tree (ie, not the raw output of gram.y). - * - * dpcontext is a list of deparse_namespace nodes representing the context - * for interpreting Vars in the node tree. - * - * forceprefix is TRUE to force all Vars to be prefixed with their table names. - * - * The result is a palloc'd string. - * ---------- - */ -char * -deparse_expression(Node *expr, List *dpcontext, bool forceprefix) -{ - StringInfoData buf; - deparse_context context; - - initStringInfo(&buf); - context.buf = &buf; - context.namespaces = dpcontext; - context.varprefix = forceprefix; - - get_rule_expr(expr, &context); - - return buf.data; -} - -/* ---------- - * deparse_context_for - Build deparse context for a single relation - * - * Given the reference name (alias) and OID of a relation, build deparsing - * context for an expression referencing only that relation (as varno 1, - * varlevelsup 0). This is sufficient for many uses of deparse_expression. - * ---------- - */ -List * -deparse_context_for(const char *aliasname, Oid relid) -{ - deparse_namespace *dpns; - RangeTblEntry *rte; - - dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace)); - - /* Build a minimal RTE for the rel */ - rte = makeNode(RangeTblEntry); - rte->rtekind = RTE_RELATION; - rte->relid = relid; - rte->eref = makeAlias(aliasname, NIL); - rte->inh = false; - rte->inFromCl = true; - - /* Build one-element rtable */ - dpns->rtable = makeList1(rte); - dpns->outer_varno = dpns->inner_varno = 0; - dpns->outer_rte = dpns->inner_rte = NULL; - - /* Return a one-deep namespace stack */ - return makeList1(dpns); -} - -/* - * deparse_context_for_plan - Build deparse context for a plan node - * - * We assume we are dealing with an upper-level plan node having either - * one or two referenceable children (pass innercontext = NULL if only one). - * The passed-in Nodes should be made using deparse_context_for_subplan - * and/or deparse_context_for_relation. The resulting context will work - * for deparsing quals, tlists, etc of the plan node. - * - * An rtable list can also be passed in case plain Vars might be seen. - * This is not needed for true upper-level expressions, but is helpful for - * Sort nodes and similar cases with slightly bogus targetlists. - */ -List * -deparse_context_for_plan(int outer_varno, Node *outercontext, - int inner_varno, Node *innercontext, - List *rtable) -{ - deparse_namespace *dpns; - - dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace)); - - dpns->rtable = rtable; - dpns->outer_varno = outer_varno; - dpns->outer_rte = (RangeTblEntry *) outercontext; - dpns->inner_varno = inner_varno; - dpns->inner_rte = (RangeTblEntry *) innercontext; - - /* Return a one-deep namespace stack */ - return makeList1(dpns); -} - -/* - * deparse_context_for_rte - Build deparse context for 1 relation - * - * Helper routine to build one of the inputs for deparse_context_for_plan. - * - * The returned node is actually the given RangeTblEntry, but we declare it - * as just Node to discourage callers from assuming anything. - */ -Node * -deparse_context_for_rte(RangeTblEntry *rte) -{ - return (Node *) rte; -} - -/* - * deparse_context_for_subplan - Build deparse context for a plan node - * - * Helper routine to build one of the inputs for deparse_context_for_plan. - * Pass the tlist of the subplan node, plus the query rangetable. - * - * The returned node is actually a RangeTblEntry, but we declare it as just - * Node to discourage callers from assuming anything. - */ -Node * -deparse_context_for_subplan(const char *name, List *tlist, - List *rtable) -{ - RangeTblEntry *rte = makeNode(RangeTblEntry); - List *attrs = NIL; - int nattrs = 0; - int rtablelength = length(rtable); - List *tl; - char buf[32]; - - foreach(tl, tlist) - { - TargetEntry *tle = lfirst(tl); - Resdom *resdom = tle->resdom; - - nattrs++; - Assert(resdom->resno == nattrs); - if (resdom->resname) - { - attrs = lappend(attrs, makeString(resdom->resname)); - continue; - } - if (tle->expr && IsA(tle->expr, Var)) - { - Var *var = (Var *) tle->expr; - - /* varno/varattno won't be any good, but varnoold might be */ - if (var->varnoold > 0 && var->varnoold <= rtablelength) - { - RangeTblEntry *varrte = rt_fetch(var->varnoold, rtable); - char *varname; - - varname = get_rte_attribute_name(varrte, var->varoattno); - attrs = lappend(attrs, makeString(varname)); - continue; - } - } - /* Fallback if can't get name */ - snprintf(buf, sizeof(buf), "?column%d?", resdom->resno); - attrs = lappend(attrs, makeString(pstrdup(buf))); - } - - rte->rtekind = RTE_SPECIAL; /* XXX */ - rte->relid = InvalidOid; - rte->eref = makeAlias(name, attrs); - rte->inh = false; - rte->inFromCl = true; - - return (Node *) rte; -} - -/* ---------- - * make_ruledef - reconstruct the CREATE RULE command - * for a given pg_rewrite tuple - * ---------- - */ -static void -make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) -{ - char *rulename; - char ev_type; - Oid ev_class; - int2 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 ON ", - quote_identifier(rulename)); - - /* 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: - elog(ERROR, "get_ruledef: 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)); - 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; - - 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 *) lfirst(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); - - context.buf = buf; - context.namespaces = makeList1(&dpns); - context.varprefix = (length(query->rtable) != 1); - dpns.rtable = query->rtable; - dpns.outer_varno = dpns.inner_varno = 0; - dpns.outer_rte = dpns.inner_rte = NULL; - - get_rule_expr(qual, &context); - } - - appendStringInfo(buf, " DO "); - - /* The INSTEAD keyword (if so) */ - if (is_instead) - appendStringInfo(buf, "INSTEAD "); - - /* Finally the rules actions */ - if (length(actions) > 1) - { - List *action; - Query *query; - - appendStringInfo(buf, "("); - foreach(action, actions) - { - query = (Query *) lfirst(action); - get_query_def(query, buf, NIL); - appendStringInfo(buf, "; "); - } - appendStringInfo(buf, ");"); - } - else - { - if (length(actions) == 0) - { - appendStringInfo(buf, "NOTHING;"); - } - else - { - Query *query; - - query = (Query *) lfirst(actions); - get_query_def(query, buf, NIL); - appendStringInfo(buf, ";"); - } - } -} - - -/* ---------- - * make_viewdef - reconstruct the SELECT part of a - * view rewrite rule - * ---------- - */ -static void -make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) -{ - Query *query; - char ev_type; - Oid ev_class; - int2 ev_attr; - bool is_instead; - char *ev_qual; - char *ev_action; - List *actions = NIL; - 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 = (int2) 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 (length(actions) != 1) - { - appendStringInfo(buf, "Not a view"); - return; - } - - query = (Query *) lfirst(actions); - - if (ev_type != '1' || ev_attr >= 0 || !is_instead || - strcmp(ev_qual, "<>") != 0) - { - appendStringInfo(buf, "Not a view"); - return; - } - - get_query_def(query, buf, NIL); - appendStringInfo(buf, ";"); -} - - -/* ---------- - * get_query_def - Parse back one action from - * the parsetree in the actions - * list - * ---------- - */ -static void -get_query_def(Query *query, StringInfo buf, List *parentnamespace) -{ - deparse_context context; - deparse_namespace dpns; - - context.buf = buf; - context.namespaces = lcons(&dpns, parentnamespace); - context.varprefix = (parentnamespace != NIL || - length(query->rtable) != 1); - dpns.rtable = query->rtable; - dpns.outer_varno = dpns.inner_varno = 0; - dpns.outer_rte = dpns.inner_rte = NULL; - - switch (query->commandType) - { - case CMD_SELECT: - get_select_query_def(query, &context); - 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, "get_query_def: unknown query command type %d", - query->commandType); - break; - } -} - - -/* ---------- - * get_select_query_def - Parse back a SELECT parsetree - * ---------- - */ -static void -get_select_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - bool force_colno; - char *sep; - List *l; - - /* - * If the Query node has a setOperations tree, then it's the top level - * of a UNION/INTERSECT/EXCEPT query; only the ORDER BY and LIMIT - * fields are interesting in the top query itself. - */ - if (query->setOperations) - { - get_setop_query(query->setOperations, query, context); - /* ORDER BY clauses must be simple in this case */ - force_colno = true; - } - else - { - get_basic_select_query(query, context); - force_colno = false; - } - - /* Add the ORDER BY clause if given */ - if (query->sortClause != NIL) - { - appendStringInfo(buf, " ORDER BY "); - sep = ""; - foreach(l, query->sortClause) - { - SortClause *srt = (SortClause *) lfirst(l); - Node *sortexpr; - Oid sortcoltype; - char *opname; - - appendStringInfo(buf, sep); - sortexpr = get_rule_sortgroupclause(srt, query->targetList, - force_colno, context); - sortcoltype = exprType(sortexpr); - opname = generate_operator_name(srt->sortop, - sortcoltype, sortcoltype); - if (strcmp(opname, "<") != 0) - { - if (strcmp(opname, ">") == 0) - appendStringInfo(buf, " DESC"); - else - appendStringInfo(buf, " USING %s", opname); - } - sep = ", "; - } - } - - /* Add the LIMIT clause if given */ - if (query->limitOffset != NULL) - { - appendStringInfo(buf, " OFFSET "); - get_rule_expr(query->limitOffset, context); - } - if (query->limitCount != NULL) - { - appendStringInfo(buf, " LIMIT "); - if (IsA(query->limitCount, Const) && - ((Const *) query->limitCount)->constisnull) - appendStringInfo(buf, "ALL"); - else - get_rule_expr(query->limitCount, context); - } -} - -static void -get_basic_select_query(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - char *sep; - List *l; - - /* - * Build up the query string - first we say SELECT - */ - appendStringInfo(buf, "SELECT"); - - /* Add the DISTINCT clause if given */ - if (query->distinctClause != NIL) - { - if (has_distinct_on_clause(query)) - { - appendStringInfo(buf, " DISTINCT ON ("); - sep = ""; - foreach(l, query->distinctClause) - { - SortClause *srt = (SortClause *) lfirst(l); - - appendStringInfo(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) */ - sep = " "; - foreach(l, query->targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - bool tell_as = false; - - if (tle->resdom->resjunk) - continue; /* ignore junk entries */ - - appendStringInfo(buf, sep); - sep = ", "; - - /* Do NOT use get_tle_expr here; see its comments! */ - get_rule_expr(tle->expr, context); - - /* Check if we must say AS ... */ - if (!IsA(tle->expr, Var)) - tell_as = (strcmp(tle->resdom->resname, "?column?") != 0); - else - { - Var *var = (Var *) (tle->expr); - char *refname; - char *attname; - - get_names_for_var(var, context, &refname, &attname); - tell_as = (attname == NULL || - strcmp(attname, tle->resdom->resname) != 0); - } - - /* and do if so */ - if (tell_as) - appendStringInfo(buf, " AS %s", - quote_identifier(tle->resdom->resname)); - } - - /* Add the FROM clause if needed */ - get_from_clause(query, context); - - /* Add the WHERE clause if given */ - if (query->jointree->quals != NULL) - { - appendStringInfo(buf, " WHERE "); - get_rule_expr(query->jointree->quals, context); - } - - /* Add the GROUP BY clause if given */ - if (query->groupClause != NULL) - { - appendStringInfo(buf, " GROUP BY "); - sep = ""; - foreach(l, query->groupClause) - { - GroupClause *grp = (GroupClause *) lfirst(l); - - appendStringInfo(buf, sep); - get_rule_sortgroupclause(grp, query->targetList, - false, context); - sep = ", "; - } - } - - /* Add the HAVING clause if given */ - if (query->havingQual != NULL) - { - appendStringInfo(buf, " HAVING "); - get_rule_expr(query->havingQual, context); - } -} - -static void -get_setop_query(Node *setOp, Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - - if (IsA(setOp, RangeTblRef)) - { - RangeTblRef *rtr = (RangeTblRef *) setOp; - RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); - Query *subquery = rte->subquery; - - Assert(subquery != NULL); - get_query_def(subquery, buf, context->namespaces); - } - else if (IsA(setOp, SetOperationStmt)) - { - SetOperationStmt *op = (SetOperationStmt *) setOp; - - appendStringInfo(buf, "(("); - get_setop_query(op->larg, query, context); - switch (op->op) - { - case SETOP_UNION: - appendStringInfo(buf, ") UNION "); - break; - case SETOP_INTERSECT: - appendStringInfo(buf, ") INTERSECT "); - break; - case SETOP_EXCEPT: - appendStringInfo(buf, ") EXCEPT "); - break; - default: - elog(ERROR, "get_setop_query: unexpected set op %d", - (int) op->op); - } - if (op->all) - appendStringInfo(buf, "ALL ("); - else - appendStringInfo(buf, "("); - get_setop_query(op->rarg, query, context); - appendStringInfo(buf, "))"); - } - else - { - elog(ERROR, "get_setop_query: unexpected node %d", - (int) nodeTag(setOp)); - } -} - -/* - * Display a sort/group clause. - * - * Also returns the expression tree, so caller need not find it again. - */ -static Node * -get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno, - deparse_context *context) -{ - StringInfo buf = context->buf; - TargetEntry *tle; - Node *expr; - - tle = get_sortgroupclause_tle(srt, tlist); - expr = tle->expr; - - /* - * Use column-number form if requested by caller or if expression is a - * constant --- a constant is ambiguous (and will be misinterpreted by - * findTargetlistEntry()) if we dump it explicitly. - */ - if (force_colno || (expr && IsA(expr, Const))) - { - Assert(!tle->resdom->resjunk); - appendStringInfo(buf, "%d", tle->resdom->resno); - } - else - get_rule_expr(expr, context); - - return expr; -} - -/* ---------- - * get_insert_query_def - Parse back an INSERT parsetree - * ---------- - */ -static void -get_insert_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - RangeTblEntry *select_rte = NULL; - RangeTblEntry *rte; - char *sep; - List *l; - - /* - * If it's an INSERT ... SELECT there will be a single subquery RTE - * for the SELECT. - */ - foreach(l, query->rtable) - { - rte = (RangeTblEntry *) lfirst(l); - if (rte->rtekind != RTE_SUBQUERY) - continue; - if (select_rte) - elog(ERROR, "get_insert_query_def: too many RTEs in INSERT!"); - select_rte = rte; - } - - /* - * Start the query with INSERT INTO relname - */ - rte = rt_fetch(query->resultRelation, query->rtable); - Assert(rte->rtekind == RTE_RELATION); - appendStringInfo(buf, "INSERT INTO %s", - generate_relation_name(rte->relid)); - - /* Add the insert-column-names list */ - sep = " ("; - foreach(l, query->targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - - if (tle->resdom->resjunk) - continue; /* ignore junk entries */ - - appendStringInfo(buf, sep); - sep = ", "; - appendStringInfo(buf, "%s", quote_identifier(tle->resdom->resname)); - } - appendStringInfo(buf, ") "); - - /* Add the VALUES or the SELECT */ - if (select_rte == NULL) - { - appendStringInfo(buf, "VALUES ("); - sep = ""; - foreach(l, query->targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - - if (tle->resdom->resjunk) - continue; /* ignore junk entries */ - - appendStringInfo(buf, sep); - sep = ", "; - get_tle_expr(tle, context); - } - appendStringInfoChar(buf, ')'); - } - else - get_query_def(select_rte->subquery, buf, NIL); -} - - -/* ---------- - * get_update_query_def - Parse back an UPDATE parsetree - * ---------- - */ -static void -get_update_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - char *sep; - RangeTblEntry *rte; - List *l; - - /* - * Start the query with UPDATE relname SET - */ - rte = rt_fetch(query->resultRelation, query->rtable); - Assert(rte->rtekind == RTE_RELATION); - appendStringInfo(buf, "UPDATE %s%s SET ", - only_marker(rte), - generate_relation_name(rte->relid)); - - /* Add the comma separated list of 'attname = value' */ - sep = ""; - foreach(l, query->targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - - if (tle->resdom->resjunk) - continue; /* ignore junk entries */ - - appendStringInfo(buf, sep); - sep = ", "; - - /* - * If the update expression is an array assignment, we mustn't put - * out "attname =" here; it will come out of the display of the - * ArrayRef node instead. - */ - if (!tleIsArrayAssign(tle)) - appendStringInfo(buf, "%s = ", - quote_identifier(tle->resdom->resname)); - get_tle_expr(tle, context); - } - - /* Add the FROM clause if needed */ - get_from_clause(query, context); - - /* Finally add a WHERE clause if given */ - if (query->jointree->quals != NULL) - { - appendStringInfo(buf, " WHERE "); - get_rule_expr(query->jointree->quals, context); - } -} - - -/* ---------- - * get_delete_query_def - Parse back a DELETE parsetree - * ---------- - */ -static void -get_delete_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - RangeTblEntry *rte; - - /* - * Start the query with DELETE FROM relname - */ - rte = rt_fetch(query->resultRelation, query->rtable); - Assert(rte->rtekind == RTE_RELATION); - appendStringInfo(buf, "DELETE FROM %s%s", - only_marker(rte), - generate_relation_name(rte->relid)); - - /* Add a WHERE clause if given */ - if (query->jointree->quals != NULL) - { - appendStringInfo(buf, " WHERE "); - get_rule_expr(query->jointree->quals, context); - } -} - - -/* ---------- - * get_utility_query_def - Parse back a UTILITY parsetree - * ---------- - */ -static void -get_utility_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - - if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt)) - { - NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt; - - appendStringInfo(buf, "NOTIFY %s", - quote_qualified_identifier(stmt->relation->schemaname, - stmt->relation->relname)); - } - else - elog(ERROR, "get_utility_query_def: unexpected statement type"); -} - - -/* - * Get the relation refname and attname for a (possibly nonlocal) Var. - * - * refname will be returned as NULL if the Var references an unnamed join. - * In this case the Var *must* be displayed without any qualification. - * - * attname will be returned as NULL if the Var represents a whole tuple - * of the relation. - */ -static void -get_names_for_var(Var *var, deparse_context *context, - char **refname, char **attname) -{ - List *nslist = context->namespaces; - int sup = var->varlevelsup; - deparse_namespace *dpns; - RangeTblEntry *rte; - - /* Find appropriate nesting depth */ - while (sup-- > 0 && nslist != NIL) - nslist = lnext(nslist); - if (nslist == NIL) - elog(ERROR, "get_names_for_var: bogus varlevelsup %d", - var->varlevelsup); - dpns = (deparse_namespace *) lfirst(nslist); - - /* Find the relevant RTE */ - if (var->varno >= 1 && var->varno <= length(dpns->rtable)) - rte = rt_fetch(var->varno, dpns->rtable); - else if (var->varno == dpns->outer_varno) - rte = dpns->outer_rte; - else if (var->varno == dpns->inner_varno) - rte = dpns->inner_rte; - else - rte = NULL; - if (rte == NULL) - elog(ERROR, "get_names_for_var: bogus varno %d", - var->varno); - - /* Emit results */ - if (rte->rtekind == RTE_JOIN && rte->alias == NULL) - *refname = NULL; - else - *refname = rte->eref->aliasname; - - if (var->varattno == InvalidAttrNumber) - *attname = NULL; - else - *attname = get_rte_attribute_name(rte, var->varattno); -} - -/* ---------- - * get_rule_expr - Parse back an expression - * ---------- - */ -static void -get_rule_expr(Node *node, deparse_context *context) -{ - 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. - * - * There might be some work left here to support additional node types. - * Can we ever see Param nodes here? - */ - switch (nodeTag(node)) - { - case T_Const: - get_const_expr((Const *) node, context); - break; - - case T_Var: - { - Var *var = (Var *) node; - char *refname; - char *attname; - - get_names_for_var(var, context, &refname, &attname); - if (refname && (context->varprefix || attname == NULL)) - { - if (strcmp(refname, "*NEW*") == 0) - appendStringInfo(buf, "new"); - else if (strcmp(refname, "*OLD*") == 0) - appendStringInfo(buf, "old"); - else - appendStringInfo(buf, "%s", - quote_identifier(refname)); - if (attname) - appendStringInfoChar(buf, '.'); - } - if (attname) - appendStringInfo(buf, "%s", quote_identifier(attname)); - } - break; - - case T_Expr: - { - Expr *expr = (Expr *) node; - List *args = expr->args; - - /* - * Expr nodes have to be handled a bit detailed - */ - switch (expr->opType) - { - case OP_EXPR: - get_oper_expr(expr, context); - break; - - case FUNC_EXPR: - get_func_expr(expr, context); - break; - - case OR_EXPR: - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) lfirst(args), context); - while ((args = lnext(args)) != NIL) - { - appendStringInfo(buf, " OR "); - get_rule_expr((Node *) lfirst(args), context); - } - appendStringInfoChar(buf, ')'); - break; - - case AND_EXPR: - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) lfirst(args), context); - while ((args = lnext(args)) != NIL) - { - appendStringInfo(buf, " AND "); - get_rule_expr((Node *) lfirst(args), context); - } - appendStringInfoChar(buf, ')'); - break; - - case NOT_EXPR: - appendStringInfo(buf, "(NOT "); - get_rule_expr((Node *) lfirst(args), context); - appendStringInfoChar(buf, ')'); - break; - - case SUBPLAN_EXPR: - /* - * We cannot see an already-planned subplan in rule - * deparsing, only while EXPLAINing a query plan. - * For now, just punt. - */ - appendStringInfo(buf, "(subplan)"); - break; - - default: - elog(ERROR, "get_rule_expr: expr opType %d not supported", - expr->opType); - } - } - break; - - case T_Aggref: - get_agg_expr((Aggref *) node, context); - break; - - case T_ArrayRef: - { - ArrayRef *aref = (ArrayRef *) node; - bool savevarprefix = context->varprefix; - List *lowlist; - List *uplist; - - /* - * If we are doing UPDATE array[n] = expr, we need to - * suppress any prefix on the array name. Currently, that - * is the only context in which we will see a non-null - * refassgnexpr --- but someday a smarter test may be - * needed. - */ - if (aref->refassgnexpr) - context->varprefix = false; - get_rule_expr(aref->refexpr, context); - context->varprefix = savevarprefix; - lowlist = aref->reflowerindexpr; - foreach(uplist, aref->refupperindexpr) - { - appendStringInfo(buf, "["); - if (lowlist) - { - get_rule_expr((Node *) lfirst(lowlist), context); - appendStringInfo(buf, ":"); - lowlist = lnext(lowlist); - } - get_rule_expr((Node *) lfirst(uplist), context); - appendStringInfo(buf, "]"); - } - if (aref->refassgnexpr) - { - appendStringInfo(buf, " = "); - get_rule_expr(aref->refassgnexpr, context); - } - } - break; - - case T_FieldSelect: - { - FieldSelect *fselect = (FieldSelect *) node; - Oid argType = exprType(fselect->arg); - HeapTuple typetup; - Form_pg_type typeStruct; - Oid typrelid; - char *fieldname; - - /* lookup arg type and get the field name */ - typetup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(argType), - 0, 0, 0); - if (!HeapTupleIsValid(typetup)) - elog(ERROR, "cache lookup of type %u failed", - argType); - typeStruct = (Form_pg_type) GETSTRUCT(typetup); - typrelid = typeStruct->typrelid; - if (!OidIsValid(typrelid)) - elog(ERROR, "Argument type %s of FieldSelect is not a tuple type", - format_type_be(argType)); - ReleaseSysCache(typetup); - fieldname = get_relid_attribute_name(typrelid, - fselect->fieldnum); - /* - * If the argument is simple enough, we could emit - * arg.fieldname, but most cases where FieldSelect is used - * are *not* simple. So, always use parenthesized syntax. - */ - appendStringInfoChar(buf, '('); - get_rule_expr(fselect->arg, context); - appendStringInfo(buf, ").%s", quote_identifier(fieldname)); - } - break; - - case T_RelabelType: - { - RelabelType *relabel = (RelabelType *) node; - - appendStringInfoChar(buf, '('); - get_rule_expr(relabel->arg, context); - appendStringInfo(buf, ")::%s", - format_type_with_typemod(relabel->resulttype, - relabel->resulttypmod)); - } - break; - - case T_CaseExpr: - { - CaseExpr *caseexpr = (CaseExpr *) node; - List *temp; - - appendStringInfo(buf, "CASE"); - foreach(temp, caseexpr->args) - { - CaseWhen *when = (CaseWhen *) lfirst(temp); - - appendStringInfo(buf, " WHEN "); - get_rule_expr(when->expr, context); - appendStringInfo(buf, " THEN "); - get_rule_expr(when->result, context); - } - appendStringInfo(buf, " ELSE "); - get_rule_expr(caseexpr->defresult, context); - appendStringInfo(buf, " END"); - } - break; - - case T_NullTest: - { - NullTest *ntest = (NullTest *) node; - - appendStringInfo(buf, "("); - get_rule_expr(ntest->arg, context); - switch (ntest->nulltesttype) - { - case IS_NULL: - appendStringInfo(buf, " IS NULL)"); - break; - case IS_NOT_NULL: - appendStringInfo(buf, " IS NOT NULL)"); - break; - default: - elog(ERROR, "get_rule_expr: unexpected nulltesttype %d", - (int) ntest->nulltesttype); - } - } - break; - - case T_BooleanTest: - { - BooleanTest *btest = (BooleanTest *) node; - - appendStringInfo(buf, "("); - get_rule_expr(btest->arg, context); - 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, "get_rule_expr: unexpected booltesttype %d", - (int) btest->booltesttype); - } - } - break; - - case T_SubLink: - get_sublink_expr(node, context); - break; - - case T_Param: - { - Param *param = (Param *) node; - - switch (param->paramkind) - { - case PARAM_NAMED: - case PARAM_NEW: - case PARAM_OLD: - appendStringInfo(buf, "$%s", param->paramname); - break; - case PARAM_NUM: - case PARAM_EXEC: - appendStringInfo(buf, "$%d", param->paramid); - break; - default: - appendStringInfo(buf, "(param)"); - break; - } - } - break; - - default: - elog(ERROR, "get_rule_expr: unknown node type %d", nodeTag(node)); - break; - } -} - - -/* - * get_oper_expr - Parse back an Oper node - */ -static void -get_oper_expr(Expr *expr, deparse_context *context) -{ - StringInfo buf = context->buf; - Oid opno = ((Oper *) expr->oper)->opno; - List *args = expr->args; - - appendStringInfoChar(buf, '('); - if (length(args) == 2) - { - /* binary operator */ - Node *arg1 = (Node *) lfirst(args); - Node *arg2 = (Node *) lsecond(args); - - get_rule_expr(arg1, context); - appendStringInfo(buf, " %s ", - generate_operator_name(opno, - exprType(arg1), - exprType(arg2))); - get_rule_expr(arg2, context); - } - else - { - /* unary operator --- but which side? */ - Node *arg = (Node *) lfirst(args); - HeapTuple tp; - Form_pg_operator optup; - - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); - if (!HeapTupleIsValid(tp)) - elog(ERROR, "cache lookup for operator %u failed", 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(arg, context); - break; - case 'r': - get_rule_expr(arg, context); - appendStringInfo(buf, " %s", - generate_operator_name(opno, - exprType(arg), - InvalidOid)); - break; - default: - elog(ERROR, "get_rule_expr: bogus oprkind"); - } - ReleaseSysCache(tp); - } - appendStringInfoChar(buf, ')'); -} - -/* - * get_func_expr - Parse back a Func node - */ -static void -get_func_expr(Expr *expr, deparse_context *context) -{ - StringInfo buf = context->buf; - Func *func = (Func *) (expr->oper); - Oid funcoid = func->funcid; - int32 coercedTypmod; - Oid argtypes[FUNC_MAX_ARGS]; - int nargs; - List *l; - char *sep; - - /* - * Check to see if function is a length-coercion function for some - * datatype. If so, display the operation as a type cast. - */ - if (exprIsLengthCoercion((Node *) expr, &coercedTypmod)) - { - Node *arg = lfirst(expr->args); - Oid rettype = get_func_rettype(funcoid); - char *typdesc; - - /* - * Strip off any type coercions on the input, so we don't print - * redundancies like x::bpchar::character(8). - * - * XXX Are there any cases where this is a bad idea? - */ - arg = strip_type_coercion(arg, rettype); - - appendStringInfoChar(buf, '('); - get_rule_expr(arg, context); - - /* - * Show typename with appropriate length decoration. Note that - * since exprIsLengthCoercion succeeded, the function's output - * type is the right thing to report. Also note we don't need - * to quote the result of format_type_with_typemod: it takes - * care of double-quoting any identifier that needs it. - */ - typdesc = format_type_with_typemod(rettype, coercedTypmod); - appendStringInfo(buf, ")::%s", typdesc); - pfree(typdesc); - - return; - } - - /* - * Normal function: display as proname(args). First we need to extract - * the argument datatypes. - */ - nargs = 0; - foreach(l, expr->args) - { - Assert(nargs < FUNC_MAX_ARGS); - argtypes[nargs] = exprType((Node *) lfirst(l)); - nargs++; - } - - appendStringInfo(buf, "%s(", - generate_function_name(funcoid, nargs, argtypes)); - - sep = ""; - foreach(l, expr->args) - { - appendStringInfo(buf, sep); - sep = ", "; - get_rule_expr((Node *) lfirst(l), context); - } - appendStringInfoChar(buf, ')'); -} - -/* - * get_agg_expr - Parse back an Aggref node - */ -static void -get_agg_expr(Aggref *aggref, deparse_context *context) -{ - StringInfo buf = context->buf; - Oid argtype = exprType(aggref->target); - - appendStringInfo(buf, "%s(%s", - generate_function_name(aggref->aggfnoid, 1, &argtype), - aggref->aggdistinct ? "DISTINCT " : ""); - if (aggref->aggstar) - appendStringInfo(buf, "*"); - else - get_rule_expr(aggref->target, context); - appendStringInfoChar(buf, ')'); -} - - -/* - * strip_type_coercion - * Strip any type coercions at the top of the given expression tree, - * as long as they are coercions to the given datatype. - * - * A RelabelType node is always a type coercion. A function call is also - * considered a type coercion if it has one argument and the function name - * is the same as the (internal) name of its result type. - * - * XXX It'd be better if the parsetree retained some explicit indication - * of the coercion, so we didn't need these heuristics. - */ -static Node * -strip_type_coercion(Node *expr, Oid resultType) -{ - if (expr == NULL || exprType(expr) != resultType) - return expr; - - if (IsA(expr, RelabelType)) - return strip_type_coercion(((RelabelType *) expr)->arg, resultType); - - if (IsA(expr, Expr) && - ((Expr *) expr)->opType == FUNC_EXPR) - { - Func *func; - HeapTuple procTuple; - HeapTuple typeTuple; - Form_pg_proc procStruct; - Form_pg_type typeStruct; - - func = (Func *) (((Expr *) expr)->oper); - Assert(IsA(func, Func)); - if (length(((Expr *) expr)->args) != 1) - return expr; - /* Lookup the function in pg_proc */ - procTuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(func->funcid), - 0, 0, 0); - if (!HeapTupleIsValid(procTuple)) - elog(ERROR, "cache lookup for proc %u failed", func->funcid); - procStruct = (Form_pg_proc) GETSTRUCT(procTuple); - /* Double-check func has one arg and correct result type */ - /* Also, it must be an implicit coercion function */ - if (procStruct->pronargs != 1 || - procStruct->prorettype != resultType || - !procStruct->proimplicit) - { - ReleaseSysCache(procTuple); - return expr; - } - /* See if function has same name/namespace as its result type */ - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(procStruct->prorettype), - 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - elog(ERROR, "cache lookup for type %u failed", - procStruct->prorettype); - typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); - if (strcmp(NameStr(procStruct->proname), - NameStr(typeStruct->typname)) != 0 || - procStruct->pronamespace != typeStruct->typnamespace) - { - ReleaseSysCache(procTuple); - ReleaseSysCache(typeTuple); - return expr; - } - /* Okay, it is indeed a type-coercion function */ - ReleaseSysCache(procTuple); - ReleaseSysCache(typeTuple); - return strip_type_coercion(lfirst(((Expr *) expr)->args), resultType); - } - - return expr; -} - - -/* ---------- - * get_tle_expr - * - * In an INSERT or UPDATE targetlist item, the parser may have inserted - * a length-coercion function call to coerce the value to the right - * length for the target column. We want to suppress the output of - * that function call, otherwise dump/reload/dump... would blow up the - * expression by adding more and more layers of length-coercion calls. - * - * As of 7.0, this hack is no longer absolutely essential, because the parser - * is now smart enough not to add a redundant length coercion function call. - * But we still suppress the function call just for neatness of displayed - * rules. - * - * Note that this hack must NOT be applied to SELECT targetlist items; - * any length coercion appearing there is something the user actually wrote. - * ---------- - */ -static void -get_tle_expr(TargetEntry *tle, deparse_context *context) -{ - Expr *expr = (Expr *) (tle->expr); - int32 coercedTypmod; - - /* - * If top level is a length coercion to the correct length, suppress - * it; else dump the expression normally. - */ - if (tle->resdom->restypmod >= 0 && - exprIsLengthCoercion((Node *) expr, &coercedTypmod) && - coercedTypmod == tle->resdom->restypmod) - get_rule_expr((Node *) lfirst(expr->args), context); - else - get_rule_expr(tle->expr, context); -} - - -/* ---------- - * get_const_expr - * - * Make a string representation of a Const - * ---------- - */ -static void -get_const_expr(Const *constval, deparse_context *context) -{ - StringInfo buf = context->buf; - HeapTuple typetup; - Form_pg_type typeStruct; - char *extval; - char *valptr; - - if (constval->constisnull) - { - /* - * Always label the type of a NULL constant to prevent misdecisions - * about type when reparsing. - */ - appendStringInfo(buf, "NULL::%s", - format_type_with_typemod(constval->consttype, -1)); - return; - } - - typetup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(constval->consttype), - 0, 0, 0); - if (!HeapTupleIsValid(typetup)) - elog(ERROR, "cache lookup of type %u failed", constval->consttype); - - typeStruct = (Form_pg_type) GETSTRUCT(typetup); - - extval = DatumGetCString(OidFunctionCall3(typeStruct->typoutput, - constval->constvalue, - ObjectIdGetDatum(typeStruct->typelem), - Int32GetDatum(-1))); - - switch (constval->consttype) - { - case INT2OID: - case INT4OID: - case OIDOID: /* int types */ - case FLOAT4OID: - case FLOAT8OID: /* float types */ - /* These types are printed without quotes */ - appendStringInfo(buf, extval); - break; - default: - - /* - * We must quote any funny characters in the constant's - * representation. XXX Any MULTIBYTE considerations here? - */ - appendStringInfoChar(buf, '\''); - for (valptr = extval; *valptr; valptr++) - { - char ch = *valptr; - - if (ch == '\'' || ch == '\\') - { - appendStringInfoChar(buf, '\\'); - appendStringInfoChar(buf, ch); - } - else if (((unsigned char) ch) < ((unsigned char) ' ')) - appendStringInfo(buf, "\\%03o", (int) ch); - else - appendStringInfoChar(buf, ch); - } - appendStringInfoChar(buf, '\''); - break; - } - - pfree(extval); - - switch (constval->consttype) - { - case INT4OID: - case FLOAT8OID: - case UNKNOWNOID: - /* These types can be left unlabeled */ - break; - default: - appendStringInfo(buf, "::%s", - format_type_with_typemod(constval->consttype, - -1)); - break; - } - - ReleaseSysCache(typetup); -} - - -/* ---------- - * get_sublink_expr - Parse back a sublink - * ---------- - */ -static void -get_sublink_expr(Node *node, deparse_context *context) -{ - StringInfo buf = context->buf; - SubLink *sublink = (SubLink *) node; - Query *query = (Query *) (sublink->subselect); - List *l; - char *sep; - Oper *oper; - bool need_paren; - - appendStringInfoChar(buf, '('); - - if (sublink->lefthand != NIL) - { - need_paren = (length(sublink->lefthand) > 1); - if (need_paren) - appendStringInfoChar(buf, '('); - - sep = ""; - foreach(l, sublink->lefthand) - { - appendStringInfo(buf, sep); - sep = ", "; - get_rule_expr((Node *) lfirst(l), context); - } - - if (need_paren) - appendStringInfo(buf, ") "); - else - appendStringInfoChar(buf, ' '); - } - - need_paren = true; - - /* - * XXX we assume here that we can get away without qualifying the - * operator name. Since the name may imply multiple physical operators - * it's rather difficult to do otherwise --- in fact, if the operators - * are in different namespaces any attempt to qualify would surely fail. - */ - switch (sublink->subLinkType) - { - case EXISTS_SUBLINK: - appendStringInfo(buf, "EXISTS "); - break; - - case ANY_SUBLINK: - oper = (Oper *) lfirst(sublink->oper); - appendStringInfo(buf, "%s ANY ", get_opname(oper->opno)); - break; - - case ALL_SUBLINK: - oper = (Oper *) lfirst(sublink->oper); - appendStringInfo(buf, "%s ALL ", get_opname(oper->opno)); - break; - - case MULTIEXPR_SUBLINK: - oper = (Oper *) lfirst(sublink->oper); - appendStringInfo(buf, "%s ", get_opname(oper->opno)); - break; - - case EXPR_SUBLINK: - need_paren = false; - break; - - default: - elog(ERROR, "get_sublink_expr: unsupported sublink type %d", - sublink->subLinkType); - break; - } - - if (need_paren) - appendStringInfoChar(buf, '('); - - get_query_def(query, buf, context->namespaces); - - if (need_paren) - appendStringInfo(buf, "))"); - else - appendStringInfoChar(buf, ')'); -} - - -/* ---------- - * get_from_clause - Parse back a FROM clause - * ---------- - */ -static void -get_from_clause(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - char *sep; - List *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.) Also ignore the rule pseudo-RTEs for NEW - * and OLD. - */ - sep = " FROM "; - - 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 (strcmp(rte->eref->aliasname, "*NEW*") == 0) - continue; - if (strcmp(rte->eref->aliasname, "*OLD*") == 0) - continue; - } - - appendStringInfo(buf, sep); - get_from_clause_item(jtnode, query, context); - sep = ", "; - } -} - -static void -get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - - if (IsA(jtnode, RangeTblRef)) - { - int varno = ((RangeTblRef *) jtnode)->rtindex; - RangeTblEntry *rte = rt_fetch(varno, query->rtable); - - switch (rte->rtekind) - { - case RTE_RELATION: - /* Normal relation RTE */ - appendStringInfo(buf, "%s%s", - only_marker(rte), - generate_relation_name(rte->relid)); - break; - case RTE_SUBQUERY: - /* Subquery RTE */ - appendStringInfoChar(buf, '('); - get_query_def(rte->subquery, buf, context->namespaces); - appendStringInfoChar(buf, ')'); - break; - case RTE_FUNCTION: - /* Function RTE */ - get_rule_expr(rte->funcexpr, context); - break; - default: - elog(ERROR, "unexpected rte kind %d", (int) rte->rtekind); - break; - } - if (rte->alias != NULL) - { - appendStringInfo(buf, " %s", - quote_identifier(rte->alias->aliasname)); - if (rte->alias->colnames != NIL) - { - List *col; - - appendStringInfo(buf, "("); - foreach(col, rte->alias->colnames) - { - if (col != rte->alias->colnames) - appendStringInfo(buf, ", "); - appendStringInfo(buf, "%s", - quote_identifier(strVal(lfirst(col)))); - } - appendStringInfoChar(buf, ')'); - } - } - } - else if (IsA(jtnode, JoinExpr)) - { - JoinExpr *j = (JoinExpr *) jtnode; - - appendStringInfoChar(buf, '('); - get_from_clause_item(j->larg, query, context); - if (j->isNatural) - appendStringInfo(buf, " NATURAL"); - switch (j->jointype) - { - case JOIN_INNER: - if (j->quals) - appendStringInfo(buf, " JOIN "); - else - appendStringInfo(buf, " CROSS JOIN "); - break; - case JOIN_LEFT: - appendStringInfo(buf, " LEFT JOIN "); - break; - case JOIN_FULL: - appendStringInfo(buf, " FULL JOIN "); - break; - case JOIN_RIGHT: - appendStringInfo(buf, " RIGHT JOIN "); - break; - case JOIN_UNION: - appendStringInfo(buf, " UNION JOIN "); - break; - default: - elog(ERROR, "get_from_clause_item: unknown join type %d", - (int) j->jointype); - } - get_from_clause_item(j->rarg, query, context); - if (!j->isNatural) - { - if (j->using) - { - List *col; - - appendStringInfo(buf, " USING ("); - foreach(col, j->using) - { - if (col != j->using) - appendStringInfo(buf, ", "); - appendStringInfo(buf, "%s", - quote_identifier(strVal(lfirst(col)))); - } - appendStringInfoChar(buf, ')'); - } - else if (j->quals) - { - appendStringInfo(buf, " ON ("); - get_rule_expr(j->quals, context); - appendStringInfoChar(buf, ')'); - } - } - 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)); - if (j->alias->colnames != NIL) - { - List *col; - - appendStringInfo(buf, "("); - foreach(col, j->alias->colnames) - { - if (col != j->alias->colnames) - appendStringInfo(buf, ", "); - appendStringInfo(buf, "%s", - quote_identifier(strVal(lfirst(col)))); - } - appendStringInfoChar(buf, ')'); - } - } - } - else - elog(ERROR, "get_from_clause_item: unexpected node type %d", - nodeTag(jtnode)); -} - -/* - * get_opclass_name - fetch name of an index operator class - * - * The opclass name is appended (after a space) to buf. - * - * Output is suppressed if the opclass is the default for the given - * actual_datatype. (If you don't want this behavior, just pass - * InvalidOid for actual_datatype.) - */ -static void -get_opclass_name(Oid opclass, Oid actual_datatype, - StringInfo buf) -{ - HeapTuple ht_opc; - Form_pg_opclass opcrec; - char *opcname; - char *nspname; - - ht_opc = SearchSysCache(CLAOID, - ObjectIdGetDatum(opclass), - 0, 0, 0); - if (!HeapTupleIsValid(ht_opc)) - elog(ERROR, "cache lookup failed for opclass %u", opclass); - opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc); - if (actual_datatype != opcrec->opcintype || !opcrec->opcdefault) - { - /* 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); -} - -/* - * tleIsArrayAssign - check for array assignment - */ -static bool -tleIsArrayAssign(TargetEntry *tle) -{ - ArrayRef *aref; - - if (tle->expr == NULL || !IsA(tle->expr, ArrayRef)) - return false; - aref = (ArrayRef *) tle->expr; - if (aref->refassgnexpr == NULL) - return false; - - /* - * Currently, it should only be possible to see non-null refassgnexpr - * if we are indeed looking at an "UPDATE array[n] = expr" situation. - * So aref->refexpr ought to match the tle's target. - */ - if (aref->refexpr == NULL || !IsA(aref->refexpr, Var) || - ((Var *) aref->refexpr)->varattno != tle->resdom->resno) - elog(WARNING, "tleIsArrayAssign: I'm confused ..."); - return true; -} - -/* - * quote_identifier - Quote an identifier only if needed - * - * When quotes are needed, we palloc the required space; slightly - * space-wasteful but well worth it for notational simplicity. - */ -const char * -quote_identifier(const char *ident) -{ - /* - * 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 (safe) - { - /* - * Check for keyword. This test is overly strong, since many of - * the "keywords" known to the parser are usable as column names, - * but the parser doesn't provide any easy way to test for whether - * an identifier is safe or not... so be safe not sorry. - * - * Note: ScanKeywordLookup() does case-insensitive comparison, but - * that's fine, since we already know we have all-lower-case. - */ - if (ScanKeywordLookup(ident) != NULL) - 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; -} - -/* - * quote_qualified_identifier - Quote a possibly-qualified identifier - * - * Return a name of the form namespace.ident, or just ident if namespace - * is NULL, quoting each component if necessary. The result is palloc'd. - */ -char * -quote_qualified_identifier(const char *namespace, - const char *ident) -{ - StringInfoData buf; - - initStringInfo(&buf); - if (namespace) - appendStringInfo(&buf, "%s.", quote_identifier(namespace)); - appendStringInfo(&buf, "%s", quote_identifier(ident)); - return buf.data; -} - -/* - * generate_relation_name - * Compute the name to display for a relation specified by OID - * - * The result includes all necessary quoting and schema-prefixing. - */ -static char * -generate_relation_name(Oid relid) -{ - HeapTuple tp; - Form_pg_class reltup; - char *nspname; - char *result; - - tp = SearchSysCache(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); - if (!HeapTupleIsValid(tp)) - elog(ERROR, "cache lookup of relation %u failed", relid); - reltup = (Form_pg_class) GETSTRUCT(tp); - - /* Qualify the name if not visible in search path */ - if (RelationIsVisible(relid)) - nspname = NULL; - else - nspname = get_namespace_name(reltup->relnamespace); - - result = quote_qualified_identifier(nspname, NameStr(reltup->relname)); - - ReleaseSysCache(tp); - - return result; -} - -/* - * generate_function_name - * Compute the name to display for a function specified by OID, - * given that it is being called with the specified actual arg types. - * (Arg types matter because of ambiguous-function resolution rules.) - * - * The result includes all necessary quoting and schema-prefixing. - */ -static char * -generate_function_name(Oid funcid, int nargs, Oid *argtypes) -{ - HeapTuple proctup; - Form_pg_proc procform; - char *proname; - char *nspname; - char *result; - FuncDetailCode p_result; - Oid p_funcid; - Oid p_rettype; - bool p_retset; - Oid *p_true_typeids; - - proctup = SearchSysCache(PROCOID, - ObjectIdGetDatum(funcid), - 0, 0, 0); - if (!HeapTupleIsValid(proctup)) - elog(ERROR, "cache lookup of function %u failed", funcid); - procform = (Form_pg_proc) GETSTRUCT(proctup); - proname = NameStr(procform->proname); - Assert(nargs == procform->pronargs); - - /* - * 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. - */ - p_result = func_get_detail(makeList1(makeString(proname)), - NIL, nargs, argtypes, - &p_funcid, &p_rettype, - &p_retset, &p_true_typeids); - if (p_result != FUNCDETAIL_NOTFOUND && p_funcid == funcid) - nspname = NULL; - else - nspname = get_namespace_name(procform->pronamespace); - - result = quote_qualified_identifier(nspname, proname); - - ReleaseSysCache(proctup); - - return result; -} - -/* - * generate_operator_name - * Compute the name to display for an operator specified by OID, - * given that it is being called with the specified actual arg types. - * (Arg types matter because of ambiguous-operator resolution rules. - * Pass InvalidOid for unused arg of a unary operator.) - * - * The result includes all necessary quoting and schema-prefixing, - * plus the OPERATOR() decoration needed to use a qualified operator name - * in an expression. - */ -static char * -generate_operator_name(Oid operid, Oid arg1, Oid arg2) -{ - StringInfoData buf; - HeapTuple opertup; - Form_pg_operator operform; - char *oprname; - char *nspname; - Operator p_result; - - initStringInfo(&buf); - - opertup = SearchSysCache(OPEROID, - ObjectIdGetDatum(operid), - 0, 0, 0); - if (!HeapTupleIsValid(opertup)) - elog(ERROR, "cache lookup of operator %u failed", 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(makeList1(makeString(oprname)), arg1, arg2, true); - break; - case 'l': - p_result = left_oper(makeList1(makeString(oprname)), arg2, true); - break; - case 'r': - p_result = right_oper(makeList1(makeString(oprname)), arg1, true); - break; - default: - elog(ERROR, "unexpected oprkind %c for operator %u", - operform->oprkind, operid); - 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)); - } - - appendStringInfo(&buf, "%s", oprname); - - if (nspname) - appendStringInfoChar(&buf, ')'); - - if (p_result != NULL) - ReleaseSysCache(p_result); - - ReleaseSysCache(opertup); - - return buf.data; -} - -/* - * get_relid_attribute_name - * Get an attribute name by its relations Oid and its attnum - * - * Same as underlying syscache routine get_attname(), except that error - * is handled by elog() instead of returning NULL. - */ -static char * -get_relid_attribute_name(Oid relid, AttrNumber attnum) -{ - char *attname; - - attname = get_attname(relid, attnum); - if (attname == NULL) - elog(ERROR, "cache lookup of attribute %d in relation %u failed", - attnum, relid); - return attname; -} |