FirebirdSQL logo
 VIEWPROCEDURE 

Ausführung von Datenbank-Triggern und Ausnahmebehandlung

Die Trigger CONNECT und DISCONNECT werden in einer eigens dafür erstellten Transaktion ausgeführt.Diese Transaktion verwendet die Standardisolationsstufe, d. h. Snapshot (Parallelität), Schreiben und Warten.Wenn alles gut geht, wird die Transaktion festgeschrieben.Nicht abgefangene Ausnahmen führen zu einem Rollback der Transaktion und

  • bei einem CONNECT-Trigger wird die Verbindung dann unterbrochen und die Ausnahme wird an den Client zurückgegeben

  • Bei einem DISCONNECT-Trigger werden Ausnahmen nicht gemeldet.Die Verbindung ist wie beabsichtigt unterbrochen

TRANSACTION-Trigger werden innerhalb der Transaktion ausgeführt, deren Start, Commit oder Rollback sie hervorruft.Die nach einer nicht abgefangenen Ausnahme ausgeführte Aktion hängt vom Ereignis ab:

  • Bei einem TRANSACTION START-Trigger wird die Ausnahme an den Client gemeldet und die Transaktion wird zurückgesetzt

  • Bei einem TRANSACTION COMMIT Trigger wird die Ausnahme gemeldet, die bisherigen Aktionen des Triggers werden rückgängig gemacht und der Commit wird abgebrochen

  • Bei einem TRANSACTION ROLLBACK-Trigger wird die Ausnahme nicht gemeldet und die Transaktion wie vorgesehen zurückgesetzt.

Fallstricke

Offensichtlich gibt es keine direkte Möglichkeit zu wissen, ob ein DISCONNECT- oder TRANSACTION ROLLBACK-Trigger eine Ausnahme verursacht hat.Daraus folgt auch, dass die Verbindung zur Datenbank nicht zustande kommen kann, wenn ein CONNECT-Trigger eine Ausnahme auslöst und eine Transaktion auch nicht starten kann, wenn ein TRANSACTION START-Trigger eine auslöst.Beide Phänomene sperren Sie effektiv aus Ihrer Datenbank, bis Sie mit unterdrückten Datenbank-Triggern dort hineinkommen und den fehlerhaften Code beheben.

docnext count = 19

Unterdrücken von Datenbank-Triggern

Einige Firebird-Befehlszeilentools wurden mit Schaltern geliefert, mit denen ein Administrator das automatische Auslösen von Datenbank-Triggern unterdrücken kann.Bisher sind das:

gbak -nodbtriggers
isql -nodbtriggers
nbackup -T
Zweiphasen-Commit

In einem zweiphasigen Commit-Szenario löst TRANSACTION COMMIT das Auslösen in der Vorbereitungsphase aus, nicht beim Commit.

Einige Vorbehalte
  1. Die Verwendung der Anweisung IN AUTONOMOUS TRANSACTION DO in den Datenbankereignis-Triggern in Bezug auf Transaktionen (TRANSACTION START, TRANSACTION ROLLBACK, TRANSACTION COMMIT) kann dazu führen, dass die autonome Transaktion in eine Endlosschleife gerät

  2. Die Ereignistrigger DISCONNECT und TRANSACTION ROLLBACK werden nicht ausgeführt, wenn Clients über Monitoring-Tabellen getrennt werden (DELETE FROM MON$ATTACHMENTS)

Nur der Datenbankbesitzer und Administratoren haben die Berechtigung, Datenbank-Trigger zu erstellen.

Beispiele für CREATE TRIGGER für „Datenbank-Trigger

  1. Erstellen eines Triggers für das Ereignis der Verbindung mit der Datenbank, der die Anmeldung von Benutzern am System protokolliert.Der Trigger wird als inaktiv angelegt.

    CREATE TRIGGER tr_log_connect
    INACTIVE ON CONNECT POSITION 0
    AS
    BEGIN
      INSERT INTO LOG_CONNECT (ID,
                               USERNAME,
                               ATIME)
      VALUES (NEXT VALUE FOR SEQ_LOG_CONNECT,
              CURRENT_USER,
              CURRENT_TIMESTAMP);
    END
  2. Erstellen eines Auslösers für das Ereignis einer Verbindung mit der Datenbank, der es keinem Benutzer außer SYSDBA erlaubt, sich außerhalb der Geschäftszeiten anzumelden.

    CREATE EXCEPTION E_INCORRECT_WORKTIME 'The working day has not started yet.';
    
    CREATE TRIGGER TR_LIMIT_WORKTIME ACTIVE
    ON CONNECT POSITION 1
    AS
    BEGIN
      IF ((CURRENT_USER <> 'SYSDBA') AND
          NOT (CURRENT_TIME BETWEEN time '9:00' AND time '17:00')) THEN
        EXCEPTION E_INCORRECT_WORKTIME;
    END

DDL-Trigger

DDL-Trigger ermöglichen Einschränkungen für Benutzer, die versuchen, ein DDL-Objekt zu erstellen, zu ändern oder zu löschen.Ihr anderer Zweck besteht darin, ein Metadatenänderungsprotokoll zu führen.

DDL-Trigger lösen bei bestimmten Metadatenänderungsereignissen in einer bestimmten Phase aus.BEFORE-Trigger werden vor Änderungen an Systemtabellen ausgeführt.AFTER-Trigger werden nach Änderungen in Systemtabellen ausgeführt.

Important

Der Ereignistyp [BEFORE | AFTER] eines DDL-Triggers kann nicht geändert werden.

In gewisser Weise sind DDL-Trigger ein Untertyp von Datenbank-Triggern.

Wer kann einen DDL-Trigger erstellen?

DDL-Trigger können erstellt werden durch:

Unterdrücken von DDL-Triggern

Ein DDL-Trigger ist eine Art Datenbank-Trigger.Siehe auch [fblangref40-ddl-trgr-dbtrigger-notrgr-de] wie man Datenbank- und DDL-Trigger unterdrückt.

Beispiele für DDL-Trigger

  1. So können Sie einen DDL-Trigger verwenden, um ein konsistentes Benennungsschema zu erzwingen. In diesem Fall sollten die Namen der gespeicherten Prozeduren mit dem Präfix “SP_” beginnen:

    set auto on;
    create exception e_invalid_sp_name 'Invalid SP name (should start with SP_)';
    
    set term !;
    
    create trigger trig_ddl_sp before CREATE PROCEDURE
    as
    begin
      if (rdb$get_context('DDL_TRIGGER', 'OBJECT_NAME') not starting 'SP_') then
        exception e_invalid_sp_name;
    end!

    Test

    create procedure sp_test
    as
    begin
    end!
    
    create procedure test
    as
    begin
    end!
    
    -- Der letzte Befehl löst diese Ausnahme aus und die Prozedur TEST wird nicht erstellt:
    -- Statement failed, SQLSTATE = 42000
    -- exception 1
    -- -E_INVALID_SP_NAME
    -- -Invalid SP name (should start with SP_)
    -- -At trigger 'TRIG_DDL_SP' line: 4, col: 5
    
    set term ;!
  2. Implementieren Sie benutzerdefinierte DDL-Sicherheit, indem Sie in diesem Fall die Ausführung von DDL-Befehlen auf bestimmte Benutzer beschränken:

    create exception e_access_denied 'Access denied';
    
    set term !;
    
    create trigger trig_ddl before any ddl statement
    as
    begin
      if (current_user <> 'SUPER_USER') then
        exception e_access_denied;
    end!

    Test

    create procedure sp_test
    as
    begin
    end!
    
    -- Der letzte Befehl löst diese Ausnahme aus und die Prozedur SP_TEST wird nicht erstellt
    -- Statement failed, SQLSTATE = 42000
    -- exception 1
    -- -E_ACCESS_DENIED
    -- -Access denied
    -- -At trigger 'TRIG_DDL' line: 4, col: 5
    
    set term ;!
    Note

    Firebird hat Berechtigungen zum Ausführen von DDL-Anweisungen, daher sollte das Schreiben eines DDL-Triggers dafür der letzte Ausweg sein, wenn der gleiche Effekt nicht mit Berechtigungen erzielt werden kann.

  3. Verwenden eines Triggers, um DDL-Aktionen und -Versuche zu protokollieren:

    create sequence ddl_seq;
    
    create table ddl_log (
      id bigint not null primary key,
      moment timestamp not null,
      user_name varchar(63) not null,
      event_type varchar(25) not null,
      object_type varchar(25) not null,
      ddl_event varchar(25) not null,
      object_name varchar(63) not null,
      sql_text blob sub_type text not null,
      ok char(1) not null
    );
    
    set term !;
    
    create trigger trig_ddl_log_before before any ddl statement
    as
      declare id type of column ddl_log.id;
    begin
      -- Wir nehmen die Änderungen in einer AUTONOMEN TRANSAKTION vor. Wenn also eine Ausnahme auftritt und
      -- der Befehl nicht ausgeführt wurde, bleibt das Protokoll erhalten.
      in autonomous transaction do
      begin
        insert into ddl_log (id, moment, user_name, event_type, object_type,
                             ddl_event, object_name, sql_text, ok)
          values (next value for ddl_seq, current_timestamp, current_user,
                  rdb$get_context('DDL_TRIGGER', 'EVENT_TYPE'),
                  rdb$get_context('DDL_TRIGGER', 'OBJECT_TYPE'),
                  rdb$get_context('DDL_TRIGGER', 'DDL_EVENT'),
                  rdb$get_context('DDL_TRIGGER', 'OBJECT_NAME'),
                  rdb$get_context('DDL_TRIGGER', 'SQL_TEXT'),
                  'N')
          returning id into id;
        rdb$set_context('USER_SESSION', 'trig_ddl_log_id', id);
      end
    end!

    Der obige Trigger wird für diesen DDL-Befehl ausgelöst.Es ist eine gute Idee, -nodbtriggers zu verwenden, wenn Sie mit ihnen arbeiten!

    create trigger trig_ddl_log_after after any ddl statement
    as
    begin
      -- Hier benötigen wir eine AUTONOME TRANSACTION, da die ursprüngliche Transaktion den Datensatz
      -- nicht sehen wird, der in den BEFORE-Trigger der
      -- autonomen Transaktion eingefügt wurde, wenn die Benutzertransaktion nicht READ COMMITTED ist.
      in autonomous transaction do
         update ddl_log set ok = 'Y'
         where id = rdb$get_context('USER_SESSION', 'trig_ddl_log_id');
    end!
    
    commit!
    
    set term ;!
    
    -- Löschen Sie den Datensatz über trig_ddl_log_after
    delete from ddl_log;
    commit;

    Test

    -- Dies wird einmalig protokolliert
    -- (da T1 nicht existierte, fungiert RECREATE als CREATE) mit OK = Y.
    recreate table t1 (
      n1 integer,
      n2 integer
    );
    
    -- Dies schlägt fehl, da T1 bereits existiert, also ist OK N.
    create table t1 (
      n1 integer,
      n2 integer
    );
    
    -- T2 existiert nicht. Es wird kein Protokoll geben.
    drop table t2;
    
    -- Dies wird zweimal protokolliert
    -- (da T1 existiert, fungiert RECREATE als DROP und CREATE) mit OK = Y.
    recreate table t1 (
      n integer
    );
    
    commit;
    select id, ddl_event, object_name, sql_text, ok
      from ddl_log order by id;
    
     ID DDL_EVENT                 OBJECT_NAME                      SQL_TEXT OK
    === ========================= ======================= ================= ======
      2 CREATE TABLE              T1                                   80:3 Y
    ====================================================
    SQL_TEXT:
    recreate table t1 (
        n1 integer,
        n2 integer
    )
    ====================================================
      3 CREATE TABLE              T1                                   80:2 N
    ====================================================
    SQL_TEXT:
    create table t1 (
        n1 integer,
        n2 integer
    )
    ====================================================
      4 DROP TABLE                T1                                   80:6 Y
    ====================================================
    SQL_TEXT:
    recreate table t1 (
        n integer
    )
    ====================================================
      5 CREATE TABLE              T1                                   80:9 Y
    ====================================================
    SQL_TEXT:
    recreate table t1 (
        n integer
    )
    ====================================================

ALTER TRIGGER

Verwendet für

Ändern und Deaktivieren eines bestehenden Triggers

Verfügbar in

DSQL, ESQL

Syntax
ALTER TRIGGER trigname
  [ACTIVE | INACTIVE]
  [{BEFORE | AFTER} <mutation_list>]
  [POSITION number]
  {<psql_trigger> | <external-module-body>}

<psql_trigger> ::=
  [<sql_security>]
  [<psql-module-body>]

<sql_security> ::=
    SQL SECURITY {INVOKER | DEFINER}
  | DROP SQL SECURITY

!! Vgl. auch die Syntax CREATE TRIGGER für weitere Regeln!!

Die ALTER TRIGGER-Anweisung erlaubt nur bestimmte Änderungen am Header und Body eines Triggers.

Zulässige Änderungen an Triggern

  • Status (ACTIVE | INACTIVE)

  • Phase (BEFORE | AFTER) (bei DML-Triggern)

  • Ereignisse (bei DML-Triggern)

  • Position in der Ausführungsfolge

  • Änderungen am Code im Trigger-Body

Wenn ein Element nicht angegeben wird, bleibt es unverändert.

Note

Ein DML-Trigger kann nicht in einen Datenbank- (oder DDL-)Trigger geändert werden.

Es ist nicht möglich, das/die Ereignis(e) oder die Phase eines Datenbank- (oder DDL-)Triggers zu ändern.

Note
Merken Sie sich

Das Schlüsselwort BEFORE weist an, dass der Trigger ausgeführt wird, bevor das zugehörige Ereignis eintritt;das Schlüsselwort AFTER weist an, dass es nach dem Ereignis ausgeführt wird.

Mehrere DML-Ereignisse – INSERT, UPDATE, DELETE – können in einem einzigen Trigger abgedeckt werden.Die Ereignisse sollten mit dem Schlüsselwort OR getrennt werden.Kein Ereignis sollte mehr als einmal erwähnt werden.

Das Schlüsselwort POSITION ermöglicht die Angabe einer optionalen Ausführungsreihenfolge (“firing order”) für eine Reihe von Triggern, die die gleiche Phase und das gleiche Ereignis wie ihr Ziel haben.Die Standardposition ist 0.Wenn keine Positionen angegeben sind oder mehrere Trigger eine einzige Positionsnummer haben, werden die Trigger in alphabetischer Reihenfolge ihrer Namen ausgeführt.====

Wer kann einen Trigger ändern?

DML-Trigger können geändert werden durch:

  • Administratoren

  • Der Besitzer der Tabelle (oder Ansicht)

  • Benutzer mit dem ALTER ANY TABLE- oder — für eine Ansicht — ALTER ANY VIEW-Privileg

Datenbank- und DDL-Trigger können geändert werden durch:

Beispiele mit ALTER TRIGGER

  1. Den Trigger set_cust_no deaktivieren (in den inaktiven Zustand schalten).

    ALTER TRIGGER set_cust_no INACTIVE;
  2. Ändern der Position der Zündreihenfolge des Triggers set_cust_no.

    ALTER TRIGGER set_cust_no POSITION 14;
  3. Den Trigger TR_CUST_LOG in den inaktiven Zustand schalten und die Ereignisliste ändern.

    ALTER TRIGGER TR_CUST_LOG
    INACTIVE AFTER INSERT OR UPDATE;
  4. Den tr_log_connect Trigger in den aktiven Status schalten, seine Position und seinen Körper ändern.

    ALTER TRIGGER tr_log_connect
    ACTIVE POSITION 1
    AS
    BEGIN
      INSERT INTO LOG_CONNECT (ID,
                               USERNAME,
                               ROLENAME,
                               ATIME)
      VALUES (NEXT VALUE FOR SEQ_LOG_CONNECT,
              CURRENT_USER,
              CURRENT_ROLE,
              CURRENT_TIMESTAMP);
    END

CREATE OR ALTER TRIGGER

Verwendet für

Erstellen eines neuen Triggers oder Ändern eines bestehenden Triggers

Verfügbar in

DSQL

Syntax
CREATE OR ALTER TRIGGER trigname
  { <relation_trigger_legacy>
  | <relation_trigger_sql2003>
  | <database_trigger>
  | <ddl_trigger> }
  {<psql_trigger> | <external-module-body>}

!!Vgl. auch die Syntax CREATE TRIGGER für weitere Regeln !!

Die Anweisung CREATE OR ALTER TRIGGER erstellt einen neuen Trigger, falls dieser nicht existiert;andernfalls ändert und kompiliert es sie mit den intakten Privilegien und unberührten Abhängigkeiten.

Beispiel für CREATE OR ALTER TRIGGER

Neuen Trigger erstellen, wenn er nicht existiert, oder ihn ändern, falls vorhanden
CREATE OR ALTER TRIGGER set_cust_no
ACTIVE BEFORE INSERT ON customer POSITION 0
AS
BEGIN
  IF (NEW.cust_no IS NULL) THEN
    NEW.cust_no = GEN_ID(cust_no_gen, 1);
END

DROP TRIGGER

Verwendet für

Löschen eines vorhandenen Triggers

Verfügbar in

DSQL, ESQL

Syntax
DROP TRIGGER trigname
Table 1. DROP TRIGGER-Anweisgungsparameter
Parameter Beschreibung

trigname

Triggername

Die Anweisung DROP TRIGGER verwirft (löscht) einen vorhandenen Trigger.

Wer kann einen Trigger fallen lassen?

DML-Trigger können gelöscht werden durch:

  • Administratoren

  • Der Besitzer der Tabelle (oder Ansicht)

  • Benutzer mit dem ALTER ANY TABLE- oder — für eine Ansicht — ALTER ANY VIEW-Privileg

Datenbank- und DDL-Trigger können gelöscht werden durch:

Beispiel für DROP TRIGGER

Löschen des Triggers set_cust_no
DROP TRIGGER set_cust_no;

RECREATE TRIGGER

Verwendet für

Erstellen eines neuen Triggers oder Neuerstellen eines vorhandenen Triggers

Verfügbar in

DSQL

Syntax
RECREATE TRIGGER trigname
  { <relation_trigger_legacy>
  | <relation_trigger_sql2003>
  | <database_trigger>
  | <ddl_trigger> }
  {<psql_trigger> | <external-module-body>}

!! Vgl. auch die Syntax CREATE TRIGGER für weitere Regeln !!

Die Anweisung RECREATE TRIGGER erstellt einen neuen Trigger, wenn kein Trigger mit dem angegebenen Namen existiert;andernfalls versucht die Anweisung RECREATE TRIGGER, den vorhandenen Trigger zu löschen und einen neuen zu erstellen.Die Operation schlägt bei COMMIT fehl, wenn der Trigger verwendet wird.

Warning

Beachten Sie, dass Abhängigkeitsfehler erst in der COMMIT-Phase dieser Operation erkannt werden.

Beispiel für RECREATE TRIGGER

Erstellen oder erneutes Erstellen des Triggers set_cust_no.

RECREATE TRIGGER set_cust_no
ACTIVE BEFORE INSERT ON customer POSITION 0
AS
BEGIN
  IF (NEW.cust_no IS NULL) THEN
    NEW.cust_no = GEN_ID(cust_no_gen, 1);
END