PostgreSQL Source Code git master
multixact_rewrite.c
Go to the documentation of this file.
1/*
2 * multixact_rewrite.c
3 *
4 * Functions to convert multixact SLRUs from the pre-v19 format to the current
5 * format with 64-bit MultiXactOffsets.
6 *
7 * Copyright (c) 2025, PostgreSQL Global Development Group
8 * src/bin/pg_upgrade/multixact_rewrite.c
9 */
10
11#include "postgres_fe.h"
12
14#include "multixact_read_v18.h"
15#include "pg_upgrade.h"
16
17static void RecordMultiXactOffset(SlruSegState *offsets_writer, MultiXactId multi,
18 MultiXactOffset offset);
19static void RecordMultiXactMembers(SlruSegState *members_writer,
20 MultiXactOffset offset,
21 int nmembers, MultiXactMember *members);
22
23/*
24 * Convert pg_multixact/offset and /members from the old pre-v19 format with
25 * 32-bit offsets to the current format.
26 *
27 * Multixids in the range [from_multi, to_multi) are read from the old
28 * cluster, and written in the new format. An important edge case is that if
29 * from_multi == to_multi, this initializes the new pg_multixact files in the
30 * new format without trying to open any old files. (We rely on that when
31 * upgrading from PostgreSQL version 9.2 or below.)
32 *
33 * Returns the new nextOffset value; the caller should set it in the new
34 * control file. The new members always start from offset 1, regardless of
35 * the offset range used in the old cluster.
36 */
39{
40 MultiXactOffset next_offset;
41 SlruSegState *offsets_writer;
42 SlruSegState *members_writer;
43 char dir[MAXPGPATH] = {0};
44 bool prev_multixid_valid = false;
45
46 /*
47 * The range of valid multi XIDs is unchanged by the conversion (they are
48 * referenced from the heap tables), but the members SLRU is rewritten to
49 * start from offset 1.
50 */
51 next_offset = 1;
52
53 /* Prepare to write the new SLRU files */
54 pg_sprintf(dir, "%s/pg_multixact/offsets", new_cluster.pgdata);
55 offsets_writer = AllocSlruWrite(dir, false);
56 SlruWriteSwitchPage(offsets_writer, MultiXactIdToOffsetPage(from_multi));
57
58 pg_sprintf(dir, "%s/pg_multixact/members", new_cluster.pgdata);
59 members_writer = AllocSlruWrite(dir, true /* use long segment names */ );
60 SlruWriteSwitchPage(members_writer, MXOffsetToMemberPage(next_offset));
61
62 /*
63 * Convert old multixids, if needed, by reading them one-by-one from the
64 * old cluster.
65 */
66 if (to_multi != from_multi)
67 {
68 OldMultiXactReader *old_reader;
69
73
74 for (MultiXactId multi = from_multi; multi != to_multi;)
75 {
76 MultiXactMember member;
77 bool multixid_valid;
78
79 /*
80 * Read this multixid's members.
81 *
82 * Locking-only XIDs that may be part of multi-xids don't matter
83 * after upgrade, as there can be no transactions running across
84 * upgrade. So as a small optimization, we only read one member
85 * from each multixid: the one updating one, or if there was no
86 * update, arbitrarily the first locking xid.
87 */
88 multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
89
90 /*
91 * Write the new offset to pg_multixact/offsets.
92 *
93 * Even if this multixid is invalid, we still need to write its
94 * offset if the *previous* multixid was valid. That's because
95 * when reading a multixid, the number of members is calculated
96 * from the difference between the two offsets.
97 */
98 RecordMultiXactOffset(offsets_writer, multi,
99 (multixid_valid || prev_multixid_valid) ? next_offset : 0);
100
101 /* Write the members */
102 if (multixid_valid)
103 {
104 RecordMultiXactMembers(members_writer, next_offset, 1, &member);
105 next_offset += 1;
106 }
107
108 /* Advance to next multixid, handling wraparound */
109 multi++;
110 if (multi < FirstMultiXactId)
111 multi = FirstMultiXactId;
112 prev_multixid_valid = multixid_valid;
113 }
114
115 FreeOldMultiXactReader(old_reader);
116 }
117
118 /* Write the final 'next' offset to the last SLRU page */
119 RecordMultiXactOffset(offsets_writer, to_multi,
120 prev_multixid_valid ? next_offset : 0);
121
122 /* Flush the last SLRU pages */
123 FreeSlruWrite(offsets_writer);
124 FreeSlruWrite(members_writer);
125
126 return next_offset;
127}
128
129
130/*
131 * Write one offset to the offset SLRU
132 */
133static void
135 MultiXactOffset offset)
136{
137 int64 pageno;
138 int entryno;
139 char *buf;
140 MultiXactOffset *offptr;
141
142 pageno = MultiXactIdToOffsetPage(multi);
143 entryno = MultiXactIdToOffsetEntry(multi);
144
145 buf = SlruWriteSwitchPage(offsets_writer, pageno);
146 offptr = (MultiXactOffset *) buf;
147 offptr[entryno] = offset;
148}
149
150/*
151 * Write the members for one multixid in the members SLRU
152 *
153 * (Currently, this is only ever called with nmembers == 1)
154 */
155static void
157 MultiXactOffset offset,
158 int nmembers, MultiXactMember *members)
159{
160 for (int i = 0; i < nmembers; i++, offset++)
161 {
162 int64 pageno;
163 char *buf;
164 TransactionId *memberptr;
165 uint32 *flagsptr;
166 uint32 flagsval;
167 int bshift;
168 int flagsoff;
169 int memberoff;
170
171 Assert(members[i].status <= MultiXactStatusUpdate);
172
173 pageno = MXOffsetToMemberPage(offset);
174 memberoff = MXOffsetToMemberOffset(offset);
175 flagsoff = MXOffsetToFlagsOffset(offset);
176 bshift = MXOffsetToFlagsBitShift(offset);
177
178 buf = SlruWriteSwitchPage(members_writer, pageno);
179
180 memberptr = (TransactionId *) (buf + memberoff);
181
182 *memberptr = members[i].xid;
183
184 flagsptr = (uint32 *) (buf + flagsoff);
185
186 flagsval = *flagsptr;
187 flagsval &= ~(((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift);
188 flagsval |= (members[i].status << bshift);
189 *flagsptr = flagsval;
190 }
191}
int64_t int64
Definition: c.h:549
TransactionId MultiXactId
Definition: c.h:681
uint64 MultiXactOffset
Definition: c.h:683
uint32_t uint32
Definition: c.h:552
uint32 TransactionId
Definition: c.h:671
Assert(PointerIsAligned(start, uint64))
int i
Definition: isn.c:77
#define FirstMultiXactId
Definition: multixact.h:26
@ MultiXactStatusUpdate
Definition: multixact.h:45
#define MXACT_MEMBER_BITS_PER_XACT
static int MXOffsetToFlagsBitShift(MultiXactOffset32 offset)
static int64 MXOffsetToMemberPage(MultiXactOffset32 offset)
bool GetOldMultiXactIdSingleMember(OldMultiXactReader *state, MultiXactId multi, MultiXactMember *member)
static int MXOffsetToMemberOffset(MultiXactOffset32 offset)
static int MultiXactIdToOffsetEntry(MultiXactId multi)
static int64 MultiXactIdToOffsetPage(MultiXactId multi)
OldMultiXactReader * AllocOldMultiXactRead(char *pgdata, MultiXactId nextMulti, MultiXactOffset32 nextOffset)
void FreeOldMultiXactReader(OldMultiXactReader *state)
static int MXOffsetToFlagsOffset(MultiXactOffset32 offset)
static void RecordMultiXactOffset(SlruSegState *offsets_writer, MultiXactId multi, MultiXactOffset offset)
static void RecordMultiXactMembers(SlruSegState *members_writer, MultiXactOffset offset, int nmembers, MultiXactMember *members)
MultiXactOffset rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
#define MAXPGPATH
static char buf[DEFAULT_XLOG_SEG_SIZE]
Definition: pg_test_fsync.c:71
ClusterInfo new_cluster
Definition: pg_upgrade.c:74
ClusterInfo old_cluster
Definition: pg_upgrade.c:73
int int int int pg_sprintf(char *str, const char *fmt,...) pg_attribute_printf(2
void FreeSlruWrite(SlruSegState *state)
Definition: slru_io.c:260
SlruSegState * AllocSlruWrite(const char *dir, bool long_segment_names)
Definition: slru_io.c:166
static char * SlruWriteSwitchPage(SlruSegState *state, uint64 pageno)
Definition: slru_io.h:45
char * pgdata
Definition: pg_upgrade.h:299
ControlData controldata
Definition: pg_upgrade.h:296
uint32 chkpnt_nxtmulti
Definition: pg_upgrade.h:244
uint64 chkpnt_nxtmxoff
Definition: pg_upgrade.h:245
TransactionId xid
Definition: multixact.h:57
MultiXactStatus status
Definition: multixact.h:58