|
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 | +subtest_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();
|