aboutsummaryrefslogtreecommitdiff
path: root/src/backend/rewrite/rewriteHandler.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2000-12-05 19:15:10 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2000-12-05 19:15:10 +0000
commita51f004d29c67823e3a99e9aff141ce9333df0e2 (patch)
tree940bd0aa2afb2c1fb02a12a6d4871f5caa4a78e4 /src/backend/rewrite/rewriteHandler.c
parentd9466046c076b023b55d448d3cce6980dac24865 (diff)
downloadpostgresql-a51f004d29c67823e3a99e9aff141ce9333df0e2.tar.gz
postgresql-a51f004d29c67823e3a99e9aff141ce9333df0e2.zip
Repair breakage of rules containing INSERT ... SELECT actions, per bug
report from Joel Burton. Turns out that my simple idea of turning the SELECT into a subquery does not interact well *at all* with the way the rule rewriter works. Really what we need to make INSERT ... SELECT work cleanly is to decouple targetlists from rangetables: an INSERT ... SELECT wants to have two levels of targetlist but only one rangetable. No time for that for 7.1, however, so I've inserted some ugly hacks to make the rewriter know explicitly about the structure of INSERT ... SELECT queries. Ugh :-(
Diffstat (limited to 'src/backend/rewrite/rewriteHandler.c')
-rw-r--r--src/backend/rewrite/rewriteHandler.c320
1 files changed, 150 insertions, 170 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index a0a9d5671e5..6b842582b43 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.83 2000/11/08 22:09:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.84 2000/12/05 19:15:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -61,6 +61,8 @@ gatherRewriteMeta(Query *parsetree,
bool instead_flag)
{
RewriteInfo *info;
+ Query *sub_action;
+ Query **sub_action_ptr;
int rt_length;
info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
@@ -70,85 +72,135 @@ gatherRewriteMeta(Query *parsetree,
info->rule_action = (Query *) copyObject(rule_action);
info->rule_qual = (Node *) copyObject(rule_qual);
if (info->rule_action == NULL)
- info->nothing = TRUE;
- else
{
- info->nothing = FALSE;
- info->action = info->rule_action->commandType;
- info->current_varno = rt_index;
- rt_length = length(parsetree->rtable);
-
- /* Adjust rule action and qual to offset its varnos */
- info->new_varno = PRS2_NEW_VARNO + rt_length;
- OffsetVarNodes((Node *) info->rule_action, rt_length, 0);
- OffsetVarNodes(info->rule_qual, rt_length, 0);
- /* but its references to *OLD* should point at original rt_index */
- ChangeVarNodes((Node *) info->rule_action,
- PRS2_OLD_VARNO + rt_length, rt_index, 0);
- ChangeVarNodes(info->rule_qual,
- PRS2_OLD_VARNO + rt_length, rt_index, 0);
+ info->nothing = TRUE;
+ return info;
+ }
+ info->nothing = FALSE;
+ info->action = info->rule_action->commandType;
+ info->current_varno = rt_index;
+ rt_length = length(parsetree->rtable);
+ info->new_varno = PRS2_NEW_VARNO + rt_length;
- /*
- * We want the main parsetree's rtable to end up as the concatenation
- * of its original contents plus those of all the relevant rule
- * actions. Also store same into all the rule_action rtables.
- * Some of the entries may be unused after we finish rewriting, but
- * if we tried to clean those out we'd have a much harder job to
- * adjust RT indexes in the query's Vars. It's OK to have unused
- * RT entries, since planner will ignore them.
- *
- * NOTE KLUGY HACK: we assume the parsetree rtable had at least one
- * entry to begin with (OK enough, else where'd the rule come from?).
- * Because of this, if multiple rules nconc() their rtable additions
- * onto parsetree->rtable, they'll all see the same rtable because
- * they all have the same list head pointer.
- */
- parsetree->rtable = nconc(parsetree->rtable,
- info->rule_action->rtable);
- info->rule_action->rtable = parsetree->rtable;
+ /*
+ * Adjust rule action and qual to offset its varnos, so that we can
+ * merge its rtable into the main parsetree's rtable.
+ *
+ * If the rule action is an INSERT...SELECT, the OLD/NEW rtable
+ * entries will be in the SELECT part, and we have to modify that
+ * rather than the top-level INSERT (kluge!).
+ */
+ sub_action = getInsertSelectQuery(info->rule_action, &sub_action_ptr);
- /*
- * Each rule action's jointree should be the main parsetree's jointree
- * plus that rule's jointree, but *without* the original rtindex
- * 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.
- */
+ OffsetVarNodes((Node *) sub_action, rt_length, 0);
+ OffsetVarNodes(info->rule_qual, rt_length, 0);
+ /* but references to *OLD* should point at original rt_index */
+ ChangeVarNodes((Node *) sub_action,
+ PRS2_OLD_VARNO + rt_length, rt_index, 0);
+ ChangeVarNodes(info->rule_qual,
+ PRS2_OLD_VARNO + rt_length, rt_index, 0);
+
+ /*
+ * Update resultRelation too ... perhaps this should be done by
+ * Offset/ChangeVarNodes?
+ */
+ if (sub_action->resultRelation)
+ {
+ int result_reln;
+ int new_result_reln;
+
+ result_reln = sub_action->resultRelation;
+ switch (result_reln)
{
- bool found;
- List *newjointree = adjustJoinTreeList(parsetree,
- rt_index,
- &found);
-
- info->rule_action->jointree->fromlist =
- nconc(newjointree,
- info->rule_action->jointree->fromlist);
+ case PRS2_OLD_VARNO:
+ new_result_reln = rt_index;
+ break;
+ case PRS2_NEW_VARNO:
+ default:
+ new_result_reln = result_reln + rt_length;
+ break;
}
+ sub_action->resultRelation = new_result_reln;
+ }
- /*
- * bug here about replace CURRENT -- sort of replace current is
- * deprecated now so this code shouldn't really need to be so
- * clutzy but.....
- */
- if (info->action != CMD_SELECT)
- { /* i.e update XXXXX */
- int result_reln;
- int new_result_reln;
+ /*
+ * We want the main parsetree's rtable to end up as the concatenation
+ * of its original contents plus those of all the relevant rule
+ * actions. Also store same into all the rule_action rtables.
+ * Some of the entries may be unused after we finish rewriting, but
+ * if we tried to clean those out we'd have a much harder job to
+ * adjust RT indexes in the query's Vars. It's OK to have unused
+ * RT entries, since planner will ignore them.
+ *
+ * NOTE KLUGY HACK: we assume the parsetree rtable had at least one
+ * entry to begin with (OK enough, else where'd the rule come from?).
+ * Because of this, if multiple rules nconc() their rtable additions
+ * onto parsetree->rtable, they'll all see the same rtable because
+ * they all have the same list head pointer.
+ */
+ parsetree->rtable = nconc(parsetree->rtable,
+ sub_action->rtable);
+ sub_action->rtable = parsetree->rtable;
- result_reln = info->rule_action->resultRelation;
- switch (result_reln)
- {
- case PRS2_OLD_VARNO:
- new_result_reln = rt_index;
- break;
- case PRS2_NEW_VARNO: /* XXX */
- default:
- new_result_reln = result_reln + rt_length;
- break;
- }
- info->rule_action->resultRelation = new_result_reln;
- }
+ /*
+ * Each rule action's jointree should be the main parsetree's jointree
+ * plus that rule's jointree, but *without* the original rtindex
+ * 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.
+ */
+ {
+ bool found;
+ List *newjointree = adjustJoinTreeList(parsetree,
+ rt_index,
+ &found);
+
+ sub_action->jointree->fromlist =
+ nconc(newjointree, sub_action->jointree->fromlist);
}
+
+ /*
+ * We copy the qualifications of the parsetree to the action and vice
+ * versa. So force hasSubLinks if one of them has it. If this is not
+ * right, the flag will get cleared later, but we mustn't risk having
+ * it not set when it needs to be.
+ */
+ if (parsetree->hasSubLinks)
+ sub_action->hasSubLinks = TRUE;
+ else if (sub_action->hasSubLinks)
+ parsetree->hasSubLinks = TRUE;
+
+ /*
+ * 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
+ */
+ AddQual(sub_action, info->rule_qual);
+
+ AddQual(sub_action, parsetree->jointree->quals);
+
+ /*
+ * Rewrite new.attribute w/ right hand side of target-list
+ * entry for appropriate field name in insert/update.
+ *
+ * KLUGE ALERT: since ResolveNew returns a mutated copy, we can't just
+ * apply it to sub_action; we have to remember to update the sublink
+ * inside info->rule_action, too.
+ */
+ if (info->event == CMD_INSERT || info->event == CMD_UPDATE)
+ {
+ sub_action = (Query *) ResolveNew((Node *) sub_action,
+ info->new_varno,
+ 0,
+ parsetree->targetList,
+ info->event,
+ info->current_varno);
+ if (sub_action_ptr)
+ *sub_action_ptr = sub_action;
+ else
+ info->rule_action = sub_action;
+ }
+
return info;
}
@@ -536,42 +588,37 @@ orderRules(List *locks)
}
-
+/*
+ * Modify the given query by adding 'AND NOT rule_qual' to its qualification.
+ * This is used to generate suitable "else clauses" for conditional INSTEAD
+ * rules.
+ *
+ * The rule_qual may contain references to OLD or NEW. OLD references are
+ * replaced by references to the specified rt_index (the relation that the
+ * rule applies to). NEW references are only possible for INSERT and UPDATE
+ * queries on the relation itself, and so they should be replaced by copies
+ * of the related entries in the query's own targetlist.
+ */
static Query *
CopyAndAddQual(Query *parsetree,
- List *actions,
Node *rule_qual,
int rt_index,
CmdType event)
{
Query *new_tree = (Query *) copyObject(parsetree);
- Node *new_qual = NULL;
- Query *rule_action = NULL;
-
- if (actions)
- rule_action = lfirst(actions);
- if (rule_qual != NULL)
- new_qual = (Node *) copyObject(rule_qual);
- if (rule_action != NULL)
- {
- List *rtable;
- int rt_length;
- List *jointreelist;
-
- rtable = new_tree->rtable;
- rt_length = length(rtable);
- rtable = nconc(rtable, copyObject(rule_action->rtable));
- new_tree->rtable = rtable;
- OffsetVarNodes(new_qual, rt_length, 0);
- ChangeVarNodes(new_qual, PRS2_OLD_VARNO + rt_length, rt_index, 0);
- 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->fromlist = nconc(new_tree->jointree->fromlist,
- jointreelist);
- }
- /* XXX -- where current doesn't work for instead nothing.... yet */
+ Node *new_qual = (Node *) copyObject(rule_qual);
+
+ /* Fix references to OLD */
+ ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
+ /* Fix references to NEW */
+ if (event == CMD_INSERT || event == CMD_UPDATE)
+ new_qual = ResolveNew(new_qual,
+ PRS2_NEW_VARNO,
+ 0,
+ parsetree->targetList,
+ event,
+ rt_index);
+ /* And attach the fixed qual */
AddNotQual(new_tree, new_qual);
return new_tree;
@@ -598,7 +645,6 @@ fireRules(Query *parsetree,
List *locks,
List **qual_products)
{
- RewriteInfo *info;
List *results = NIL;
List *i;
@@ -623,7 +669,6 @@ fireRules(Query *parsetree,
if (event_qual != NULL && *instead_flag)
{
Query *qual_product;
- RewriteInfo qual_info;
/* ----------
* If there are instead rules with qualifications,
@@ -642,60 +687,23 @@ fireRules(Query *parsetree,
else
qual_product = (Query *) lfirst(*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,
rt_index,
event);
- qual_info.rule_action = qual_product;
-
- if (event == CMD_INSERT || event == CMD_UPDATE)
- FixNew(&qual_info, qual_product);
-
*qual_products = makeList1(qual_product);
}
foreach(r, actions)
{
Query *rule_action = lfirst(r);
- Node *rule_qual = copyObject(event_qual);
+ RewriteInfo *info;
if (rule_action->commandType == CMD_NOTHING)
continue;
- /*--------------------------------------------------
- * We copy the qualifications of the parsetree
- * to the action and vice versa. So force
- * hasSubLinks if one of them has it.
- *
- * As of 6.4 only parsetree qualifications can
- * have sublinks. If this changes, we must make
- * this a node lookup at the end of rewriting.
- *
- * Jan
- *--------------------------------------------------
- */
- if (parsetree->hasSubLinks && !rule_action->hasSubLinks)
- {
- rule_action = copyObject(rule_action);
- rule_action->hasSubLinks = TRUE;
- }
- if (!parsetree->hasSubLinks && rule_action->hasSubLinks)
- parsetree->hasSubLinks = TRUE;
-
- /*--------------------------------------------------
- * Step 1:
- * Rewrite current.attribute or current to tuple variable
- * this appears to be done in parser?
- *--------------------------------------------------
- */
- info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
+ info = gatherRewriteMeta(parsetree, rule_action, event_qual,
rt_index, event, *instead_flag);
/* handle escapable cases, or those handled by other code */
@@ -707,34 +715,6 @@ fireRules(Query *parsetree,
continue;
}
- if (info->action == info->event &&
- info->event == CMD_SELECT)
- continue;
-
- /*
- * 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
- */
- 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
- * entry for appropriate field name in insert/update
- *--------------------------------------------------
- */
- if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))
- FixNew(info, parsetree);
-
- /*--------------------------------------------------
- * Step 3:
- * Simplify? hey, no algorithm for simplification... let
- * the planner do it.
- *--------------------------------------------------
- */
results = lappend(results, info->rule_action);
pfree(info);