aboutsummaryrefslogtreecommitdiff
path: root/src/backend/rewrite/rewriteHandler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/rewrite/rewriteHandler.c')
-rw-r--r--src/backend/rewrite/rewriteHandler.c53
1 files changed, 43 insertions, 10 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b35a52d2ef4..45bcc84cf02 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -418,6 +418,10 @@ rewriteRuleAction(Query *parsetree,
* NOTE: because planner will destructively alter rtable, we must ensure
* that rule action's rtable is separate and shares no substructure with
* the main rtable. Hence do a deep copy here.
+ *
+ * Note also that RewriteQuery() relies on the fact that RT entries from
+ * the original query appear at the start of the expanded rtable, so
+ * beware of changing this.
*/
sub_action->rtable = list_concat(copyObject(parsetree->rtable),
sub_action->rtable);
@@ -3622,9 +3626,13 @@ rewriteTargetView(Query *parsetree, Relation view)
*
* rewrite_events is a list of open query-rewrite actions, so we can detect
* infinite recursion.
+ *
+ * orig_rt_length is the length of the originating query's rtable, for product
+ * queries created by fireRules(), and 0 otherwise. This is used to skip any
+ * already-processed VALUES RTEs from the original query.
*/
static List *
-RewriteQuery(Query *parsetree, List *rewrite_events)
+RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
{
CmdType event = parsetree->commandType;
bool instead = false;
@@ -3648,7 +3656,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
if (ctequery->commandType == CMD_SELECT)
continue;
- newstuff = RewriteQuery(ctequery, rewrite_events);
+ newstuff = RewriteQuery(ctequery, rewrite_events, 0);
/*
* Currently we can only handle unconditional, single-statement DO
@@ -3722,6 +3730,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
RangeTblEntry *rt_entry;
Relation rt_entry_relation;
List *locks;
+ int product_orig_rt_length;
List *product_queries;
bool hasUpdate = false;
int values_rte_index = 0;
@@ -3743,23 +3752,30 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
*/
if (event == CMD_INSERT)
{
+ ListCell *lc2;
RangeTblEntry *values_rte = NULL;
/*
- * If it's an INSERT ... VALUES (...), (...), ... there will be a
- * single RTE for the VALUES targetlists.
+ * Test if it's a multi-row INSERT ... VALUES (...), (...), ... by
+ * looking for a VALUES RTE in the fromlist. For product queries,
+ * we must ignore any already-processed VALUES RTEs from the
+ * original query. These appear at the start of the rangetable.
*/
- if (list_length(parsetree->jointree->fromlist) == 1)
+ foreach(lc2, parsetree->jointree->fromlist)
{
- RangeTblRef *rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
+ RangeTblRef *rtr = (RangeTblRef *) lfirst(lc2);
- if (IsA(rtr, RangeTblRef))
+ if (IsA(rtr, RangeTblRef) && rtr->rtindex > orig_rt_length)
{
RangeTblEntry *rte = rt_fetch(rtr->rtindex,
parsetree->rtable);
if (rte->rtekind == RTE_VALUES)
{
+ /* should not find more than one VALUES RTE */
+ if (values_rte != NULL)
+ elog(ERROR, "more than one VALUES RTE found");
+
values_rte = rte;
values_rte_index = rtr->rtindex;
}
@@ -3837,7 +3853,11 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
break;
case CMD_UPDATE:
case CMD_INSERT:
- /* XXX is it possible to have a VALUES clause? */
+
+ /*
+ * MERGE actions do not permit multi-row INSERTs, so
+ * there is no VALUES RTE to deal with here.
+ */
action->targetList =
rewriteTargetListIU(action->targetList,
action->commandType,
@@ -3864,6 +3884,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
locks = matchLocks(event, rt_entry_relation->rd_rules,
result_relation, parsetree, &hasUpdate);
+ product_orig_rt_length = list_length(parsetree->rtable);
product_queries = fireRules(parsetree,
result_relation,
event,
@@ -4020,7 +4041,19 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
Query *pt = (Query *) lfirst(n);
List *newstuff;
- newstuff = RewriteQuery(pt, rewrite_events);
+ /*
+ * For an updatable view, pt might be the rewritten version of
+ * the original query, in which case we pass on orig_rt_length
+ * to finish processing any VALUES RTE it contained.
+ *
+ * Otherwise, we have a product query created by fireRules().
+ * Any VALUES RTEs from the original query have been fully
+ * processed, and must be skipped when we recurse.
+ */
+ newstuff = RewriteQuery(pt, rewrite_events,
+ pt == parsetree ?
+ orig_rt_length :
+ product_orig_rt_length);
rewritten = list_concat(rewritten, newstuff);
}
@@ -4172,7 +4205,7 @@ QueryRewrite(Query *parsetree)
*
* Apply all non-SELECT rules possibly getting 0 or many queries
*/
- querylist = RewriteQuery(parsetree, NIL);
+ querylist = RewriteQuery(parsetree, NIL, 0);
/*
* Step 2