Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * heapdesc.c
4 : * rmgr descriptor routines for access/heap/heapam.c
5 : *
6 : * Portions Copyright (c) 1996-2024, 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 123982 : infobits_desc(StringInfo buf, uint8 infobits, const char *keyname)
27 : {
28 123982 : appendStringInfo(buf, "%s: [", keyname);
29 :
30 : Assert(buf->data[buf->len - 1] != ' ');
31 :
32 123982 : if (infobits & XLHL_XMAX_IS_MULTI)
33 0 : appendStringInfoString(buf, "IS_MULTI, ");
34 123982 : if (infobits & XLHL_XMAX_LOCK_ONLY)
35 60952 : appendStringInfoString(buf, "LOCK_ONLY, ");
36 123982 : if (infobits & XLHL_XMAX_EXCL_LOCK)
37 60952 : appendStringInfoString(buf, "EXCL_LOCK, ");
38 123982 : if (infobits & XLHL_XMAX_KEYSHR_LOCK)
39 0 : appendStringInfoString(buf, "KEYSHR_LOCK, ");
40 123982 : if (infobits & XLHL_KEYS_UPDATED)
41 426 : appendStringInfoString(buf, "KEYS_UPDATED, ");
42 :
43 123982 : if (buf->data[buf->len - 1] == ' ')
44 : {
45 : /* Truncate-away final unneeded ", " */
46 : Assert(buf->data[buf->len - 2] == ',');
47 61378 : buf->len -= 2;
48 61378 : buf->data[buf->len] = '\0';
49 : }
50 :
51 123982 : appendStringInfoChar(buf, ']');
52 123982 : }
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 1204 : plan_elem_desc(StringInfo buf, void *plan, void *data)
77 : {
78 1204 : xlhp_freeze_plan *new_plan = (xlhp_freeze_plan *) plan;
79 1204 : OffsetNumber **offsets = data;
80 :
81 1204 : appendStringInfo(buf, "{ xmax: %u, infomask: %u, infomask2: %u, ntuples: %u",
82 : new_plan->xmax,
83 1204 : new_plan->t_infomask, new_plan->t_infomask2,
84 1204 : new_plan->ntuples);
85 :
86 1204 : appendStringInfoString(buf, ", offsets:");
87 1204 : array_desc(buf, *offsets, sizeof(OffsetNumber), new_plan->ntuples,
88 : &offset_elem_desc, NULL);
89 :
90 1204 : *offsets += new_plan->ntuples;
91 :
92 1204 : appendStringInfoString(buf, " }");
93 1204 : }
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 3020 : 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 3020 : if (flags & XLHP_HAS_FREEZE_PLANS)
113 : {
114 996 : xlhp_freeze_plans *freeze_plans = (xlhp_freeze_plans *) cursor;
115 :
116 996 : *nplans = freeze_plans->nplans;
117 : Assert(*nplans > 0);
118 996 : *plans = freeze_plans->plans;
119 :
120 996 : cursor += offsetof(xlhp_freeze_plans, plans);
121 996 : cursor += sizeof(xlhp_freeze_plan) * *nplans;
122 : }
123 : else
124 : {
125 2024 : *nplans = 0;
126 2024 : *plans = NULL;
127 : }
128 :
129 3020 : if (flags & XLHP_HAS_REDIRECTIONS)
130 : {
131 704 : xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
132 :
133 704 : *nredirected = subrecord->ntargets;
134 : Assert(*nredirected > 0);
135 704 : *redirected = &subrecord->data[0];
136 :
137 704 : cursor += offsetof(xlhp_prune_items, data);
138 704 : cursor += sizeof(OffsetNumber[2]) * *nredirected;
139 : }
140 : else
141 : {
142 2316 : *nredirected = 0;
143 2316 : *redirected = NULL;
144 : }
145 :
146 3020 : if (flags & XLHP_HAS_DEAD_ITEMS)
147 : {
148 816 : xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
149 :
150 816 : *ndead = subrecord->ntargets;
151 : Assert(*ndead > 0);
152 816 : *nowdead = subrecord->data;
153 :
154 816 : cursor += offsetof(xlhp_prune_items, data);
155 816 : cursor += sizeof(OffsetNumber) * *ndead;
156 : }
157 : else
158 : {
159 2204 : *ndead = 0;
160 2204 : *nowdead = NULL;
161 : }
162 :
163 3020 : if (flags & XLHP_HAS_NOW_UNUSED_ITEMS)
164 : {
165 736 : xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
166 :
167 736 : *nunused = subrecord->ntargets;
168 : Assert(*nunused > 0);
169 736 : *nowunused = subrecord->data;
170 :
171 736 : cursor += offsetof(xlhp_prune_items, data);
172 736 : cursor += sizeof(OffsetNumber) * *nunused;
173 : }
174 : else
175 : {
176 2284 : *nunused = 0;
177 2284 : *nowunused = NULL;
178 : }
179 :
180 3020 : *frz_offsets = (OffsetNumber *) cursor;
181 3020 : }
182 :
183 : void
184 270378 : heap_desc(StringInfo buf, XLogReaderState *record)
185 : {
186 270378 : char *rec = XLogRecGetData(record);
187 270378 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
188 :
189 270378 : info &= XLOG_HEAP_OPMASK;
190 270378 : if (info == XLOG_HEAP_INSERT)
191 : {
192 143764 : xl_heap_insert *xlrec = (xl_heap_insert *) rec;
193 :
194 143764 : appendStringInfo(buf, "off: %u, flags: 0x%02X",
195 143764 : xlrec->offnum,
196 143764 : xlrec->flags);
197 : }
198 126614 : 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 126188 : else if (info == XLOG_HEAP_UPDATE)
208 : {
209 61012 : xl_heap_update *xlrec = (xl_heap_update *) rec;
210 :
211 61012 : appendStringInfo(buf, "old_xmax: %u, old_off: %u, ",
212 61012 : xlrec->old_xmax, xlrec->old_offnum);
213 61012 : infobits_desc(buf, xlrec->old_infobits_set, "old_infobits");
214 61012 : appendStringInfo(buf, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
215 61012 : xlrec->flags, xlrec->new_xmax, xlrec->new_offnum);
216 : }
217 65176 : else if (info == XLOG_HEAP_HOT_UPDATE)
218 : {
219 1592 : xl_heap_update *xlrec = (xl_heap_update *) rec;
220 :
221 1592 : appendStringInfo(buf, "old_xmax: %u, old_off: %u, ",
222 1592 : xlrec->old_xmax, xlrec->old_offnum);
223 1592 : infobits_desc(buf, xlrec->old_infobits_set, "old_infobits");
224 1592 : appendStringInfo(buf, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
225 1592 : xlrec->flags, xlrec->new_xmax, xlrec->new_offnum);
226 : }
227 63584 : 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 63572 : 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 63572 : else if (info == XLOG_HEAP_LOCK)
244 : {
245 60952 : xl_heap_lock *xlrec = (xl_heap_lock *) rec;
246 :
247 60952 : appendStringInfo(buf, "xmax: %u, off: %u, ",
248 60952 : xlrec->xmax, xlrec->offnum);
249 60952 : infobits_desc(buf, xlrec->infobits_set, "infobits");
250 60952 : 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 270378 : }
262 :
263 : void
264 17012 : heap2_desc(StringInfo buf, XLogReaderState *record)
265 : {
266 17012 : char *rec = XLogRecGetData(record);
267 17012 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
268 :
269 17012 : info &= XLOG_HEAP_OPMASK;
270 17012 : if (info == XLOG_HEAP2_PRUNE_ON_ACCESS ||
271 14556 : info == XLOG_HEAP2_PRUNE_VACUUM_SCAN ||
272 : info == XLOG_HEAP2_PRUNE_VACUUM_CLEANUP)
273 3020 : {
274 3020 : xl_heap_prune *xlrec = (xl_heap_prune *) rec;
275 :
276 3020 : if (xlrec->flags & XLHP_HAS_CONFLICT_HORIZON)
277 : {
278 : TransactionId conflict_xid;
279 :
280 2336 : memcpy(&conflict_xid, rec + SizeOfHeapPrune, sizeof(TransactionId));
281 :
282 2336 : appendStringInfo(buf, "snapshotConflictHorizon: %u",
283 : conflict_xid);
284 : }
285 :
286 3020 : appendStringInfo(buf, ", isCatalogRel: %c",
287 3020 : xlrec->flags & XLHP_IS_CATALOG_REL ? 'T' : 'F');
288 :
289 3020 : 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 3020 : char *cursor = XLogRecGetBlockData(record, 0, &datalen);
303 :
304 3020 : 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 3020 : appendStringInfo(buf, ", nplans: %u, nredirected: %u, ndead: %u, nunused: %u",
311 : nplans, nredirected, ndead, nunused);
312 :
313 3020 : if (nplans > 0)
314 : {
315 996 : appendStringInfoString(buf, ", plans:");
316 996 : array_desc(buf, plans, sizeof(xlhp_freeze_plan), nplans,
317 : &plan_elem_desc, &frz_offsets);
318 : }
319 :
320 3020 : if (nredirected > 0)
321 : {
322 704 : appendStringInfoString(buf, ", redirected:");
323 704 : array_desc(buf, redirected, sizeof(OffsetNumber) * 2,
324 : nredirected, &redirect_elem_desc, NULL);
325 : }
326 :
327 3020 : if (ndead > 0)
328 : {
329 816 : appendStringInfoString(buf, ", dead:");
330 816 : array_desc(buf, nowdead, sizeof(OffsetNumber), ndead,
331 : &offset_elem_desc, NULL);
332 : }
333 :
334 3020 : if (nunused > 0)
335 : {
336 736 : appendStringInfoString(buf, ", unused:");
337 736 : array_desc(buf, nowunused, sizeof(OffsetNumber), nunused,
338 : &offset_elem_desc, NULL);
339 : }
340 : }
341 : }
342 13992 : else if (info == XLOG_HEAP2_VISIBLE)
343 : {
344 3128 : xl_heap_visible *xlrec = (xl_heap_visible *) rec;
345 :
346 3128 : appendStringInfo(buf, "snapshotConflictHorizon: %u, flags: 0x%02X",
347 3128 : xlrec->snapshotConflictHorizon, xlrec->flags);
348 : }
349 10864 : else if (info == XLOG_HEAP2_MULTI_INSERT)
350 : {
351 7634 : xl_heap_multi_insert *xlrec = (xl_heap_multi_insert *) rec;
352 7634 : bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
353 :
354 7634 : appendStringInfo(buf, "ntuples: %d, flags: 0x%02X", xlrec->ntuples,
355 7634 : xlrec->flags);
356 :
357 7634 : if (XLogRecHasBlockData(record, 0) && !isinit)
358 : {
359 7336 : appendStringInfoString(buf, ", offsets:");
360 7336 : array_desc(buf, xlrec->offsets, sizeof(OffsetNumber),
361 7336 : 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 17012 : }
387 :
388 : const char *
389 270396 : heap_identify(uint8 info)
390 : {
391 270396 : const char *id = NULL;
392 :
393 270396 : switch (info & ~XLR_INFO_MASK)
394 : {
395 142356 : case XLOG_HEAP_INSERT:
396 142356 : id = "INSERT";
397 142356 : break;
398 1412 : case XLOG_HEAP_INSERT | XLOG_HEAP_INIT_PAGE:
399 1412 : id = "INSERT+INIT";
400 1412 : break;
401 428 : case XLOG_HEAP_DELETE:
402 428 : id = "DELETE";
403 428 : break;
404 60570 : case XLOG_HEAP_UPDATE:
405 60570 : id = "UPDATE";
406 60570 : break;
407 446 : case XLOG_HEAP_UPDATE | XLOG_HEAP_INIT_PAGE:
408 446 : id = "UPDATE+INIT";
409 446 : break;
410 1594 : case XLOG_HEAP_HOT_UPDATE:
411 1594 : id = "HOT_UPDATE";
412 1594 : 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 60954 : case XLOG_HEAP_LOCK:
423 60954 : id = "LOCK";
424 60954 : break;
425 2622 : case XLOG_HEAP_INPLACE:
426 2622 : id = "INPLACE";
427 2622 : break;
428 : }
429 :
430 270396 : return id;
431 : }
432 :
433 : const char *
434 17026 : heap2_identify(uint8 info)
435 : {
436 17026 : const char *id = NULL;
437 :
438 17026 : switch (info & ~XLR_INFO_MASK)
439 : {
440 1086 : case XLOG_HEAP2_PRUNE_ON_ACCESS:
441 1086 : id = "PRUNE_ON_ACCESS";
442 1086 : break;
443 1374 : case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
444 1374 : id = "PRUNE_VACUUM_SCAN";
445 1374 : break;
446 566 : case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
447 566 : id = "PRUNE_VACUUM_CLEANUP";
448 566 : break;
449 3130 : case XLOG_HEAP2_VISIBLE:
450 3130 : id = "VISIBLE";
451 3130 : break;
452 7416 : case XLOG_HEAP2_MULTI_INSERT:
453 7416 : id = "MULTI_INSERT";
454 7416 : 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 17026 : return id;
470 : }
|