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