From c92b9b3cc8726ce252193c0fffe9250d461ddad7 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Sun, 26 Apr 2026 09:43:57 -0400
Subject: [PATCH] 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
---
 src/test/modules/test_misc/meson.build       |   1 +
 src/test/modules/test_misc/t/012_ddlutils.pl | 286 +++++++++++++++++++
 src/test/regress/expected/database_ddl.out   |  88 ------
 src/test/regress/expected/role_ddl.out       | 143 ----------
 src/test/regress/expected/tablespace_ddl.out |  84 ------
 src/test/regress/parallel_schedule           |   1 -
 src/test/regress/sql/database_ddl.sql        |  66 -----
 src/test/regress/sql/role_ddl.sql            |  96 -------
 src/test/regress/sql/tablespace_ddl.sql      |  58 ----
 9 files changed, 287 insertions(+), 536 deletions(-)
 create mode 100644 src/test/modules/test_misc/t/012_ddlutils.pl
 delete mode 100644 src/test/regress/expected/database_ddl.out
 delete mode 100644 src/test/regress/expected/role_ddl.out
 delete mode 100644 src/test/regress/expected/tablespace_ddl.out
 delete mode 100644 src/test/regress/sql/database_ddl.sql
 delete mode 100644 src/test/regress/sql/role_ddl.sql
 delete mode 100644 src/test/regress/sql/tablespace_ddl.sql

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 288e94dc408..02a595e3367 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -135,7 +135,6 @@ test: compression compression_lz4 compression_pglz
 # 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;
-- 
2.43.0

