Event Triggers
Automatically execute SQL on database events.
In Postgres, an event trigger is similar to a trigger, except that it is triggered by database level events (and is usually reserved for superusers)
With our Supautils extension (installed automatically for all Supabase projects), the postgres user has the ability to create and manage event triggers.
Some use cases for event triggers are:
- Capturing Data Definition Language (DDL) changes - these are changes to your database schema (though the pgAudit extension provides a more complete solution)
- Enforcing/monitoring/preventing actions - such as preventing tables from being dropped in Production or enforcing RLS on all new tables
The guide covers two example event triggers:
- Preventing accidental dropping of a table
- Automatically enabling Row Level Security on new tables in the
publicschema
Creating an event trigger#
Only the postgres user can create event triggers, so make sure you are authenticated as them. As with triggers, event triggers consist of 2 parts
- A Function which will be executed when the triggering event occurs
- The actual Event Trigger object, with parameters around when the trigger should be run
Example trigger function - prevent dropping tables#
This example protects any table from being dropped. You can override it by temporarily disabling the event trigger: ALTER EVENT TRIGGER dont_drop_trigger DISABLE;
-- FunctionCREATE OR REPLACE FUNCTION dont_drop_function() RETURNS event_trigger LANGUAGE plpgsql AS $$DECLARE obj record; tbl_name text;BEGIN FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() LOOP IF obj.object_type = 'table' THEN RAISE EXCEPTION 'ERROR: All tables in this schema are protected and cannot be dropped'; END IF; END LOOP;END;$$;-- Event triggerCREATE EVENT TRIGGER dont_drop_triggerON sql_dropEXECUTE FUNCTION dont_drop_function();Example trigger function - auto enable Row Level Security#
If you want Row Level Security enabled automatically for new tables, create an event trigger that runs after table creation and calls ALTER TABLE ... ENABLE ROW LEVEL SECURITY on each newly created table.
CREATE OR REPLACE FUNCTION rls_auto_enable()RETURNS EVENT_TRIGGERLANGUAGE plpgsqlSECURITY DEFINERSET search_path = pg_catalogAS $$DECLARE cmd record;BEGIN FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() WHERE command_tag IN ('CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO') AND object_type IN ('table','partitioned table') LOOP IF cmd.schema_name IS NOT NULL AND cmd.schema_name IN ('public') AND cmd.schema_name NOT IN ('pg_catalog','information_schema') AND cmd.schema_name NOT LIKE 'pg_toast%' AND cmd.schema_name NOT LIKE 'pg_temp%' THEN BEGIN EXECUTE format('alter table if exists %s enable row level security', cmd.object_identity); RAISE LOG 'rls_auto_enable: enabled RLS on %', cmd.object_identity; EXCEPTION WHEN OTHERS THEN RAISE LOG 'rls_auto_enable: failed to enable RLS on %', cmd.object_identity; RAISE; END; ELSE RAISE LOG 'rls_auto_enable: skip % (either system schema or not in enforced list: %.)', cmd.object_identity, cmd.schema_name; END IF; END LOOP;END;$$;DROP EVENT TRIGGER IF EXISTS ensure_rls;CREATE EVENT TRIGGER ensure_rlsON ddl_command_endWHEN TAG IN ('CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO')EXECUTE FUNCTION rls_auto_enable();Note that this applies to tables created after the trigger is installed. Existing tables still need RLS enabled manually.
Event trigger Functions and firing events#
Event triggers can be triggered on:
ddl_command_start- occurs before a DDL command for almost all objects within a schemaddl_command_end- occurs after a DDL command for almost all objects within a schemasql_drop- occurs beforeddl_command_endfor any DDL commands thatDROPa database object (note that altering a table can cause it to be dropped)table_rewrite- occurs before a table is rewritten using theALTER TABLEcommand
Event triggers run for each DDL command specified above and can consume resources which may cause performance issues if not used carefully.
Within each event trigger, helper functions exist to view the objects being modified or the command being run. For example, our example calls pg_event_trigger_dropped_objects() to view the object(s) being dropped. For a more comprehensive overview of these functions, read the official event trigger definition documentation
To view the matrix commands that cause an event trigger to fire, read the official event trigger matrix documentation
Disabling an event trigger#
You can disable an event trigger using the alter event trigger command:
ALTER EVENT TRIGGER dont_drop_trigger DISABLE;Dropping an event trigger#
You can delete a trigger using the drop event trigger command:
DROP EVENT TRIGGER dont_drop_trigger;Resources#
- Official Postgres Docs: Event Trigger Behaviours
- Official Postgres Docs: Event Trigger Firing Matrix
- Supabase blog: Postgres Event Triggers without superuser access