Предотвращение удаления определенных строк в Oracle - PullRequest
5 голосов
/ 03 января 2012

Я хочу предотвратить удаление строки с VERSIONID=1 в определенной таблице.Я также хочу записать это в таблицу аудита, чтобы мы могли видеть, когда это происходит для целей ведения журнала.Я пытаюсь сделать это с помощью триггера:

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;

Я получаю следующие результаты:

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'

Однако таблица TPM_AUDIT пуста.Я что-то не так делаю?

Ответы [ 3 ]

10 голосов
/ 04 января 2012

Если ваш триггер вызывает ошибку, оператор DELETE завершается неудачно, и транзакция откатывается до неявной точки сохранения, созданной до запуска оператора. Это означает, что любые изменения, сделанные триггером, также будут отменены.

Вы можете обойти это, используя автономные транзакции. Что-то вроде

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;

Это поместит INSERT в TPM_AUDIT в отдельную транзакцию, которая может быть зафиксирована вне контекста оператора DELETE. Будьте очень осторожны с использованием автономных транзакций, однако

  1. Если вы когда-нибудь обнаружите, что используете автономные транзакции для чего-то кроме записи в таблицу журнала, вы почти наверняка делаете что-то не так.
  2. Код в блоке PL / SQL, объявленном с использованием автономных транзакций, действительно автономен, поэтому он не может видеть незафиксированные изменения, внесенные текущим сеансом.
  3. Из-за согласованности записи вполне возможно, что Oracle частично выполнит оператор DELETE, несколько раз запустив триггер уровня строки, откатит эту работу и затем повторно выполнит DELETE. Однако этот тихий откат не откатит изменения, сделанные автономной транзакцией. Поэтому вполне возможно, что один DELETE из одной строки фактически вызовет срабатывание триггера более одного раза и, следовательно, создаст несколько строк в TPM_AUDIT.
1 голос
/ 04 января 2012

Если вы можете создать ограничение UNIQUE для столбцов pk TPM_PROJECTVERSION + столбец версии, то вы можете создать вторую таблицу, которая будет ссылаться на эти строки.

Попытка удалить строку в TPM_PROJECTVERSION приведет к сбою, так как присутствуют дочерние строки.Это, по крайней мере, приведет к ошибке в вашем приложении и предотвратит удаление.

Другая таблица может автоматически заполняться через триггер вставки в TPM_PROJECTVERSION.

Если вы отмените привилегию DELETE для этой вспомогательной таблицы, удалить эти строки будет невозможно.

0 голосов
/ 04 января 2012

Полагаю, вам нужно выполнить операцию INSERT перед вызовом RAISE_APPLICATION_ERROR, который откатывает транзакцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...