2012-01-03 12 views
5

Desidero impedire che nessuna riga con VERSIONID=1 venga eliminata in una determinata tabella. Voglio anche registrarlo in una tabella di controllo in modo che possiamo vedere quando ciò accade per scopi di registrazione. Sto cercando di fare questo con un innesco:Impedire l'eliminazione di determinate righe in Oracle

CREATE TRIGGER TPMDBO.PreventVersionDelete 
    BEFORE DELETE ON TPM_PROJECTVERSION 
    FOR EACH ROW 
DECLARE 
BEGIN 
    IF(:old.VERSIONID = 1) 
    THEN 
    INSERT INTO TPM_AUDIT VALUES ('Query has attempted to delete root project version!', sysdate); 
    RAISE_APPLICATION_ERROR(-20001, 'Query has attempted to delete root project version!'); 
    END IF; 
END; 

Ottengo i seguenti risultati:

SQL> delete from TPM_PROJECTVERSION where PROJECTID=70 and VERSIONID=1; 
delete from TPM_PROJECTVERSION where PROJECTID=70 and VERSIONID=1 
      * 
ERROR at line 1: 
ORA-20001: Query has attempted to delete root project version! 
ORA-06512: at "TPMDBO.PREVENTVERSIONDELETE", line 6 
ORA-04088: error during execution of trigger 'TPMDBO.PREVENTVERSIONDELETE' 

Tuttavia, la tabella TPM_AUDIT è vuota. Sto facendo qualcosa di sbagliato?

risposta

10

Se il trigger genera un errore, l'istruzione DELETE non riesce e la transazione viene ripristinata al punto di salvataggio implicito creato prima dell'esecuzione dell'istruzione. Ciò significa che anche tutte le modifiche apportate dal trigger vengono ripristinate.

È possibile aggirare il problema utilizzando transazioni autonome. Qualcosa di simile

CREATE PROCEDURE write_audit 
AS 
    PRAGMA AUTOMOMOUS_TRANSACTION; 
BEGIN 
    INSERT INTO tpm_audit 
    VALUES('Query has attempted to delete root project version!', 
      sysdate); 
    commit; 
END; 

CREATE TRIGGER TPMDBO.PreventVersionDelete 
    BEFORE DELETE ON TPM_PROJECTVERSION 
    FOR EACH ROW 
DECLARE 
BEGIN 
    IF(:old.VERSIONID = 1) 
    THEN 
    write_audit; 
    RAISE_APPLICATION_ERROR(-20001, 'Query has attempted to delete root project version!'); 
    END IF; 
END; 

Questo metterà la INSERT in TPM_AUDIT in una transazione separata che può essere commesso al di fuori del contesto della dichiarazione DELETE. State molto attenti all'utilizzo di transazioni autonome, tuttavia

  1. Se mai vi trovate a utilizzare transazioni autonome per qualcosa di diverso da scrivere su una tabella di log, state quasi certamente facendo qualcosa di sbagliato.
  2. Il codice in un blocco PL/SQL dichiarato utilizzando le transazioni autonome è veramente autonomo e pertanto non è possibile visualizzare le modifiche non vincolanti apportate dalla sessione corrente.
  3. A causa della coerenza di scrittura, è del tutto possibile che Oracle esegua parzialmente un'istruzione DELETE, esegua il trigger a livello di riga un certo numero di volte, ripristini il lavoro e quindi esegua nuovamente lo DELETE. Questo rollback silenzioso, tuttavia, non ripristinerà le modifiche apportate dalla transazione autonoma. Quindi è del tutto possibile che un singolo DELETE di una singola riga causi effettivamente l'attivazione del trigger più di una volta e, pertanto, crei più righe in TPM_AUDIT.
+0

Grazie! Analizzerò questo approccio, anche se ora ritengo che il controllo possa essere una caratteristica migliore da utilizzare poiché includerà anche il testo SQL (che non posso ottenere da un trigger).Fondamentalmente, sto cercando di capire perché queste righe vengono cancellate casualmente alcune volte al mese anche se non c'è nulla nel codice base che cancelli qualcosa da questa tabella. –

0

Credo che sia necessario COMMITARE l'operazione INSERT prima di chiamare RAISE_APPLICATION_ERROR, che ripristina la transazione.

+2

non è possibile eseguire il commit all'interno di un trigger –

1

Se è possibile creare un vincolo UNI01 sulle colonne pk TPM_PROJECTVERSION + la colonna della versione, è possibile creare una seconda tabella che faccia riferimento a tali righe.

Se si tenta di eliminare una riga in TPM_PROJECTVERSION, le righe figlio sono presenti. Questo dovrebbe almeno generare un errore nella tua applicazione e impedire la cancellazione.

L'altra tabella può essere popolata automaticamente tramite un trigger di inserimento su TPM_PROJECTVERSION.

Se si revoca il privilegio DELETE su quella tabella helper, non sarebbe mai possibile rimuovere tali righe.

Problemi correlati