Atlas now manages CockroachDB triggers, functions, and procedures as first-class resources - inspected, diffed, and applied alongside tables and views.
CockroachDB users can now manage triggers, functions, and procedures with Atlas. They are written exactly like their PostgreSQL counterparts, and Atlas takes care of the places where CockroachDB's SQL surface differs.
Triggers
A trigger is declared with the table it fires on, the events it reacts to, and the function it executes:
function "audit_order" {schema = schema.publiclang = PLpgSQLreturn = triggeras = <<-SQLBEGINRETURN new;END;SQL}trigger "audit_orders" {on = table.ordersbefore {insert = true}for = ROWwhen = "((new).amount > 100)"execute {function = function.audit_orderargs = ["orders"]}}
Atlas plans:
-- create "audit_order" function:CREATE FUNCTION "public"."audit_order" () RETURNS trigger LANGUAGE plpgsql AS $$BEGINRETURN new;END;$$;-- create trigger "audit_orders":CREATE TRIGGER "audit_orders" BEFORE INSERT ON "public"."orders" FOR EACH ROW WHEN ((new).amount > 100) EXECUTE FUNCTION "public"."audit_order"('orders');
Changing a trigger uses CREATE OR REPLACE TRIGGER from v26.2:
-- modify "audit_orders" trigger:CREATE OR REPLACE TRIGGER "audit_orders" BEFORE INSERT ON "public"."orders" FOR EACH ROW WHEN ((new).amount > 500) EXECUTE FUNCTION "public"."audit_order"('orders');
On earlier versions, Atlas drops and recreates it instead:
-- drop "audit_orders" trigger:DROP TRIGGER "audit_orders" ON "public"."orders";-- create trigger "audit_orders":CREATE TRIGGER "audit_orders" BEFORE INSERT ON "public"."orders" FOR EACH ROW WHEN ((new).amount > 500) EXECUTE FUNCTION "public"."audit_order"('orders');
Trigger inspection requires CockroachDB v25.3 or later. Before that, pg_trigger is empty and Atlas cannot read triggers back.
Functions and Procedures
Functions and procedures are inspected with their arguments, language, body, and security mode:
function "order_fee" {schema = schema.publiclang = SQLarg "amount" {type = numeric}return = numericas = "SELECT amount * 0.03;"security = DEFINER}procedure "archive_orders" {schema = schema.publiclang = SQLas = "DELETE FROM defaultdb.public.orders WHERE amount = 0;"security = DEFINERdepends_on = [table.orders]}
Switching both to SECURITY INVOKER shows two of CockroachDB's quirks at once. It implements ALTER FUNCTION ... SECURITY but not the procedure equivalent, so Atlas replaces the procedure - and writes the clause explicitly, since older versions keep the previous value when a routine is replaced without one:
-- modify "order_fee" function attribute:ALTER FUNCTION "public"."order_fee" SECURITY INVOKER;-- modify "archive_orders" procedure:CREATE OR REPLACE PROCEDURE "public"."archive_orders" () LANGUAGE sql SECURITY INVOKER AS $$ DELETE FROM defaultdb.public.orders WHERE amount = 0; $$;
Argument defaults are only readable from v26.2. On earlier versions CockroachDB does not report them, so they are not part of the inspected state.
Dependency Ordering
CockroachDB rejects DROP FUNCTION ... CASCADE, so a function cannot be dropped while a trigger still executes it. Atlas orders the statements so the database accepts them. Pointing the trigger at a new function drops the trigger before the function it used to execute:
-- create "audit_order_v2" function:CREATE FUNCTION "public"."audit_order_v2" () RETURNS trigger LANGUAGE plpgsql AS $$BEGINRETURN new;END;$$;-- drop "audit_orders" trigger:DROP TRIGGER "audit_orders" ON "public"."orders";-- create trigger "audit_orders":CREATE TRIGGER "audit_orders" BEFORE INSERT ON "public"."orders" FOR EACH ROW WHEN ((new).amount > 500) EXECUTE FUNCTION "public"."audit_order_v2"('orders');-- drop "audit_order" function:DROP FUNCTION "public"."audit_order";
Getting Started
Inspect an existing cluster to see your triggers, functions, and procedures as code:
atlas schema inspect -u "crdb://user:pass@localhost:26257/defaultdb?sslmode=disable"
The CockroachDB guide walks through the full setup, from connecting to planning and applying migrations.