@@ -31,6 +31,8 @@ static const char *progname;
31
31
static int WalSegSz ;
32
32
static volatile sig_atomic_t time_to_stop = false;
33
33
34
+ static const RelFileNode emptyRelFileNode = {0 , 0 , 0 };
35
+
34
36
typedef struct XLogDumpPrivate
35
37
{
36
38
TimeLineID timeline ;
@@ -55,6 +57,13 @@ typedef struct XLogDumpConfig
55
57
bool filter_by_rmgr_enabled ;
56
58
TransactionId filter_by_xid ;
57
59
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 ;
58
67
} XLogDumpConfig ;
59
68
60
69
typedef struct Stats
@@ -391,6 +400,59 @@ WALDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
391
400
return count ;
392
401
}
393
402
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
+
394
456
/*
395
457
* Calculate the size of a record, split into !FPI and FPI parts.
396
458
*/
@@ -765,6 +827,10 @@ usage(void)
765
827
printf (_ (" -b, --bkp-details output detailed information about backup blocks\n" ));
766
828
printf (_ (" -e, --end=RECPTR stop reading at WAL location RECPTR\n" ));
767
829
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" ));
768
834
printf (_ (" -n, --limit=N number of records to display\n" ));
769
835
printf (_ (" -p, --path=PATH directory in which to find log segment files or a\n"
770
836
" directory with a ./pg_wal that contains such files\n"
@@ -777,6 +843,7 @@ usage(void)
777
843
" (default: 1 or the value used in STARTSEG)\n" ));
778
844
printf (_ (" -V, --version output version information, then exit\n" ));
779
845
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" ));
780
847
printf (_ (" -z, --stats[=record] show statistics instead of records\n"
781
848
" (optionally, show per-record statistics)\n" ));
782
849
printf (_ (" -?, --help show this help, then exit\n" ));
@@ -800,12 +867,16 @@ main(int argc, char **argv)
800
867
801
868
static struct option long_options [] = {
802
869
{"bkp-details" , no_argument , NULL , 'b' },
870
+ {"block" , required_argument , NULL , 'k' },
803
871
{"end" , required_argument , NULL , 'e' },
804
872
{"follow" , no_argument , NULL , 'f' },
873
+ {"fork" , required_argument , NULL , 'F' },
874
+ {"fullpage" , no_argument , NULL , 'w' },
805
875
{"help" , no_argument , NULL , '?' },
806
876
{"limit" , required_argument , NULL , 'n' },
807
877
{"path" , required_argument , NULL , 'p' },
808
878
{"quiet" , no_argument , NULL , 'q' },
879
+ {"relation" , required_argument , NULL , 'l' },
809
880
{"rmgr" , required_argument , NULL , 'r' },
810
881
{"start" , required_argument , NULL , 's' },
811
882
{"timeline" , required_argument , NULL , 't' },
@@ -858,6 +929,11 @@ main(int argc, char **argv)
858
929
config .filter_by_rmgr_enabled = false;
859
930
config .filter_by_xid = InvalidTransactionId ;
860
931
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;
861
937
config .stats = false;
862
938
config .stats_per_record = false;
863
939
@@ -870,7 +946,7 @@ main(int argc, char **argv)
870
946
goto bad_argument ;
871
947
}
872
948
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" ,
874
950
long_options , & optindex )) != -1 )
875
951
{
876
952
switch (option )
@@ -890,6 +966,47 @@ main(int argc, char **argv)
890
966
case 'f' :
891
967
config .follow = true;
892
968
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 ;
893
1010
case 'n' :
894
1011
if (sscanf (optarg , "%d" , & config .stop_after_records ) != 1 )
895
1012
{
@@ -947,6 +1064,9 @@ main(int argc, char **argv)
947
1064
goto bad_argument ;
948
1065
}
949
1066
break ;
1067
+ case 'w' :
1068
+ config .filter_by_fpw = true;
1069
+ break ;
950
1070
case 'x' :
951
1071
if (sscanf (optarg , "%u" , & config .filter_by_xid ) != 1 )
952
1072
{
@@ -976,6 +1096,13 @@ main(int argc, char **argv)
976
1096
}
977
1097
}
978
1098
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
+
979
1106
if ((optind + 2 ) < argc )
980
1107
{
981
1108
pg_log_error ("too many command-line arguments (first is \"%s\")" ,
@@ -1148,6 +1275,21 @@ main(int argc, char **argv)
1148
1275
config .filter_by_xid != record -> xl_xid )
1149
1276
continue ;
1150
1277
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
+
1151
1293
/* perform any per-record work */
1152
1294
if (!config .quiet )
1153
1295
{
0 commit comments