# Copyright (c) 2021-2025, PostgreSQL Global Development Group
use strict;
use warnings FATAL => 'all';
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
my $tempdir = PostgreSQL::Test::Utils::tempdir;
############################################################### # Definition of the pg_dump runs to make. # # Each of these runs are named and those names are used below # to define how each test should (or shouldn't) treat a result # from a given run. # # test_key indicates that a given run should simply use the same # set of like/unlike tests as another run, and which run that is. # # compile_option indicates if the commands run depend on a compilation # option, if any. This can be used to control if tests should be # skipped when a build dependency is not satisfied. # # dump_cmd is the pg_dump command to run, which is an array of # the full command and arguments to run. Note that this is run # using $node->command_ok(), so the port does not need to be # specified and is pulled from $PGPORT, which is set by the # PostgreSQL::Test::Cluster system. # # compress_cmd is the utility command for (de)compression, if any. # Note that this should generally be used on pg_dump's output # either to generate a text file to run the through the tests, or # to test pg_restore's ability to parse manually compressed files # that otherwise pg_dump does not compress on its own (e.g. *.toc). # # glob_patterns is an optional array consisting of strings compilable # with glob() to check the files generated after a dump. # # command_like is an optional utility that can be used to compare # the output generated by a command on the contents of an existing # dump, like the TOC description of a pg_restore command. # # restore_cmd is the pg_restore command to run, if any. Note # that this should generally be used when the pg_dump goes to # a non-text file and that the restore can then be used to # generate a text file to run through the tests from the # non-text file generated by pg_dump. # # TODO: Have pg_restore actually restore to an independent # database and then pg_dump *that* database (or something along # those lines) to validate that part of the process.
my $supports_icu = ($ENV{with_icu} eq 'yes');
my $supports_gzip = check_pg_config("#define HAVE_LIBZ 1");
my $supports_lz4 = check_pg_config("#define USE_LZ4 1");
my $supports_zstd = check_pg_config("#define USE_ZSTD 1");
# Do not use --no-sync to give test coverage for data sync. # By default, the custom format compresses its data file # when the code is compiled with gzip support, and lets them # uncompressed when not compiled with it.
defaults_custom_format => {
test_key => 'defaults',
dump_cmd => [ 'pg_dump', '--format' => 'custom', '--file' => "$tempdir/defaults_custom_format.dump", '--statistics', 'postgres',
],
restore_cmd => [ 'pg_restore', '--format' => 'custom', '--file' => "$tempdir/defaults_custom_format.sql", '--statistics', "$tempdir/defaults_custom_format.dump",
],
command_like => {
command => [ 'pg_restore', '--list', "$tempdir/defaults_custom_format.dump",
],
expected => $supports_gzip
? qr/Compression: gzip/
: qr/Compression: none/,
name => 'data content is gzip-compressed by default if available',
},
},
# Do not use --no-sync to give test coverage for data sync. # By default, the directory format compresses its data files # when the code is compiled with gzip support, and lets them # uncompressed when not compiled with it.
defaults_dir_format => {
test_key => 'defaults',
dump_cmd => [ 'pg_dump', '--format' => 'directory', '--file' => "$tempdir/defaults_dir_format", '--statistics', 'postgres',
],
restore_cmd => [ 'pg_restore', '--format' => 'directory', '--file' => "$tempdir/defaults_dir_format.sql", '--statistics', "$tempdir/defaults_dir_format",
],
command_like => {
command =>
[ 'pg_restore', '--list', "$tempdir/defaults_dir_format", ],
expected => $supports_gzip ? qr/Compression: gzip/
: qr/Compression: none/,
name => 'data content is gzip-compressed by default',
},
glob_patterns => [ "$tempdir/defaults_dir_format/toc.dat", "$tempdir/defaults_dir_format/blobs_*.toc",
$supports_gzip ? "$tempdir/defaults_dir_format/*.dat.gz"
: "$tempdir/defaults_dir_format/*.dat",
],
},
# Do not use --no-sync to give test coverage for data sync.
defaults_parallel => {
test_key => 'defaults',
dump_cmd => [ 'pg_dump', '--format' => 'directory', '--jobs' => 2, '--file' => "$tempdir/defaults_parallel", '--statistics', 'postgres',
],
restore_cmd => [ 'pg_restore', '--file' => "$tempdir/defaults_parallel.sql", '--statistics', "$tempdir/defaults_parallel",
],
},
############################################################### # Definition of the tests to run. # # Each test is defined using the log message that will be used. # # A regexp should be defined for each test which provides the # basis for the test. That regexp will be run against the output # file of each of the runs which the test is to be run against # and the success of the result will depend on if the regexp # result matches the expected 'like' or 'unlike' case. # # The runs listed as 'like' will be checked if they match the # regexp and, if so, the test passes. All runs which are not # listed as 'like' will be checked to ensure they don't match # the regexp; if they do, the test will fail. # # The below hashes provide convenience sets of runs. Individual # runs can be excluded from a general hash by placing that run # into the 'unlike' section. # # For example, there is an 'exclude_test_table' run which runs a # full pg_dump but with an exclude flag to not include the test # table. The CREATE TABLE test which creates the test table is # defined with %full_runs but then has 'exclude_test_table' in # its 'unlike' list, excluding that test. # # There can then be a 'create_sql' and 'create_order' for a # given test. The 'create_sql' commands are collected up in # 'create_order' and then run against the database prior to any # of the pg_dump runs happening. This is what "seeds" the # system with objects to be dumped out. # # There can be a flag called 'lz4', which can be set if the test # case depends on LZ4. Tests marked with this flag are skipped if # the build used does not support LZ4. # # Building of this hash takes a bit of time as all of the regexps # included in it are compiled. This greatly improves performance # as the regexps are used for each run the test applies to.
# Tests which target the 'dump_test' schema, specifically.
my %dump_test_schema_runs = (
only_dump_test_schema => 1,
only_dump_measurement => 1,
test_schema_plus_large_objects => 1,);
# Tests which are considered 'full' dumps by pg_dump, but there # are flags used to exclude specific items (ACLs, LOs, etc).
my %full_runs = (
binary_upgrade => 1, clean => 1,
clean_if_exists => 1,
compression => 1,
createdb => 1,
defaults => 1,
exclude_dump_test_schema => 1,
exclude_test_table => 1,
exclude_test_table_data => 1,
exclude_measurement => 1,
exclude_measurement_data => 1,
no_toast_compression => 1,
no_large_objects => 1,
no_owner => 1,
no_policies => 1,
no_policies_restore => 1,
no_privs => 1,
no_statistics => 1,
no_subscriptions => 1,
no_subscriptions_restore => 1,
no_table_access_method => 1,
pg_dumpall_dbprivs => 1,
pg_dumpall_exclude => 1,
schema_only => 1,
schema_only_with_statistics => 1,);
# This is where the actual tests are defined.
my %tests = ( 'restrict' => {
all_runs => 1,
regexp => qr/^\\restrict [a-zA-Z0-9]+$/m,
},
'ALTER DEFAULT PRIVILEGES FOR ROLE regress_dump_test_role GRANT' => {
create_order => 14,
create_sql => 'ALTER DEFAULT PRIVILEGES FOR ROLE regress_dump_test_role IN SCHEMA dump_test
GRANT SELECT ON TABLES TO regress_dump_test_role;',
regexp => qr/^
\QALTER DEFAULT PRIVILEGES \E
\QFOR ROLE regress_dump_test_role IN SCHEMA dump_test \E
\QGRANT SELECT ON TABLES TO regress_dump_test_role;\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_post_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
no_privs => 1,
only_dump_measurement => 1,
},
},
'ALTER DEFAULT PRIVILEGES FOR ROLE regress_dump_test_role GRANT EXECUTE ON FUNCTIONS'
=> {
create_order => 15,
create_sql => 'ALTER DEFAULT PRIVILEGES FOR ROLE regress_dump_test_role IN SCHEMA dump_test
GRANT EXECUTE ON FUNCTIONS TO regress_dump_test_role;',
regexp => qr/^
\QALTER DEFAULT PRIVILEGES \E
\QFOR ROLE regress_dump_test_role IN SCHEMA dump_test \E
\QGRANT ALL ON FUNCTIONS TO regress_dump_test_role;\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_post_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
no_privs => 1,
only_dump_measurement => 1,
},
},
'ALTER DEFAULT PRIVILEGES FOR ROLE regress_dump_test_role REVOKE' => {
create_order => 55,
create_sql => 'ALTER DEFAULT PRIVILEGES FOR ROLE regress_dump_test_role
REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;',
regexp => qr/^
\QALTER DEFAULT PRIVILEGES \E
\QFOR ROLE regress_dump_test_role \E
\QREVOKE ALL ON FUNCTIONS FROM PUBLIC;\E
/xm,
like => { %full_runs, section_post_data => 1, },
unlike => { no_privs => 1, },
},
'ALTER DEFAULT PRIVILEGES FOR ROLE regress_dump_test_role REVOKE SELECT'
=> {
create_order => 56,
create_sql => 'ALTER DEFAULT PRIVILEGES FOR ROLE regress_dump_test_role
REVOKE SELECT ON TABLES FROM regress_dump_test_role;',
regexp => qr/^
\QALTER DEFAULT PRIVILEGES \E
\QFOR ROLE regress_dump_test_role \E
\QREVOKE ALL ON TABLES FROM regress_dump_test_role;\E\n
\QALTER DEFAULT PRIVILEGES \E
\QFOR ROLE regress_dump_test_role \E
\QGRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLES TO regress_dump_test_role;\E
/xm,
like => { %full_runs, section_post_data => 1, },
unlike => { no_privs => 1, },
},
'ALTER ROLE regress_dump_test_role' => {
regexp => qr/^
\QALTER ROLE regress_dump_test_role WITH \E
\QNOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN \E
\QNOREPLICATION NOBYPASSRLS;\E
/xm,
like => {
pg_dumpall_dbprivs => 1,
pg_dumpall_globals => 1,
pg_dumpall_globals_clean => 1,
pg_dumpall_exclude => 1,
},
},
'CONSTRAINT NOT NULL / NOT VALID' => {
create_sql => 'CREATE TABLE dump_test.test_table_nn (
col1 int);
CREATE TABLE dump_test.test_table_nn_2 (
col1 int NOT NULL);
CREATE TABLE dump_test.test_table_nn_chld1 (
) INHERITS (dump_test.test_table_nn);
CREATE TABLE dump_test.test_table_nn_chld2 (
col1 int
) INHERITS (dump_test.test_table_nn);
CREATE TABLE dump_test.test_table_nn_chld3 (
) INHERITS (dump_test.test_table_nn, dump_test.test_table_nn_2);
ALTER TABLE dump_test.test_table_nn ADD CONSTRAINT nn NOT NULL col1 NOT VALID;
ALTER TABLE dump_test.test_table_nn_chld1 VALIDATE CONSTRAINT nn;
ALTER TABLE dump_test.test_table_nn_chld2 VALIDATE CONSTRAINT nn;
COMMENT ON CONSTRAINT nn ON dump_test.test_table_nn IS \'nn comment is valid\';
COMMENT ON CONSTRAINT nn ON dump_test.test_table_nn_chld2 IS \'nn_chld2 comment is valid\';',
regexp => qr/^
\QALTER TABLE dump_test.test_table_nn\E \n^\s+
\QADD CONSTRAINT nn NOT NULL col1 NOT VALID;\E
/xm,
like => {
%full_runs, %dump_test_schema_runs, section_post_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
# This constraint is invalid therefore it goes in SECTION_POST_DATA 'COMMENT ON CONSTRAINT ON test_table_nn' => {
regexp => qr/^
\QCOMMENT ON CONSTRAINT nn ON dump_test.test_table_nn IS\E
/xm,
like => {
%full_runs, %dump_test_schema_runs, section_post_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
# This constraint is valid therefore it goes in SECTION_PRE_DATA 'COMMENT ON CONSTRAINT ON test_table_chld2' => {
regexp => qr/^
\QCOMMENT ON CONSTRAINT nn ON dump_test.test_table_nn_chld2 IS\E
/xm,
like => {
%full_runs, %dump_test_schema_runs, section_pre_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CONSTRAINT NOT NULL / NOT VALID (child1)' => {
regexp => qr/^
\QCREATE TABLE dump_test.test_table_nn_chld1 (\E\n
^\s+\QCONSTRAINT nn NOT NULL col1\E$
/xm,
like => {
%full_runs, %dump_test_schema_runs, section_pre_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
binary_upgrade => 1,
},
},
'CONSTRAINT NOT NULL / NOT VALID (child2)' => {
regexp => qr/^
\QCREATE TABLE dump_test.test_table_nn_chld2 (\E\n
^\s+\Qcol1 integer CONSTRAINT nn NOT NULL\E$
/xm,
like => {
%full_runs, %dump_test_schema_runs, section_pre_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON DATABASE postgres' => {
regexp => qr/^COMMENT ON DATABASE postgres IS .+;/m,
# Should appear in the same tests as "CREATE DATABASE postgres"
like => { createdb => 1, },
},
'COMMENT ON EXTENSION plpgsql' => {
regexp => qr/^COMMENT ON EXTENSION plpgsql IS .+;/m,
# this shouldn't ever get emitted anymore
like => {},
},
'COMMENT ON SCHEMA public' => {
regexp => qr/^COMMENT ON SCHEMA public IS .+;/m, # regress_public_owner emits this, due to create_sql of next test
like => {
pg_dumpall_dbprivs => 1,
pg_dumpall_exclude => 1,
},
},
'COMMENT ON SCHEMA public IS NULL' => {
database => 'regress_public_owner',
create_order => 100,
create_sql => 'COMMENT ON SCHEMA public IS NULL;',
regexp => qr/^COMMENT ON SCHEMA public IS '';/m,
like => { defaults_public_owner => 1 },
},
'COMMENT ON TABLE dump_test.test_table' => {
create_order => 36,
create_sql => 'COMMENT ON TABLE dump_test.test_table
IS \'comment on table\';',
regexp =>
qr/^\QCOMMENT ON TABLE dump_test.test_table IS 'comment on table';\E/m,
like => {
%full_runs,
%dump_test_schema_runs,
only_dump_test_table => 1,
section_pre_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
exclude_test_table => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON COLUMN dump_test.test_table.col1' => {
create_order => 36,
create_sql => 'COMMENT ON COLUMN dump_test.test_table.col1
IS \'comment on column\';',
regexp => qr/^
\QCOMMENT ON COLUMN dump_test.test_table.col1 IS 'comment on column';\E
/xm,
like => {
%full_runs,
%dump_test_schema_runs,
only_dump_test_table => 1,
section_pre_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
exclude_test_table => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON COLUMN dump_test.composite.f1' => {
create_order => 44,
create_sql => 'COMMENT ON COLUMN dump_test.composite.f1
IS \'comment on column of type\';',
regexp => qr/^
\QCOMMENT ON COLUMN dump_test.composite.f1 IS 'comment on column of type';\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON COLUMN dump_test.test_second_table.col1' => {
create_order => 63,
create_sql => 'COMMENT ON COLUMN dump_test.test_second_table.col1
IS \'comment on column col1\';',
regexp => qr/^
\QCOMMENT ON COLUMN dump_test.test_second_table.col1 IS 'comment on column col1';\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON COLUMN dump_test.test_second_table.col2' => {
create_order => 64,
create_sql => 'COMMENT ON COLUMN dump_test.test_second_table.col2
IS \'comment on column col2\';',
regexp => qr/^
\QCOMMENT ON COLUMN dump_test.test_second_table.col2 IS 'comment on column col2';\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON CONVERSION dump_test.test_conversion' => {
create_order => 79,
create_sql => 'COMMENT ON CONVERSION dump_test.test_conversion
IS \'comment on test conversion\';',
regexp =>
qr/^\QCOMMENT ON CONVERSION dump_test.test_conversion IS 'comment on test conversion';\E/m,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON COLLATION test0' => {
create_order => 77,
create_sql => 'COMMENT ON COLLATION test0
IS \'comment on test0 collation\';',
regexp =>
qr/^\QCOMMENT ON COLLATION public.test0 IS 'comment on test0 collation';\E/m,
collation => 1,
like => { %full_runs, section_pre_data => 1, },
},
'COMMENT ON LARGE OBJECT ...' => {
create_order => 65,
create_sql => 'DO $$
DECLARE myoid oid; BEGIN
SELECT loid FROM pg_largeobject INTO myoid;
EXECUTE \'COMMENT ON LARGE OBJECT \' || myoid || \' IS \'\'comment on large object\'\';\';
END;
$$;',
regexp => qr/^
\QCOMMENT ON LARGE OBJECT \E[0-9]+\Q IS 'comment on large object';\E
/xm,
like => {
%full_runs,
column_inserts => 1,
data_only => 1,
inserts => 1,
no_schema => 1,
section_data => 1,
test_schema_plus_large_objects => 1,
},
unlike => {
no_large_objects => 1,
schema_only => 1,
schema_only_with_statistics => 1,
},
},
'COMMENT ON POLICY p1' => {
create_order => 55,
create_sql => 'COMMENT ON POLICY p1 ON dump_test.test_table
IS \'comment on policy\';',
regexp =>
qr/^COMMENT ON POLICY p1 ON dump_test.test_table IS 'comment on policy';/m,
like => {
%full_runs,
%dump_test_schema_runs,
only_dump_test_table => 1,
section_post_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
exclude_test_table => 1,
no_policies => 1,
no_policies_restore => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON PUBLICATION pub1' => {
create_order => 55,
create_sql => 'COMMENT ON PUBLICATION pub1
IS \'comment on publication\';',
regexp =>
qr/^COMMENT ON PUBLICATION pub1 IS 'comment on publication';/m,
like => { %full_runs, section_post_data => 1, },
},
'COMMENT ON SUBSCRIPTION sub1' => {
create_order => 55,
create_sql => 'COMMENT ON SUBSCRIPTION sub1
IS \'comment on subscription\';',
regexp =>
qr/^COMMENT ON SUBSCRIPTION sub1 IS 'comment on subscription';/m,
like => { %full_runs, section_post_data => 1, },
unlike => {
no_subscriptions => 1,
no_subscriptions_restore => 1,
},
},
'COMMENT ON TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1' => {
create_order => 84,
create_sql => 'COMMENT ON TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1
IS \'comment on text search configuration\';',
regexp =>
qr/^\QCOMMENT ON TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1 IS 'comment on text search configuration';\E/m,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON TEXT SEARCH DICTIONARY dump_test.alt_ts_dict1' => {
create_order => 84,
create_sql => 'COMMENT ON TEXT SEARCH DICTIONARY dump_test.alt_ts_dict1
IS \'comment on text search dictionary\';',
regexp =>
qr/^\QCOMMENT ON TEXT SEARCH DICTIONARY dump_test.alt_ts_dict1 IS 'comment on text search dictionary';\E/m,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON TEXT SEARCH PARSER dump_test.alt_ts_prs1' => {
create_order => 84,
create_sql => 'COMMENT ON TEXT SEARCH PARSER dump_test.alt_ts_prs1
IS \'comment on text search parser\';',
regexp =>
qr/^\QCOMMENT ON TEXT SEARCH PARSER dump_test.alt_ts_prs1 IS 'comment on text search parser';\E/m,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON TEXT SEARCH TEMPLATE dump_test.alt_ts_temp1' => {
create_order => 84,
create_sql => 'COMMENT ON TEXT SEARCH TEMPLATE dump_test.alt_ts_temp1
IS \'comment on text search template\';',
regexp =>
qr/^\QCOMMENT ON TEXT SEARCH TEMPLATE dump_test.alt_ts_temp1 IS 'comment on text search template';\E/m,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON TYPE dump_test.planets - ENUM' => {
create_order => 68,
create_sql => 'COMMENT ON TYPE dump_test.planets
IS \'comment on enum type\';',
regexp =>
qr/^\QCOMMENT ON TYPE dump_test.planets IS 'comment on enum type';\E/m,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON TYPE dump_test.textrange - RANGE' => {
create_order => 69,
create_sql => 'COMMENT ON TYPE dump_test.textrange
IS \'comment on range type\';',
regexp =>
qr/^\QCOMMENT ON TYPE dump_test.textrange IS 'comment on range type';\E/m,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON TYPE dump_test.int42 - Regular' => {
create_order => 70,
create_sql => 'COMMENT ON TYPE dump_test.int42
IS \'comment on regular type\';',
regexp =>
qr/^\QCOMMENT ON TYPE dump_test.int42 IS 'comment on regular type';\E/m,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON TYPE dump_test.undefined - Undefined' => {
create_order => 71,
create_sql => 'COMMENT ON TYPE dump_test.undefined
IS \'comment on undefined type\';',
regexp =>
qr/^\QCOMMENT ON TYPE dump_test.undefined IS 'comment on undefined type';\E/m,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
# In a data-only dump, we try to actually order according to FKs, # so this check is just making sure that the referring table comes after # the referred-to table. 'COPY fk_reference_test_table second' => {
regexp => qr/^
\QCOPY dump_test.test_table (col1, col2, col3, col4) FROM stdin;\E
\n(?:\d\t\\N\t\\N\t\\N\n){9}\\\.\n.*
\QCOPY dump_test.fk_reference_test_table (col1) FROM stdin;\E
\n(?:\d\n){5}\\\.\n
/xms,
like => {
data_only => 1,
no_schema => 1,
},
},
'INSERT INTO test_third_table (colnames)' => {
regexp =>
qr/^INSERT INTO dump_test\.test_third_table \(f1, "F3"\) VALUES \(123, 456\);\n/m,
like => { column_inserts => 1, },
},
'INSERT INTO test_third_table' => {
regexp =>
qr/^INSERT INTO dump_test\.test_third_table VALUES \(123, DEFAULT, 456, DEFAULT\);\n/m,
like => { inserts => 1, },
},
'INSERT INTO test_fourth_table' => {
regexp =>
qr/^(?:INSERT INTO dump_test\.test_fourth_table DEFAULT VALUES;\n){2}/m,
like => { column_inserts => 1, inserts => 1, rows_per_insert => 1, },
},
'INSERT INTO test_fifth_table' => {
regexp =>
qr/^\QINSERT INTO dump_test.test_fifth_table (col1, col2, col3, col4, col5) VALUES (NULL, true, false, B'11001', 'NaN');\E/m,
like => { column_inserts => 1, },
},
'INSERT INTO test_table_identity' => {
regexp =>
qr/^\QINSERT INTO dump_test.test_table_identity (col1, col2) OVERRIDING SYSTEM VALUE VALUES (1, 'test');\E/m,
like => { column_inserts => 1, },
},
'CREATE ROLE regress_dump_test_role' => {
create_order => 1,
create_sql => 'CREATE ROLE regress_dump_test_role;',
regexp => qr/^CREATE ROLE regress_dump_test_role;/m,
like => {
pg_dumpall_dbprivs => 1,
pg_dumpall_exclude => 1,
pg_dumpall_globals => 1,
pg_dumpall_globals_clean => 1,
},
},
'CREATE ROLE regress_quoted...' => {
create_order => 1,
create_sql => 'CREATE ROLE "regress_quoted \"" role";',
regexp => qr/^CREATE ROLE "regress_quoted \\"" role";/m,
like => {
pg_dumpall_dbprivs => 1,
pg_dumpall_exclude => 1,
pg_dumpall_globals => 1,
pg_dumpall_globals_clean => 1,
},
},
'newline of role or table name in comment' => {
create_sql => qq{CREATE ROLE regress_newline;
ALTER ROLE regress_newline SET enable_seqscan = off;
ALTER ROLE regress_newline
RENAME TO "regress_newline\nattack";
-- meet getPartitioningInfo() "unsafe" condition
CREATE TYPE pp_colors AS
ENUM ('green', 'blue', 'black');
CREATE TABLE pp_enumpart (a pp_colors)
PARTITION BY HASH (a);
CREATE TABLE pp_enumpart1 PARTITION OF pp_enumpart FOR VALUES WITH (MODULUS 2, REMAINDER 0);
CREATE TABLE pp_enumpart2 PARTITION OF pp_enumpart FOR VALUES WITH (MODULUS 2, REMAINDER 1);
ALTER TABLE pp_enumpart
RENAME TO "pp_enumpart\nattack";},
regexp => qr/\n--[^\n]*\nattack/s,
like => {},
},
'CREATE CAST FOR timestamptz' => {
create_order => 51,
create_sql => 'CREATE CAST (timestamptz AS interval) WITH FUNCTION age(timestamptz) AS ASSIGNMENT;',
regexp =>
qr/CREATE CAST \(timestamp with time zone AS interval\) WITH FUNCTION pg_catalog\.age\(timestamp with time zone\) AS ASSIGNMENT;/m,
like => { %full_runs, section_pre_data => 1, },
},
'CREATE CONVERSION dump_test.test_conversion' => {
create_order => 78,
create_sql => 'CREATE DEFAULT CONVERSION dump_test.test_conversion FOR \'LATIN1\' TO \'UTF8\' FROM iso8859_1_to_utf8;',
regexp =>
qr/^\QCREATE DEFAULT CONVERSION dump_test.test_conversion FOR'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8;\E/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE DOMAIN dump_test.us_postal_code' => {
create_order => 29,
create_sql => 'CREATE DOMAIN dump_test.us_postal_code AS TEXT
COLLATE "C"
DEFAULT \'10014\'
CONSTRAINT nn NOT NULL
CHECK(VALUE ~ \'^\d{5}$\' OR
VALUE ~ \'^\d{5}-\d{4}$\');
COMMENT ON CONSTRAINT nn
ON DOMAIN dump_test.us_postal_code IS \'not null\';
COMMENT ON CONSTRAINT us_postal_code_check
ON DOMAIN dump_test.us_postal_code IS \'check it\';',
regexp => qr/^
\QCREATE DOMAIN dump_test.us_postal_code AS text COLLATE pg_catalog."C" CONSTRAINT nn NOT NULL DEFAULT '10014'::text\E\n\s+
\QCONSTRAINT us_postal_code_check CHECK \E
\Q(((VALUE ~ '^\d{5}\E
\$\Q'::text) OR (VALUE ~ '^\d{5}-\d{4}\E\$
\Q'::text)));\E(.|\n)*
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON CONSTRAINT ON DOMAIN (1)' => {
regexp => qr/^
\QCOMMENT ON CONSTRAINT nn ON DOMAIN dump_test.us_postal_code IS 'not null';\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'COMMENT ON CONSTRAINT ON DOMAIN (2)' => {
regexp => qr/^
\QCOMMENT ON CONSTRAINT us_postal_code_check ON DOMAIN dump_test.us_postal_code IS 'check it';\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE FUNCTION dump_test.pltestlang_call_handler' => {
create_order => 17,
create_sql => 'CREATE FUNCTION dump_test.pltestlang_call_handler()
RETURNS LANGUAGE_HANDLER AS \'$libdir/plpgsql\',
\'plpgsql_call_handler\' LANGUAGE C;',
regexp => qr/^
\QCREATE FUNCTION dump_test.pltestlang_call_handler() \E
\QRETURNS language_handler\E
\n\s+\QLANGUAGE c\E
\n\s+AS\ \'\$
\Qlibdir\/plpgsql', 'plpgsql_call_handler';\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE FUNCTION dump_test.trigger_func' => {
create_order => 30,
create_sql => 'CREATE FUNCTION dump_test.trigger_func()
RETURNS trigger LANGUAGE plpgsql
AS $$ BEGIN RETURN NULL; END;$$;',
regexp => qr/^
\QCREATE FUNCTION dump_test.trigger_func() RETURNS trigger\E
\n\s+\QLANGUAGE plpgsql\E
\n\s+AS\ \$\$
\Q BEGIN RETURN NULL; END;\E
\$\$;/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE FUNCTION dump_test.event_trigger_func' => {
create_order => 32,
create_sql => 'CREATE FUNCTION dump_test.event_trigger_func()
RETURNS event_trigger LANGUAGE plpgsql
AS $$ BEGIN RETURN; END;$$;',
regexp => qr/^
\QCREATE FUNCTION dump_test.event_trigger_func() RETURNS event_trigger\E
\n\s+\QLANGUAGE plpgsql\E
\n\s+AS\ \$\$
\Q BEGIN RETURN; END;\E
\$\$;/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE OPERATOR FAMILY dump_test.op_family' => {
create_order => 73,
create_sql => 'CREATE OPERATOR FAMILY dump_test.op_family USING btree;',
regexp => qr/^
\QCREATE OPERATOR FAMILY dump_test.op_family USING btree;\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE OPERATOR CLASS dump_test.op_class' => {
create_order => 74,
create_sql => 'CREATE OPERATOR CLASS dump_test.op_class FOR TYPE bigint USING btree FAMILY dump_test.op_family
AS STORAGE bigint,
OPERATOR 1 <(bigint,bigint),
OPERATOR 2 <=(bigint,bigint),
OPERATOR 3 =(bigint,bigint),
OPERATOR 4 >=(bigint,bigint),
OPERATOR 5 >(bigint,bigint), FUNCTION1 btint8cmp(bigint,bigint), FUNCTION2 btint8sortsupport(internal), FUNCTION4 btequalimage(oid);', # note: it's correct that btint8sortsupport and btequalimage # are NOT included here (they're optional support functions):
regexp => qr/^
\QCREATE OPERATOR CLASS dump_test.op_class\E\n\s+
\QFOR TYPE bigint USING btree FAMILY dump_test.op_family AS\E\n\s+
\QOPERATOR 1 <(bigint,bigint) ,\E\n\s+
\QOPERATOR 2 <=(bigint,bigint) ,\E\n\s+
\QOPERATOR 3 =(bigint,bigint) ,\E\n\s+
\QOPERATOR 4 >=(bigint,bigint) ,\E\n\s+
\QOPERATOR 5 >(bigint,bigint) ,\E\n\s+
\QFUNCTION 1 (bigint, bigint) btint8cmp(bigint,bigint);\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
# verify that a custom operator/opclass/range type is dumped in right order 'CREATE OPERATOR CLASS dump_test.op_class_custom' => {
create_order => 74,
create_sql => 'CREATE OPERATOR dump_test.~~ (
PROCEDURE = int4eq,
LEFTARG = int,
RIGHTARG = int);
CREATE OPERATOR CLASS dump_test.op_class_custom FOR TYPE int USING btree AS
OPERATOR 3 dump_test.~~;
CREATE TYPE dump_test.range_type_custom AS RANGE (
subtype = int,
subtype_opclass = dump_test.op_class_custom);',
regexp => qr/^
\QCREATE OPERATOR dump_test.~~ (\E\n.+
\QCREATE OPERATOR FAMILY dump_test.op_class_custom USING btree;\E\n.+
\QCREATE OPERATOR CLASS dump_test.op_class_custom\E\n\s+
\QFOR TYPE integer USING btree FAMILY dump_test.op_class_custom AS\E\n\s+
\QOPERATOR 3 dump_test.~~(integer,integer);\E\n.+
\QCREATE TYPE dump_test.range_type_custom AS RANGE (\E\n\s+
\Qsubtype = integer,\E\n\s+
\Qmultirange_type_name = dump_test.multirange_type_custom,\E\n\s+
\Qsubtype_opclass = dump_test.op_class_custom\E\n
\Q);\E
/xms,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE OPERATOR CLASS dump_test.op_class_empty' => {
create_order => 89,
create_sql => 'CREATE OPERATOR CLASS dump_test.op_class_empty FOR TYPE bigint USING btree FAMILY dump_test.op_family
AS STORAGE bigint;',
regexp => qr/^
\QCREATE OPERATOR CLASS dump_test.op_class_empty\E\n\s+
\QFOR TYPE bigint USING btree FAMILY dump_test.op_family AS\E\n\s+
\QSTORAGE bigint;\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE TRIGGER test_trigger' => {
create_order => 31,
create_sql => 'CREATE TRIGGER test_trigger
BEFORE INSERT ON dump_test.test_table FOR EACH ROW WHEN (NEW.col1 > 10)
EXECUTE FUNCTION dump_test.trigger_func();',
regexp => qr/^
\QCREATE TRIGGER test_trigger BEFORE INSERT ON dump_test.test_table \E
\QFOR EACH ROW WHEN ((new.col1 > 10)) \E
\QEXECUTE FUNCTION dump_test.trigger_func();\E
/xm,
like => {
%full_runs,
%dump_test_schema_runs,
only_dump_test_table => 1,
section_post_data => 1,
},
unlike => {
exclude_test_table => 1,
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE TYPE dump_test.planets AS ENUM' => {
create_order => 37,
create_sql => 'CREATE TYPE dump_test.planets
AS ENUM ( \'venus\', \'earth\', \'mars\' );',
regexp => qr/^
\QCREATE TYPE dump_test.planets AS ENUM (\E
\n\s+'venus',
\n\s+'earth',
\n\s+'mars'
\n\);/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
binary_upgrade => 1,
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE TYPE dump_test.planets AS ENUM pg_upgrade' => {
regexp => qr/^
\QCREATE TYPE dump_test.planets AS ENUM (\E
\n\);.*^
\QALTER TYPE dump_test.planets ADD VALUE 'venus';\E
\n.*^
\QALTER TYPE dump_test.planets ADD VALUE 'earth';\E
\n.*^
\QALTER TYPE dump_test.planets ADD VALUE 'mars';\E
\n/xms,
like => { binary_upgrade => 1, },
},
'CREATE TYPE dump_test.textrange AS RANGE' => {
create_order => 38,
create_sql => 'CREATE TYPE dump_test.textrange
AS RANGE (subtype=text, collation="C");',
regexp => qr/^
\QCREATE TYPE dump_test.textrange AS RANGE (\E
\n\s+\Qsubtype = text,\E
\n\s+\Qmultirange_type_name = dump_test.textmultirange,\E
\n\s+\Qcollation = pg_catalog."C"\E
\n\);/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE TYPE dump_test.int42' => {
create_order => 39,
create_sql => 'CREATE TYPE dump_test.int42;',
regexp => qr/^\QCREATE TYPE dump_test.int42;\E/m,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'ALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1 ...' => {
regexp => qr/^
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR asciiword WITH english_stem;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR word WITH english_stem;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR numword WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR email WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR url WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR host WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR sfloat WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR version WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR hword_numpart WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR hword_part WITH english_stem;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR hword_asciipart WITH english_stem;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR numhword WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR asciihword WITH english_stem;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR hword WITH english_stem;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR url_path WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR file WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR"float" WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR"int" WITH simple;\E\n
\n
\QALTER TEXT SEARCH CONFIGURATION dump_test.alt_ts_conf1\E\n
\s+\QADD MAPPING FOR uint WITH simple;\E\n
\n
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE FUNCTION dump_test.int42_in' => {
create_order => 40,
create_sql => 'CREATE FUNCTION dump_test.int42_in(cstring)
RETURNS dump_test.int42 AS \'int4in\'
LANGUAGE internal STRICT IMMUTABLE;',
regexp => qr/^
\QCREATE FUNCTION dump_test.int42_in(cstring) RETURNS dump_test.int42\E
\n\s+\QLANGUAGE internal IMMUTABLE STRICT\E
\n\s+AS\ \$\$int4in\$\$;
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE FUNCTION dump_test.int42_out' => {
create_order => 41,
create_sql => 'CREATE FUNCTION dump_test.int42_out(dump_test.int42)
RETURNS cstring AS \'int4out\'
LANGUAGE internal STRICT IMMUTABLE;',
regexp => qr/^
\QCREATE FUNCTION dump_test.int42_out(dump_test.int42) RETURNS cstring\E
\n\s+\QLANGUAGE internal IMMUTABLE STRICT\E
\n\s+AS\ \$\$int4out\$\$;
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE FUNCTION ... SUPPORT' => {
create_order => 41,
create_sql => 'CREATE FUNCTION dump_test.func_with_support() RETURNS int LANGUAGE sql AS $$ SELECT 1 $$ SUPPORT varchar_support;',
regexp => qr/^
\QCREATE FUNCTION dump_test.func_with_support() RETURNS integer\E
\n\s+\QLANGUAGE sql SUPPORT varchar_support\E
\n\s+AS\ \$\$\Q SELECT 1 \E\$\$;
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'Check ordering of a function that depends on a primary key' => {
create_order => 41,
create_sql => '
CREATE TABLE dump_test.ordering_table (id int primary key, data int);
CREATE FUNCTION dump_test.ordering_func ()
RETURNS SETOF dump_test.ordering_table
LANGUAGE sql BEGIN ATOMIC
SELECT * FROM dump_test.ordering_table GROUP BY id; END;',
regexp => qr/^
\QALTER TABLE ONLY dump_test.ordering_table\E
\n\s+\QADD CONSTRAINT ordering_table_pkey PRIMARY KEY (id);\E
.*^
\QCREATE FUNCTION dump_test.ordering_func\E/xms,
like =>
{ %full_runs, %dump_test_schema_runs, section_post_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE USER MAPPING FOR regress_dump_test_role SERVER s1' => {
create_order => 86,
create_sql => 'CREATE USER MAPPING FOR regress_dump_test_role SERVER s1;',
regexp =>
qr/CREATE USER MAPPING FOR regress_dump_test_role SERVER s1;/m,
like => { %full_runs, section_pre_data => 1, },
},
'CREATE TRANSFORM FOR int' => {
create_order => 34,
create_sql => 'CREATE TRANSFORM FOR int LANGUAGE SQL (FROM SQL WITH FUNCTION prsd_lextype(internal), TO SQL WITH FUNCTION int4recv(internal));',
regexp =>
qr/CREATE TRANSFORM FOR integer LANGUAGE sql \(FROM SQL WITH FUNCTION pg_catalog\.prsd_lextype\(internal\), TO SQL WITH FUNCTION pg_catalog\.int4recv\(internal\)\);/m,
like => { %full_runs, section_pre_data => 1, },
},
# Regardless of whether the table or schema is excluded, publications must # still be dumped, as excluded objects do not apply to publications. We # perform table and schema exclusion via full_runs. 'ALTER PUBLICATION pub1 ADD TABLE test_table' => {
create_order => 51,
create_sql => 'ALTER PUBLICATION pub1 ADD TABLE dump_test.test_table;',
regexp => qr/^
\QALTER PUBLICATION pub1 ADD TABLE ONLY dump_test.test_table;\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
# Regardless of whether the table or schema is excluded, publications must # still be dumped, as excluded objects do not apply to publications. We # perform table and schema exclusion via full_runs. 'ALTER PUBLICATION pub1 ADD TABLE test_second_table' => {
create_order => 52,
create_sql => 'ALTER PUBLICATION pub1 ADD TABLE dump_test.test_second_table;',
regexp => qr/^
\QALTER PUBLICATION pub1 ADD TABLE ONLY dump_test.test_second_table;\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
# Regardless of whether the table or schema is excluded, publications must # still be dumped, as excluded objects do not apply to publications. We # perform table and schema exclusion via full_runs. 'ALTER PUBLICATION pub1 ADD TABLE test_sixth_table (col3, col2)' => {
create_order => 52,
create_sql => 'ALTER PUBLICATION pub1 ADD TABLE dump_test.test_sixth_table (col3, col2);',
regexp => qr/^
\QALTER PUBLICATION pub1 ADD TABLE ONLY dump_test.test_sixth_table (col2, col3);\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
# Regardless of whether the table or schema is excluded, publications must # still be dumped, as excluded objects do not apply to publications. We # perform table and schema exclusion via full_runs. 'ALTER PUBLICATION pub1 ADD TABLE test_seventh_table (col3, col2) WHERE (col1 = 1)'
=> {
create_order => 52,
create_sql => 'ALTER PUBLICATION pub1 ADD TABLE dump_test.test_seventh_table (col3, col2) WHERE (col1 = 1);',
regexp => qr/^
\QALTER PUBLICATION pub1 ADD TABLE ONLY dump_test.test_seventh_table (col2, col3) WHERE ((col1 = 1));\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
# Regardless of whether the table or schema is excluded, publications must # still be dumped, as excluded objects do not apply to publications. We # perform table and schema exclusion via full_runs. 'ALTER PUBLICATION pub3 ADD TABLES IN SCHEMA dump_test' => {
create_order => 51,
create_sql => 'ALTER PUBLICATION pub3 ADD TABLES IN SCHEMA dump_test;',
regexp => qr/^
\QALTER PUBLICATION pub3 ADD TABLES IN SCHEMA dump_test;\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
# Regardless of whether the table or schema is excluded, publications must # still be dumped, as excluded objects do not apply to publications. We # perform table and schema exclusion via full_runs. 'ALTER PUBLICATION pub3 ADD TABLES IN SCHEMA public' => {
create_order => 52,
create_sql => 'ALTER PUBLICATION pub3 ADD TABLES IN SCHEMA public;',
regexp => qr/^
\QALTER PUBLICATION pub3 ADD TABLES IN SCHEMA public;\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
# Regardless of whether the table or schema is excluded, publications must # still be dumped, as excluded objects do not apply to publications. We # perform table and schema exclusion via full_runs. 'ALTER PUBLICATION pub3 ADD TABLE test_table' => {
create_order => 51,
create_sql => 'ALTER PUBLICATION pub3 ADD TABLE dump_test.test_table;',
regexp => qr/^
\QALTER PUBLICATION pub3 ADD TABLE ONLY dump_test.test_table;\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
# Regardless of whether the table or schema is excluded, publications must # still be dumped, as excluded objects do not apply to publications. We # perform table and schema exclusion via full_runs. 'ALTER PUBLICATION pub4 ADD TABLE test_table WHERE (col1 > 0);' => {
create_order => 51,
create_sql => 'ALTER PUBLICATION pub4 ADD TABLE dump_test.test_table WHERE (col1 > 0);',
regexp => qr/^
\QALTER PUBLICATION pub4 ADD TABLE ONLY dump_test.test_table WHERE ((col1 > 0));\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
# Regardless of whether the table or schema is excluded, publications must # still be dumped, as excluded objects do not apply to publications. We # perform table and schema exclusion via full_runs. 'ALTER PUBLICATION pub4 ADD TABLE test_second_table WHERE (col2 = \'test\');'
=> {
create_order => 52,
create_sql => 'ALTER PUBLICATION pub4 ADD TABLE dump_test.test_second_table WHERE (col2 = \'test\');',
regexp => qr/^
\QALTER PUBLICATION pub4 ADD TABLE ONLY dump_test.test_second_table WHERE ((col2 = 'test'::text));\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
# We should never see the creation of a trigger on a partition 'Disabled trigger on partition is not created' => {
regexp => qr/CREATE TRIGGER test_trigger.*ON dump_test_second_schema/,
like => {},
},
# Triggers on partitions should not be dropped individually 'Triggers on partitions are not dropped' => {
regexp => qr/DROP TRIGGER test_trigger.*ON dump_test_second_schema/,
like => {}
},
'CREATE TABLE test_third_table_generated_cols' => {
create_order => 6,
create_sql => 'CREATE TABLE dump_test.test_third_table (
f1 int, junk int,
g1 int generated always as (f1 * 2) stored, "F3" int,
g2 int generated always as ("F3" * 3) stored
);
ALTER TABLE dump_test.test_third_table DROP COLUMN junk;',
regexp => qr/^
\QCREATE TABLE dump_test.test_third_table (\E\n
\s+\Qf1 integer,\E\n
\s+\Qg1 integer GENERATED ALWAYS AS ((f1 * 2)) STORED,\E\n
\s+\Q"F3" integer,\E\n
\s+\Qg2 integer GENERATED ALWAYS AS (("F3" * 3)) STORED\E\n
\);\n
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
binary_upgrade => 1,
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'DROP EXTENSION IF EXISTS plpgsql' => {
regexp => qr/^DROP EXTENSION IF EXISTS plpgsql;/m,
# this shouldn't ever get emitted anymore
like => {},
},
'DROP FUNCTION IF EXISTS dump_test.pltestlang_call_handler()' => {
regexp => qr/^
\QDROP FUNCTIONIF EXISTS dump_test.pltestlang_call_handler();\E
/xm,
like => { clean_if_exists => 1, },
},
'DROP LANGUAGE IF EXISTS pltestlang' => {
regexp => qr/^DROP PROCEDURAL LANGUAGE IF EXISTS pltestlang;/m,
like => { clean_if_exists => 1, },
},
'DROP SCHEMA IF EXISTS dump_test' => {
regexp => qr/^DROP SCHEMA IF EXISTS dump_test;/m,
like => { clean_if_exists => 1, },
},
'DROP SCHEMA IF EXISTS dump_test_second_schema' => {
regexp => qr/^DROP SCHEMA IF EXISTS dump_test_second_schema;/m,
like => { clean_if_exists => 1, },
},
'DROP TABLE IF EXISTS test_table' => {
regexp => qr/^DROP TABLE IF EXISTS dump_test\.test_table;/m,
like => { clean_if_exists => 1, },
},
'DROP TABLE IF EXISTS test_second_table' => {
regexp => qr/^DROP TABLE IF EXISTS dump_test\.test_second_table;/m,
like => { clean_if_exists => 1, },
},
'DROP ROLE regress_dump_test_role' => {
regexp => qr/^
\QDROP ROLE regress_dump_test_role;\E
/xm,
like => { pg_dumpall_globals_clean => 1, },
},
'DROP ROLE pg_' => {
regexp => qr/^
\QDROP ROLE pg_\E.+;
/xm,
# this shouldn't ever get emitted anywhere
like => {},
},
'GRANT USAGE ON SCHEMA dump_test_second_schema' => {
create_order => 10,
create_sql => 'GRANT USAGE ON SCHEMA dump_test_second_schema
TO regress_dump_test_role;',
regexp => qr/^
\QGRANT USAGE ON SCHEMA dump_test_second_schema TO regress_dump_test_role;\E
/xm,
like => {
%full_runs,
role => 1,
section_pre_data => 1,
},
unlike => { no_privs => 1, },
},
'GRANT USAGE ON FOREIGN DATA WRAPPER dummy' => {
create_order => 85,
create_sql => 'GRANT USAGE ON FOREIGN DATA WRAPPER dummy
TO regress_dump_test_role;',
regexp => qr/^
\QGRANT ALL ON FOREIGN DATA WRAPPER dummy TO regress_dump_test_role;\E
/xm,
like => { %full_runs, section_pre_data => 1, },
unlike => { no_privs => 1, },
},
'GRANT USAGE ON FOREIGN SERVER s1' => {
create_order => 85,
create_sql => 'GRANT USAGE ON FOREIGN SERVER s1
TO regress_dump_test_role;',
regexp => qr/^
\QGRANT ALL ON FOREIGN SERVER s1 TO regress_dump_test_role;\E
/xm,
like => { %full_runs, section_pre_data => 1, },
unlike => { no_privs => 1, },
},
'GRANT USAGE ON DOMAIN dump_test.us_postal_code' => {
create_order => 72,
create_sql => 'GRANT USAGE ON DOMAIN dump_test.us_postal_code TO regress_dump_test_role;',
regexp => qr/^
\QGRANT ALL ON TYPE dump_test.us_postal_code TO regress_dump_test_role;\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
no_privs => 1,
only_dump_measurement => 1,
},
},
'GRANT USAGE ON TYPE dump_test.int42' => {
create_order => 87,
create_sql => 'GRANT USAGE ON TYPE dump_test.int42 TO regress_dump_test_role;',
regexp => qr/^
\QGRANT ALL ON TYPE dump_test.int42 TO regress_dump_test_role;\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
no_privs => 1,
only_dump_measurement => 1,
},
},
'GRANT USAGE ON TYPE dump_test.planets - ENUM' => {
create_order => 66,
create_sql => 'GRANT USAGE ON TYPE dump_test.planets TO regress_dump_test_role;',
regexp => qr/^
\QGRANT ALL ON TYPE dump_test.planets TO regress_dump_test_role;\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
no_privs => 1,
only_dump_measurement => 1,
},
},
'GRANT USAGE ON TYPE dump_test.textrange - RANGE' => {
create_order => 67,
create_sql => 'GRANT USAGE ON TYPE dump_test.textrange TO regress_dump_test_role;',
regexp => qr/^
\QGRANT ALL ON TYPE dump_test.textrange TO regress_dump_test_role;\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
no_privs => 1,
only_dump_measurement => 1,
},
},
'GRANT CREATE ON DATABASE dump_test' => {
create_order => 48,
create_sql => 'GRANT CREATE ON DATABASE dump_test TO regress_dump_test_role;',
regexp => qr/^
\QGRANT CREATE ON DATABASE dump_test TO regress_dump_test_role;\E
/xm,
like => { pg_dumpall_dbprivs => 1, },
},
'GRANT SELECT ON TABLE test_table' => {
create_order => 5,
create_sql => 'GRANT SELECT ON TABLE dump_test.test_table
TO regress_dump_test_role;',
regexp =>
qr/^\QGRANT SELECT ON TABLE dump_test.test_table TO regress_dump_test_role;\E/m,
like => {
%full_runs,
%dump_test_schema_runs,
only_dump_test_table => 1,
section_pre_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
exclude_test_table => 1,
no_privs => 1,
only_dump_measurement => 1,
},
},
'GRANT SELECT ON TABLE measurement' => {
create_order => 91,
create_sql => 'GRANT SELECT ON TABLE dump_test.measurement
TO regress_dump_test_role;
GRANT SELECT(city_id) ON TABLE dump_test.measurement
TO "regress_quoted \"" role";',
regexp =>
qr/^\QGRANT SELECT ON TABLE dump_test.measurement TO regress_dump_test_role;\E\n.*
^\QGRANT SELECT(city_id) ON TABLE dump_test.measurement TO "regress_quoted \"" role";\E/xms,
like => {
%full_runs,
%dump_test_schema_runs,
section_pre_data => 1,
only_dump_measurement => 1,
},
unlike => {
exclude_dump_test_schema => 1,
no_privs => 1,
exclude_measurement => 1,
},
},
'GRANT SELECT ON TABLE measurement_y2006m2' => {
create_order => 94,
create_sql => 'GRANT SELECT ON TABLE
dump_test_second_schema.measurement_y2006m2,
dump_test_second_schema.measurement_y2006m3,
dump_test_second_schema.measurement_y2006m4,
dump_test_second_schema.measurement_y2006m5
TO regress_dump_test_role;',
regexp =>
qr/^\QGRANT SELECT ON TABLE dump_test_second_schema.measurement_y2006m2 TO regress_dump_test_role;\E/m,
like => {
%full_runs,
role => 1,
section_pre_data => 1,
only_dump_measurement => 1,
},
unlike => {
no_privs => 1,
exclude_measurement => 1,
},
},
'GRANT ALL ON LARGE OBJECT ...' => {
create_order => 60,
create_sql => 'DO $$
DECLARE myoid oid; BEGIN
SELECT loid FROM pg_largeobject INTO myoid;
EXECUTE \'GRANT ALL ON LARGE OBJECT \' || myoid || \' TO regress_dump_test_role;\';
END;
$$;',
regexp => qr/^
\QGRANT ALL ON LARGE OBJECT \E[0-9]+\Q TO regress_dump_test_role;\E
/xm,
like => {
%full_runs,
column_inserts => 1,
data_only => 1,
inserts => 1,
no_schema => 1,
section_data => 1,
test_schema_plus_large_objects => 1,
binary_upgrade => 1,
},
unlike => {
no_large_objects => 1,
no_privs => 1,
schema_only => 1,
schema_only_with_statistics => 1,
},
},
'GRANT INSERT(col1) ON TABLE test_second_table' => {
create_order => 8,
create_sql => 'GRANT INSERT (col1) ON TABLE dump_test.test_second_table
TO regress_dump_test_role;',
regexp => qr/^
\QGRANT INSERT(col1) ON TABLE dump_test.test_second_table TO regress_dump_test_role;\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
no_privs => 1,
only_dump_measurement => 1,
},
},
'GRANT EXECUTE ON FUNCTION pg_sleep() TO regress_dump_test_role' => {
create_order => 16,
create_sql => 'GRANT EXECUTE ON FUNCTION pg_sleep(float8)
TO regress_dump_test_role;',
regexp => qr/^
\QGRANT ALL ON FUNCTION pg_catalog.pg_sleep(double precision) TO regress_dump_test_role;\E
/xm,
like => { %full_runs, section_pre_data => 1, },
unlike => { no_privs => 1, },
},
'GRANT SELECT (proname ...) ON TABLE pg_proc TO public' => {
create_order => 46,
create_sql => 'GRANT SELECT (
tableoid,
oid,
proname,
pronamespace,
proowner,
prolang,
procost,
prorows,
provariadic,
prosupport,
prokind,
prosecdef,
proleakproof,
proisstrict,
proretset,
provolatile,
proparallel,
pronargs,
pronargdefaults,
prorettype,
proargtypes,
proallargtypes,
proargmodes,
proargnames,
proargdefaults,
protrftypes,
prosrc,
probin,
proconfig,
proacl
) ON TABLE pg_proc TO public;',
regexp => qr/
\QGRANT SELECT(tableoid) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(oid) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proname) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(pronamespace) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proowner) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(prolang) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(procost) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(prorows) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(provariadic) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(prosupport) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(prokind) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(prosecdef) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proleakproof) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proisstrict) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proretset) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(provolatile) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proparallel) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(pronargs) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(pronargdefaults) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(prorettype) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proargtypes) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proallargtypes) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proargmodes) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proargnames) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proargdefaults) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(protrftypes) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(prosrc) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(probin) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proconfig) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
\QGRANT SELECT(proacl) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E/xms,
like => { %full_runs, section_pre_data => 1, },
unlike => { no_privs => 1, },
},
'GRANT USAGE ON SCHEMA public TO public' => {
regexp => qr/^
\Q--\E\n\n
\QGRANT USAGE ON SCHEMA public TO PUBLIC;\E
/xm,
# this shouldn't ever get emitted anymore
like => {},
},
'REVOKE CONNECT ON DATABASE dump_test FROM public' => {
create_order => 49,
create_sql => 'REVOKE CONNECT ON DATABASE dump_test FROM public;',
regexp => qr/^
\QREVOKE CONNECT,TEMPORARY ON DATABASE dump_test FROM PUBLIC;\E\n
\QGRANT TEMPORARY ON DATABASE dump_test TO PUBLIC;\E\n
\QGRANT CREATE ON DATABASE dump_test TO regress_dump_test_role;\E
/xm,
like => { pg_dumpall_dbprivs => 1, },
},
'REVOKE EXECUTE ON FUNCTION pg_sleep() FROM public' => {
create_order => 15,
create_sql => 'REVOKE EXECUTE ON FUNCTION pg_sleep(float8)
FROM public;',
regexp => qr/^
\QREVOKE ALL ON FUNCTION pg_catalog.pg_sleep(double precision) FROM PUBLIC;\E
/xm,
like => { %full_runs, section_pre_data => 1, },
unlike => { no_privs => 1, },
},
# With the exception of the public schema, we don't dump ownership changes # for objects originating at initdb. Hence, any GRANT or REVOKE affecting # owner privileges for those objects should reference the bootstrap # superuser, not the dump-time owner. 'REVOKE EXECUTE ON FUNCTION pg_stat_reset FROM regress_dump_test_role' =>
{
create_order => 15,
create_sql => '
ALTER FUNCTION pg_stat_reset OWNER TO regress_dump_test_role;
REVOKE EXECUTE ON FUNCTION pg_stat_reset
FROM regress_dump_test_role;',
regexp => qr/^[^-].*pg_stat_reset.* regress_dump_test_role/m,
# this shouldn't ever get emitted
like => {},
},
'REVOKE SELECT ON TABLE pg_proc FROM public' => {
create_order => 45,
create_sql => 'REVOKE SELECT ON TABLE pg_proc FROM public;',
regexp =>
qr/^\QREVOKE SELECT ON TABLE pg_catalog.pg_proc FROM PUBLIC;\E/m,
like => { %full_runs, section_pre_data => 1, },
unlike => { no_privs => 1, },
},
'REVOKE ALL ON SCHEMA public' => {
create_order => 16,
create_sql => 'REVOKE ALL ON SCHEMA public FROM "regress_quoted \"" role";',
regexp =>
qr/^REVOKE ALL ON SCHEMA public FROM "regress_quoted \\"" role";/m,
like => { %full_runs, section_pre_data => 1, },
unlike => { no_privs => 1, },
},
'REVOKE USAGE ON LANGUAGE plpgsql FROM public' => {
create_order => 16,
create_sql => 'REVOKE USAGE ON LANGUAGE plpgsql FROM public;',
regexp => qr/^REVOKE ALL ON LANGUAGE plpgsql FROM PUBLIC;/m,
like => {
%full_runs,
%dump_test_schema_runs,
only_dump_test_table => 1,
role => 1,
section_pre_data => 1,
only_dump_measurement => 1,
},
unlike => { no_privs => 1, },
},
# It's a bit tricky to ensure that the proper SET of default table # AM occurs. To achieve that we create a table with the standard # AM, test AM, standard AM. That guarantees that there needs to be # a SET interspersed. Then use a regex that prevents interspersed # SET ...; statements, followed by the expected CREATE TABLE. Not # pretty, but seems hard to do better in this framework. 'CREATE TABLE regress_pg_dump_table_am' => {
create_order => 12,
create_sql => '
CREATE TABLE dump_test.regress_pg_dump_table_am_0() USING heap;
CREATE TABLE dump_test.regress_pg_dump_table_am_1 (col1 int) USING regress_table_am;
CREATE TABLE dump_test.regress_pg_dump_table_am_2() USING heap;',
regexp => qr/^
\QSET default_table_access_method = regress_table_am;\E
(\n(?!SET[^;]+;)[^\n]*)*
\n\QCREATE TABLE dump_test.regress_pg_dump_table_am_1 (\E
\n\s+\Qcol1 integer\E
\n\);/xm,
like => {
%full_runs, %dump_test_schema_runs, section_pre_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
no_table_access_method => 1,
only_dump_measurement => 1,
},
},
'CREATE MATERIALIZED VIEW regress_pg_dump_matview_am' => {
create_order => 13,
create_sql => '
CREATE MATERIALIZED VIEW dump_test.regress_pg_dump_matview_am_0 USING heap AS SELECT 1;
CREATE MATERIALIZED VIEW dump_test.regress_pg_dump_matview_am_1
USING regress_table_am AS SELECT count(*) FROM pg_class;
CREATE MATERIALIZED VIEW dump_test.regress_pg_dump_matview_am_2 USING heap AS SELECT 1;',
regexp => qr/^
\QSET default_table_access_method = regress_table_am;\E
(\n(?!SET[^;]+;)[^\n]*)*
\QCREATE MATERIALIZED VIEW dump_test.regress_pg_dump_matview_am_1 AS\E
\n\s+\QSELECT count(*) AS count\E
\n\s+\QFROM pg_class\E
\n\s+\QWITH NO DATA;\E\n/xm,
like => {
%full_runs, %dump_test_schema_runs, section_pre_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
no_table_access_method => 1,
only_dump_measurement => 1,
},
},
# # TABLE and MATVIEW stats will end up in SECTION_DATA. # INDEX stats (expression columns only) will end up in SECTION_POST_DATA. # 'statistics_import' => {
create_sql => '
CREATE TABLE dump_test.has_stats
AS SELECT g.g AS x, g.g / 2 AS y FROM generate_series(1,100) AS g(g);
CREATE MATERIALIZED VIEW dump_test.has_stats_mv AS SELECT * FROM dump_test.has_stats;
CREATE INDEX """dump_test""\'s post-data index" ON dump_test.has_stats(x, (x - 1));
ANALYZE dump_test.has_stats, dump_test.has_stats_mv;',
regexp => qr/^
\QSELECT * FROM pg_catalog.pg_restore_relation_stats(\E\s+ 'version',\s'\d+'::integer,\s+ 'schemaname',\s'dump_test',\s+ 'relname',\s'"dump_test"''s\ post-data\ index',\s+ 'relpages',\s'\d+'::integer,\s+ 'reltuples',\s'\d+'::real,\s+ 'relallvisible',\s'\d+'::integer,\s+ 'relallfrozen',\s'\d+'::integer\s+
\);\s+
\QSELECT * FROM pg_catalog.pg_restore_attribute_stats(\E\s+ 'version',\s'\d+'::integer,\s+ 'schemaname',\s'dump_test',\s+ 'relname',\s'"dump_test"''s\ post-data\ index',\s+ 'attnum',\s'2'::smallint,\s+ 'inherited',\s'f'::boolean,\s+ 'null_frac',\s'0'::real,\s+ 'avg_width',\s'4'::integer,\s+ 'n_distinct',\s'-1'::real,\s+ 'histogram_bounds',\s'\{[0-9,]+\}'::text,\s+ 'correlation',\s'1'::real\s+
\);/xm,
like => {
%full_runs,
%dump_test_schema_runs,
no_data_no_schema => 1,
no_schema => 1,
section_post_data => 1,
statistics_only => 1,
schema_only_with_statistics => 1,
},
unlike => {
exclude_dump_test_schema => 1,
no_statistics => 1,
only_dump_measurement => 1,
schema_only => 1,
},
},
# # While attribute stats (aka pg_statistic stats) only appear for tables # that have been analyzed, all tables will have relation stats because # those come from pg_class. # 'relstats_on_unanalyzed_tables' => {
regexp => qr/pg_catalog.pg_restore_relation_stats/,
# CREATE TABLE with partitioned table and various AMs. One # partition uses the same default as the parent, and a second # uses its own AM. 'CREATE TABLE regress_pg_dump_table_part' => {
create_order => 19,
create_sql => '
CREATE TABLE dump_test.regress_pg_dump_table_am_parent (id int) PARTITION BY LIST (id);
ALTER TABLE dump_test.regress_pg_dump_table_am_parent SET ACCESS METHOD regress_table_am;
CREATE TABLE dump_test.regress_pg_dump_table_am_child_1
PARTITION OF dump_test.regress_pg_dump_table_am_parent FOR VALUES IN (1);
CREATE TABLE dump_test.regress_pg_dump_table_am_child_2
PARTITION OF dump_test.regress_pg_dump_table_am_parent FOR VALUES IN (2) USING heap;',
regexp => qr/^
\n\QCREATE TABLE dump_test.regress_pg_dump_table_am_parent (\E
(\n(?!SET[^;]+;)[^\n]*)*
\QALTER TABLE dump_test.regress_pg_dump_table_am_parent SET ACCESS METHOD regress_table_am;\E
(.*\n)*
\QSET default_table_access_method = regress_table_am;\E
(\n(?!SET[^;]+;)[^\n]*)*
\n\QCREATE TABLE dump_test.regress_pg_dump_table_am_child_1 (\E
(.*\n)*
\QSET default_table_access_method = heap;\E
(\n(?!SET[^;]+;)[^\n]*)*
\n\QCREATE TABLE dump_test.regress_pg_dump_table_am_child_2 (\E
(.*\n)*/xm,
like => {
%full_runs, %dump_test_schema_runs, section_pre_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
no_table_access_method => 1,
only_dump_measurement => 1,
},
});
######################################### # Create a PG instance to test actually dumping from
my $node = PostgreSQL::Test::Cluster->new('main');
$node->init;
$node->start;
my $port = $node->port;
# We need to see if this system supports CREATE COLLATION or not # If it doesn't then we will skip all the COLLATION-related tests.
my $collation_support = 0;
my $collation_check_stderr;
$node->psql( 'postgres', "CREATE COLLATION testing FROM \"C\"; DROP COLLATION testing;",
on_error_stop => 0,
stderr => \$collation_check_stderr);
if ($collation_check_stderr !~ /ERROR: /)
{
$collation_support = 1;
}
# ICU doesn't work with some encodings
my $encoding = $node->safe_psql('postgres', 'show server_encoding');
$supports_icu = 0if $encoding eq 'SQL_ASCII';
# Create additional databases for mutations of schema public
$node->psql('postgres', 'create database regress_pg_dump_test;');
$node->psql('postgres', 'create database regress_public_owner;');
######################################### # Set up schemas, tables, etc, to be dumped.
# Build up the create statements
my %create_sql = ();
foreach my $test (
sort { if ($tests{$a}->{create_order} and $tests{$b}->{create_order})
{
$tests{$a}->{create_order} <=> $tests{$b}->{create_order};
}
elsif ($tests{$a}->{create_order})
{
-1;
}
elsif ($tests{$b}->{create_order})
{ 1;
} else
{ 0;
}
} keys %tests)
{
my $test_db = 'postgres';
if (defined($tests{$test}->{database}))
{
$test_db = $tests{$test}->{database};
}
if (defined($tests{$test}->{icu}))
{
$tests{$test}->{collation} = 1;
}
if ($tests{$test}->{create_sql})
{
# Skip any collation-related commands if there is no collation support if (!$collation_support && defined($tests{$test}->{collation}))
{
next;
}
# Skip any icu-related collation commands if build was without icu if (!$supports_icu && defined($tests{$test}->{icu}))
{
next;
}
# Skip tests specific to LZ4 if this build does not support # this option. if (!$supports_lz4 && defined($tests{$test}->{lz4}))
{
next;
}
# Normalize command ending: strip all line endings, add # semicolon if missing, add two newlines.
my $create_sql = $tests{$test}->{create_sql};
chomp $create_sql;
$create_sql .= ';' unless substr($create_sql, -1) eq ';';
$create_sql{$test_db} .= $create_sql . "\n\n";
}
}
# Send the combined set of commands to psql foreach my $db (sort keys %create_sql)
{
$node->safe_psql($db, $create_sql{$db});
}
######################################### # Test connecting to a non-existent database
command_fails_like(
[ 'pg_dump', '--port' => $port, 'qqq' ],
qr/pg_dump: error: connection to server .* failed: FATAL: database "qqq" does not exist/, 'connecting to a non-existent database');
######################################### # Test connecting to an invalid database
$node->command_fails_like(
[ 'pg_dump', '--dbname' => 'regression_invalid' ],
qr/pg_dump: error: connection to server .* failed: FATAL: cannot connect to invalid database "regression_invalid"/, 'connecting to an invalid database');
######################################### # Test connecting with an unprivileged user
$node->command_fails_like(
[ 'pg_dump', '--table' => 'otherdb.pg_catalog.pg_class' ],
qr/pg_dump: error: cross-database references are not implemented: otherdb\.pg_catalog\.pg_class/, 'pg_dump: option --table rejects cross-database three part table names');
command_fails_like(
[ 'pg_dump', '--port' => $port, '--table' => '"some.other.db".pg_catalog.pg_class'
],
qr/pg_dump: error: cross-database references are not implemented: "some\.other\.db"\.pg_catalog\.pg_class/, 'pg_dump: option --table rejects cross-database three part table names with embedded dots'
);
######################################### # Run all runs
foreach my $run (sort keys %pgdump_runs)
{
my $test_key = $run;
my $run_db = 'postgres';
# Skip command-level tests for gzip/lz4/zstd if the tool is not supported if ($pgdump_runs{$run}->{compile_option}
&& (($pgdump_runs{$run}->{compile_option} eq 'gzip'
&& !$supports_gzip)
|| ($pgdump_runs{$run}->{compile_option} eq 'lz4'
&& !$supports_lz4)
|| ($pgdump_runs{$run}->{compile_option} eq 'zstd'
&& !$supports_zstd)))
{
note "$run: skipped due to no $pgdump_runs{$run}->{compile_option} support";
next;
}
if ($pgdump_runs{$run}->{glob_patterns})
{
my $glob_patterns = $pgdump_runs{$run}->{glob_patterns}; foreach my $glob_pattern (@{$glob_patterns})
{
my @glob_output = glob($glob_pattern);
is(scalar(@glob_output) > 0, 1, "$run: glob check for $glob_pattern");
}
}
if ($pgdump_runs{$run}->{command_like})
{
my $cmd_like = $pgdump_runs{$run}->{command_like};
$node->command_like(
\@{ $cmd_like->{command} },
$cmd_like->{expected}, "$run: " . $cmd_like->{name});
}
if ($pgdump_runs{$run}->{restore_cmd})
{
$node->command_ok(\@{ $pgdump_runs{$run}->{restore_cmd} }, "$run: pg_restore runs");
}
if ($pgdump_runs{$run}->{test_key})
{
$test_key = $pgdump_runs{$run}->{test_key};
}
my $output_file = slurp_file("$tempdir/${run}.sql");
######################################### # Run all tests where this run is included # as either a 'like' or 'unlike' test.
foreach my $test (sort keys %tests)
{
my $test_db = 'postgres';
if (defined($pgdump_runs{$run}->{database}))
{
$run_db = $pgdump_runs{$run}->{database};
}
if (defined($tests{$test}->{database}))
{
$test_db = $tests{$test}->{database};
}
# Check for proper test definitions # # Either "all_runs" should be set or there should be a "like" list, # even if it is empty. (This makes the test more self-documenting.) if (!defined($tests{$test}->{all_runs})
&& !defined($tests{$test}->{like}))
{
die "missing \"like\" in test \"$test\"";
} # Check for useless entries in "unlike" list. Runs that are # not listed in "like" don't need to be excluded in "unlike". if ($tests{$test}->{unlike}->{$test_key}
&& !defined($tests{$test}->{like}->{$test_key}))
{
die "useless \"unlike\" entry \"$test_key\" in test \"$test\"";
}
# Skip any collation-related commands if there is no collation support if (!$collation_support && defined($tests{$test}->{collation}))
{
next;
}
# Skip any icu-related collation commands if build was without icu if (!$supports_icu && defined($tests{$test}->{icu}))
{
next;
}
# Skip tests specific to LZ4 if this build does not support # this option. if (!$supports_lz4 && defined($tests{$test}->{lz4}))
{
next;
}
if ($run_db ne $test_db)
{
next;
}
# Run the test if all_runs is set or if listed as a like, unless it is # specifically noted as an unlike (generally due to an explicit # exclusion or similar). if (($tests{$test}->{like}->{$test_key} || $tests{$test}->{all_runs})
&& !defined($tests{$test}->{unlike}->{$test_key}))
{ if (!ok($output_file =~ $tests{$test}->{regexp}, "$run: should dump $test"))
{
diag("Review $run results in $tempdir");
}
} else
{ if (!ok($output_file !~ $tests{$test}->{regexp}, "$run: should not dump $test"))
{
diag("Review $run results in $tempdir");
}
}
}
}
######################################### # Stop the database instance, which will be removed at the end of the tests.
$node->stop('fast');
done_testing();
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.72 Sekunden
(vorverarbeitet am 2026-08-10)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.