v23-0006-Refactor-logic-for-page-manipulations-of-sequenc.patch
text/x-diff
Filename: v23-0006-Refactor-logic-for-page-manipulations-of-sequenc.patch
Type: text/x-diff
Part: 5
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v23-0006
Subject: Refactor logic for page manipulations of sequence AMs
| File | + | − |
|---|---|---|
| src/backend/access/sequence/seqlocalam.c | 5 | 47 |
| src/include/access/sequence_page.h | 95 | 0 |
From 2e5573203af88d533905cd17c7b9ac6b4cac9f0f Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 21 Aug 2025 11:29:39 +0900
Subject: [PATCH v23 6/7] Refactor logic for page manipulations of sequence AMs
This introduces a new header, named sequence_page.h, aimed at providing
helper macros that can be used with sequence implementations that rely
on a single on-disk page. The in-core "local" sequence AM is one case.
A follow-up patch will rely on that to make its implementation simpler.
---
src/include/access/sequence_page.h | 95 ++++++++++++++++++++++++
src/backend/access/sequence/seqlocalam.c | 52 ++-----------
2 files changed, 100 insertions(+), 47 deletions(-)
create mode 100644 src/include/access/sequence_page.h
diff --git a/src/include/access/sequence_page.h b/src/include/access/sequence_page.h
new file mode 100644
index 000000000000..b59f545607cc
--- /dev/null
+++ b/src/include/access/sequence_page.h
@@ -0,0 +1,95 @@
+/*-------------------------------------------------------------------------
+ *
+ * sequence_page.h
+ * Helper macros for page manipulations with sequence access methods.
+ *
+ * These macros are useful for sequence access methods that hold their data
+ * on a single page, like the in-core "local" method.
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/sequence_page.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQUENCE_PAGE_H
+#define SEQUENCE_PAGE_H
+
+/*
+ * Initialize the first page of a sequence relation. This embeds the
+ * handling for the special magic number, and enforces a frozen XID,
+ * for VACUUM.
+ *
+ * Since VACUUM does not process sequences, we have to force the tuple to
+ * have xmin = FrozenTransactionId now. Otherwise it would become
+ * invisible to SELECTs after 2G transactions. It is okay to do this
+ * because if the current transaction aborts, no other xact will ever
+ * examine the sequence tuple anyway.
+ *
+ * "seqam_special" is the structure used for the special area of a
+ * sequence access method.
+ * "seqam_magic_value" is a value stored in the special area, used for
+ * the validation of the page.
+ */
+#define SEQUENCE_PAGE_INIT(seqam_special, seqam_magic_value) \
+do { \
+ buf = ExtendBufferedRel(BMR_REL(rel), forkNum, NULL, \
+ EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK); \
+ Assert(BufferGetBlockNumber(buf) == 0); \
+ \
+ page = BufferGetPage(buf); \
+ \
+ PageInit(page, BufferGetPageSize(buf), sizeof(seqam_special)); \
+ sm = (seqam_special *) PageGetSpecialPointer(page); \
+ sm->magic = seqam_magic_value; \
+ \
+ /* Now insert sequence tuple */ \
+ HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId); \
+ HeapTupleHeaderSetXminFrozen(tuple->t_data); \
+ HeapTupleHeaderSetCmin(tuple->t_data, FirstCommandId); \
+ HeapTupleHeaderSetXmax(tuple->t_data, InvalidTransactionId); \
+ tuple->t_data->t_infomask |= HEAP_XMAX_INVALID; \
+ ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber); \
+} while(0)
+
+
+/*
+ * Read the first page of a sequence relation, previously initialized with
+ * SEQUENCE_PAGE_INIT.
+ *
+ * "Form_seqam_data" is the data retrieved from the page.
+ * "seqam_special" is the structure used for the special area of a
+ * sequence access method.
+ * "seqam_magic_value" is a value stored in the special area, used for
+ * the validation of the page.
+ */
+#define SEQUENCE_PAGE_READ(Form_seqam_data, seqam_special, seqam_magic_value) \
+do { \
+ Page page; \
+ ItemId lp; \
+ \
+ *buf = ReadBuffer(rel, 0); \
+ LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE); \
+ \
+ page = BufferGetPage(*buf); \
+ sm = (seqam_special *) PageGetSpecialPointer(page); \
+ \
+ if (sm->magic != seqam_magic_value) \
+ elog(ERROR, "bad magic number in sequence \"%s\": %08X", \
+ RelationGetRelationName(rel), sm->magic); \
+ \
+ lp = PageGetItemId(page, FirstOffsetNumber); \
+ Assert(ItemIdIsNormal(lp)); \
+ \
+ /* \
+ * Note we currently only bother to set these two fields of \
+ * *seqdatatuple. \
+ */ \
+ seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp); \
+ seqdatatuple->t_len = ItemIdGetLength(lp); \
+ \
+ seq = (Form_seqam_data) GETSTRUCT(seqdatatuple); \
+} while(0)
+
+#endif /* SEQUENCE_PAGE_H */
diff --git a/src/backend/access/sequence/seqlocalam.c b/src/backend/access/sequence/seqlocalam.c
index 8c9ad974a92e..d7868f4fcbb0 100644
--- a/src/backend/access/sequence/seqlocalam.c
+++ b/src/backend/access/sequence/seqlocalam.c
@@ -19,6 +19,7 @@
#include "access/multixact.h"
#include "access/seqlocalam.h"
#include "access/sequenceam.h"
+#include "access/sequence_page.h"
#include "access/xact.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
@@ -83,27 +84,11 @@ static void fill_seq_fork_with_data(Relation rel, HeapTuple tuple,
static Form_pg_seq_local_data
read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
{
- Page page;
- ItemId lp;
seq_local_magic *sm;
Form_pg_seq_local_data seq;
- *buf = ReadBuffer(rel, 0);
- LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
-
- page = BufferGetPage(*buf);
- sm = (seq_local_magic *) PageGetSpecialPointer(page);
-
- if (sm->magic != SEQ_LOCAL_MAGIC)
- elog(ERROR, "bad magic number in sequence \"%s\": %08X",
- RelationGetRelationName(rel), sm->magic);
-
- lp = PageGetItemId(page, FirstOffsetNumber);
- Assert(ItemIdIsNormal(lp));
-
- /* Note we currently only bother to set these two fields of *seqdatatuple */
- seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- seqdatatuple->t_len = ItemIdGetLength(lp);
+ /* Retrieve data from the sequence page */
+ SEQUENCE_PAGE_READ(Form_pg_seq_local_data, seq_local_magic, SEQ_LOCAL_MAGIC);
/*
* Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -122,8 +107,6 @@ read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
MarkBufferDirtyHint(*buf, true);
}
- seq = (Form_pg_seq_local_data) GETSTRUCT(seqdatatuple);
-
return seq;
}
@@ -162,33 +145,8 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
seq_local_magic *sm;
OffsetNumber offnum;
- /* Initialize first page of relation with special magic number */
-
- buf = ExtendBufferedRel(BMR_REL(rel), forkNum, NULL,
- EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
- Assert(BufferGetBlockNumber(buf) == 0);
-
- page = BufferGetPage(buf);
-
- PageInit(page, BufferGetPageSize(buf), sizeof(seq_local_magic));
- sm = (seq_local_magic *) PageGetSpecialPointer(page);
- sm->magic = SEQ_LOCAL_MAGIC;
-
- /* Now insert sequence tuple */
-
- /*
- * Since VACUUM does not process sequences, we have to force the tuple to
- * have xmin = FrozenTransactionId now. Otherwise it would become
- * invisible to SELECTs after 2G transactions. It is okay to do this
- * because if the current transaction aborts, no other xact will ever
- * examine the sequence tuple anyway.
- */
- HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
- HeapTupleHeaderSetXminFrozen(tuple->t_data);
- HeapTupleHeaderSetCmin(tuple->t_data, FirstCommandId);
- HeapTupleHeaderSetXmax(tuple->t_data, InvalidTransactionId);
- tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
- ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
+ /* Initialize first page of relation */
+ SEQUENCE_PAGE_INIT(seq_local_magic, SEQ_LOCAL_MAGIC);
/* check the comment above nextval_internal()'s equivalent call. */
if (RelationNeedsWAL(rel))
--
2.51.0