aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-03-12 00:52:10 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-03-12 00:52:10 +0000
commit6eeb95f0f56bb5e8a0a9328aeec04c9e6de87272 (patch)
tree5f209c5926768472f9d4fd7210065c18831dbd9c /src/backend/utils/adt/ruleutils.c
parent66b6bf67a197db73a880d2a4d9dab05168cde8e0 (diff)
downloadpostgresql-6eeb95f0f56bb5e8a0a9328aeec04c9e6de87272.tar.gz
postgresql-6eeb95f0f56bb5e8a0a9328aeec04c9e6de87272.zip
Restructure representation of join alias variables. An explicit JOIN
now has an RTE of its own, and references to its outputs now are Vars referencing the JOIN RTE, rather than CASE-expressions. This allows reverse-listing in ruleutils.c to use the correct alias easily, rather than painfully reverse-engineering the alias namespace as it used to do. Also, nested FULL JOINs work correctly, because the result of the inner joins are simple Vars that the planner can cope with. This fixes a bug reported a couple times now, notably by Tatsuo on 18-Nov-01. The alias Vars are expanded into COALESCE expressions where needed at the very end of planning, rather than during parsing. Also, beginnings of support for showing plan qualifier expressions in EXPLAIN. There are probably still cases that need work. initdb forced due to change of stored-rule representation.
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c471
1 files changed, 167 insertions, 304 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 169bcd23e29..d47bf3bb945 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.92 2002/03/06 19:58:26 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.93 2002/03/12 00:51:59 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -73,17 +73,22 @@ typedef struct
/*
* Each level of query context around a subtree needs a level of Var namespace.
- * The rangetable is the list of actual RTEs, and the namespace indicates
- * which parts of the rangetable are accessible (and under what aliases)
- * in the expression currently being looked at. A Var having varlevelsup=N
- * refers to the N'th item (counting from 0) in the current context's
- * namespaces list.
+ * 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 */
- List *namespace; /* List of joinlist items (RangeTblRef and
- * JoinExpr 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;
@@ -122,12 +127,6 @@ static void get_rule_sortgroupclause(SortClause *srt, List *tlist,
deparse_context *context);
static void get_names_for_var(Var *var, deparse_context *context,
char **refname, char **attname);
-static bool get_alias_for_case(CaseExpr *caseexpr, deparse_context *context,
- char **refname, char **attname);
-static bool find_alias_in_namespace(Node *nsnode, Node *expr,
- List *rangetable, int levelsup,
- char **refname, char **attname);
-static bool phony_equal(Node *expr1, Node *expr2, int levelsup);
static void get_rule_expr(Node *node, deparse_context *context);
static void get_func_expr(Expr *expr, deparse_context *context);
static Node *strip_type_coercion(Node *expr, Oid resultType);
@@ -644,7 +643,7 @@ deparse_expression(Node *expr, List *dpcontext, bool forceprefix)
*
* Given the name and OID of a relation, build deparsing context for an
* expression referencing only that relation (as varno 1, varlevelsup 0).
- * This is presently sufficient for the external uses of deparse_expression.
+ * This is sufficient for many uses of deparse_expression.
* ----------
*/
List *
@@ -652,30 +651,119 @@ deparse_context_for(char *relname, Oid relid)
{
deparse_namespace *dpns;
RangeTblEntry *rte;
- RangeTblRef *rtr;
dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
/* Build a minimal RTE for the rel */
rte = makeNode(RangeTblEntry);
+ rte->rtekind = RTE_RELATION;
rte->relname = relname;
rte->relid = relid;
rte->eref = makeNode(Attr);
rte->eref->relname = relname;
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;
- /* Build a namespace list referencing this RTE only */
- rtr = makeNode(RangeTblRef);
- rtr->rtindex = 1;
- dpns->namespace = makeList1(rtr);
+ /* 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.
+ * The resulting context will work for deparsing quals, tlists, etc of the
+ * plan node.
+ */
+List *
+deparse_context_for_plan(int outer_varno, Node *outercontext,
+ int inner_varno, Node *innercontext)
+{
+ deparse_namespace *dpns;
+
+ dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
+
+ dpns->rtable = NIL;
+ 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_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->relname = pstrdup(name);
+ rte->relid = InvalidOid;
+ rte->eref = makeNode(Attr);
+ rte->eref->relname = rte->relname;
+ rte->eref->attrs = attrs;
+ rte->inh = false;
+ rte->inFromCl = true;
+
+ return (Node *) rte;
+}
+
/* ----------
* make_ruledef - reconstruct the CREATE RULE command
* for a given pg_rewrite tuple
@@ -789,7 +877,8 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
context.namespaces = makeList1(&dpns);
context.varprefix = (length(query->rtable) != 1);
dpns.rtable = query->rtable;
- dpns.namespace = query->jointree ? query->jointree->fromlist : NIL;
+ dpns.outer_varno = dpns.inner_varno = 0;
+ dpns.outer_rte = dpns.inner_rte = NULL;
get_rule_expr(qual, &context);
}
@@ -912,7 +1001,8 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace)
context.varprefix = (parentnamespace != NIL ||
length(query->rtable) != 1);
dpns.rtable = query->rtable;
- dpns.namespace = query->jointree ? query->jointree->fromlist : NIL;
+ dpns.outer_varno = dpns.inner_varno = 0;
+ dpns.outer_rte = dpns.inner_rte = NULL;
switch (query->commandType)
{
@@ -1382,12 +1472,11 @@ get_utility_query_def(Query *query, deparse_context *context)
/*
* 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.
- *
- * This is trickier than it ought to be because of the possibility of aliases
- * and limited scope of refnames. We have to try to return the correct alias
- * with respect to the current namespace given by the context.
*/
static void
get_names_for_var(Var *var, deparse_context *context,
@@ -1406,262 +1495,31 @@ get_names_for_var(Var *var, deparse_context *context,
var->varlevelsup);
dpns = (deparse_namespace *) lfirst(nslist);
- /* Scan namespace to see if we can find an alias for the var */
- if (find_alias_in_namespace((Node *) dpns->namespace, (Node *) var,
- dpns->rtable, var->varlevelsup,
- refname, attname))
- return;
+ /* 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->relname;
- /*
- * Otherwise, fall back on the rangetable entry. This should happen
- * only for uses of special RTEs like *NEW* and *OLD*, which won't get
- * placed in our namespace.
- */
- rte = rt_fetch(var->varno, dpns->rtable);
- *refname = rte->eref->relname;
if (var->varattno == InvalidAttrNumber)
*attname = NULL;
else
*attname = get_rte_attribute_name(rte, var->varattno);
}
-/*
- * Check to see if a CASE expression matches a FULL JOIN's output expression.
- * If so, return the refname and alias it should be expressed as.
- */
-static bool
-get_alias_for_case(CaseExpr *caseexpr, deparse_context *context,
- char **refname, char **attname)
-{
- List *nslist;
- int sup;
-
- /*
- * This could be done more efficiently if we first groveled through
- * the CASE to find varlevelsup values, but it's probably not worth
- * the trouble. All this code will go away someday anyway ...
- */
-
- sup = 0;
- foreach(nslist, context->namespaces)
- {
- deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
-
- if (find_alias_in_namespace((Node *) dpns->namespace,
- (Node *) caseexpr,
- dpns->rtable, sup,
- refname, attname))
- return true;
- sup++;
- }
- return false;
-}
-
-/*
- * Recursively scan a namespace (same representation as a jointree) to see
- * if we can find an alias for the given expression. If so, return the
- * correct alias refname and attname. The expression may be either a plain
- * Var or a CASE expression (which may be a FULL JOIN reference).
- */
-static bool
-find_alias_in_namespace(Node *nsnode, Node *expr,
- List *rangetable, int levelsup,
- char **refname, char **attname)
-{
- if (nsnode == NULL)
- return false;
- if (IsA(nsnode, RangeTblRef))
- {
- if (IsA(expr, Var))
- {
- Var *var = (Var *) expr;
- int rtindex = ((RangeTblRef *) nsnode)->rtindex;
-
- if (var->varno == rtindex && var->varlevelsup == levelsup)
- {
- RangeTblEntry *rte = rt_fetch(rtindex, rangetable);
-
- *refname = rte->eref->relname;
- if (var->varattno == InvalidAttrNumber)
- *attname = NULL;
- else
- *attname = get_rte_attribute_name(rte, var->varattno);
- return true;
- }
- }
- }
- else if (IsA(nsnode, JoinExpr))
- {
- JoinExpr *j = (JoinExpr *) nsnode;
-
- if (j->alias)
- {
- List *vlist;
- List *nlist;
-
- /*
- * Does the expr match any of the output columns of the join?
- *
- * We can't just use equal() here, because the given expr may
- * have nonzero levelsup, whereas the saved expression in the
- * JoinExpr should have zero levelsup.
- */
- nlist = j->colnames;
- foreach(vlist, j->colvars)
- {
- if (phony_equal(lfirst(vlist), expr, levelsup))
- {
- *refname = j->alias->relname;
- *attname = strVal(lfirst(nlist));
- return true;
- }
- nlist = lnext(nlist);
- }
-
- /*
- * Tables within an aliased join are invisible from outside
- * the join, according to the scope rules of SQL92 (the join
- * is considered a subquery). So, stop here.
- */
- return false;
- }
- if (find_alias_in_namespace(j->larg, expr,
- rangetable, levelsup,
- refname, attname))
- return true;
- if (find_alias_in_namespace(j->rarg, expr,
- rangetable, levelsup,
- refname, attname))
- return true;
- }
- else if (IsA(nsnode, List))
- {
- List *l;
-
- foreach(l, (List *) nsnode)
- {
- if (find_alias_in_namespace(lfirst(l), expr,
- rangetable, levelsup,
- refname, attname))
- return true;
- }
- }
- else
- elog(ERROR, "find_alias_in_namespace: unexpected node type %d",
- nodeTag(nsnode));
- return false;
-}
-
-/*
- * Check for equality of two expressions, with the proviso that all Vars in
- * expr1 should have varlevelsup = 0, while all Vars in expr2 should have
- * varlevelsup = levelsup.
- *
- * In reality we only need to support equality checks on Vars and the type
- * of CASE expression that is used for FULL JOIN outputs, so not all node
- * types need be handled here.
- *
- * Otherwise, this code is a straight ripoff from equalfuncs.c.
- */
-static bool
-phony_equal(Node *expr1, Node *expr2, int levelsup)
-{
- if (expr1 == NULL || expr2 == NULL)
- return (expr1 == expr2);
- if (nodeTag(expr1) != nodeTag(expr2))
- return false;
- if (IsA(expr1, Var))
- {
- Var *a = (Var *) expr1;
- Var *b = (Var *) expr2;
-
- if (a->varno != b->varno)
- return false;
- if (a->varattno != b->varattno)
- return false;
- if (a->vartype != b->vartype)
- return false;
- if (a->vartypmod != b->vartypmod)
- return false;
- if (a->varlevelsup != 0 || b->varlevelsup != levelsup)
- return false;
- if (a->varnoold != b->varnoold)
- return false;
- if (a->varoattno != b->varoattno)
- return false;
- return true;
- }
- if (IsA(expr1, CaseExpr))
- {
- CaseExpr *a = (CaseExpr *) expr1;
- CaseExpr *b = (CaseExpr *) expr2;
-
- if (a->casetype != b->casetype)
- return false;
- if (!phony_equal(a->arg, b->arg, levelsup))
- return false;
- if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup))
- return false;
- if (!phony_equal(a->defresult, b->defresult, levelsup))
- return false;
- return true;
- }
- if (IsA(expr1, CaseWhen))
- {
- CaseWhen *a = (CaseWhen *) expr1;
- CaseWhen *b = (CaseWhen *) expr2;
-
- if (!phony_equal(a->expr, b->expr, levelsup))
- return false;
- if (!phony_equal(a->result, b->result, levelsup))
- return false;
- return true;
- }
- if (IsA(expr1, Expr))
- {
- Expr *a = (Expr *) expr1;
- Expr *b = (Expr *) expr2;
-
- if (a->opType != b->opType)
- return false;
- if (!phony_equal(a->oper, b->oper, levelsup))
- return false;
- if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup))
- return false;
- return true;
- }
- if (IsA(expr1, Func))
- {
- Func *a = (Func *) expr1;
- Func *b = (Func *) expr2;
-
- if (a->funcid != b->funcid)
- return false;
- if (a->functype != b->functype)
- return false;
- return true;
- }
- if (IsA(expr1, List))
- {
- List *la = (List *) expr1;
- List *lb = (List *) expr2;
- List *l;
-
- if (length(la) != length(lb))
- return false;
- foreach(l, la)
- {
- if (!phony_equal(lfirst(l), lfirst(lb), levelsup))
- return false;
- lb = lnext(lb);
- }
- return true;
- }
- /* If we get here, there was something weird in a JOIN's colvars list */
- elog(ERROR, "phony_equal: unexpected node type %d", nodeTag(expr1));
- return false;
-}
-
/* ----------
* get_rule_expr - Parse back an expression
* ----------
@@ -1695,7 +1553,7 @@ get_rule_expr(Node *node, deparse_context *context)
char *attname;
get_names_for_var(var, context, &refname, &attname);
- if (context->varprefix || attname == NULL)
+ if (refname && (context->varprefix || attname == NULL))
{
if (strcmp(refname, "*NEW*") == 0)
appendStringInfo(buf, "new");
@@ -1767,6 +1625,10 @@ get_rule_expr(Node *node, deparse_context *context)
appendStringInfoChar(buf, ')');
break;
+ case FUNC_EXPR:
+ get_func_expr((Expr *) node, context);
+ break;
+
case OR_EXPR:
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) lfirst(args), context);
@@ -1795,8 +1657,13 @@ get_rule_expr(Node *node, deparse_context *context)
appendStringInfoChar(buf, ')');
break;
- case FUNC_EXPR:
- get_func_expr((Expr *) node, context);
+ 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:
@@ -1927,19 +1794,6 @@ get_rule_expr(Node *node, deparse_context *context)
{
CaseExpr *caseexpr = (CaseExpr *) node;
List *temp;
- char *refname;
- char *attname;
-
- /* Hack for providing aliases for FULL JOIN outputs */
- if (get_alias_for_case(caseexpr, context,
- &refname, &attname))
- {
- if (context->varprefix)
- appendStringInfo(buf, "%s.",
- quote_identifier(refname));
- appendStringInfo(buf, "%s", quote_identifier(attname));
- break;
- }
appendStringInfo(buf, "CASE");
foreach(temp, caseexpr->args)
@@ -2015,6 +1869,28 @@ get_rule_expr(Node *node, deparse_context *context)
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:
printf("\n%s\n", nodeToString(node));
elog(ERROR, "get_ruledef of %s: unknown node type %d in get_rule_expr()",
@@ -2442,16 +2318,6 @@ static void
get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
- deparse_namespace *dpns;
- List *sv_namespace;
-
- /*
- * FROM-clause items have limited visibility of query's namespace.
- * Save and restore the outer namespace setting while we munge it.
- */
- dpns = (deparse_namespace *) lfirst(context->namespaces);
- sv_namespace = dpns->namespace;
- dpns->namespace = NIL;
if (IsA(jtnode, RangeTblRef))
{
@@ -2544,7 +2410,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
}
else if (j->quals)
{
- dpns->namespace = makeList2(j->larg, j->rarg);
appendStringInfo(buf, " ON (");
get_rule_expr(j->quals, context);
appendStringInfoChar(buf, ')');
@@ -2575,8 +2440,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
else
elog(ERROR, "get_from_clause_item: unexpected node type %d",
nodeTag(jtnode));
-
- dpns->namespace = sv_namespace;
}
/* ----------