Skip to content

Commit 5309ae4

Browse files
michaelpqCommitfest Bot
authored andcommitted
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.
1 parent 5a5775b commit 5309ae4

File tree

2 files changed

+100
-47
lines changed

2 files changed

+100
-47
lines changed

src/backend/access/sequence/seqlocalam.c

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "access/multixact.h"
2020
#include "access/seqlocalam.h"
2121
#include "access/sequenceam.h"
22+
#include "access/sequence_page.h"
2223
#include "access/xact.h"
2324
#include "access/xloginsert.h"
2425
#include "access/xlogutils.h"
@@ -83,27 +84,11 @@ static void fill_seq_fork_with_data(Relation rel, HeapTuple tuple,
8384
static Form_pg_seq_local_data
8485
read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
8586
{
86-
Page page;
87-
ItemId lp;
8887
seq_local_magic *sm;
8988
Form_pg_seq_local_data seq;
9089

91-
*buf = ReadBuffer(rel, 0);
92-
LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
93-
94-
page = BufferGetPage(*buf);
95-
sm = (seq_local_magic *) PageGetSpecialPointer(page);
96-
97-
if (sm->magic != SEQ_LOCAL_MAGIC)
98-
elog(ERROR, "bad magic number in sequence \"%s\": %08X",
99-
RelationGetRelationName(rel), sm->magic);
100-
101-
lp = PageGetItemId(page, FirstOffsetNumber);
102-
Assert(ItemIdIsNormal(lp));
103-
104-
/* Note we currently only bother to set these two fields of *seqdatatuple */
105-
seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
106-
seqdatatuple->t_len = ItemIdGetLength(lp);
90+
/* Retrieve data from the sequence page */
91+
SEQUENCE_PAGE_READ(Form_pg_seq_local_data, seq_local_magic, SEQ_LOCAL_MAGIC);
10792

10893
/*
10994
* Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -122,8 +107,6 @@ read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
122107
MarkBufferDirtyHint(*buf, true);
123108
}
124109

125-
seq = (Form_pg_seq_local_data) GETSTRUCT(seqdatatuple);
126-
127110
return seq;
128111
}
129112

@@ -162,33 +145,8 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
162145
seq_local_magic *sm;
163146
OffsetNumber offnum;
164147

165-
/* Initialize first page of relation with special magic number */
166-
167-
buf = ExtendBufferedRel(BMR_REL(rel), forkNum, NULL,
168-
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
169-
Assert(BufferGetBlockNumber(buf) == 0);
170-
171-
page = BufferGetPage(buf);
172-
173-
PageInit(page, BufferGetPageSize(buf), sizeof(seq_local_magic));
174-
sm = (seq_local_magic *) PageGetSpecialPointer(page);
175-
sm->magic = SEQ_LOCAL_MAGIC;
176-
177-
/* Now insert sequence tuple */
178-
179-
/*
180-
* Since VACUUM does not process sequences, we have to force the tuple to
181-
* have xmin = FrozenTransactionId now. Otherwise it would become
182-
* invisible to SELECTs after 2G transactions. It is okay to do this
183-
* because if the current transaction aborts, no other xact will ever
184-
* examine the sequence tuple anyway.
185-
*/
186-
HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
187-
HeapTupleHeaderSetXminFrozen(tuple->t_data);
188-
HeapTupleHeaderSetCmin(tuple->t_data, FirstCommandId);
189-
HeapTupleHeaderSetXmax(tuple->t_data, InvalidTransactionId);
190-
tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
191-
ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
148+
/* Initialize first page of relation */
149+
SEQUENCE_PAGE_INIT(seq_local_magic, SEQ_LOCAL_MAGIC);
192150

193151
/* check the comment above nextval_internal()'s equivalent call. */
194152
if (RelationNeedsWAL(rel))

src/include/access/sequence_page.h

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* sequence_page.h
4+
* Helper macros for page manipulations with sequence access methods.
5+
*
6+
* These macros are useful for sequence access methods that hold their data
7+
* on a single page, like the in-core "local" method.
8+
*
9+
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
10+
* Portions Copyright (c) 1994, Regents of the University of California
11+
*
12+
* src/include/access/sequence_page.h
13+
*
14+
*-------------------------------------------------------------------------
15+
*/
16+
#ifndef SEQUENCE_PAGE_H
17+
#define SEQUENCE_PAGE_H
18+
19+
/*
20+
* Initialize the first page of a sequence relation. This embeds the
21+
* handling for the special magic number, and enforces a frozen XID,
22+
* for VACUUM.
23+
*
24+
* Since VACUUM does not process sequences, we have to force the tuple to
25+
* have xmin = FrozenTransactionId now. Otherwise it would become
26+
* invisible to SELECTs after 2G transactions. It is okay to do this
27+
* because if the current transaction aborts, no other xact will ever
28+
* examine the sequence tuple anyway.
29+
*
30+
* "seqam_special" is the structure used for the special area of a
31+
* sequence access method.
32+
* "seqam_magic_value" is a value stored in the special area, used for
33+
* the validation of the page.
34+
*/
35+
#define SEQUENCE_PAGE_INIT(seqam_special, seqam_magic_value) \
36+
do { \
37+
buf = ExtendBufferedRel(BMR_REL(rel), forkNum, NULL, \
38+
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK); \
39+
Assert(BufferGetBlockNumber(buf) == 0); \
40+
\
41+
page = BufferGetPage(buf); \
42+
\
43+
PageInit(page, BufferGetPageSize(buf), sizeof(seqam_special)); \
44+
sm = (seqam_special *) PageGetSpecialPointer(page); \
45+
sm->magic = seqam_magic_value; \
46+
\
47+
/* Now insert sequence tuple */ \
48+
HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId); \
49+
HeapTupleHeaderSetXminFrozen(tuple->t_data); \
50+
HeapTupleHeaderSetCmin(tuple->t_data, FirstCommandId); \
51+
HeapTupleHeaderSetXmax(tuple->t_data, InvalidTransactionId); \
52+
tuple->t_data->t_infomask |= HEAP_XMAX_INVALID; \
53+
ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber); \
54+
} while(0)
55+
56+
57+
/*
58+
* Read the first page of a sequence relation, previously initialized with
59+
* SEQUENCE_PAGE_INIT.
60+
*
61+
* "Form_seqam_data" is the data retrieved from the page.
62+
* "seqam_special" is the structure used for the special area of a
63+
* sequence access method.
64+
* "seqam_magic_value" is a value stored in the special area, used for
65+
* the validation of the page.
66+
*/
67+
#define SEQUENCE_PAGE_READ(Form_seqam_data, seqam_special, seqam_magic_value) \
68+
do { \
69+
Page page; \
70+
ItemId lp; \
71+
\
72+
*buf = ReadBuffer(rel, 0); \
73+
LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE); \
74+
\
75+
page = BufferGetPage(*buf); \
76+
sm = (seqam_special *) PageGetSpecialPointer(page); \
77+
\
78+
if (sm->magic != seqam_magic_value) \
79+
elog(ERROR, "bad magic number in sequence \"%s\": %08X", \
80+
RelationGetRelationName(rel), sm->magic); \
81+
\
82+
lp = PageGetItemId(page, FirstOffsetNumber); \
83+
Assert(ItemIdIsNormal(lp)); \
84+
\
85+
/* \
86+
* Note we currently only bother to set these two fields of \
87+
* *seqdatatuple. \
88+
*/ \
89+
seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp); \
90+
seqdatatuple->t_len = ItemIdGetLength(lp); \
91+
\
92+
seq = (Form_seqam_data) GETSTRUCT(seqdatatuple); \
93+
} while(0)
94+
95+
#endif /* SEQUENCE_PAGE_H */

0 commit comments

Comments
 (0)