2012-01-03 10 views
5

Quiero evitar que se borre cualquier fila con VERSIONID=1 en una tabla determinada. También quiero registrar esto en una tabla de auditoría para que podamos ver cuándo sucede esto con fines de registro. Estoy tratando de hacer esto con un disparador:Evitar que ciertas filas se eliminen en 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; 

me sale el siguiente resultado:

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' 

Sin embargo, la tabla TPM_AUDIT está vacía. ¿Estoy haciendo algo mal?

Respuesta

10

Si su desencadenante genera un error, la instrucción DELETE falla y la transacción se retrotrae al punto de almacenamiento implícito que se crea antes de ejecutar la instrucción. Eso significa que cualquier cambio realizado por el desencadenador también se revierte.

Puede solucionar esto utilizando transacciones autónomas. Algo así como

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; 

Esto pondrá el INSERT en TPM_AUDIT en una transacción separada que se puede cometer fuera del contexto de la declaración DELETE. Tenga mucho cuidado al usar transacciones autónomas, sin embargo,

  1. Si alguna vez se encuentra usando transacciones autónomas para cualquier cosa que no sea escribir en una tabla de registro, es casi seguro que está haciendo algo mal.
  2. El código en un bloque PL/SQL declarado mediante transacciones autónomas es verdaderamente autónomo por lo que no puede ver los cambios no confirmados realizados por la sesión actual.
  3. Debido a la coherencia de escritura, es muy posible que Oracle ejecute parcialmente una instrucción DELETE, activando el desencadenador de nivel de fila varias veces, revierte ese trabajo y luego vuelva a ejecutar DELETE. Sin embargo, esa reversión silenciosa no revertirá los cambios realizados por la transacción autónoma. Por lo tanto, es muy posible que un solo DELETE de una sola fila provoque que el disparador se active más de una vez y, por lo tanto, cree varias filas en TPM_AUDIT.
+0

Gracias! Analizaré este enfoque, aunque ahora creo que la auditoría podría ser una mejor característica, ya que también incluirá el texto SQL (que no puedo obtener de un activador).Básicamente, estoy tratando de descubrir por qué estas filas se eliminan aleatoriamente varias veces al mes, aunque no hay nada en la base de código que elimine nada de esta tabla. –

0

Creo que es necesario COMPROMETER la operación INSERT antes de llamar a RAISE_APPLICATION_ERROR, que revierte la transacción.

+2

no puede comprometerse dentro de un disparador –

1

Si puede crear una restricción ÚNICA en las columnas de pmp TPM_PROJECTVERSION pk + la columna de versión, puede crear una segunda tabla que haga referencia a esas filas.

Al intentar eliminar una fila en TPM_PROJECTVERSION fallaría porque hay filas secundarias presentes. Al menos arrojaría un error en su aplicación y evitaría la eliminación.

La otra tabla podría completarse automáticamente mediante un desencadenador de inserción en TPM_PROJECTVERSION.

Si revoca el privilegio DELETE en esa tabla auxiliar, nunca sería posible eliminar esas filas.

Cuestiones relacionadas