Hi,

thanks for the continued review.

On 09/03/14 12:15, Amit Kapila wrote:
> 1.
> "> ISTM that you should be printing just the value and the unique index
> > there, and not any information about the tuple proper.
> 
> Good point, I will have a look at this."
> 
> This point is still not handled in patch, […]

There have been some complaints about this by Andres, so I left it.

> […] due to which the message it displays seems to be
> incomplete. Message it displays is as below:
> 
> LOG:  process 1800 still waiting for ShareLock on transaction 679 after 
> 1014.000
>  ms
> CONTEXT:  while attempting to lock in relation "public"."idx_t1" of database 
> pos
> tgres

Well, there is no tuple information available, so we have to leave it out.

> Here it is not clear what it attempts *lock in*

Ok, I reworded the message as you suggested below. Should make this
clearer as well.

> 2. In function XactLockTableWaitErrorContextCallback(), ignore dropped
> columns in tuple, else it will lead to following failure:
> […]
> Problem is in Session-2 (cache lookup failed), when it tries to print values
> for dropped attribute.

Urghs. I really thought I had done this in the patch before. Thanks
for pointing out, fixed in attached patch.

> 3.
> " in relation \"%s\".\"%s\" of database %s",
> Why to display only relation name in quotes?
> I think database name should also be displayed in quotes.

Didn't think that it would make sense; the quoting makes sense for the
schema and relation name, but I did not see any need to quote the
database name. However, fixed in attached patch.

> 4.
> Context message:
> "while attempting to lock tuple (0,2) ".
> 
> I think it will be better to rephrase it (may be by using 'operate on'
> instead of 'lock').
> The reason is that actually we trying to lock on transaction, so it doesn't 
> make
> good sense to use "lock on tuple"

Fixed.

> 5.
> + XactLockTableWaitWithInfo(relation, &tp, xwait);
> 
> + MultiXactIdWaitWithErrorContext(relation, &tp, (MultiXactId) xwait,
> +  MultiXactStatusUpdate, NULL, infomask);
> 
> I think we should make the name of MultiXactIdWaitWithErrorContext()
> similar to XactLockTableWaitWithInfo.

Fixed as well.

Can you explain why you prefer the WithInfo naming? Just curious, it
seemed to me the more descriptive name should have been preferred.

> 6.
> @@ -1981,7 +1981,8 @@ EvalPlanQualFetch(EState *estate, Relation
> relation, int lockmode,
>   if (TransactionIdIsValid(SnapshotDirty.xmax))
>   {
>   ReleaseBuffer(buffer);
> - XactLockTableWait(SnapshotDirty.xmax);
> + XactLockTableWaitWithInfo(relation, &tuple,
> +  SnapshotDirty.xmax);
> 
> In function EvalPlanQualFetch(), we are using SnapshotDirty to fetch
> the tuple, so I think there is a chance that it will log the tuple which
> otherwise will not be visible. I don't think this is right.

Hm, after checking source I tend to agree. I replaced it with NULL.

> 8.
>  void
>  WaitForLockers(LOCKTAG heaplocktag, LOCKMODE lockmode)
>  {
> - List   *l;
> + List   *l;
> 
> Extra spacing not needed.

What is the policy to do here? The extra spaces have been added by
pg_indent, and there have been more changes in a couple of other files
which I had to drop manually as well. How should this been handled
generally?

> 9.
> /*
>  * XactLockTableWaitErrorContextCallback
>  * error context callback set up by
>  * XactLockTableWaitWithInfo. It reports some
>  * tuple information and the relation of a lock to aquire
>  *
>  */
> Please improve indentation of above function header

Heh. Seems that Emacs' fill-paragraph messed this up. Thanks, fixed.

> 10.
> +#include "access/htup.h"
> +
> +struct XactLockTableWaitLockInfo
> +{
> + Relation rel;
> + HeapTuple tuple;
> +};
> 
> I think it is better to add comments for this structure.
> You can refer comments for struct HeapUpdateFailureData

Fixed.

> 
> 11.
> + *
> + * Use this instead of calling XactTableLockWait()
> 
> In above comment, function name used is wrong and I am not sure this
> is right statement to use because still we are using XactLockTableWait()
> at few places like in function Do_MultiXactIdWait() […]

Fixed function name, but I'd like to keep the wording;
Do_MultiXactIdWait() is wrapped by MultiXactIdWaitWithInfo() and
therefore sets up the context as well.

> […] and recently this is used in function SnapBuildFindSnapshot().

Hm, should we add the context there, too?

> 12.
> heap_update()
> {
> ..
> ..
> /*
>  * There was no UPDATE in the MultiXact; or it aborted. No
>  * TransactionIdIsInProgress() call needed here, since we called
>  * MultiXactIdWait() above.
> 
> Change the function name in above comment.

Fixed.

Best regards,

-- 
 Christian Kruse               http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 71ec740..0cf3537 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -107,6 +107,8 @@ static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax,
 						uint16 t_infomask);
 static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
 				int *remaining, uint16 infomask);
+static void MultiXactIdWaitWithInfo(Relation rel, HeapTuple tup, MultiXactId multi,
+					MultiXactStatus status, int *remaining, uint16 infomask);
 static bool ConditionalMultiXactIdWait(MultiXactId multi,
 						   MultiXactStatus status, int *remaining,
 						   uint16 infomask);
@@ -2714,8 +2716,8 @@ l1:
 		if (infomask & HEAP_XMAX_IS_MULTI)
 		{
 			/* wait for multixact */
-			MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate,
-							NULL, infomask);
+			MultiXactIdWaitWithInfo(relation, &tp, (MultiXactId) xwait,
+									MultiXactStatusUpdate, NULL, infomask);
 			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
 			/*
@@ -2741,7 +2743,7 @@ l1:
 		else
 		{
 			/* wait for regular transaction to end */
-			XactLockTableWait(xwait);
+			XactLockTableWaitWithInfo(relation, &tp, xwait);
 			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
 			/*
@@ -3266,8 +3268,8 @@ l2:
 			int			remain;
 
 			/* wait for multixact */
-			MultiXactIdWait((MultiXactId) xwait, mxact_status, &remain,
-							infomask);
+			MultiXactIdWaitWithInfo(relation, &oldtup,
+					   (MultiXactId) xwait, mxact_status, &remain, infomask);
 			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
 			/*
@@ -3306,7 +3308,7 @@ l2:
 			/*
 			 * There was no UPDATE in the MultiXact; or it aborted. No
 			 * TransactionIdIsInProgress() call needed here, since we called
-			 * MultiXactIdWait() above.
+			 * MultiXactIdWaitWithInfo() above.
 			 */
 			if (!TransactionIdIsValid(update_xact) ||
 				TransactionIdDidAbort(update_xact))
@@ -3341,7 +3343,7 @@ l2:
 			else
 			{
 				/* wait for regular transaction to end */
-				XactLockTableWait(xwait);
+				XactLockTableWaitWithInfo(relation, &oldtup, xwait);
 				LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
 				/*
@@ -4409,7 +4411,8 @@ l3:
 										RelationGetRelationName(relation))));
 				}
 				else
-					MultiXactIdWait((MultiXactId) xwait, status, NULL, infomask);
+					MultiXactIdWaitWithInfo(relation, tuple,
+								(MultiXactId) xwait, status, NULL, infomask);
 
 				/* if there are updates, follow the update chain */
 				if (follow_updates &&
@@ -4464,7 +4467,7 @@ l3:
 										RelationGetRelationName(relation))));
 				}
 				else
-					XactLockTableWait(xwait);
+					XactLockTableWaitWithInfo(relation, tuple, xwait);
 
 				/* if there are updates, follow the update chain */
 				if (follow_updates &&
@@ -5151,7 +5154,7 @@ l4:
 					if (needwait)
 					{
 						LockBuffer(buf, BUFFER_LOCK_UNLOCK);
-						XactLockTableWait(members[i].xid);
+						XactLockTableWaitWithInfo(rel, &mytup, members[i].xid);
 						pfree(members);
 						goto l4;
 					}
@@ -5211,7 +5214,7 @@ l4:
 				if (needwait)
 				{
 					LockBuffer(buf, BUFFER_LOCK_UNLOCK);
-					XactLockTableWait(rawxmax);
+					XactLockTableWaitWithInfo(rel, &mytup, rawxmax);
 					goto l4;
 				}
 				if (res != HeapTupleMayBeUpdated)
@@ -6169,6 +6172,33 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
 }
 
 /*
+ * MultiXactIdWaitWithInfo
+ *		Sets up the error context callback for reporting tuple
+ *		information and relation for a lock to aquire
+ *
+ * Use this instead of calling MultiXactIdWait()
+ */
+static void
+MultiXactIdWaitWithInfo(Relation rel, HeapTuple tup, MultiXactId multi,
+					 MultiXactStatus status, int *remaining, uint16 infomask)
+{
+	struct XactLockTableWaitLockInfo info;
+	ErrorContextCallback callback;
+
+	info.rel = rel;
+	info.tuple = tup;
+
+	callback.callback = XactLockTableWaitErrorContextCallback;
+	callback.arg = &info;
+	callback.previous = error_context_stack;
+	error_context_stack = &callback;
+
+	MultiXactIdWait(multi, status, remaining, infomask);
+
+	error_context_stack = callback.previous;
+}
+
+/*
  * ConditionalMultiXactIdWait
  *		As above, but only lock if we can get the lock without blocking.
  *
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 5f7953f..fc40c1c 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -164,7 +164,7 @@ top:
 		{
 			/* Have to wait for the other guy ... */
 			_bt_relbuf(rel, buf);
-			XactLockTableWait(xwait);
+			XactLockTableWaitWithInfo(rel, NULL, xwait);
 			/* start over... */
 			_bt_freestack(stack);
 			goto top;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 877d767..49d7cd6 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2295,7 +2295,8 @@ IndexBuildHeapScan(Relation heapRelation,
 							 * Must drop the lock on the buffer before we wait
 							 */
 							LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
-							XactLockTableWait(xwait);
+							XactLockTableWaitWithInfo(heapRelation,
+													  heapTuple, xwait);
 							goto recheck;
 						}
 					}
@@ -2341,7 +2342,8 @@ IndexBuildHeapScan(Relation heapRelation,
 							 * Must drop the lock on the buffer before we wait
 							 */
 							LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
-							XactLockTableWait(xwait);
+							XactLockTableWaitWithInfo(heapRelation,
+													  heapTuple, xwait);
 							goto recheck;
 						}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 9e3d879..4bf4f0f 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1982,7 +1982,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 			if (TransactionIdIsValid(SnapshotDirty.xmax))
 			{
 				ReleaseBuffer(buffer);
-				XactLockTableWait(SnapshotDirty.xmax);
+				XactLockTableWaitWithInfo(relation, NULL, SnapshotDirty.xmax);
 				continue;		/* loop back to repeat heap_fetch */
 			}
 
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 46895b2..46b64c2 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -1307,7 +1307,7 @@ retry:
 		if (TransactionIdIsValid(xwait))
 		{
 			index_endscan(index_scan);
-			XactLockTableWait(xwait);
+			XactLockTableWaitWithInfo(heap, NULL, xwait);
 			goto retry;
 		}
 
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index 4c61a6f..5f2cac9 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -18,12 +18,15 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/xact.h"
+#include "access/htup_details.h"
 #include "catalog/catalog.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
 #include "storage/procarray.h"
 #include "utils/inval.h"
-
+#include "utils/lsyscache.h"
+#include "mb/pg_wchar.h"
+#include "commands/dbcommands.h"
 
 /*
  * RelationInitLockInfo
@@ -534,6 +537,140 @@ ConditionalXactLockTableWait(TransactionId xid)
 }
 
 /*
+ *		XactLockTableWaitErrorContextCallback
+ *
+ * error context callback set up by XactLockTableWaitWithInfo. It reports some
+ * tuple information and the relation of a lock to aquire
+ *
+ */
+void
+XactLockTableWaitErrorContextCallback(void *arg)
+{
+	StringInfoData buf,
+				error_message;
+	int			i;
+	bool		write_comma = false;
+	int			maxfieldlen = 30;
+	struct XactLockTableWaitLockInfo *info = (struct XactLockTableWaitLockInfo *) arg;
+
+	initStringInfo(&error_message);
+	appendStringInfoString(&error_message, "while attempting to operate");
+
+	if (info->tuple != NULL)
+	{
+		/*
+		 * Can't produce an error message including the tuple if we're in a
+		 * possibly aborted transaction state, database access might not be
+		 * safe.
+		 */
+		appendStringInfo(&error_message, " on tuple (%u,%u)",
+					  BlockIdGetBlockNumber(&(info->tuple->t_self.ip_blkid)),
+						 info->tuple->t_self.ip_posid);
+
+		if (geterrlevel() < ERROR)
+		{
+			TupleDesc	desc = RelationGetDescr(info->rel);
+			Datum	   *values = palloc(desc->natts * sizeof(*values));
+			bool	   *nulls = palloc(desc->natts * sizeof(*values));
+
+			heap_deform_tuple(info->tuple, desc, values, nulls);
+
+			initStringInfo(&buf);
+			appendStringInfoChar(&buf, '(');
+
+			for (i = 0; i < desc->natts; i++)
+			{
+				char	   *val;
+				int			vallen;
+
+				if (desc->attrs[i]->attisdropped)
+					continue;
+
+				if (nulls[i])
+					val = "null";
+				else
+				{
+					Oid			foutoid;
+					bool		typisvarlena;
+
+					getTypeOutputInfo(desc->attrs[i]->atttypid,
+									  &foutoid, &typisvarlena);
+
+					val = OidOutputFunctionCall(foutoid, values[i]);
+				}
+
+				if (write_comma)
+					appendStringInfoString(&buf, ", ");
+				else
+					write_comma = true;
+
+				vallen = strlen(val);
+				if (vallen <= maxfieldlen)
+					appendStringInfoString(&buf, val);
+				else
+				{
+					vallen = pg_mbcliplen(val, vallen, maxfieldlen);
+					appendBinaryStringInfo(&buf, val, vallen);
+					appendStringInfoString(&buf, "...");
+				}
+			}
+
+			appendStringInfoChar(&buf, ')');
+
+			appendStringInfo(&error_message, " with values %s",
+							 buf.data);
+
+			pfree(buf.data);
+			pfree(values);
+			pfree(nulls);
+		}
+	}
+
+	if (info->rel != NULL)
+	{
+		appendStringInfo(&error_message,
+						 " in relation \"%s\".\"%s\" of database \"%s\"",
+						 get_namespace_name(RelationGetNamespace(info->rel)),
+						 RelationGetRelationName(info->rel),
+						 get_database_name(info->rel->rd_node.dbNode));
+	}
+	else if (MyDatabaseId != InvalidOid)
+	{
+		appendStringInfo(&error_message, " in database \"%s\"",
+						 get_database_name(MyDatabaseId));
+	}
+
+	errcontext("%s", error_message.data);
+	pfree(error_message.data);
+}
+
+/*
+ * XactLockTableWaitWithInfo
+ *		Sets up the error context callback for reporting tuple
+ *		information and relation for a lock to aquire
+ *
+ * Use this instead of calling XactLockTableWait()
+ */
+void
+XactLockTableWaitWithInfo(Relation rel, HeapTuple tuple, TransactionId xid)
+{
+	struct XactLockTableWaitLockInfo info;
+	ErrorContextCallback callback;
+
+	info.rel = rel;
+	info.tuple = tuple;
+
+	callback.callback = XactLockTableWaitErrorContextCallback;
+	callback.arg = &info;
+	callback.previous = error_context_stack;
+	error_context_stack = &callback;
+
+	XactLockTableWait(xid);
+
+	error_context_stack = callback.previous;
+}
+
+/*
  * WaitForLockersMultiple
  *		Wait until no transaction holds locks that conflict with the given
  *		locktags at the given lockmode.
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 8705586..6231db6 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -1216,6 +1216,23 @@ geterrposition(void)
 }
 
 /*
+ * geterrlevel --- return the currently set error level
+ *
+ * This is only intended for use in error callback subroutines, since there
+ * is no other place outside elog.c where the concept is meaningful.
+ */
+int
+geterrlevel(void)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	return edata->elevel;
+}
+
+/*
  * getinternalerrposition --- same for internal error position
  *
  * This is only intended for use in error callback subroutines, since there
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index e74ae21..954decc 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -18,6 +18,21 @@
 #include "storage/itemptr.h"
 #include "storage/lock.h"
 #include "utils/rel.h"
+#include "access/htup.h"
+
+/*
+ * XactLockTableWaitWithInfo/MultiXactIdWaitWithInfo will fill this struct for
+ * XactLockTableWaitErrorContextCallback. When a log_lock_wait message is
+ * caused or the user cancels the request or something else happens,
+ * XactLockTableWaitErrorContextCallback will use it to provide some hopefully
+ * useful information. Relation rel is the relation we're operating on and
+ * HeapTuple tuple is the tuple we're operating on.
+ */
+struct XactLockTableWaitLockInfo
+{
+	Relation	rel;
+	HeapTuple	tuple;
+};
 
 
 extern void RelationInitLockInfo(Relation relation);
@@ -57,6 +72,10 @@ extern void XactLockTableDelete(TransactionId xid);
 extern void XactLockTableWait(TransactionId xid);
 extern bool ConditionalXactLockTableWait(TransactionId xid);
 
+extern void XactLockTableWaitWithInfo(Relation rel, HeapTuple tuple,
+						  TransactionId xid);
+extern void XactLockTableWaitErrorContextCallback(void *arg);
+
 /* Lock VXIDs, specified by conflicting locktags */
 extern void WaitForLockers(LOCKTAG heaplocktag, LOCKMODE lockmode);
 extern void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode);
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index d7916c2..04fd3a2 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -224,6 +224,7 @@ extern int	err_generic_string(int field, const char *str);
 
 extern int	geterrcode(void);
 extern int	geterrposition(void);
+extern int	geterrlevel(void);
 extern int	getinternalerrposition(void);
 
 

Attachment: pgpF6z7PTRX4j.pgp
Description: PGP signature

Reply via email to