|
| 1 | +use strict; |
| 2 | +use warnings; |
| 3 | + |
| 4 | +use PostgresNode; |
| 5 | +use TestLib; |
| 6 | +use Test::Moretests=> 22; |
| 7 | + |
| 8 | +# Test pg_xlogdump for timestamp output. |
| 9 | + |
| 10 | +subcommand_output |
| 11 | +{ |
| 12 | +my ($cmd,$expected_stdout,$test_name) =@_; |
| 13 | +my ($stdout,$stderr); |
| 14 | +print("# Running:" .join("", @{$cmd}) ."\n"); |
| 15 | +my$result = IPC::Run::run$cmd,'>', \$stdout,'2>', \$stderr; |
| 16 | +ok($result,"@$cmd exit code 0"); |
| 17 | +is($stderr,'',"@$cmd no stderr"); |
| 18 | +return$stdout; |
| 19 | +} |
| 20 | + |
| 21 | +my$node = get_new_node('main'); |
| 22 | +$node->init; |
| 23 | +$node->start; |
| 24 | +my$pgdata =$node->data_dir; |
| 25 | +my$xlogfilename0 =$node->safe_psql('postgres', |
| 26 | +"SELECT pg_xlogfile_name(pg_current_xlog_location())"); |
| 27 | +my$startseg ="$pgdata/pg_xlog/$xlogfilename0"; |
| 28 | +my$endseg =$startseg; |
| 29 | +$node->command_like( |
| 30 | +['pg_xlogdump','-S',"$pgdata/pg_xlog/$xlogfilename0" ], |
| 31 | +qr/pg_xlogdump: start timestamp:/, |
| 32 | +'pg_xlogdump prints start timestamp'); |
| 33 | +$node->command_like( |
| 34 | +['pg_xlogdump','-S',"$pgdata/pg_xlog/$xlogfilename0" ], |
| 35 | +qr/start timestamp[^\n]*\n((?!end timestamp).)*$/m, |
| 36 | +'pg_xlogdump does not print end timestamp by default'); |
| 37 | +$node->command_fails( |
| 38 | +['pg_xlogdump','-E',"$pgdata/pg_xlog/$xlogfilename0" ], |
| 39 | +'pg_xlogdump requires -S argument for printing start/end timestamps'); |
| 40 | +$node->command_fails( |
| 41 | +['pg_xlogdump','-S','-F','XXX',"$pgdata/pg_xlog/$xlogfilename0" ], |
| 42 | +'pg_xlogdump fails with invalid filter'); |
| 43 | +$node->command_like( |
| 44 | +['pg_xlogdump','-S','-F','XLOG_XACT_COMMIT,XLOG_XACT_ABORT',"$pgdata/pg_xlog/$xlogfilename0" ], |
| 45 | +qr/pg_xlogdump: start timestamp:/, |
| 46 | +'pg_xlogdump accepts filter "XLOG_XACT_COMMIT,XLOG_XACT_ABORT"'); |
| 47 | + |
| 48 | +open CONF,">>$pgdata/postgresql.conf"; |
| 49 | +print CONF"track_commit_timestamp = on\n"; |
| 50 | +close CONF; |
| 51 | +$node->restart; |
| 52 | + |
| 53 | +# create test tables and move to a new segment |
| 54 | +$node->safe_psql('postgres', |
| 55 | +"CREATE TABLE test1 (a int); CREATE TABLE test2 (a int); SELECT pg_switch_xlog()"); |
| 56 | + |
| 57 | +# get initial commit_timestamp and logfile_name |
| 58 | +$node->safe_psql('postgres', |
| 59 | +"INSERT INTO test1 VALUES (1)"); |
| 60 | +my$tx1_timestamp =$node->safe_psql('postgres', |
| 61 | +"SELECT pg_xact_commit_timestamp(xmin) FROM test1" |
| 62 | +); |
| 63 | +print("# transaction 1 commit timestamp:$tx1_timestamp\n"); |
| 64 | +my$xlogfilename1 =$node->safe_psql('postgres', |
| 65 | +"SELECT pg_xlogfile_name(pg_current_xlog_location())" |
| 66 | +); |
| 67 | + |
| 68 | +# produce some xlog segments |
| 69 | +for (my$i = 0;$i < 10;$i++) { |
| 70 | +$node->safe_psql('postgres', |
| 71 | +"BEGIN; INSERT INTO test2 VALUES ($i); COMMIT; SELECT pg_switch_xlog()"); |
| 72 | +} |
| 73 | + |
| 74 | +# get a segment from the middle of the sequence |
| 75 | +my$xlogfilenameM =$node->safe_psql('postgres', |
| 76 | +"SELECT pg_xlogfile_name(pg_current_xlog_location())"); |
| 77 | + |
| 78 | +# produce last segment |
| 79 | +$node->safe_psql('postgres', |
| 80 | +"SELECT pg_sleep(2); DELETE FROM test2; INSERT INTO test2 VALUES (1)"); |
| 81 | + |
| 82 | +# insert XLOG_XACT_ABORT to make sure that the default bahaviour is to show only COMMIT TIMESTAMP |
| 83 | +$node->safe_psql('postgres', |
| 84 | +"SELECT pg_sleep(2); BEGIN TRANSACTION; DELETE FROM test2; ROLLBACK TRANSACTION"); |
| 85 | + |
| 86 | +# get final logfile_name and commit_timestamp (and switch segment) |
| 87 | +my$xlogfilenameN =$node->safe_psql('postgres', |
| 88 | +"SELECT pg_xlogfile_name(pg_current_xlog_location())"); |
| 89 | +$node->safe_psql('postgres', |
| 90 | +"SELECT pg_switch_xlog()"); |
| 91 | +my$tx2_timestamp =$node->safe_psql('postgres', |
| 92 | +"SELECT pg_xact_commit_timestamp(xmin) FROM test2" |
| 93 | +); |
| 94 | +print("# transaction N commit timestamp:$tx2_timestamp\n"); |
| 95 | + |
| 96 | +# remove a file from the middle to make sure that the modified pg_xlogdump reads only necessary files |
| 97 | +unlink"$pgdata/pg_xlog/$xlogfilenameM"; |
| 98 | + |
| 99 | +# run pg_xlogdump to check it's output |
| 100 | +my$xld_output = command_output( |
| 101 | +['pg_xlogdump','-S','-E',"$pgdata/pg_xlog/$xlogfilename1","$pgdata/pg_xlog/$xlogfilenameN" ]); |
| 102 | +ok($xld_output =~qr/pg_xlogdump: start timestamp: ([^,]+), lsn: (.*)/,"start timestamp and lsn found"); |
| 103 | +my ($startts,$startlsn) = ($1,$2); |
| 104 | +ok($xld_output =~qr/pg_xlogdump: end timestamp: ([^,]+), lsn: (.*)/,"end timestamp and lsn found"); |
| 105 | + |
| 106 | +# check commit timestamps for first and last commits |
| 107 | +my ($endts,$endlsn) = ($1,$2); |
| 108 | +my$timediff1 =$node->safe_psql('postgres', |
| 109 | +"SELECT EXTRACT(EPOCH FROM pg_xact_commit_timestamp(xmin) -\'$startts\'::timestamp) FROM test1"); |
| 110 | +ok($timediff1 >= 0 &&$timediff1 < 1,"xlog start timestamp ($startts) equals to transaction 1 timestamp ($tx1_timestamp)"); |
| 111 | +my$timediff2 =$node->safe_psql('postgres', |
| 112 | +"SELECT EXTRACT(EPOCH FROM\'$endts\'::timestamp - pg_xact_commit_timestamp(xmin)) FROM test2"); |
| 113 | +ok($timediff2 >= 0 &&$timediff2 < 1,"xlog end timestamp ($endts) equals to transaction N timestamp ($tx2_timestamp)"); |
| 114 | + |
| 115 | +# check lsns |
| 116 | +my$lsndiff1 =$node->safe_psql('postgres', |
| 117 | +"SELECT\'$endlsn\'::pg_lsn -\'$startlsn\'::pg_lsn"); |
| 118 | +print("lsndiff1:$lsndiff1\n"); |
| 119 | +ok($lsndiff1 > 0,"xlog end lsn ($endlsn) greater than xlog start lsn ($startlsn)"); |
| 120 | +my$lsndiff2 =$node->safe_psql('postgres', |
| 121 | +"SELECT pg_current_xlog_location() -\'$endlsn\'::pg_lsn"); |
| 122 | +ok($lsndiff2 >= 0,"xlog current lsn greater than or equal to xlog end lsn"); |
| 123 | + |
| 124 | +# check search for non-existing record types |
| 125 | +$node->command_like( |
| 126 | +['pg_xlogdump','-S','-F','XLOG_RESTORE_POINT',"$pgdata/pg_xlog/$xlogfilename0" ], |
| 127 | +qr/xlog record with timestamp is not found/, |
| 128 | +'pg_xlogdump accepts filter XLOG_RESTORE_POINT and processes a segment without such records'); |