0001-Fix-bugs.patch

text/plain

Filename: 0001-Fix-bugs.patch
Type: text/plain
Part: 0
Message: Re: [COMMITTERS] pgsql: Fix freezing of a dead HOT-updated tuple

Patch

Format: unified
Series: patch 0001
File+
src/backend/access/heap/heapam.c 36 5
src/backend/access/heap/pruneheap.c 2 2
src/backend/executor/execMain.c 2 4
src/include/access/heapam.h 3 0
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index e00dc6c1ca..87bce0e7ea 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -1718,8 +1718,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
 		 * broken.
 		 */
 		if (TransactionIdIsValid(prev_xmax) &&
-			!TransactionIdEquals(prev_xmax,
-								 HeapTupleHeaderGetXmin(heapTuple->t_data)))
+			!HeapTupleUpdateXmaxMatchesXmin(prev_xmax, heapTuple->t_data))
 			break;
 
 		/*
@@ -1888,7 +1887,7 @@ heap_get_latest_tid(Relation relation,
 		 * tuple.  Check for XMIN match.
 		 */
 		if (TransactionIdIsValid(priorXmax) &&
-		  !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
+			!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tp.t_data))
 		{
 			UnlockReleaseBuffer(buffer);
 			break;
@@ -1920,6 +1919,36 @@ heap_get_latest_tid(Relation relation,
 	}							/* end of loop */
 }
 
+/*
+ * Given a tuple, verify whether the given Xmax matches the tuple's Xmin,
+ * taking into account that the Xmin might have been frozen.
+ */
+bool
+HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax, HeapTupleHeader htup)
+{
+	TransactionId	xmin = HeapTupleHeaderGetXmin(htup);
+
+	/* xmax must not be invalid or frozen */
+	Assert(TransactionIdIsNormal(xmax));
+
+	/*
+	 * If the xmax of the old tuple is identical to the xmin of the new one,
+	 * it's a match.
+	 */
+	if (xmax == xmin)
+		return true;
+
+	/* FIXME Here we need to check the HEAP_XMIN_FROZEN in 9.4 and up */
+
+	/*
+	 * We actually don't know if there's a match, but if the previous tuple
+	 * was frozen, we cannot really rely on a perfect match.
+	 */
+	if (xmin == FrozenTransactionId)
+		return true;
+
+	return false;
+}
 
 /*
  * UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -5045,8 +5074,7 @@ l4:
 		 * the end of the chain, we're done, so return success.
 		 */
 		if (TransactionIdIsValid(priorXmax) &&
-			!TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
-								 priorXmax))
+			!HeapTupleUpdateXmaxMatchesXmin(priorXmax, mytup.t_data))
 		{
 			UnlockReleaseBuffer(buf);
 			return HeapTupleMayBeUpdated;
@@ -5500,7 +5528,10 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
 			if (TransactionIdPrecedes(xid, cutoff_xid))
 			{
 				if (TransactionIdDidCommit(xid))
+				{
+					xid = FrozenTransactionId;
 					*flags = FRM_MARK_COMMITTED | FRM_RETURN_IS_XID;
+				}
 				else
 				{
 					*flags |= FRM_INVALIDATE_XMAX;
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 9a8db74cb9..a342f57f42 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -435,7 +435,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
 		 * Check the tuple XMIN against prior XMAX, if any
 		 */
 		if (TransactionIdIsValid(priorXmax) &&
-			!TransactionIdEquals(HeapTupleHeaderGetXmin(htup), priorXmax))
+			!HeapTupleUpdateXmaxMatchesXmin(priorXmax, htup))
 			break;
 
 		/*
@@ -774,7 +774,7 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
 			htup = (HeapTupleHeader) PageGetItem(page, lp);
 
 			if (TransactionIdIsValid(priorXmax) &&
-				!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(htup)))
+				!HeapTupleUpdateXmaxMatchesXmin(priorXmax, htup))
 				break;
 
 			/* Remember the root line pointer for this item */
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 315a555dcf..79e4701bb8 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2019,8 +2019,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 			 * buffer's content lock, since xmin never changes in an existing
 			 * tuple.)
 			 */
-			if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
-									 priorXmax))
+			if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tuple.t_data))
 			{
 				ReleaseBuffer(buffer);
 				return NULL;
@@ -2138,8 +2137,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 		/*
 		 * As above, if xmin isn't what we're expecting, do nothing.
 		 */
-		if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
-								 priorXmax))
+		if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tuple.t_data))
 		{
 			ReleaseBuffer(buffer);
 			return NULL;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4a187f899c..93a481f483 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -127,6 +127,9 @@ extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
 					ItemPointer tid);
 extern void setLastTid(const ItemPointer tid);
 
+extern bool HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax,
+							   HeapTupleHeader htup);
+
 extern BulkInsertState GetBulkInsertState(void);
 extern void FreeBulkInsertState(BulkInsertState);
 
-- 
2.11.0