Line data Source code
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"
18 : #include "access/rmgrdesc_utils.h"
19 : #include "access/visibilitymapdefs.h"
20 : #include "storage/standbydefs.h"
21 :
22 : /*
23 : * NOTE: "keyname" argument cannot have trailing spaces or punctuation
24 : * characters
25 : */
26 : static void
27 124122 : infobits_desc(StringInfo buf, uint8 infobits, const char *keyname)
28 : {
29 124122 : appendStringInfo(buf, "%s: [", keyname);
30 :
31 : Assert(buf->data[buf->len - 1] != ' ');
32 :
33 124122 : if (infobits & XLHL_XMAX_IS_MULTI)
34 0 : appendStringInfoString(buf, "IS_MULTI, ");
35 124122 : if (infobits & XLHL_XMAX_LOCK_ONLY)
36 61000 : appendStringInfoString(buf, "LOCK_ONLY, ");
37 124122 : if (infobits & XLHL_XMAX_EXCL_LOCK)
38 61000 : appendStringInfoString(buf, "EXCL_LOCK, ");
39 124122 : if (infobits & XLHL_XMAX_KEYSHR_LOCK)
40 0 : appendStringInfoString(buf, "KEYSHR_LOCK, ");
41 124122 : if (infobits & XLHL_KEYS_UPDATED)
42 430 : appendStringInfoString(buf, "KEYS_UPDATED, ");
43 :
44 124122 : if (buf->data[buf->len - 1] == ' ')
45 : {
46 : /* Truncate-away final unneeded ", " */
47 : Assert(buf->data[buf->len - 2] == ',');
48 61430 : buf->len -= 2;
49 61430 : buf->data[buf->len] = '\0';
50 : }
51 :
52 124122 : appendStringInfoChar(buf, ']');
53 124122 : }
54 :
55 : static void
56 12 : truncate_flags_desc(StringInfo buf, uint8 flags)
57 : {
58 12 : appendStringInfoString(buf, "flags: [");
59 :
60 12 : if (flags & XLH_TRUNCATE_CASCADE)
61 0 : appendStringInfoString(buf, "CASCADE, ");
62 12 : if (flags & XLH_TRUNCATE_RESTART_SEQS)
63 0 : appendStringInfoString(buf, "RESTART_SEQS, ");
64 :
65 12 : if (buf->data[buf->len - 1] == ' ')
66 : {
67 : /* Truncate-away final unneeded ", " */
68 : Assert(buf->data[buf->len - 2] == ',');
69 0 : buf->len -= 2;
70 0 : buf->data[buf->len] = '\0';
71 : }
72 :
73 12 : appendStringInfoChar(buf, ']');
74 12 : }
75 :
76 : static void
77 1236 : plan_elem_desc(StringInfo buf, void *plan, void *data)
78 : {
79 1236 : xlhp_freeze_plan *new_plan = (xlhp_freeze_plan *) plan;
80 1236 : OffsetNumber **offsets = data;
81 :
82 1236 : appendStringInfo(buf, "{ xmax: %u, infomask: %u, infomask2: %u, ntuples: %u",
83 : new_plan->xmax,
84 1236 : new_plan->t_infomask, new_plan->t_infomask2,
85 1236 : new_plan->ntuples);
86 :
87 1236 : appendStringInfoString(buf, ", offsets:");
88 1236 : array_desc(buf, *offsets, sizeof(OffsetNumber), new_plan->ntuples,
89 : &offset_elem_desc, NULL);
90 :
91 1236 : *offsets += new_plan->ntuples;
92 :
93 1236 : appendStringInfoString(buf, " }");
94 1236 : }
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 : */
105 : void
106 3068 : heap_xlog_deserialize_prune_and_freeze(char *cursor, uint8 flags,
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 3068 : if (flags & XLHP_HAS_FREEZE_PLANS)
114 : {
115 1028 : xlhp_freeze_plans *freeze_plans = (xlhp_freeze_plans *) cursor;
116 :
117 1028 : *nplans = freeze_plans->nplans;
118 : Assert(*nplans > 0);
119 1028 : *plans = freeze_plans->plans;
120 :
121 1028 : cursor += offsetof(xlhp_freeze_plans, plans);
122 1028 : cursor += sizeof(xlhp_freeze_plan) * *nplans;
123 : }
124 : else
125 : {
126 2040 : *nplans = 0;
127 2040 : *plans = NULL;
128 : }
129 :
130 3068 : if (flags & XLHP_HAS_REDIRECTIONS)
131 : {
132 708 : xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
133 :
134 708 : *nredirected = subrecord->ntargets;
135 : Assert(*nredirected > 0);
136 708 : *redirected = &subrecord->data[0];
137 :
138 708 : cursor += offsetof(xlhp_prune_items, data);
139 708 : cursor += sizeof(OffsetNumber[2]) * *nredirected;
140 : }
141 : else
142 : {
143 2360 : *nredirected = 0;
144 2360 : *redirected = NULL;
145 : }
146 :
147 3068 : if (flags & XLHP_HAS_DEAD_ITEMS)
148 : {
149 816 : xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
150 :
151 816 : *ndead = subrecord->ntargets;
152 : Assert(*ndead > 0);
153 816 : *nowdead = subrecord->data;
154 :
155 816 : cursor += offsetof(xlhp_prune_items, data);
156 816 : cursor += sizeof(OffsetNumber) * *ndead;
157 : }
158 : else
159 : {
160 2252 : *ndead = 0;
161 2252 : *nowdead = NULL;
162 : }
163 :
164 3068 : if (flags & XLHP_HAS_NOW_UNUSED_ITEMS)
165 : {
166 748 : xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
167 :
168 748 : *nunused = subrecord->ntargets;
169 : Assert(*nunused > 0);
170 748 : *nowunused = subrecord->data;
171 :
172 748 : cursor += offsetof(xlhp_prune_items, data);
173 748 : cursor += sizeof(OffsetNumber) * *nunused;
174 : }
175 : else
176 : {
177 2320 : *nunused = 0;
178 2320 : *nowunused = NULL;
179 : }
180 :
181 3068 : *frz_offsets = (OffsetNumber *) cursor;
182 3068 : }
183 :
184 : void
185 270860 : heap_desc(StringInfo buf, XLogReaderState *record)
186 : {
187 270860 : char *rec = XLogRecGetData(record);
188 270860 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
189 :
190 270860 : info &= XLOG_HEAP_OPMASK;
191 270860 : if (info == XLOG_HEAP_INSERT)
192 : {
193 144098 : xl_heap_insert *xlrec = (xl_heap_insert *) rec;
194 :
195 144098 : appendStringInfo(buf, "off: %u, flags: 0x%02X",
196 144098 : xlrec->offnum,
197 144098 : xlrec->flags);
198 : }
199 126762 : else if (info == XLOG_HEAP_DELETE)
200 : {
201 430 : xl_heap_delete *xlrec = (xl_heap_delete *) rec;
202 :
203 430 : appendStringInfo(buf, "xmax: %u, off: %u, ",
204 430 : xlrec->xmax, xlrec->offnum);
205 430 : infobits_desc(buf, xlrec->infobits_set, "infobits");
206 430 : appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
207 : }
208 126332 : else if (info == XLOG_HEAP_UPDATE)
209 : {
210 61056 : xl_heap_update *xlrec = (xl_heap_update *) rec;
211 :
212 61056 : appendStringInfo(buf, "old_xmax: %u, old_off: %u, ",
213 61056 : xlrec->old_xmax, xlrec->old_offnum);
214 61056 : infobits_desc(buf, xlrec->old_infobits_set, "old_infobits");
215 61056 : appendStringInfo(buf, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
216 61056 : xlrec->flags, xlrec->new_xmax, xlrec->new_offnum);
217 : }
218 65276 : else if (info == XLOG_HEAP_HOT_UPDATE)
219 : {
220 1636 : xl_heap_update *xlrec = (xl_heap_update *) rec;
221 :
222 1636 : appendStringInfo(buf, "old_xmax: %u, old_off: %u, ",
223 1636 : xlrec->old_xmax, xlrec->old_offnum);
224 1636 : infobits_desc(buf, xlrec->old_infobits_set, "old_infobits");
225 1636 : appendStringInfo(buf, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
226 1636 : xlrec->flags, xlrec->new_xmax, xlrec->new_offnum);
227 : }
228 63640 : else if (info == XLOG_HEAP_TRUNCATE)
229 : {
230 12 : xl_heap_truncate *xlrec = (xl_heap_truncate *) rec;
231 :
232 12 : truncate_flags_desc(buf, xlrec->flags);
233 12 : appendStringInfo(buf, ", nrelids: %u", xlrec->nrelids);
234 12 : appendStringInfoString(buf, ", relids:");
235 12 : array_desc(buf, xlrec->relids, sizeof(Oid), xlrec->nrelids,
236 : &oid_elem_desc, NULL);
237 : }
238 63628 : else if (info == XLOG_HEAP_CONFIRM)
239 : {
240 0 : xl_heap_confirm *xlrec = (xl_heap_confirm *) rec;
241 :
242 0 : appendStringInfo(buf, "off: %u", xlrec->offnum);
243 : }
244 63628 : else if (info == XLOG_HEAP_LOCK)
245 : {
246 61000 : xl_heap_lock *xlrec = (xl_heap_lock *) rec;
247 :
248 61000 : appendStringInfo(buf, "xmax: %u, off: %u, ",
249 61000 : xlrec->xmax, xlrec->offnum);
250 61000 : infobits_desc(buf, xlrec->infobits_set, "infobits");
251 61000 : appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
252 : }
253 2628 : else if (info == XLOG_HEAP_INPLACE)
254 : {
255 2628 : xl_heap_inplace *xlrec = (xl_heap_inplace *) rec;
256 :
257 2628 : appendStringInfo(buf, "off: %u", xlrec->offnum);
258 2628 : standby_desc_invalidations(buf, xlrec->nmsgs, xlrec->msgs,
259 : xlrec->dbId, xlrec->tsId,
260 2628 : xlrec->relcacheInitFileInval);
261 : }
262 270860 : }
263 :
264 : void
265 17144 : heap2_desc(StringInfo buf, XLogReaderState *record)
266 : {
267 17144 : char *rec = XLogRecGetData(record);
268 17144 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
269 :
270 17144 : info &= XLOG_HEAP_OPMASK;
271 17144 : if (info == XLOG_HEAP2_PRUNE_ON_ACCESS ||
272 14648 : info == XLOG_HEAP2_PRUNE_VACUUM_SCAN ||
273 : info == XLOG_HEAP2_PRUNE_VACUUM_CLEANUP)
274 3068 : {
275 3068 : xl_heap_prune *xlrec = (xl_heap_prune *) rec;
276 :
277 3068 : if (xlrec->flags & XLHP_HAS_CONFLICT_HORIZON)
278 : {
279 : TransactionId conflict_xid;
280 :
281 2360 : memcpy(&conflict_xid, rec + SizeOfHeapPrune, sizeof(TransactionId));
282 :
283 2360 : appendStringInfo(buf, "snapshotConflictHorizon: %u",
284 : conflict_xid);
285 : }
286 :
287 3068 : appendStringInfo(buf, ", isCatalogRel: %c",
288 3068 : xlrec->flags & XLHP_IS_CATALOG_REL ? 'T' : 'F');
289 :
290 3068 : if (XLogRecHasBlockData(record, 0))
291 : {
292 : Size datalen;
293 : OffsetNumber *redirected;
294 : OffsetNumber *nowdead;
295 : OffsetNumber *nowunused;
296 : int nredirected;
297 : int nunused;
298 : int ndead;
299 : int nplans;
300 : xlhp_freeze_plan *plans;
301 : OffsetNumber *frz_offsets;
302 :
303 3068 : char *cursor = XLogRecGetBlockData(record, 0, &datalen);
304 :
305 3068 : heap_xlog_deserialize_prune_and_freeze(cursor, xlrec->flags,
306 : &nplans, &plans, &frz_offsets,
307 : &nredirected, &redirected,
308 : &ndead, &nowdead,
309 : &nunused, &nowunused);
310 :
311 3068 : appendStringInfo(buf, ", nplans: %u, nredirected: %u, ndead: %u, nunused: %u",
312 : nplans, nredirected, ndead, nunused);
313 :
314 3068 : if (nplans > 0)
315 : {
316 1028 : appendStringInfoString(buf, ", plans:");
317 1028 : array_desc(buf, plans, sizeof(xlhp_freeze_plan), nplans,
318 : &plan_elem_desc, &frz_offsets);
319 : }
320 :
321 3068 : if (nredirected > 0)
322 : {
323 708 : appendStringInfoString(buf, ", redirected:");
324 708 : array_desc(buf, redirected, sizeof(OffsetNumber) * 2,
325 : nredirected, &redirect_elem_desc, NULL);
326 : }
327 :
328 3068 : if (ndead > 0)
329 : {
330 816 : appendStringInfoString(buf, ", dead:");
331 816 : array_desc(buf, nowdead, sizeof(OffsetNumber), ndead,
332 : &offset_elem_desc, NULL);
333 : }
334 :
335 3068 : if (nunused > 0)
336 : {
337 748 : appendStringInfoString(buf, ", unused:");
338 748 : array_desc(buf, nowunused, sizeof(OffsetNumber), nunused,
339 : &offset_elem_desc, NULL);
340 : }
341 : }
342 : }
343 14076 : else if (info == XLOG_HEAP2_VISIBLE)
344 : {
345 3132 : xl_heap_visible *xlrec = (xl_heap_visible *) rec;
346 :
347 3132 : appendStringInfo(buf, "snapshotConflictHorizon: %u, flags: 0x%02X",
348 3132 : xlrec->snapshotConflictHorizon, xlrec->flags);
349 : }
350 10944 : else if (info == XLOG_HEAP2_MULTI_INSERT)
351 : {
352 7714 : xl_heap_multi_insert *xlrec = (xl_heap_multi_insert *) rec;
353 7714 : bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
354 :
355 7714 : appendStringInfo(buf, "ntuples: %d, flags: 0x%02X", xlrec->ntuples,
356 7714 : xlrec->flags);
357 :
358 7714 : if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
359 0 : appendStringInfo(buf, ", vm_flags: 0x%02X",
360 : VISIBILITYMAP_ALL_VISIBLE |
361 : VISIBILITYMAP_ALL_FROZEN);
362 :
363 7714 : if (XLogRecHasBlockData(record, 0) && !isinit)
364 : {
365 7432 : appendStringInfoString(buf, ", offsets:");
366 7432 : array_desc(buf, xlrec->offsets, sizeof(OffsetNumber),
367 7432 : xlrec->ntuples, &offset_elem_desc, NULL);
368 : }
369 : }
370 3230 : else if (info == XLOG_HEAP2_LOCK_UPDATED)
371 : {
372 0 : xl_heap_lock_updated *xlrec = (xl_heap_lock_updated *) rec;
373 :
374 0 : appendStringInfo(buf, "xmax: %u, off: %u, ",
375 0 : xlrec->xmax, xlrec->offnum);
376 0 : infobits_desc(buf, xlrec->infobits_set, "infobits");
377 0 : appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
378 : }
379 3230 : else if (info == XLOG_HEAP2_NEW_CID)
380 : {
381 3230 : xl_heap_new_cid *xlrec = (xl_heap_new_cid *) rec;
382 :
383 3230 : appendStringInfo(buf, "rel: %u/%u/%u, tid: %u/%u",
384 : xlrec->target_locator.spcOid,
385 : xlrec->target_locator.dbOid,
386 : xlrec->target_locator.relNumber,
387 3230 : ItemPointerGetBlockNumber(&(xlrec->target_tid)),
388 3230 : ItemPointerGetOffsetNumber(&(xlrec->target_tid)));
389 3230 : appendStringInfo(buf, ", cmin: %u, cmax: %u, combo: %u",
390 : xlrec->cmin, xlrec->cmax, xlrec->combocid);
391 : }
392 17144 : }
393 :
394 : const char *
395 270878 : heap_identify(uint8 info)
396 : {
397 270878 : const char *id = NULL;
398 :
399 270878 : switch (info & ~XLR_INFO_MASK)
400 : {
401 142674 : case XLOG_HEAP_INSERT:
402 142674 : id = "INSERT";
403 142674 : break;
404 1428 : case XLOG_HEAP_INSERT | XLOG_HEAP_INIT_PAGE:
405 1428 : id = "INSERT+INIT";
406 1428 : break;
407 432 : case XLOG_HEAP_DELETE:
408 432 : id = "DELETE";
409 432 : break;
410 60606 : case XLOG_HEAP_UPDATE:
411 60606 : id = "UPDATE";
412 60606 : break;
413 454 : case XLOG_HEAP_UPDATE | XLOG_HEAP_INIT_PAGE:
414 454 : id = "UPDATE+INIT";
415 454 : break;
416 1638 : case XLOG_HEAP_HOT_UPDATE:
417 1638 : id = "HOT_UPDATE";
418 1638 : break;
419 0 : case XLOG_HEAP_HOT_UPDATE | XLOG_HEAP_INIT_PAGE:
420 0 : id = "HOT_UPDATE+INIT";
421 0 : break;
422 14 : case XLOG_HEAP_TRUNCATE:
423 14 : id = "TRUNCATE";
424 14 : break;
425 0 : case XLOG_HEAP_CONFIRM:
426 0 : id = "HEAP_CONFIRM";
427 0 : break;
428 61002 : case XLOG_HEAP_LOCK:
429 61002 : id = "LOCK";
430 61002 : break;
431 2630 : case XLOG_HEAP_INPLACE:
432 2630 : id = "INPLACE";
433 2630 : break;
434 : }
435 :
436 270878 : return id;
437 : }
438 :
439 : const char *
440 17158 : heap2_identify(uint8 info)
441 : {
442 17158 : const char *id = NULL;
443 :
444 17158 : switch (info & ~XLR_INFO_MASK)
445 : {
446 1094 : case XLOG_HEAP2_PRUNE_ON_ACCESS:
447 1094 : id = "PRUNE_ON_ACCESS";
448 1094 : break;
449 1406 : case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
450 1406 : id = "PRUNE_VACUUM_SCAN";
451 1406 : break;
452 574 : case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
453 574 : id = "PRUNE_VACUUM_CLEANUP";
454 574 : break;
455 3134 : case XLOG_HEAP2_VISIBLE:
456 3134 : id = "VISIBLE";
457 3134 : break;
458 7484 : case XLOG_HEAP2_MULTI_INSERT:
459 7484 : id = "MULTI_INSERT";
460 7484 : break;
461 234 : case XLOG_HEAP2_MULTI_INSERT | XLOG_HEAP_INIT_PAGE:
462 234 : id = "MULTI_INSERT+INIT";
463 234 : break;
464 0 : case XLOG_HEAP2_LOCK_UPDATED:
465 0 : id = "LOCK_UPDATED";
466 0 : break;
467 3232 : case XLOG_HEAP2_NEW_CID:
468 3232 : id = "NEW_CID";
469 3232 : break;
470 0 : case XLOG_HEAP2_REWRITE:
471 0 : id = "REWRITE";
472 0 : break;
473 : }
474 :
475 17158 : return id;
476 : }
|