Skip to content

Commit 127aea2

Browse files
committed
Add additional filtering options to pg_waldump.
Allow filtering by RelFileNode, BlockNumber, ForkNum and FPW. Author: David Christensen <david.christensen@crunchydata.com> Reviewed-by: Japin Li <japinli@hotmail.com> Reviewed-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> Reviewed-by: Cary Huang <cary.huang@highgo.ca> Reviewed-by: Thomas Munro <thomas.munro@gmail.com> Discussion: https://postgr.es/m/lzzgmgm6e5.fsf%40veeddrois.attlocal.net
1 parent ac9c5dc commit 127aea2

File tree

2 files changed

+192
-1
lines changed

2 files changed

+192
-1
lines changed

doc/src/sgml/ref/pg_waldump.sgml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,45 @@ PostgreSQL documentation
100100
</listitem>
101101
</varlistentry>
102102

103+
<varlistentry>
104+
<term><option>-k <replaceable>block</replaceable></option></term>
105+
<term><option>--block=<replaceable>block</replaceable></option></term>
106+
<listitem>
107+
<para>
108+
Only display records that modify the given block. The relation must
109+
also be provided with <option>--relation</option> or
110+
<option>-l</option>.
111+
</para>
112+
</listitem>
113+
</varlistentry>
114+
115+
<varlistentry>
116+
<term><option>-F <replaceable>fork</replaceable></option></term>
117+
<term><option>--fork=<replaceable>fork</replaceable></option></term>
118+
<listitem>
119+
<para>
120+
If provided, only display records that modify blocks in the given fork.
121+
The valid values are <literal>0</literal> for the main fork,
122+
<literal>1</literal> for the free space map,
123+
<literal>2</literal> for the visibility map,
124+
and <literal>3</literal> for the init fork.
125+
</para>
126+
</listitem>
127+
</varlistentry>
128+
129+
<varlistentry>
130+
<term><option>-l <replaceable>tblspc</replaceable>/<replaceable>db</replaceable>/<replaceable>rel</replaceable></option></term>
131+
<term><option>--relation=<replaceable>tblspc</replaceable>/<replaceable>db</replaceable>/<replaceable>rel</replaceable></option></term>
132+
<listitem>
133+
<para>
134+
Only display records that modify blocks in the given relation. The
135+
relation is specified with tablespace OID, database OID, and relfilenode
136+
separated by slashes, for example <literal>1234/12345/12345</literal>.
137+
This is the same format used for relations in the program's output.
138+
</para>
139+
</listitem>
140+
</varlistentry>
141+
103142
<varlistentry>
104143
<term><option>-n <replaceable>limit</replaceable></option></term>
105144
<term><option>--limit=<replaceable>limit</replaceable></option></term>
@@ -183,6 +222,16 @@ PostgreSQL documentation
183222
</listitem>
184223
</varlistentry>
185224

225+
<varlistentry>
226+
<term><option>-w</option></term>
227+
<term><option>--fullpage</option></term>
228+
<listitem>
229+
<para>
230+
Only display records that include full page images.
231+
</para>
232+
</listitem>
233+
</varlistentry>
234+
186235
<varlistentry>
187236
<term><option>-x <replaceable>xid</replaceable></option></term>
188237
<term><option>--xid=<replaceable>xid</replaceable></option></term>

src/bin/pg_waldump/pg_waldump.c

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ static const char *progname;
3131
static int WalSegSz;
3232
static volatile sig_atomic_t time_to_stop = false;
3333

34+
static const RelFileNode emptyRelFileNode = {0, 0, 0};
35+
3436
typedef struct XLogDumpPrivate
3537
{
3638
TimeLineID timeline;
@@ -55,6 +57,13 @@ typedef struct XLogDumpConfig
5557
bool filter_by_rmgr_enabled;
5658
TransactionId filter_by_xid;
5759
bool filter_by_xid_enabled;
60+
RelFileNode filter_by_relation;
61+
bool filter_by_extended;
62+
bool filter_by_relation_enabled;
63+
BlockNumber filter_by_relation_block;
64+
bool filter_by_relation_block_enabled;
65+
ForkNumber filter_by_relation_forknum;
66+
bool filter_by_fpw;
5867
} XLogDumpConfig;
5968

6069
typedef struct Stats
@@ -391,6 +400,59 @@ WALDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
391400
return count;
392401
}
393402

403+
/*
404+
* Boolean to return whether the given WAL record matches a specific relation
405+
* and optionally block.
406+
*/
407+
static bool
408+
XLogRecordMatchesRelationBlock(XLogReaderState *record,
409+
RelFileNode matchRnode,
410+
BlockNumber matchBlock,
411+
ForkNumber matchFork)
412+
{
413+
int block_id;
414+
415+
for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
416+
{
417+
RelFileNode rnode;
418+
ForkNumber forknum;
419+
BlockNumber blk;
420+
421+
if (!XLogRecHasBlockRef(record, block_id))
422+
continue;
423+
424+
XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk);
425+
426+
if ((matchFork == InvalidForkNumber || matchFork == forknum) &&
427+
(RelFileNodeEquals(matchRnode, emptyRelFileNode) ||
428+
RelFileNodeEquals(matchRnode, rnode)) &&
429+
(matchBlock == InvalidBlockNumber || matchBlock == blk))
430+
return true;
431+
}
432+
433+
return false;
434+
}
435+
436+
/*
437+
* Boolean to return whether the given WAL record contains a full page write.
438+
*/
439+
static bool
440+
XLogRecordHasFPW(XLogReaderState *record)
441+
{
442+
int block_id;
443+
444+
for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
445+
{
446+
if (!XLogRecHasBlockRef(record, block_id))
447+
continue;
448+
449+
if (XLogRecHasBlockImage(record, block_id))
450+
return true;
451+
}
452+
453+
return false;
454+
}
455+
394456
/*
395457
* Calculate the size of a record, split into !FPI and FPI parts.
396458
*/
@@ -765,6 +827,10 @@ usage(void)
765827
printf(_(" -b, --bkp-details output detailed information about backup blocks\n"));
766828
printf(_(" -e, --end=RECPTR stop reading at WAL location RECPTR\n"));
767829
printf(_(" -f, --follow keep retrying after reaching end of WAL\n"));
830+
printf(_(" -k, --block=N with --relation, only show records matching this block\n"));
831+
printf(_(" -F, --fork=N only show records matching a specific fork number\n"
832+
" (defaults to showing all)\n"));
833+
printf(_(" -l, --relation=N/N/N only show records that affect a specific relation\n"));
768834
printf(_(" -n, --limit=N number of records to display\n"));
769835
printf(_(" -p, --path=PATH directory in which to find log segment files or a\n"
770836
" directory with a ./pg_wal that contains such files\n"
@@ -777,6 +843,7 @@ usage(void)
777843
" (default: 1 or the value used in STARTSEG)\n"));
778844
printf(_(" -V, --version output version information, then exit\n"));
779845
printf(_(" -x, --xid=XID only show records with transaction ID XID\n"));
846+
printf(_(" -w, --fullpage only show records with a full page write\n"));
780847
printf(_(" -z, --stats[=record] show statistics instead of records\n"
781848
" (optionally, show per-record statistics)\n"));
782849
printf(_(" -?, --help show this help, then exit\n"));
@@ -800,12 +867,16 @@ main(int argc, char **argv)
800867

801868
static struct option long_options[] = {
802869
{"bkp-details", no_argument, NULL, 'b'},
870+
{"block", required_argument, NULL, 'k'},
803871
{"end", required_argument, NULL, 'e'},
804872
{"follow", no_argument, NULL, 'f'},
873+
{"fork", required_argument, NULL, 'F'},
874+
{"fullpage", no_argument, NULL, 'w'},
805875
{"help", no_argument, NULL, '?'},
806876
{"limit", required_argument, NULL, 'n'},
807877
{"path", required_argument, NULL, 'p'},
808878
{"quiet", no_argument, NULL, 'q'},
879+
{"relation", required_argument, NULL, 'l'},
809880
{"rmgr", required_argument, NULL, 'r'},
810881
{"start", required_argument, NULL, 's'},
811882
{"timeline", required_argument, NULL, 't'},
@@ -858,6 +929,11 @@ main(int argc, char **argv)
858929
config.filter_by_rmgr_enabled = false;
859930
config.filter_by_xid = InvalidTransactionId;
860931
config.filter_by_xid_enabled = false;
932+
config.filter_by_extended = false;
933+
config.filter_by_relation_enabled = false;
934+
config.filter_by_relation_block_enabled = false;
935+
config.filter_by_relation_forknum = InvalidForkNumber;
936+
config.filter_by_fpw = false;
861937
config.stats = false;
862938
config.stats_per_record = false;
863939

@@ -870,7 +946,7 @@ main(int argc, char **argv)
870946
goto bad_argument;
871947
}
872948

873-
while ((option = getopt_long(argc, argv, "be:fn:p:qr:s:t:x:z",
949+
while ((option = getopt_long(argc, argv, "be:fF:k:l:n:p:qr:s:t:wx:z",
874950
long_options, &optindex)) != -1)
875951
{
876952
switch (option)
@@ -890,6 +966,47 @@ main(int argc, char **argv)
890966
case 'f':
891967
config.follow = true;
892968
break;
969+
case 'F':
970+
{
971+
unsigned int forknum;
972+
973+
if (sscanf(optarg, "%u", &forknum) != 1 ||
974+
forknum > MAX_FORKNUM)
975+
{
976+
pg_log_error("could not parse valid fork number (0..%d) \"%s\"",
977+
MAX_FORKNUM, optarg);
978+
goto bad_argument;
979+
}
980+
config.filter_by_relation_forknum = (ForkNumber) forknum;
981+
config.filter_by_extended = true;
982+
}
983+
break;
984+
case 'k':
985+
if (sscanf(optarg, "%u", &config.filter_by_relation_block) != 1 ||
986+
!BlockNumberIsValid(config.filter_by_relation_block))
987+
{
988+
pg_log_error("could not parse valid block number \"%s\"", optarg);
989+
goto bad_argument;
990+
}
991+
config.filter_by_relation_block_enabled = true;
992+
config.filter_by_extended = true;
993+
break;
994+
case 'l':
995+
if (sscanf(optarg, "%u/%u/%u",
996+
&config.filter_by_relation.spcNode,
997+
&config.filter_by_relation.dbNode,
998+
&config.filter_by_relation.relNode) != 3 ||
999+
!OidIsValid(config.filter_by_relation.spcNode) ||
1000+
!OidIsValid(config.filter_by_relation.relNode))
1001+
{
1002+
pg_log_error("could not parse valid relation from \"%s\""
1003+
" (expecting \"tablespace OID/database OID/"
1004+
"relation filenode\")", optarg);
1005+
goto bad_argument;
1006+
}
1007+
config.filter_by_relation_enabled = true;
1008+
config.filter_by_extended = true;
1009+
break;
8931010
case 'n':
8941011
if (sscanf(optarg, "%d", &config.stop_after_records) != 1)
8951012
{
@@ -947,6 +1064,9 @@ main(int argc, char **argv)
9471064
goto bad_argument;
9481065
}
9491066
break;
1067+
case 'w':
1068+
config.filter_by_fpw = true;
1069+
break;
9501070
case 'x':
9511071
if (sscanf(optarg, "%u", &config.filter_by_xid) != 1)
9521072
{
@@ -976,6 +1096,13 @@ main(int argc, char **argv)
9761096
}
9771097
}
9781098

1099+
if (config.filter_by_relation_block_enabled &&
1100+
!config.filter_by_relation_enabled)
1101+
{
1102+
pg_log_error("--block option requires --relation option to be specified");
1103+
goto bad_argument;
1104+
}
1105+
9791106
if ((optind + 2) < argc)
9801107
{
9811108
pg_log_error("too many command-line arguments (first is \"%s\")",
@@ -1148,6 +1275,21 @@ main(int argc, char **argv)
11481275
config.filter_by_xid != record->xl_xid)
11491276
continue;
11501277

1278+
/* check for extended filtering */
1279+
if (config.filter_by_extended &&
1280+
!XLogRecordMatchesRelationBlock(xlogreader_state,
1281+
config.filter_by_relation_enabled ?
1282+
config.filter_by_relation :
1283+
emptyRelFileNode,
1284+
config.filter_by_relation_block_enabled ?
1285+
config.filter_by_relation_block :
1286+
InvalidBlockNumber,
1287+
config.filter_by_relation_forknum))
1288+
continue;
1289+
1290+
if (config.filter_by_fpw && !XLogRecordHasFPW(xlogreader_state))
1291+
continue;
1292+
11511293
/* perform any per-record work */
11521294
if (!config.quiet)
11531295
{

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy