diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 7a1151f0c9..7c73a87824 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.96 2000/11/12 00:36:58 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.97 2000/12/06 23:55:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,6 +25,7 @@ #include "optimizer/subselect.h" #include "optimizer/tlist.h" #include "optimizer/var.h" +#include "parser/analyze.h" #include "parser/parsetree.h" #include "parser/parse_expr.h" #include "rewrite/rewriteManip.h" @@ -255,6 +256,7 @@ pull_up_subqueries(Query *parse, Node *jtnode) int rtoffset; Node *subjointree; List *subtlist; + List *l; /* * First, recursively pull up the subquery's subqueries, @@ -289,10 +291,22 @@ pull_up_subqueries(Query *parse, Node *jtnode) parse->havingQual = ResolveNew(parse->havingQual, varno, 0, subtlist, CMD_SELECT, 0); + /* + * Pull up any FOR UPDATE markers, too. + */ + foreach(l, subquery->rowMarks) + { + int submark = lfirsti(l); + + parse->rowMarks = lappendi(parse->rowMarks, + submark + rtoffset); + } /* * Miscellaneous housekeeping. */ parse->hasSubLinks |= subquery->hasSubLinks; + /* subquery won't be pulled up if it hasAggs, so no work there */ + /* * Return the adjusted subquery jointree to replace the * RangeTblRef entry in my jointree. @@ -340,11 +354,6 @@ is_simple_subquery(Query *subquery) subquery->into != NULL || subquery->isPortal) elog(ERROR, "is_simple_subquery: subquery is bogus"); - /* - * Also check for currently-unsupported features. - */ - if (subquery->rowMarks) - elog(ERROR, "FOR UPDATE is not supported in subselects"); /* * Can't currently pull up a query with setops. * Maybe after querytree redesign... @@ -707,6 +716,13 @@ grouping_planner(Query *parse, double tuple_fraction) tlist = postprocess_setop_tlist(result_plan->targetlist, tlist); + /* + * Can't handle FOR UPDATE here (parser should have checked already, + * but let's make sure). + */ + if (parse->rowMarks) + elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"); + /* * We set current_pathkeys NIL indicating we do not know sort * order. This is correct when the top set operation is UNION ALL, @@ -744,6 +760,18 @@ grouping_planner(Query *parse, double tuple_fraction) { List *l; + /* + * We've got trouble if the FOR UPDATE appears inside grouping, + * since grouping renders a reference to individual tuple CTIDs + * invalid. This is also checked at parse time, but that's + * insufficient because of rule substitution, query pullup, etc. + */ + CheckSelectForUpdate(parse); + + /* Currently the executor only supports FOR UPDATE at top level */ + if (PlannerQueryLevel > 1) + elog(ERROR, "SELECT FOR UPDATE is not allowed in subselects"); + foreach(l, parse->rowMarks) { Index rti = lfirsti(l); diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index e483415876..2c33dba0ec 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.c,v 1.170 2000/12/05 19:57:55 tgl Exp $ + * $Id: analyze.c,v 1.171 2000/12/06 23:55:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,8 +37,6 @@ #include "mb/pg_wchar.h" #endif -void CheckSelectForUpdate(Query *qry); /* no points for style... */ - static Query *transformStmt(ParseState *pstate, Node *stmt); static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 6b842582b4..21372e0794 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.84 2000/12/05 19:15:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.85 2000/12/06 23:55:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,9 +31,6 @@ #include "utils/lsyscache.h" -extern void CheckSelectForUpdate(Query *rule_action); /* in analyze.c */ - - static RewriteInfo *gatherRewriteMeta(Query *parsetree, Query *rule_action, Node *rule_qual, @@ -100,29 +97,6 @@ gatherRewriteMeta(Query *parsetree, 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) - { - 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; - } - /* * We want the main parsetree's rtable to end up as the concatenation * of its original contents plus those of all the relevant rule @@ -336,8 +310,6 @@ ApplyRetrieveRule(Query *parsetree, { Index innerrti = 1; - CheckSelectForUpdate(rule_action); - /* * Remove the view from the list of rels that will actually be * marked FOR UPDATE by the executor. It will still be access- diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 02aa2d7665..0e84c5fa04 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.52 2000/12/05 19:15:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.53 2000/12/06 23:55:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -169,8 +169,29 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up) * sublevels_up doesn't get incremented prematurely. */ if (node && IsA(node, Query)) - query_tree_walker((Query *) node, OffsetVarNodes_walker, + { + Query *qry = (Query *) node; + List *l; + + /* + * If we are starting at a Query, and sublevels_up is zero, then we + * must also fix rangetable indexes in the Query itself --- namely + * resultRelation and rowMarks entries. sublevels_up cannot be zero + * when recursing into a subquery, so there's no need to have the + * same logic inside OffsetVarNodes_walker. + */ + if (sublevels_up == 0) + { + if (qry->resultRelation) + qry->resultRelation += offset; + foreach(l, qry->rowMarks) + { + lfirsti(l) += offset; + } + } + query_tree_walker(qry, OffsetVarNodes_walker, (void *) &context, true); + } else OffsetVarNodes_walker(node, &context); } @@ -252,8 +273,30 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up) * sublevels_up doesn't get incremented prematurely. */ if (node && IsA(node, Query)) - query_tree_walker((Query *) node, ChangeVarNodes_walker, + { + Query *qry = (Query *) node; + List *l; + + /* + * If we are starting at a Query, and sublevels_up is zero, then we + * must also fix rangetable indexes in the Query itself --- namely + * resultRelation and rowMarks entries. sublevels_up cannot be zero + * when recursing into a subquery, so there's no need to have the + * same logic inside ChangeVarNodes_walker. + */ + if (sublevels_up == 0) + { + if (qry->resultRelation == rt_index) + qry->resultRelation = new_index; + foreach(l, qry->rowMarks) + { + if (lfirsti(l) == rt_index) + lfirsti(l) = new_index; + } + } + query_tree_walker(qry, ChangeVarNodes_walker, (void *) &context, true); + } else ChangeVarNodes_walker(node, &context); } diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h index 9d60e0f64c..baf5b57052 100644 --- a/src/include/parser/analyze.h +++ b/src/include/parser/analyze.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.h,v 1.12 2000/10/07 00:58:21 tgl Exp $ + * $Id: analyze.h,v 1.13 2000/12/06 23:55:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,4 +17,6 @@ extern List *parse_analyze(Node *parseTree, ParseState *parentParseState); +extern void CheckSelectForUpdate(Query *qry); + #endif /* ANALYZE_H */