Новое в 8i: триггеры схемы БД

Владимир Пржиялковский

В версии 8i разработчик сталкивается с целой серией новых видов триггеров. Шесть из них запускаются событиями, возникающими при выполнении операций на уровне схемы, и поэтому носят название "триггеры для событий уровня схемы БД" (schema-level event triggers). Что это за триггеры?

Вот перечень запускающих их событий:
 

Событие   Описание триггера  
SERVERERROR Триггер срабатывает при возникновении серверной ошибки
LOGON Триггер срабатывает при успешном подключении к системе клиентского приложения
LOGOFF Триггер срабатывает перед отключением клиентского приложения
CREATE Триггер срабатывает при добавлении к схеме нового объекта командой CREATE
DROP Триггер срабатывает перед попыткой удалить из схемы объект командой DROP
ALTER Триггер срабатывает при изменении объекта схемы командой ALTER

Общий синтаксис описания триггеров схемы таков:

CREATE [ OR REPLACE ] TRIGGER имя_триггера

{ BEFORE / AFTER }

{ SERVERERROR / LOGON / LOGOFF / CREATE / DROP / ALTER }

ON имя_схемы .SCHEMA

BEGIN

текст на PL/SQL

END;

С каждым событием из таблицы выше связано несколько атрибутов. Фактически эти атрибуты - системные функции, возвращающие при обращении к ним из тела триггера некоторый результат. Ниже эти атрибуты перечисляются, причем первые шесть из них относятся к уровню базы данных (но могут использоваться в триггерах уровня схемы).
 

Имя   Тип   Описание  
SYSEVENT VARCHAR2(30) Имя события, активизировавшего триггер
LOGIN_USER VARCHAR2(30) Имя пользователя, инициировавшего сеанс работы с Oracle
INSTANCE_NUM NUMBER Имя экземпляра СУБД
DATABASE_NAME VARCHAR2(50) Имя БД
SERVER_ERROR NUMBER Функция, возвращающая номер ошибки на указанном месте магазина ошибок. 1 соответствует верхушке магазина. Пример: SERVER_ERROR(2) выдаст номер ошибки на втором от верха месте в магазине.
IS_SERVERERROR BOOLEAN Функция, возвращающая TRUE при наличии указанной ошибке в текущем магазине ошибок; FALSE в противном случае.
DICTIONARY_OBJ_OWNER VARCHAR2(30) Владелец объекта из словаря-справочника, действие с которым привело к активизации триггера.
DICTIONARY_OBJ_NAME VARCHAR2(30) Имя объекта из словаря-справочника, действие с которым привело к активизации триггера
DICTIONARY_OBJ_TYPE VARCHAR2(30) Тип объекта из словаря-справочника, действие с которым привело к активизации триггера
DES_ENCRYPTED_PASSWORD VARCHAR2(30) Зашифрованный (DES) пароль создаваемого или изменяемого пользователя.

Вот какие правила и атрибуты свойственны каждому событию:
 

Событие   Правило   Атрибуты  
LOGON Условие можно указать, воспользовавшись USERID( ) или USERNAME( ) SYSEVENT

LOGIN_USER

INSTANCE_NUM

DATABASE_NAME

LOGOFF Условие можно указать, воспользовавшись USERID( ) или USERNAME( ) SYSEVENT

LOGIN_USER

INSTANCE_NUM

DATABASE_NAME

BEFORE CREATE

AFTER CREATE

В пределах триггера удалять создаваемый объект нельзя. Триггер выполняется в рамках текущей транзакции. SYSEVENT

LOGIN_USER

INSTANCE_NUM

DATABASE_NAME

DICTIONARY_OBJ_TYPE

DICTIONARY_OBJ_NAME

DICTIONARY_OBJ_OWNER

BEFORE ALTER

AFTER ALTER

В пределах триггера удалять изменяемый объект нельзя. Триггер выполняется в рамках текущей транзакции. SYSEVENT

LOGIN_USER

INSTANCE_NUM

DATABASE_NAME

DICTIONARY_OBJ_TYPE

DICTIONARY_OBJ_NAME

DICTIONARY_OBJ_OWNER

BEFORE DROP

AFTER DROP

В пределах триггера изменять удаляемый объект нельзя. Триггер выполняется в рамках текущей транзакции. SYSEVENT

LOGIN_USER

INSTANCE_NUM

DATABASE_NAME

DICTIONARY_OBJ_TYPE

DICTIONARY_OBJ_NAME

DICTIONARY_OBJ_OWNER


В своей последней книге Стив Фойерштайн приводит пример использования триггера уровня схемы, который здесь помещается с небольшими изменениями.

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

CREATE OR REPLACE TRIGGER no_drop_trg

BEFORE DROP ON SCOTT.SCHEMA

DECLARE

v_msg VARCHAR2(1000) :=

'No drop allowed on ' //

DICTIONARY_OBJ_OWNER // '.' //

DICTIONARY_OBJ_NAME // ' from ' //

LOGIN_USER;

BEGIN

IF DICTIONARY_OBJ_OWNER = 'SCOTT' AND

DICTIONARY_OBJ_NAME LIKE 'EMP%' AND

DICTIONARY_OBJ_TYPE = 'TABLE'

THEN

RAISE_APPLICATION_ERROR (

-20905, v_msg);

END IF;

END;

/

(Для того, чтобы триггер странслировался, нужно установить параметр COMPATIBLE в INIT.ORA в значение, не меньшее, чем 8.1.0.0.0).

Теперь в ответ на

DROP TABLE EMP;

вы получите результат

drop table emp

*

ERROR at line 1:

ORA-00604: error occurred at recursive SQL level 1

ORA-20905: No drop allowed on SCOTT.EMP from SCOTT

ORA-06512: at line 12

и таблица останется целой (удаление не сработает, поскольку мы сынициировали ошибку).

Остается отметить, что мы защитили нашу таблицу на уровне DDL (и, тем самым, сохранили по крайней мере ее структуру), но не предохранились от несанкционированного удаления строк из таблицы (уровень DML). Для этого придется воспользоваться другими средствами Oracle: обычными триггерами таблицы и … средствами резервного копирования и восстановления. В промышленной же прикладной системе, передаваемой заказчику, и то, и другое, и третье, вероятно, должно играть роль "второго эшелона обороны", вступающего в игру после отказов в "первом эшелоне" - процедурном доступе к таблицам или доступе, полностью контролируемом приложением.

С другой стороны, если наша цель - не просто защитить таблицу от удаления, а предпринять при попытке удаления какие-нибудь вспомогательные действия (журнализировать акт такой попытки, скопировать таблицу во временную область перед удалением и так далее), то приемлемой альтернативы триггеру типа приведенного выше нет. 


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=3781