PostgreSQL Source Code git master
heapdesc.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * heapdesc.c
4 * rmgr descriptor routines for access/heap/heapam.c
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/access/rmgrdesc/heapdesc.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include "access/heapam_xlog.h"
19#include "storage/standbydefs.h"
20
21/*
22 * NOTE: "keyname" argument cannot have trailing spaces or punctuation
23 * characters
24 */
25static void
26infobits_desc(StringInfo buf, uint8 infobits, const char *keyname)
27{
28 appendStringInfo(buf, "%s: [", keyname);
29
30 Assert(buf->data[buf->len - 1] != ' ');
31
32 if (infobits & XLHL_XMAX_IS_MULTI)
33 appendStringInfoString(buf, "IS_MULTI, ");
34 if (infobits & XLHL_XMAX_LOCK_ONLY)
35 appendStringInfoString(buf, "LOCK_ONLY, ");
36 if (infobits & XLHL_XMAX_EXCL_LOCK)
37 appendStringInfoString(buf, "EXCL_LOCK, ");
38 if (infobits & XLHL_XMAX_KEYSHR_LOCK)
39 appendStringInfoString(buf, "KEYSHR_LOCK, ");
40 if (infobits & XLHL_KEYS_UPDATED)
41 appendStringInfoString(buf, "KEYS_UPDATED, ");
42
43 if (buf->data[buf->len - 1] == ' ')
44 {
45 /* Truncate-away final unneeded ", " */
46 Assert(buf->data[buf->len - 2] == ',');
47 buf->len -= 2;
48 buf->data[buf->len] = '\0';
49 }
50
52}
53
54static void
56{
57 appendStringInfoString(buf, "flags: [");
58
59 if (flags & XLH_TRUNCATE_CASCADE)
60 appendStringInfoString(buf, "CASCADE, ");
61 if (flags & XLH_TRUNCATE_RESTART_SEQS)
62 appendStringInfoString(buf, "RESTART_SEQS, ");
63
64 if (buf->data[buf->len - 1] == ' ')
65 {
66 /* Truncate-away final unneeded ", " */
67 Assert(buf->data[buf->len - 2] == ',');
68 buf->len -= 2;
69 buf->data[buf->len] = '\0';
70 }
71
73}
74
75static void
77{
79 OffsetNumber **offsets = data;
80
81 appendStringInfo(buf, "{ xmax: %u, infomask: %u, infomask2: %u, ntuples: %u",
82 new_plan->xmax,
83 new_plan->t_infomask, new_plan->t_infomask2,
84 new_plan->ntuples);
85
86 appendStringInfoString(buf, ", offsets:");
87 array_desc(buf, *offsets, sizeof(OffsetNumber), new_plan->ntuples,
88 &offset_elem_desc, NULL);
89
90 *offsets += new_plan->ntuples;
91
93}
94
95
96/*
97 * Given a MAXALIGNed buffer returned by XLogRecGetBlockData() and pointed to
98 * by cursor and any xl_heap_prune flags, deserialize the arrays of
99 * OffsetNumbers contained in an XLOG_HEAP2_PRUNE_* record.
100 *
101 * This is in heapdesc.c so it can be shared between heap2_redo and heap2_desc
102 * code, the latter of which is used in frontend (pg_waldump) code.
103 */
104void
106 int *nplans, xlhp_freeze_plan **plans,
107 OffsetNumber **frz_offsets,
108 int *nredirected, OffsetNumber **redirected,
109 int *ndead, OffsetNumber **nowdead,
110 int *nunused, OffsetNumber **nowunused)
111{
112 if (flags & XLHP_HAS_FREEZE_PLANS)
113 {
114 xlhp_freeze_plans *freeze_plans = (xlhp_freeze_plans *) cursor;
115
116 *nplans = freeze_plans->nplans;
117 Assert(*nplans > 0);
118 *plans = freeze_plans->plans;
119
120 cursor += offsetof(xlhp_freeze_plans, plans);
121 cursor += sizeof(xlhp_freeze_plan) * *nplans;
122 }
123 else
124 {
125 *nplans = 0;
126 *plans = NULL;
127 }
128
129 if (flags & XLHP_HAS_REDIRECTIONS)
130 {
132
133 *nredirected = subrecord->ntargets;
134 Assert(*nredirected > 0);
135 *redirected = &subrecord->data[0];
136
137 cursor += offsetof(xlhp_prune_items, data);
138 cursor += sizeof(OffsetNumber[2]) * *nredirected;
139 }
140 else
141 {
142 *nredirected = 0;
143 *redirected = NULL;
144 }
145
146 if (flags & XLHP_HAS_DEAD_ITEMS)
147 {
149
150 *ndead = subrecord->ntargets;
151 Assert(*ndead > 0);
152 *nowdead = subrecord->data;
153
154 cursor += offsetof(xlhp_prune_items, data);
155 cursor += sizeof(OffsetNumber) * *ndead;
156 }
157 else
158 {
159 *ndead = 0;
160 *nowdead = NULL;
161 }
162
163 if (flags & XLHP_HAS_NOW_UNUSED_ITEMS)
164 {
166
167 *nunused = subrecord->ntargets;
168 Assert(*nunused > 0);
169 *nowunused = subrecord->data;
170
171 cursor += offsetof(xlhp_prune_items, data);
172 cursor += sizeof(OffsetNumber) * *nunused;
173 }
174 else
175 {
176 *nunused = 0;
177 *nowunused = NULL;
178 }
179
180 *frz_offsets = (OffsetNumber *) cursor;
181}
182
183void
185{
186 char *rec = XLogRecGetData(record);
187 uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
188
189 info &= XLOG_HEAP_OPMASK;
190 if (info == XLOG_HEAP_INSERT)
191 {
192 xl_heap_insert *xlrec = (xl_heap_insert *) rec;
193
194 appendStringInfo(buf, "off: %u, flags: 0x%02X",
195 xlrec->offnum,
196 xlrec->flags);
197 }
198 else if (info == XLOG_HEAP_DELETE)
199 {
200 xl_heap_delete *xlrec = (xl_heap_delete *) rec;
201
202 appendStringInfo(buf, "xmax: %u, off: %u, ",
203 xlrec->xmax, xlrec->offnum);
204 infobits_desc(buf, xlrec->infobits_set, "infobits");
205 appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
206 }
207 else if (info == XLOG_HEAP_UPDATE)
208 {
209 xl_heap_update *xlrec = (xl_heap_update *) rec;
210
211 appendStringInfo(buf, "old_xmax: %u, old_off: %u, ",
212 xlrec->old_xmax, xlrec->old_offnum);
213 infobits_desc(buf, xlrec->old_infobits_set, "old_infobits");
214 appendStringInfo(buf, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
215 xlrec->flags, xlrec->new_xmax, xlrec->new_offnum);
216 }
217 else if (info == XLOG_HEAP_HOT_UPDATE)
218 {
219 xl_heap_update *xlrec = (xl_heap_update *) rec;
220
221 appendStringInfo(buf, "old_xmax: %u, old_off: %u, ",
222 xlrec->old_xmax, xlrec->old_offnum);
223 infobits_desc(buf, xlrec->old_infobits_set, "old_infobits");
224 appendStringInfo(buf, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
225 xlrec->flags, xlrec->new_xmax, xlrec->new_offnum);
226 }
227 else if (info == XLOG_HEAP_TRUNCATE)
228 {
229 xl_heap_truncate *xlrec = (xl_heap_truncate *) rec;
230
232 appendStringInfo(buf, ", nrelids: %u", xlrec->nrelids);
233 appendStringInfoString(buf, ", relids:");
234 array_desc(buf, xlrec->relids, sizeof(Oid), xlrec->nrelids,
235 &oid_elem_desc, NULL);
236 }
237 else if (info == XLOG_HEAP_CONFIRM)
238 {
239 xl_heap_confirm *xlrec = (xl_heap_confirm *) rec;
240
241 appendStringInfo(buf, "off: %u", xlrec->offnum);
242 }
243 else if (info == XLOG_HEAP_LOCK)
244 {
245 xl_heap_lock *xlrec = (xl_heap_lock *) rec;
246
247 appendStringInfo(buf, "xmax: %u, off: %u, ",
248 xlrec->xmax, xlrec->offnum);
249 infobits_desc(buf, xlrec->infobits_set, "infobits");
250 appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
251 }
252 else if (info == XLOG_HEAP_INPLACE)
253 {
254 xl_heap_inplace *xlrec = (xl_heap_inplace *) rec;
255
256 appendStringInfo(buf, "off: %u", xlrec->offnum);
258 xlrec->dbId, xlrec->tsId,
259 xlrec->relcacheInitFileInval);
260 }
261}
262
263void
265{
266 char *rec = XLogRecGetData(record);
267 uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
268
269 info &= XLOG_HEAP_OPMASK;
270 if (info == XLOG_HEAP2_PRUNE_ON_ACCESS ||
273 {
274 xl_heap_prune *xlrec = (xl_heap_prune *) rec;
275
276 if (xlrec->flags & XLHP_HAS_CONFLICT_HORIZON)
277 {
278 TransactionId conflict_xid;
279
280 memcpy(&conflict_xid, rec + SizeOfHeapPrune, sizeof(TransactionId));
281
282 appendStringInfo(buf, "snapshotConflictHorizon: %u",
283 conflict_xid);
284 }
285
286 appendStringInfo(buf, ", isCatalogRel: %c",
287 xlrec->flags & XLHP_IS_CATALOG_REL ? 'T' : 'F');
288
289 if (XLogRecHasBlockData(record, 0))
290 {
291 Size datalen;
292 OffsetNumber *redirected;
293 OffsetNumber *nowdead;
294 OffsetNumber *nowunused;
295 int nredirected;
296 int nunused;
297 int ndead;
298 int nplans;
299 xlhp_freeze_plan *plans;
300 OffsetNumber *frz_offsets;
301
302 char *cursor = XLogRecGetBlockData(record, 0, &datalen);
303
305 &nplans, &plans, &frz_offsets,
306 &nredirected, &redirected,
307 &ndead, &nowdead,
308 &nunused, &nowunused);
309
310 appendStringInfo(buf, ", nplans: %u, nredirected: %u, ndead: %u, nunused: %u",
311 nplans, nredirected, ndead, nunused);
312
313 if (nplans > 0)
314 {
315 appendStringInfoString(buf, ", plans:");
316 array_desc(buf, plans, sizeof(xlhp_freeze_plan), nplans,
317 &plan_elem_desc, &frz_offsets);
318 }
319
320 if (nredirected > 0)
321 {
322 appendStringInfoString(buf, ", redirected:");
323 array_desc(buf, redirected, sizeof(OffsetNumber) * 2,
324 nredirected, &redirect_elem_desc, NULL);
325 }
326
327 if (ndead > 0)
328 {
329 appendStringInfoString(buf, ", dead:");
330 array_desc(buf, nowdead, sizeof(OffsetNumber), ndead,
331 &offset_elem_desc, NULL);
332 }
333
334 if (nunused > 0)
335 {
336 appendStringInfoString(buf, ", unused:");
337 array_desc(buf, nowunused, sizeof(OffsetNumber), nunused,
338 &offset_elem_desc, NULL);
339 }
340 }
341 }
342 else if (info == XLOG_HEAP2_VISIBLE)
343 {
344 xl_heap_visible *xlrec = (xl_heap_visible *) rec;
345
346 appendStringInfo(buf, "snapshotConflictHorizon: %u, flags: 0x%02X",
347 xlrec->snapshotConflictHorizon, xlrec->flags);
348 }
349 else if (info == XLOG_HEAP2_MULTI_INSERT)
350 {
352 bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
353
354 appendStringInfo(buf, "ntuples: %d, flags: 0x%02X", xlrec->ntuples,
355 xlrec->flags);
356
357 if (XLogRecHasBlockData(record, 0) && !isinit)
358 {
359 appendStringInfoString(buf, ", offsets:");
360 array_desc(buf, xlrec->offsets, sizeof(OffsetNumber),
361 xlrec->ntuples, &offset_elem_desc, NULL);
362 }
363 }
364 else if (info == XLOG_HEAP2_LOCK_UPDATED)
365 {
367
368 appendStringInfo(buf, "xmax: %u, off: %u, ",
369 xlrec->xmax, xlrec->offnum);
370 infobits_desc(buf, xlrec->infobits_set, "infobits");
371 appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
372 }
373 else if (info == XLOG_HEAP2_NEW_CID)
374 {
375 xl_heap_new_cid *xlrec = (xl_heap_new_cid *) rec;
376
377 appendStringInfo(buf, "rel: %u/%u/%u, tid: %u/%u",
378 xlrec->target_locator.spcOid,
379 xlrec->target_locator.dbOid,
383 appendStringInfo(buf, ", cmin: %u, cmax: %u, combo: %u",
384 xlrec->cmin, xlrec->cmax, xlrec->combocid);
385 }
386}
387
388const char *
390{
391 const char *id = NULL;
392
393 switch (info & ~XLR_INFO_MASK)
394 {
395 case XLOG_HEAP_INSERT:
396 id = "INSERT";
397 break;
399 id = "INSERT+INIT";
400 break;
401 case XLOG_HEAP_DELETE:
402 id = "DELETE";
403 break;
404 case XLOG_HEAP_UPDATE:
405 id = "UPDATE";
406 break;
408 id = "UPDATE+INIT";
409 break;
411 id = "HOT_UPDATE";
412 break;
414 id = "HOT_UPDATE+INIT";
415 break;
417 id = "TRUNCATE";
418 break;
420 id = "HEAP_CONFIRM";
421 break;
422 case XLOG_HEAP_LOCK:
423 id = "LOCK";
424 break;
426 id = "INPLACE";
427 break;
428 }
429
430 return id;
431}
432
433const char *
435{
436 const char *id = NULL;
437
438 switch (info & ~XLR_INFO_MASK)
439 {
441 id = "PRUNE_ON_ACCESS";
442 break;
444 id = "PRUNE_VACUUM_SCAN";
445 break;
447 id = "PRUNE_VACUUM_CLEANUP";
448 break;
450 id = "VISIBLE";
451 break;
453 id = "MULTI_INSERT";
454 break;
456 id = "MULTI_INSERT+INIT";
457 break;
459 id = "LOCK_UPDATED";
460 break;
462 id = "NEW_CID";
463 break;
465 id = "REWRITE";
466 break;
467 }
468
469 return id;
470}
uint8_t uint8
Definition: c.h:486
#define Assert(condition)
Definition: c.h:815
uint32 TransactionId
Definition: c.h:609
size_t Size
Definition: c.h:562
struct xlhp_freeze_plan xlhp_freeze_plan
#define XLOG_HEAP2_MULTI_INSERT
Definition: heapam_xlog.h:64
#define XLOG_HEAP_HOT_UPDATE
Definition: heapam_xlog.h:37
#define XLOG_HEAP_DELETE
Definition: heapam_xlog.h:34
#define XLHP_HAS_CONFLICT_HORIZON
Definition: heapam_xlog.h:317
#define XLOG_HEAP2_REWRITE
Definition: heapam_xlog.h:59
#define XLHP_HAS_FREEZE_PLANS
Definition: heapam_xlog.h:323
#define XLOG_HEAP_TRUNCATE
Definition: heapam_xlog.h:36
#define XLOG_HEAP_OPMASK
Definition: heapam_xlog.h:42
#define XLH_TRUNCATE_RESTART_SEQS
Definition: heapam_xlog.h:127
#define XLOG_HEAP_UPDATE
Definition: heapam_xlog.h:35
#define SizeOfHeapPrune
Definition: heapam_xlog.h:296
#define XLHL_XMAX_KEYSHR_LOCK
Definition: heapam_xlog.h:389
#define XLHP_HAS_NOW_UNUSED_ITEMS
Definition: heapam_xlog.h:332
#define XLHL_XMAX_IS_MULTI
Definition: heapam_xlog.h:386
#define XLHP_HAS_REDIRECTIONS
Definition: heapam_xlog.h:330
#define XLOG_HEAP2_PRUNE_VACUUM_SCAN
Definition: heapam_xlog.h:61
#define XLHL_XMAX_LOCK_ONLY
Definition: heapam_xlog.h:387
#define XLOG_HEAP_INPLACE
Definition: heapam_xlog.h:40
#define XLOG_HEAP2_LOCK_UPDATED
Definition: heapam_xlog.h:65
#define XLHL_XMAX_EXCL_LOCK
Definition: heapam_xlog.h:388
#define XLOG_HEAP2_PRUNE_ON_ACCESS
Definition: heapam_xlog.h:60
#define XLOG_HEAP2_NEW_CID
Definition: heapam_xlog.h:66
#define XLHP_HAS_DEAD_ITEMS
Definition: heapam_xlog.h:331
#define XLOG_HEAP_LOCK
Definition: heapam_xlog.h:39
#define XLOG_HEAP2_PRUNE_VACUUM_CLEANUP
Definition: heapam_xlog.h:62
#define XLH_TRUNCATE_CASCADE
Definition: heapam_xlog.h:126
#define XLOG_HEAP_INSERT
Definition: heapam_xlog.h:33
#define XLHL_KEYS_UPDATED
Definition: heapam_xlog.h:390
#define XLOG_HEAP2_VISIBLE
Definition: heapam_xlog.h:63
#define XLHP_IS_CATALOG_REL
Definition: heapam_xlog.h:299
#define XLOG_HEAP_INIT_PAGE
Definition: heapam_xlog.h:47
#define XLOG_HEAP_CONFIRM
Definition: heapam_xlog.h:38
void heap_desc(StringInfo buf, XLogReaderState *record)
Definition: heapdesc.c:184
void heap2_desc(StringInfo buf, XLogReaderState *record)
Definition: heapdesc.c:264
void heap_xlog_deserialize_prune_and_freeze(char *cursor, uint8 flags, int *nplans, xlhp_freeze_plan **plans, OffsetNumber **frz_offsets, int *nredirected, OffsetNumber **redirected, int *ndead, OffsetNumber **nowdead, int *nunused, OffsetNumber **nowunused)
Definition: heapdesc.c:105
static void plan_elem_desc(StringInfo buf, void *plan, void *data)
Definition: heapdesc.c:76
const char * heap2_identify(uint8 info)
Definition: heapdesc.c:434
static void truncate_flags_desc(StringInfo buf, uint8 flags)
Definition: heapdesc.c:55
const char * heap_identify(uint8 info)
Definition: heapdesc.c:389
static void infobits_desc(StringInfo buf, uint8 infobits, const char *keyname)
Definition: heapdesc.c:26
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition: itemptr.h:124
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
uint16 OffsetNumber
Definition: off.h:24
const void * data
#define plan(x)
Definition: pg_regress.c:161
static char * buf
Definition: pg_test_fsync.c:72
unsigned int Oid
Definition: postgres_ext.h:32
void oid_elem_desc(StringInfo buf, void *relid, void *data)
void redirect_elem_desc(StringInfo buf, void *offset, void *data)
void array_desc(StringInfo buf, void *array, size_t elem_size, int count, void(*elem_desc)(StringInfo buf, void *elem, void *data), void *data)
void offset_elem_desc(StringInfo buf, void *offset, void *data)
void standby_desc_invalidations(StringInfo buf, int nmsgs, SharedInvalidationMessage *msgs, Oid dbId, Oid tsId, bool relcacheInitFileInval)
Definition: standbydesc.c:101
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:242
RelFileNumber relNumber
Definition: type.h:138
OffsetNumber offnum
Definition: heapam_xlog.h:420
TransactionId xmax
Definition: heapam_xlog.h:115
OffsetNumber offnum
Definition: heapam_xlog.h:116
uint8 infobits_set
Definition: heapam_xlog.h:117
OffsetNumber offnum
Definition: heapam_xlog.h:428
SharedInvalidationMessage msgs[FLEXIBLE_ARRAY_MEMBER]
Definition: heapam_xlog.h:433
bool relcacheInitFileInval
Definition: heapam_xlog.h:431
OffsetNumber offnum
Definition: heapam_xlog.h:162
TransactionId xmax
Definition: heapam_xlog.h:409
OffsetNumber offnum
Definition: heapam_xlog.h:410
uint8 infobits_set
Definition: heapam_xlog.h:400
OffsetNumber offnum
Definition: heapam_xlog.h:399
TransactionId xmax
Definition: heapam_xlog.h:398
OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]
Definition: heapam_xlog.h:185
CommandId cmin
Definition: heapam_xlog.h:459
CommandId combocid
Definition: heapam_xlog.h:461
ItemPointerData target_tid
Definition: heapam_xlog.h:467
CommandId cmax
Definition: heapam_xlog.h:460
RelFileLocator target_locator
Definition: heapam_xlog.h:466
Oid relids[FLEXIBLE_ARRAY_MEMBER]
Definition: heapam_xlog.h:139
TransactionId new_xmax
Definition: heapam_xlog.h:224
uint8 old_infobits_set
Definition: heapam_xlog.h:222
TransactionId old_xmax
Definition: heapam_xlog.h:220
OffsetNumber old_offnum
Definition: heapam_xlog.h:221
OffsetNumber new_offnum
Definition: heapam_xlog.h:225
TransactionId snapshotConflictHorizon
Definition: heapam_xlog.h:446
TransactionId xmax
Definition: heapam_xlog.h:344
xlhp_freeze_plan plans[FLEXIBLE_ARRAY_MEMBER]
Definition: heapam_xlog.h:368
OffsetNumber data[FLEXIBLE_ARRAY_MEMBER]
Definition: heapam_xlog.h:381
char * XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len)
Definition: xlogreader.c:2025
#define XLogRecHasBlockData(decoder, block_id)
Definition: xlogreader.h:427
#define XLogRecGetInfo(decoder)
Definition: xlogreader.h:410
#define XLogRecGetData(decoder)
Definition: xlogreader.h:415
#define XLR_INFO_MASK
Definition: xlogrecord.h:62