1 /*-------------------------------------------------------------------------
4 * rmgr descriptor routines for access/heap/heapam.c
6 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/access/rmgrdesc/heapdesc.c
13 *-------------------------------------------------------------------------
17 #include "access/heapam_xlog.h"
18 #include "access/rmgrdesc_utils.h"
19 #include "storage/standbydefs.h"
22 * NOTE: "keyname" argument cannot have trailing spaces or punctuation
26 infobits_desc(StringInfo buf
, uint8 infobits
, const char *keyname
)
28 appendStringInfo(buf
, "%s: [", keyname
);
30 Assert(buf
->data
[buf
->len
- 1] != ' ');
32 if (infobits
& XLHL_XMAX_IS_MULTI
)
33 appendStringInfoString(buf
, "IS_MULTI, ");
34 if (infobits
& XLHL_XMAX_LOCK_ONLY
)
35 appendStringInfoString(buf
, "LOCK_ONLY, ");
36 if (infobits
& XLHL_XMAX_EXCL_LOCK
)
37 appendStringInfoString(buf
, "EXCL_LOCK, ");
38 if (infobits
& XLHL_XMAX_KEYSHR_LOCK
)
39 appendStringInfoString(buf
, "KEYSHR_LOCK, ");
40 if (infobits
& XLHL_KEYS_UPDATED
)
41 appendStringInfoString(buf
, "KEYS_UPDATED, ");
43 if (buf
->data
[buf
->len
- 1] == ' ')
45 /* Truncate-away final unneeded ", " */
46 Assert(buf
->data
[buf
->len
- 2] == ',');
48 buf
->data
[buf
->len
] = '\0';
51 appendStringInfoChar(buf
, ']');
55 truncate_flags_desc(StringInfo buf
, uint8 flags
)
57 appendStringInfoString(buf
, "flags: [");
59 if (flags
& XLH_TRUNCATE_CASCADE
)
60 appendStringInfoString(buf
, "CASCADE, ");
61 if (flags
& XLH_TRUNCATE_RESTART_SEQS
)
62 appendStringInfoString(buf
, "RESTART_SEQS, ");
64 if (buf
->data
[buf
->len
- 1] == ' ')
66 /* Truncate-away final unneeded ", " */
67 Assert(buf
->data
[buf
->len
- 2] == ',');
69 buf
->data
[buf
->len
] = '\0';
72 appendStringInfoChar(buf
, ']');
76 plan_elem_desc(StringInfo buf
, void *plan
, void *data
)
78 xlhp_freeze_plan
*new_plan
= (xlhp_freeze_plan
*) plan
;
79 OffsetNumber
**offsets
= data
;
81 appendStringInfo(buf
, "{ xmax: %u, infomask: %u, infomask2: %u, ntuples: %u",
83 new_plan
->t_infomask
, new_plan
->t_infomask2
,
86 appendStringInfoString(buf
, ", offsets:");
87 array_desc(buf
, *offsets
, sizeof(OffsetNumber
), new_plan
->ntuples
,
88 &offset_elem_desc
, NULL
);
90 *offsets
+= new_plan
->ntuples
;
92 appendStringInfoString(buf
, " }");
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.
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.
105 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
)
112 if (flags
& XLHP_HAS_FREEZE_PLANS
)
114 xlhp_freeze_plans
*freeze_plans
= (xlhp_freeze_plans
*) cursor
;
116 *nplans
= freeze_plans
->nplans
;
118 *plans
= freeze_plans
->plans
;
120 cursor
+= offsetof(xlhp_freeze_plans
, plans
);
121 cursor
+= sizeof(xlhp_freeze_plan
) * *nplans
;
129 if (flags
& XLHP_HAS_REDIRECTIONS
)
131 xlhp_prune_items
*subrecord
= (xlhp_prune_items
*) cursor
;
133 *nredirected
= subrecord
->ntargets
;
134 Assert(*nredirected
> 0);
135 *redirected
= &subrecord
->data
[0];
137 cursor
+= offsetof(xlhp_prune_items
, data
);
138 cursor
+= sizeof(OffsetNumber
[2]) * *nredirected
;
146 if (flags
& XLHP_HAS_DEAD_ITEMS
)
148 xlhp_prune_items
*subrecord
= (xlhp_prune_items
*) cursor
;
150 *ndead
= subrecord
->ntargets
;
152 *nowdead
= subrecord
->data
;
154 cursor
+= offsetof(xlhp_prune_items
, data
);
155 cursor
+= sizeof(OffsetNumber
) * *ndead
;
163 if (flags
& XLHP_HAS_NOW_UNUSED_ITEMS
)
165 xlhp_prune_items
*subrecord
= (xlhp_prune_items
*) cursor
;
167 *nunused
= subrecord
->ntargets
;
168 Assert(*nunused
> 0);
169 *nowunused
= subrecord
->data
;
171 cursor
+= offsetof(xlhp_prune_items
, data
);
172 cursor
+= sizeof(OffsetNumber
) * *nunused
;
180 *frz_offsets
= (OffsetNumber
*) cursor
;
184 heap_desc(StringInfo buf
, XLogReaderState
*record
)
186 char *rec
= XLogRecGetData(record
);
187 uint8 info
= XLogRecGetInfo(record
) & ~XLR_INFO_MASK
;
189 info
&= XLOG_HEAP_OPMASK
;
190 if (info
== XLOG_HEAP_INSERT
)
192 xl_heap_insert
*xlrec
= (xl_heap_insert
*) rec
;
194 appendStringInfo(buf
, "off: %u, flags: 0x%02X",
198 else if (info
== XLOG_HEAP_DELETE
)
200 xl_heap_delete
*xlrec
= (xl_heap_delete
*) rec
;
202 appendStringInfo(buf
, "xmax: %u, off: %u, ",
203 xlrec
->xmax
, xlrec
->offnum
);
204 infobits_desc(buf
, xlrec
->infobits_set
, "infobits");
205 appendStringInfo(buf
, ", flags: 0x%02X", xlrec
->flags
);
207 else if (info
== XLOG_HEAP_UPDATE
)
209 xl_heap_update
*xlrec
= (xl_heap_update
*) rec
;
211 appendStringInfo(buf
, "old_xmax: %u, old_off: %u, ",
212 xlrec
->old_xmax
, xlrec
->old_offnum
);
213 infobits_desc(buf
, xlrec
->old_infobits_set
, "old_infobits");
214 appendStringInfo(buf
, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
215 xlrec
->flags
, xlrec
->new_xmax
, xlrec
->new_offnum
);
217 else if (info
== XLOG_HEAP_HOT_UPDATE
)
219 xl_heap_update
*xlrec
= (xl_heap_update
*) rec
;
221 appendStringInfo(buf
, "old_xmax: %u, old_off: %u, ",
222 xlrec
->old_xmax
, xlrec
->old_offnum
);
223 infobits_desc(buf
, xlrec
->old_infobits_set
, "old_infobits");
224 appendStringInfo(buf
, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
225 xlrec
->flags
, xlrec
->new_xmax
, xlrec
->new_offnum
);
227 else if (info
== XLOG_HEAP_TRUNCATE
)
229 xl_heap_truncate
*xlrec
= (xl_heap_truncate
*) rec
;
231 truncate_flags_desc(buf
, xlrec
->flags
);
232 appendStringInfo(buf
, ", nrelids: %u", xlrec
->nrelids
);
233 appendStringInfoString(buf
, ", relids:");
234 array_desc(buf
, xlrec
->relids
, sizeof(Oid
), xlrec
->nrelids
,
235 &oid_elem_desc
, NULL
);
237 else if (info
== XLOG_HEAP_CONFIRM
)
239 xl_heap_confirm
*xlrec
= (xl_heap_confirm
*) rec
;
241 appendStringInfo(buf
, "off: %u", xlrec
->offnum
);
243 else if (info
== XLOG_HEAP_LOCK
)
245 xl_heap_lock
*xlrec
= (xl_heap_lock
*) rec
;
247 appendStringInfo(buf
, "xmax: %u, off: %u, ",
248 xlrec
->xmax
, xlrec
->offnum
);
249 infobits_desc(buf
, xlrec
->infobits_set
, "infobits");
250 appendStringInfo(buf
, ", flags: 0x%02X", xlrec
->flags
);
252 else if (info
== XLOG_HEAP_INPLACE
)
254 xl_heap_inplace
*xlrec
= (xl_heap_inplace
*) rec
;
256 appendStringInfo(buf
, "off: %u", xlrec
->offnum
);
257 standby_desc_invalidations(buf
, xlrec
->nmsgs
, xlrec
->msgs
,
258 xlrec
->dbId
, xlrec
->tsId
,
259 xlrec
->relcacheInitFileInval
);
264 heap2_desc(StringInfo buf
, XLogReaderState
*record
)
266 char *rec
= XLogRecGetData(record
);
267 uint8 info
= XLogRecGetInfo(record
) & ~XLR_INFO_MASK
;
269 info
&= XLOG_HEAP_OPMASK
;
270 if (info
== XLOG_HEAP2_PRUNE_ON_ACCESS
||
271 info
== XLOG_HEAP2_PRUNE_VACUUM_SCAN
||
272 info
== XLOG_HEAP2_PRUNE_VACUUM_CLEANUP
)
274 xl_heap_prune
*xlrec
= (xl_heap_prune
*) rec
;
276 if (xlrec
->flags
& XLHP_HAS_CONFLICT_HORIZON
)
278 TransactionId conflict_xid
;
280 memcpy(&conflict_xid
, rec
+ SizeOfHeapPrune
, sizeof(TransactionId
));
282 appendStringInfo(buf
, "snapshotConflictHorizon: %u",
286 appendStringInfo(buf
, ", isCatalogRel: %c",
287 xlrec
->flags
& XLHP_IS_CATALOG_REL
? 'T' : 'F');
289 if (XLogRecHasBlockData(record
, 0))
292 OffsetNumber
*redirected
;
293 OffsetNumber
*nowdead
;
294 OffsetNumber
*nowunused
;
299 xlhp_freeze_plan
*plans
;
300 OffsetNumber
*frz_offsets
;
302 char *cursor
= XLogRecGetBlockData(record
, 0, &datalen
);
304 heap_xlog_deserialize_prune_and_freeze(cursor
, xlrec
->flags
,
305 &nplans
, &plans
, &frz_offsets
,
306 &nredirected
, &redirected
,
308 &nunused
, &nowunused
);
310 appendStringInfo(buf
, ", nplans: %u, nredirected: %u, ndead: %u, nunused: %u",
311 nplans
, nredirected
, ndead
, nunused
);
315 appendStringInfoString(buf
, ", plans:");
316 array_desc(buf
, plans
, sizeof(xlhp_freeze_plan
), nplans
,
317 &plan_elem_desc
, &frz_offsets
);
322 appendStringInfoString(buf
, ", redirected:");
323 array_desc(buf
, redirected
, sizeof(OffsetNumber
) * 2,
324 nredirected
, &redirect_elem_desc
, NULL
);
329 appendStringInfoString(buf
, ", dead:");
330 array_desc(buf
, nowdead
, sizeof(OffsetNumber
), ndead
,
331 &offset_elem_desc
, NULL
);
336 appendStringInfoString(buf
, ", unused:");
337 array_desc(buf
, nowunused
, sizeof(OffsetNumber
), nunused
,
338 &offset_elem_desc
, NULL
);
342 else if (info
== XLOG_HEAP2_VISIBLE
)
344 xl_heap_visible
*xlrec
= (xl_heap_visible
*) rec
;
346 appendStringInfo(buf
, "snapshotConflictHorizon: %u, flags: 0x%02X",
347 xlrec
->snapshotConflictHorizon
, xlrec
->flags
);
349 else if (info
== XLOG_HEAP2_MULTI_INSERT
)
351 xl_heap_multi_insert
*xlrec
= (xl_heap_multi_insert
*) rec
;
352 bool isinit
= (XLogRecGetInfo(record
) & XLOG_HEAP_INIT_PAGE
) != 0;
354 appendStringInfo(buf
, "ntuples: %d, flags: 0x%02X", xlrec
->ntuples
,
357 if (XLogRecHasBlockData(record
, 0) && !isinit
)
359 appendStringInfoString(buf
, ", offsets:");
360 array_desc(buf
, xlrec
->offsets
, sizeof(OffsetNumber
),
361 xlrec
->ntuples
, &offset_elem_desc
, NULL
);
364 else if (info
== XLOG_HEAP2_LOCK_UPDATED
)
366 xl_heap_lock_updated
*xlrec
= (xl_heap_lock_updated
*) rec
;
368 appendStringInfo(buf
, "xmax: %u, off: %u, ",
369 xlrec
->xmax
, xlrec
->offnum
);
370 infobits_desc(buf
, xlrec
->infobits_set
, "infobits");
371 appendStringInfo(buf
, ", flags: 0x%02X", xlrec
->flags
);
373 else if (info
== XLOG_HEAP2_NEW_CID
)
375 xl_heap_new_cid
*xlrec
= (xl_heap_new_cid
*) rec
;
377 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 ItemPointerGetBlockNumber(&(xlrec
->target_tid
)),
382 ItemPointerGetOffsetNumber(&(xlrec
->target_tid
)));
383 appendStringInfo(buf
, ", cmin: %u, cmax: %u, combo: %u",
384 xlrec
->cmin
, xlrec
->cmax
, xlrec
->combocid
);
389 heap_identify(uint8 info
)
391 const char *id
= NULL
;
393 switch (info
& ~XLR_INFO_MASK
)
395 case XLOG_HEAP_INSERT
:
398 case XLOG_HEAP_INSERT
| XLOG_HEAP_INIT_PAGE
:
401 case XLOG_HEAP_DELETE
:
404 case XLOG_HEAP_UPDATE
:
407 case XLOG_HEAP_UPDATE
| XLOG_HEAP_INIT_PAGE
:
410 case XLOG_HEAP_HOT_UPDATE
:
413 case XLOG_HEAP_HOT_UPDATE
| XLOG_HEAP_INIT_PAGE
:
414 id
= "HOT_UPDATE+INIT";
416 case XLOG_HEAP_TRUNCATE
:
419 case XLOG_HEAP_CONFIRM
:
425 case XLOG_HEAP_INPLACE
:
434 heap2_identify(uint8 info
)
436 const char *id
= NULL
;
438 switch (info
& ~XLR_INFO_MASK
)
440 case XLOG_HEAP2_PRUNE_ON_ACCESS
:
441 id
= "PRUNE_ON_ACCESS";
443 case XLOG_HEAP2_PRUNE_VACUUM_SCAN
:
444 id
= "PRUNE_VACUUM_SCAN";
446 case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP
:
447 id
= "PRUNE_VACUUM_CLEANUP";
449 case XLOG_HEAP2_VISIBLE
:
452 case XLOG_HEAP2_MULTI_INSERT
:
455 case XLOG_HEAP2_MULTI_INSERT
| XLOG_HEAP_INIT_PAGE
:
456 id
= "MULTI_INSERT+INIT";
458 case XLOG_HEAP2_LOCK_UPDATED
:
461 case XLOG_HEAP2_NEW_CID
:
464 case XLOG_HEAP2_REWRITE
: