Wer kann einen DML-Trigger erstellen?
DML-Trigger können erstellt werden durch:
-
Der Besitzer der Tabelle (oder Ansicht)
-
Benutzer mit dem
ALTER ANY TABLE
- oder — für eine Ansicht —ALTER ANY VIEW
-Privileg
DML-Trigger können erstellt werden durch:
Der Besitzer der Tabelle (oder Ansicht)
Benutzer mit dem ALTER ANY TABLE
- oder — für eine Ansicht — ALTER ANY VIEW
-Privileg
Firebird unterstützt zwei Deklarationsformen für Relations-Trigger:
Die ursprüngliche, veraltete Syntax
Das SQL:2003 standardkonforme Formular (empfohlen)
Das mit SQL:2003 standardkonforme Formular wird empfohlen.
Ein Relationstrigger spezifiziert — unter anderem — eine Phase und ein oder mehrere Ereignisse.
Phase betrifft das Timing des Triggers in Bezug auf das Change-of-State-Ereignis in der Datenzeile:
Ein BEFORE
-Trigger wird ausgelöst, bevor die angegebene Datenbankoperation (insert, update oder delete) ausgeführt wird
Ein 'AFTER'-Trigger wird ausgelöst, nachdem die Datenbankoperation abgeschlossen ist
Eine Relations-Trigger-Definition spezifiziert mindestens eine der DML-Operationen 'INSERT', 'UPDATE' und 'DELETE', um ein oder mehrere Ereignisse anzugeben, bei denen der Trigger ausgelöst werden soll.Werden mehrere Operationen angegeben, müssen diese durch das Schlüsselwort OR
getrennt werden.Keine Operation darf mehr als einmal erfolgen.
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.
CREATE TRIGGER
für Tabellen und AnsichtenErstellen eines Triggers in “legacy”-Form, der ausgelöst wird, bevor ein neuer Datensatz in die Tabelle CUSTOMER
eingefügt wird.
CREATE TRIGGER SET_CUST_NO FOR CUSTOMER
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
IF (NEW.CUST_NO IS NULL) THEN
NEW.CUST_NO = GEN_ID(CUST_NO_GEN, 1);
END
Erstellen einer Triggerauslösung vor dem Einfügen eines neuen Datensatzes in die CUSTOMER
-Tabelle in SQL:2003-Standard-konformer Form.
CREATE 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
Erstellen eines Triggers, der nach dem Einfügen, Aktualisieren oder Löschen eines Datensatzes in der CUSTOMER
-Tabelle ausgelöst wird.
CREATE TRIGGER TR_CUST_LOG
ACTIVE AFTER INSERT OR UPDATE OR DELETE
ON CUSTOMER POSITION 10
AS
BEGIN
INSERT INTO CHANGE_LOG (LOG_ID,
ID_TABLE,
TABLE_NAME,
MUTATION)
VALUES (NEXT VALUE FOR SEQ_CHANGE_LOG,
OLD.CUST_NO,
'CUSTOMER',
CASE
WHEN INSERTING THEN 'INSERT'
WHEN UPDATING THEN 'UPDATE'
WHEN DELETING THEN 'DELETE'
END);
END
DEFINER
für den Trigger tr_ins
gesetzt ist, benötigt der Benutzer US
nur das INSERT
-Privileg auf tr
.Wenn es auf INVOKER
gesetzt wäre, bräuchte entweder der Benutzer oder der Trigger auch das INSERT
-Privileg für die Tabelle t
.
create table tr (i integer);
create table t (i integer);
set term ^;
create trigger tr_ins for tr after insert SQL SECURITY DEFINER
as
begin
insert into t values (NEW.i);
end^
set term ;^
grant insert on table tr to user us;
commit;
connect 'localhost:/tmp/29.fdb' user us password 'pas';
insert into tr values(2);
Das Ergebnis wäre dasselbe, wenn SQL SECURITY DEFINER
für die Tabelle TR
angegeben würde:
create table tr (i integer) SQL SECURITY DEFINER;
create table t (i integer);
set term ^;
create trigger tr_ins for tr after insert
as
begin
insert into t values (NEW.i);
end^
set term ;^
grant insert on table tr to user us;
commit;
connect 'localhost:/tmp/29.fdb' user us password 'pas';
insert into tr values(2);
Trigger können so definiert werden, dass sie bei “Datenbankereignissen” ausgelöst werden, was sich wirklich auf eine Mischung aus Ereignissen bezieht, die über den Umfang einer Sitzung (Verbindung) und Ereignissen, die über den Umfang einer einzelnen Transaktion hinweg wirken:
CONNECT
DISCONNECT
TRANSACTION START
TRANSACTION COMMIT
TRANSACTION ROLLBACK
[fblangref40-ddl-trgr-ddltrigger-de] sind eine Unterart von Datenbank-Triggern, die in einem separaten Abschnitt behandelt werden.
Datenbank-Trigger können erstellt werden durch:
Benutzer mit dem ALTER DATABASE
-Privileg
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.
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.
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
In einem zweiphasigen Commit-Szenario löst TRANSACTION COMMIT
das Auslösen in der Vorbereitungsphase aus, nicht beim Commit.
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
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.
CREATE TRIGGER
für „Datenbank-Trigger
“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
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 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 |
In gewisser Weise sind DDL-Trigger ein Untertyp von Datenbank-Triggern.
DDL-Trigger können erstellt werden durch:
Benutzer mit dem ALTER DATABASE
-Privileg
Ein DDL-Trigger ist eine Art Datenbank-Trigger.Siehe auch [fblangref40-ddl-trgr-dbtrigger-notrgr-de] wie man Datenbank- und DDL-Trigger unterdrückt.
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 ;!
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. |
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
)
====================================================
[fblangref40-ddl-trgr-alter-de], [fblangref40-ddl-trgr-crtalter-de], [fblangref40-ddl-trgr-recreate-de], [fblangref40-ddl-trgr-drop-de], DDL-Trigger im Kapitel Procedural SQL (PSQL)-Anweisungen
ALTER TRIGGER
Ändern und Deaktivieren eines bestehenden Triggers
DSQL, ESQL
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.
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 Mehrere DML-Ereignisse – Das Schlüsselwort |
DML-Trigger können geändert werden durch:
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:
Benutzer mit dem ALTER DATABASE
-Privileg
Den Trigger set_cust_no
deaktivieren (in den inaktiven Zustand schalten).
ALTER TRIGGER set_cust_no INACTIVE;
Ändern der Position der Zündreihenfolge des Triggers set_cust_no
.
ALTER TRIGGER set_cust_no POSITION 14;
Den Trigger TR_CUST_LOG
in den inaktiven Zustand schalten und die Ereignisliste ändern.
ALTER TRIGGER TR_CUST_LOG
INACTIVE AFTER INSERT OR UPDATE;
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
Erstellen eines neuen Triggers oder Ändern eines bestehenden Triggers
DSQL
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.
CREATE OR ALTER TRIGGER
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
Löschen eines vorhandenen Triggers
DSQL, ESQL
DROP TRIGGER trigname
Parameter | Beschreibung |
---|---|
trigname |
Triggername |
Die Anweisung DROP TRIGGER
verwirft (löscht) einen vorhandenen Trigger.
DML-Trigger können gelöscht werden durch:
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:
Benutzer mit dem ALTER DATABASE
-Privileg
DROP TRIGGER
set_cust_no
DROP TRIGGER set_cust_no;
RECREATE TRIGGER
Erstellen eines neuen Triggers oder Neuerstellen eines vorhandenen Triggers
DSQL
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 |
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