PostgreSQL Source Code git master
Loading...
Searching...
No Matches
multixact_read_v18.c File Reference
#include "postgres_fe.h"
#include "multixact_read_v18.h"
#include "pg_upgrade.h"
Include dependency graph for multixact_read_v18.c:

Go to the source code of this file.

Macros

#define MultiXactOffset   should_not_be_used
 
#define MULTIXACT_OFFSETS_PER_PAGE   (BLCKSZ / sizeof(MultiXactOffset32))
 
#define MXACT_MEMBER_BITS_PER_XACT   8
 
#define MXACT_MEMBER_FLAGS_PER_BYTE   1
 
#define MXACT_MEMBER_XACT_BITMASK   ((1 << MXACT_MEMBER_BITS_PER_XACT) - 1)
 
#define MULTIXACT_FLAGBYTES_PER_GROUP   4
 
#define MULTIXACT_MEMBERS_PER_MEMBERGROUP    (MULTIXACT_FLAGBYTES_PER_GROUP * MXACT_MEMBER_FLAGS_PER_BYTE)
 
#define MULTIXACT_MEMBERGROUP_SIZE    (sizeof(TransactionId) * MULTIXACT_MEMBERS_PER_MEMBERGROUP + MULTIXACT_FLAGBYTES_PER_GROUP)
 
#define MULTIXACT_MEMBERGROUPS_PER_PAGE   (BLCKSZ / MULTIXACT_MEMBERGROUP_SIZE)
 
#define MULTIXACT_MEMBERS_PER_PAGE    (MULTIXACT_MEMBERGROUPS_PER_PAGE * MULTIXACT_MEMBERS_PER_MEMBERGROUP)
 

Functions

static int64 MultiXactIdToOffsetPage (MultiXactId multi)
 
static int MultiXactIdToOffsetEntry (MultiXactId multi)
 
static int64 MXOffsetToMemberPage (MultiXactOffset32 offset)
 
static int MXOffsetToFlagsOffset (MultiXactOffset32 offset)
 
static int MXOffsetToMemberOffset (MultiXactOffset32 offset)
 
static int MXOffsetToFlagsBitShift (MultiXactOffset32 offset)
 
OldMultiXactReaderAllocOldMultiXactRead (char *pgdata, MultiXactId nextMulti, MultiXactOffset32 nextOffset)
 
bool GetOldMultiXactIdSingleMember (OldMultiXactReader *state, MultiXactId multi, MultiXactMember *member)
 
void FreeOldMultiXactReader (OldMultiXactReader *state)
 

Macro Definition Documentation

◆ MULTIXACT_FLAGBYTES_PER_GROUP

#define MULTIXACT_FLAGBYTES_PER_GROUP   4

Definition at line 65 of file multixact_read_v18.c.

◆ MULTIXACT_MEMBERGROUP_SIZE

Definition at line 69 of file multixact_read_v18.c.

78{
79 return offset / MULTIXACT_MEMBERS_PER_PAGE;
80}
81
82/* Location (byte offset within page) of flag word for a given member */
83static inline int
85{
89
90 return byteoff;
91}
92
93/* Location (byte offset within page) of TransactionId of given member */
94static inline int
96{
98
99 return MXOffsetToFlagsOffset(offset) +
102}
103
104static inline int
106{
109
110 return bshift;
111}
112
113/*
114 * Construct reader of old multixacts.
115 *
116 * Returns the malloced memory used by the all other calls in this module.
117 */
119AllocOldMultiXactRead(char *pgdata, MultiXactId nextMulti,
120 MultiXactOffset32 nextOffset)
121{
123 char dir[MAXPGPATH] = {0};
124
125 state->nextMXact = nextMulti;
126 state->nextOffset = nextOffset;
127
128 pg_sprintf(dir, "%s/pg_multixact/offsets", pgdata);
129 state->offset = AllocSlruRead(dir, false);
130
131 pg_sprintf(dir, "%s/pg_multixact/members", pgdata);
132 state->members = AllocSlruRead(dir, false);
133
134 return state;
135}
136
137/*
138 * This is a simplified version of the GetMultiXactIdMembers() server
139 * function:
140 *
141 * - Only return the updating member, if any. Upgrade only cares about the
142 * updaters. If there is no updating member, return somewhat arbitrarily
143 * the first locking-only member, because we don't have any way to represent
144 * "no members".
145 *
146 * - Because there's no concurrent activity, we don't need to worry about
147 * locking and some corner cases.
148 *
149 * - Don't bail out on invalid entries that could've been left behind after a
150 * server crash. Such multixids won't appear anywhere else on disk, so the
151 * server will never try to read them. During upgrade, however, we scan
152 * through all multixids in order, and will encounter such invalid but
153 * unreferenced multixids too. We try to distinguish between entries that
154 * are invalid because of missed disk writes, like entries with zeros in
155 * offsets or members, and entries that look corrupt in other ways that
156 * should not happen even on a server crash.
157 *
158 * Returns true on success, false if the multixact was invalid.
159 */
160bool
162 MultiXactMember *member)
163{
164 MultiXactId nextMXact,
165 nextOffset,
166 tmpMXact;
167 int64 pageno,
169 int entryno,
170 length;
171 char *buf;
173 offset;
177
178 nextMXact = state->nextMXact;
179 nextOffset = state->nextOffset;
180
181 /*
182 * Comment copied from GetMultiXactIdMembers in PostgreSQL v18
183 * multixact.c:
184 *
185 * Find out the offset at which we need to start reading MultiXactMembers
186 * and the number of members in the multixact. We determine the latter as
187 * the difference between this multixact's starting offset and the next
188 * one's. However, there are some corner cases to worry about:
189 *
190 * 1. This multixact may be the latest one created, in which case there is
191 * no next one to look at. The next multixact's offset should be set
192 * already, as we set it in RecordNewMultiXact(), but we used to not do
193 * that in older minor versions. To cope with that case, if this
194 * multixact is the latest one created, use the nextOffset value we read
195 * above as the endpoint.
196 *
197 * 2. Because GetNewMultiXactId skips over offset zero, to reserve zero
198 * for to mean "unset", there is an ambiguity near the point of offset
199 * wraparound. If we see next multixact's offset is one, is that our
200 * multixact's actual endpoint, or did it end at zero with a subsequent
201 * increment? We handle this using the knowledge that if the zero'th
202 * member slot wasn't filled, it'll contain zero, and zero isn't a valid
203 * transaction ID so it can't be a multixact member. Therefore, if we
204 * read a zero from the members array, just ignore it.
205 */
206
207 pageno = MultiXactIdToOffsetPage(multi);
209
210 buf = SlruReadSwitchPage(state->offset, pageno);
212 offptr += entryno;
213 offset = *offptr;
214
215 if (offset == 0)
216 {
217 /* Invalid entry. These can be left behind on a server crash. */
218 return false;
219 }
220
221 /*
222 * Use the same increment rule as GetNewMultiXactId(), that is, don't
223 * handle wraparound explicitly until needed.
224 */
225 tmpMXact = multi + 1;
226
227 if (nextMXact == tmpMXact)
228 {
229 /* Corner case 1: there is no next multixact */
230 nextMXOffset = nextOffset;
231 }
232 else
233 {
234 /* handle wraparound if needed */
237
238 prev_pageno = pageno;
239
242
243 if (pageno != prev_pageno)
244 buf = SlruReadSwitchPage(state->offset, pageno);
245
247 offptr += entryno;
249 }
250
251 if (nextMXOffset == 0)
252 {
253 /* Invalid entry. These can be left behind on a server crash. */
254 return false;
255 }
256 length = nextMXOffset - offset;
257
258 if (length < 0)
259 {
260 /*
261 * This entry is corrupt. We should not see these even after a server
262 * crash.
263 */
264 pg_fatal("multixact %u has an invalid length (%d)", multi, length);
265 }
266 if (length == 0)
267 {
268 /*
269 * Invalid entry. The server never writes multixids with zero
270 * members, but it's not clear if a server crash or using pg_resetwal
271 * could leave them behind. Seems best to accept them.
272 */
273 return false;
274 }
275
276 /* read the members */
277 prev_pageno = -1;
278 for (int i = 0; i < length; i++, offset++)
279 {
282 int flagsoff;
283 int bshift;
284 int memberoff;
285 MultiXactStatus status;
286
287 pageno = MXOffsetToMemberPage(offset);
289
290 if (pageno != prev_pageno)
291 {
292 buf = SlruReadSwitchPage(state->members, pageno);
293 prev_pageno = pageno;
294 }
295
298 {
299 /*
300 * Corner case 2: offset must have wrapped around to unused slot
301 * zero.
302 */
303 if (offset == 0)
304 continue;
305
306 /*
307 * Otherwise this is an invalid entry that should not be
308 * referenced from anywhere in the heap. These can be left behind
309 * on a server crash. We could return 'false' here, but we prefer
310 * to continue reading the members and converting them the best we
311 * can, to preserve evidence in case this is corruption that
312 * should not have happened.
313 */
314 }
315
318 flagsptr = (uint32 *) (buf + flagsoff);
319
321
322 /*
323 * Remember the updating XID among the members, or first locking XID
324 * if no updating XID.
325 */
326 if (ISUPDATE_from_mxstatus(status))
327 {
328 /* sanity check */
330 {
331 /*
332 * We don't expect to see more than one updating member, even
333 * if the server had crashed.
334 */
335 pg_fatal("multixact %u has more than one updating member",
336 multi);
337 }
339 result_status = status;
340 }
342 {
344 result_status = status;
345 }
346 }
347
348 member->xid = result_xid;
349 member->status = result_status;
350 return true;
351}
352
353/*
354 * Frees the malloced reader.
355 */
356void
358{
359 FreeSlruRead(state->offset);
360 FreeSlruRead(state->members);
361
362 pfree(state);
363}
int64_t int64
Definition c.h:543
TransactionId MultiXactId
Definition c.h:676
uint32_t uint32
Definition c.h:546
uint32 TransactionId
Definition c.h:666
#define pg_malloc_object(type)
Definition fe_memutils.h:50
int i
Definition isn.c:77
void pfree(void *pointer)
Definition mcxt.c:1616
#define FirstMultiXactId
Definition multixact.h:26
MultiXactStatus
Definition multixact.h:37
#define ISUPDATE_from_mxstatus(status)
Definition multixact.h:51
#define MXACT_MEMBER_BITS_PER_XACT
static int MXOffsetToFlagsBitShift(MultiXactOffset32 offset)
#define MXACT_MEMBER_XACT_BITMASK
#define MULTIXACT_FLAGBYTES_PER_GROUP
static int64 MXOffsetToMemberPage(MultiXactOffset32 offset)
bool GetOldMultiXactIdSingleMember(OldMultiXactReader *state, MultiXactId multi, MultiXactMember *member)
static int MXOffsetToMemberOffset(MultiXactOffset32 offset)
static int MultiXactIdToOffsetEntry(MultiXactId multi)
#define MULTIXACT_MEMBERGROUPS_PER_PAGE
static int64 MultiXactIdToOffsetPage(MultiXactId multi)
OldMultiXactReader * AllocOldMultiXactRead(char *pgdata, MultiXactId nextMulti, MultiXactOffset32 nextOffset)
#define MULTIXACT_MEMBERGROUP_SIZE
#define MULTIXACT_MEMBERS_PER_MEMBERGROUP
#define MULTIXACT_MEMBERS_PER_PAGE
void FreeOldMultiXactReader(OldMultiXactReader *state)
static int MXOffsetToFlagsOffset(MultiXactOffset32 offset)
uint32 MultiXactOffset32
#define pg_fatal(...)
#define MAXPGPATH
static char buf[DEFAULT_XLOG_SEG_SIZE]
int int int int pg_sprintf(char *str, const char *fmt,...) pg_attribute_printf(2
static int fb(int x)
SlruSegState * AllocSlruRead(const char *dir, bool long_segment_names)
Definition slru_io.c:62
void FreeSlruRead(SlruSegState *state)
Definition slru_io.c:153
static char * SlruReadSwitchPage(SlruSegState *state, uint64 pageno)
Definition slru_io.h:33
TransactionId xid
Definition multixact.h:57
MultiXactStatus status
Definition multixact.h:58
#define InvalidTransactionId
Definition transam.h:31
#define TransactionIdIsValid(xid)
Definition transam.h:41

◆ MULTIXACT_MEMBERGROUPS_PER_PAGE

#define MULTIXACT_MEMBERGROUPS_PER_PAGE   (BLCKSZ / MULTIXACT_MEMBERGROUP_SIZE)

Definition at line 71 of file multixact_read_v18.c.

◆ MULTIXACT_MEMBERS_PER_MEMBERGROUP

#define MULTIXACT_MEMBERS_PER_MEMBERGROUP    (MULTIXACT_FLAGBYTES_PER_GROUP * MXACT_MEMBER_FLAGS_PER_BYTE)

Definition at line 66 of file multixact_read_v18.c.

◆ MULTIXACT_MEMBERS_PER_PAGE

Definition at line 72 of file multixact_read_v18.c.

◆ MULTIXACT_OFFSETS_PER_PAGE

#define MULTIXACT_OFFSETS_PER_PAGE   (BLCKSZ / sizeof(MultiXactOffset32))

Definition at line 33 of file multixact_read_v18.c.

◆ MultiXactOffset

◆ MXACT_MEMBER_BITS_PER_XACT

#define MXACT_MEMBER_BITS_PER_XACT   8

Definition at line 60 of file multixact_read_v18.c.

◆ MXACT_MEMBER_FLAGS_PER_BYTE

#define MXACT_MEMBER_FLAGS_PER_BYTE   1

Definition at line 61 of file multixact_read_v18.c.

◆ MXACT_MEMBER_XACT_BITMASK

#define MXACT_MEMBER_XACT_BITMASK   ((1 << MXACT_MEMBER_BITS_PER_XACT) - 1)

Definition at line 62 of file multixact_read_v18.c.

Function Documentation

◆ AllocOldMultiXactRead()

OldMultiXactReader * AllocOldMultiXactRead ( char pgdata,
MultiXactId  nextMulti,
MultiXactOffset32  nextOffset 
)

Definition at line 119 of file multixact_read_v18.c.

121{
123 char dir[MAXPGPATH] = {0};
124
125 state->nextMXact = nextMulti;
126 state->nextOffset = nextOffset;
127
128 pg_sprintf(dir, "%s/pg_multixact/offsets", pgdata);
129 state->offset = AllocSlruRead(dir, false);
130
131 pg_sprintf(dir, "%s/pg_multixact/members", pgdata);
132 state->members = AllocSlruRead(dir, false);
133
134 return state;
135}

References AllocSlruRead(), MAXPGPATH, pg_malloc_object, and pg_sprintf().

Referenced by rewrite_multixacts().

◆ FreeOldMultiXactReader()

void FreeOldMultiXactReader ( OldMultiXactReader state)

Definition at line 357 of file multixact_read_v18.c.

358{
359 FreeSlruRead(state->offset);
360 FreeSlruRead(state->members);
361
362 pfree(state);
363}

References FreeSlruRead(), and pfree().

Referenced by rewrite_multixacts().

◆ GetOldMultiXactIdSingleMember()

bool GetOldMultiXactIdSingleMember ( OldMultiXactReader state,
MultiXactId  multi,
MultiXactMember member 
)

Definition at line 161 of file multixact_read_v18.c.

163{
164 MultiXactId nextMXact,
165 nextOffset,
166 tmpMXact;
167 int64 pageno,
169 int entryno,
170 length;
171 char *buf;
173 offset;
177
178 nextMXact = state->nextMXact;
179 nextOffset = state->nextOffset;
180
181 /*
182 * Comment copied from GetMultiXactIdMembers in PostgreSQL v18
183 * multixact.c:
184 *
185 * Find out the offset at which we need to start reading MultiXactMembers
186 * and the number of members in the multixact. We determine the latter as
187 * the difference between this multixact's starting offset and the next
188 * one's. However, there are some corner cases to worry about:
189 *
190 * 1. This multixact may be the latest one created, in which case there is
191 * no next one to look at. The next multixact's offset should be set
192 * already, as we set it in RecordNewMultiXact(), but we used to not do
193 * that in older minor versions. To cope with that case, if this
194 * multixact is the latest one created, use the nextOffset value we read
195 * above as the endpoint.
196 *
197 * 2. Because GetNewMultiXactId skips over offset zero, to reserve zero
198 * for to mean "unset", there is an ambiguity near the point of offset
199 * wraparound. If we see next multixact's offset is one, is that our
200 * multixact's actual endpoint, or did it end at zero with a subsequent
201 * increment? We handle this using the knowledge that if the zero'th
202 * member slot wasn't filled, it'll contain zero, and zero isn't a valid
203 * transaction ID so it can't be a multixact member. Therefore, if we
204 * read a zero from the members array, just ignore it.
205 */
206
207 pageno = MultiXactIdToOffsetPage(multi);
209
210 buf = SlruReadSwitchPage(state->offset, pageno);
212 offptr += entryno;
213 offset = *offptr;
214
215 if (offset == 0)
216 {
217 /* Invalid entry. These can be left behind on a server crash. */
218 return false;
219 }
220
221 /*
222 * Use the same increment rule as GetNewMultiXactId(), that is, don't
223 * handle wraparound explicitly until needed.
224 */
225 tmpMXact = multi + 1;
226
227 if (nextMXact == tmpMXact)
228 {
229 /* Corner case 1: there is no next multixact */
230 nextMXOffset = nextOffset;
231 }
232 else
233 {
234 /* handle wraparound if needed */
237
238 prev_pageno = pageno;
239
242
243 if (pageno != prev_pageno)
244 buf = SlruReadSwitchPage(state->offset, pageno);
245
247 offptr += entryno;
249 }
250
251 if (nextMXOffset == 0)
252 {
253 /* Invalid entry. These can be left behind on a server crash. */
254 return false;
255 }
256 length = nextMXOffset - offset;
257
258 if (length < 0)
259 {
260 /*
261 * This entry is corrupt. We should not see these even after a server
262 * crash.
263 */
264 pg_fatal("multixact %u has an invalid length (%d)", multi, length);
265 }
266 if (length == 0)
267 {
268 /*
269 * Invalid entry. The server never writes multixids with zero
270 * members, but it's not clear if a server crash or using pg_resetwal
271 * could leave them behind. Seems best to accept them.
272 */
273 return false;
274 }
275
276 /* read the members */
277 prev_pageno = -1;
278 for (int i = 0; i < length; i++, offset++)
279 {
282 int flagsoff;
283 int bshift;
284 int memberoff;
285 MultiXactStatus status;
286
287 pageno = MXOffsetToMemberPage(offset);
289
290 if (pageno != prev_pageno)
291 {
292 buf = SlruReadSwitchPage(state->members, pageno);
293 prev_pageno = pageno;
294 }
295
298 {
299 /*
300 * Corner case 2: offset must have wrapped around to unused slot
301 * zero.
302 */
303 if (offset == 0)
304 continue;
305
306 /*
307 * Otherwise this is an invalid entry that should not be
308 * referenced from anywhere in the heap. These can be left behind
309 * on a server crash. We could return 'false' here, but we prefer
310 * to continue reading the members and converting them the best we
311 * can, to preserve evidence in case this is corruption that
312 * should not have happened.
313 */
314 }
315
318 flagsptr = (uint32 *) (buf + flagsoff);
319
321
322 /*
323 * Remember the updating XID among the members, or first locking XID
324 * if no updating XID.
325 */
326 if (ISUPDATE_from_mxstatus(status))
327 {
328 /* sanity check */
330 {
331 /*
332 * We don't expect to see more than one updating member, even
333 * if the server had crashed.
334 */
335 pg_fatal("multixact %u has more than one updating member",
336 multi);
337 }
339 result_status = status;
340 }
342 {
344 result_status = status;
345 }
346 }
347
348 member->xid = result_xid;
349 member->status = result_status;
350 return true;
351}

References buf, fb(), FirstMultiXactId, i, InvalidTransactionId, ISUPDATE_from_mxstatus, MultiXactIdToOffsetEntry(), MultiXactIdToOffsetPage(), MXACT_MEMBER_XACT_BITMASK, MXOffsetToFlagsBitShift(), MXOffsetToFlagsOffset(), MXOffsetToMemberOffset(), MXOffsetToMemberPage(), pg_fatal, SlruReadSwitchPage(), MultiXactMember::status, TransactionIdIsValid, and MultiXactMember::xid.

Referenced by rewrite_multixacts().

◆ MultiXactIdToOffsetEntry()

static int MultiXactIdToOffsetEntry ( MultiXactId  multi)
inlinestatic

◆ MultiXactIdToOffsetPage()

◆ MXOffsetToFlagsBitShift()

◆ MXOffsetToFlagsOffset()

◆ MXOffsetToMemberOffset()

◆ MXOffsetToMemberPage()