*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.68 2000/03/12 18:57:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.69 2000/03/16 03:23:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up);
static bool attribute_used(Node *node, int rt_index, int attno,
int sublevels_up);
-static bool modifyAggrefUplevel(Node *node, void *context);
static bool modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
int sublevels_up, int new_sublevels_up);
static Node *modifyAggrefDropQual(Node *node, Node *targetNode);
static SubLink *modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree);
static Node *modifyAggrefQual(Node *node, Query *parsetree);
-static bool checkQueryHasAggs(Node *node);
-static bool checkQueryHasAggs_walker(Node *node, void *context);
-static bool checkQueryHasSubLink(Node *node);
-static bool checkQueryHasSubLink_walker(Node *node, void *context);
static Query *fireRIRrules(Query *parsetree);
static Query *Except_Intersect_Rewrite(Query *parsetree);
static void check_targetlists_are_compatible(List *prev_target,
}
-/*
- * modifyAggrefUplevel -
- * In the newly created sublink for an aggregate column used in
- * the qualification, we must increment the varlevelsup in all the
- * var nodes.
- *
- * NOTE: although this has the form of a walker, we cheat and modify the
- * Var nodes in-place. The given expression tree should have been copied
- * earlier to ensure that no unwanted side-effects occur!
- */
-static bool
-modifyAggrefUplevel(Node *node, void *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- var->varlevelsup++;
- return false;
- }
- if (IsA(node, SubLink))
- {
- /*
- * Standard expression_tree_walker will not recurse into subselect,
- * but here we must do so.
- */
- SubLink *sub = (SubLink *) node;
-
- if (modifyAggrefUplevel((Node *) (sub->lefthand), context))
- return true;
- if (modifyAggrefUplevel((Node *) (sub->subselect), context))
- return true;
- return false;
- }
- if (IsA(node, Query))
- {
- /* Reach here after recursing down into subselect above... */
- Query *qry = (Query *) node;
-
- if (modifyAggrefUplevel((Node *) (qry->targetList), context))
- return true;
- if (modifyAggrefUplevel((Node *) (qry->qual), context))
- return true;
- if (modifyAggrefUplevel((Node *) (qry->havingQual), context))
- return true;
- return false;
- }
- return expression_tree_walker(node, modifyAggrefUplevel,
- (void *) context);
-}
-
-
/*
* modifyAggrefChangeVarnodes -
* Change the var nodes in a sublink created for an aggregate column
* used in the qualification to point to the correct local RTE.
*
+ * XXX if we still need this after redoing querytree design, it should
+ * be combined with ChangeVarNodes, which is the same thing except for
+ * not having the option to adjust the vars' varlevelsup.
+ *
* NOTE: although this has the form of a walker, we cheat and modify the
* Var nodes in-place. The given expression tree should have been copied
* earlier to ensure that no unwanted side-effects occur!
* Recursing would be a bad idea --- we'd likely produce an
* infinite recursion. This whole technique is a crock, really...
*/
- if (checkQueryHasAggs(subquery->qual))
+ if (checkExprHasAggs(subquery->qual))
elog(ERROR, "Cannot handle multiple aggregate functions in WHERE clause");
subquery->groupClause = NIL;
subquery->havingQual = NULL;
subquery->hasAggs = TRUE;
- subquery->hasSubLinks = checkQueryHasSubLink(subquery->qual);
+ subquery->hasSubLinks = checkExprHasSubLink(subquery->qual);
subquery->unionClause = NULL;
/* Increment all varlevelsup fields in the new subquery */
- modifyAggrefUplevel((Node *) subquery, NULL);
+ IncrementVarSublevelsUp((Node *) subquery, 1, 0);
- /* Replace references to the target table with correct varno.
+ /* Replace references to the target table with correct local varno.
* Note +1 here to account for effects of previous line!
*/
modifyAggrefChangeVarnodes((Node *) subquery, target->varno,
}
-/*
- * checkQueryHasAggs -
- * Queries marked hasAggs might not have them any longer after
- * rewriting. Check it.
- */
-static bool
-checkQueryHasAggs(Node *node)
-{
- return checkQueryHasAggs_walker(node, NULL);
-}
-
-static bool
-checkQueryHasAggs_walker(Node *node, void *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Aggref))
- return true; /* abort the tree traversal and return true */
- return expression_tree_walker(node, checkQueryHasAggs_walker, context);
-}
-
-/*
- * checkQueryHasSubLink -
- * Queries marked hasSubLinks might not have them any longer after
- * rewriting. Check it.
- */
-static bool
-checkQueryHasSubLink(Node *node)
-{
- return checkQueryHasSubLink_walker(node, NULL);
-}
-
-static bool
-checkQueryHasSubLink_walker(Node *node, void *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, SubLink))
- return true; /* abort the tree traversal and return true */
- return expression_tree_walker(node, checkQueryHasSubLink_walker, context);
-}
-
-
static Node *
FindMatchingTLEntry(List *tlist, char *e_attname)
{
}
-/*
- * apply_RIR_adjust_sublevel -
- * Set the varlevelsup field of all Var nodes in the given expression tree
- * to sublevels_up. We do NOT recurse into subselects.
- *
- * NOTE: although this has the form of a walker, we cheat and modify the
- * Var nodes in-place. The given expression tree should have been copied
- * earlier to ensure that no unwanted side-effects occur!
- */
-static bool
-apply_RIR_adjust_sublevel_walker(Node *node, int *sublevels_up)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- var->varlevelsup = *sublevels_up;
- return false;
- }
- return expression_tree_walker(node, apply_RIR_adjust_sublevel_walker,
- (void *) sublevels_up);
-}
-
-static void
-apply_RIR_adjust_sublevel(Node *node, int sublevels_up)
-{
- apply_RIR_adjust_sublevel_walker(node, &sublevels_up);
-}
-
-
/*
* apply_RIR_view
* Replace Vars matching a given RT index with copies of TL expressions.
return make_null(var->vartype);
}
+ /* Make a copy of the tlist item to return */
expr = copyObject(expr);
+ /* Adjust varlevelsup if tlist item is from higher query level */
if (var->varlevelsup > 0)
- apply_RIR_adjust_sublevel(expr, var->varlevelsup);
+ IncrementVarSublevelsUp(expr, var->varlevelsup, 0);
+
*(context->modified) = true;
return (Node *) expr;
}
else
qual_product = (Query *) nth(0, *qual_products);
+ MemSet(&qual_info, 0, sizeof(qual_info));
qual_info.event = qual_product->commandType;
+ qual_info.current_varno = rt_index;
qual_info.new_varno = length(qual_product->rtable) + 2;
+
qual_product = CopyAndAddQual(qual_product,
actions,
event_qual,
if (query->hasAggs)
{
query->hasAggs =
- checkQueryHasAggs((Node *) (query->targetList)) ||
- checkQueryHasAggs((Node *) (query->havingQual));
- if (checkQueryHasAggs((Node *) (query->qual)))
+ checkExprHasAggs((Node *) (query->targetList)) ||
+ checkExprHasAggs((Node *) (query->havingQual));
+ if (checkExprHasAggs((Node *) (query->qual)))
elog(ERROR, "BasicQueryRewrite: failed to remove aggs from qual");
}
if (query->hasSubLinks)
query->hasSubLinks =
- checkQueryHasSubLink((Node *) (query->targetList)) ||
- checkQueryHasSubLink((Node *) (query->qual)) ||
- checkQueryHasSubLink((Node *) (query->havingQual));
+ checkExprHasSubLink((Node *) (query->targetList)) ||
+ checkExprHasSubLink((Node *) (query->qual)) ||
+ checkExprHasSubLink((Node *) (query->havingQual));
results = lappend(results, query);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.44 2000/01/27 18:11:37 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.45 2000/03/16 03:23:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define MUTATE(newfield, oldfield, fieldtype, mutator, context) \
( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) )
+static bool checkExprHasAggs_walker(Node *node, void *context);
+static bool checkExprHasSubLink_walker(Node *node, void *context);
+
+
+/*
+ * checkExprHasAggs -
+ * Queries marked hasAggs might not have them any longer after
+ * rewriting. Check it.
+ */
+bool
+checkExprHasAggs(Node *node)
+{
+ return checkExprHasAggs_walker(node, NULL);
+}
+
+static bool
+checkExprHasAggs_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Aggref))
+ return true; /* abort the tree traversal and return true */
+ return expression_tree_walker(node, checkExprHasAggs_walker, context);
+}
+
+/*
+ * checkExprHasSubLink -
+ * Queries marked hasSubLinks might not have them any longer after
+ * rewriting. Check it.
+ */
+bool
+checkExprHasSubLink(Node *node)
+{
+ return checkExprHasSubLink_walker(node, NULL);
+}
+
+static bool
+checkExprHasSubLink_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, SubLink))
+ return true; /* abort the tree traversal and return true */
+ return expression_tree_walker(node, checkExprHasSubLink_walker, context);
+}
+
/*
* OffsetVarNodes - adjust Vars when appending one query's RT to another
ChangeVarNodes_walker(node, &context);
}
+/*
+ * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
+ *
+ * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
+ * and add delta_sublevels_up to their varlevelsup value. This is needed when
+ * an expression that's correct for some nesting level is inserted into a
+ * subquery. Ordinarily the initial call has min_sublevels_up == 0 so that
+ * all Vars are affected. The point of min_sublevels_up is that we can
+ * increment it when we recurse into a sublink, so that local variables in
+ * that sublink are not affected, only outer references to vars that belong
+ * to the expression's original query level or parents thereof.
+ *
+ * NOTE: although this has the form of a walker, we cheat and modify the
+ * Var nodes in-place. The given expression tree should have been copied
+ * earlier to ensure that no unwanted side-effects occur!
+ */
+
+typedef struct {
+ int delta_sublevels_up;
+ int min_sublevels_up;
+} IncrementVarSublevelsUp_context;
+
+static bool
+IncrementVarSublevelsUp_walker(Node *node,
+ IncrementVarSublevelsUp_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ if (var->varlevelsup >= context->min_sublevels_up)
+ var->varlevelsup += context->delta_sublevels_up;
+ return false;
+ }
+ if (IsA(node, SubLink))
+ {
+ /*
+ * Standard expression_tree_walker will not recurse into subselect,
+ * but here we must do so.
+ */
+ SubLink *sub = (SubLink *) node;
+
+ if (IncrementVarSublevelsUp_walker((Node *) (sub->lefthand),
+ context))
+ return true;
+ IncrementVarSublevelsUp((Node *) (sub->subselect),
+ context->delta_sublevels_up,
+ context->min_sublevels_up + 1);
+ return false;
+ }
+ if (IsA(node, Query))
+ {
+ /* Reach here after recursing down into subselect above... */
+ Query *qry = (Query *) node;
+
+ if (IncrementVarSublevelsUp_walker((Node *) (qry->targetList),
+ context))
+ return true;
+ if (IncrementVarSublevelsUp_walker((Node *) (qry->qual),
+ context))
+ return true;
+ if (IncrementVarSublevelsUp_walker((Node *) (qry->havingQual),
+ context))
+ return true;
+ return false;
+ }
+ return expression_tree_walker(node, IncrementVarSublevelsUp_walker,
+ (void *) context);
+}
+
+void
+IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
+ int min_sublevels_up)
+{
+ IncrementVarSublevelsUp_context context;
+
+ context.delta_sublevels_up = delta_sublevels_up;
+ context.min_sublevels_up = min_sublevels_up;
+ IncrementVarSublevelsUp_walker(node, &context);
+}
+
/*
* Add the given qualifier condition to the query's WHERE clause
*/
parsetree->qual = copy;
else
parsetree->qual = (Node *) make_andclause(makeList(old, copy, -1));
+
+ /*
+ * Make sure query is marked correctly if added qual has sublinks or
+ * aggregates (not sure it can ever have aggs, but sublinks definitely).
+ */
+ parsetree->hasAggs |= checkExprHasAggs(copy);
+ parsetree->hasSubLinks |= checkExprHasSubLink(copy);
}
/*
parsetree->havingQual = copy;
else
parsetree->havingQual = (Node *) make_andclause(makeList(old, copy, -1));
+
+ /*
+ * Make sure query is marked correctly if added qual has sublinks or
+ * aggregates (not sure it can ever have aggs, but sublinks definitely).
+ */
+ parsetree->hasAggs |= checkExprHasAggs(copy);
+ parsetree->hasSubLinks |= checkExprHasSubLink(copy);
}
#ifdef NOT_USED
{
/* Make a copy of the tlist item to return */
n = copyObject(n);
- if (IsA(n, Var))
- {
- ((Var *) n)->varlevelsup = this_varlevelsup;
- }
- /* XXX what to do if tlist item is NOT a var?
- * Should we be using something like apply_RIR_adjust_sublevel?
- */
+ /* Adjust varlevelsup if tlist item is from higher query */
+ if (this_varlevelsup > 0)
+ IncrementVarSublevelsUp(n, this_varlevelsup, 0);
return n;
}
}
}
else
{
- NameData name_to_look_for;
-
- NameStr(name_to_look_for)[0] = '\0';
- namestrcpy(&name_to_look_for,
- (char *) get_attname(getrelid(this_varno,
- context->rtable),
- this_varattno));
- if (NameStr(name_to_look_for)[0])
+ char *name_to_look_for;
+
+ name_to_look_for = get_attname(getrelid(this_varno,
+ context->rtable),
+ this_varattno);
+ if (name_to_look_for)
{
Node *n;
*context->modified = TRUE;
n = FindMatchingTLEntry(context->targetlist,
- (char *) &name_to_look_for);
+ name_to_look_for);
if (n == NULL)
return make_null(var->vartype);
- else
- return copyObject(n);
+ /* Make a copy of the tlist item to return */
+ n = copyObject(n);
+ /* Adjust varlevelsup if tlist item is from higher query */
+ if (this_varlevelsup > 0)
+ IncrementVarSublevelsUp(n, this_varlevelsup, 0);
+ return n;
}
}
}