Branch data 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-2026, 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.
29 : : *
30 : : * Returns the new nextOffset value; the caller should set it in the new
31 : : * control file. The new members always start from offset 1, regardless of
32 : : * the offset range used in the old cluster.
33 : : */
34 : : MultiXactOffset
35 : 0 : rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
36 : : {
37 : : MultiXactOffset next_offset;
38 : : SlruSegState *offsets_writer;
39 : : SlruSegState *members_writer;
40 : 0 : char dir[MAXPGPATH] = {0};
41 : 0 : bool prev_multixid_valid = false;
42 : : OldMultiXactReader *old_reader;
43 : :
44 : : /*
45 : : * The range of valid multi XIDs is unchanged by the conversion (they are
46 : : * referenced from the heap tables), but the members SLRU is rewritten to
47 : : * start from offset 1.
48 : : */
49 : 0 : next_offset = 1;
50 : :
51 : : /* Prepare to write the new SLRU files */
52 : 0 : pg_sprintf(dir, "%s/pg_multixact/offsets", new_cluster.pgdata);
53 : 0 : offsets_writer = AllocSlruWrite(dir, false);
54 : 0 : SlruWriteSwitchPage(offsets_writer, MultiXactIdToOffsetPage(from_multi));
55 : :
56 : 0 : pg_sprintf(dir, "%s/pg_multixact/members", new_cluster.pgdata);
57 : 0 : members_writer = AllocSlruWrite(dir, true /* use long segment names */ );
58 : 0 : SlruWriteSwitchPage(members_writer, MXOffsetToMemberPage(next_offset));
59 : :
60 : : /*
61 : : * Convert old multixids, if needed, by reading them one-by-one from the
62 : : * old cluster.
63 : : */
64 : 0 : old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
65 : : old_cluster.controldata.chkpnt_nxtmulti,
66 : 0 : old_cluster.controldata.chkpnt_nxtmxoff);
67 : :
68 [ # # ]: 0 : for (MultiXactId multi = from_multi; multi != to_multi;)
69 : : {
70 : : MultiXactMember member;
71 : : bool multixid_valid;
72 : :
73 : : /*
74 : : * Read this multixid's members.
75 : : *
76 : : * Locking-only XIDs that may be part of multi-xids don't matter after
77 : : * upgrade, as there can be no transactions running across upgrade. So
78 : : * as a small optimization, we only read one member from each
79 : : * multixid: the one updating one, or if there was no update,
80 : : * arbitrarily the first locking xid.
81 : : */
82 : 0 : multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
83 : :
84 : : /*
85 : : * Write the new offset to pg_multixact/offsets.
86 : : *
87 : : * Even if this multixid is invalid, we still need to write its offset
88 : : * if the *previous* multixid was valid. That's because when reading
89 : : * a multixid, the number of members is calculated from the difference
90 : : * between the two offsets.
91 : : */
92 [ # # ]: 0 : RecordMultiXactOffset(offsets_writer, multi,
93 [ # # ]: 0 : (multixid_valid || prev_multixid_valid) ? next_offset : 0);
94 : :
95 : : /* Write the members */
96 [ # # ]: 0 : if (multixid_valid)
97 : : {
98 : 0 : RecordMultiXactMembers(members_writer, next_offset, 1, &member);
99 : 0 : next_offset += 1;
100 : : }
101 : :
102 : : /* Advance to next multixid, handling wraparound */
103 : 0 : multi++;
104 [ # # ]: 0 : if (multi < FirstMultiXactId)
105 : 0 : multi = FirstMultiXactId;
106 : 0 : prev_multixid_valid = multixid_valid;
107 : : }
108 : :
109 : 0 : FreeOldMultiXactReader(old_reader);
110 : :
111 : : /* Write the final 'next' offset to the last SLRU page */
112 [ # # ]: 0 : RecordMultiXactOffset(offsets_writer, to_multi,
113 : : prev_multixid_valid ? next_offset : 0);
114 : :
115 : : /* Flush the last SLRU pages */
116 : 0 : FreeSlruWrite(offsets_writer);
117 : 0 : FreeSlruWrite(members_writer);
118 : :
119 : 0 : return next_offset;
120 : : }
121 : :
122 : :
123 : : /*
124 : : * Write one offset to the offset SLRU
125 : : */
126 : : static void
127 : 0 : RecordMultiXactOffset(SlruSegState *offsets_writer, MultiXactId multi,
128 : : MultiXactOffset offset)
129 : : {
130 : : int64 pageno;
131 : : int entryno;
132 : : char *buf;
133 : : MultiXactOffset *offptr;
134 : :
135 : 0 : pageno = MultiXactIdToOffsetPage(multi);
136 : 0 : entryno = MultiXactIdToOffsetEntry(multi);
137 : :
138 : 0 : buf = SlruWriteSwitchPage(offsets_writer, pageno);
139 : 0 : offptr = (MultiXactOffset *) buf;
140 : 0 : offptr[entryno] = offset;
141 : 0 : }
142 : :
143 : : /*
144 : : * Write the members for one multixid in the members SLRU
145 : : *
146 : : * (Currently, this is only ever called with nmembers == 1)
147 : : */
148 : : static void
149 : 0 : RecordMultiXactMembers(SlruSegState *members_writer,
150 : : MultiXactOffset offset,
151 : : int nmembers, MultiXactMember *members)
152 : : {
153 [ # # ]: 0 : for (int i = 0; i < nmembers; i++, offset++)
154 : : {
155 : : int64 pageno;
156 : : char *buf;
157 : : TransactionId *memberptr;
158 : : uint32 *flagsptr;
159 : : uint32 flagsval;
160 : : int bshift;
161 : : int flagsoff;
162 : : int memberoff;
163 : :
164 : : Assert(members[i].status <= MultiXactStatusUpdate);
165 : :
166 : 0 : pageno = MXOffsetToMemberPage(offset);
167 : 0 : memberoff = MXOffsetToMemberOffset(offset);
168 : 0 : flagsoff = MXOffsetToFlagsOffset(offset);
169 : 0 : bshift = MXOffsetToFlagsBitShift(offset);
170 : :
171 : 0 : buf = SlruWriteSwitchPage(members_writer, pageno);
172 : :
173 : 0 : memberptr = (TransactionId *) (buf + memberoff);
174 : :
175 : 0 : *memberptr = members[i].xid;
176 : :
177 : 0 : flagsptr = (uint32 *) (buf + flagsoff);
178 : :
179 : 0 : flagsval = *flagsptr;
180 : 0 : flagsval &= ~(((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift);
181 : 0 : flagsval |= (members[i].status << bshift);
182 : 0 : *flagsptr = flagsval;
183 : : }
184 : 0 : }
|