Tom Lane escribió: > Alvaro Herrera <alvhe...@2ndquadrant.com> writes: > > Please see my reply to Robert. My proposal (in form of a patch) is > > while operating on tuple (0,2) in table "foo": updating tuple > > Would this work for you? > > It's pretty lousy from a readability standpoint, even in English; > I shudder to think what it might come out as after translation.
Well, the same thing actually. I didn't think it was too bad. > I think the enum idea you floated is probably worth doing. It's > certainly no more complex than passing a string, no? Okay, done that way, attached. I think this one solves all concerns there were. -- Álvaro Herrera http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services
*** a/src/backend/access/heap/heapam.c --- b/src/backend/access/heap/heapam.c *************** *** 105,115 **** static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask, uint16 *new_infomask2); static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask); ! static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, ! int *remaining, uint16 infomask); ! static bool ConditionalMultiXactIdWait(MultiXactId multi, ! MultiXactStatus status, int *remaining, ! uint16 infomask); static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup); static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified, bool *copy); --- 105,116 ---- uint16 *new_infomask2); static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask); ! static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, ! Relation rel, ItemPointer ctid, XLTW_Oper oper, ! int *remaining); ! static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, ! uint16 infomask, Relation rel, ItemPointer ctid, ! XLTW_Oper oper, int *remaining); static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup); static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified, bool *copy); *************** *** 2714,2721 **** l1: if (infomask & HEAP_XMAX_IS_MULTI) { /* wait for multixact */ ! MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate, ! NULL, infomask); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* --- 2715,2723 ---- if (infomask & HEAP_XMAX_IS_MULTI) { /* wait for multixact */ ! MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate, infomask, ! relation, &tp.t_data->t_ctid, XLTW_Delete, ! NULL); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* *************** *** 2741,2747 **** l1: else { /* wait for regular transaction to end */ ! XactLockTableWait(xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* --- 2743,2749 ---- else { /* wait for regular transaction to end */ ! XactLockTableWait(xwait, relation, &tp.t_data->t_ctid, XLTW_Delete); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* *************** *** 3266,3273 **** l2: int remain; /* wait for multixact */ ! MultiXactIdWait((MultiXactId) xwait, mxact_status, &remain, ! infomask); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* --- 3268,3276 ---- int remain; /* wait for multixact */ ! MultiXactIdWait((MultiXactId) xwait, mxact_status, infomask, ! relation, &oldtup.t_data->t_ctid, XLTW_Update, ! &remain); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* *************** *** 3341,3347 **** l2: else { /* wait for regular transaction to end */ ! XactLockTableWait(xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* --- 3344,3351 ---- else { /* wait for regular transaction to end */ ! XactLockTableWait(xwait, relation, &oldtup.t_data->t_ctid, ! XLTW_Update); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* *************** *** 4402,4415 **** l3: if (nowait) { if (!ConditionalMultiXactIdWait((MultiXactId) xwait, ! status, NULL, infomask)) ereport(ERROR, (errcode(ERRCODE_LOCK_NOT_AVAILABLE), errmsg("could not obtain lock on row in relation \"%s\"", RelationGetRelationName(relation)))); } else ! MultiXactIdWait((MultiXactId) xwait, status, NULL, infomask); /* if there are updates, follow the update chain */ if (follow_updates && --- 4406,4423 ---- if (nowait) { if (!ConditionalMultiXactIdWait((MultiXactId) xwait, ! status, infomask, relation, ! &tuple->t_data->t_ctid, ! XLTW_Lock, NULL)) ereport(ERROR, (errcode(ERRCODE_LOCK_NOT_AVAILABLE), errmsg("could not obtain lock on row in relation \"%s\"", RelationGetRelationName(relation)))); } else ! MultiXactIdWait((MultiXactId) xwait, status, infomask, ! relation, &tuple->t_data->t_ctid, ! XLTW_Lock, NULL); /* if there are updates, follow the update chain */ if (follow_updates && *************** *** 4464,4470 **** l3: RelationGetRelationName(relation)))); } else ! XactLockTableWait(xwait); /* if there are updates, follow the update chain */ if (follow_updates && --- 4472,4479 ---- RelationGetRelationName(relation)))); } else ! XactLockTableWait(xwait, relation, &tuple->t_data->t_ctid, ! XLTW_Lock); /* if there are updates, follow the update chain */ if (follow_updates && *************** *** 5151,5157 **** l4: if (needwait) { LockBuffer(buf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(members[i].xid); pfree(members); goto l4; } --- 5160,5168 ---- if (needwait) { LockBuffer(buf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(members[i].xid, rel, ! &mytup.t_data->t_ctid, ! XLTW_LockUpdated); pfree(members); goto l4; } *************** *** 5211,5217 **** l4: if (needwait) { LockBuffer(buf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(rawxmax); goto l4; } if (res != HeapTupleMayBeUpdated) --- 5222,5229 ---- if (needwait) { LockBuffer(buf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(rawxmax, rel, &mytup.t_data->t_ctid, ! XLTW_LockUpdated); goto l4; } if (res != HeapTupleMayBeUpdated) *************** *** 6076,6081 **** HeapTupleGetUpdateXid(HeapTupleHeader tuple) --- 6088,6102 ---- * Do_MultiXactIdWait * Actual implementation for the two functions below. * + * 'multi', 'status' and 'infomask' indicate what to sleep on (the status is + * needed to ensure we only sleep on conflicting members, and the infomask is + * used to optimize multixact access in case it's a lock-only multi); 'nowait' + * indicates whether to use conditional lock acquisition, to allow callers to + * fail if lock is unavailable. 'rel', 'ctid' and 'oper' are used to set up + * context information for error messages. 'remaining', if not NULL, receives + * the number of members that are still running, including any (non-aborted) + * subtransactions of our own transaction. + * * We do this by sleeping on each member using XactLockTableWait. Any * members that belong to the current backend are *not* waited for, however; * this would not merely be useless but would lead to Assert failure inside *************** *** 6093,6099 **** HeapTupleGetUpdateXid(HeapTupleHeader tuple) */ static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, ! int *remaining, uint16 infomask, bool nowait) { bool allow_old; bool result = true; --- 6114,6122 ---- */ static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, ! uint16 infomask, bool nowait, ! Relation rel, ItemPointer ctid, XLTW_Oper oper, ! int *remaining) { bool allow_old; bool result = true; *************** *** 6130,6135 **** Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, --- 6153,6164 ---- /* * This member conflicts with our multi, so we have to sleep (or * return failure, if asked to avoid waiting.) + * + * Note that we don't set up an error context callback ourselves, + * but instead we pass the info down to XactLockTableWait. This + * might seem a bit wasteful because the context is set up and + * tore down for each member of the multixact, but in reality it + * should be barely noticeable, and it avoids duplicate code. */ if (nowait) { *************** *** 6138,6144 **** Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, break; } else ! XactLockTableWait(memxid); } pfree(members); --- 6167,6173 ---- break; } else ! XactLockTableWait(memxid, rel, ctid, oper); } pfree(members); *************** *** 6159,6171 **** Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, * * We return (in *remaining, if not NULL) the number of members that are still * running, including any (non-aborted) subtransactions of our own transaction. - * */ static void ! MultiXactIdWait(MultiXactId multi, MultiXactStatus status, ! int *remaining, uint16 infomask) { ! Do_MultiXactIdWait(multi, status, remaining, infomask, false); } /* --- 6188,6201 ---- * * We return (in *remaining, if not NULL) the number of members that are still * running, including any (non-aborted) subtransactions of our own transaction. */ static void ! MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, ! Relation rel, ItemPointer ctid, XLTW_Oper oper, ! int *remaining) { ! (void) Do_MultiXactIdWait(multi, status, infomask, false, ! rel, ctid, oper, remaining); } /* *************** *** 6183,6191 **** MultiXactIdWait(MultiXactId multi, MultiXactStatus status, */ static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, ! int *remaining, uint16 infomask) { ! return Do_MultiXactIdWait(multi, status, remaining, infomask, true); } /* --- 6213,6223 ---- */ static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, ! uint16 infomask, Relation rel, ItemPointer ctid, ! XLTW_Oper oper, int *remaining) { ! return Do_MultiXactIdWait(multi, status, infomask, true, ! rel, ctid, oper, remaining); } /* *** a/src/backend/access/nbtree/nbtinsert.c --- b/src/backend/access/nbtree/nbtinsert.c *************** *** 164,170 **** top: { /* Have to wait for the other guy ... */ _bt_relbuf(rel, buf); ! XactLockTableWait(xwait); /* start over... */ _bt_freestack(stack); goto top; --- 164,170 ---- { /* Have to wait for the other guy ... */ _bt_relbuf(rel, buf); ! XactLockTableWait(xwait, rel, &itup->t_tid, XLTW_InsertIndex); /* start over... */ _bt_freestack(stack); goto top; *** a/src/backend/catalog/index.c --- b/src/backend/catalog/index.c *************** *** 2295,2301 **** IndexBuildHeapScan(Relation heapRelation, * Must drop the lock on the buffer before we wait */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait); goto recheck; } } --- 2295,2303 ---- * Must drop the lock on the buffer before we wait */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait, heapRelation, ! &heapTuple->t_data->t_ctid, ! XLTW_InsertIndexUnique); goto recheck; } } *************** *** 2341,2347 **** IndexBuildHeapScan(Relation heapRelation, * Must drop the lock on the buffer before we wait */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait); goto recheck; } --- 2343,2351 ---- * Must drop the lock on the buffer before we wait */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait, heapRelation, ! &heapTuple->t_data->t_ctid, ! XLTW_InsertIndexUnique); goto recheck; } *** a/src/backend/executor/execMain.c --- b/src/backend/executor/execMain.c *************** *** 1982,1988 **** EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, if (TransactionIdIsValid(SnapshotDirty.xmax)) { ReleaseBuffer(buffer); ! XactLockTableWait(SnapshotDirty.xmax); continue; /* loop back to repeat heap_fetch */ } --- 1982,1990 ---- if (TransactionIdIsValid(SnapshotDirty.xmax)) { ReleaseBuffer(buffer); ! XactLockTableWait(SnapshotDirty.xmax, ! relation, &tuple.t_data->t_ctid, ! XLTW_FetchUpdated); continue; /* loop back to repeat heap_fetch */ } *** a/src/backend/executor/execUtils.c --- b/src/backend/executor/execUtils.c *************** *** 1307,1313 **** retry: if (TransactionIdIsValid(xwait)) { index_endscan(index_scan); ! XactLockTableWait(xwait); goto retry; } --- 1307,1314 ---- if (TransactionIdIsValid(xwait)) { index_endscan(index_scan); ! XactLockTableWait(xwait, heap, &tup->t_data->t_ctid, ! XLTW_RecheckExclusionConstr); goto retry; } *** a/src/backend/replication/logical/snapbuild.c --- b/src/backend/replication/logical/snapbuild.c *************** *** 1343,1349 **** SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn if (TransactionIdIsCurrentTransactionId(xid)) elog(ERROR, "waiting for ourselves"); ! XactLockTableWait(xid); } /* nothing could have built up so far, so don't perform cleanup */ --- 1343,1349 ---- if (TransactionIdIsCurrentTransactionId(xid)) elog(ERROR, "waiting for ourselves"); ! XactLockTableWait(xid, NULL, NULL, XLTW_None); } /* nothing could have built up so far, so don't perform cleanup */ *** a/src/backend/storage/lmgr/lmgr.c --- b/src/backend/storage/lmgr/lmgr.c *************** *** 26,31 **** --- 26,46 ---- /* + * Struct to hold context info for transaction lock waits. + * + * 'oper' is the operation that needs to wait for the other transaction; 'rel' + * and 'ctid' specify the address of the tuple being waited for. + */ + typedef struct XactLockTableWaitInfo + { + XLTW_Oper oper; + Relation rel; + ItemPointer ctid; + } XactLockTableWaitInfo; + + static void XactLockTableWaitErrorCb(void *arg); + + /* * RelationInitLockInfo * Initializes the lock information in a relation descriptor. * *************** *** 471,477 **** XactLockTableDelete(TransactionId xid) /* * XactLockTableWait * ! * Wait for the specified transaction to commit or abort. * * Note that this does the right thing for subtransactions: if we wait on a * subtransaction, we will exit as soon as it aborts or its top parent commits. --- 486,494 ---- /* * XactLockTableWait * ! * Wait for the specified transaction to commit or abort. If an operation ! * is specified, an error context callback is set up. If 'oper' is passed as ! * None, no error context callback is set up. * * Note that this does the right thing for subtransactions: if we wait on a * subtransaction, we will exit as soon as it aborts or its top parent commits. *************** *** 481,489 **** XactLockTableDelete(TransactionId xid) * and if so wait for its parent. */ void ! XactLockTableWait(TransactionId xid) { LOCKTAG tag; for (;;) { --- 498,528 ---- * and if so wait for its parent. */ void ! XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, ! XLTW_Oper oper) { LOCKTAG tag; + XactLockTableWaitInfo info; + ErrorContextCallback callback; + + /* + * If an operation is specified, set up our verbose error context + * callback. + */ + if (oper != XLTW_None) + { + Assert(RelationIsValid(rel)); + Assert(ItemPointerIsValid(ctid)); + + info.rel = rel; + info.ctid = ctid; + info.oper = oper; + + callback.callback = XactLockTableWaitErrorCb; + callback.arg = &info; + callback.previous = error_context_stack; + error_context_stack = &callback; + } for (;;) { *************** *** 500,505 **** XactLockTableWait(TransactionId xid) --- 539,547 ---- break; xid = SubTransGetParent(xid); } + + if (oper != XLTW_None) + error_context_stack = callback.previous; } /* *************** *** 534,539 **** ConditionalXactLockTableWait(TransactionId xid) --- 576,637 ---- } /* + * XactLockTableWaitErrorContextCb + * Error context callback for transaction lock waits. + */ + static void + XactLockTableWaitErrorCb(void *arg) + { + XactLockTableWaitInfo *info = (XactLockTableWaitInfo *) arg; + + /* + * We would like to print schema name too, but that would require a + * syscache lookup. + */ + if (info->oper != XLTW_None && + ItemPointerIsValid(info->ctid) && RelationIsValid(info->rel)) + { + const char *cxt; + + switch (info->oper) + { + case XLTW_Update: + cxt = gettext_noop("while updating tuple (%u,%u) in relation \"%s\""); + break; + case XLTW_Delete: + cxt = gettext_noop("while deleting tuple (%u,%u) in relation \"%s\""); + break; + case XLTW_Lock: + cxt = gettext_noop("while locking tuple (%u,%u) in relation \"%s\""); + break; + case XLTW_LockUpdated: + cxt = gettext_noop("while locking updated version (%u,%u) of tuple in relation \"%s\""); + break; + case XLTW_InsertIndex: + cxt = gettext_noop("while inserting index tuple (%u,%u) in relation \"%s\""); + break; + case XLTW_InsertIndexUnique: + cxt = gettext_noop("while checking uniqueness of tuple (%u,%u) in relation \"%s\""); + break; + case XLTW_FetchUpdated: + cxt = gettext_noop("while rechecking updated tuple (%u,%u) in relation \"%s\""); + break; + case XLTW_RecheckExclusionConstr: + cxt = gettext_noop("while checking exclusion constraint on tuple (%u,%u) in relation \"%s\""); + break; + + default: + return; + } + + errcontext(cxt, + ItemPointerGetBlockNumber(info->ctid), + ItemPointerGetOffsetNumber(info->ctid), + RelationGetRelationName(info->rel)); + } + } + + /* * WaitForLockersMultiple * Wait until no transaction holds locks that conflict with the given * locktags at the given lockmode. *** a/src/include/storage/lmgr.h --- b/src/include/storage/lmgr.h *************** *** 20,25 **** --- 20,39 ---- #include "utils/rel.h" + /* XactLockTableWait operations */ + typedef enum XLTW_Oper + { + XLTW_None, + XLTW_Update, + XLTW_Delete, + XLTW_Lock, + XLTW_LockUpdated, + XLTW_InsertIndex, + XLTW_InsertIndexUnique, + XLTW_FetchUpdated, + XLTW_RecheckExclusionConstr + } XLTW_Oper; + extern void RelationInitLockInfo(Relation relation); /* Lock a relation */ *************** *** 54,60 **** 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 VXIDs, specified by conflicting locktags */ --- 68,75 ---- /* 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, Relation rel, ! ItemPointer ctid, XLTW_Oper oper); extern bool ConditionalXactLockTableWait(TransactionId xid); /* Lock VXIDs, specified by conflicting locktags */ *** a/src/nls-global.mk --- b/src/nls-global.mk *************** *** 57,63 **** BACKEND_COMMON_GETTEXT_TRIGGERS = \ errmsg errmsg_plural:1,2 \ errdetail errdetail_log errdetail_plural:1,2 \ errhint \ ! errcontext BACKEND_COMMON_GETTEXT_FLAGS = \ errmsg:1:c-format errmsg_plural:1:c-format errmsg_plural:2:c-format \ errdetail:1:c-format errdetail_log:1:c-format errdetail_plural:1:c-format errdetail_plural:2:c-format \ --- 57,66 ---- errmsg errmsg_plural:1,2 \ errdetail errdetail_log errdetail_plural:1,2 \ errhint \ ! errcontext \ ! XactLockTableWait:4 \ ! MultiXactIdWait:6 \ ! ConditionalMultiXactIdWait:6 BACKEND_COMMON_GETTEXT_FLAGS = \ errmsg:1:c-format errmsg_plural:1:c-format errmsg_plural:2:c-format \ errdetail:1:c-format errdetail_log:1:c-format errdetail_plural:1:c-format errdetail_plural:2:c-format \
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers