diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build index 1b25d98f7f3..356d8454b39 100644 --- a/src/test/modules/test_misc/meson.build +++ b/src/test/modules/test_misc/meson.build @@ -20,6 +20,7 @@ tests += { 't/009_log_temp_files.pl', 't/010_index_concurrently_upsert.pl', 't/011_lock_stats.pl', + 't/012_ddlutils.pl', ], # The injection points are cluster-wide, so disable installcheck 'runningcheck': false, diff --git a/src/test/modules/test_misc/t/012_ddlutils.pl b/src/test/modules/test_misc/t/012_ddlutils.pl new file mode 100644 index 00000000000..b11d4777ccc --- /dev/null +++ b/src/test/modules/test_misc/t/012_ddlutils.pl @@ -0,0 +1,286 @@ + +# Copyright (c) 2026, PostgreSQL Global Development Group + +# Tests for pg_get_database_ddl(), pg_get_tablespace_ddl(), and +# pg_get_role_ddl(). These are TAP tests rather than plain regression +# tests because they create databases and tablespaces, which are +# heavyweight operations that should run only once rather than being +# repeated with every invocation of the core regression suite. + +use strict; +use warnings FATAL => 'all'; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +my $node = PostgreSQL::Test::Cluster->new('main'); +$node->init; +$node->start; + +# Perl helper that strips locale/collation details from DDL output so +# that results are stable across platforms. +sub ddl_filter +{ + my ($text) = @_; + $text =~ s/\s*\bLOCALE_PROVIDER\b\s*=\s*(?:'[^']*'|"[^"]*"|\S+)//gi; + $text =~ s/\s*LC_COLLATE\s*=\s*(['"])[^'"]*\1//gi; + $text =~ s/\s*LC_CTYPE\s*=\s*(['"])[^'"]*\1//gi; + $text =~ s/\s*\S*LOCALE\S*\s*=?\s*(['"])[^'"]*\1//gi; + $text =~ s/\s*\S*COLLATION\S*\s*=?\s*(['"])[^'"]*\1//gi; + return $text; +} + + +######################################################################## +# pg_get_role_ddl tests +######################################################################## + +# Basic role +$node->safe_psql('postgres', 'CREATE ROLE regress_role_ddl_test1'); +my $result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_test1')}); +like($result, + qr/CREATE ROLE regress_role_ddl_test1 .* NOLOGIN/, + 'basic role DDL'); + +# Role with multiple privileges +$node->safe_psql('postgres', q{ + CREATE ROLE regress_role_ddl_test2 + LOGIN SUPERUSER CREATEDB CREATEROLE + CONNECTION LIMIT 5 + VALID UNTIL '2030-12-31 23:59:59+00'}); +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_test2')}); +like($result, qr/SUPERUSER/, 'role with SUPERUSER'); +like($result, qr/CREATEDB/, 'role with CREATEDB'); +like($result, qr/CONNECTION LIMIT 5/, 'role with CONNECTION LIMIT'); +like($result, qr/VALID UNTIL '2030-12-31/, 'role with VALID UNTIL'); + +# Role with configuration parameters +$node->safe_psql('postgres', q{ + ALTER ROLE regress_role_ddl_test1 SET work_mem TO '256MB'; + ALTER ROLE regress_role_ddl_test1 SET search_path TO myschema, public}); +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_test1')}); +like($result, qr/SET work_mem TO '256MB'/, 'role with work_mem setting'); +like($result, qr/SET search_path TO/, 'role with search_path setting'); + +# Role with database-specific configuration (needs a real database) +$node->safe_psql('postgres', q{ + CREATE DATABASE regression_ddlutils_test + TEMPLATE template0 ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C'; + ALTER ROLE regress_role_ddl_test2 + IN DATABASE regression_ddlutils_test SET work_mem TO '128MB'}); +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_test2')}); +like($result, + qr/IN DATABASE regression_ddlutils_test SET work_mem TO '128MB'/, + 'role with database-specific setting'); + +# Role with special characters (requires quoting) +$node->safe_psql('postgres', q{CREATE ROLE "regress_role-with-dash"}); +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_role_ddl('regress_role-with-dash')}); +like($result, qr/"regress_role-with-dash"/, + 'role name requiring quoting'); + +# Pretty-printed output +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_test2', 'pretty', 'true')}); +like($result, qr/\n\s+SUPERUSER/, 'role pretty-print indents attributes'); + +# Role with memberships +$node->safe_psql('postgres', q{ + CREATE ROLE regress_role_ddl_grantor CREATEROLE; + CREATE ROLE regress_role_ddl_group1; + CREATE ROLE regress_role_ddl_group2; + CREATE ROLE regress_role_ddl_member; + GRANT regress_role_ddl_group1 TO regress_role_ddl_grantor WITH ADMIN TRUE; + GRANT regress_role_ddl_group2 TO regress_role_ddl_grantor WITH ADMIN TRUE; + SET ROLE regress_role_ddl_grantor; + GRANT regress_role_ddl_group1 TO regress_role_ddl_member + WITH INHERIT TRUE, SET FALSE; + GRANT regress_role_ddl_group2 TO regress_role_ddl_member + WITH ADMIN TRUE; + RESET ROLE}); +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_member')}); +like($result, qr/GRANT regress_role_ddl_group1 TO regress_role_ddl_member/, + 'role with memberships includes GRANT'); +like($result, qr/SET FALSE/, 'membership includes SET FALSE'); +like($result, qr/ADMIN TRUE/, 'membership includes ADMIN TRUE'); + +# Memberships suppressed +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_role_ddl('regress_role_ddl_member', 'memberships', 'false')}); +unlike($result, qr/GRANT/, 'memberships suppressed'); + +# Non-existent role (should error) +my ($ret, $stdout, $stderr) = $node->psql('postgres', + q{SELECT * FROM pg_get_role_ddl(9999999::oid)}); +isnt($ret, 0, 'non-existent role errors'); +like($stderr, qr/does not exist/, 'non-existent role error message'); + +# NULL input (should return no rows) +$result = $node->safe_psql('postgres', + q{SELECT count(*) FROM pg_get_role_ddl(NULL)}); +is($result, '0', 'NULL role returns no rows'); + +# Permission check: revoke SELECT on pg_authid +$node->safe_psql('postgres', q{ + CREATE ROLE regress_role_ddl_noaccess; + REVOKE SELECT ON pg_authid FROM PUBLIC}); +($ret, $stdout, $stderr) = $node->psql('postgres', + q{SET ROLE regress_role_ddl_noaccess; + SELECT * FROM pg_get_role_ddl('regress_role_ddl_test1')}); +isnt($ret, 0, 'role DDL denied without pg_authid access'); +$node->safe_psql('postgres', q{ + GRANT SELECT ON pg_authid TO PUBLIC}); + + +######################################################################## +# pg_get_database_ddl tests +######################################################################## + +# Set up: the test database was already created above for role tests. +$node->safe_psql('postgres', q{ + ALTER DATABASE regression_ddlutils_test OWNER TO regress_role_ddl_test2; + ALTER DATABASE regression_ddlutils_test CONNECTION LIMIT 123; + ALTER DATABASE regression_ddlutils_test SET random_page_cost = 2.0; + ALTER ROLE regress_role_ddl_test2 + IN DATABASE regression_ddlutils_test SET random_page_cost = 1.1}); + +# Non-existent database +($ret, $stdout, $stderr) = $node->psql('postgres', + q{SELECT * FROM pg_get_database_ddl('regression_no_such_db')}); +isnt($ret, 0, 'non-existent database errors'); + +# NULL input +$result = $node->safe_psql('postgres', + q{SELECT count(*) FROM pg_get_database_ddl(NULL)}); +is($result, '0', 'NULL database returns no rows'); + +# Invalid option +($ret, $stdout, $stderr) = $node->psql('postgres', + q{SELECT * FROM pg_get_database_ddl('regression_ddlutils_test', 'owner', 'invalid')}); +isnt($ret, 0, 'invalid boolean option errors'); +like($stderr, qr/invalid value/, 'invalid option error message'); + +# Duplicate option +($ret, $stdout, $stderr) = $node->psql('postgres', + q{SELECT * FROM pg_get_database_ddl('regression_ddlutils_test', + 'owner', 'false', 'owner', 'true')}); +isnt($ret, 0, 'duplicate option errors'); + +# Basic output (without locale details) +$result = ddl_filter($node->safe_psql('postgres', + q{SELECT pg_get_database_ddl + FROM pg_get_database_ddl('regression_ddlutils_test')})); +like($result, qr/CREATE DATABASE regression_ddlutils_test/, + 'database DDL includes CREATE'); +like($result, qr/TEMPLATE = template0/, 'database DDL includes TEMPLATE'); +like($result, qr/ENCODING = 'UTF8'/, 'database DDL includes ENCODING'); +like($result, qr/OWNER TO regress_role_ddl_test2/, 'database DDL includes OWNER'); +like($result, qr/CONNECTION LIMIT = 123/, 'database DDL includes CONNLIMIT'); +like($result, qr/SET random_page_cost TO '2.0'/, + 'database DDL includes GUC setting'); + +# Pretty-printed output +$result = ddl_filter($node->safe_psql('postgres', + q{SELECT pg_get_database_ddl + FROM pg_get_database_ddl('regression_ddlutils_test', + 'pretty', 'true', 'tablespace', 'false')})); +like($result, qr/\n\s+WITH TEMPLATE/, 'database DDL pretty-prints WITH'); + +# Permission check +$node->safe_psql('postgres', q{ + REVOKE CONNECT ON DATABASE regression_ddlutils_test FROM PUBLIC}); +($ret, $stdout, $stderr) = $node->psql('postgres', + q{SET ROLE regress_role_ddl_noaccess; + SELECT * FROM pg_get_database_ddl('regression_ddlutils_test')}); +isnt($ret, 0, 'database DDL denied without CONNECT'); +$node->safe_psql('postgres', q{ + GRANT CONNECT ON DATABASE regression_ddlutils_test TO PUBLIC}); + + +######################################################################## +# pg_get_tablespace_ddl tests +######################################################################## + +# Non-existent tablespace by name +($ret, $stdout, $stderr) = $node->psql('postgres', + q{SELECT * FROM pg_get_tablespace_ddl('regress_nonexistent_tblsp')}); +isnt($ret, 0, 'non-existent tablespace errors'); + +# Non-existent tablespace by OID +($ret, $stdout, $stderr) = $node->psql('postgres', + q{SELECT * FROM pg_get_tablespace_ddl(0::oid)}); +isnt($ret, 0, 'non-existent tablespace OID errors'); + +# NULL input (name and OID variants) +$result = $node->safe_psql('postgres', + q{SELECT count(*) FROM pg_get_tablespace_ddl(NULL::name)}); +is($result, '0', 'NULL tablespace name returns no rows'); +$result = $node->safe_psql('postgres', + q{SELECT count(*) FROM pg_get_tablespace_ddl(NULL::oid)}); +is($result, '0', 'NULL tablespace OID returns no rows'); + +# Tablespace name requiring quoting +$node->safe_psql('postgres', q{ + SET allow_in_place_tablespaces = true; + CREATE TABLESPACE "regress_ tblsp" OWNER regress_role_ddl_test1 + LOCATION ''}); +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_tablespace_ddl('regress_ tblsp')}); +like($result, qr/"regress_ tblsp"/, 'tablespace name is quoted'); + +# Rename and add options; reuse this tablespace for the remaining tests +$node->safe_psql('postgres', q{ + ALTER TABLESPACE "regress_ tblsp" RENAME TO regress_allopt_tblsp; + ALTER TABLESPACE regress_allopt_tblsp + SET (seq_page_cost = '1.5', random_page_cost = '1.1234567890', + effective_io_concurrency = '17', maintenance_io_concurrency = '18')}); + +# Tablespace with multiple options +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp')}); +like($result, qr/CREATE TABLESPACE regress_allopt_tblsp/, + 'tablespace DDL includes CREATE'); +like($result, qr/OWNER regress_role_ddl_test1/, + 'tablespace DDL includes OWNER'); +like($result, qr/seq_page_cost='1.5'/, 'tablespace DDL includes options'); + +# Pretty-printed output +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp', + 'pretty', 'true')}); +like($result, qr/\n\s+OWNER/, 'tablespace DDL pretty-prints OWNER'); + +# Owner suppressed +$result = $node->safe_psql('postgres', + q{SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp', + 'owner', 'false')}); +unlike($result, qr/OWNER/, 'tablespace DDL owner suppressed'); + +# Lookup by OID +$result = $node->safe_psql('postgres', q{ + SELECT pg_get_tablespace_ddl + FROM pg_get_tablespace_ddl( + (SELECT oid FROM pg_tablespace + WHERE spcname = 'regress_allopt_tblsp'))}); +like($result, qr/CREATE TABLESPACE regress_allopt_tblsp/, + 'tablespace DDL by OID'); + +# Permission check +$node->safe_psql('postgres', + q{REVOKE SELECT ON pg_tablespace FROM PUBLIC}); +($ret, $stdout, $stderr) = $node->psql('postgres', + q{SET ROLE regress_role_ddl_noaccess; + SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp')}); +isnt($ret, 0, 'tablespace DDL denied without pg_tablespace access'); +$node->safe_psql('postgres', q{ + GRANT SELECT ON pg_tablespace TO PUBLIC}); + +$node->stop; + +done_testing(); diff --git a/src/test/regress/expected/database_ddl.out b/src/test/regress/expected/database_ddl.out deleted file mode 100644 index 97657e52cfa..00000000000 --- a/src/test/regress/expected/database_ddl.out +++ /dev/null @@ -1,88 +0,0 @@ --- --- Tests for pg_get_database_ddl() --- --- To produce stable regression test output, strip locale/collation details --- from the DDL output. Uses a plain SQL function to avoid a PL/pgSQL --- dependency. -CREATE OR REPLACE FUNCTION ddl_filter(ddl_input TEXT) -RETURNS TEXT LANGUAGE sql AS $$ -SELECT regexp_replace( - regexp_replace( - regexp_replace( - regexp_replace( - regexp_replace( - ddl_input, - '\s*\mLOCALE_PROVIDER\M\s*=\s*([''"]?[^''"\s]+[''"]?)', '', 'gi'), - '\s*LC_COLLATE\s*=\s*([''"])[^''"]*\1', '', 'gi'), - '\s*LC_CTYPE\s*=\s*([''"])[^''"]*\1', '', 'gi'), - '\s*\S*LOCALE\S*\s*=?\s*([''"])[^''"]*\1', '', 'gi'), - '\s*\S*COLLATION\S*\s*=?\s*([''"])[^''"]*\1', '', 'gi') -$$; -CREATE ROLE regress_datdba; -CREATE DATABASE regression_database_ddl - ENCODING utf8 LC_COLLATE "C" LC_CTYPE "C" TEMPLATE template0 - OWNER regress_datdba; -ALTER DATABASE regression_database_ddl CONNECTION_LIMIT 123; -ALTER DATABASE regression_database_ddl SET random_page_cost = 2.0; -ALTER ROLE regress_datdba IN DATABASE regression_database_ddl SET random_page_cost = 1.1; --- Database doesn't exist -SELECT * FROM pg_get_database_ddl('regression_database'); -ERROR: database "regression_database" does not exist -LINE 1: SELECT * FROM pg_get_database_ddl('regression_database'); - ^ --- NULL value -SELECT * FROM pg_get_database_ddl(NULL); - pg_get_database_ddl ---------------------- -(0 rows) - --- Invalid option value (should error) -SELECT * FROM pg_get_database_ddl('regression_database_ddl', 'owner', 'invalid'); -ERROR: invalid value for boolean option "owner": invalid --- Duplicate option (should error) -SELECT * FROM pg_get_database_ddl('regression_database_ddl', 'owner', 'false', 'owner', 'true'); -ERROR: option "owner" is specified more than once --- Without options -SELECT ddl_filter(pg_get_database_ddl) FROM pg_get_database_ddl('regression_database_ddl'); - ddl_filter --------------------------------------------------------------------------------------- - CREATE DATABASE regression_database_ddl WITH TEMPLATE = template0 ENCODING = 'UTF8'; - ALTER DATABASE regression_database_ddl OWNER TO regress_datdba; - ALTER DATABASE regression_database_ddl CONNECTION LIMIT = 123; - ALTER DATABASE regression_database_ddl SET random_page_cost TO '2.0'; -(4 rows) - --- With owner -SELECT ddl_filter(pg_get_database_ddl) FROM pg_get_database_ddl('regression_database_ddl', 'owner', 'true'); - ddl_filter --------------------------------------------------------------------------------------- - CREATE DATABASE regression_database_ddl WITH TEMPLATE = template0 ENCODING = 'UTF8'; - ALTER DATABASE regression_database_ddl OWNER TO regress_datdba; - ALTER DATABASE regression_database_ddl CONNECTION LIMIT = 123; - ALTER DATABASE regression_database_ddl SET random_page_cost TO '2.0'; -(4 rows) - --- Pretty-printed output -\pset format unaligned -SELECT ddl_filter(pg_get_database_ddl) FROM pg_get_database_ddl('regression_database_ddl', 'pretty', 'true', 'tablespace', 'false'); -ddl_filter -CREATE DATABASE regression_database_ddl - WITH TEMPLATE = template0 - ENCODING = 'UTF8'; -ALTER DATABASE regression_database_ddl OWNER TO regress_datdba; -ALTER DATABASE regression_database_ddl CONNECTION LIMIT = 123; -ALTER DATABASE regression_database_ddl SET random_page_cost TO '2.0'; -(4 rows) -\pset format aligned --- Permission check: revoke CONNECT on database -CREATE ROLE regress_db_ddl_noaccess; -REVOKE CONNECT ON DATABASE regression_database_ddl FROM PUBLIC; -SET ROLE regress_db_ddl_noaccess; -SELECT * FROM pg_get_database_ddl('regression_database_ddl'); -- should fail -ERROR: permission denied for database regression_database_ddl -RESET ROLE; -GRANT CONNECT ON DATABASE regression_database_ddl TO PUBLIC; -DROP ROLE regress_db_ddl_noaccess; -DROP DATABASE regression_database_ddl; -DROP FUNCTION ddl_filter(text); -DROP ROLE regress_datdba; diff --git a/src/test/regress/expected/role_ddl.out b/src/test/regress/expected/role_ddl.out deleted file mode 100644 index 575111da55c..00000000000 --- a/src/test/regress/expected/role_ddl.out +++ /dev/null @@ -1,143 +0,0 @@ --- Consistent test results -SET timezone TO 'UTC'; -SET DateStyle TO 'ISO, YMD'; --- Create test database -CREATE DATABASE regression_role_ddl_test; --- Basic role -CREATE ROLE regress_role_ddl_test1; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test1'); - pg_get_role_ddl -------------------------------------------------------------------------------------------------------------------- - CREATE ROLE regress_role_ddl_test1 NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; -(1 row) - --- Role with LOGIN -CREATE ROLE regress_role_ddl_test2 LOGIN; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test2'); - pg_get_role_ddl ------------------------------------------------------------------------------------------------------------------ - CREATE ROLE regress_role_ddl_test2 NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB LOGIN NOREPLICATION NOBYPASSRLS; -(1 row) - --- Role with multiple privileges -CREATE ROLE regress_role_ddl_test3 - LOGIN - SUPERUSER - CREATEDB - CREATEROLE - CONNECTION LIMIT 5 - VALID UNTIL '2030-12-31 23:59:59+00'; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test3'); - pg_get_role_ddl -------------------------------------------------------------------------------------------------------------------------------------------------------------------- - CREATE ROLE regress_role_ddl_test3 SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN NOREPLICATION NOBYPASSRLS CONNECTION LIMIT 5 VALID UNTIL '2030-12-31 23:59:59+00'; -(1 row) - --- Role with configuration parameters -CREATE ROLE regress_role_ddl_test4; -ALTER ROLE regress_role_ddl_test4 SET work_mem TO '256MB'; -ALTER ROLE regress_role_ddl_test4 SET search_path TO myschema, public; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test4'); - pg_get_role_ddl -------------------------------------------------------------------------------------------------------------------- - CREATE ROLE regress_role_ddl_test4 NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; - ALTER ROLE regress_role_ddl_test4 SET work_mem TO '256MB'; - ALTER ROLE regress_role_ddl_test4 SET search_path TO 'myschema', 'public'; -(3 rows) - --- Role with database-specific configuration -CREATE ROLE regress_role_ddl_test5; -ALTER ROLE regress_role_ddl_test5 IN DATABASE regression_role_ddl_test SET work_mem TO '128MB'; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test5'); - pg_get_role_ddl -------------------------------------------------------------------------------------------------------------------- - CREATE ROLE regress_role_ddl_test5 NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; - ALTER ROLE regress_role_ddl_test5 IN DATABASE regression_role_ddl_test SET work_mem TO '128MB'; -(2 rows) - --- Role with special characters (requires quoting) -CREATE ROLE "regress_role-with-dash"; -SELECT * FROM pg_get_role_ddl('regress_role-with-dash'); - pg_get_role_ddl ---------------------------------------------------------------------------------------------------------------------- - CREATE ROLE "regress_role-with-dash" NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; -(1 row) - --- Pretty-printed output -\pset format unaligned -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test3', 'pretty', 'true'); -pg_get_role_ddl -CREATE ROLE regress_role_ddl_test3 - SUPERUSER - INHERIT - CREATEROLE - CREATEDB - LOGIN - NOREPLICATION - NOBYPASSRLS - CONNECTION LIMIT 5 - VALID UNTIL '2030-12-31 23:59:59+00'; -(1 row) -\pset format aligned --- Role with memberships -CREATE ROLE regress_role_ddl_grantor CREATEROLE; -CREATE ROLE regress_role_ddl_group1; -CREATE ROLE regress_role_ddl_group2; -CREATE ROLE regress_role_ddl_member; -GRANT regress_role_ddl_group1 TO regress_role_ddl_grantor WITH ADMIN TRUE; -GRANT regress_role_ddl_group2 TO regress_role_ddl_grantor WITH ADMIN TRUE; -SET ROLE regress_role_ddl_grantor; -GRANT regress_role_ddl_group1 TO regress_role_ddl_member WITH INHERIT TRUE, SET FALSE; -GRANT regress_role_ddl_group2 TO regress_role_ddl_member WITH ADMIN TRUE; -RESET ROLE; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_member'); - pg_get_role_ddl ------------------------------------------------------------------------------------------------------------------------------------------ - CREATE ROLE regress_role_ddl_member NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; - GRANT regress_role_ddl_group1 TO regress_role_ddl_member WITH ADMIN FALSE, INHERIT TRUE, SET FALSE GRANTED BY regress_role_ddl_grantor; - GRANT regress_role_ddl_group2 TO regress_role_ddl_member WITH ADMIN TRUE, INHERIT TRUE, SET TRUE GRANTED BY regress_role_ddl_grantor; -(3 rows) - --- Role with memberships suppressed -SELECT * FROM pg_get_role_ddl('regress_role_ddl_member', 'memberships', 'false'); - pg_get_role_ddl --------------------------------------------------------------------------------------------------------------------- - CREATE ROLE regress_role_ddl_member NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; -(1 row) - --- Non-existent role (should error) -SELECT * FROM pg_get_role_ddl(9999999::oid); -ERROR: role with OID 9999999 does not exist --- NULL input (should return no rows) -SELECT * FROM pg_get_role_ddl(NULL); - pg_get_role_ddl ------------------ -(0 rows) - --- Permission check: revoke SELECT on pg_authid -CREATE ROLE regress_role_ddl_noaccess; -REVOKE SELECT ON pg_authid FROM PUBLIC; -SET ROLE regress_role_ddl_noaccess; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test1'); -- should fail -ERROR: permission denied for role regress_role_ddl_test1 -RESET ROLE; -GRANT SELECT ON pg_authid TO PUBLIC; -DROP ROLE regress_role_ddl_noaccess; --- Cleanup -DROP ROLE regress_role_ddl_test1; -DROP ROLE regress_role_ddl_test2; -DROP ROLE regress_role_ddl_test3; -DROP ROLE regress_role_ddl_test4; -DROP ROLE regress_role_ddl_test5; -DROP ROLE "regress_role-with-dash"; -SET ROLE regress_role_ddl_grantor; -REVOKE regress_role_ddl_group1 FROM regress_role_ddl_member; -REVOKE regress_role_ddl_group2 FROM regress_role_ddl_member; -RESET ROLE; -DROP ROLE regress_role_ddl_member; -DROP ROLE regress_role_ddl_group1; -DROP ROLE regress_role_ddl_group2; -DROP ROLE regress_role_ddl_grantor; -DROP DATABASE regression_role_ddl_test; --- Reset timezone to default -RESET timezone; diff --git a/src/test/regress/expected/tablespace_ddl.out b/src/test/regress/expected/tablespace_ddl.out deleted file mode 100644 index e52043273a9..00000000000 --- a/src/test/regress/expected/tablespace_ddl.out +++ /dev/null @@ -1,84 +0,0 @@ --- --- Tests for pg_get_tablespace_ddl() --- -SET allow_in_place_tablespaces = true; -CREATE ROLE regress_tblspc_ddl_user; --- error: non-existent tablespace by name -SELECT * FROM pg_get_tablespace_ddl('regress_nonexistent_tblsp'); -ERROR: tablespace "regress_nonexistent_tblsp" does not exist --- error: non-existent tablespace by OID -SELECT * FROM pg_get_tablespace_ddl(0::oid); -ERROR: tablespace with OID 0 does not exist --- NULL input returns no rows (name variant) -SELECT * FROM pg_get_tablespace_ddl(NULL::name); - pg_get_tablespace_ddl ------------------------ -(0 rows) - --- NULL input returns no rows (OID variant) -SELECT * FROM pg_get_tablespace_ddl(NULL::oid); - pg_get_tablespace_ddl ------------------------ -(0 rows) - --- tablespace name requiring quoting -CREATE TABLESPACE "regress_ tblsp" OWNER regress_tblspc_ddl_user LOCATION ''; -SELECT * FROM pg_get_tablespace_ddl('regress_ tblsp'); - pg_get_tablespace_ddl -------------------------------------------------------------------------------- - CREATE TABLESPACE "regress_ tblsp" OWNER regress_tblspc_ddl_user LOCATION ''; -(1 row) - -DROP TABLESPACE "regress_ tblsp"; --- tablespace with multiple options -CREATE TABLESPACE regress_allopt_tblsp OWNER regress_tblspc_ddl_user LOCATION '' - WITH (seq_page_cost = '1.5', random_page_cost = '1.1234567890', - effective_io_concurrency = '17', maintenance_io_concurrency = '18'); -SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp'); - pg_get_tablespace_ddl -------------------------------------------------------------------------------------------------------------------------------------------------------------------- - CREATE TABLESPACE regress_allopt_tblsp OWNER regress_tblspc_ddl_user LOCATION ''; - ALTER TABLESPACE regress_allopt_tblsp SET (seq_page_cost='1.5', random_page_cost='1.1234567890', effective_io_concurrency='17', maintenance_io_concurrency='18'); -(2 rows) - --- pretty-printed output -\pset format unaligned -SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp', 'pretty', 'true'); -pg_get_tablespace_ddl -CREATE TABLESPACE regress_allopt_tblsp - OWNER regress_tblspc_ddl_user - LOCATION ''; -ALTER TABLESPACE regress_allopt_tblsp SET (seq_page_cost='1.5', random_page_cost='1.1234567890', effective_io_concurrency='17', maintenance_io_concurrency='18'); -(2 rows) -\pset format aligned --- tablespace with owner suppressed -SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp', 'owner', 'false'); - pg_get_tablespace_ddl -------------------------------------------------------------------------------------------------------------------------------------------------------------------- - CREATE TABLESPACE regress_allopt_tblsp LOCATION ''; - ALTER TABLESPACE regress_allopt_tblsp SET (seq_page_cost='1.5', random_page_cost='1.1234567890', effective_io_concurrency='17', maintenance_io_concurrency='18'); -(2 rows) - -DROP TABLESPACE regress_allopt_tblsp; --- test by OID -CREATE TABLESPACE regress_oid_tblsp OWNER regress_tblspc_ddl_user LOCATION ''; -SELECT oid AS tsid FROM pg_tablespace WHERE spcname = 'regress_oid_tblsp' \gset -SELECT * FROM pg_get_tablespace_ddl(:tsid); - pg_get_tablespace_ddl --------------------------------------------------------------------------------- - CREATE TABLESPACE regress_oid_tblsp OWNER regress_tblspc_ddl_user LOCATION ''; -(1 row) - -DROP TABLESPACE regress_oid_tblsp; --- Permission check: revoke SELECT on pg_tablespace -CREATE TABLESPACE regress_acl_tblsp OWNER regress_tblspc_ddl_user LOCATION ''; -CREATE ROLE regress_tblspc_ddl_noaccess; -REVOKE SELECT ON pg_tablespace FROM PUBLIC; -SET ROLE regress_tblspc_ddl_noaccess; -SELECT * FROM pg_get_tablespace_ddl('regress_acl_tblsp'); -- should fail -ERROR: permission denied for tablespace regress_acl_tblsp -RESET ROLE; -GRANT SELECT ON pg_tablespace TO PUBLIC; -DROP TABLESPACE regress_acl_tblsp; -DROP ROLE regress_tblspc_ddl_noaccess; -DROP ROLE regress_tblspc_ddl_user; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 5d4f910155e..8fa0a6c47fb 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -135,7 +135,6 @@ test: compression compression_lz4 compression_pglz cluster # oidjoins is read-only, though, and should run late for best coverage test: oidjoins event_trigger -test: role_ddl tablespace_ddl database_ddl # event_trigger_login cannot run concurrently with any other tests because # on-login event handling could catch connection of a concurrent test. diff --git a/src/test/regress/sql/database_ddl.sql b/src/test/regress/sql/database_ddl.sql deleted file mode 100644 index 89753ac6411..00000000000 --- a/src/test/regress/sql/database_ddl.sql +++ /dev/null @@ -1,66 +0,0 @@ --- --- Tests for pg_get_database_ddl() --- - --- To produce stable regression test output, strip locale/collation details --- from the DDL output. Uses a plain SQL function to avoid a PL/pgSQL --- dependency. - -CREATE OR REPLACE FUNCTION ddl_filter(ddl_input TEXT) -RETURNS TEXT LANGUAGE sql AS $$ -SELECT regexp_replace( - regexp_replace( - regexp_replace( - regexp_replace( - regexp_replace( - ddl_input, - '\s*\mLOCALE_PROVIDER\M\s*=\s*([''"]?[^''"\s]+[''"]?)', '', 'gi'), - '\s*LC_COLLATE\s*=\s*([''"])[^''"]*\1', '', 'gi'), - '\s*LC_CTYPE\s*=\s*([''"])[^''"]*\1', '', 'gi'), - '\s*\S*LOCALE\S*\s*=?\s*([''"])[^''"]*\1', '', 'gi'), - '\s*\S*COLLATION\S*\s*=?\s*([''"])[^''"]*\1', '', 'gi') -$$; - -CREATE ROLE regress_datdba; -CREATE DATABASE regression_database_ddl - ENCODING utf8 LC_COLLATE "C" LC_CTYPE "C" TEMPLATE template0 - OWNER regress_datdba; -ALTER DATABASE regression_database_ddl CONNECTION_LIMIT 123; -ALTER DATABASE regression_database_ddl SET random_page_cost = 2.0; -ALTER ROLE regress_datdba IN DATABASE regression_database_ddl SET random_page_cost = 1.1; - --- Database doesn't exist -SELECT * FROM pg_get_database_ddl('regression_database'); - --- NULL value -SELECT * FROM pg_get_database_ddl(NULL); - --- Invalid option value (should error) -SELECT * FROM pg_get_database_ddl('regression_database_ddl', 'owner', 'invalid'); - --- Duplicate option (should error) -SELECT * FROM pg_get_database_ddl('regression_database_ddl', 'owner', 'false', 'owner', 'true'); - --- Without options -SELECT ddl_filter(pg_get_database_ddl) FROM pg_get_database_ddl('regression_database_ddl'); - --- With owner -SELECT ddl_filter(pg_get_database_ddl) FROM pg_get_database_ddl('regression_database_ddl', 'owner', 'true'); - --- Pretty-printed output -\pset format unaligned -SELECT ddl_filter(pg_get_database_ddl) FROM pg_get_database_ddl('regression_database_ddl', 'pretty', 'true', 'tablespace', 'false'); -\pset format aligned - --- Permission check: revoke CONNECT on database -CREATE ROLE regress_db_ddl_noaccess; -REVOKE CONNECT ON DATABASE regression_database_ddl FROM PUBLIC; -SET ROLE regress_db_ddl_noaccess; -SELECT * FROM pg_get_database_ddl('regression_database_ddl'); -- should fail -RESET ROLE; -GRANT CONNECT ON DATABASE regression_database_ddl TO PUBLIC; -DROP ROLE regress_db_ddl_noaccess; - -DROP DATABASE regression_database_ddl; -DROP FUNCTION ddl_filter(text); -DROP ROLE regress_datdba; diff --git a/src/test/regress/sql/role_ddl.sql b/src/test/regress/sql/role_ddl.sql deleted file mode 100644 index 3d0142242ec..00000000000 --- a/src/test/regress/sql/role_ddl.sql +++ /dev/null @@ -1,96 +0,0 @@ --- Consistent test results -SET timezone TO 'UTC'; -SET DateStyle TO 'ISO, YMD'; - --- Create test database -CREATE DATABASE regression_role_ddl_test; - --- Basic role -CREATE ROLE regress_role_ddl_test1; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test1'); - --- Role with LOGIN -CREATE ROLE regress_role_ddl_test2 LOGIN; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test2'); - --- Role with multiple privileges -CREATE ROLE regress_role_ddl_test3 - LOGIN - SUPERUSER - CREATEDB - CREATEROLE - CONNECTION LIMIT 5 - VALID UNTIL '2030-12-31 23:59:59+00'; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test3'); - --- Role with configuration parameters -CREATE ROLE regress_role_ddl_test4; -ALTER ROLE regress_role_ddl_test4 SET work_mem TO '256MB'; -ALTER ROLE regress_role_ddl_test4 SET search_path TO myschema, public; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test4'); - --- Role with database-specific configuration -CREATE ROLE regress_role_ddl_test5; -ALTER ROLE regress_role_ddl_test5 IN DATABASE regression_role_ddl_test SET work_mem TO '128MB'; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test5'); - --- Role with special characters (requires quoting) -CREATE ROLE "regress_role-with-dash"; -SELECT * FROM pg_get_role_ddl('regress_role-with-dash'); - --- Pretty-printed output -\pset format unaligned -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test3', 'pretty', 'true'); -\pset format aligned - --- Role with memberships -CREATE ROLE regress_role_ddl_grantor CREATEROLE; -CREATE ROLE regress_role_ddl_group1; -CREATE ROLE regress_role_ddl_group2; -CREATE ROLE regress_role_ddl_member; -GRANT regress_role_ddl_group1 TO regress_role_ddl_grantor WITH ADMIN TRUE; -GRANT regress_role_ddl_group2 TO regress_role_ddl_grantor WITH ADMIN TRUE; -SET ROLE regress_role_ddl_grantor; -GRANT regress_role_ddl_group1 TO regress_role_ddl_member WITH INHERIT TRUE, SET FALSE; -GRANT regress_role_ddl_group2 TO regress_role_ddl_member WITH ADMIN TRUE; -RESET ROLE; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_member'); - --- Role with memberships suppressed -SELECT * FROM pg_get_role_ddl('regress_role_ddl_member', 'memberships', 'false'); - --- Non-existent role (should error) -SELECT * FROM pg_get_role_ddl(9999999::oid); - --- NULL input (should return no rows) -SELECT * FROM pg_get_role_ddl(NULL); - --- Permission check: revoke SELECT on pg_authid -CREATE ROLE regress_role_ddl_noaccess; -REVOKE SELECT ON pg_authid FROM PUBLIC; -SET ROLE regress_role_ddl_noaccess; -SELECT * FROM pg_get_role_ddl('regress_role_ddl_test1'); -- should fail -RESET ROLE; -GRANT SELECT ON pg_authid TO PUBLIC; -DROP ROLE regress_role_ddl_noaccess; - --- Cleanup -DROP ROLE regress_role_ddl_test1; -DROP ROLE regress_role_ddl_test2; -DROP ROLE regress_role_ddl_test3; -DROP ROLE regress_role_ddl_test4; -DROP ROLE regress_role_ddl_test5; -DROP ROLE "regress_role-with-dash"; -SET ROLE regress_role_ddl_grantor; -REVOKE regress_role_ddl_group1 FROM regress_role_ddl_member; -REVOKE regress_role_ddl_group2 FROM regress_role_ddl_member; -RESET ROLE; -DROP ROLE regress_role_ddl_member; -DROP ROLE regress_role_ddl_group1; -DROP ROLE regress_role_ddl_group2; -DROP ROLE regress_role_ddl_grantor; - -DROP DATABASE regression_role_ddl_test; - --- Reset timezone to default -RESET timezone; diff --git a/src/test/regress/sql/tablespace_ddl.sql b/src/test/regress/sql/tablespace_ddl.sql deleted file mode 100644 index ee3cc6e2e1e..00000000000 --- a/src/test/regress/sql/tablespace_ddl.sql +++ /dev/null @@ -1,58 +0,0 @@ --- --- Tests for pg_get_tablespace_ddl() --- - -SET allow_in_place_tablespaces = true; -CREATE ROLE regress_tblspc_ddl_user; - --- error: non-existent tablespace by name -SELECT * FROM pg_get_tablespace_ddl('regress_nonexistent_tblsp'); - --- error: non-existent tablespace by OID -SELECT * FROM pg_get_tablespace_ddl(0::oid); - --- NULL input returns no rows (name variant) -SELECT * FROM pg_get_tablespace_ddl(NULL::name); - --- NULL input returns no rows (OID variant) -SELECT * FROM pg_get_tablespace_ddl(NULL::oid); - --- tablespace name requiring quoting -CREATE TABLESPACE "regress_ tblsp" OWNER regress_tblspc_ddl_user LOCATION ''; -SELECT * FROM pg_get_tablespace_ddl('regress_ tblsp'); -DROP TABLESPACE "regress_ tblsp"; - --- tablespace with multiple options -CREATE TABLESPACE regress_allopt_tblsp OWNER regress_tblspc_ddl_user LOCATION '' - WITH (seq_page_cost = '1.5', random_page_cost = '1.1234567890', - effective_io_concurrency = '17', maintenance_io_concurrency = '18'); -SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp'); - --- pretty-printed output -\pset format unaligned -SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp', 'pretty', 'true'); -\pset format aligned - --- tablespace with owner suppressed -SELECT * FROM pg_get_tablespace_ddl('regress_allopt_tblsp', 'owner', 'false'); - -DROP TABLESPACE regress_allopt_tblsp; - --- test by OID -CREATE TABLESPACE regress_oid_tblsp OWNER regress_tblspc_ddl_user LOCATION ''; -SELECT oid AS tsid FROM pg_tablespace WHERE spcname = 'regress_oid_tblsp' \gset -SELECT * FROM pg_get_tablespace_ddl(:tsid); -DROP TABLESPACE regress_oid_tblsp; - --- Permission check: revoke SELECT on pg_tablespace -CREATE TABLESPACE regress_acl_tblsp OWNER regress_tblspc_ddl_user LOCATION ''; -CREATE ROLE regress_tblspc_ddl_noaccess; -REVOKE SELECT ON pg_tablespace FROM PUBLIC; -SET ROLE regress_tblspc_ddl_noaccess; -SELECT * FROM pg_get_tablespace_ddl('regress_acl_tblsp'); -- should fail -RESET ROLE; -GRANT SELECT ON pg_tablespace TO PUBLIC; -DROP TABLESPACE regress_acl_tblsp; -DROP ROLE regress_tblspc_ddl_noaccess; - -DROP ROLE regress_tblspc_ddl_user;