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

Reply via email to