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