2016-12-09 5 views
2

Ich möchte einen Server-Level-Trigger setzen, um das Löschen einer Datenbank zu verhindern, die kein Datenbank-Snapshot ist. Auf den ersten Blick erscheint das Unten, als ob es funktionieren sollte, aber es nie tut. Ich habe versucht, die Logik umzukehren, und das hat nicht geholfen. Hat jemand eine Idee was ich falsch mache?SQL Server-Trigger zum Verhindern des Datenbankabsturzes

DECLARE @DBName NVARCHAR(100), 
     @eventData XML; 

SET @eventData = EVENTDATA();   
SELECT @DBName = @eventData.value('data(/EVENT_INSTANCE/DatabaseName)[1]', 'SYSNAME'); 

RAISERROR('Attempting delete of %s.', 10, 1, @DBName); 

IF @DBName IN (SELECT name 
       FROM sys.databases 
       WHERE source_database_id IS NOT NULL) 
    BEGIN 
     RAISERROR('[%s] was successfully dropped.', 10, 1, @DBname) WITH LOG; 
    END; 
ELSE   
    BEGIN 
     RAISERROR('[%s] cannot be deleted without first disabling the server trigger "tgr_prevent_db_drop".', 10, 1, @DBname) WITH LOG; 
     ROLLBACK; 
    END; 

Die RAISERROR an der Spitze bestätigt, immer die richtige Datenbank (zum Beispiel der, gelöscht zu werden), und wenn ich die SELECT von sys.databases manuell immer wieder die entsprechenden Daten ausgeführt werden. Leider fällt das, egal was ich mache, immer in den Abschnitt "..was erfolgreich gelöscht" sowohl für echte Datenbanken als auch für ihre Datenbank-Snapshots.

Antwort

1

Ich versuchte immer, dies zu funktionieren auch, aber es gab zu viele Berechtigungshindernisse, da sys.databases nur Werte zurückgibt, die vom aktuellen Benutzer sichtbar sind. (Und ich konnte keine zuverlässige generische Lösung mit "Executing As" versuchen.)

Schließlich entschied ich mich einfach, den Datenbanknamen als Filter zu verwenden. Beispiel:

DECLARE @DBName NVARCHAR(100), 
     @eventData XML; 

SET @eventData = EVENTDATA();   
SELECT @DBName = @eventData.value('data(/EVENT_INSTANCE/DatabaseName)[1]', 'SYSNAME'); 

RAISERROR('Attempting delete of %s.', 10, 1, @DBName); 

IF Right(@DBName, 9) <> '_SnapShot' --Checking via db name due to permissions affecting sys.databases 
    BEGIN 
     RAISERROR('[%s] was successfully dropped.', 10, 1, @DBname) WITH LOG; 
    END; 
ELSE   
    BEGIN 
     RAISERROR('[%s] cannot be deleted without first disabling the server trigger "tgr_prevent_db_drop".', 10, 1, @DBname) WITH LOG; 
     ROLLBACK; 
    END; 
0

diese Bedingung ist immer wahr ..

IF @DBName IN (SELECT name 
       FROM sys.databases 
       WHERE source_database_id IS NOT NULL) 
    BEGIN 
     RAISERROR('[%s] was successfully dropped.', 10, 1, @DBname) WITH LOG; 
    END; 

so, wird Ihre Datenbank unabhängig davon fallen gelassen werden what..Instead unter anderem Klausel über Block bewegen

RAISERROR('[%s] cannot be deleted without first disabling the server trigger "tgr_prevent_db_drop".', 10, 1, @DBname) WITH LOG; 
     ROLLBACK; 
+0

Hallo @TheGameIswar, ich bin nicht Tracking. Wenn es sich bei der Datenbank um eine Momentaufnahme handelt, hat sie in der Spalte source_database_id einen Wert und erscheint in SELECT, so dass die Bedingung immer dann wahr sein sollte. Wenn es sich nicht um einen Snapshot handelt, muss source_database_id NULL sein, also würde ich erwarten, dass dies als false ausgewertet wird. Wenn ich dies außerhalb des DDL-Triggers ausführe, funktioniert es wie erwartet. Es ist nur innerhalb des DDL-Triggers, dass es fehlschlägt. Letztendlich möchte ich das Löschen von Snapshots zulassen, aber das Löschen von übergeordneten Datenbanken verhindern. – PseudoToad

+0

@PseudoToad, die sys.databases hat das Problem, Werte basierend auf den Berechtigungen des Benutzers zurückzugeben. Wenn Ihr Trigger die Snapshot-Datenbanken in sys.databases nicht anzeigt, wird der Trigger als Benutzer ausgeführt, der keine Berechtigungen dafür besitzt. –

Verwandte Themen