|
3 | 3 |
|
4 | 4 | use strict;
|
5 | 5 | use warnings;
|
| 6 | +use PostgreSQL::Test::Cluster; |
6 | 7 | use PostgreSQL::Test::Utils;
|
7 | 8 | use Test::More;
|
8 | 9 |
|
9 | 10 | program_help_ok('pg_waldump');
|
10 | 11 | program_version_ok('pg_waldump');
|
11 | 12 | program_options_handling_ok('pg_waldump');
|
12 | 13 |
|
| 14 | +# wrong number of arguments |
| 15 | +command_fails_like([ 'pg_waldump', ], qr/error: no arguments/, 'no arguments'); |
| 16 | +command_fails_like([ 'pg_waldump', 'foo', 'bar', 'baz' ], qr/error: too many command-line arguments/, 'too many arguments'); |
| 17 | + |
| 18 | +# invalid option arguments |
| 19 | +command_fails_like([ 'pg_waldump', '--block', 'bad' ], qr/error: invalid block number/, 'invalid block number'); |
| 20 | +command_fails_like([ 'pg_waldump', '--fork', 'bad' ], qr/error: invalid fork name/, 'invalid fork name'); |
| 21 | +command_fails_like([ 'pg_waldump', '--limit', 'bad' ], qr/error: invalid value/, 'invalid limit'); |
| 22 | +command_fails_like([ 'pg_waldump', '--relation', 'bad' ], qr/error: invalid relation/, 'invalid relation specification'); |
| 23 | +command_fails_like([ 'pg_waldump', '--rmgr', 'bad' ], qr/error: resource manager .* does not exist/, 'invalid rmgr name'); |
| 24 | +command_fails_like([ 'pg_waldump', '--start', 'bad' ], qr/error: invalid WAL location/, 'invalid start LSN'); |
| 25 | +command_fails_like([ 'pg_waldump', '--end', 'bad' ], qr/error: invalid WAL location/, 'invalid end LSN'); |
| 26 | + |
| 27 | +# rmgr list: If you add one to the list, consider also adding a test |
| 28 | +# case exercising the new rmgr below. |
| 29 | +command_like([ 'pg_waldump', '--rmgr=list'], qr/^XLOG |
| 30 | +Transaction |
| 31 | +Storage |
| 32 | +CLOG |
| 33 | +Database |
| 34 | +Tablespace |
| 35 | +MultiXact |
| 36 | +RelMap |
| 37 | +Standby |
| 38 | +Heap2 |
| 39 | +Heap |
| 40 | +Btree |
| 41 | +Hash |
| 42 | +Gin |
| 43 | +Gist |
| 44 | +Sequence |
| 45 | +SPGist |
| 46 | +BRIN |
| 47 | +CommitTs |
| 48 | +ReplicationOrigin |
| 49 | +Generic |
| 50 | +LogicalMessage$/, |
| 51 | + 'rmgr list'); |
| 52 | + |
| 53 | + |
| 54 | +my $node = PostgreSQL::Test::Cluster->new('main'); |
| 55 | +$node->init; |
| 56 | +$node->append_conf('postgresql.conf', q{ |
| 57 | +autovacuum = off |
| 58 | +checkpoint_timeout = 1h |
| 59 | +
|
| 60 | +# for standbydesc |
| 61 | +archive_mode=on |
| 62 | +archive_command='' |
| 63 | +
|
| 64 | +# for XLOG_HEAP_TRUNCATE |
| 65 | +wal_level=logical |
| 66 | +}); |
| 67 | +$node->start; |
| 68 | + |
| 69 | +my ($start_lsn, $start_walfile) = split /\|/, $node->safe_psql('postgres', q{SELECT pg_current_wal_insert_lsn(), pg_walfile_name(pg_current_wal_insert_lsn())}); |
| 70 | +
|
| 71 | +$node->safe_psql('postgres', q{ |
| 72 | +-- heap, btree, hash, sequence |
| 73 | +CREATE TABLE t1 (a int GENERATED ALWAYS AS IDENTITY, b text); |
| 74 | +CREATE INDEX i1a ON t1 USING btree (a); |
| 75 | +CREATE INDEX i1b ON t1 USING hash (b); |
| 76 | +INSERT INTO t1 VALUES (default, 'one'), (default, 'two'); |
| 77 | +DELETE FROM t1 WHERE b = 'one'; |
| 78 | +TRUNCATE t1; |
| 79 | +
|
| 80 | +-- abort |
| 81 | +START TRANSACTION; |
| 82 | +INSERT INTO t1 VALUES (default, 'three'); |
| 83 | +ROLLBACK; |
| 84 | +
|
| 85 | +-- unlogged/init fork |
| 86 | +CREATE UNLOGGED TABLE t2 (x int); |
| 87 | +CREATE INDEX i2 ON t2 USING btree (x); |
| 88 | +INSERT INTO t2 SELECT generate_series(1, 10); |
| 89 | +
|
| 90 | +-- gin |
| 91 | +CREATE TABLE gin_idx_tbl (id bigserial PRIMARY KEY, data jsonb); |
| 92 | +CREATE INDEX gin_idx ON gin_idx_tbl USING gin (data); |
| 93 | +INSERT INTO gin_idx_tbl |
| 94 | + WITH random_json AS ( |
| 95 | + SELECT json_object_agg(key, trunc(random() * 10)) as json_data |
| 96 | + FROM unnest(array['a', 'b', 'c']) as u(key)) |
| 97 | + SELECT generate_series(1,500), json_data FROM random_json; |
| 98 | +
|
| 99 | +-- gist, spgist |
| 100 | +CREATE TABLE gist_idx_tbl (p point); |
| 101 | +CREATE INDEX gist_idx ON gist_idx_tbl USING gist (p); |
| 102 | +CREATE INDEX spgist_idx ON gist_idx_tbl USING spgist (p); |
| 103 | +INSERT INTO gist_idx_tbl (p) VALUES (point '(1, 1)'), (point '(3, 2)'), (point '(6, 3)'); |
| 104 | +
|
| 105 | +-- brin |
| 106 | +CREATE TABLE brin_idx_tbl (col1 int, col2 text, col3 text ); |
| 107 | +CREATE INDEX brin_idx ON brin_idx_tbl USING brin (col1, col2, col3) WITH (autosummarize=on); |
| 108 | +INSERT INTO brin_idx_tbl SELECT generate_series(1, 10000), 'dummy', 'dummy'; |
| 109 | +UPDATE brin_idx_tbl SET col2 = 'updated' WHERE col1 BETWEEN 1 AND 5000; |
| 110 | +SELECT brin_summarize_range('brin_idx', 0); |
| 111 | +SELECT brin_desummarize_range('brin_idx', 0); |
| 112 | +
|
| 113 | +VACUUM; |
| 114 | +
|
| 115 | +-- logical message |
| 116 | +SELECT pg_logical_emit_message(true, 'foo', 'bar'); |
| 117 | +
|
| 118 | +-- relmap |
| 119 | +VACUUM FULL pg_authid; |
| 120 | +
|
| 121 | +-- database |
| 122 | +CREATE DATABASE d1; |
| 123 | +DROP DATABASE d1; |
| 124 | +}); |
| 125 | +
|
| 126 | +my $tblspc_path = PostgreSQL::Test::Utils::tempdir_short(); |
| 127 | +
|
| 128 | +$node->safe_psql('postgres', qq{ |
| 129 | +CREATE TABLESPACE ts1 LOCATION '$tblspc_path'; |
| 130 | +DROP TABLESPACE ts1; |
| 131 | +}); |
| 132 | +
|
| 133 | +my ($end_lsn, $end_walfile) = split /\|/, $node->safe_psql('postgres', q{SELECT pg_current_wal_insert_lsn(), pg_walfile_name(pg_current_wal_insert_lsn())}); |
| 134 | + |
| 135 | +my $default_ts_oid = $node->safe_psql('postgres', q{SELECT oid FROM pg_tablespace WHERE spcname = 'pg_default'}); |
| 136 | +my $postgres_db_oid = $node->safe_psql('postgres', q{SELECT oid FROM pg_database WHERE datname = 'postgres'}); |
| 137 | +my $rel_t1_oid = $node->safe_psql('postgres', q{SELECT oid FROM pg_class WHERE relname = 't1'}); |
| 138 | +my $rel_i1a_oid = $node->safe_psql('postgres', q{SELECT oid FROM pg_class WHERE relname = 'i1a'}); |
| 139 | + |
| 140 | +$node->stop; |
| 141 | + |
| 142 | + |
| 143 | +# various ways of specifying WAL range |
| 144 | +command_fails_like([ 'pg_waldump', 'foo', 'bar' ], qr/error: could not locate WAL file "foo"/, 'start file not found'); |
| 145 | +command_like([ 'pg_waldump', $node->data_dir . '/pg_wal/' . $start_walfile ], qr/./, 'runs with start segment specified'); |
| 146 | +command_fails_like([ 'pg_waldump', $node->data_dir . '/pg_wal/' . $start_walfile, 'bar' ], qr/error: could not open file "bar"/, 'end file not found'); |
| 147 | +command_like([ 'pg_waldump', $node->data_dir . '/pg_wal/' . $start_walfile, $node->data_dir . '/pg_wal/' . $end_walfile ], qr/./, 'runs with start and end segment specified'); |
| 148 | +command_fails_like([ 'pg_waldump', '-p', $node->data_dir ], qr/error: no start WAL location given/, 'path option requires start location'); |
| 149 | +command_like([ 'pg_waldump', '-p', $node->data_dir, '--start', $start_lsn, '--end', $end_lsn ], qr/./, 'runs with path option and start and end locations'); |
| 150 | +command_fails_like([ 'pg_waldump', '-p', $node->data_dir, '--start', $start_lsn ], qr/error: error in WAL record at/, 'falling off the end of the WAL results in an error'); |
| 151 | + |
| 152 | +command_like([ 'pg_waldump', '--quiet', $node->data_dir . '/pg_wal/' . $start_walfile ], qr/^$/, 'no output with --quiet option'); |
| 153 | +command_fails_like([ 'pg_waldump', '--quiet', '-p', $node->data_dir, '--start', $start_lsn ], qr/error: error in WAL record at/, 'errors are shown with --quiet'); |
| 154 | + |
| 155 | + |
| 156 | +# Helper function to test various options. Pass options as arguments. |
| 157 | +# Output lines are returned as array. |
| 158 | +sub test_pg_waldump |
| 159 | +{ |
| 160 | + local $Test::Builder::Level = $Test::Builder::Level + 1; |
| 161 | + my @opts = @_; |
| 162 | + |
| 163 | + my (@cmd, $stdout, $stderr, $result, @lines); |
| 164 | + |
| 165 | + @cmd = ('pg_waldump', '-p', $node->data_dir, '--start', $start_lsn, '--end', $end_lsn); |
| 166 | + push @cmd, @opts; |
| 167 | + $result = IPC::Run::run \@cmd, '>', \$stdout, '2>', \$stderr; |
| 168 | + ok($result, "pg_waldump @opts: runs ok"); |
| 169 | + is($stderr, '', "pg_waldump @opts: no stderr"); |
| 170 | + @lines = split /\n/, $stdout; |
| 171 | + ok(@lines > 0, "pg_waldump @opts: some lines are output"); |
| 172 | + return @lines; |
| 173 | +} |
| 174 | + |
| 175 | +my @lines; |
| 176 | + |
| 177 | +@lines = test_pg_waldump; |
| 178 | +is(grep(!/^rmgr: \w/, @lines), 0, 'all output lines are rmgr lines'); |
| 179 | + |
| 180 | +@lines = test_pg_waldump('--limit', 6); |
| 181 | +is(@lines, 6, 'limit option observed'); |
| 182 | + |
| 183 | +@lines = test_pg_waldump('--fullpage'); |
| 184 | +is(grep(!/^rmgr:.*\bFPW\b/, @lines), 0, 'all output lines are FPW'); |
| 185 | + |
| 186 | +@lines = test_pg_waldump('--stats'); |
| 187 | +like($lines[0], qr/WAL statistics/, "statistics on stdout"); |
| 188 | +is(grep(/^rmgr:/, @lines), 0, 'no rmgr lines output'); |
| 189 | + |
| 190 | +@lines = test_pg_waldump('--stats=record'); |
| 191 | +like($lines[0], qr/WAL statistics/, "statistics on stdout"); |
| 192 | +is(grep(/^rmgr:/, @lines), 0, 'no rmgr lines output'); |
| 193 | + |
| 194 | +@lines = test_pg_waldump('--rmgr', 'Btree'); |
| 195 | +is(grep(!/^rmgr: Btree/, @lines), 0, 'only Btree lines'); |
| 196 | + |
| 197 | +@lines = test_pg_waldump('--fork', 'init'); |
| 198 | +is(grep(!/fork init/, @lines), 0, 'only init fork lines'); |
| 199 | + |
| 200 | +@lines = test_pg_waldump('--relation', "$default_ts_oid/$postgres_db_oid/$rel_t1_oid"); |
| 201 | +is(grep(!/rel $default_ts_oid\/$postgres_db_oid\/$rel_t1_oid/, @lines), 0, 'only lines for selected relation'); |
| 202 | + |
| 203 | +@lines = test_pg_waldump('--relation', "$default_ts_oid/$postgres_db_oid/$rel_i1a_oid", '--block', 1); |
| 204 | +is(grep(!/\bblk 1\b/, @lines), 0, 'only lines for selected block'); |
| 205 | + |
| 206 | + |
13 | 207 | done_testing();
|
0 commit comments