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