aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorMarc G. Fournier <scrappy@hub.org>1998-08-18 00:49:04 +0000
committerMarc G. Fournier <scrappy@hub.org>1998-08-18 00:49:04 +0000
commit338c54cbc10ed9ed7f7216f839b9b14e5e42b7db (patch)
treed2f549772ab7e62167c9ed9afd06f96513843889 /src/backend
parentfde65267539aa5daa00ed9a674a1a3a42cb0917d (diff)
downloadpostgresql-338c54cbc10ed9ed7f7216f839b9b14e5e42b7db.tar.gz
postgresql-338c54cbc10ed9ed7f7216f839b9b14e5e42b7db.zip
From: Jan Wieck <jwieck@debis.com>
Hi, as proposed here comes the first patch for the query rewrite system. <for details, see archive dated Mon, 17 Aug 1998>
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/parser/analyze.c28
-rw-r--r--src/backend/parser/gram.y3
-rw-r--r--src/backend/parser/parse_relation.c9
-rw-r--r--src/backend/rewrite/rewriteDefine.c7
-rw-r--r--src/backend/rewrite/rewriteHandler.c254
5 files changed, 263 insertions, 38 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 65f40f4cad8..2bdcef170f3 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.79 1998/07/20 20:48:51 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.80 1998/08/18 00:48:54 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -742,11 +742,33 @@ static Query *
transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
{
Query *qry;
+ Query *action;
List *actions;
qry = makeNode(Query);
qry->commandType = CMD_UTILITY;
+ /*
+ * 'instead nothing' rules with a qualification need a
+ * query a rangetable so the rewrite handler can add the
+ * negated rule qualification to the original query. We
+ * create a query with the new command type CMD_NOTHING
+ * here that is treated special by the rewrite system.
+ */
+ if (stmt->actions == NIL) {
+ Query *nothing_qry = makeNode(Query);
+ nothing_qry->commandType = CMD_NOTHING;
+
+ addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
+ FALSE, FALSE);
+ addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
+ FALSE, FALSE);
+
+ nothing_qry->rtable = pstate->p_rtable;
+
+ stmt->actions = lappend(NIL, nothing_qry);
+ }
+
actions = stmt->actions;
/*
@@ -768,7 +790,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
pstate->p_is_rule = true; /* for expand all */
pstate->p_hasAggs = false;
- lfirst(actions) = transformStmt(pstate, lfirst(actions));
+ action = (Query *)lfirst(actions);
+ if (action->commandType != CMD_NOTHING)
+ lfirst(actions) = transformStmt(pstate, lfirst(actions));
actions = lnext(actions);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0e0015f3e49..23db7a10731 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.22 1998/08/17 16:08:34 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.23 1998/08/18 00:48:55 scrappy Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -1955,6 +1955,7 @@ RuleStmt: CREATE RULE name AS
OptStmtList: NOTHING { $$ = NIL; }
| OptimizableStmt { $$ = lcons($1, NIL); }
| '[' OptStmtBlock ']' { $$ = $2; }
+ | '(' OptStmtBlock ')' { $$ = $2; }
;
OptStmtBlock: OptStmtMulti
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index fef834d2c6d..181152bfa5b 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.12 1998/07/08 14:04:11 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.13 1998/08/18 00:48:57 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -191,8 +191,13 @@ addRangeTableEntry(ParseState *pstate,
if (pstate != NULL)
{
if (refnameRangeTablePosn(pstate, refname, &sublevels_up) != 0 &&
- (!inFromCl || sublevels_up == 0))
+ (!inFromCl || sublevels_up == 0)) {
+ if (!strcmp(refname, "*CURRENT*") || !strcmp(refname, "*NEW*")) {
+ int rt_index = refnameRangeTablePosn(pstate, refname, &sublevels_up);
+ return (RangeTblEntry *)nth(rt_index - 1, pstate->p_rtable);
+ }
elog(ERROR, "Table name %s specified more than once", refname);
+ }
}
rte->relname = pstrdup(relname);
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index cb1bbd5bcf1..3c1b6528605 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.16 1998/06/15 19:29:06 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.17 1998/08/18 00:48:58 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -155,12 +155,7 @@ ValidateRule(int event_type,
"rules not allowed for insert or delete events to an attribute");
}
- if (event_qual && !*action && is_instead)
- elog(ERROR,
- "event_quals on 'instead nothing' rules not currently supported");
-
#if 0
-
/*
* on retrieve to class.attribute do instead nothing is converted to
* 'on retrieve to class.attribute do instead retrieve (attribute =
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index aff40937edb..e80fc4cf55e 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.17 1998/07/19 05:49:24 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.18 1998/08/18 00:48:59 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,6 +20,7 @@
#include "nodes/primnodes.h"
#include "parser/parsetree.h" /* for parsetree manipulation */
+#include "parser/parse_relation.h"
#include "nodes/parsenodes.h"
#include "rewrite/rewriteSupport.h"
@@ -45,6 +46,8 @@ static void QueryRewriteSubLink(Node *node);
static List *QueryRewriteOne(Query *parsetree);
static List *deepRewriteQuery(Query *parsetree);
static void CheckViewPerms(Relation view, List *rtable);
+static void RewritePreprocessQuery(Query *parsetree);
+static Query *RewritePostprocessNonSelect(Query *parsetree);
/*
* gatherRewriteMeta -
@@ -138,25 +141,30 @@ OptimizeRIRRules(List *locks)
}
/*
- * idea is to put instead rules before regular rules so that
- * excess semantically queasy queries aren't processed
+ * idea is to fire regular rules first, then qualified instead
+ * rules and unqualified instead rules last. Any lemming is counted for.
*/
static List *
orderRules(List *locks)
{
- List *regular = NIL,
- *i;
- List *instead_rules = NIL;
+ List *regular = NIL;
+ List *instead_rules = NIL;
+ List *instead_qualified = NIL;
+ List *i;
foreach(i, locks)
{
RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
- if (rule_lock->isInstead)
- instead_rules = lappend(instead_rules, rule_lock);
- else
+ if (rule_lock->isInstead) {
+ if (rule_lock->qual == NULL)
+ instead_rules = lappend(instead_rules, rule_lock);
+ else
+ instead_qualified = lappend(instead_qualified, rule_lock);
+ } else
regular = lappend(regular, rule_lock);
}
+ regular = nconc(regular, instead_qualified);
return nconc(regular, instead_rules);
}
@@ -234,7 +242,6 @@ FireRetrieveRulesAtQuery(Query *parsetree,
{
*instead_flag = TRUE;
FixResdomTypes(parsetree->targetList);
-
return lcons(parsetree, NIL);
}
}
@@ -411,8 +418,9 @@ ProcessRetrieveQuery(Query *parsetree,
rule);
}
heap_close(rt_entry_relation);
- if (*instead_flag)
+ if (*instead_flag) {
return result;
+ }
}
if (rule)
return NIL;
@@ -486,10 +494,13 @@ CopyAndAddQual(Query *parsetree,
/*
* fireRules -
- * Iterate through rule locks applying rules. After an instead rule
- * rule has been applied, return just new parsetree and let RewriteQuery
- * start the process all over again. The locks are reordered to maintain
- * sensible semantics. remember: reality is for dead birds -- glass
+ * Iterate through rule locks applying rules.
+ * All rules create their own parsetrees. Instead rules
+ * with rule qualification save the original parsetree
+ * and add their negated qualification to it. Real instead
+ * rules finally throw away the original parsetree.
+ *
+ * remember: reality is for dead birds -- glass
*
*/
static List *
@@ -516,7 +527,7 @@ fireRules(Query *parsetree,
return NIL;
}
- locks = orderRules(locks); /* instead rules first */
+ locks = orderRules(locks); /* real instead rules last */
foreach(i, locks)
{
RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
@@ -530,16 +541,52 @@ fireRules(Query *parsetree,
*instead_flag = rule_lock->isInstead;
event_qual = rule_lock->qual;
actions = rule_lock->actions;
- if (event_qual != NULL && *instead_flag)
- *qual_products =
- lappend(*qual_products,
- CopyAndAddQual(parsetree, actions, event_qual,
- rt_index, event));
+ if (event_qual != NULL && *instead_flag) {
+ Query *qual_product;
+ RewriteInfo qual_info;
+
+ /* ----------
+ * If there are instead rules with qualifications,
+ * the original query is still performed. But all
+ * the negated rule qualifications of the instead
+ * rules are added so it does it's actions only
+ * in cases where the rule quals of all instead
+ * rules are false. Think of it as the default
+ * action in a case. We save this in *qual_products
+ * so deepRewriteQuery() can add it to the query
+ * list after we mangled it up enough.
+ * ----------
+ */
+ if (*qual_products == NIL) {
+ qual_product = parsetree;
+ } else {
+ qual_product = (Query *)nth(0, *qual_products);
+ }
+
+ qual_info.event = qual_product->commandType;
+ qual_info.new_varno = length(qual_product->rtable) + 2;
+ qual_product = CopyAndAddQual(qual_product,
+ actions,
+ event_qual,
+ rt_index,
+ event);
+
+ qual_info.rule_action = qual_product;
+
+ if (event == CMD_INSERT || event == CMD_UPDATE)
+ FixNew(&qual_info, qual_product);
+
+ *qual_products = lappend(NIL, qual_product);
+ }
+
foreach(r, actions)
{
Query *rule_action = lfirst(r);
Node *rule_qual = copyObject(event_qual);
+ if (rule_action->commandType == CMD_NOTHING)
+ continue;
+
/*--------------------------------------------------
* Step 1:
* Rewrite current.attribute or current to tuple variable
@@ -563,7 +610,7 @@ fireRules(Query *parsetree,
continue;
/*
- * Event Qualification forces copying of parsetree --- XXX and
+ * Event Qualification forces copying of parsetree and
* splitting into two queries one w/rule_qual, one w/NOT
* rule_qual. Also add user query qual onto rule action
*/
@@ -601,12 +648,126 @@ fireRules(Query *parsetree,
pfree(info);
}
- if (*instead_flag)
- break;
+
+ /* ----------
+ * If this was an unqualified instead rule,
+ * throw away an eventually saved 'default' parsetree
+ * ----------
+ */
+ if (event_qual == NULL && *instead_flag) {
+ *qual_products = NIL;
+ }
}
return results;
}
+/* ----------
+ * RewritePreprocessQuery -
+ * adjust details in the parsetree, the rule system
+ * depends on
+ * ----------
+ */
+static void
+RewritePreprocessQuery(Query *parsetree)
+{
+ /* ----------
+ * if the query has a resultRelation, reassign the
+ * result domain numbers to the attribute numbers in the
+ * target relation. FixNew() depends on it when replacing
+ * *new* references in a rule action by the expressions
+ * from the rewritten query.
+ * ----------
+ */
+ if (parsetree->resultRelation > 0) {
+ RangeTblEntry *rte;
+ Relation rd;
+ List *tl;
+ TargetEntry *tle;
+ int resdomno;
+
+ rte = (RangeTblEntry *)nth(parsetree->resultRelation - 1,
+ parsetree->rtable);
+ rd = heap_openr(rte->relname);
+
+ foreach (tl, parsetree->targetList) {
+ tle = (TargetEntry *)lfirst(tl);
+ resdomno = attnameAttNum(rd, tle->resdom->resname);
+ tle->resdom->resno = resdomno;
+ }
+
+ heap_close(rd);
+ }
+}
+
+
+/* ----------
+ * RewritePostprocessNonSelect -
+ * apply instead select rules on a query fired in by
+ * the rewrite system
+ * ----------
+ */
+static Query *
+RewritePostprocessNonSelect(Query *parsetree)
+{
+ List *rt;
+ int rt_index = 0;
+ Query *newtree = copyObject(parsetree);
+
+ foreach(rt, parsetree->rtable)
+ {
+ RangeTblEntry *rt_entry = lfirst(rt);
+ Relation rt_entry_relation = NULL;
+ RuleLock *rt_entry_locks = NULL;
+ List *locks = NIL;
+ List *instead_locks = NIL;
+ List *lock;
+ RewriteRule *rule;
+
+ rt_index++;
+ rt_entry_relation = heap_openr(rt_entry->relname);
+ rt_entry_locks = rt_entry_relation->rd_rules;
+
+ if (rt_entry_locks)
+ {
+ int origcmdtype = newtree->commandType;
+ newtree->commandType = CMD_SELECT;
+ locks =
+ matchLocks(CMD_SELECT, rt_entry_locks, rt_index, newtree);
+ newtree->commandType = origcmdtype;
+ }
+ if (locks != NIL)
+ {
+ foreach (lock, locks) {
+ rule = (RewriteRule *)lfirst(lock);
+ if (rule->isInstead) {
+ instead_locks = nconc(instead_locks, lock);
+ }
+ }
+ }
+ if (instead_locks != NIL)
+ {
+ foreach (lock, instead_locks) {
+ int relation_level;
+ int modified = 0;
+
+ rule = (RewriteRule *)lfirst(lock);
+ relation_level = (rule->attrno == -1);
+
+ ApplyRetrieveRule(newtree,
+ rule,
+ rt_index,
+ relation_level,
+ rt_entry_relation,
+ &modified);
+ }
+ }
+
+ heap_close(rt_entry_relation);
+ }
+
+ return newtree;
+}
+
static List *
RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
{
@@ -648,7 +809,6 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
{
List *locks =
matchLocks(event, rt_entry_locks, result_relation, parsetree);
-
product_queries =
fireRules(parsetree,
result_relation,
@@ -657,6 +817,27 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
locks,
qual_products);
}
+
+ /* ----------
+ * deepRewriteQuery does not handle the situation
+ * where a query fired by a rule uses relations that
+ * have instead select rules defined (views and the like).
+ * So we care for them here.
+ * ----------
+ */
+ if (product_queries != NIL) {
+ List *pq;
+ Query *tmp;
+ List *new_products = NIL;
+
+ foreach (pq, product_queries) {
+ tmp = (Query *)lfirst(pq);
+ tmp = RewritePostprocessNonSelect(tmp);
+ new_products = lappend(new_products, tmp);
+ }
+ product_queries = new_products;
+ }
+
return product_queries;
}
else
@@ -697,6 +878,8 @@ static int numQueryRewriteInvoked = 0;
List *
QueryRewrite(Query *parsetree)
{
+ RewritePreprocessQuery(parsetree);
+
QueryRewriteSubLink(parsetree->qual);
QueryRewriteSubLink(parsetree->havingQual);
@@ -807,8 +990,6 @@ deepRewriteQuery(Query *parsetree)
instead = FALSE;
result = RewriteQuery(parsetree, &instead, &qual_products);
- if (!instead)
- rewritten = lcons(parsetree, NIL);
foreach(n, result)
{
@@ -819,9 +1000,28 @@ deepRewriteQuery(Query *parsetree)
if (newstuff != NIL)
rewritten = nconc(rewritten, newstuff);
}
+
+ /* ----------
+ * qual_products are the original query with the negated
+ * rule qualification of an instead rule
+ * ----------
+ */
if (qual_products != NIL)
rewritten = nconc(rewritten, qual_products);
+ /* ----------
+ * The original query is appended last if not instead
+ * because update and delete rule actions might not do
+ * anything if they are invoked after the update or
+ * delete is performed. The command counter increment
+ * between the query execution makes the deleted (and
+ * maybe the updated) tuples disappear so the scans
+ * for them in the rule actions cannot find them.
+ * ----------
+ */
+ if (!instead)
+ rewritten = lappend(rewritten, parsetree);
+
return rewritten;
}