Convert ddlutils regression tests to TAP tests.

The regression tests for pg_get_role_ddl(), pg_get_database_ddl(),
and pg_get_tablespace_ddl() created databases and tablespaces, which
are heavyweight operations.  As noted by Andres Freund, this is
wasteful in the core regression suite which gets run repeatedly.

Convert the three test files (role_ddl.sql, database_ddl.sql,
tablespace_ddl.sql) into a single TAP test that runs once, covering
all the same functionality: basic DDL generation, pretty-printing,
option handling, error cases, permission checks, and edge cases like
quoted names and role memberships.

Discussion: https://postgr.es/m/5c67dc79-909a-4e17-8606-6686667da6c6@dunslane.net
master
Andrew Dunstan 2 weeks ago
parent 748d871b7c
commit c529ee38b9
  1. 1
      src/test/modules/test_misc/meson.build
  2. 286
      src/test/modules/test_misc/t/012_ddlutils.pl
  3. 88
      src/test/regress/expected/database_ddl.out
  4. 143
      src/test/regress/expected/role_ddl.out
  5. 84
      src/test/regress/expected/tablespace_ddl.out
  6. 1
      src/test/regress/parallel_schedule
  7. 66
      src/test/regress/sql/database_ddl.sql
  8. 96
      src/test/regress/sql/role_ddl.sql
  9. 58
      src/test/regress/sql/tablespace_ddl.sql

@ -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,

@ -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();

@ -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;

@ -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;

@ -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;

@ -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.

@ -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;

@ -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;

@ -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;
Loading…
Cancel
Save