summaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c2847
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;
-}