From 613bdb92636237e1f0fc7bd30c109d7ba3c83cf4 Mon Sep 17 00:00:00 2001 From: Ian Barwick Date: Fri, 17 Jul 2026 11:18:40 +0900 Subject: [PATCH v1] Add pg_get_event_trigger_ddl() function Add a new SQL-callable function that returns the DDL statement needed to recreate an event trigger. It takes a name argument and an optional boolean argument for formatted output. --- doc/src/sgml/func/func-info.sgml | 18 ++++ src/backend/utils/adt/ddlutils.c | 105 ++++++++++++++++++++ src/include/catalog/pg_proc.dat | 5 + src/test/regress/expected/event_trigger.out | 35 +++++++ src/test/regress/sql/event_trigger.sql | 13 +++ 5 files changed, 176 insertions(+) diff --git a/doc/src/sgml/func/func-info.sgml b/doc/src/sgml/func/func-info.sgml index 69ef3857cfa..285a4b8c9c1 100644 --- a/doc/src/sgml/func/func-info.sgml +++ b/doc/src/sgml/func/func-info.sgml @@ -3902,6 +3902,24 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} TABLESPACE clause is omitted. + + + + pg_get_event_trigger_ddl + + pg_get_event_trigger_ddl + ( event_trigger name + , pretty boolean + DEFAULT false ) + text + + + Reconstructs the + CREATE EVENT TRIGGER statement for the specified + event trigger. When pretty is true, the output + is pretty-printed. + + diff --git a/src/backend/utils/adt/ddlutils.c b/src/backend/utils/adt/ddlutils.c index a70f1c28655..db273356e40 100644 --- a/src/backend/utils/adt/ddlutils.c +++ b/src/backend/utils/adt/ddlutils.c @@ -26,6 +26,8 @@ #include "catalog/pg_collation.h" #include "catalog/pg_database.h" #include "catalog/pg_db_role_setting.h" +#include "catalog/pg_event_trigger.h" +#include "catalog/pg_proc.h" #include "catalog/pg_tablespace.h" #include "commands/tablespace.h" #include "common/relpath.h" @@ -975,3 +977,106 @@ pg_get_database_ddl(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); } } + +/* + * pg_get_event_trigger_ddl + * Get the DDL statement for an event trigger + */ +Datum +pg_get_event_trigger_ddl(PG_FUNCTION_ARGS) +{ + Name evtName; + bool pretty; + HeapTuple evtTup; + HeapTuple procTup; + Form_pg_event_trigger evtForm; + char *evtevent; + Datum evttagsDatum; + bool evttagsIsNull; + Form_pg_proc proc; + char *proname; + StringInfoData buf; + + evtName = PG_GETARG_NAME(0); + pretty = PG_GETARG_BOOL(1); + + evtTup = SearchSysCache1(EVENTTRIGGERNAME, NameGetDatum(evtName)); + + if (!HeapTupleIsValid(evtTup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("event trigger \"%s\" does not exist", NameStr(*evtName)))); + + evtForm = (Form_pg_event_trigger) GETSTRUCT(evtTup); + evtevent = NameStr(evtForm->evtevent); + + procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(evtForm->evtfoid)); + + if (!HeapTupleIsValid(procTup)) + elog(ERROR, + "cache lookup failed for function %u", evtForm->evtfoid); + + proc = (Form_pg_proc) GETSTRUCT(procTup); + proname = NameStr(proc->proname); + + initStringInfo(&buf); + + appendStringInfo(&buf, "CREATE EVENT TRIGGER %s", + quote_identifier(NameStr(*evtName))); + append_ddl_option(&buf, pretty, 4, "ON %s", + quote_identifier(evtevent)); + + /* + * Generate WHEN clause, if any filter events were specified. + * + * Note that as of PostgreSQL 20, the only filter_variable supported + * is "TAG", which is not stored in pg_event_trigger, so is hard-coded + * in the generated output. This also means that by definition, the AND + * clause supported by the CREATE EVENT TRIGGER grammar cannot be actually + * be used in a valid statement, and thus will never be part of the + * generated output. + */ + evttagsDatum = SysCacheGetAttr(EVENTTRIGGEROID, evtTup, + Anum_pg_event_trigger_evttags, + &evttagsIsNull); + + if (!evttagsIsNull) + { + ArrayType *arr = DatumGetArrayTypeP(evttagsDatum); + Datum *elems; + int nelems; + int i; + StringInfoData filters; + + initStringInfo(&filters); + + deconstruct_array_builtin(arr, TEXTOID, &elems, NULL, &nelems); + + for (i = 0; i < nelems; ++i) + { + char *str = TextDatumGetCString(elems[i]); + + if (i) + appendStringInfoString(&filters, ", "); + + appendStringInfo(&filters, "'%s'", str); + } + + append_ddl_option(&buf, pretty, 4, "WHEN TAG IN (%s)", + filters.data); + } + + /* + * Finally, add the EXECUTE clause. Note that although the command syntax + * accepts the keyword PROCEDURE for historical reasons, only an actual + * FUNCTION can be referenced, so we don't check the prokind. + */ + append_ddl_option(&buf, pretty, 4, "EXECUTE FUNCTION %s.%s();", + quote_identifier(get_namespace_name(proc->pronamespace)), + quote_identifier(proname)); + + ReleaseSysCache(evtTup); + ReleaseSysCache(procTup); + + PG_RETURN_TEXT_P(cstring_to_text(buf.data)); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 1c55a4dea34..d8c2ea59f87 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8631,6 +8631,11 @@ proargtypes => 'regdatabase bool bool bool', proargnames => '{database,pretty,owner,tablespace}', proargdefaults => '{false,true,true}', prosrc => 'pg_get_database_ddl' }, +{ oid => '9067', descr => 'get CREATE statement for event trigger', + proname => 'pg_get_event_trigger_ddl', proisstrict => 't', + provolatile => 's', prorettype => 'text', + proargtypes => 'name bool', proargnames => '{event_trigger,pretty}', + proargdefaults => '{false}', prosrc => 'pg_get_event_trigger_ddl' }, { oid => '2509', descr => 'deparse an encoded expression with pretty-print option', proname => 'pg_get_expr', provolatile => 's', prorettype => 'text', diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out index 86ae50ce531..4be8628b1ba 100644 --- a/src/test/regress/expected/event_trigger.out +++ b/src/test/regress/expected/event_trigger.out @@ -84,6 +84,41 @@ create event trigger regress_event_trigger2 on ddl_command_start execute procedure test_event_trigger(); -- OK comment on event trigger regress_event_trigger is 'test comment'; +-- these are all OK - checks the DDL output without a tag filter and with one +SELECT pg_get_event_trigger_ddl('regress_event_trigger'); + pg_get_event_trigger_ddl +--------------------------------------------------------------------------------------------------------------- + CREATE EVENT TRIGGER regress_event_trigger ON ddl_command_start EXECUTE FUNCTION public.test_event_trigger(); +(1 row) + +SELECT pg_get_event_trigger_ddl('regress_event_trigger2'); + pg_get_event_trigger_ddl +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start WHEN TAG IN ('CREATE TABLE', 'CREATE FUNCTION') EXECUTE FUNCTION public.test_event_trigger(); +(1 row) + +-- check formatted output +SELECT pg_get_event_trigger_ddl('regress_event_trigger', true); + pg_get_event_trigger_ddl +--------------------------------------------------- + CREATE EVENT TRIGGER regress_event_trigger + + ON ddl_command_start + + EXECUTE FUNCTION public.test_event_trigger(); +(1 row) + +-- will return NULL +SELECT pg_get_event_trigger_ddl(NULL); + pg_get_event_trigger_ddl +-------------------------- + +(1 row) + +-- should fail, no argument +SELECT pg_get_event_trigger_ddl(); +ERROR: function pg_get_event_trigger_ddl() does not exist +LINE 1: SELECT pg_get_event_trigger_ddl(); + ^ +DETAIL: No function of that name accepts the given number of arguments. -- drop as non-superuser should fail create role regress_evt_user; set role regress_evt_user; diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql index d0e6ba295fe..77fc7f662f2 100644 --- a/src/test/regress/sql/event_trigger.sql +++ b/src/test/regress/sql/event_trigger.sql @@ -85,6 +85,19 @@ create event trigger regress_event_trigger2 on ddl_command_start -- OK comment on event trigger regress_event_trigger is 'test comment'; +-- these are all OK - checks the DDL output without a tag filter and with one +SELECT pg_get_event_trigger_ddl('regress_event_trigger'); +SELECT pg_get_event_trigger_ddl('regress_event_trigger2'); + +-- check formatted output +SELECT pg_get_event_trigger_ddl('regress_event_trigger', true); + +-- will return NULL +SELECT pg_get_event_trigger_ddl(NULL); + +-- should fail, no argument +SELECT pg_get_event_trigger_ddl(); + -- drop as non-superuser should fail create role regress_evt_user; set role regress_evt_user; -- 2.52.0