aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2013-12-18 13:31:27 -0300
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2013-12-18 13:31:27 -0300
commitdb1014bc46de21a6de1751b807ea084e607104ad (patch)
tree6d2e5bbf0f8407dd8207e7c7411065c635a2cc05 /src/backend
parent8e9a16ab8f7f0e5813644975cc3f336e5b064b6e (diff)
downloadpostgresql-db1014bc46de21a6de1751b807ea084e607104ad.tar.gz
postgresql-db1014bc46de21a6de1751b807ea084e607104ad.zip
Don't ignore tuple locks propagated by our updates
If a tuple was locked by transaction A, and transaction B updated it, the new version of the tuple created by B would be locked by A, yet visible only to B; due to an oversight in HeapTupleSatisfiesUpdate, the lock held by A wouldn't get checked if transaction B later deleted (or key-updated) the new version of the tuple. This might cause referential integrity checks to give false positives (that is, allow deletes that should have been rejected). This is an easy oversight to have made, because prior to improved tuple locks in commit 0ac5ad5134f it wasn't possible to have tuples created by our own transaction that were also locked by remote transactions, and so locks weren't even considered in that code path. It is recommended that foreign keys be rechecked manually in bulk after installing this update, in case some referenced rows are missing with some referencing row remaining. Per bug reported by Daniel Wood in CAPweHKe5QQ1747X2c0tA=5zf4YnS2xcvGf13Opd-1Mq24rF1cQ@mail.gmail.com
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/transam/multixact.c33
-rw-r--r--src/backend/utils/time/tqual.c36
2 files changed, 67 insertions, 2 deletions
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 55a8ca7ac49..03581bea663 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -1275,6 +1275,39 @@ retry:
}
/*
+ * MultiXactHasRunningRemoteMembers
+ * Does the given multixact have still-live members from
+ * transactions other than our own?
+ */
+bool
+MultiXactHasRunningRemoteMembers(MultiXactId multi)
+{
+ MultiXactMember *members;
+ int nmembers;
+ int i;
+
+ nmembers = GetMultiXactIdMembers(multi, &members, true);
+ if (nmembers <= 0)
+ return false;
+
+ for (i = 0; i < nmembers; i++)
+ {
+ /* not interested in our own members */
+ if (TransactionIdIsCurrentTransactionId(members[i].xid))
+ continue;
+
+ if (TransactionIdIsInProgress(members[i].xid))
+ {
+ pfree(members);
+ return true;
+ }
+ }
+
+ pfree(members);
+ return false;
+}
+
+/*
* mxactMemberComparator
* qsort comparison function for MultiXactMember
*
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index f787f2cbdc9..44b4ddcb02f 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -686,8 +686,36 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return HeapTupleMayBeUpdated;
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return HeapTupleMayBeUpdated;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ /*
+ * Careful here: even though this tuple was created by our own
+ * transaction, it might be locked by other transactions, if
+ * the original version was key-share locked when we updated
+ * it.
+ */
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (MultiXactHasRunningRemoteMembers(xmax))
+ return HeapTupleBeingUpdated;
+ else
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* if locker is gone, all's well */
+ if (!TransactionIdIsInProgress(xmax))
+ return HeapTupleMayBeUpdated;
+
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return HeapTupleBeingUpdated;
+ else
+ return HeapTupleMayBeUpdated;
+ }
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
@@ -700,7 +728,11 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
/* updating subtransaction must have aborted */
if (!TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (MultiXactHasRunningRemoteMembers(HeapTupleHeaderGetRawXmax(tuple)))
+ return HeapTupleBeingUpdated;
return HeapTupleMayBeUpdated;
+ }
else
{
if (HeapTupleHeaderGetCmax(tuple) >= curcid)