Skip to content

Commit 122376f

Browse files
Show record information in pg_get_wal_block_info.
Expand the output parameters in pg_walinspect's pg_get_wal_block_info function to return additional information that was previously only available from pg_walinspect's pg_get_wal_records_info function. Some of the details are attributed to individual block references, rather than aggregated into whole-record values, since the function returns one row per block reference per WAL record (unlike pg_get_wal_records_info, which always returns one row per WAL record). This structure is much easier to work with when writing queries that track how individual blocks changed over time, or when attributing costs to individual blocks (not WAL records) is useful. This is the second time that pg_get_wal_block_info has been enhanced in recent weeks. Commit 9ecb134 expanded on the original version of the function added in commit c31cf1c (where it first appeared under the name pg_get_wal_fpi_info). There still hasn't been a stable release since commit c31cf1c, so no bump in the pg_walinspect extension version. Author: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> Author: Peter Geoghegan <pg@bowt.ie> Reviewed-By: Peter Geoghegan <pg@bowt.ie> Reviewed-By: Kyotaro HORIGUCHI <horikyota.ntt@gmail.com> Discussion: https://postgr.es/m/CALj2ACVRK5=Z+2ZVsjgTTSkfEnQzCuwny7iigpG7g1btk4Ws2A@mail.gmail.com
1 parent 63cc202 commit 122376f

File tree

5 files changed

+273
-160
lines changed

5 files changed

+273
-160
lines changed

contrib/pg_walinspect/expected/pg_walinspect.out

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ UPDATE sample_tbl SET col1 = col1 + 1 WHERE col1 = 1;
121121
SELECT pg_current_wal_lsn() AS wal_lsn4 \gset
122122
-- Check if we get block data from WAL record.
123123
SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_block_info(:'wal_lsn3', :'wal_lsn4')
124-
WHERE relfilenode = :'sample_tbl_oid' AND blockdata IS NOT NULL;
124+
WHERE relfilenode = :'sample_tbl_oid' AND block_data IS NOT NULL;
125125
ok
126126
----
127127
t
@@ -134,7 +134,7 @@ UPDATE sample_tbl SET col1 = col1 + 1 WHERE col1 = 2;
134134
SELECT pg_current_wal_lsn() AS wal_lsn6 \gset
135135
-- Check if we get FPI from WAL record.
136136
SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_block_info(:'wal_lsn5', :'wal_lsn6')
137-
WHERE relfilenode = :'sample_tbl_oid' AND fpi IS NOT NULL;
137+
WHERE relfilenode = :'sample_tbl_oid' AND block_fpi_data IS NOT NULL;
138138
ok
139139
----
140140
t

contrib/pg_walinspect/pg_walinspect--1.0--1.1.sql

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,26 @@ DROP FUNCTION pg_get_wal_stats_till_end_of_wal(pg_lsn, boolean);
1212
--
1313
CREATE FUNCTION pg_get_wal_block_info(IN start_lsn pg_lsn,
1414
IN end_lsn pg_lsn,
15-
OUT lsn pg_lsn,
16-
OUT blockid int2,
15+
OUT start_lsn pg_lsn,
16+
OUT end_lsn pg_lsn,
17+
OUT prev_lsn pg_lsn,
18+
OUT block_id int2,
1719
OUT reltablespace oid,
1820
OUT reldatabase oid,
1921
OUT relfilenode oid,
22+
OUT relforknumber int2,
2023
OUT relblocknumber int8,
21-
OUT forkname text,
22-
OUT blockdata bytea,
23-
OUT fpi bytea,
24-
OUT fpilen int4,
25-
OUT fpiinfo text[]
24+
OUT xid xid,
25+
OUT resource_manager text,
26+
OUT record_type text,
27+
OUT record_length int4,
28+
OUT main_data_length int4,
29+
OUT block_data_length int4,
30+
OUT block_fpi_length int4,
31+
OUT block_fpi_info text[],
32+
OUT description text,
33+
OUT block_data bytea,
34+
OUT block_fpi_data bytea
2635
)
2736
RETURNS SETOF record
2837
AS 'MODULE_PATHNAME', 'pg_get_wal_block_info'

contrib/pg_walinspect/pg_walinspect.c

Lines changed: 120 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -176,24 +176,29 @@ ReadNextXLogRecord(XLogReaderState *xlogreader)
176176
}
177177

178178
/*
179-
* Get a single WAL record info.
179+
* Output values that make up a row describing caller's WAL record.
180+
*
181+
* This function leaks memory. Caller may need to use its own custom memory
182+
* context.
183+
*
184+
* Keep this in sync with GetWALBlockInfo.
180185
*/
181186
static void
182187
GetWALRecordInfo(XLogReaderState *record, Datum *values,
183188
bool *nulls, uint32 ncols)
184189
{
185-
const char *id;
190+
const char *record_type;
186191
RmgrData desc;
187192
uint32 fpi_len = 0;
188193
StringInfoData rec_desc;
189194
StringInfoData rec_blk_ref;
190195
int i = 0;
191196

192197
desc = GetRmgr(XLogRecGetRmid(record));
193-
id = desc.rm_identify(XLogRecGetInfo(record));
198+
record_type = desc.rm_identify(XLogRecGetInfo(record));
194199

195-
if (id == NULL)
196-
id = psprintf("UNKNOWN (%x)", XLogRecGetInfo(record) & ~XLR_INFO_MASK);
200+
if (record_type == NULL)
201+
record_type = psprintf("UNKNOWN (%x)", XLogRecGetInfo(record) & ~XLR_INFO_MASK);
197202

198203
initStringInfo(&rec_desc);
199204
desc.rm_desc(&rec_desc, record);
@@ -209,7 +214,7 @@ GetWALRecordInfo(XLogReaderState *record, Datum *values,
209214
values[i++] = LSNGetDatum(XLogRecGetPrev(record));
210215
values[i++] = TransactionIdGetDatum(XLogRecGetXid(record));
211216
values[i++] = CStringGetTextDatum(desc.rm_name);
212-
values[i++] = CStringGetTextDatum(id);
217+
values[i++] = CStringGetTextDatum(record_type);
213218
values[i++] = UInt32GetDatum(XLogRecGetTotalLen(record));
214219
values[i++] = UInt32GetDatum(XLogRecGetDataLen(record));
215220
values[i++] = UInt32GetDatum(fpi_len);
@@ -229,24 +234,48 @@ GetWALRecordInfo(XLogReaderState *record, Datum *values,
229234

230235

231236
/*
232-
* Store a set of block information from a single record (FPI and block
233-
* information).
237+
* Output one or more rows in rsinfo tuple store, each describing a single
238+
* block reference from caller's WAL record. (Should only be called with
239+
* records that have block references.)
240+
*
241+
* This function leaks memory. Caller may need to use its own custom memory
242+
* context.
243+
*
244+
* Keep this in sync with GetWALRecordInfo.
234245
*/
235246
static void
236247
GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record)
237248
{
238-
#define PG_GET_WAL_BLOCK_INFO_COLS 11
249+
#define PG_GET_WAL_BLOCK_INFO_COLS 20
239250
int block_id;
240251
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
252+
RmgrData desc;
253+
const char *record_type;
254+
StringInfoData rec_desc;
255+
256+
Assert(XLogRecHasAnyBlockRefs(record));
257+
258+
desc = GetRmgr(XLogRecGetRmid(record));
259+
record_type = desc.rm_identify(XLogRecGetInfo(record));
260+
261+
if (record_type == NULL)
262+
record_type = psprintf("UNKNOWN (%x)",
263+
XLogRecGetInfo(record) & ~XLR_INFO_MASK);
264+
265+
initStringInfo(&rec_desc);
266+
desc.rm_desc(&rec_desc, record);
241267

242268
for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
243269
{
244270
DecodedBkpBlock *blk;
245271
BlockNumber blkno;
246272
RelFileLocator rnode;
247-
ForkNumber fork;
273+
ForkNumber forknum;
248274
Datum values[PG_GET_WAL_BLOCK_INFO_COLS] = {0};
249275
bool nulls[PG_GET_WAL_BLOCK_INFO_COLS] = {0};
276+
uint32 block_data_len = 0,
277+
block_fpi_len = 0;
278+
ArrayType *block_fpi_info = NULL;
250279
int i = 0;
251280

252281
if (!XLogRecHasBlockRef(record, block_id))
@@ -255,99 +284,117 @@ GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record)
255284
blk = XLogRecGetBlock(record, block_id);
256285

257286
(void) XLogRecGetBlockTagExtended(record, block_id,
258-
&rnode, &fork, &blkno, NULL);
287+
&rnode, &forknum, &blkno, NULL);
259288

289+
/* Save block_data_len */
290+
if (blk->has_data)
291+
block_data_len = blk->data_len;
292+
293+
if (blk->has_image)
294+
{
295+
/* Block reference has an FPI, so prepare relevant output */
296+
int bitcnt;
297+
int cnt = 0;
298+
Datum *flags;
299+
300+
/* Save block_fpi_len */
301+
block_fpi_len = blk->bimg_len;
302+
303+
/* Construct and save block_fpi_info */
304+
bitcnt = pg_popcount((const char *) &blk->bimg_info,
305+
sizeof(uint8));
306+
flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
307+
if ((blk->bimg_info & BKPIMAGE_HAS_HOLE) != 0)
308+
flags[cnt++] = CStringGetTextDatum("HAS_HOLE");
309+
if (blk->apply_image)
310+
flags[cnt++] = CStringGetTextDatum("APPLY");
311+
if ((blk->bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
312+
flags[cnt++] = CStringGetTextDatum("COMPRESS_PGLZ");
313+
if ((blk->bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
314+
flags[cnt++] = CStringGetTextDatum("COMPRESS_LZ4");
315+
if ((blk->bimg_info & BKPIMAGE_COMPRESS_ZSTD) != 0)
316+
flags[cnt++] = CStringGetTextDatum("COMPRESS_ZSTD");
317+
318+
Assert(cnt <= bitcnt);
319+
block_fpi_info = construct_array_builtin(flags, cnt, TEXTOID);
320+
}
321+
322+
/* start_lsn, end_lsn, prev_lsn, and blockid outputs */
260323
values[i++] = LSNGetDatum(record->ReadRecPtr);
324+
values[i++] = LSNGetDatum(record->EndRecPtr);
325+
values[i++] = LSNGetDatum(XLogRecGetPrev(record));
261326
values[i++] = Int16GetDatum(block_id);
327+
328+
/* relfile and block related outputs */
262329
values[i++] = ObjectIdGetDatum(blk->rlocator.spcOid);
263330
values[i++] = ObjectIdGetDatum(blk->rlocator.dbOid);
264331
values[i++] = ObjectIdGetDatum(blk->rlocator.relNumber);
332+
values[i++] = Int16GetDatum(forknum);
265333
values[i++] = Int64GetDatum((int64) blkno);
266334

267-
if (fork >= 0 && fork <= MAX_FORKNUM)
268-
values[i++] = CStringGetTextDatum(forkNames[fork]);
335+
/* xid, resource_manager, and record_type outputs */
336+
values[i++] = TransactionIdGetDatum(XLogRecGetXid(record));
337+
values[i++] = CStringGetTextDatum(desc.rm_name);
338+
values[i++] = CStringGetTextDatum(record_type);
339+
340+
/*
341+
* record_length, main_data_length, block_data_len, and
342+
* block_fpi_length outputs
343+
*/
344+
values[i++] = UInt32GetDatum(XLogRecGetTotalLen(record));
345+
values[i++] = UInt32GetDatum(XLogRecGetDataLen(record));
346+
values[i++] = UInt32GetDatum(block_data_len);
347+
values[i++] = UInt32GetDatum(block_fpi_len);
348+
349+
/* block_fpi_info (text array) output */
350+
if (block_fpi_info)
351+
values[i++] = PointerGetDatum(block_fpi_info);
269352
else
270-
ereport(ERROR,
271-
(errcode(ERRCODE_INTERNAL_ERROR),
272-
errmsg_internal("invalid fork number: %u", fork)));
353+
nulls[i++] = true;
273354

274-
/* Block data */
355+
/* description output (describes WAL record) */
356+
if (rec_desc.len > 0)
357+
values[i++] = CStringGetTextDatum(rec_desc.data);
358+
else
359+
nulls[i++] = true;
360+
361+
/* block_data output */
275362
if (blk->has_data)
276363
{
277-
bytea *raw_data;
364+
bytea *block_data;
278365

279-
/* Initialize bytea buffer to copy the data to */
280-
raw_data = (bytea *) palloc(blk->data_len + VARHDRSZ);
281-
SET_VARSIZE(raw_data, blk->data_len + VARHDRSZ);
282-
283-
/* Copy the data */
284-
memcpy(VARDATA(raw_data), blk->data, blk->data_len);
285-
values[i++] = PointerGetDatum(raw_data);
366+
block_data = (bytea *) palloc(block_data_len + VARHDRSZ);
367+
SET_VARSIZE(block_data, block_data_len + VARHDRSZ);
368+
memcpy(VARDATA(block_data), blk->data, block_data_len);
369+
values[i++] = PointerGetDatum(block_data);
286370
}
287371
else
288-
{
289-
/* No data, so set this field to NULL */
290372
nulls[i++] = true;
291-
}
292373

374+
/* block_fpi_data output */
293375
if (blk->has_image)
294376
{
295377
PGAlignedBlock buf;
296378
Page page;
297-
bytea *raw_page;
298-
int bitcnt;
299-
int cnt = 0;
300-
Datum *flags;
301-
ArrayType *a;
379+
bytea *block_fpi_data;
302380

303381
page = (Page) buf.data;
304-
305-
/* Full page image exists, so let's save it */
306382
if (!RestoreBlockImage(record, block_id, page))
307383
ereport(ERROR,
308384
(errcode(ERRCODE_INTERNAL_ERROR),
309385
errmsg_internal("%s", record->errormsg_buf)));
310386

311-
/* Initialize bytea buffer to copy the FPI to */
312-
raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
313-
SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
314-
315-
/* Take a verbatim copy of the FPI */
316-
memcpy(VARDATA(raw_page), page, BLCKSZ);
317-
318-
values[i++] = PointerGetDatum(raw_page);
319-
values[i++] = UInt32GetDatum(blk->bimg_len);
320-
321-
/* FPI flags */
322-
bitcnt = pg_popcount((const char *) &blk->bimg_info,
323-
sizeof(uint8));
324-
/* Build set of raw flags */
325-
flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
326-
327-
if ((blk->bimg_info & BKPIMAGE_HAS_HOLE) != 0)
328-
flags[cnt++] = CStringGetTextDatum("HAS_HOLE");
329-
if (blk->apply_image)
330-
flags[cnt++] = CStringGetTextDatum("APPLY");
331-
if ((blk->bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
332-
flags[cnt++] = CStringGetTextDatum("COMPRESS_PGLZ");
333-
if ((blk->bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
334-
flags[cnt++] = CStringGetTextDatum("COMPRESS_LZ4");
335-
if ((blk->bimg_info & BKPIMAGE_COMPRESS_ZSTD) != 0)
336-
flags[cnt++] = CStringGetTextDatum("COMPRESS_ZSTD");
337-
338-
Assert(cnt <= bitcnt);
339-
a = construct_array_builtin(flags, cnt, TEXTOID);
340-
values[i++] = PointerGetDatum(a);
387+
block_fpi_data = (bytea *) palloc(BLCKSZ + VARHDRSZ);
388+
SET_VARSIZE(block_fpi_data, BLCKSZ + VARHDRSZ);
389+
memcpy(VARDATA(block_fpi_data), page, BLCKSZ);
390+
values[i++] = PointerGetDatum(block_fpi_data);
341391
}
342392
else
343-
{
344-
/* No full page image, so store NULLs for all its fields */
345-
memset(&nulls[i], true, 3 * sizeof(bool));
346-
i += 3;
347-
}
393+
nulls[i++] = true;
348394

349395
Assert(i == PG_GET_WAL_BLOCK_INFO_COLS);
350396

397+
/* Store a tuple for this block reference */
351398
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
352399
values, nulls);
353400
}
@@ -356,11 +403,7 @@ GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record)
356403
}
357404

358405
/*
359-
* Get information about all the blocks saved in WAL records between start
360-
* and end LSNs. This produces information about the full page images with
361-
* their relation information, and the data saved in each block associated
362-
* to a record. Decompression is applied to the full page images, if
363-
* necessary.
406+
* Get WAL record info, unnested by block reference
364407
*/
365408
Datum
366409
pg_get_wal_block_info(PG_FUNCTION_ARGS)
@@ -484,7 +527,7 @@ ValidateInputLSNs(XLogRecPtr start_lsn, XLogRecPtr *end_lsn)
484527
}
485528

486529
/*
487-
* Get info and data of all WAL records between start LSN and end LSN.
530+
* Get info of all WAL records between start LSN and end LSN.
488531
*/
489532
static void
490533
GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
@@ -536,7 +579,7 @@ GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
536579
}
537580

538581
/*
539-
* Get info and data of all WAL records between start LSN and end LSN.
582+
* Get info of all WAL records between start LSN and end LSN.
540583
*/
541584
Datum
542585
pg_get_wal_records_info(PG_FUNCTION_ARGS)

contrib/pg_walinspect/sql/pg_walinspect.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ UPDATE sample_tbl SET col1 = col1 + 1 WHERE col1 = 1;
7878
SELECT pg_current_wal_lsn() AS wal_lsn4 \gset
7979
-- Check if we get block data from WAL record.
8080
SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_block_info(:'wal_lsn3', :'wal_lsn4')
81-
WHERE relfilenode = :'sample_tbl_oid' AND blockdata IS NOT NULL;
81+
WHERE relfilenode = :'sample_tbl_oid' AND block_data IS NOT NULL;
8282

8383
-- Force full-page image on the next update.
8484
SELECT pg_current_wal_lsn() AS wal_lsn5 \gset
@@ -87,7 +87,7 @@ UPDATE sample_tbl SET col1 = col1 + 1 WHERE col1 = 2;
8787
SELECT pg_current_wal_lsn() AS wal_lsn6 \gset
8888
-- Check if we get FPI from WAL record.
8989
SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_block_info(:'wal_lsn5', :'wal_lsn6')
90-
WHERE relfilenode = :'sample_tbl_oid' AND fpi IS NOT NULL;
90+
WHERE relfilenode = :'sample_tbl_oid' AND block_fpi_data IS NOT NULL;
9191

9292
-- ===================================================================
9393
-- Tests for permissions

0 commit comments

Comments
 (0)