From 07bf3ed7d7c299cd6aeb955398373ed79ec0df4a Mon Sep 17 00:00:00 2001 From: Akshay Joshi Date: Wed, 21 Jan 2026 14:21:28 +0530 Subject: [PATCH v8 2/2] Add pg_get_database_ddl() function to reconstruct CREATE DATABASE statements. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a new system function, pg_get_database_ddl(database_name/database_oid, ddl_options), which reconstructs the CREATE DATABASE statement for a given database name or database oid. Supported ddl_options are 'pretty', 'owner=no/false/0', 'tablespace=no/false/0' and 'defaults'. Usage: SELECT pg_get_database_ddl('postgres'); // Non pretty-formatted DDL SELECT pg_get_database_ddl(16835); // Non pretty-formatted DDL SELECT pg_get_database_ddl('postgres', 'pretty'); // pretty-formatted DDL SELECT pg_get_database_ddl('postgres', 'owner=no', 'tablespace=false'); -- Omits Owner and Tablespace clauses. SELECT pg_get_database_ddl('postgres', 'pretty', 'defaults=yes'); -- Includes clauses for parameters at their default values. SELECT pg_get_database_ddl('postgres', 'pretty', 'defaults'); -- Includes clauses for parameters at their default values. Reference: PG-150 Author: Akshay Joshi Reviewed-by: Álvaro Herrera Reviewed-by: Quan Zongliang Reviewed-by: Japin Li Reviewed-by: Chao Li Reviewed-by: Euler Taveira --- doc/src/sgml/func/func-info.sgml | 17 ++-- src/backend/utils/adt/ruleutils.c | 103 +++++++++++++++++++++---- src/include/utils/ddl_defaults.h | 4 +- src/test/regress/expected/database.out | 20 ++--- src/test/regress/sql/database.sql | 14 ++-- 5 files changed, 120 insertions(+), 38 deletions(-) diff --git a/doc/src/sgml/func/func-info.sgml b/doc/src/sgml/func/func-info.sgml index 2250031bb83..71a60dbc11c 100644 --- a/doc/src/sgml/func/func-info.sgml +++ b/doc/src/sgml/func/func-info.sgml @@ -3859,7 +3859,10 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} pg_get_database_ddl - pg_get_database_ddl ( database_id regdatabase , VARIADIC ddl_options text[] ) + pg_get_database_ddl + ( database_id regdatabase + , VARIADIC ddl_options + text[] ) text @@ -3867,8 +3870,9 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} system catalogs for a specified database. The first argument is the OID or name of the database. The optional variadic argument is an array of text flags to control the output. Supported options include - pretty, --no-owner, - --no-tablespace, and --with-defaults. + pretty, owner=no/false/0, + tablespace=no/false/0, and defaults + Or defaults=yes. @@ -3886,18 +3890,19 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} - --no-owner: Omits the OWNER clause from + owner=no/false/0: Omits the OWNER clause from the reconstructed statement. - --no-tablespace: Omits the TABLESPACE clause. + tablespace=no/false/0: Omits the TABLESPACE + clause from the reconstructed statement. - --with-defaults: Includes clauses for parameters that are + defaults: Includes clauses for parameters that are currently at their default values (e.g., CONNECTION LIMIT -1), which are normally omitted for brevity. diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index c6b59790655..8181010224a 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -13813,34 +13813,109 @@ parse_ddl_options(ArrayType *ddl_options) if (ddl_options == NULL) return flags; - deconstruct_array(ddl_options, - TEXTOID, -1, false, 'i', + deconstruct_array(ddl_options, TEXTOID, -1, false, 'i', &options, &nulls, &n_options); for (i = 0; i < n_options; i++) { char *opt; + char *name; + char *value; if (nulls[i]) continue; opt = TextDatumGetCString(options[i]); + name = opt; + value = strchr(opt, '='); + + if (value != NULL) + { + *value = '\0'; + value++; + } + + /* + * * Logic for "owner": handle 'owner=no', 'owner=0', 'owner=false', + * etc. Using pg_strcasecmp for the key and parse_bool for the value. + */ + if (pg_strcasecmp(name, "owner") == 0) + { + bool bval; + + if (value == NULL) + continue; + + if (parse_bool(value, &bval)) + { + if (!bval) + flags |= PG_DDL_NO_OWNER; + } + else + goto invalid_value; + } + /* Logic for "tablespace" */ + else if (pg_strcasecmp(name, "tablespace") == 0) + { + bool bval; + + if (value == NULL) + continue; + + if (parse_bool(value, &bval)) + { + if (!bval) + flags |= PG_DDL_NO_TABLESPACE; + } + else + goto invalid_value; + } + /* Logic for "defaults" */ + else if (pg_strcasecmp(name, "defaults") == 0) + { + bool bval; + + /* If just 'defaults' is passed without '=val', we assume true */ + if (value == NULL) + flags |= PG_DDL_WITH_DEFAULTS; + else if (parse_bool(value, &bval)) + { + if (bval) + flags |= PG_DDL_WITH_DEFAULTS; + } + else + goto invalid_value; + } + /* Logic for "pretty" */ + else if (pg_strcasecmp(name, "pretty") == 0) + { + /* Usually a standalone flag, but we check boolean if provided */ + bool bval; - /* Map strings to bitmask flags */ - if (strcmp(opt, "pretty") == 0) - flags |= PG_DDL_PRETTY_INDENT; - else if (strcmp(opt, "--no-owner") == 0) - flags |= PG_DDL_NO_OWNER; - else if (strcmp(opt, "--no-tablespace") == 0) - flags |= PG_DDL_NO_TABLESPACE; - else if (strcmp(opt, "--with-defaults") == 0) - flags |= PG_DDL_WITH_DEFAULTS; + if (value == NULL) + flags |= PG_DDL_PRETTY_INDENT; + else if (parse_bool(value, &bval)) + { + if (bval) + flags |= PG_DDL_PRETTY_INDENT; + } + else + goto invalid_value; + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized option: %s", opt))); + errmsg("unrecognized option: %s", name))); + } pfree(opt); + continue; + +invalid_value: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid value for option \"%s\": %s", name, value))); } pfree(options); @@ -13929,7 +14004,7 @@ pg_get_database_ddl_worker(Oid db_oid, ArrayType *ddl_options) quote_identifier(dbform->datname.data)); get_formatted_string(&buf, pretty_flags, 4, "WITH"); - /* Set the OWNER in the DDL if --no-owner is not specified */ + /* Set the OWNER in the DDL if owner is not omitted */ if (OidIsValid(dbform->datdba) && !(ddl_flags & PG_DDL_NO_OWNER)) { get_formatted_string(&buf, pretty_flags, 8, "OWNER = %s", @@ -13997,7 +14072,7 @@ pg_get_database_ddl_worker(Oid db_oid, ArrayType *ddl_options) else if (is_with_defaults) get_formatted_string(&buf, pretty_flags, 8, "LOCALE_PROVIDER = libc"); - /* Set the TABLESPACE in the DDL if --no-tablespace is not specified */ + /* Set the TABLESPACE in the DDL if tablespace is not omitted */ if (OidIsValid(dbform->dattablespace) && !(ddl_flags & PG_DDL_NO_TABLESPACE)) { /* Get the tablespace name respective to the given tablespace oid */ diff --git a/src/include/utils/ddl_defaults.h b/src/include/utils/ddl_defaults.h index 84ef61b4f3d..d17e843fe09 100644 --- a/src/include/utils/ddl_defaults.h +++ b/src/include/utils/ddl_defaults.h @@ -13,6 +13,8 @@ #ifndef DDL_DEFAULTS_H #define DDL_DEFAULTS_H +#include + static const struct { struct @@ -34,4 +36,4 @@ static const struct } }; -#endif /* DDL_DEFAULTS_H */ \ No newline at end of file +#endif /* DDL_DEFAULTS_H */ diff --git a/src/test/regress/expected/database.out b/src/test/regress/expected/database.out index eb3a13bcf9e..a816b0b525f 100644 --- a/src/test/regress/expected/database.out +++ b/src/test/regress/expected/database.out @@ -87,28 +87,28 @@ SELECT ddl_filter(pg_get_database_ddl('regression_utf8')); (1 row) -- With No Owner -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', '--no-owner')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'owner=no')); ddl_filter -------------------------------------------------------------- CREATE DATABASE regression_utf8 WITH CONNECTION LIMIT = 123; (1 row) -- With No Tablespace -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', '--no-tablespace')); - ddl_filter -------------------------------------------------------------------------------------------- - CREATE DATABASE regression_utf8 WITH OWNER = regress_datdba_after CONNECTION LIMIT = 123; +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'defaults', 'tablespace=no')); + ddl_filter +-------------------------------------------------------------------------------------------------------------------------------------- + CREATE DATABASE regression_utf8 WITH OWNER = regress_datdba_after ENCODING = 'UTF8' ALLOW_CONNECTIONS = true CONNECTION LIMIT = 123; (1 row) -- With Defaults -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', '--with-defaults')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'defaults=yes')); ddl_filter -------------------------------------------------------------------------------------------------------------------------------------------------------------- CREATE DATABASE regression_utf8 WITH OWNER = regress_datdba_after ENCODING = 'UTF8' TABLESPACE = pg_default ALLOW_CONNECTIONS = true CONNECTION LIMIT = 123; (1 row) -- With No Owner, No Tablespace and With Defaults -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', '--no-owner', '--no-tablespace', '--with-defaults')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'owner=0', 'tablespace=0', 'defaults=1')); ddl_filter --------------------------------------------------------------------------------------------------------- CREATE DATABASE regression_utf8 WITH ENCODING = 'UTF8' ALLOW_CONNECTIONS = true CONNECTION LIMIT = 123; @@ -124,14 +124,14 @@ CREATE DATABASE regression_utf8 CONNECTION LIMIT = 123; (1 row) -- With No Owner and No Tablespace -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', '--no-owner', '--no-tablespace')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', 'owner=no', 'tablespace=no')); ddl_filter CREATE DATABASE regression_utf8 WITH CONNECTION LIMIT = 123; (1 row) -- With Defaults -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', '--with-defaults')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', 'defaults')); ddl_filter CREATE DATABASE regression_utf8 WITH @@ -142,7 +142,7 @@ CREATE DATABASE regression_utf8 CONNECTION LIMIT = 123; (1 row) -- With No Owner, No Tablespace and With Defaults -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', '--no-owner', '--no-tablespace', '--with-defaults')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', 'owner=false', 'tablespace=false', 'defaults=true')); ddl_filter CREATE DATABASE regression_utf8 WITH diff --git a/src/test/regress/sql/database.sql b/src/test/regress/sql/database.sql index 918b28c47da..4cb17a8d0d6 100644 --- a/src/test/regress/sql/database.sql +++ b/src/test/regress/sql/database.sql @@ -86,29 +86,29 @@ SELECT pg_get_database_ddl(NULL); SELECT ddl_filter(pg_get_database_ddl('regression_utf8')); -- With No Owner -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', '--no-owner')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'owner=no')); -- With No Tablespace -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', '--no-tablespace')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'defaults', 'tablespace=no')); -- With Defaults -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', '--with-defaults')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'defaults=yes')); -- With No Owner, No Tablespace and With Defaults -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', '--no-owner', '--no-tablespace', '--with-defaults')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'owner=0', 'tablespace=0', 'defaults=1')); -- With Pretty formatted \pset format unaligned SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty')); -- With No Owner and No Tablespace -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', '--no-owner', '--no-tablespace')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', 'owner=no', 'tablespace=no')); -- With Defaults -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', '--with-defaults')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', 'defaults')); -- With No Owner, No Tablespace and With Defaults -SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', '--no-owner', '--no-tablespace', '--with-defaults')); +SELECT ddl_filter(pg_get_database_ddl('regression_utf8', 'pretty', 'owner=false', 'tablespace=false', 'defaults=true')); DROP DATABASE regression_utf8; DROP ROLE regress_datdba_before; -- 2.51.0