summaryrefslogtreecommitdiff
path: root/src/backend/rewrite/rewriteHandler.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2000-09-29 18:21:41 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2000-09-29 18:21:41 +0000
commit3a94e789f5c9537d804210be3cb26f7fb08e3b9e (patch)
treef1eac12405e3c0ded881d7dd7e59cec35b30c335 /src/backend/rewrite/rewriteHandler.c
parent6f64c2e54a0b14154a335249f4dca91a39c61c50 (diff)
downloadpostgresql-3a94e789f5c9537d804210be3cb26f7fb08e3b9e.tar.gz
Subselects in FROM clause, per ISO syntax: FROM (SELECT ...) [AS] alias.
(Don't forget that an alias is required.) Views reimplemented as expanding to subselect-in-FROM. Grouping, aggregates, DISTINCT in views actually work now (he says optimistically). No UNION support in subselects/views yet, but I have some ideas about that. Rule-related permissions checking moved out of rewriter and into executor. INITDB REQUIRED!
Diffstat (limited to 'src/backend/rewrite/rewriteHandler.c')
-rw-r--r--src/backend/rewrite/rewriteHandler.c802
1 files changed, 173 insertions, 629 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 49dfae5b90..5d1e3a4070 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.80 2000/09/12 21:07:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.81 2000/09/29 18:21:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,38 +27,22 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "parser/parse_type.h"
-#include "rewrite/locks.h"
#include "rewrite/rewriteManip.h"
-#include "utils/acl.h"
#include "utils/lsyscache.h"
extern void CheckSelectForUpdate(Query *rule_action); /* in analyze.c */
-/* macros borrowed from expression_tree_mutator */
-
-#define FLATCOPY(newnode, node, nodetype) \
- ( (newnode) = makeNode(nodetype), \
- memcpy((newnode), (node), sizeof(nodetype)) )
-
-#define MUTATE(newfield, oldfield, fieldtype, mutator, context) \
- ( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) )
-
-
static RewriteInfo *gatherRewriteMeta(Query *parsetree,
Query *rule_action,
Node *rule_qual,
int rt_index,
CmdType event,
bool instead_flag);
-static List *adjustJoinTree(Query *parsetree, int rt_index, bool *found);
-static bool modifyAggrefChangeVarnodes(Query *query,
- 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 List *adjustJoinTreeList(Query *parsetree, int rt_index, bool *found);
+static List *matchLocks(CmdType event, RuleLock *rulelocks,
+ int varno, Query *parsetree);
static Query *fireRIRrules(Query *parsetree);
static Query *Except_Intersect_Rewrite(Query *parsetree);
static void check_targetlists_are_compatible(List *prev_target,
@@ -134,15 +118,16 @@ gatherRewriteMeta(Query *parsetree,
* that we're replacing (if present, which it won't be for INSERT).
* Note that if the rule refers to OLD, its jointree will add back
* a reference to rt_index.
- *
- * XXX This might be wrong for subselect-in-FROM?
*/
{
bool found;
- List *newjointree = adjustJoinTree(parsetree, rt_index, &found);
+ List *newjointree = adjustJoinTreeList(parsetree,
+ rt_index,
+ &found);
- info->rule_action->jointree = nconc(newjointree,
- info->rule_action->jointree);
+ info->rule_action->jointree->fromlist =
+ nconc(newjointree,
+ info->rule_action->jointree->fromlist);
}
/*
@@ -175,13 +160,15 @@ gatherRewriteMeta(Query *parsetree,
/*
* Copy the query's jointree list, and attempt to remove any occurrence
* of the given rt_index as a top-level join item (we do not look for it
- * within JoinExprs). Returns modified jointree list --- original list
+ * within join items; this is OK because we are only expecting to find it
+ * as an UPDATE or DELETE target relation, which will be at the top level
+ * of the join). Returns modified jointree list --- original list
* is not changed. *found is set to indicate if we found the rt_index.
*/
static List *
-adjustJoinTree(Query *parsetree, int rt_index, bool *found)
+adjustJoinTreeList(Query *parsetree, int rt_index, bool *found)
{
- List *newjointree = listCopy(parsetree->jointree);
+ List *newjointree = listCopy(parsetree->jointree->fromlist);
List *jjt;
*found = false;
@@ -201,559 +188,147 @@ adjustJoinTree(Query *parsetree, int rt_index, bool *found)
/*
- * 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!
+ * matchLocks -
+ * match the list of locks and returns the matching rules
*/
-
-typedef struct
+static List *
+matchLocks(CmdType event,
+ RuleLock *rulelocks,
+ int varno,
+ Query *parsetree)
{
- int rt_index;
- int new_index;
- int sublevels_up;
- int new_sublevels_up;
-} modifyAggrefChangeVarnodes_context;
+ List *real_locks = NIL;
+ int nlocks;
+ int i;
-static bool
-modifyAggrefChangeVarnodes_walker(Node *node,
- modifyAggrefChangeVarnodes_context *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
+ Assert(rulelocks != NULL); /* we get called iff there is some lock */
+ Assert(parsetree != NULL);
- if (var->varlevelsup == context->sublevels_up &&
- var->varno == context->rt_index)
- {
- var->varno = context->new_index;
- var->varnoold = context->new_index;
- var->varlevelsup = context->new_sublevels_up;
- }
- return false;
- }
- if (IsA(node, Query))
+ if (parsetree->commandType != CMD_SELECT)
{
- /* Recurse into subselects */
- bool result;
-
- context->sublevels_up++;
- context->new_sublevels_up++;
- result = query_tree_walker((Query *) node,
- modifyAggrefChangeVarnodes_walker,
- (void *) context);
- context->sublevels_up--;
- context->new_sublevels_up--;
- return result;
+ if (parsetree->resultRelation != varno)
+ return NIL;
}
- return expression_tree_walker(node, modifyAggrefChangeVarnodes_walker,
- (void *) context);
-}
-static bool
-modifyAggrefChangeVarnodes(Query *query, int rt_index, int new_index,
- int sublevels_up, int new_sublevels_up)
-{
- modifyAggrefChangeVarnodes_context context;
-
- context.rt_index = rt_index;
- context.new_index = new_index;
- context.sublevels_up = sublevels_up;
- context.new_sublevels_up = new_sublevels_up;
- return query_tree_walker(query, modifyAggrefChangeVarnodes_walker,
- (void *) &context);
-}
+ nlocks = rulelocks->numLocks;
-
-/*
- * modifyAggrefDropQual -
- * remove the pure aggref clause from a qualification
- *
- * targetNode is an Aggref node somewhere within the given expression tree.
- * Find the boolean operator that's presumably somewhere above it, and replace
- * that whole operator expression with a constant TRUE. (This is NOT really
- * quite the right thing, but it handles simple cases. This whole set of
- * Aggref-in-qual routines needs to be thrown away when we can do subselects
- * in FROM.)
- *
- * The return tree is a modified copy of the given tree; the given tree
- * is not altered.
- *
- * Note: we don't recurse into subselects looking for targetNode; that's
- * not necessary in the current usage, since in fact targetNode will be
- * within the same select level as the given toplevel node.
- */
-static Node *
-modifyAggrefDropQual(Node *node, Node *targetNode)
-{
- if (node == NULL)
- return NULL;
- if (node == targetNode)
- {
- /* Oops, it's not inside an Expr we can rearrange... */
- elog(ERROR, "Cannot handle aggregate function inserted at this place in WHERE clause");
- }
- if (IsA(node, Expr))
+ for (i = 0; i < nlocks; i++)
{
- Expr *expr = (Expr *) node;
- List *i;
+ RewriteRule *oneLock = rulelocks->rules[i];
- foreach(i, expr->args)
+ if (oneLock->event == event)
{
- if (((Node *) lfirst(i)) == targetNode)
- {
- /* Found the parent expression containing the Aggref */
- if (expr->typeOid != BOOLOID)
- elog(ERROR,
- "aggregate function in qual must be argument of boolean operator");
- return (Node *) makeConst(BOOLOID, 1, (Datum) true,
- false, true, false, false);
- }
+ if (parsetree->commandType != CMD_SELECT ||
+ (oneLock->attrno == -1 ?
+ rangeTableEntry_used((Node *) parsetree, varno, 0) :
+ attribute_used((Node *) parsetree,
+ varno, oneLock->attrno, 0)))
+ real_locks = lappend(real_locks, oneLock);
}
- /* else this isn't the expr we want, keep going */
}
- return expression_tree_mutator(node, modifyAggrefDropQual,
- (void *) targetNode);
+
+ return real_locks;
}
-/*
- * modifyAggrefMakeSublink -
- * Create a sublink node for a qualification expression that
- * uses an aggregate column of a view
- */
-static SubLink *
-modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
-{
- List *aggVarNos;
- /* rte points to old structure: */
- RangeTblEntry *rte;
+static Query *
+ApplyRetrieveRule(Query *parsetree,
+ RewriteRule *rule,
+ int rt_index,
+ bool relation_level,
+ Relation relation,
+ bool relIsUsed)
+{
+ Query *rule_action;
+ RangeTblEntry *rte,
+ *subrte;
+ List *l;
- /* these point to newly-created structures: */
- Query *subquery;
- SubLink *sublink;
- TargetEntry *tle;
- Resdom *resdom;
- RangeTblRef *rtr;
-
- aggVarNos = pull_varnos(aggref->target);
- if (length(aggVarNos) != 1)
- elog(ERROR, "rewrite: aggregates of views only allowed on single tables for now");
- rte = rt_fetch(lfirsti(aggVarNos), parsetree->rtable);
-
- resdom = makeNode(Resdom);
- resdom->resno = 1;
- resdom->restype = aggref->aggtype;
- resdom->restypmod = -1;
- resdom->resname = pstrdup("<noname>");
- resdom->reskey = 0;
- resdom->reskeyop = 0;
- resdom->resjunk = false;
-
- tle = makeNode(TargetEntry);
- tle->resdom = resdom;
- tle->expr = copyObject(aggref); /* make a modifiable copy! */
-
- subquery = makeNode(Query);
-
- sublink = makeNode(SubLink);
- sublink->subLinkType = EXPR_SUBLINK;
- sublink->useor = false;
- sublink->lefthand = NIL;
- sublink->oper = NIL;
- sublink->subselect = (Node *) subquery;
-
- subquery->commandType = CMD_SELECT;
- subquery->utilityStmt = NULL;
- subquery->resultRelation = 0;
- subquery->into = NULL;
- subquery->isPortal = FALSE;
- subquery->isBinary = FALSE;
- subquery->isTemp = FALSE;
- subquery->unionall = FALSE;
- subquery->distinctClause = NIL;
- subquery->sortClause = NIL;
- subquery->rtable = lcons(copyObject(rte), NIL);
- rtr = makeNode(RangeTblRef);
- rtr->rtindex = 1;
- subquery->jointree = lcons(rtr, NIL);
- subquery->targetList = lcons(tle, NIL);
- subquery->qual = modifyAggrefDropQual((Node *) parsetree->qual,
- (Node *) aggref);
+ if (length(rule->actions) != 1)
+ elog(ERROR, "ApplyRetrieveRule: expected just one rule action");
+ if (rule->qual != NULL)
+ elog(ERROR, "ApplyRetrieveRule: can't handle qualified ON SELECT rule");
+ if (! relation_level)
+ elog(ERROR, "ApplyRetrieveRule: can't handle per-attribute ON SELECT rule");
/*
- * If there are still aggs in the subselect's qual, give up. Recursing
- * would be a bad idea --- we'd likely produce an infinite recursion.
- * This whole technique is a crock, really...
+ * Make a modifiable copy of the view query, and recursively expand
+ * any view references inside it.
*/
- 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 = checkExprHasSubLink(subquery->qual);
- subquery->unionClause = NULL;
+ rule_action = copyObject(lfirst(rule->actions));
- /* Increment all varlevelsup fields in the new subquery */
- IncrementVarSublevelsUp((Node *) subquery, 1, 0);
+ rule_action = fireRIRrules(rule_action);
/*
- * Replace references to the target table with correct local varno, 1.
- * Note that because of previous line, these references have
- * varlevelsup = 1, which must be changed to 0.
+ * VIEWs are really easy --- just plug the view query in as a subselect,
+ * replacing the relation's original RTE.
*/
- modifyAggrefChangeVarnodes(subquery,
- lfirsti(aggVarNos), 1,
- 1, 0);
+ rte = rt_fetch(rt_index, parsetree->rtable);
- return sublink;
-}
-
-
-/*
- * modifyAggrefQual -
- * Search for qualification expressions that contain aggregate
- * functions and substitute them by sublinks. These expressions
- * originally come from qualifications that use aggregate columns
- * of a view.
- *
- * The return value is a modified copy of the given expression tree.
- */
-static Node *
-modifyAggrefQual(Node *node, Query *parsetree)
-{
- if (node == NULL)
- return NULL;
- if (IsA(node, Aggref))
- {
- SubLink *sub = modifyAggrefMakeSublink((Aggref *) node, parsetree);
-
- parsetree->hasSubLinks = true;
- return (Node *) sub;
- }
+ rte->relname = NULL;
+ rte->relid = InvalidOid;
+ rte->subquery = rule_action;
+ rte->inh = false; /* must not be set for a subquery */
/*
- * Otherwise, fall through and copy the expr normally.
- *
- * We do NOT recurse into subselects in this routine. It's sufficient to
- * get rid of aggregates that are in the qual expression proper.
+ * We move the view's permission check data down to its rangetable.
+ * The checks will actually be done against the *OLD* entry therein.
*/
- return expression_tree_mutator(node, modifyAggrefQual,
- (void *) parsetree);
-}
+ subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
+ Assert(subrte->relid == relation->rd_id);
+ subrte->checkForRead = rte->checkForRead;
+ subrte->checkForWrite = rte->checkForWrite;
-
-static Node *
-FindMatchingTLEntry(List *tlist, char *e_attname)
-{
- List *i;
-
- foreach(i, tlist)
- {
- TargetEntry *tle = lfirst(i);
- char *resname;
-
- resname = tle->resdom->resname;
- if (!strcmp(e_attname, resname))
- return (tle->expr);
- }
- return NULL;
-}
-
-
-static Node *
-make_null(Oid type)
-{
- Const *c = makeNode(Const);
-
- c->consttype = type;
- c->constlen = get_typlen(type);
- c->constvalue = PointerGetDatum(NULL);
- c->constisnull = true;
- c->constbyval = get_typbyval(type);
- return (Node *) c;
-}
-
-
-/*
- * apply_RIR_view
- * Replace Vars matching a given RT index with copies of TL expressions.
- */
-
-typedef struct
-{
- int rt_index;
- int sublevels_up;
- RangeTblEntry *rte;
- List *tlist;
- int *modified;
-} apply_RIR_view_context;
-
-static Node *
-apply_RIR_view_mutator(Node *node,
- apply_RIR_view_context *context)
-{
- if (node == NULL)
- return NULL;
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- if (var->varlevelsup == context->sublevels_up &&
- var->varno == context->rt_index)
- {
- Node *expr;
-
- if (var->varattno < 0)
- elog(ERROR, "system column %s not available - %s is a view",
- get_attname(context->rte->relid, var->varattno),
- context->rte->relname);
-
- expr = FindMatchingTLEntry(context->tlist,
- get_attname(context->rte->relid,
- var->varattno));
- if (expr == NULL)
- {
- /* XXX shouldn't this be an error condition? */
- 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)
- IncrementVarSublevelsUp(expr, var->varlevelsup, 0);
-
- *(context->modified) = true;
- return (Node *) expr;
- }
- /* otherwise fall through to copy the var normally */
- }
+ rte->checkForRead = false; /* no permission check on subquery itself */
+ rte->checkForWrite = false;
/*
- * Since expression_tree_mutator won't touch subselects, we have to
- * handle them specially.
+ * FOR UPDATE of view?
*/
- if (IsA(node, SubLink))
- {
- SubLink *sublink = (SubLink *) node;
- SubLink *newnode;
-
- FLATCOPY(newnode, sublink, SubLink);
- MUTATE(newnode->lefthand, sublink->lefthand, List *,
- apply_RIR_view_mutator, context);
- context->sublevels_up++;
- MUTATE(newnode->subselect, sublink->subselect, Node *,
- apply_RIR_view_mutator, context);
- context->sublevels_up--;
- return (Node *) newnode;
- }
- if (IsA(node, Query))
+ if (intMember(rt_index, parsetree->rowMarks))
{
- Query *query = (Query *) node;
- Query *newnode;
-
- FLATCOPY(newnode, query, Query);
- MUTATE(newnode->targetList, query->targetList, List *,
- apply_RIR_view_mutator, context);
- MUTATE(newnode->qual, query->qual, Node *,
- apply_RIR_view_mutator, context);
- MUTATE(newnode->havingQual, query->havingQual, Node *,
- apply_RIR_view_mutator, context);
- MUTATE(newnode->jointree, query->jointree, List *,
- apply_RIR_view_mutator, context);
- return (Node *) newnode;
- }
- return expression_tree_mutator(node, apply_RIR_view_mutator,
- (void *) context);
-}
-
-static Node *
-apply_RIR_view(Node *node, int rt_index, RangeTblEntry *rte, List *tlist,
- int *modified, int sublevels_up)
-{
- apply_RIR_view_context context;
-
- context.rt_index = rt_index;
- context.sublevels_up = sublevels_up;
- context.rte = rte;
- context.tlist = tlist;
- context.modified = modified;
-
- return apply_RIR_view_mutator(node, &context);
-}
+ Index innerrti = 1;
+ CheckSelectForUpdate(rule_action);
-static Query *
-ApplyRetrieveRule(Query *parsetree,
- RewriteRule *rule,
- int rt_index,
- int relation_level,
- Relation relation,
- bool relIsUsed)
-{
- Query *rule_action = NULL;
- Node *rule_qual;
- List *rtable,
- *addedrtable,
- *l;
- int nothing,
- rt_length;
- int modified = false;
- int badsql = false;
-
- rule_qual = rule->qual;
- if (rule->actions)
- {
- if (length(rule->actions) > 1) /* ??? because we don't handle
- * rules with more than one
- * action? -ay */
-
- return parsetree;
- rule_action = copyObject(lfirst(rule->actions));
- nothing = FALSE;
- }
- else
- nothing = TRUE;
-
- rtable = copyObject(parsetree->rtable);
- rt_length = length(rtable); /* original length, not counting rule */
-
- addedrtable = copyObject(rule_action->rtable);
-
- /*
- * If the original rel wasn't in the join set (which'd be the case
- * for the target of an INSERT, for example), none of its spawn is.
- * If it was, then the spawn has to be added to the join set.
- */
- if (relIsUsed)
- {
/*
- * QUICK HACK: this all needs to be replaced, but for now, find
- * the original rel in the jointree, remove it, and add the rule
- * action's jointree. This will not work for views referenced
- * in JoinExprs!!
- *
- * Note: it is possible that the old rel is referenced in the query
- * but isn't present in the jointree; this should only happen for
- * *OLD* and *NEW*. We must not fail if so, but add the rule's
- * jointree anyway. (This is a major crock ... should fix rule
- * representation ...)
+ * Remove the view from the list of rels that will actually be
+ * marked FOR UPDATE by the executor. It will still be access-
+ * checked for write access, though.
*/
- bool found;
- List *newjointree = adjustJoinTree(parsetree, rt_index, &found);
- List *addedjointree = (List *) copyObject(rule_action->jointree);
-
- if (!found)
- elog(DEBUG, "ApplyRetrieveRule: can't find old rel %s (%d) in jointree",
- rt_fetch(rt_index, rtable)->eref->relname, rt_index);
- OffsetVarNodes((Node *) addedjointree, rt_length, 0);
- newjointree = nconc(newjointree, addedjointree);
- parsetree->jointree = newjointree;
- }
-
- rtable = nconc(rtable, addedrtable);
- parsetree->rtable = rtable;
-
- /* FOR UPDATE of view... */
- foreach(l, parsetree->rowMark)
- {
- if (((RowMark *) lfirst(l))->rti == rt_index)
- break;
- }
- if (l != NULL) /* oh, hell -:) */
- {
- RowMark *newrm;
- Index rti = 1;
- List *l2;
-
- CheckSelectForUpdate(rule_action);
+ parsetree->rowMarks = lremovei(rt_index, parsetree->rowMarks);
/*
- * We believe that rt_index is VIEW - nothing should be marked for
- * VIEW, but ACL check must be done. As for real tables of VIEW -
- * their rows must be marked, but we have to skip ACL check for
- * them.
+ * Set up the view's referenced tables as if FOR UPDATE.
*/
- ((RowMark *) lfirst(l))->info &= ~ROW_MARK_FOR_UPDATE;
-
- foreach(l2, rule_action->rtable)
+ foreach(l, rule_action->rtable)
{
+ subrte = (RangeTblEntry *) lfirst(l);
/*
- * RTable of VIEW has two entries of VIEW itself - we use
- * relid to skip them.
+ * RTable of VIEW has two entries of VIEW itself - skip them!
+ * Also keep hands off of sub-subqueries.
*/
- if (relation->rd_id != ((RangeTblEntry *) lfirst(l2))->relid)
+ if (innerrti != PRS2_OLD_VARNO && innerrti != PRS2_NEW_VARNO &&
+ subrte->relid != InvalidOid)
{
- newrm = makeNode(RowMark);
- newrm->rti = rti + rt_length;
- newrm->info = ROW_MARK_FOR_UPDATE;
- lnext(l) = lcons(newrm, lnext(l));
- l = lnext(l);
+ if (!intMember(innerrti, rule_action->rowMarks))
+ rule_action->rowMarks = lappendi(rule_action->rowMarks,
+ innerrti);
+ subrte->checkForWrite = true;
}
- rti++;
+ innerrti++;
}
}
- rule_action->rtable = rtable;
- OffsetVarNodes((Node *) rule_qual, rt_length, 0);
- OffsetVarNodes((Node *) rule_action, rt_length, 0);
-
- ChangeVarNodes((Node *) rule_qual,
- PRS2_OLD_VARNO + rt_length, rt_index, 0);
- ChangeVarNodes((Node *) rule_action,
- PRS2_OLD_VARNO + rt_length, rt_index, 0);
-
- if (relation_level)
- {
- RangeTblEntry *rte = rt_fetch(rt_index, rtable);
-
- parsetree = (Query *) apply_RIR_view((Node *) parsetree,
- rt_index, rte,
- rule_action->targetList,
- &modified, 0);
- rule_action = (Query *) apply_RIR_view((Node *) rule_action,
- rt_index, rte,
- rule_action->targetList,
- &modified, 0);
- /* always apply quals of relation-level rules, whether we found a
- * var to substitute or not.
- */
- modified = true;
- }
- else
- {
- HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
- rt_index, rule->attrno, &modified, &badsql);
- /* quals will be inserted only if we found uses of the attribute */
- }
- if (modified && !badsql)
- {
- AddQual(parsetree, rule_action->qual);
- AddGroupClause(parsetree, rule_action->groupClause,
- rule_action->targetList);
- AddHavingQual(parsetree, rule_action->havingQual);
- parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
- parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
- }
-
return parsetree;
}
/*
- * fireRIRonSubselect -
- * Apply fireRIRrules() to each subselect found in the given tree.
+ * fireRIRonSubLink -
+ * Apply fireRIRrules() to each SubLink (subselect in expression) found
+ * in the given tree.
*
* NOTE: although this has the form of a walker, we cheat and modify the
* SubLink nodes in-place. It is caller's responsibility to ensure that
@@ -764,7 +339,7 @@ ApplyRetrieveRule(Query *parsetree,
* the SubLink's subselect link with the possibly-rewritten subquery.
*/
static bool
-fireRIRonSubselect(Node *node, void *context)
+fireRIRonSubLink(Node *node, void *context)
{
if (node == NULL)
return false;
@@ -778,9 +353,9 @@ fireRIRonSubselect(Node *node, void *context)
}
/*
* Do NOT recurse into Query nodes, because fireRIRrules already
- * processed subselects for us.
+ * processed subselects of subselects for us.
*/
- return expression_tree_walker(node, fireRIRonSubselect,
+ return expression_tree_walker(node, fireRIRonSubLink,
(void *) context);
}
@@ -798,7 +373,6 @@ fireRIRrules(Query *parsetree)
List *locks;
RuleLock *rules;
RewriteRule *rule;
- RewriteRule RIRonly;
bool relIsUsed;
int i;
List *l;
@@ -815,6 +389,17 @@ fireRIRrules(Query *parsetree)
rte = rt_fetch(rt_index, parsetree->rtable);
/*
+ * A subquery RTE can't have associated rules, so there's nothing
+ * to do to this level of the query, but we must recurse into the
+ * subquery to expand any rule references in it.
+ */
+ if (rte->subquery)
+ {
+ rte->subquery = fireRIRrules(rte->subquery);
+ continue;
+ }
+
+ /*
* If the table is not referenced in the query, then we ignore it.
* This prevents infinite expansion loop due to new rtable entries
* inserted by expansion of a rule. A table is referenced if it is
@@ -856,26 +441,16 @@ fireRIRrules(Query *parsetree)
}
/*
- * Check permissions
- */
- checkLockPerms(locks, parsetree, rt_index);
-
- /*
* Now apply them
*/
foreach(l, locks)
{
rule = lfirst(l);
- RIRonly.event = rule->event;
- RIRonly.attrno = rule->attrno;
- RIRonly.qual = rule->qual;
- RIRonly.actions = rule->actions;
-
parsetree = ApplyRetrieveRule(parsetree,
- &RIRonly,
+ rule,
rt_index,
- RIRonly.attrno == -1,
+ rule->attrno == -1,
rel,
relIsUsed);
}
@@ -883,11 +458,30 @@ fireRIRrules(Query *parsetree)
heap_close(rel, AccessShareLock);
}
- if (parsetree->hasAggs)
- parsetree->qual = modifyAggrefQual(parsetree->qual, parsetree);
+ /*
+ * Recurse into sublink subqueries, too.
+ */
+ if (parsetree->hasSubLinks)
+ query_tree_walker(parsetree, fireRIRonSubLink, NULL);
+ /*
+ * If the query was marked having aggregates, check if this is
+ * still true after rewriting. Ditto for sublinks. Note there
+ * should be no aggs in the qual at this point. (Does this code
+ * still do anything useful? The view-becomes-subselect-in-FROM
+ * approach doesn't look like it could remove aggs or sublinks...)
+ */
+ if (parsetree->hasAggs)
+ {
+ parsetree->hasAggs = checkExprHasAggs((Node *) parsetree);
+ if (parsetree->hasAggs)
+ if (checkExprHasAggs((Node *) parsetree->jointree))
+ elog(ERROR, "fireRIRrules: failed to remove aggs from qual");
+ }
if (parsetree->hasSubLinks)
- query_tree_walker(parsetree, fireRIRonSubselect, NULL);
+ {
+ parsetree->hasSubLinks = checkExprHasSubLink((Node *) parsetree);
+ }
return parsetree;
}
@@ -919,8 +513,7 @@ orderRules(List *locks)
else
regular = lappend(regular, rule_lock);
}
- regular = nconc(regular, instead_qualified);
- return nconc(regular, instead_rules);
+ return nconc(nconc(regular, instead_qualified), instead_rules);
}
@@ -944,20 +537,20 @@ CopyAndAddQual(Query *parsetree,
{
List *rtable;
int rt_length;
- List *jointree;
+ List *jointreelist;
rtable = new_tree->rtable;
rt_length = length(rtable);
rtable = nconc(rtable, copyObject(rule_action->rtable));
- /* XXX above possibly wrong for subselect-in-FROM */
new_tree->rtable = rtable;
OffsetVarNodes(new_qual, rt_length, 0);
ChangeVarNodes(new_qual, PRS2_OLD_VARNO + rt_length, rt_index, 0);
- jointree = copyObject(rule_action->jointree);
- OffsetVarNodes((Node *) jointree, rt_length, 0);
- ChangeVarNodes((Node *) jointree, PRS2_OLD_VARNO + rt_length,
+ jointreelist = copyObject(rule_action->jointree->fromlist);
+ OffsetVarNodes((Node *) jointreelist, rt_length, 0);
+ ChangeVarNodes((Node *) jointreelist, PRS2_OLD_VARNO + rt_length,
rt_index, 0);
- new_tree->jointree = nconc(new_tree->jointree, jointree);
+ new_tree->jointree->fromlist = nconc(new_tree->jointree->fromlist,
+ jointreelist);
}
/* XXX -- where current doesn't work for instead nothing.... yet */
AddNotQual(new_tree, new_qual);
@@ -995,6 +588,7 @@ fireRules(Query *parsetree,
return NIL;
locks = orderRules(locks); /* real instead rules last */
+
foreach(i, locks)
{
RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
@@ -1002,49 +596,11 @@ fireRules(Query *parsetree,
List *actions;
List *r;
- /*
- * Instead rules change the resultRelation of the query. So the
- * permission checks on the initial resultRelation would never be
- * done (this is normally done in the executor deep down). So we
- * must do it here. The result relations resulting from earlier
- * rewrites are already checked against the rules eventrelation
- * owner (during matchLocks) and have the skipAcl flag set.
- */
- if (rule_lock->isInstead &&
- parsetree->commandType != CMD_SELECT)
- {
- RangeTblEntry *rte;
- int32 acl_rc;
- int32 reqperm;
-
- switch (parsetree->commandType)
- {
- case CMD_INSERT:
- reqperm = ACL_AP;
- break;
- default:
- reqperm = ACL_WR;
- break;
- }
-
- rte = rt_fetch(parsetree->resultRelation, parsetree->rtable);
- if (!rte->skipAcl)
- {
- acl_rc = pg_aclcheck(rte->relname,
- GetUserId(), reqperm);
- if (acl_rc != ACLCHECK_OK)
- {
- elog(ERROR, "%s: %s",
- rte->relname,
- aclcheck_error_strings[acl_rc]);
- }
- }
- }
-
/* multiple rule action time */
*instead_flag = rule_lock->isInstead;
event_qual = rule_lock->qual;
actions = rule_lock->actions;
+
if (event_qual != NULL && *instead_flag)
{
Query *qual_product;
@@ -1065,7 +621,7 @@ fireRules(Query *parsetree,
if (*qual_products == NIL)
qual_product = parsetree;
else
- qual_product = (Query *) nth(0, *qual_products);
+ qual_product = (Query *) lfirst(*qual_products);
MemSet(&qual_info, 0, sizeof(qual_info));
qual_info.event = qual_product->commandType;
@@ -1083,7 +639,7 @@ fireRules(Query *parsetree,
if (event == CMD_INSERT || event == CMD_UPDATE)
FixNew(&qual_info, qual_product);
- *qual_products = lappend(NIL, qual_product);
+ *qual_products = makeList1(qual_product);
}
foreach(r, actions)
@@ -1141,10 +697,10 @@ fireRules(Query *parsetree,
* splitting into two queries one w/rule_qual, one w/NOT
* rule_qual. Also add user query qual onto rule action
*/
- AddQual(info->rule_action, parsetree->qual);
-
AddQual(info->rule_action, info->rule_qual);
+ AddQual(info->rule_action, parsetree->jointree->quals);
+
/*--------------------------------------------------
* Step 2:
* Rewrite new.attribute w/ right hand side of target-list
@@ -1183,10 +739,10 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
{
CmdType event;
List *product_queries = NIL;
- int result_relation = 0;
+ int result_relation;
RangeTblEntry *rt_entry;
- Relation rt_entry_relation = NULL;
- RuleLock *rt_entry_locks = NULL;
+ Relation rt_entry_relation;
+ RuleLock *rt_entry_locks;
Assert(parsetree != NULL);
@@ -1251,7 +807,7 @@ deepRewriteQuery(Query *parsetree)
{
List *n;
List *rewritten = NIL;
- List *result = NIL;
+ List *result;
bool instead;
List *qual_products = NIL;
@@ -1267,7 +823,7 @@ deepRewriteQuery(Query *parsetree)
foreach(n, result)
{
Query *pt = lfirst(n);
- List *newstuff = NIL;
+ List *newstuff;
newstuff = deepRewriteQuery(pt);
if (newstuff != NIL)
@@ -1343,23 +899,6 @@ BasicQueryRewrite(Query *parsetree)
foreach(l, querylist)
{
query = fireRIRrules((Query *) lfirst(l));
-
- /*
- * If the query was marked having aggregates, check if this is
- * still true after rewriting. Ditto for sublinks. Note there
- * should be no aggs in the qual at this point.
- */
- if (query->hasAggs)
- {
- query->hasAggs = checkExprHasAggs((Node *) query);
- if (query->hasAggs)
- if (checkExprHasAggs(query->qual))
- elog(ERROR, "BasicQueryRewrite: failed to remove aggs from qual");
- }
- if (query->hasSubLinks)
- {
- query->hasSubLinks = checkExprHasSubLink((Node *) query);
- }
results = lappend(results, query);
}
@@ -1498,7 +1037,8 @@ check_targetlists_are_compatible(List *prev_target, List *current_target)
}
}
-/* Rewrites UNION INTERSECT and EXCEPT queries to semantiacally equivalent
+/*
+ * Rewrites UNION INTERSECT and EXCEPT queries to semantically equivalent
* queries that use IN and NOT IN subselects.
*
* The operator tree is attached to 'intersectClause' (see rule
@@ -1516,7 +1056,8 @@ check_targetlists_are_compatible(List *prev_target, List *current_target)
* (sortClause etc) are attached to the new top Node (Note that the
* new top Node can differ from the parsetree given as argument because of
* the translation to DNF. That's why we have to remember the sortClause
- * and so on!) */
+ * and so on!)
+ */
static Query *
Except_Intersect_Rewrite(Query *parsetree)
{
@@ -1829,11 +1370,12 @@ Except_Intersect_Rewrite(Query *parsetree)
return result;
}
-/* Create a list of nodes that are either Query nodes of NOT Expr
+/*
+ * Create a list of nodes that are either Query nodes of NOT Expr
* nodes followed by a Query node. The tree given in ptr contains at
* least one non negated Query node. This node is attached to the
- * beginning of the list */
-
+ * beginning of the list.
+ */
static void
create_intersect_list(Node *ptr, List **intersect_list)
{
@@ -1864,10 +1406,12 @@ create_intersect_list(Node *ptr, List **intersect_list)
}
}
-/* The nodes given in 'tree' are still 'raw' so 'cook' them using parse_analyze().
- * The node given in first_select has already been cooked, so don't transform
- * it again but return a pointer to the previously cooked version given in 'parsetree'
- * instead. */
+/*
+ * The nodes given in 'tree' are still 'raw' so 'cook' them using
+ * parse_analyze(). The node given in first_select has already been cooked,
+ * so don't transform it again but return a pointer to the previously cooked
+ * version given in 'parsetree' instead.
+ */
static Node *
intersect_tree_analyze(Node *tree, Node *first_select, Node *parsetree)
{
@@ -1886,7 +1430,7 @@ intersect_tree_analyze(Node *tree, Node *first_select, Node *parsetree)
else
{
/* transform the 'raw' nodes to 'cooked' Query nodes */
- List *qtree = parse_analyze(lcons(tree, NIL), NULL);
+ List *qtree = parse_analyze(makeList1(tree), NULL);
result = (Node *) lfirst(qtree);
}