Skip to content

Add pg_get_table_ddl() to reconstruct CREATE TABLE statements - #8

Open
akshay-joshi wants to merge 1 commit into
masterfrom
pg_get_table_ddl
Open

Add pg_get_table_ddl() to reconstruct CREATE TABLE statements#8
akshay-joshi wants to merge 1 commit into
masterfrom
pg_get_table_ddl

Conversation

@akshay-joshi

Copy link
Copy Markdown
Owner

The function reconstructs the CREATE TABLE statement for an ordinary or partitioned table, followed by the ALTER TABLE / CREATE INDEX / CREATE RULE / CREATE STATISTICS statements needed to restore its full definition. Each statement is returned as a separate row.

Supported per-column features: data type with type modifiers, COLLATE, STORAGE, COMPRESSION (pglz / lz4), GENERATED ALWAYS AS (expr) STORED/VIRTUAL, GENERATED ALWAYS|BY DEFAULT AS IDENTITY (with sequence options), DEFAULT, NOT NULL, and per-column attoptions emitted as ALTER COLUMN SET (...).

Supported table-level features: UNLOGGED, INHERITS, PARTITION BY (RANGE / LIST / HASH parents), PARTITION OF parent FOR VALUES (FROM/TO, WITH modulus/remainder, DEFAULT), USING table access method, WITH (reloptions), TABLESPACE, and inline CHECK constraints in the CREATE TABLE body.

Supported sub-objects (re-using existing deparse helpers from ruleutils.c): indexes via pg_get_indexdef_string, constraints (PRIMARY KEY, UNIQUE, FOREIGN KEY, EXCLUDE, named NOT NULL) via pg_get_constraintdef_command, rules via pg_get_ruledef, extended statistics via pg_get_statisticsobjdef_string, REPLICA IDENTITY NOTHING/FULL/USING INDEX, ALTER TABLE ENABLE/FORCE ROW LEVEL SECURITY, and child-local DEFAULT overrides on inheritance/partition children.

Default omission convention: every optional clause is dropped when its value equals what the system would reapply on round-trip, including type-default COLLATE, per-type STORAGE, the auto-generated identity sequence name and parameter defaults, heap access method, default REPLICA IDENTITY, disabled RLS toggles, empty reloptions, and the default tablespace.

A regression test under src/test/regress covers ordinary tables, identity (default and custom sequence options), generated columns, STORAGE/COMPRESSION, constraints (CHECK/UNIQUE/FK with deferrable), functional and partial indexes, inheritance and partitioning, partition children with FOR VALUES FROM/TO, WITH modulus/remainder, and DEFAULT, rules, extended statistics, RLS toggles, REPLICA IDENTITY, UNLOGGED with reloptions, per-column attoptions, child DEFAULT overrides, pretty mode, owner=false, and the error paths for views, sequences, NULL, unknown options, and odd-variadic argument counts.

Author: Akshay Joshi akshay.joshi@enterprisedb.com

@akshay-joshi
akshay-joshi force-pushed the pg_get_table_ddl branch 7 times, most recently from 6fe8fc4 to 4d1e585 Compare June 9, 2026 08:52
@akshay-joshi
akshay-joshi force-pushed the pg_get_table_ddl branch 4 times, most recently from 9f4de53 to 98856d7 Compare June 15, 2026 08:51
@akshay-joshi
akshay-joshi force-pushed the pg_get_table_ddl branch 7 times, most recently from 07d3079 to b291ba0 Compare June 24, 2026 09:02
@akshay-joshi
akshay-joshi force-pushed the pg_get_table_ddl branch 9 times, most recently from c300172 to bc6baad Compare July 3, 2026 08:19
@akshay-joshi
akshay-joshi force-pushed the pg_get_table_ddl branch 3 times, most recently from 3299af6 to 3718fdb Compare July 8, 2026 11:05
@akshay-joshi
akshay-joshi force-pushed the pg_get_table_ddl branch 5 times, most recently from b31f2e1 to 77109fd Compare July 16, 2026 06:21
@akshay-joshi
akshay-joshi force-pushed the pg_get_table_ddl branch 2 times, most recently from 5630a33 to 991c622 Compare July 27, 2026 10:07
The function reconstructs the CREATE TABLE statement for an ordinary or
partitioned table, followed by the ALTER TABLE / CREATE INDEX /
CREATE RULE / CREATE STATISTICS statements needed to restore its full
definition.  Each statement is returned as a separate row.

Supported per-column features: data type with type modifiers, COLLATE,
STORAGE, COMPRESSION (pglz / lz4), GENERATED ALWAYS AS (expr)
STORED/VIRTUAL, GENERATED ALWAYS|BY DEFAULT AS IDENTITY (with sequence
options), DEFAULT, NOT NULL (including named NOT NULL constraints), and
per-column attoptions emitted as ALTER COLUMN SET (...).

Supported table-level features: UNLOGGED, INHERITS, PARTITION BY (RANGE
/ LIST / HASH parents), PARTITION OF parent FOR VALUES (FROM/TO, WITH
modulus/remainder, DEFAULT), USING table access method, WITH
(reloptions), TABLESPACE, and inline CHECK constraints in the CREATE
TABLE body.

Supported sub-objects (re-using existing deparse helpers from
ruleutils.c): indexes (including partial and functional) via
pg_get_indexdef_ddl; constraints (PRIMARY KEY with WITHOUT OVERLAPS for
temporal keys, UNIQUE with NULLS NOT DISTINCT and INCLUDE columns,
FOREIGN KEY with ON DELETE/UPDATE referential actions and MATCH clause,
NOT ENFORCED foreign keys, EXCLUDE, named NOT NULL) via
pg_get_constraintdef_body; rules via pg_get_ruledef_ddl; extended
statistics via pg_get_statisticsobjdef_ddl; REPLICA IDENTITY
NOTHING/FULL/USING INDEX; ALTER TABLE ENABLE/FORCE ROW LEVEL SECURITY;
and child-local DEFAULT overrides on inheritance/partition children.
DDL for partition children of a partitioned-table parent is appended
after the parent by default.

The function signature follows the named-parameter convention
established by pg_get_role_ddl(), pg_get_tablespace_ddl(), and
pg_get_database_ddl():

  pg_get_table_ddl(relation         regclass,
                   pretty           boolean  DEFAULT false,
                   owner            boolean  DEFAULT true,
                   tablespace       boolean  DEFAULT true,
                   schema_qualified boolean  DEFAULT true,
                   only_kinds       text[]   DEFAULT NULL,
                   except_kinds     text[]   DEFAULT NULL)

pretty controls pretty-printed output.  owner controls emission of the
ALTER TABLE ... OWNER TO statement.  tablespace controls the TABLESPACE
clause on CREATE TABLE.  schema_qualified controls whether object names
are emitted with their schema prefix: when true (the default) the
active search_path is temporarily narrowed to pg_catalog so every
deparse helper produces fully-qualified names; when false it is narrowed
to the target table's own schema so same-schema references come out
unqualified while cross-schema references remain qualified for
correctness.  Temporary tables are never schema-qualified regardless of
this setting: the TEMPORARY keyword already places them in pg_temp, and
emitting pg_temp_NN.relname would produce non-replayable DDL.

Object-class filtering uses two mutually-exclusive text-array
parameters, only_kinds and except_kinds.  When only_kinds is set, only
the listed kinds are emitted; when except_kinds is set, every kind
except the listed ones is emitted; when neither is set every kind is
emitted.  Each array element is matched case-insensitively with
leading/trailing whitespace trimmed; an unrecognized name raises an
error.  The kind vocabulary is:

  table, index, primary_key, unique, check, foreign_key, exclusion,
  rule, statistics, rls, replica_identity, partition

trigger and policy are accepted in the vocabulary but currently produce
no output; they are reserved for when standalone pg_get_trigger_ddl()
and pg_get_policy_ddl() helpers become available.

NOT NULL is not part of the vocabulary: it is always emitted to prevent
producing schemas that silently accept NULLs the source would reject.
When replica_identity is in the active filter and the table's REPLICA
IDENTITY USING INDEX references an index that the filter would suppress,
the function raises an error before emitting any output, so the
generated DDL never references an index it did not produce.

Default omission convention: every optional clause is omitted when its
value matches what the server would reapply on round-trip, including
type-default COLLATE, per-type STORAGE, the auto-generated identity
sequence name and parameter defaults, heap access method, default
REPLICA IDENTITY, disabled RLS toggles, empty reloptions, and the
default tablespace.

Author: Akshay Joshi <akshay.joshi@enterprisedb.com>
Reviewed-by: Marcos Pegoraro <marcos@f10.com.br>
Reviewed-by: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Rui Zhao <zhaorui126@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant