Line data Source code
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 :
13 : #include "access/multixact_internal.h"
14 : #include "multixact_read_v18.h"
15 : #include "pg_upgrade.h"
16 :
17 : static void RecordMultiXactOffset(SlruSegState *offsets_writer, MultiXactId multi,
18 : MultiXactOffset offset);
19 : static 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 : */
37 : MultiXactOffset
38 0 : rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
39 : {
40 : MultiXactOffset next_offset;
41 : SlruSegState *offsets_writer;
42 : SlruSegState *members_writer;
43 0 : char dir[MAXPGPATH] = {0};
44 0 : 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 0 : next_offset = 1;
52 :
53 : /* Prepare to write the new SLRU files */
54 0 : pg_sprintf(dir, "%s/pg_multixact/offsets", new_cluster.pgdata);
55 0 : offsets_writer = AllocSlruWrite(dir, false);
56 0 : SlruWriteSwitchPage(offsets_writer, MultiXactIdToOffsetPage(from_multi));
57 :
58 0 : pg_sprintf(dir, "%s/pg_multixact/members", new_cluster.pgdata);
59 0 : members_writer = AllocSlruWrite(dir, true /* use long segment names */ );
60 0 : 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 0 : if (to_multi != from_multi)
67 : {
68 : OldMultiXactReader *old_reader;
69 :
70 0 : old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
71 : old_cluster.controldata.chkpnt_nxtmulti,
72 0 : old_cluster.controldata.chkpnt_nxtmxoff);
73 :
74 0 : 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 0 : 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 0 : RecordMultiXactOffset(offsets_writer, multi,
99 0 : (multixid_valid || prev_multixid_valid) ? next_offset : 0);
100 :
101 : /* Write the members */
102 0 : if (multixid_valid)
103 : {
104 0 : RecordMultiXactMembers(members_writer, next_offset, 1, &member);
105 0 : next_offset += 1;
106 : }
107 :
108 : /* Advance to next multixid, handling wraparound */
109 0 : multi++;
110 0 : if (multi < FirstMultiXactId)
111 0 : multi = FirstMultiXactId;
112 0 : prev_multixid_valid = multixid_valid;
113 : }
114 :
115 0 : FreeOldMultiXactReader(old_reader);
116 : }
117 :
118 : /* Write the final 'next' offset to the last SLRU page */
119 0 : RecordMultiXactOffset(offsets_writer, to_multi,
120 : prev_multixid_valid ? next_offset : 0);
121 :
122 : /* Flush the last SLRU pages */
123 0 : FreeSlruWrite(offsets_writer);
124 0 : FreeSlruWrite(members_writer);
125 :
126 0 : return next_offset;
127 : }
128 :
129 :
130 : /*
131 : * Write one offset to the offset SLRU
132 : */
133 : static void
134 0 : RecordMultiXactOffset(SlruSegState *offsets_writer, MultiXactId multi,
135 : MultiXactOffset offset)
136 : {
137 : int64 pageno;
138 : int entryno;
139 : char *buf;
140 : MultiXactOffset *offptr;
141 :
142 0 : pageno = MultiXactIdToOffsetPage(multi);
143 0 : entryno = MultiXactIdToOffsetEntry(multi);
144 :
145 0 : buf = SlruWriteSwitchPage(offsets_writer, pageno);
146 0 : offptr = (MultiXactOffset *) buf;
147 0 : offptr[entryno] = offset;
148 0 : }
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 : */
155 : static void
156 0 : RecordMultiXactMembers(SlruSegState *members_writer,
157 : MultiXactOffset offset,
158 : int nmembers, MultiXactMember *members)
159 : {
160 0 : 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 0 : pageno = MXOffsetToMemberPage(offset);
174 0 : memberoff = MXOffsetToMemberOffset(offset);
175 0 : flagsoff = MXOffsetToFlagsOffset(offset);
176 0 : bshift = MXOffsetToFlagsBitShift(offset);
177 :
178 0 : buf = SlruWriteSwitchPage(members_writer, pageno);
179 :
180 0 : memberptr = (TransactionId *) (buf + memberoff);
181 :
182 0 : *memberptr = members[i].xid;
183 :
184 0 : flagsptr = (uint32 *) (buf + flagsoff);
185 :
186 0 : flagsval = *flagsptr;
187 0 : flagsval &= ~(((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift);
188 0 : flagsval |= (members[i].status << bshift);
189 0 : *flagsptr = flagsval;
190 : }
191 0 : }
|