Native PostgreSQL logical replication syncs table data only. To change a table definition on the publisher — for example, adding a column — you must apply the same change manually on every subscriber before data replication can continue. Schema drift causes replication errors.
PolarDB for PostgreSQL extends logical replication to replicate data definition language (DDL) statements. When you enable DDL replication, schema changes on the publisher are captured and applied on subscribers alongside data changes, keeping both sides in sync automatically — no manual schema synchronization required.
Quick start
-- On the publisher: create a publication that replicates all DDL
CREATE PUBLICATION mypub FOR ALL TABLES WITH (pubddl = 'all');
-- On the subscriber: create a subscription
CREATE SUBSCRIPTION mysub
CONNECTION '<publisher_connection_string>'
PUBLICATION mypub;Replace <publisher_connection_string> with the actual connection string of your publisher cluster, for example: host=<host> port=5432 dbname=<dbname> user=<user> password=<password>.
Prerequisites
Before you begin, ensure that you have:
wal_levelset tological. For instructions, see Configure cluster parameters.polar_enable_ddl_replicationset totrue(the default).
How it works
Create a publication with
pubddlset totableorallto enable DDL replication.Create a subscription on the subscriber node. Optionally set
dump_schema = trueto copy existing object definitions from the publisher at subscription creation time.Execute DDL statements on the publisher. PolarDB captures them in the write-ahead log (WAL) and forwards them to subscribers.
Verify that the schema changes appear on the subscriber.
Configure DDL replication
CREATE PUBLICATION — pubddl parameter
PolarDB adds the pubddl parameter to publication_parameter in CREATE PUBLICATION:
CREATE PUBLICATION name
[ FOR TABLE [ ONLY ] table_name [ * ] [, ...]
| FOR ALL TABLES ]
[ WITH ( publication_parameter [= value] [, ... ] ) ]
publication_parameter:
...
pubddl = 'none | table | all'| Value | DDL statements replicated |
|---|---|
none (default) | None — DDL replication disabled |
table | Table-related DDL only: CREATE TABLE, ALTER TABLE, DROP TABLE, CREATE TABLE AS |
all | All supported DDL statements (see list below) |
Supported statements when `pubddl = 'all'`:
ALTER INDEX,ALTER SEQUENCE,ALTER TABLE,ALTER TYPECREATE INDEX,CREATE SCHEMA,CREATE SEQUENCE,CREATE TABLE,CREATE TABLE AS,CREATE TYPE,CREATE TYPE HEADER,CREATE TYPE BODYDROP INDEX,DROP SCHEMA,DROP SEQUENCE,DROP TABLE,DROP TYPE
Whenpubddl = 'all', the publication must useFOR ALL TABLES. Global statements —ROLE,DATABASE,TABLESPACE, andGRANT/REVOKEon global objects — can execute on all databases but are not replicated.
CREATE SUBSCRIPTION — dump_schema parameter
PolarDB adds the dump_schema parameter to subscription_parameter in CREATE SUBSCRIPTION:
CREATE SUBSCRIPTION subscription_name
CONNECTION 'conninfo'
PUBLICATION publication_name [, ...]
[ WITH ( subscription_parameter [= value] [, ... ] ) ]
subscription_parameter:
...
dump_schema = false | true| Value | Behavior |
|---|---|
false (default) | Does not copy existing object definitions from the publisher when creating the subscription |
true | Copies existing object definitions from the publisher when creating the subscription |
dump_schema = trueusespg_dumporpg_restore. The cluster must accept connections onhost='127.0.0.1'; otherwise the restore fails. Dumped files are stored in thepg_logical/schemadumpsdirectory and are deleted after the restore completes or encounters an error.
Parameters
| Parameter | Default | Valid values | Description |
|---|---|---|---|
polar_enable_ddl_replication | true | true, false | Enables or disables DDL logical replication |
polar_enable_debug_ddl_replication | false | true, false | Enables debug logging for DDL replication |
Limitations
When
pubddl = 'all', the publication must useFOR ALL TABLES. Table-specific publications are not supported withpubddl = 'all'.Global statements (
ROLE,DATABASE,TABLESPACE,GRANT, andREVOKEon global objects) are not replicated. They execute locally on all databases.dump_schema = truerequires the cluster to accept connections onhost='127.0.0.1'. If this connection is blocked, the restore fails.
Example: end-to-end DDL replication
This example shows how DDL changes — including ALTER TABLE and DROP TABLE — propagate from publisher to subscriber.
Step 1: Create a publication that replicates all DDL.
CREATE PUBLICATION mypub FOR ALL TABLES WITH (pubddl = 'all');Expected output:
CREATE PUBLICATIONStep 2: Create a subscription.
CREATE SUBSCRIPTION mysub
CONNECTION '$publisher_connstr'
PUBLICATION mypub;Expected output:
NOTICE: created replication slot "mysub" on publisher
CREATE SUBSCRIPTIONStep 3: Run DDL and DML on the publisher.
-- Create a table
CREATE TABLE t1 (id int, val char(3));
-- Insert rows
INSERT INTO t1 VALUES (1, 'a');
INSERT INTO t1 VALUES (2, 'b');
INSERT INTO t1 VALUES (3, 'c');
-- Add a generated column
ALTER TABLE t1
ADD COLUMN c int GENERATED BY DEFAULT AS IDENTITY,
ALTER COLUMN c SET GENERATED ALWAYS;
-- Verify on the publisher
SELECT * FROM t1;Expected output:
id | val | c
----+-----+---
1 | a | 1
2 | b | 2
3 | c | 3
(3 rows)Step 4: Verify replication on the subscriber.
SELECT * FROM t1;Expected output — data and schema match the publisher:
id | val | c
----+-----+---
1 | a | 1
2 | b | 2
3 | c | 3
(3 rows)Step 5: Drop the table on the publisher.
DROP TABLE t1;Step 6: Verify the drop propagated to the subscriber.
SELECT * FROM t1;Expected output:
ERROR: relation "t1" does not exist
LINE 1: SELECT * FROM t1;The DROP TABLE was replicated. The table no longer exists on the subscriber.
Decoding extension
PolarDB adds two callback interfaces to OutputPluginCallbacks for logical decoding plugins:
typedef struct OutputPluginCallbacks
{
...
LogicalDecodeDDLMessageCB ddl_cb;
...
LogicalDecodeStreamDDLMessageCB stream_ddl_cb;
...
} OutputPluginCallbacks;
/* Callback for committed DDL messages */
typedef void (*LogicalDecodeDDLMessageCB) (
struct LogicalDecodingContext *ctx,
ReorderBufferTXN *txn,
XLogRecPtr message_lsn,
const char *prefix,
Oid relid,
DeparsedCommandType cmdtype,
Size message_size,
const char *message
);
/* Callback for in-progress transaction DDL messages (streaming) */
typedef void (*LogicalDecodeStreamDDLMessageCB) (
struct LogicalDecodingContext *ctx,
ReorderBufferTXN *txn,
XLogRecPtr message_lsn,
const char *prefix,
Oid relid,
DeparsedCommandType cmdtype,
Size message_size,
const char *message
);The DDL message method is implemented in the test_decoding extension. The following example creates a publication, creates a replication slot, creates a table, and reads the decoded DDL output:
CREATE PUBLICATION mypub FOR ALL TABLES WITH (pubddl = 'all');
SELECT * FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding', false, true);
CREATE TABLE t3 (id int);
SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL);The output includes a message field containing the DDL in JSON format:
lsn | xid | data
------------+-----+--------------------------------------------
0/C001BF10 | 783 | BEGIN 783
0/C001EBC0 | 783 | message: prefix: deparse, relid: 16418, cmdtype: Simple, sz: 1505 content:{"fmt": "CREATE %{persistence}s TABLE ..."}
0/C001EE98 | 783 | COMMIT 783To expand a DDL JSON message into a SQL string, use polar_catalog.ddl_deparse_expand_command:
SELECT polar_catalog.ddl_deparse_expand_command('<json_string>');Expected output:
ddl_deparse_expand_command
---------------------------------------------------------
CREATE TABLE public.t3 (id pg_catalog.int4 STORAGE plain )
(1 row)System functions and tables
polar_catalog.ddl_deparse_to_json
Parses a DDL parsetree into a JSON string.
Syntax: ddl_deparse_to_json(IN pg_ddl_command) RETURN text
| Parameter | Type | Description |
|---|---|---|
| Input | pg_ddl_command (PARSETREE) | Internal DDL parse tree |
| Output | text | JSON string representing the DDL command |
polar_catalog.ddl_deparse_expand_command
Parses a DDL JSON string into a SQL string.
Syntax: ddl_deparse_expand_command(IN text) RETURN text
| Parameter | Type | Description |
|---|---|---|
| Input | text | JSON string from ddl_deparse_to_json |
| Output | text | Reconstructed SQL string |
polar_catalog.polar_publication
A system table that stores DDL replication metadata for publications.
TABLE polar_publication
(
puboid Oid PRIMARY KEY, -- Publication OID
pubddl "char", -- DDL replication scope for the publication
pubglobal "char", -- Global object replication scope (coming soon)
pubflags int -- Reserved flag bits
);What's next
Configure cluster parameters — set
wal_leveland other replication-related parameters