aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/heap/heapam.c52
-rw-r--r--src/backend/access/heap/pruneheap.c4
-rw-r--r--src/backend/executor/execMain.c6
-rw-r--r--src/include/access/heapam.h3
4 files changed, 54 insertions, 11 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 0c0f640f640..52dda41cc43 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2074,8 +2074,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;
/*
@@ -2261,7 +2260,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;
@@ -2293,6 +2292,50 @@ heap_get_latest_tid(Relation relation,
} /* end of loop */
}
+/*
+ * HeapTupleUpdateXmaxMatchesXmin - verify update chain xmax/xmin lineage
+ *
+ * Given the new version of a tuple after some update, verify whether the
+ * given Xmax (corresponding to the previous version) matches the tuple's
+ * Xmin, taking into account that the Xmin might have been frozen after the
+ * update.
+ */
+bool
+HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax, HeapTupleHeader htup)
+{
+ TransactionId xmin = HeapTupleHeaderGetXmin(htup);
+
+ /*
+ * If the xmax of the old tuple is identical to the xmin of the new one,
+ * it's a match.
+ */
+ if (TransactionIdEquals(xmax, xmin))
+ return true;
+
+ /*
+ * If the Xmin that was in effect prior to a freeze matches the Xmax,
+ * it's good too.
+ */
+ if (HeapTupleHeaderXminFrozen(htup) &&
+ TransactionIdEquals(HeapTupleHeaderGetRawXmin(htup), xmax))
+ return true;
+
+ /*
+ * When a tuple is frozen, the original Xmin is lost, but we know it's a
+ * committed transaction. So unless the Xmax is InvalidXid, we don't know
+ * for certain that there is a match, but there may be one; and we must
+ * return true so that a HOT chain that is half-frozen can be walked
+ * correctly.
+ *
+ * We no longer freeze tuples this way, but we must keep this in order to
+ * interpret pre-pg_upgrade pages correctly.
+ */
+ if (TransactionIdEquals(xmin, FrozenTransactionId) &&
+ TransactionIdIsValid(xmax))
+ return true;
+
+ return false;
+}
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -5712,8 +5755,7 @@ l4:
* end of the chain, we're done, so return success.
*/
if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
- priorXmax))
+ !HeapTupleUpdateXmaxMatchesXmin(priorXmax, mytup.t_data))
{
result = HeapTupleMayBeUpdated;
goto out_locked;
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 52231ac4178..7753ee7b120 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -473,7 +473,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;
/*
@@ -813,7 +813,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 384ad70f2d9..8359beb463a 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2594,8 +2594,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* atomic, and Xmin never changes in an existing tuple, except to
* invalid or frozen, and neither of those can match priorXmax.)
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
- priorXmax))
+ if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tuple.t_data))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2742,8 +2741,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 4e41024e926..9f4367d704e 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -146,6 +146,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);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);