|
| 1 | +use strict; |
| 2 | +use warnings; |
| 3 | + |
| 4 | +use PostgresNode; |
| 5 | +use TestLib; |
| 6 | +use Test::More tests => 22; |
| 7 | + |
| 8 | +# Test pg_xlogdump for timestamp output. |
| 9 | + |
| 10 | +sub command_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'); |
0 commit comments