<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.88 2005/07/14 06:17:36 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.89 2005/08/01 20:31:04 tgl Exp $
PostgreSQL documentation
-->
[ 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> [, ...] ] ]
+ [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
where <replaceable class="parameter">from_item</replaceable> can be one of:
</listitem>
</orderedlist>
</para>
-
+
<para>
You must have <literal>SELECT</literal> privilege on a table to
read its values. The use of <literal>FOR UPDATE</literal> or
<replaceable class="parameter">select_statement</replaceable> is
any <command>SELECT</command> statement without an <literal>ORDER
BY</>, <literal>LIMIT</>, <literal>FOR UPDATE</literal>, or
- <literal>FOR SHARE</literal> clause.
+ <literal>FOR SHARE</literal> clause.
(<literal>ORDER BY</> and <literal>LIMIT</> can be attached to a
subexpression if it is enclosed in parentheses. Without
parentheses, these clauses will be taken to apply to the result of
<para>
The <literal>FOR UPDATE</literal> clause has this form:
<synopsis>
-FOR UPDATE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
+FOR UPDATE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ]
</synopsis>
</para>
<para>
The closely related <literal>FOR SHARE</literal> clause has this form:
<synopsis>
-FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ]
+FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ]
</synopsis>
</para>
linkend="mvcc">.
</para>
+ <para>
+ To prevent the operation from waiting for other transactions to commit,
+ use the <literal>NOWAIT</> option. <command>SELECT FOR UPDATE
+ NOWAIT</command> reports an error, rather than waiting, if a selected row
+ cannot be locked immediately. Note that <literal>NOWAIT</> applies only
+ to the row-level lock(s) — the required <literal>ROW SHARE</literal>
+ table-level lock is still taken in the ordinary way (see
+ <xref linkend="mvcc">). You can use the <literal>NOWAIT</> option of
+ <xref linkend="sql-lock" endterm="sql-lock-title">
+ if you need to acquire the table-level lock without waiting.
+ </para>
+
<para>
<literal>FOR SHARE</literal> behaves similarly, except that it
acquires a shared rather than exclusive lock on each retrieved
<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>.
+ <literal>FOR SHARE</literal>, nor can different parts of the statement use
+ both <literal>NOWAIT</> and normal waiting mode.
</para>
<para>
</para>
<para>
- It is possible for a <command>SELECT</> command using both
- <literal>LIMIT</literal> and <literal>FOR UPDATE/SHARE</literal>
+ It is possible for a <command>SELECT</> command using both
+ <literal>LIMIT</literal> and <literal>FOR UPDATE/SHARE</literal>
clauses to return fewer rows than specified by <literal>LIMIT</literal>.
This is because <literal>LIMIT</> is applied first. The command
selects the specified number of rows,
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.35 2005/04/28 21:47:10 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.36 2005/08/01 20:31:04 tgl Exp $
PostgreSQL documentation
-->
[ 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">tablename</replaceable> [, ...] ] ]
+ [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
</synopsis>
</refsynopsisdiv>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.37 2005/07/14 06:17:35 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.38 2005/08/01 20:31:05 tgl Exp $
-->
<chapter id="sql-intro">
[ 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">class_name</replaceable> [, ...] ] ]
+ [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] ]
</synopsis>
</para>
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.195 2005/06/20 18:37:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.196 2005/08/01 20:31:05 tgl Exp $
*
*
* INTERFACE ROUTINES
*/
HTSU_Result
heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,
- CommandId cid, LockTupleMode mode)
+ CommandId cid, LockTupleMode mode, bool nowait)
{
HTSU_Result result;
ItemPointer tid = &(tuple->t_self);
*/
if (!have_tuple_lock)
{
- LockTuple(relation, tid, tuple_lock_type);
+ if (nowait)
+ {
+ if (!ConditionalLockTuple(relation, tid, tuple_lock_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ }
+ else
+ LockTuple(relation, tid, tuple_lock_type);
have_tuple_lock = true;
}
else if (infomask & HEAP_XMAX_IS_MULTI)
{
/* wait for multixact to end */
- MultiXactIdWait((MultiXactId) xwait);
+ if (nowait)
+ {
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ }
+ else
+ MultiXactIdWait((MultiXactId) xwait);
+
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
/*
else
{
/* wait for regular transaction to end */
- XactLockTableWait(xwait);
+ if (nowait)
+ {
+ if (!ConditionalXactLockTableWait(xwait))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ }
+ else
+ XactLockTableWait(xwait);
+
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
/*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.5 2005/06/08 15:50:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.6 2005/08/01 20:31:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
}
+/*
+ * ConditionalMultiXactIdWait
+ * As above, but only lock if we can get the lock without blocking.
+ */
+bool
+ConditionalMultiXactIdWait(MultiXactId multi)
+{
+ bool result = true;
+ TransactionId *members;
+ int nmembers;
+
+ nmembers = GetMultiXactIdMembers(multi, &members);
+
+ if (nmembers >= 0)
+ {
+ int i;
+
+ for (i = 0; i < nmembers; i++)
+ {
+ TransactionId member = members[i];
+
+ debug_elog4(DEBUG2, "ConditionalMultiXactIdWait: trying %d (%u)",
+ i, member);
+ if (!TransactionIdIsCurrentTransactionId(member))
+ {
+ result = ConditionalXactLockTableWait(member);
+ if (!result)
+ break;
+ }
+ }
+
+ pfree(members);
+ }
+
+ return result;
+}
+
/*
* CreateMultiXactId
* Make a new MultiXactId
*/
multi = mXactCacheGetBySet(nxids, xids);
if (MultiXactIdIsValid(multi))
- {
+ {
debug_elog2(DEBUG2, "Create: in cache!");
return multi;
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.189 2005/05/30 07:20:58 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.190 2005/08/01 20:31:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*newSlot = NULL;
tuple.t_self = *tid;
ltrmark:;
- test = heap_lock_tuple(relation, &tuple, &buffer, cid, LockTupleExclusive);
+ test = heap_lock_tuple(relation, &tuple, &buffer, cid,
+ LockTupleExclusive, false);
switch (test)
{
case HeapTupleSelfUpdated:
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.251 2005/06/28 05:08:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.252 2005/08/01 20:31:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* Have to lock relations selected FOR UPDATE/FOR SHARE
*/
- estate->es_rowMark = NIL;
+ estate->es_rowMarks = NIL;
estate->es_forUpdate = parseTree->forUpdate;
+ estate->es_rowNoWait = parseTree->rowNoWait;
if (parseTree->rowMarks != NIL)
{
ListCell *l;
erm->relation = relation;
erm->rti = rti;
snprintf(erm->resname, sizeof(erm->resname), "ctid%u", rti);
- estate->es_rowMark = lappend(estate->es_rowMark, erm);
+ estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
}
}
}
heap_close(estate->es_into_relation_descriptor, NoLock);
- }
+ }
/*
* close any relations selected FOR UPDATE/FOR SHARE, again keeping locks
*/
- foreach(l, estate->es_rowMark)
+ foreach(l, estate->es_rowMarks)
{
execRowMark *erm = lfirst(l);
/*
* Process any FOR UPDATE or FOR SHARE locking requested.
*/
- else if (estate->es_rowMark != NIL)
+ else if (estate->es_rowMarks != NIL)
{
ListCell *l;
lmark: ;
- foreach(l, estate->es_rowMark)
+ foreach(l, estate->es_rowMarks)
{
execRowMark *erm = lfirst(l);
Buffer buffer;
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
test = heap_lock_tuple(erm->relation, &tuple, &buffer,
estate->es_snapshot->curcid,
- lockmode);
+ lockmode, estate->es_rowNoWait);
ReleaseBuffer(buffer);
switch (test)
{
ListCell *l;
relation = NULL;
- foreach(l, estate->es_rowMark)
+ foreach(l, estate->es_rowMarks)
{
if (((execRowMark *) lfirst(l))->rti == rti)
{
if (estate->es_topPlan->nParamExec > 0)
epqstate->es_param_exec_vals = (ParamExecData *)
palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData));
- epqstate->es_rowMark = estate->es_rowMark;
+ 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;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.124 2005/06/20 18:37:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.125 2005/08/01 20:31:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
estate->es_processed = 0;
estate->es_lastoid = InvalidOid;
- estate->es_rowMark = NIL;
+ estate->es_rowMarks = NIL;
estate->es_forUpdate = false;
+ estate->es_rowNoWait = false;
estate->es_instrument = false;
estate->es_select_into = false;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.314 2005/08/01 04:03:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.315 2005/08/01 20:31:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return newnode;
}
+static LockingClause *
+_copyLockingClause(LockingClause *from)
+{
+ LockingClause *newnode = makeNode(LockingClause);
+
+ COPY_NODE_FIELD(lockedRels);
+ COPY_SCALAR_FIELD(forUpdate);
+ COPY_SCALAR_FIELD(nowait);
+
+ return newnode;
+}
+
static Query *
_copyQuery(Query *from)
{
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);
COPY_NODE_FIELD(sortClause);
COPY_NODE_FIELD(limitOffset);
COPY_NODE_FIELD(limitCount);
- COPY_NODE_FIELD(lockedRels);
- COPY_SCALAR_FIELD(forUpdate);
+ COPY_NODE_FIELD(lockingClause);
COPY_SCALAR_FIELD(op);
COPY_SCALAR_FIELD(all);
COPY_NODE_FIELD(larg);
case T_DefElem:
retval = _copyDefElem(from);
break;
+ case T_LockingClause:
+ retval = _copyLockingClause(from);
+ break;
case T_RangeTblEntry:
retval = _copyRangeTblEntry(from);
break;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.251 2005/08/01 04:03:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.252 2005/08/01 20:31:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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);
COMPARE_NODE_FIELD(sortClause);
COMPARE_NODE_FIELD(limitOffset);
COMPARE_NODE_FIELD(limitCount);
- COMPARE_NODE_FIELD(lockedRels);
- COMPARE_SCALAR_FIELD(forUpdate);
+ COMPARE_NODE_FIELD(lockingClause);
COMPARE_SCALAR_FIELD(op);
COMPARE_SCALAR_FIELD(all);
COMPARE_NODE_FIELD(larg);
return true;
}
+static bool
+_equalLockingClause(LockingClause *a, LockingClause *b)
+{
+ COMPARE_NODE_FIELD(lockedRels);
+ COMPARE_SCALAR_FIELD(forUpdate);
+ COMPARE_SCALAR_FIELD(nowait);
+
+ return true;
+}
+
static bool
_equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
{
case T_DefElem:
retval = _equalDefElem(a, b);
break;
+ case T_LockingClause:
+ retval = _equalLockingClause(a, b);
+ break;
case T_RangeTblEntry:
retval = _equalRangeTblEntry(a, b);
break;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.258 2005/07/02 23:00:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.259 2005/08/01 20:31:08 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
WRITE_NODE_FIELD(sortClause);
WRITE_NODE_FIELD(limitOffset);
WRITE_NODE_FIELD(limitCount);
- WRITE_NODE_FIELD(lockedRels);
- WRITE_BOOL_FIELD(forUpdate);
+ WRITE_NODE_FIELD(lockingClause);
WRITE_ENUM_FIELD(op, SetOperation);
WRITE_BOOL_FIELD(all);
WRITE_NODE_FIELD(larg);
WRITE_NODE_FIELD(arg);
}
+static void
+_outLockingClause(StringInfo str, LockingClause *node)
+{
+ WRITE_NODE_TYPE("LOCKINGCLAUSE");
+
+ WRITE_NODE_FIELD(lockedRels);
+ WRITE_BOOL_FIELD(forUpdate);
+ WRITE_BOOL_FIELD(nowait);
+}
+
static void
_outColumnDef(StringInfo str, ColumnDef *node)
{
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);
case T_DefElem:
_outDefElem(str, obj);
break;
+ case T_LockingClause:
+ _outLockingClause(str, obj);
+ break;
default:
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.180 2005/06/28 05:08:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.181 2005/08/01 20:31:08 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
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);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.29 2005/06/05 22:32:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.30 2005/08/01 20:31:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* already adjusted the marker values, so just list_concat the
* list.)
*
- * Executor can't handle multiple FOR UPDATE/SHARE flags, so
- * complain if they are valid but different
+ * Executor can't handle multiple FOR UPDATE/SHARE/NOWAIT flags,
+ * so complain if they are valid but different
*/
- if (parse->rowMarks && subquery->rowMarks &&
- 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->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
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.323 2005/07/28 22:27:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.324 2005/08/01 20:31:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool isAddConstraint);
static void applyColumnNames(List *dst, List *src);
static List *getSetColTypes(ParseState *pstate, Node *node);
-static void transformLocking(Query *qry, List *lockedRels, bool forUpdate);
+static void transformLockingClause(Query *qry, LockingClause *lc);
static void transformConstraintAttrs(List *constraintList);
static void transformColumnType(ParseState *pstate, ColumnDef *column);
static void release_pstate_resources(ParseState *pstate);
qry->commandType = CMD_SELECT;
- /* make FOR UPDATE/FOR SHARE list available to addRangeTableEntry */
- pstate->p_lockedRels = stmt->lockedRels;
+ /* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */
+ pstate->p_locking_clause = stmt->lockingClause;
/* process the FROM clause */
transformFromClause(pstate, stmt->fromClause);
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
- if (stmt->lockedRels != NIL)
- transformLocking(qry, stmt->lockedRels, stmt->forUpdate);
+ if (stmt->lockingClause)
+ transformLockingClause(qry, stmt->lockingClause);
return qry;
}
List *sortClause;
Node *limitOffset;
Node *limitCount;
- List *lockedRels;
- bool forUpdate;
+ LockingClause *lockingClause;
Node *node;
ListCell *left_tlist,
*dtlist;
sortClause = stmt->sortClause;
limitOffset = stmt->limitOffset;
limitCount = stmt->limitCount;
- lockedRels = stmt->lockedRels;
- forUpdate = stmt->forUpdate;
+ lockingClause = stmt->lockingClause;
stmt->sortClause = NIL;
stmt->limitOffset = NULL;
stmt->limitCount = NULL;
- stmt->lockedRels = NIL;
+ stmt->lockingClause = NULL;
/* We don't support FOR UPDATE/SHARE with set ops at the moment. */
- if (lockedRels)
+ if (lockingClause)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
- if (lockedRels != NIL)
- transformLocking(qry, lockedRels, forUpdate);
+ if (lockingClause)
+ transformLockingClause(qry, lockingClause);
return qry;
}
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT")));
/* We don't support FOR UPDATE/SHARE with set ops at the moment. */
- if (stmt->lockedRels)
+ if (stmt->lockingClause)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
{
Assert(stmt->larg != NULL && stmt->rarg != NULL);
if (stmt->sortClause || stmt->limitOffset || stmt->limitCount ||
- stmt->lockedRels)
+ stmt->lockingClause)
isLeaf = true;
else
isLeaf = false;
* in rewriteHandler.c.
*/
static void
-transformLocking(Query *qry, List *lockedRels, bool forUpdate)
+transformLockingClause(Query *qry, LockingClause *lc)
{
+ List *lockedRels = lc->lockedRels;
List *rowMarks;
ListCell *l;
ListCell *rt;
Index i;
+ LockingClause *allrels;
- if (qry->rowMarks && forUpdate != qry->forUpdate)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
- qry->forUpdate = forUpdate;
+ 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);
+
+ /* 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;
- CheckSelectLocking(qry, forUpdate);
-
rowMarks = qry->rowMarks;
- if (linitial(lockedRels) == NULL)
+ if (lockedRels == NIL)
{
/* all regular tables used in query */
i = 0;
* FOR UPDATE/SHARE of subquery is propagated to all
* of subquery's rels
*/
- transformLocking(rte->subquery, list_make1(NULL),
- forUpdate);
+ transformLockingClause(rte->subquery, allrels);
break;
default:
/* ignore JOIN, SPECIAL, FUNCTION RTEs */
* FOR UPDATE/SHARE of subquery is propagated to
* all of subquery's rels
*/
- transformLocking(rte->subquery, list_make1(NULL),
- forUpdate);
+ transformLockingClause(rte->subquery, allrels);
break;
case RTE_JOIN:
ereport(ERROR,
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.506 2005/08/01 04:03:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.507 2005/08/01 20:31:09 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
static List *extractArgTypes(List *parameters);
static SelectStmt *findLeftmostSelect(SelectStmt *node);
static void insertSelectOptions(SelectStmt *stmt,
- List *sortClause, List *lockingClause,
+ List *sortClause, Node *lockingClause,
Node *limitOffset, Node *limitCount);
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
static Node *doNegate(Node *n);
%type <node> stmt schema_stmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt
- AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
+ AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
%type <dbehavior> opt_drop_behavior
-%type <list> createdb_opt_list alterdb_opt_list copy_opt_list
+%type <list> createdb_opt_list alterdb_opt_list copy_opt_list
transaction_mode_list
%type <defelt> createdb_opt_item alterdb_opt_item copy_opt_item
transaction_mode_item
%type <oncommit> OnCommitOption
%type <withoids> OptWithOids WithOidsAs
-%type <list> for_locking_clause opt_for_locking_clause
- update_list
+%type <node> for_locking_clause opt_for_locking_clause
+%type <list> locked_rels_list
%type <boolean> opt_all
%type <node> join_outer join_qual
*****************************************************************************/
AlterDatabaseStmt:
- ALTER DATABASE database_name opt_with alterdb_opt_list
+ ALTER DATABASE database_name opt_with alterdb_opt_list
{
AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt);
n->dbname = $3;
| ACCESS EXCLUSIVE { $$ = AccessExclusiveLock; }
;
-opt_nowait: NOWAIT { $$ = TRUE; }
+opt_nowait: NOWAIT { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
simple_select { $$ = $1; }
| select_clause sort_clause
{
- insertSelectOptions((SelectStmt *) $1, $2, NIL,
+ insertSelectOptions((SelectStmt *) $1, $2, NULL,
NULL, NULL);
$$ = $1;
}
a_expr { $$ = $1; }
;
-/*
- * jimmy bell-style recursive queries aren't supported in the
- * current system.
- *
- * ...however, recursive addattr and rename supported. make special
- * cases for these.
- */
-
group_clause:
GROUP_P BY expr_list { $$ = $3; }
| /*EMPTY*/ { $$ = NIL; }
;
for_locking_clause:
- FOR UPDATE update_list { $$ = lcons(makeString("for_update"), $3); }
- | FOR SHARE update_list { $$ = lcons(makeString("for_share"), $3); }
+ FOR UPDATE locked_rels_list opt_nowait
+ {
+ LockingClause *n = makeNode(LockingClause);
+ n->lockedRels = $3;
+ n->forUpdate = TRUE;
+ n->nowait = $4;
+ $$ = (Node *) n;
+ }
+ | FOR SHARE locked_rels_list opt_nowait
+ {
+ LockingClause *n = makeNode(LockingClause);
+ n->lockedRels = $3;
+ n->forUpdate = FALSE;
+ n->nowait = $4;
+ $$ = (Node *) n;
+ }
| FOR READ ONLY { $$ = NULL; }
;
| /* EMPTY */ { $$ = NULL; }
;
-update_list:
+locked_rels_list:
OF name_list { $$ = $2; }
- | /* EMPTY */ { $$ = list_make1(NULL); }
+ | /* EMPTY */ { $$ = NIL; }
;
/*****************************************************************************
*/
static void
insertSelectOptions(SelectStmt *stmt,
- List *sortClause, List *lockingClause,
+ List *sortClause, Node *lockingClause,
Node *limitOffset, Node *limitCount)
{
/*
}
if (lockingClause)
{
- Value *type;
-
- if (stmt->lockedRels)
+ if (stmt->lockingClause)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple FOR UPDATE/FOR SHARE clauses not allowed")));
-
- Assert(list_length(lockingClause) > 1);
- /* 1st is Value node containing "for_update" or "for_share" */
- type = (Value *) linitial(lockingClause);
- Assert(IsA(type, String));
- if (strcmp(strVal(type), "for_update") == 0)
- stmt->forUpdate = true;
- else if (strcmp(strVal(type), "for_share") == 0)
- stmt->forUpdate = false;
- else
- elog(ERROR, "invalid first node in locking clause");
-
- stmt->lockedRels = list_delete_first(lockingClause);
+ stmt->lockingClause = (LockingClause *) lockingClause;
}
if (limitOffset)
{
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.112 2005/06/28 05:08:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.113 2005/08/01 20:31:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Outer loop to check parent query levels as well as this one */
while (pstate != NULL)
{
- if (pstate->p_lockedRels != NIL)
+ if (pstate->p_locking_clause)
{
- if (linitial(pstate->p_lockedRels) == NULL)
+ if (pstate->p_locking_clause->lockedRels == NIL)
{
/* all tables used in query */
return true;
/* just the named tables */
ListCell *l;
- foreach(l, pstate->p_lockedRels)
+ foreach(l, pstate->p_locking_clause->lockedRels)
{
char *rname = strVal(lfirst(l));
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.75 2005/05/29 18:24:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.76 2005/08/01 20:31:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
stmt->sortClause != NIL ||
stmt->limitOffset != NULL ||
stmt->limitCount != NULL ||
- stmt->lockedRels != NIL ||
+ stmt->lockingClause != NULL ||
stmt->op != SETOP_NONE)
goto fail;
if (list_length(stmt->targetList) != 1)
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.156 2005/07/28 22:27:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.157 2005/08/01 20:31:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
TargetEntry *prior_tle,
const char *attrName);
static Node *get_assignment_input(Node *node);
-static void markQueryForLocking(Query *qry, bool forUpdate, bool skipOldNew);
+static void markQueryForLocking(Query *qry, bool forUpdate, bool noWait,
+ bool skipOldNew);
static List *matchLocks(CmdType event, RuleLock *rulelocks,
int varno, Query *parsetree);
static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
/*
* Set up the view's referenced tables as if FOR UPDATE/SHARE.
*/
- markQueryForLocking(rule_action, parsetree->forUpdate, true);
+ markQueryForLocking(rule_action, parsetree->forUpdate,
+ parsetree->rowNoWait, true);
}
return parsetree;
* NB: this must agree with the parser's transformLocking() routine.
*/
static void
-markQueryForLocking(Query *qry, bool forUpdate, bool skipOldNew)
+markQueryForLocking(Query *qry, bool forUpdate, bool noWait, bool skipOldNew)
{
Index rti = 0;
ListCell *l;
- if (qry->rowMarks && forUpdate != qry->forUpdate)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use both FOR UPDATE and FOR SHARE in one query")));
+ 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)
{
else if (rte->rtekind == RTE_SUBQUERY)
{
/* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
- markQueryForLocking(rte->subquery, forUpdate, false);
+ markQueryForLocking(rte->subquery, forUpdate, noWait, false);
}
}
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.77 2005/06/17 22:32:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.78 2005/08/01 20:31:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
lockmode, false, false);
}
+/*
+ * ConditionalLockTuple
+ *
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE iff the lock was acquired.
+ */
+bool
+ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_TUPLE(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId,
+ ItemPointerGetBlockNumber(tid),
+ ItemPointerGetOffsetNumber(tid));
+
+ return (LockAcquire(LockTableId, &tag, relation->rd_istemp,
+ lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
+}
+
/*
* UnlockTuple
*/
TransactionIdAbort(xid);
}
+/*
+ * ConditionalXactLockTableWait
+ *
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE if the lock was acquired.
+ */
+bool
+ConditionalXactLockTableWait(TransactionId xid)
+{
+ LOCKTAG tag;
+
+ for (;;)
+ {
+ Assert(TransactionIdIsValid(xid));
+ Assert(!TransactionIdEquals(xid, GetTopTransactionId()));
+
+ SET_LOCKTAG_TRANSACTION(tag, xid);
+
+ if (LockAcquire(LockTableId, &tag, false,
+ ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
+ return false;
+
+ LockRelease(LockTableId, &tag, ShareLock, false);
+
+ if (!TransactionIdIsInProgress(xid))
+ break;
+ xid = SubTransGetParent(xid);
+ }
+
+ /*
+ * Transaction was committed/aborted/crashed - we have to update
+ * pg_clog if transaction is still marked as running.
+ */
+ if (!TransactionIdDidCommit(xid) && !TransactionIdDidAbort(xid))
+ TransactionIdAbort(xid);
+
+ return true;
+}
/*
* LockDatabaseObject
* back to source text
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.204 2005/07/15 18:39:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.205 2005/08/01 20:31:12 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
static void get_const_expr(Const *constval, deparse_context *context);
static void get_sublink_expr(SubLink *sublink, deparse_context *context);
static void get_from_clause(Query *query, const char *prefix,
- deparse_context *context);
+ deparse_context *context);
static void get_from_clause_item(Node *jtnode, Query *query,
deparse_context *context);
static void get_from_clause_alias(Alias *alias, int varno,
quote_identifier(rte->eref->aliasname));
sep = ", ";
}
+ if (query->rowNoWait)
+ appendStringInfo(buf, " NOWAIT");
}
}
only_marker(rte),
generate_relation_name(rte->relid));
- /* Add the USING clause if given */
- get_from_clause(query, " USING ", context);
+ /* Add the USING clause if given */
+ get_from_clause(query, " USING ", context);
/* Add a WHERE clause if given */
if (query->jointree->quals != NULL)
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.102 2005/06/20 18:37:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.103 2005/08/01 20:31:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple tup,
ItemPointer ctid, CommandId cid, Snapshot crosscheck, bool wait);
extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tup,
- Buffer *userbuf, CommandId cid, LockTupleMode mode);
+ Buffer *userbuf, CommandId cid,
+ LockTupleMode mode, bool nowait);
extern Oid simple_heap_insert(Relation relation, HeapTuple tup);
extern void simple_heap_delete(Relation relation, ItemPointer tid);
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/access/multixact.h,v 1.3 2005/06/08 15:50:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/multixact.h,v 1.4 2005/08/01 20:31:13 tgl Exp $
*/
#ifndef MULTIXACT_H
#define MULTIXACT_H
extern MultiXactId MultiXactIdExpand(MultiXactId multi, TransactionId xid);
extern bool MultiXactIdIsRunning(MultiXactId multi);
extern void MultiXactIdWait(MultiXactId multi);
+extern bool ConditionalMultiXactIdWait(MultiXactId multi);
extern void MultiXactIdSetOldestMember(void);
extern void AtEOXact_MultiXact(void);
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.294 2005/07/31 17:19:20 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.295 2005/08/01 20:31:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200507301
+#define CATALOG_VERSION_NO 200508011
#endif
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.136 2005/06/26 22:05:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.137 2005/08/01 20:31:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
uint32 es_processed; /* # of tuples processed */
Oid es_lastoid; /* last oid processed (by INSERT) */
- List *es_rowMark; /* not good place, but there is no other */
- bool es_forUpdate; /* was it FOR UPDATE or FOR SHARE */
+ 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 */
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.174 2005/08/01 04:03:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.175 2005/08/01 20:31:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
T_CompositeTypeStmt,
T_InhRelation,
T_FunctionParameter,
+ T_LockingClause,
/*
* TAGS FOR RANDOM OTHER STUFF
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.288 2005/08/01 04:03:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.289 2005/08/01 20:31:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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) */
Node *arg; /* a (Value *) or a (TypeName *) */
} DefElem;
+/*
+ * LockingClause - raw representation of FOR UPDATE/SHARE options
+ *
+ * Note: lockedRels == NIL means "all relations in query". Otherwise it
+ * is a list of String nodes giving relation eref names.
+ */
+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 */
+} LockingClause;
+
/****************************************************************************
* Nodes for a Query tree
List *sortClause; /* sort clause (a list of SortBy's) */
Node *limitOffset; /* # of result tuples to skip */
Node *limitCount; /* # of result tuples to return */
- List *lockedRels; /* FOR UPDATE or FOR SHARE relations */
- bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
+ LockingClause *lockingClause; /* FOR UPDATE/FOR SHARE */
/*
* These fields are used only in upper-level SelectStmts.
/* Eventually add fields for CORRESPONDING spec here */
} SelectStmt;
+
/* ----------------------
* Set Operation node for post-analysis query trees
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.44 2005/06/05 00:38:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.45 2005/08/01 20:31:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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 */
- List *p_lockedRels; /* FOR UPDATE/SHARE, if any (see gram.y) */
- Node *p_value_substitute; /* what to replace VALUE with, if
- * any */
+ LockingClause *p_locking_clause; /* 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;
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.50 2005/06/17 22:32:50 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.51 2005/08/01 20:31:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Lock a tuple (see heap_lock_tuple before assuming you understand this) */
extern void LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode);
+extern bool ConditionalLockTuple(Relation relation, ItemPointer tid,
+ LOCKMODE lockmode);
extern void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode);
/* Lock an XID (used to wait for a transaction to finish) */
extern void XactLockTableInsert(TransactionId xid);
extern void XactLockTableDelete(TransactionId xid);
extern void XactLockTableWait(TransactionId xid);
+extern bool ConditionalXactLockTableWait(TransactionId xid);
/* Lock a general object (other than a relation) of the current database */
extern void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,