Hi, On 11/03/14 13:23, Amit Kapila wrote: > [… snip …] > So I think it's better to leave logging it as you have done in > patch.
Agreed. > […] > 2. Name new functions as > MultiXactIdWaitExtended()/XactLockTableWaitExtended() > or MultiXactIdWaitEx()/XactLockTableWaitEx(). > You can find some other similar functions (ReadBufferExtended, > relation_openrv_extended) > > 3. MultiXactIdWaitWithInfo()/XactLockTableWaitWithInfo() > > Earlier I found option 3 as a good choice, but now again thinking on > it I am leaning towards option 2. Changing it once again will only cause more work and won't do a big difference. So I suggest sticking with the current function names. > Today, again looking into it, I found that function > heap_lock_updated_tuple_rec() is using SnapshotAny to fetch the tuple > and I think for this case also it's not safe to Log the tuple. > > Could you please once check (if you are comfortable doing so) wherever > this patch is passing tuple, whether it is okay to pass it based on visibility > rules, else I will again verify it once. I think I got all places, but it would be nice to have a confirmation. > > [… policy regarding whitespaces …] > The simple rule I follow is don't touch the code which has no relation > to current patch. OK. Thanks. 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..bcc656a 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, NULL, members[i].xid);
pfree(members);
goto l4;
}
@@ -5211,7 +5214,7 @@ l4:
if (needwait)
{
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(rawxmax);
+ XactLockTableWaitWithInfo(rel, NULL, 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..4ef5b50 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 "commands/dbcommands.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "storage/lmgr.h"
#include "storage/procarray.h"
#include "utils/inval.h"
-
+#include "utils/lsyscache.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);
pgpkyjyB4p8fw.pgp
Description: PGP signature
