diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index f862d5f873..b15aa361a0 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -1,5 +1,5 @@ @@ -30,7 +30,7 @@ SELECT [ ALL | DISTINCT [ ON ( expressionexpression [ ASC | DESC | USING operator ] [, ...] ] [ LIMIT { count | ALL } ] [ OFFSET start ] - [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] ] + [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ] where from_item can be one of: @@ -142,8 +142,8 @@ where from_item can be one of: - If the FOR UPDATE or FOR SHARE - clause is specified, the + If FOR UPDATE or FOR SHARE + is specified, the SELECT statement locks the selected rows against concurrent updates. (See below.) @@ -852,19 +852,27 @@ FOR SHARE [ OF table_name [, ...] ] from performing SELECT FOR SHARE. - - It is currently not allowed for a single SELECT - statement to include both FOR UPDATE and - FOR SHARE, nor can different parts of the statement use - both NOWAIT and normal waiting mode. - - If specific tables are named in FOR UPDATE or FOR SHARE, then only rows coming from those tables are locked; any other tables used in the SELECT are simply read as - usual. + usual. A FOR UPDATE or FOR SHARE + clause without a table list affects all tables used in the command. + If FOR UPDATE or FOR SHARE is + applied to a view or sub-query, it affects all tables used in + the view or sub-query. + + + + Multiple FOR UPDATE and FOR SHARE + 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 FOR UPDATE and + FOR SHARE clauses, then it is processed as + FOR UPDATE. Similarly, a table is processed + as NOWAIT if that is specified in any of the clauses + affecting it. diff --git a/doc/src/sgml/ref/select_into.sgml b/doc/src/sgml/ref/select_into.sgml index d9967197b8..283fd4f8b2 100644 --- a/doc/src/sgml/ref/select_into.sgml +++ b/doc/src/sgml/ref/select_into.sgml @@ -1,5 +1,5 @@ @@ -31,7 +31,7 @@ SELECT [ ALL | DISTINCT [ ON ( expressionexpression [ ASC | DESC | USING operator ] [, ...] ] [ LIMIT { count | ALL } ] [ OFFSET start ] - [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] ] + [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ] diff --git a/doc/src/sgml/sql.sgml b/doc/src/sgml/sql.sgml index 86539ded11..b27400b4a4 100644 --- a/doc/src/sgml/sql.sgml +++ b/doc/src/sgml/sql.sgml @@ -1,4 +1,4 @@ - + SQL @@ -863,7 +863,7 @@ SELECT [ ALL | DISTINCT [ ON ( expressionexpression [ ASC | DESC | USING operator ] [, ...] ] [ LIMIT { count | ALL } ] [ OFFSET start ] - [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] ] + [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ] diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 31951db4c2..e8e0c8bd56 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.269 2006/03/05 15:58:25 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.270 2006/04/30 18:30:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; + RowMarkClause *rc = (RowMarkClause *) lfirst(l); + Oid relid = getrelid(rc->rti, rangeTable); + Relation relation; + ExecRowMark *erm; - 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); - } + 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 3696b5a208..d1a294f9bb 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.133 2006/03/05 15:58:26 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.134 2006/04/30 18:30:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 b060d38b5e..6bf342ab92 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.334 2006/04/22 01:25:58 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.335 2006/04/30 18:30:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 94799d0c85..7f7eb06fbc 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.270 2006/04/22 01:25:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.271 2006/04/30 18:30:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; } @@ -1723,6 +1721,16 @@ _equalSortClause(SortClause *a, SortClause *b) return true; } +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) { @@ -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 314d68d2ef..25d3a11279 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.273 2006/04/22 01:25:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.274 2006/04/30 18:30:39 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -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); } @@ -1546,6 +1544,16 @@ _outGroupClause(StringInfo str, GroupClause *node) WRITE_OID_FIELD(sortop); } +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) { @@ -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 9fdf02fd56..dd6995a172 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.188 2006/04/22 01:25:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.189 2006/04/30 18:30:39 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -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); @@ -219,6 +217,21 @@ _readGroupClause(void) READ_DONE(); } +/* + * _readRowMarkClause + */ +static RowMarkClause * +_readRowMarkClause(void) +{ + READ_LOCALS(RowMarkClause); + + READ_UINT_FIELD(rti); + READ_BOOL_FIELD(forUpdate); + READ_BOOL_FIELD(noWait); + + READ_DONE(); +} + /* * _readSetOperationStmt */ @@ -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 9ce72837b9..f35e87962d 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.144 2006/03/05 15:58:28 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.145 2006/04/30 18:30:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 9750789f0c..174b20f622 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.209 2006/04/25 16:54:09 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.210 2006/04/30 18:30:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 ae538c9cc0..f3b6df46f2 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.37 2006/03/07 01:00:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.38 2006/04/30 18:30:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 5a37b86eb5..0e06d0e888 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.81 2006/04/05 22:11:55 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.82 2006/04/30 18:30:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 8c0457295c..3b9e740cad 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.131 2006/03/05 15:58:31 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.132 2006/04/30 18:30:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 d69c0f1786..566c9a0488 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.333 2006/04/22 01:25:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.334 2006/04/30 18:30:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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))); } } +} - qry->rowMarks = rowMarks; +/* + * 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; + } + + /* 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 8870261e44..69e7a20142 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.543 2006/04/27 00:33:45 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.544 2006/04/30 18:30:39 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -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 OnCommitOption %type OptWithOids -%type for_locking_clause opt_for_locking_clause +%type for_locking_item +%type for_locking_clause opt_for_locking_clause for_locking_items %type locked_rels_list %type 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 d3e138e8ec..10f71712ff 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.122 2006/03/23 00:19:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.123 2006/04/30 18:30:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; @@ -1702,6 +1708,26 @@ get_tle_by_resno(List *tlist, AttrNumber resno) return NULL; } +/* + * 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 * diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index fec4552c9c..5965bf5ae4 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.80 2006/04/04 19:35:35 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.81 2006/04/30 18:30:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 6d1ace66f1..d3052f8da2 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.162 2006/04/05 22:11:55 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.163 2006/04/30 18:30:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 175df7a695..906e7c4a04 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.98 2006/04/05 22:11:55 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.99 2006/04/30 18:30:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 b54d50702b..5729b58450 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.256 2006/04/15 17:45:41 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.257 2006/04/30 18:30:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 c0db64bf89..67897a2938 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2,7 +2,7 @@ * ruleutils.c - Functions to convert stored expressions/querytrees * back to source text * - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.220 2006/04/22 01:26:00 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.221 2006/04/30 18:30:40 tgl Exp $ **********************************************************************/ #include "postgres.h" @@ -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 f3c037f97c..4c76aa70fa 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.327 2006/04/30 02:09:07 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.328 2006/04/30 18:30:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 2d346df359..7a9a651e1e 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.149 2006/03/05 15:58:56 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.150 2006/04/30 18:30:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 fe5ab018f5..53f3ee1d61 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.185 2006/04/15 17:45:41 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.186 2006/04/30 18:30:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 a0a31be51b..896b426370 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.309 2006/04/30 02:09:07 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.310 2006/04/30 18:30:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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<