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