diff options
author | Tom Lane | 2006-04-30 18:30:40 +0000 |
---|---|---|
committer | Tom Lane | 2006-04-30 18:30:40 +0000 |
commit | 49bcc6fae0e550b5e7eb32b9e86f4c7e9012d344 (patch) | |
tree | 35342339c64b732b2a5dca6489838caedbcea269 | |
parent | 59ee46e60d9f7d99b8e90e0e1d98958537df097e (diff) |
Improve the representation of FOR UPDATE/FOR SHARE so that we can
support both FOR UPDATE and FOR SHARE in one command, as well as both
NOWAIT and normal WAIT behavior. The more general code is actually
simpler and cleaner.
29 files changed, 292 insertions, 217 deletions
diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index c5c44fe120..3957c6076b 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -30,7 +30,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replac [ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [, ...] ] [ LIMIT { <replaceable class="parameter">count</replaceable> | ALL } ] [ OFFSET <replaceable class="parameter">start</replaceable> ] - [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ] + [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ] where <replaceable class="parameter">from_item</replaceable> can be one of: @@ -142,8 +142,8 @@ where <replaceable class="parameter">from_item</replaceable> can be one of: <listitem> <para> - If the <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> - clause is specified, the + If <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> + is specified, the <command>SELECT</command> statement locks the selected rows against concurrent updates. (See <xref linkend="sql-for-update-share" endterm="sql-for-update-share-title"> below.) @@ -853,18 +853,26 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] </para> <para> - It is currently not allowed for a single <command>SELECT</command> - statement to include both <literal>FOR UPDATE</literal> and - <literal>FOR SHARE</literal>, nor can different parts of the statement use - both <literal>NOWAIT</> and normal waiting mode. - </para> - - <para> If specific tables are named in <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>, then only rows coming from those tables are locked; any other tables used in the <command>SELECT</command> are simply read as - usual. + usual. A <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> + clause without a table list affects all tables used in the command. + If <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> is + applied to a view or sub-query, it affects all tables used in + the view or sub-query. + </para> + + <para> + Multiple <literal>FOR UPDATE</literal> and <literal>FOR SHARE</literal> + clauses can be written if it is necessary to specify different locking + behavior for different tables. If the same table is mentioned (or + implicitly affected) by both <literal>FOR UPDATE</literal> and + <literal>FOR SHARE</literal> clauses, then it is processed as + <literal>FOR UPDATE</literal>. Similarly, a table is processed + as <literal>NOWAIT</> if that is specified in any of the clauses + affecting it. </para> <para> diff --git a/doc/src/sgml/ref/select_into.sgml b/doc/src/sgml/ref/select_into.sgml index 21d87e45d0..1fcff0a315 100644 --- a/doc/src/sgml/ref/select_into.sgml +++ b/doc/src/sgml/ref/select_into.sgml @@ -31,7 +31,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replac [ ORDER BY <replaceable class="PARAMETER">expression</replaceable> [ ASC | DESC | USING <replaceable class="PARAMETER">operator</replaceable> ] [, ...] ] [ LIMIT { <replaceable class="PARAMETER">count</replaceable> | ALL } ] [ OFFSET <replaceable class="PARAMETER">start</replaceable> ] - [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ] + [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ] </synopsis> </refsynopsisdiv> diff --git a/doc/src/sgml/sql.sgml b/doc/src/sgml/sql.sgml index bcb5a7669b..122929dacb 100644 --- a/doc/src/sgml/sql.sgml +++ b/doc/src/sgml/sql.sgml @@ -863,7 +863,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replac [ ORDER BY <replaceable class="PARAMETER">expression</replaceable> [ ASC | DESC | USING <replaceable class="PARAMETER">operator</replaceable> ] [, ...] ] [ LIMIT { <replaceable class="PARAMETER">count</replaceable> | ALL } ] [ OFFSET <replaceable class="PARAMETER">start</replaceable> ] - [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ] + [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ] </synopsis> </para> diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b4dd7d4fce..33275b12b4 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -452,6 +452,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) Relation intoRelationDesc; bool do_select_into; TupleDesc tupType; + ListCell *l; /* * Do permissions checks. It's sufficient to examine the query's top @@ -486,7 +487,6 @@ InitPlan(QueryDesc *queryDesc, int eflags) * parseTree->resultRelations identifies them all */ ResultRelInfo *resultRelInfo; - ListCell *l; numResultRelations = list_length(resultRelations); resultRelInfos = (ResultRelInfo *) @@ -549,26 +549,21 @@ InitPlan(QueryDesc *queryDesc, int eflags) * Have to lock relations selected FOR UPDATE/FOR SHARE */ estate->es_rowMarks = NIL; - estate->es_forUpdate = parseTree->forUpdate; - estate->es_rowNoWait = parseTree->rowNoWait; - if (parseTree->rowMarks != NIL) + foreach(l, parseTree->rowMarks) { - ListCell *l; - - foreach(l, parseTree->rowMarks) - { - Index rti = lfirst_int(l); - Oid relid = getrelid(rti, rangeTable); - Relation relation; - ExecRowMark *erm; - - relation = heap_open(relid, RowShareLock); - erm = (ExecRowMark *) palloc(sizeof(ExecRowMark)); - erm->relation = relation; - erm->rti = rti; - snprintf(erm->resname, sizeof(erm->resname), "ctid%u", rti); - estate->es_rowMarks = lappend(estate->es_rowMarks, erm); - } + RowMarkClause *rc = (RowMarkClause *) lfirst(l); + Oid relid = getrelid(rc->rti, rangeTable); + Relation relation; + ExecRowMark *erm; + + relation = heap_open(relid, RowShareLock); + erm = (ExecRowMark *) palloc(sizeof(ExecRowMark)); + erm->relation = relation; + erm->rti = rc->rti; + erm->forUpdate = rc->forUpdate; + erm->noWait = rc->noWait; + snprintf(erm->resname, sizeof(erm->resname), "ctid%u", rc->rti); + estate->es_rowMarks = lappend(estate->es_rowMarks, erm); } /* @@ -1222,7 +1217,7 @@ lnext: ; tuple.t_self = *((ItemPointer) DatumGetPointer(datum)); - if (estate->es_forUpdate) + if (erm->forUpdate) lockmode = LockTupleExclusive; else lockmode = LockTupleShared; @@ -1230,7 +1225,7 @@ lnext: ; test = heap_lock_tuple(erm->relation, &tuple, &buffer, &update_ctid, &update_xmax, estate->es_snapshot->curcid, - lockmode, estate->es_rowNoWait); + lockmode, erm->noWait); ReleaseBuffer(buffer); switch (test) { @@ -2258,8 +2253,6 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq) epqstate->es_param_exec_vals = (ParamExecData *) palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData)); epqstate->es_rowMarks = estate->es_rowMarks; - epqstate->es_forUpdate = estate->es_forUpdate; - epqstate->es_rowNoWait = estate->es_rowNoWait; epqstate->es_instrument = estate->es_instrument; epqstate->es_select_into = estate->es_select_into; epqstate->es_into_oids = estate->es_into_oids; diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 10a26c8c44..42dbb4235c 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -204,8 +204,6 @@ CreateExecutorState(void) estate->es_processed = 0; estate->es_lastoid = InvalidOid; estate->es_rowMarks = NIL; - estate->es_forUpdate = false; - estate->es_rowNoWait = false; estate->es_instrument = false; estate->es_select_into = false; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 79fd3fb99c..f919c9cda7 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1409,6 +1409,18 @@ _copyGroupClause(GroupClause *from) return newnode; } +static RowMarkClause * +_copyRowMarkClause(RowMarkClause *from) +{ + RowMarkClause *newnode = makeNode(RowMarkClause); + + COPY_SCALAR_FIELD(rti); + COPY_SCALAR_FIELD(forUpdate); + COPY_SCALAR_FIELD(noWait); + + return newnode; +} + static A_Expr * _copyAExpr(A_Expr *from) { @@ -1650,7 +1662,7 @@ _copyLockingClause(LockingClause *from) COPY_NODE_FIELD(lockedRels); COPY_SCALAR_FIELD(forUpdate); - COPY_SCALAR_FIELD(nowait); + COPY_SCALAR_FIELD(noWait); return newnode; } @@ -1673,9 +1685,6 @@ _copyQuery(Query *from) COPY_SCALAR_FIELD(hasSubLinks); COPY_NODE_FIELD(rtable); COPY_NODE_FIELD(jointree); - COPY_NODE_FIELD(rowMarks); - COPY_SCALAR_FIELD(forUpdate); - COPY_SCALAR_FIELD(rowNoWait); COPY_NODE_FIELD(targetList); COPY_NODE_FIELD(groupClause); COPY_NODE_FIELD(havingQual); @@ -1683,6 +1692,7 @@ _copyQuery(Query *from) COPY_NODE_FIELD(sortClause); COPY_NODE_FIELD(limitOffset); COPY_NODE_FIELD(limitCount); + COPY_NODE_FIELD(rowMarks); COPY_NODE_FIELD(setOperations); COPY_NODE_FIELD(resultRelations); @@ -3284,6 +3294,9 @@ copyObject(void *from) case T_GroupClause: retval = _copyGroupClause(from); break; + case T_RowMarkClause: + retval = _copyRowMarkClause(from); + break; case T_FkConstraint: retval = _copyFkConstraint(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 869e47e191..37cfe1248f 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -665,9 +665,6 @@ _equalQuery(Query *a, Query *b) COMPARE_SCALAR_FIELD(hasSubLinks); COMPARE_NODE_FIELD(rtable); COMPARE_NODE_FIELD(jointree); - COMPARE_NODE_FIELD(rowMarks); - COMPARE_SCALAR_FIELD(forUpdate); - COMPARE_SCALAR_FIELD(rowNoWait); COMPARE_NODE_FIELD(targetList); COMPARE_NODE_FIELD(groupClause); COMPARE_NODE_FIELD(havingQual); @@ -675,6 +672,7 @@ _equalQuery(Query *a, Query *b) COMPARE_NODE_FIELD(sortClause); COMPARE_NODE_FIELD(limitOffset); COMPARE_NODE_FIELD(limitCount); + COMPARE_NODE_FIELD(rowMarks); COMPARE_NODE_FIELD(setOperations); COMPARE_NODE_FIELD(resultRelations); @@ -1688,7 +1686,7 @@ _equalLockingClause(LockingClause *a, LockingClause *b) { COMPARE_NODE_FIELD(lockedRels); COMPARE_SCALAR_FIELD(forUpdate); - COMPARE_SCALAR_FIELD(nowait); + COMPARE_SCALAR_FIELD(noWait); return true; } @@ -1724,6 +1722,16 @@ _equalSortClause(SortClause *a, SortClause *b) } static bool +_equalRowMarkClause(RowMarkClause *a, RowMarkClause *b) +{ + COMPARE_SCALAR_FIELD(rti); + COMPARE_SCALAR_FIELD(forUpdate); + COMPARE_SCALAR_FIELD(noWait); + + return true; +} + +static bool _equalFkConstraint(FkConstraint *a, FkConstraint *b) { COMPARE_STRING_FIELD(constr_name); @@ -2297,6 +2305,9 @@ equal(void *a, void *b) /* GroupClause is equivalent to SortClause */ retval = _equalSortClause(a, b); break; + case T_RowMarkClause: + retval = _equalRowMarkClause(a, b); + break; case T_FkConstraint: retval = _equalFkConstraint(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index ece4bd16e0..d0fa62fcbb 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1418,7 +1418,7 @@ _outLockingClause(StringInfo str, LockingClause *node) WRITE_NODE_FIELD(lockedRels); WRITE_BOOL_FIELD(forUpdate); - WRITE_BOOL_FIELD(nowait); + WRITE_BOOL_FIELD(noWait); } static void @@ -1514,9 +1514,6 @@ _outQuery(StringInfo str, Query *node) WRITE_BOOL_FIELD(hasSubLinks); WRITE_NODE_FIELD(rtable); WRITE_NODE_FIELD(jointree); - WRITE_NODE_FIELD(rowMarks); - WRITE_BOOL_FIELD(forUpdate); - WRITE_BOOL_FIELD(rowNoWait); WRITE_NODE_FIELD(targetList); WRITE_NODE_FIELD(groupClause); WRITE_NODE_FIELD(havingQual); @@ -1524,6 +1521,7 @@ _outQuery(StringInfo str, Query *node) WRITE_NODE_FIELD(sortClause); WRITE_NODE_FIELD(limitOffset); WRITE_NODE_FIELD(limitCount); + WRITE_NODE_FIELD(rowMarks); WRITE_NODE_FIELD(setOperations); WRITE_NODE_FIELD(resultRelations); } @@ -1547,6 +1545,16 @@ _outGroupClause(StringInfo str, GroupClause *node) } static void +_outRowMarkClause(StringInfo str, RowMarkClause *node) +{ + WRITE_NODE_TYPE("ROWMARKCLAUSE"); + + WRITE_UINT_FIELD(rti); + WRITE_BOOL_FIELD(forUpdate); + WRITE_BOOL_FIELD(noWait); +} + +static void _outSetOperationStmt(StringInfo str, SetOperationStmt *node) { WRITE_NODE_TYPE("SETOPERATIONSTMT"); @@ -2113,6 +2121,9 @@ _outNode(StringInfo str, void *obj) case T_GroupClause: _outGroupClause(str, obj); break; + case T_RowMarkClause: + _outRowMarkClause(str, obj); + break; case T_SetOperationStmt: _outSetOperationStmt(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index f882f5f610..33e6692c7e 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -147,9 +147,6 @@ _readQuery(void) READ_BOOL_FIELD(hasSubLinks); READ_NODE_FIELD(rtable); READ_NODE_FIELD(jointree); - READ_NODE_FIELD(rowMarks); - READ_BOOL_FIELD(forUpdate); - READ_BOOL_FIELD(rowNoWait); READ_NODE_FIELD(targetList); READ_NODE_FIELD(groupClause); READ_NODE_FIELD(havingQual); @@ -157,6 +154,7 @@ _readQuery(void) READ_NODE_FIELD(sortClause); READ_NODE_FIELD(limitOffset); READ_NODE_FIELD(limitCount); + READ_NODE_FIELD(rowMarks); READ_NODE_FIELD(setOperations); READ_NODE_FIELD(resultRelations); @@ -220,6 +218,21 @@ _readGroupClause(void) } /* + * _readRowMarkClause + */ +static RowMarkClause * +_readRowMarkClause(void) +{ + READ_LOCALS(RowMarkClause); + + READ_UINT_FIELD(rti); + READ_BOOL_FIELD(forUpdate); + READ_BOOL_FIELD(noWait); + + READ_DONE(); +} + +/* * _readSetOperationStmt */ static SetOperationStmt * @@ -934,6 +947,8 @@ parseNodeString(void) return_value = _readSortClause(); else if (MATCH("GROUPCLAUSE", 11)) return_value = _readGroupClause(); + else if (MATCH("ROWMARKCLAUSE", 13)) + return_value = _readRowMarkClause(); else if (MATCH("SETOPERATIONSTMT", 16)) return_value = _readSetOperationStmt(); else if (MATCH("ALIAS", 5)) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index dc12ea99a2..d5dd9e973e 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -268,7 +268,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, * currently supposes that every rowMark relation is involved in every * row returned by the query.) */ - if (list_member_int(root->parse->rowMarks, parentRTindex)) + if (get_rowmark(root->parse, parentRTindex)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries"))); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 632917f430..ae96c1fdb9 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -843,7 +843,7 @@ create_indexscan_plan(PlannerInfo *root, if (best_path->indexinfo->indpred) { if (baserelid != root->parse->resultRelation && - !list_member_int(root->parse->rowMarks, baserelid)) + get_rowmark(root->parse, baserelid) == NULL) if (predicate_implied_by(clausel, best_path->indexinfo->indpred)) continue; @@ -962,7 +962,7 @@ create_bitmap_scan_plan(PlannerInfo *root, if (ipath->indexinfo->indpred) { if (baserelid != root->parse->resultRelation && - !list_member_int(root->parse->rowMarks, baserelid)) + get_rowmark(root->parse, baserelid) == NULL) if (predicate_implied_by(clausel, ipath->indexinfo->indpred)) continue; diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index c74e84a151..98791b6cf0 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -409,29 +409,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes - * already adjusted the marker values, so just list_concat the - * list.) - * - * Executor can't handle multiple FOR UPDATE/SHARE/NOWAIT flags, - * so complain if they are valid but different + * already adjusted the marker rtindexes, so just concat the lists.) */ - if (parse->rowMarks && subquery->rowMarks) - { - if (parse->forUpdate != subquery->forUpdate) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot use both FOR UPDATE and FOR SHARE in one query"))); - if (parse->rowNoWait != subquery->rowNoWait) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot use both wait and NOWAIT in one query"))); - } parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks); - if (subquery->rowMarks) - { - parse->forUpdate = subquery->forUpdate; - parse->rowNoWait = subquery->rowNoWait; - } /* * We also have to fix the relid sets of any parent InClauseInfo diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 9ca52de3ba..9cf8d02cf5 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -116,7 +116,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) * invalid. This is also checked at parse time, but that's * insufficient because of rule substitution, query pullup, etc. */ - CheckSelectLocking(parse, parse->forUpdate); + CheckSelectLocking(parse); /* * Currently the executor only supports FOR UPDATE/SHARE at top level @@ -128,19 +128,19 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) foreach(l, parse->rowMarks) { - Index rti = lfirst_int(l); + RowMarkClause *rc = (RowMarkClause *) lfirst(l); Var *var; char *resname; TargetEntry *tle; - var = makeVar(rti, + var = makeVar(rc->rti, SelfItemPointerAttributeNumber, TIDOID, -1, 0); resname = (char *) palloc(32); - snprintf(resname, 32, "ctid%u", rti); + snprintf(resname, 32, "ctid%u", rc->rti); tle = makeTargetEntry((Expr *) var, list_length(tlist) + 1, diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 10d931b85f..d3e15e6e26 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -816,7 +816,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) */ if (rti == parse->resultRelation) lockmode = RowExclusiveLock; - else if (list_member_int(parse->rowMarks, rti)) + else if (get_rowmark(parse, rti)) lockmode = RowShareLock; else lockmode = AccessShareLock; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 3a7ff24742..405822f3fe 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1805,6 +1805,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) { Query *qry = makeNode(Query); Node *qual; + ListCell *l; qry->commandType = CMD_SELECT; @@ -1870,8 +1871,10 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); - if (stmt->lockingClause) - transformLockingClause(qry, stmt->lockingClause); + foreach(l, stmt->lockingClause) + { + transformLockingClause(qry, (LockingClause *) lfirst(l)); + } return qry; } @@ -1899,10 +1902,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) List *sortClause; Node *limitOffset; Node *limitCount; - LockingClause *lockingClause; + List *lockingClause; Node *node; ListCell *left_tlist, - *dtlist; + *dtlist, + *l; List *targetvars, *targetnames, *sv_relnamespace, @@ -1942,7 +1946,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) stmt->sortClause = NIL; stmt->limitOffset = NULL; stmt->limitCount = NULL; - stmt->lockingClause = NULL; + stmt->lockingClause = NIL; /* We don't support FOR UPDATE/SHARE with set ops at the moment. */ if (lockingClause) @@ -2084,8 +2088,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); - if (lockingClause) - transformLockingClause(qry, lockingClause); + foreach(l, lockingClause) + { + transformLockingClause(qry, (LockingClause *) lfirst(l)); + } return qry; } @@ -2743,44 +2749,34 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt) /* exported so planner can check again after rewriting, query pullup, etc */ void -CheckSelectLocking(Query *qry, bool forUpdate) +CheckSelectLocking(Query *qry) { - const char *operation; - - if (forUpdate) - operation = "SELECT FOR UPDATE"; - else - operation = "SELECT FOR SHARE"; - if (qry->setOperations) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - /* translator: %s is a SQL command, like SELECT FOR UPDATE */ - errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", operation))); + errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); if (qry->distinctClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - /* translator: %s is a SQL command, like SELECT FOR UPDATE */ - errmsg("%s is not allowed with DISTINCT clause", operation))); + errmsg("SELECT FOR UPDATE/SHARE is not allowed with DISTINCT clause"))); if (qry->groupClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - /* translator: %s is a SQL command, like SELECT FOR UPDATE */ - errmsg("%s is not allowed with GROUP BY clause", operation))); + errmsg("SELECT FOR UPDATE/SHARE is not allowed with GROUP BY clause"))); if (qry->havingQual != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - /* translator: %s is a SQL command, like SELECT FOR UPDATE */ - errmsg("%s is not allowed with HAVING clause", operation))); + errmsg("SELECT FOR UPDATE/SHARE is not allowed with HAVING clause"))); if (qry->hasAggs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - /* translator: %s is a SQL command, like SELECT FOR UPDATE */ - errmsg("%s is not allowed with aggregate functions", operation))); + errmsg("SELECT FOR UPDATE/SHARE is not allowed with aggregate functions"))); } /* - * Convert FOR UPDATE/SHARE name list into rowMarks list of integer relids + * Transform a FOR UPDATE/SHARE clause + * + * This basically involves replacing names by integer relids. * * NB: if you need to change this, see also markQueryForLocking() * in rewriteHandler.c. @@ -2789,35 +2785,18 @@ static void transformLockingClause(Query *qry, LockingClause *lc) { List *lockedRels = lc->lockedRels; - List *rowMarks; ListCell *l; ListCell *rt; Index i; LockingClause *allrels; - if (qry->rowMarks) - { - if (lc->forUpdate != qry->forUpdate) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot use both FOR UPDATE and FOR SHARE in one query"))); - if (lc->nowait != qry->rowNoWait) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot use both wait and NOWAIT in one query"))); - } - qry->forUpdate = lc->forUpdate; - qry->rowNoWait = lc->nowait; - - CheckSelectLocking(qry, lc->forUpdate); + CheckSelectLocking(qry); /* make a clause we can pass down to subqueries to select all rels */ allrels = makeNode(LockingClause); allrels->lockedRels = NIL; /* indicates all rels */ allrels->forUpdate = lc->forUpdate; - allrels->nowait = lc->nowait; - - rowMarks = qry->rowMarks; + allrels->noWait = lc->noWait; if (lockedRels == NIL) { @@ -2831,8 +2810,7 @@ transformLockingClause(Query *qry, LockingClause *lc) switch (rte->rtekind) { case RTE_RELATION: - /* use list_append_unique to avoid duplicates */ - rowMarks = list_append_unique_int(rowMarks, i); + applyLockingClause(qry, i, lc->forUpdate, lc->noWait); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; break; case RTE_SUBQUERY: @@ -2867,8 +2845,8 @@ transformLockingClause(Query *qry, LockingClause *lc) switch (rte->rtekind) { case RTE_RELATION: - /* use list_append_unique to avoid duplicates */ - rowMarks = list_append_unique_int(rowMarks, i); + applyLockingClause(qry, i, + lc->forUpdate, lc->noWait); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; break; case RTE_SUBQUERY: @@ -2909,8 +2887,41 @@ transformLockingClause(Query *qry, LockingClause *lc) relname))); } } +} + +/* + * Record locking info for a single rangetable item + */ +void +applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait) +{ + RowMarkClause *rc; + + /* Check for pre-existing entry for same rtindex */ + if ((rc = get_rowmark(qry, rtindex)) != NULL) + { + /* + * If the same RTE is specified both FOR UPDATE and FOR SHARE, + * treat it as FOR UPDATE. (Reasonable, since you can't take + * both a shared and exclusive lock at the same time; it'll + * end up being exclusive anyway.) + * + * We also consider that NOWAIT wins if it's specified both ways. + * This is a bit more debatable but raising an error doesn't + * seem helpful. (Consider for instance SELECT FOR UPDATE NOWAIT + * from a view that internally contains a plain FOR UPDATE spec.) + */ + rc->forUpdate |= forUpdate; + rc->noWait |= noWait; + return; + } - qry->rowMarks = rowMarks; + /* Make a new RowMarkClause */ + rc = makeNode(RowMarkClause); + rc->rti = rtindex; + rc->forUpdate = forUpdate; + rc->noWait = noWait; + qry->rowMarks = lappend(qry->rowMarks, rc); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 711f7fb0b2..04cc159ef7 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -96,7 +96,7 @@ static List *check_func_name(List *names); static List *extractArgTypes(List *parameters); static SelectStmt *findLeftmostSelect(SelectStmt *node); static void insertSelectOptions(SelectStmt *stmt, - List *sortClause, Node *lockingClause, + List *sortClause, List *lockingClause, Node *limitOffset, Node *limitCount); static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg); static Node *doNegate(Node *n, int location); @@ -253,7 +253,8 @@ static void doNegateFloat(Value *v); %type <oncommit> OnCommitOption %type <withoids> OptWithOids -%type <node> for_locking_clause opt_for_locking_clause +%type <node> for_locking_item +%type <list> for_locking_clause opt_for_locking_clause for_locking_items %type <list> locked_rels_list %type <boolean> opt_all @@ -5400,7 +5401,7 @@ select_no_parens: simple_select { $$ = $1; } | select_clause sort_clause { - insertSelectOptions((SelectStmt *) $1, $2, NULL, + insertSelectOptions((SelectStmt *) $1, $2, NIL, NULL, NULL); $$ = $1; } @@ -5644,12 +5645,27 @@ having_clause: ; for_locking_clause: + for_locking_items { $$ = $1; } + | FOR READ ONLY { $$ = NIL; } + ; + +opt_for_locking_clause: + for_locking_clause { $$ = $1; } + | /* EMPTY */ { $$ = NIL; } + ; + +for_locking_items: + for_locking_item { $$ = list_make1($1); } + | for_locking_items for_locking_item { $$ = lappend($1, $2); } + ; + +for_locking_item: FOR UPDATE locked_rels_list opt_nowait { LockingClause *n = makeNode(LockingClause); n->lockedRels = $3; n->forUpdate = TRUE; - n->nowait = $4; + n->noWait = $4; $$ = (Node *) n; } | FOR SHARE locked_rels_list opt_nowait @@ -5657,15 +5673,9 @@ for_locking_clause: LockingClause *n = makeNode(LockingClause); n->lockedRels = $3; n->forUpdate = FALSE; - n->nowait = $4; + n->noWait = $4; $$ = (Node *) n; } - | FOR READ ONLY { $$ = NULL; } - ; - -opt_for_locking_clause: - for_locking_clause { $$ = $1; } - | /* EMPTY */ { $$ = NULL; } ; locked_rels_list: @@ -8976,7 +8986,7 @@ findLeftmostSelect(SelectStmt *node) */ static void insertSelectOptions(SelectStmt *stmt, - List *sortClause, Node *lockingClause, + List *sortClause, List *lockingClause, Node *limitOffset, Node *limitCount) { /* @@ -8991,14 +9001,8 @@ insertSelectOptions(SelectStmt *stmt, errmsg("multiple ORDER BY clauses not allowed"))); stmt->sortClause = sortClause; } - if (lockingClause) - { - if (stmt->lockingClause) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("multiple FOR UPDATE/FOR SHARE clauses not allowed"))); - stmt->lockingClause = (LockingClause *) lockingClause; - } + /* We can handle multiple locking clauses, though */ + stmt->lockingClause = list_concat(stmt->lockingClause, lockingClause); if (limitOffset) { if (stmt->limitOffset) diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index e99fd019fd..79812be576 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1001,6 +1001,8 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Has the specified refname been selected FOR UPDATE/FOR SHARE? + * + * Note: we pay no attention to whether it's FOR UPDATE vs FOR SHARE. */ static bool isLockedRel(ParseState *pstate, char *refname) @@ -1008,9 +1010,13 @@ isLockedRel(ParseState *pstate, char *refname) /* Outer loop to check parent query levels as well as this one */ while (pstate != NULL) { - if (pstate->p_locking_clause) + ListCell *l; + + foreach(l, pstate->p_locking_clause) { - if (pstate->p_locking_clause->lockedRels == NIL) + LockingClause *lc = (LockingClause *) lfirst(l); + + if (lc->lockedRels == NIL) { /* all tables used in query */ return true; @@ -1018,11 +1024,11 @@ isLockedRel(ParseState *pstate, char *refname) else { /* just the named tables */ - ListCell *l; + ListCell *l2; - foreach(l, pstate->p_locking_clause->lockedRels) + foreach(l2, lc->lockedRels) { - char *rname = strVal(lfirst(l)); + char *rname = strVal(lfirst(l2)); if (strcmp(refname, rname) == 0) return true; @@ -1703,6 +1709,26 @@ get_tle_by_resno(List *tlist, AttrNumber resno) } /* + * Given a Query and rangetable index, return relation's RowMarkClause if any + * + * Returns NULL if relation is not selected FOR UPDATE/SHARE + */ +RowMarkClause * +get_rowmark(Query *qry, Index rtindex) +{ + ListCell *l; + + foreach(l, qry->rowMarks) + { + RowMarkClause *rc = (RowMarkClause *) lfirst(l); + + if (rc->rti == rtindex) + return rc; + } + return NULL; +} + +/* * given relation and att name, return attnum of variable * * Returns InvalidAttrNumber if the attr doesn't exist (or is dropped). diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 5892aafd60..224a59a697 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -432,7 +432,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod) stmt->sortClause != NIL || stmt->limitOffset != NULL || stmt->limitCount != NULL || - stmt->lockingClause != NULL || + stmt->lockingClause != NIL || stmt->op != SETOP_NONE) goto fail; if (list_length(stmt->targetList) != 1) diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index c17191808c..20f77681e4 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -130,7 +130,7 @@ AcquireRewriteLocks(Query *parsetree) */ if (rt_index == parsetree->resultRelation) lockmode = RowExclusiveLock; - else if (list_member_int(parsetree->rowMarks, rt_index)) + else if (get_rowmark(parsetree, rt_index)) lockmode = RowShareLock; else lockmode = AccessShareLock; @@ -907,6 +907,7 @@ ApplyRetrieveRule(Query *parsetree, Query *rule_action; RangeTblEntry *rte, *subrte; + RowMarkClause *rc; if (list_length(rule->actions) != 1) elog(ERROR, "expected just one rule action"); @@ -954,20 +955,20 @@ ApplyRetrieveRule(Query *parsetree, /* * FOR UPDATE/SHARE of view? */ - if (list_member_int(parsetree->rowMarks, rt_index)) + if ((rc = get_rowmark(parsetree, rt_index)) != NULL) { /* * Remove the view from the list of rels that will actually be marked - * FOR UPDATE/SHARE by the executor. It will still be access- checked + * FOR UPDATE/SHARE by the executor. It will still be access-checked * for write access, though. */ - parsetree->rowMarks = list_delete_int(parsetree->rowMarks, rt_index); + parsetree->rowMarks = list_delete_ptr(parsetree->rowMarks, rc); /* * Set up the view's referenced tables as if FOR UPDATE/SHARE. */ - markQueryForLocking(rule_action, parsetree->forUpdate, - parsetree->rowNoWait, true); + markQueryForLocking(rule_action, rc->forUpdate, + rc->noWait, true); } return parsetree; @@ -987,20 +988,6 @@ markQueryForLocking(Query *qry, bool forUpdate, bool noWait, bool skipOldNew) Index rti = 0; ListCell *l; - if (qry->rowMarks) - { - if (forUpdate != qry->forUpdate) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot use both FOR UPDATE and FOR SHARE in one query"))); - if (noWait != qry->rowNoWait) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot use both wait and NOWAIT in one query"))); - } - qry->forUpdate = forUpdate; - qry->rowNoWait = noWait; - foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); @@ -1014,7 +1001,7 @@ markQueryForLocking(Query *qry, bool forUpdate, bool noWait, bool skipOldNew) if (rte->rtekind == RTE_RELATION) { - qry->rowMarks = list_append_unique_int(qry->rowMarks, rti); + applyLockingClause(qry, rti, forUpdate, noWait); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index de7a142e20..3882c509d2 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -240,7 +240,11 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up) if (qry->resultRelation) qry->resultRelation += offset; foreach(l, qry->rowMarks) - lfirst_int(l) += offset; + { + RowMarkClause *rc = (RowMarkClause *) lfirst(l); + + rc->rti += offset; + } } query_tree_walker(qry, OffsetVarNodes_walker, (void *) &context, 0); @@ -395,8 +399,10 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up) qry->resultRelation = new_index; foreach(l, qry->rowMarks) { - if (lfirst_int(l) == rt_index) - lfirst_int(l) = new_index; + RowMarkClause *rc = (RowMarkClause *) lfirst(l); + + if (rc->rti == rt_index) + rc->rti = new_index; } } query_tree_walker(qry, ChangeVarNodes_walker, diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 6e14051718..742ac85f7b 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1796,7 +1796,7 @@ CreateQueryTag(Query *parsetree) tag = "SELECT INTO"; else if (parsetree->rowMarks != NIL) { - if (parsetree->forUpdate) + if (((RowMarkClause *) linitial(parsetree->rowMarks))->forUpdate) tag = "SELECT FOR UPDATE"; else tag = "SELECT FOR SHARE"; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index e6f3f4565e..5a7ccb3fd3 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -1858,27 +1858,21 @@ get_select_query_def(Query *query, deparse_context *context, get_rule_expr(query->limitCount, context, false); } - /* Add the FOR UPDATE/SHARE clause if present */ - if (query->rowMarks != NIL) + /* Add FOR UPDATE/SHARE clauses if present */ + foreach(l, query->rowMarks) { - if (query->forUpdate) - appendContextKeyword(context, " FOR UPDATE OF ", + RowMarkClause *rc = (RowMarkClause *) lfirst(l); + RangeTblEntry *rte = rt_fetch(rc->rti, query->rtable); + + if (rc->forUpdate) + appendContextKeyword(context, " FOR UPDATE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); else - appendContextKeyword(context, " FOR SHARE OF ", + appendContextKeyword(context, " FOR SHARE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - sep = ""; - foreach(l, query->rowMarks) - { - int rtindex = lfirst_int(l); - RangeTblEntry *rte = rt_fetch(rtindex, query->rtable); - - appendStringInfo(buf, "%s%s", - sep, - quote_identifier(rte->eref->aliasname)); - sep = ", "; - } - if (query->rowNoWait) + appendStringInfo(buf, " OF %s", + quote_identifier(rte->eref->aliasname)); + if (rc->noWait) appendStringInfo(buf, " NOWAIT"); } } diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index eeca11539a..564b573704 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200604291 +#define CATALOG_VERSION_NO 200604301 #endif diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index a11b671353..4e87e01dfd 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -321,8 +321,6 @@ typedef struct EState uint32 es_processed; /* # of tuples processed */ Oid es_lastoid; /* last oid processed (by INSERT) */ List *es_rowMarks; /* not good place, but there is no other */ - bool es_forUpdate; /* true = FOR UPDATE, false = FOR SHARE */ - bool es_rowNoWait; /* FOR UPDATE/SHARE NOWAIT option */ bool es_instrument; /* true requests runtime instrumentation */ bool es_select_into; /* true if doing SELECT INTO */ @@ -351,6 +349,8 @@ typedef struct ExecRowMark { Relation relation; /* opened and RowShareLock'd relation */ Index rti; /* its range table index */ + bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */ + bool noWait; /* NOWAIT option */ char resname[32]; /* name for its ctid junk attribute */ } ExecRowMark; diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 25c7d6f854..914774bacf 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -320,6 +320,7 @@ typedef enum NodeTag T_InhRelation, T_FunctionParameter, T_LockingClause, + T_RowMarkClause, /* * TAGS FOR RANDOM OTHER STUFF diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 657905a3c2..af3291fa0c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -57,7 +57,7 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */ #define ACL_USAGE (1<<8) /* for languages and namespaces */ #define ACL_CREATE (1<<9) /* for namespaces and databases */ #define ACL_CREATE_TEMP (1<<10) /* for databases */ -#define ACL_CONNECT (1<<11) /* for database connection privilege */ +#define ACL_CONNECT (1<<11) /* for databases */ #define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */ #define ACL_NO_RIGHTS 0 /* Currently, SELECT ... FOR UPDATE/FOR SHARE requires UPDATE privileges */ @@ -102,13 +102,6 @@ typedef struct Query List *rtable; /* list of range table entries */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ - List *rowMarks; /* integer list of RT indexes of relations - * that are selected FOR UPDATE/SHARE */ - - bool forUpdate; /* true if rowMarks are FOR UPDATE, false if - * they are FOR SHARE */ - bool rowNoWait; /* FOR UPDATE/SHARE NOWAIT option */ - List *targetList; /* target list (of TargetEntry) */ List *groupClause; /* a list of GroupClause's */ @@ -122,6 +115,8 @@ typedef struct Query Node *limitOffset; /* # of result tuples to skip */ Node *limitCount; /* # of result tuples to return */ + List *rowMarks; /* a list of RowMarkClause's */ + Node *setOperations; /* set-operation tree if this is top level of * a UNION/INTERSECT/EXCEPT query */ @@ -448,7 +443,7 @@ typedef struct LockingClause NodeTag type; List *lockedRels; /* FOR UPDATE or FOR SHARE relations */ bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */ - bool nowait; /* NOWAIT option */ + bool noWait; /* NOWAIT option */ } LockingClause; @@ -615,6 +610,19 @@ typedef struct SortClause */ typedef SortClause GroupClause; +/* + * RowMarkClause - + * representation of FOR UPDATE/SHARE clauses + * + * We create a separate RowMarkClause node for each target relation + */ +typedef struct RowMarkClause +{ + NodeTag type; + Index rti; /* range table index of target relation */ + bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */ + bool noWait; /* NOWAIT option */ +} RowMarkClause; /***************************************************************************** * Optimizable Statements @@ -724,7 +732,7 @@ typedef struct SelectStmt List *sortClause; /* sort clause (a list of SortBy's) */ Node *limitOffset; /* # of result tuples to skip */ Node *limitCount; /* # of result tuples to return */ - LockingClause *lockingClause; /* FOR UPDATE/FOR SHARE */ + List *lockingClause; /* FOR UPDATE (list of LockingClause's) */ /* * These fields are used only in upper-level SelectStmts. diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h index 0b1d28b6ba..ed3157ea70 100644 --- a/src/include/parser/analyze.h +++ b/src/include/parser/analyze.h @@ -22,6 +22,8 @@ extern List *parse_analyze_varparams(Node *parseTree, const char *sourceText, Oid **paramTypes, int *numParams); extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState); extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt); -extern void CheckSelectLocking(Query *qry, bool forUpdate); +extern void CheckSelectLocking(Query *qry); +extern void applyLockingClause(Query *qry, Index rtindex, + bool forUpdate, bool noWait); #endif /* ANALYZE_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index f5d6b4b1a4..44ca29f95b 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -71,8 +71,8 @@ typedef struct ParseState Oid *p_paramtypes; /* OIDs of types for $n parameter symbols */ int p_numparams; /* allocated size of p_paramtypes[] */ int p_next_resno; /* next targetlist resno to assign */ - LockingClause *p_locking_clause; /* FOR UPDATE/FOR SHARE info */ - Node *p_value_substitute; /* what to replace VALUE with, if any */ + List *p_locking_clause; /* raw FOR UPDATE/FOR SHARE info */ + Node *p_value_substitute; /* what to replace VALUE with, if any */ bool p_variableparams; bool p_hasAggs; bool p_hasSubLinks; diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h index 07c52c87a0..1dde4a4807 100644 --- a/src/include/parser/parsetree.h +++ b/src/include/parser/parsetree.h @@ -70,4 +70,11 @@ extern bool get_rte_attribute_is_dropped(RangeTblEntry *rte, extern TargetEntry *get_tle_by_resno(List *tlist, AttrNumber resno); +/* ---------------- + * FOR UPDATE/SHARE info + * ---------------- + */ + +extern RowMarkClause *get_rowmark(Query *qry, Index rtindex); + #endif /* PARSETREE_H */ |