Age Owner Branch data TLA 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
7 heikki.linnakangas@i 38 :UNC 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 [ # # ]: 0 : 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 : }
|