Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)