Redrock Postgres Documentation
Home Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Subscribe DDL Commands

PostgreSQL provides event triggers. Event triggers are global to a particular database and are capable of capturing DDL events occured in the database. Redrock Postgres makes the following improvements to PostgreSQL so that users can subscribe DDL commands with event triggers:

  • In Redrock Postgres, roles and tablespaces are database-level objects. Event triggers also support DDL commands for role and tablespace objects. For details, see Event Trigger Firing Matrix.
  • Support capturing events of dropping objects at the end of a command (corresponding to the ddl_command_end event).
  • Add a function pg_ddl_command_deparse to deparse a command from internal format of type pg_ddl_command, and returns a SQL statement in textual representation of type text.

The following example shows how to subscribe and replicate DDL commands occured on the publisher side. You can modify the commands in the example according to the actual situation.

Record and Publish DDL Commands

We need to record DDL commands in a log table with event trigger on the publisher side, and publish the log table. Before proceeding, make sure the value of the configuration parameter wal_level on the publisher side is logical, and the database server needs to be restarted to take effect after the parameter is modified. Then, you can login to the database using a superuser or database owner and do the following:

Create DDL history table

Creating a log table to record DDL commands:

CREATE SCHEMA IF NOT EXISTS audit;
REVOKE ALL ON SCHEMA audit FROM public;

-- create table for ddl record
CREATE TABLE IF NOT EXISTS audit.ddl_history(
  ord             int,
  event_time      timestamp,
  username        text,
  object_type     text,
  schema_name     text,
  object_identity text,
  command_tag     text,
  command         text
);

-- grant privileges to all user
GRANT INSERT ON TABLE audit.ddl_history TO PUBLIC;

The table audit.ddl_history includes the following columns:

Name Description
ord The number of DDL subcommand decomposed from the current DDL query
event_time The time when the DDL command was executed
username The user who executed the DDL command
object_type Type of the object. Possible types are table, index, sequence, view, materialized view, foreign table, aggregate, function, type, cast, collation, rule, trigger, schema, role, tablespace, foreign data wrapper, server, user mapping, extension, policy, publication and subscription.
schema_name Name of the schema the object belongs in, if any; otherwise NULL. No quoting is applied.
object_identity Text rendering of the object identity, schema-qualified. Each identifier included in the identity is quoted if necessary.
command_tag Command tag. Possible values see Event Trigger Support by Command Tag.
command the DDL command in textual representation

Publish DDL history table

Publish the log table:

CREATE PUBLICATION ddl_publication FOR TABLE ONLY audit.ddl_history;

Create event trigger capturing DDL commands

-- create function for event triggers
CREATE OR REPLACE FUNCTION audit.ddl_pub_trigger_func()
  RETURNS event_trigger AS $$
BEGIN
  INSERT INTO audit.ddl_history
    SELECT ordinality, now(), current_user,
           object_type, schema_name, object_identity,
           command_tag, pg_ddl_command_deparse(command)
      FROM pg_event_trigger_ddl_commands() WITH ORDINALITY;
END;
$$ LANGUAGE plpgsql;

-- create ddl_command_end event trigger
CREATE EVENT TRIGGER ddl_event_trigger
  ON ddl_command_end
  EXECUTE PROCEDURE audit.ddl_pub_trigger_func();
After the preceding commands are executed, your DDL commands will be recorded in the table audit.ddl_history.

Configure the recording and publishing

You can configure the recording and publishing of DDL commands by modifying the DDL event trigger function definition. Referring to the definition of the table audit.ddl_history in above, you can configure the recording and publishing of DDL commands by adding filter conditions to the definition of DDL event trigger functions based on the user who executed the DDL command, the type of object affected, the schema the object belongs in, the object name, and the command tag. For example, we can modify the definition of the above event trigger function audit.ddl_pub_trigger_func to limit the schemas and object types involved in the recorded and published DDL commands as follows:

CREATE OR REPLACE FUNCTION audit.ddl_pub_trigger_func()
  RETURNS event_trigger AS $$
BEGIN
  INSERT INTO audit.ddl_history
    SELECT ordinality, now(), current_user,
           object_type, schema_name, object_identity,
           command_tag, pg_ddl_command_deparse(command)
      FROM pg_event_trigger_ddl_commands() WITH ORDINALITY
      WHERE schema_name IN ('testschema', 'myschema')
        AND object_type IN ('table', 'index');
END;
$$ LANGUAGE plpgsql;

In the example above, we limit the schemas involved in DDL commands to testschema and myschema, and limit the object types to table and index.

Subscribe and Replicate DDL Commands

On the publisher side, we recorded the executed DDL commands in the log table audit.ddl_history, and published the table. Subscribers can replicate and execute the DDL commands in the log table. Login to the database using a superuser or database owner and do the following:

Create the same DDL history table

On the subscriber side, create a DDL history table same as the table on the publisher side:

CREATE SCHEMA IF NOT EXISTS audit;
REVOKE ALL ON SCHEMA audit FROM public;

-- create table for ddl record
CREATE TABLE IF NOT EXISTS audit.ddl_history(
  ord             int,
  event_time      timestamp,
  username        text,
  object_type     text,
  schema_name     text,
  object_identity text,
  command_tag     text,
  command         text
);

-- grant privileges to all user
GRANT INSERT ON TABLE audit.ddl_history TO PUBLIC;

Subscribe DDL history table

Subscribe the log table:

CREATE SUBSCRIPTION ddl_subscriptin CONNECTION
  'host=192.168.1.50 port=5432 dbname=testdb user=testuser password=pgpass'
  PUBLICATION ddl_publication;
You need to modify the access information of the publisher in the above SQL command according to the actual deployment environment.

Replicate DDL commands with table level triggers

Create a trigger for the table audit.ddl_history on the subscriber side to replicate and execute DDL commands.

CREATE OR REPLACE FUNCTION audit.ddl_sub_trigger_func()
  RETURNS trigger AS $$
BEGIN
  EXECUTE NEW.command;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER ddl_sub_trigger
  AFTER INSERT ON audit.ddl_history
  FOR EACH ROW
  EXECUTE PROCEDURE audit.ddl_sub_trigger_func();

Configure the DDL replication

You can configure the replication of DDL commands by modifying the table level trigger function definition. Referring to the definition of the table audit.ddl_history in above, you can configure the replication of DDL commands by adding filter conditions to the definition of DDL event trigger functions based on the user who executed the DDL command, the type of object affected, the schema the object belongs in, the object name, and the command tag. For example, we can modify the definition of the above table level trigger function audit.ddl_sub_trigger_func to limit the schemas and object types involved in the replicated DDL commands as follows:

CREATE OR REPLACE FUNCTION audit.ddl_sub_trigger_func()
  RETURNS trigger AS $$
BEGIN
  IF NEW.schema_name IN ('testschema', 'myschema') AND
     NEW.object_type IN ('table', 'index')
  THEN
    EXECUTE NEW.command;
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

In the example above, we limit the schemas involved in DDL commands to testschema and myschema, and limit the object types to table and index.