1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-31 22:04:40 +03:00

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 :-(
This commit is contained in:
Tom Lane
2000-12-05 19:15:10 +00:00
parent d9466046c0
commit a51f004d29
5 changed files with 268 additions and 212 deletions

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.51 2000/11/16 22:30:29 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.52 2000/12/05 19:15:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -471,6 +471,70 @@ attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
}
/*
* If the given Query is an INSERT ... SELECT construct, extract and
* return the sub-Query node that represents the SELECT part. Otherwise
* return the given Query.
*
* If subquery_ptr is not NULL, then *subquery_ptr is set to the location
* of the link to the SELECT subquery inside parsetree, or NULL if not an
* INSERT ... SELECT.
*
* This is a hack needed because transformations on INSERT ... SELECTs that
* appear in rule actions should be applied to the source SELECT, not to the
* INSERT part. Perhaps this can be cleaned up with redesigned querytrees.
*/
Query *
getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
{
Query *selectquery;
RangeTblEntry *selectrte;
RangeTblRef *rtr;
if (subquery_ptr)
*subquery_ptr = NULL;
if (parsetree == NULL)
return parsetree;
if (parsetree->commandType != CMD_INSERT)
return parsetree;
/*
* Currently, this is ONLY applied to rule-action queries, and so
* we expect to find the *OLD* and *NEW* placeholder entries in the
* given query. If they're not there, it must be an INSERT/SELECT
* in which they've been pushed down to the SELECT.
*/
if (length(parsetree->rtable) >= 2 &&
strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->relname,
"*OLD*") == 0 &&
strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->relname,
"*NEW*") == 0)
return parsetree;
Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
if (length(parsetree->jointree->fromlist) != 1)
elog(ERROR, "getInsertSelectQuery: expected to find SELECT subquery");
rtr = (RangeTblRef *) lfirst(parsetree->jointree->fromlist);
Assert(IsA(rtr, RangeTblRef));
selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
selectquery = selectrte->subquery;
if (! (selectquery && IsA(selectquery, Query) &&
selectquery->commandType == CMD_SELECT))
elog(ERROR, "getInsertSelectQuery: expected to find SELECT subquery");
if (length(selectquery->rtable) >= 2 &&
strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->relname,
"*OLD*") == 0 &&
strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->relname,
"*NEW*") == 0)
{
if (subquery_ptr)
*subquery_ptr = & (selectrte->subquery);
return selectquery;
}
elog(ERROR, "getInsertSelectQuery: can't find rule placeholders");
return NULL; /* not reached */
}
/*
* Add the given qualifier condition to the query's WHERE clause
*/
@ -721,25 +785,6 @@ ResolveNew(Node *node, int target_varno, int sublevels_up,
return ResolveNew_mutator(node, &context);
}
/*
* Alternate interface to ResolveNew: substitute Vars in info->rule_action
* with targetlist items from the parsetree's targetlist.
*/
void
FixNew(RewriteInfo *info, Query *parsetree)
{
ResolveNew_context context;
context.target_varno = info->new_varno;
context.sublevels_up = 0;
context.targetlist = parsetree->targetList;
context.event = info->event;
context.update_varno = info->current_varno;
query_tree_mutator(info->rule_action, ResolveNew_mutator,
(void *) &context, true);
}
#ifdef NOT_USED