Создание триггера

Функция

Определение оператора создания триггера.

Спецификация
   
< триггер на обработку данных >::=
< операция >::=
INSERT | DELETE | UPDATE [OF имя столбца[, …]];
< триггер на системные события >::=
{AFTER LOGON | BEFORE LOGOFF}
ON {имя пользователя|DATABASE}
< псевдоним для старой записи >::=
< псевдоним для новой записи >::=
< тело триггера >::=
текст триггера
Синтаксические правила
  1. < Имя таблицы > должно задавать объект (базовую таблицу или представление), для которого создается триггер.

  2. < Имя схемы > задает имя схемы, владельцем которой является текущий пользователь. Создавать триггеры в «чужих» схемах запрещено.

  3. Опция OR REPLACE заставляет удалять существующий в БД триггер и создавать его под тем же именем, но с указанными параметрами.

  4. Опция IF NOT EXISTS отменяет выполнение оператора, если указанный триггер уже существует в БД.

  5. Одновременное использование опций IF NOT EXISTS и OR REPLACE запрещено.

  6. < Операция > задает операцию по обработке данных таблицы, при выполнении которой должен срабатывать триггер:

    • INSERT – при добавлении данных;

    • DELETE – при удалении данных;

    • UPDATE – при корректировке данных.

  7. Если указана операция UPDATE без конструкции OF, триггер будет срабатывать при корректировке любого поля записи таблицы.

  8. < Имя столбца > должно ссылаться на столбец в < имени таблицы >.

  9. Если указана операция UPDATE OF < столбец >[, …], триггер будет срабатывать при корректировке только перечисленных столбцов записи таблицы.

  10. Опции BEFORE, AFTER, INSTEAD OF определяют момент срабатывания триггера при выполнении операций обработки данных:

    • BEFORE – перед выполнением < операции >;

    • AFTER – после выполнения < операции >;

    • INSTEAD OF – взамен выполнения < операции >.

  11. Конструкция INSTEAD OF применима только для базовых таблиц (не представлений).

  12. Конструкция INSTEAD OF не применима для таблиц, работающих в циклическом режиме, поскольку такие триггеры препятствуют удалению записей (см. оператор ALTER TABLE … SET RECORDS LIMIT пункта «Модификация таблицы»).

  13. Конструкция INSTEAD OF допустима как для режима FOR EACH ROW, так и для FOR STATEMENT. Она позволяет выполнить проверку достоверности значений, которые должны быть добавлены, обновлены или удалены, и, в случае их достоверности, выполняет соответствующую операцию. Например, при обработке заказа на некоторый товар триггер INSTEAD OF UPDATE может проверить, есть ли в таблице заказов строка с требуемым товаром. Если да, сумма заказа будет увеличена (операция UPDATE), если нет – запись будет добавлена (операция INSERT).

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

  15. Конструкция FOR EACH… определяет сферу действия триггера:

    • ROW – для каждой записи. Триггер будет срабатывать во время выполнения < операции > при обработке каждой записи таблицы. Возврат триггером значения TRUE разрешает, а FALSE – запрещает выполнение операции только для текущей записи (имеет смысл применительно к BEFORE);

    • STATEMENT – для каждого оператора. Триггер будет срабатывать один раз при выполнении оператора, соответствующего заданной < операции >. Возврат триггером BEFORE ... FOR EACH STATEMENT значения FALSE отменяет действие всей операции.

  16. Если конструкция FOR EACH… не задана, по умолчанию используется FOR EACH STATEMENT.

  17. Если один из BEFORE-триггеров запретил операцию, то последующие AFTER-триггеры выполняться не будут.

    Примечание

    Например, в операторе UPDATE AUTO SET MAKE ='BMW' where PERSONID IN (SELECT PERSONID FROM PERSON WHERE SALARY BETWEEN 2000 AND 50000); триггер на UPDATE при указании FOR EACH ROW выполнится столько раз, сколько записей выбирается по условию WHERE; при указании FOR EACH STATEMENT – только один раз (в случае выполнения условия WHERE).

  18. < Псевдоним для старой записи > задает префикс, используемый для обращения к старым значениям столбца. Если не задан, по умолчанию используется OLD.

  19. < Псевдоним для новой записи > задает префикс, используемый для обращения к новым значениям столбца. Если не задан, по умолчанию используется NEW.

    update auto set old.color=new.color where personid=256;
    update auto set "Старое".color="Новое".color where personid=256;
  20. Понятия «старое» и «новое» значение столбца применимы только для операции UPDATE, независимо от времени действия триггера.

  21. Для операции INSERT имеет смысл только «новое» значение столбца.

  22. Для операции DELETE имеет смысл только «старое» значение столбца.

  23. Запрещено создавать триггер на таблицу другого пользователя.

  24. Имя триггера должно быть уникально в пределах < имя схемы >.

  25. Опции < триггер на системные события >:

    • AFTER LOGON – активизирует триггер после успешного открытия канала с СУБД (после установления соединения с СУБД). Если триггер возвращает ошибку, то для всех пользователей, за исключением LINTER_SYSTEM_USER, выполняется очистка канала;

    • BEFORE LOGOFF – активизирует триггер перед закрытием канала (перед закрытием соединения по инициативе пользователя или при выполнении команд KILL, CKIL интерфейса нижнего уровня). Для курсорных запросов эти триггеры выполнять не следует;

    • DATABASE – активизирует триггер при открытии/закрытии любого канала;

    • < имя пользователя > – активизирует триггер при открытии/закрытии канала для указанного пользователя.

  26. Триггеры AFTER LOGON/BEFORE LOGOFF может создавать/модифицировать/удалять только создатель БД.

  27. Тело триггера (конструкция EXECUTE < тело триггера >) должно быть написано на процедурном языке СУБД ЛИНТЕР (см. документ «СУБД ЛИНТЕР. Процедурный язык», раздел «Элементы языка»).

  28. Опция FOR DEBUG позволяет выполнять отладку триггера с помощью отладчика триггеров и хранимых процедур СУБД ЛИНТЕР.

Общие правила
  1. На каждую операцию можно создавать не более 255 триггеров.

  2. Для хранения тела оттранслированной процедуры, возвращающей тип данных «курсор», в БД должна существовать системная таблица $$$PRCD.

  3. Порядок вызова триггеров с разными значениями < время действия > следующий:

    • BEFORE EACH STATEMENT;

    • BEFORE EACH ROW;

    • AFTER EACH ROW;

    • AFTER EACH STATEMENT.

  4. Триггер BEFORE EACH ROW может изменить значение поля записи, помещаемое в БД, для операторов INSERT и UPDATE.

  5. Возврат триггером BEFORE ... FOR EACH ROW значения FALSE означает отмену действия операции только для текущей записи.

  6. В режиме FOR STATEMENT перед обработкой триггеров на условие BEFORE STATEMENT происходит выполнение всех триггеров на условие INSTEAD OF. Если был выполнен хотя бы один триггер INSTEAD OF (даже если он вернул ошибку), дальнейшая обработка запроса и всех его триггеров прекращается.

  7. В режиме FOR EACH ROW перед обработкой триггеров на условие BEFORE происходит выполнение всех триггеров на условие INSTEAD OF. Если был выполнен хотя бы один триггер INSTEAD OF (даже если он вернул ошибку), обработка записи и триггеров FOR EACH ROW прекращается, происходит переход к обработке следующей записи. При этом триггеры на условие FOR STATEMENT (BEFORE и AFTER) выполняются в обычном режиме.

  8. В момент активизации < триггера на системные события > канал получает статус «занят», что дает возможность триггеру выполняться без помех.

  9. В триггере на системные события все операторы выполняются от имени пользователя, который устанавливает/разрывает соединение, в триггере на обработку данных все операторы выполняются от имени пользователя, который выполняет операцию.

  10. При наличии нескольких < триггеров на системные события > сначала выполняются триггеры для всей БД, затем – для указанного пользователя.

    create or replace table test (i int, ch char( 40 ));
    create or replace trigger test1 after logon on SYSTEM
    execute code 
      execute direct "insert into test values (1,'After logon for user 1');"; 
      return TRUE; 
    end;
    
    create or replace trigger test2 after logon on DATABASE
    execute code 
      execute direct "insert into test values (1,'After logon for database 1');"; 
      return TRUE; 
    end;
    
    create or replace trigger test3 after logon on SYSTEM
    execute code 
      execute direct "insert into test values (1,'After logon for user 2');"; 
      return TRUE; 
    end;
    
    create or replace trigger test4 after logon on DATABASE
    execute code 
      execute direct "insert into test values (1,'After logon for database 2');"; 
      return TRUE; 
    end;
    
    username SYSTEM/MANAGER8
    select * from test;
    I           CH
     -           --
    |          1|After logon for database 1              |
    |          1|After logon for database 2              |
    |          1|After logon for user 1                  |
    |          1|After logon for user 2                  |
  11. Если один из триггеров вернул ошибку, остальные триггеры не выполняются.

    create or replace table test (i int, ch char( 40 ));
    create or replace trigger test1 after logon on SYSTEM
    execute code 
      execute direct "insert into test values (1,'After logon for user 1');"; 
      return FALSE; 
    end;
    
    create or replace trigger test2 after logon on DATABASE
    execute code 
      execute direct "insert into test values (1,'After logon for database 1');";
      return FALSE; 
    end;
    
    create or replace trigger test3 after logon on SYSTEM
    execute code 
      execute direct "insert into test values (1,'After logon for user 2');"; 
      return FALSE; 
    end;
    
    create or replace trigger test4 after logon on DATABASE
    execute code 
      execute direct "insert into test values (1,'After logon for database 2');";
      return FALSE; 
    end;
    
    username SYSTEM/MANAGER8
    // триггер test1 вернул FALSE, поэтому триггеры test2, test3, test4
    // выполняться не будут, канал для пользователя SYSTEM открыт (т.к. это
    // создатель БД – создателю БД нельзя случайно запретить подсоединение к БД
    // созданием LOGON-триггера, возвращающего значение FALSE)
    
    create or replace user TTT identified by '12345678';
    username TTT/12345678
    // ошибка – нет привилегий 1022 (возвращается в случае возврата
    // LOGON-триггером значения FALSE для любого пользователя, кроме создателя БД)
    // триггер test1 вернул FALSE, поэтому триггеры test1, test2, test3, test4 выполняться не будут
    
    username SYSTEM/MANAGER8
    select * from  test;
    I           CH
     -           --
    |          1|After logon for database 1              |
    |          1|After logon for database 1              |
  12. По окончании работы триггеров статус канала восстанавливается.

  13. В случае занятости транслятора процедурного языка ядро СУБД ЛИНТЕР будет ожидать его освобождения.

Пример
CREATE TRIGGER UPDPERSON AFTER UPDATE ON PERSON FOR EACH ROW EXECUTE
CODE
  if old.personid = new.personid then
    execute direct "insert into journal values('PERSON','UPDATE'," + itoa(old.personid) + ",sysdate, 'personid not changed');"; //
    return true; //
  endif
  execute direct "insert into journal values('PERSON','UPDATE'," + itoa(old.personid) + ",sysdate, 'set personid ='+itoa(new.personid)+ ' – cause update for   auto');"; //
  execute direct "update auto set personid=" + itoa(new.personid) + " where personid = " + itoa(old.personid) + ";"; //
END;