Skip to content

Triggers ​

The Trigger class in Weasel.Firebird.Triggers manages a trigger on a table or a view. Body is the PSQL after AS: a BEGIN … END block, with any DECLARE VARIABLE lines before it.

Defining a Trigger ​

cs
var trigger = new Trigger("trg_orders_stamp", "orders", """
    BEGIN
        NEW.updated_at = CURRENT_TIMESTAMP;
        IF (INSERTING) THEN NEW.created_at = CURRENT_TIMESTAMP;
    END
    """)
{
    Timing = TriggerTiming.Before,
    Events = TriggerEvents.Insert | TriggerEvents.Update
};
// CREATE OR ALTER TRIGGER trg_orders_stamp FOR orders ACTIVE BEFORE INSERT OR UPDATE AS ...

snippet source | anchor

A body that is neither a block nor starts with its declarations is wrapped in BEGIN … END.

Generating DDL ​

StatementWritten as
CreateCREATE OR ALTER TRIGGER name FOR table ACTIVE timing events AS body, inside SET TERM ^ ;
UpdateThe same statement: it changes the table, timing, events and body in place
DropAn EXECUTE BLOCK that runs DROP TRIGGER only while the trigger exists
Racing appliersA CREATE OR ALTER that loses a catalog race runs again, so every applier succeeds

ACTIVE is written out: CREATE OR ALTER otherwise leaves an inactive trigger inactive.

PropertyFirebird
TimingBefore or After. InsteadOf throws NotSupportedException
EventsInsert, Update, Delete, any combination, written INSERT OR UPDATE OR DELETE. Truncate throws
ConditionThrows: Firebird has no WHEN clause. Test the condition in the body
ForEachRowNot emitted: Firebird triggers are row-level
PreserveTargetCaseNames the table exactly, delimited, for a case-preserved (EF Core) table. Copied from PreserveIdentifierCase when the constructor is given a Table

Triggers on Views ​

Firebird has no INSTEAD OF. A BEFORE trigger on a view does the same job:

cs
var trigger = new Trigger("trg_order_notes_insert", "order_notes", """
    BEGIN
        INSERT INTO orders (id, note) VALUES (NEW.id, NEW.note);
    END
    """)
{
    Timing = TriggerTiming.Before,
    Events = TriggerEvents.Insert
};

snippet source | anchor

Delta Detection ​

Firebird keeps the body as source, AS included, and the rest in RDB$TRIGGERS. All of it is compared:

PartChange?
The table or view it fires onYes
TimingYes
EventsYes; their order is not: UPDATE OR INSERT is INSERT OR UPDATE
Switched off with ALTER TRIGGER … INACTIVEYes; the update makes it active again
BodyIgnoring whitespace and case outside literals

Not Modelled ​

Database triggers (ON CONNECT, ON TRANSACTION …) and DDL triggers, as on MySQL and Oracle. POSITION is neither written nor compared.

Teardown ​

Dropping a table drops its triggers. DropSchemaAsync drops the rest, triggers on views included.

Released under the MIT License.