Продолжается подписка на наши издания! Вы не забыли подписаться?

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

Владимир Пржиялковский
09 марта 2000 г.

В версии 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: обычными триггерами таблицы и … средствами резервного копирования и восстановления. В промышленной же прикладной системе, передаваемой заказчику, и то, и другое, и третье, вероятно, должно играть роль “второго эшелона обороны”, вступающего в игру после отказов в “первом эшелоне” – процедурном доступе к таблицам или доступе, полностью контролируемом приложением.

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

Триггеры событий в СУБД

Кроме триггеров для событий уровня схемы БД, в Oracle 8i существуют так называемые триггерах для событий уровня системы (database-level event triggers). Они запускаются при возникновении в системе (СУБД) следующих событий:

Событие

Описание триггера

SERVERERROR

Триггер срабатывает при возникновении серверной ошибки

LOGON

Триггер срабатывает при успешном подключении к системе клиентского приложения

LOGOFF

Триггер срабатывает перед отключением клиентского приложения от СУБД

STARTUP

Триггер срабатывает немедленно после открытия БД

SHUTDOWN

Триггер срабатывает непосредственно перед попыткой закрыть СУБД “нормальным образом”, то есть всеми вариантами команды SHUTDOWN кроме варианта SHUTDOWN ABORT

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

CREATE [ OR REPLACE ] TRIGGER имя_триггера
{ BEFORE | AFTER }
{ SERVERERROR | LOGON | LOGOFF | STARTUP | SHUTDOWN }
ON DATABASE
BEGIN
текст на PL/SQL
END;

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

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

Событие

Правило

Атрибуты

SERVERERROR

По умолчанию триггер будет срабатывать при всех событиях. Однако специальным указанием можно “сказать”, чтобы триггер срабатывал только при интересующих нас событиях.

SYSEVENT
LOGIN_USER
INSTANCE_NUM
DATABASE_NAME
SERVER_ERROR
IS_SERVERERROR

LOGON

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

SYSEVENT
LOGIN_USER
INSTANCE_NUM
DATABASE_NAME

LOGOFF

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

SYSEVENT
LOGIN_USER
INSTANCE_NUM
DATABASE_NAME

STARTUP

В теле триггера не допускается использование операций с БД – DML и запросов. Можно, однако, запускать программы (например, listener), закреплять в SGA пакеты и т. д.

SYSEVENT
LOGIN_USER
INSTANCE_NUM
DATABASE_NAME

SHUTDOWN

В теле триггера не допускается использование операций с БД – DML и запросов. Можно, однако, останавливать или запускать программы (например, для сбора статистики работы СУБД и занесения ее в журнал)

SYSEVENT
LOGIN_USER
INSTANCE_NUM
DATABASE_NAME

При написании триггера нужно учитывать следующие обстоятельства:

Триггеры событий в СУБД могут использоваться в разных целях: для прикрепления объектов к SGA при старте системы, для отслеживания входов в БД, для трассировки ошибок.

Так, прикрепление наиболее часто используемых пакетов в SGA при старте системы может (в случае интенсивной загрузки СУБД) ускорить среднее время обращения к процедурам этих пакетов (они не будут участвовать в страничном обмене) и уменьшить отрицательные последствия фрагментации SGA. Следующий пример Стивена Фойерстина показывает, как можно таким образом закрепить в SGA пакеты STANDARD и DBMS_STANDARD, встроенные в систему:

CREATE OR REPLACE TRIGGER pin_code_on_startup
  AFTER STARTUP ON DATABASE
BEGIN
  DBMS_SHARED_POOL.KEEP (‘SYS.STANDARD’, ‘P’);
  DBMS_SHARED_POOL.KEEP (‘SYS.DBMS_STANDARD’, ‘P’);
END;
/

Для закрепления пользовательских пакетов, включающих требуемые процедуры или функции, можно самостоятельно составить более сложные схемы, базирующиеся в конце-концов на триггере AFTER STARTUP.

Литература
  1. Oracle PL/SQL Programming. Steven Feuerstein. O’Reilly & Associates, October 1999.

  2. Документация по Oracle


Copyright © 1994-2016 ООО "К-Пресс"