Перейти к основному содержимому

Обзор поведения триггеров

примечание

Эта страница переведена при помощи нейросети GigaChat.

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

Для таблиц и внешних таблиц можно определить триггеры для выполнения до или после любой операции INSERT, UPDATE, или DELETE, либо один раз для каждой измененной строки, либо один раз для каждого оператора SQL. Кроме того, триггеры UPDATE могут быть настроены таким образом, чтобы срабатывать только в том случае, если определенные столбцы упоминаются в предложении SET предложения UPDATE. Триггеры также могут срабатывать при выполнении операторов TRUNCATE. Если происходит событие триггера, функция триггера вызывается в соответствующее время для обработки события.

Для представлений триггеры могут быть определены для выполнения вместо операций INSERT, UPDATE, или DELETE. Такие INSTEAD OF триггеры срабатывают один раз для каждой строки, которую необходимо изменить в представлении. Это ответственность функции триггера выполнять необходимые изменения в базовой таблице(ах) представления и, где это уместно, возвращать измененную строку так, как она будет отображаться в представлении. Триггеры на представления также могут быть определены для выполнения один раз за SQL-оператор до или после операций INSERT, UPDATE, или DELETE. Однако такие триггеры срабатывают только в том случае, если существует также триггер INSTEAD OF на представление. В противном случае любое предложение, нацеленное на представление, должно быть переписано в предложение, влияющее на его базовую таблицу(ы), а затем сработают те триггеры, которые прикреплены к базовой таблице(ам).

Функция триггера должна быть определена перед тем, как сам триггер может быть создан. Функция триггера должна быть объявлена как функция, не принимающая аргументов и возвращающая тип trigger. (Функция триггера получает свои входные данные через специально передаваемую структуру TriggerData, а не в виде обычных аргументов функции.)

После создания подходящей функции триггера триггер устанавливается с помощью CREATE TRIGGER. Одна и та же функция триггера может использоваться для нескольких триггеров.

PostgreSQL предлагает как триггеры для каждой строки, так и триггеры для каждого оператора. С триггером для каждой строки функция триггера вызывается один раз для каждой строки, которая затронута оператором, вызвавшим триггер. В отличие от этого, триггер для каждого оператора вызывается только один раз при выполнении соответствующего оператора, независимо от количества строк, затронутых этим оператором. В частности, оператор, который не затрагивает ни одной строки, все равно приведет к выполнению любых применимых триггеров уровня оператора. Эти два типа триггеров иногда называют триггерами уровня строки и триггерами уровня оператора соответственно. Триггеры на TRUNCATE могут быть определены только на уровне оператора, а не для каждой строки.

Триггеры также классифицируются в зависимости от того, срабатывают ли они до, после или вместо операции. Они называются BEFORE триггерами, AFTER триггерами и INSTEAD OF триггерами соответственно. Триггеры уровня оператора BEFORE естественно срабатывают перед началом выполнения оператора, тогда как триггеры уровня оператора AFTER срабатывают в самом конце оператора. Эти типы триггеров могут быть определены на таблицах, представлениях или внешних таблицах. Триггеры уровня строки BEFORE срабатывают непосредственно перед выполнением конкретной строки, тогда как триггеры уровня строки AFTER срабатывают в конце оператора (но перед любыми триггерами уровня оператора AFTER). Эти типы триггеров могут быть определены только на таблицах и внешних таблицах, но не на представлениях. INSTEAD OF триггеры могут быть определены только на представлениях и только на уровне строки; они срабатывают сразу же, как только каждая строка в представлении определяется как требующая обработки.

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

Если оператор INSERT содержит предложение ON CONFLICT DO UPDATE, возможно, что триггеры уровня строки BEFORE INSERT и затем BEFORE UPDATE выполняются на инициированных строках. Подобные взаимодействия могут быть сложными, если триггеры не являются идемпотентными, поскольку изменения, внесенные триггерами BEFORE INSERT, будут видны триггерам BEFORE UPDATE, включая изменения в столбцах EXCLUDED.

Обратите внимание, что триггеры уровня оператора UPDATE выполняются, когда указано ON CONFLICT DO UPDATE, независимо от того, были ли затронуты какие-либо строки оператором UPDATE (и независимо от того, была ли выбрана альтернативная траектория UPDATE). Оператор INSERT с предложением ON CONFLICT DO UPDATE сначала выполнит триггеры уровня оператора BEFORE INSERT, затем триггеры уровня оператора BEFORE UPDATE, далее триггеры уровня оператора AFTER UPDATE и наконец триггеры уровня оператора AFTER INSERT.

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

Отдельные триггеры для MERGE не определены. Вместо этого срабатывают триггеры уровня оператора или уровня строки UPDATE, DELETE и INSERT, в зависимости от (для триггеров уровня оператора) действий, указанных в запросе MERGE, и (для триггеров уровня строки) выполняемых действий.

При выполнении команды MERGE срабатывают триггеры уровня оператора BEFORE и AFTER для событий, указанных в действиях команды MERGE, независимо от того, выполняется действие в конечном итоге или нет. Это то же самое, что оператор UPDATE, который обновляет ноль строк, но триггеры уровня оператора срабатывают. Триггеры уровня строки срабатывают только тогда, когда строка фактически обновляется, вставляется или удаляется. Поэтому вполне законно, чтобы при срабатывании триггеров уровня оператора для определенных типов действий не срабатывали триггеры уровня строки для такого же типа действия.

Триггерные функции, вызываемые триггерами для каждого оператора, всегда должны возвращать NULL. Триггерные функции, вызываемые построчными триггерами, могут вернуть строку таблицы (значение типа HeapTuple) вызывающему исполнителю, если они выберут. Построчный триггер, срабатывающий перед операцией, имеет следующие варианты:

  • Он может вернуть NULL, чтобы пропустить операцию для текущей строки. Это инструктирует исполнителя не выполнять операцию уровня строки, которая вызвала триггер (вставку, изменение или удаление конкретной строки таблицы).
  • Только для построчных триггеров INSERT и UPDATE возвращаемая строка становится строкой, которая будет вставлена или заменит обновляемую строку. Это позволяет функции триггера изменять вставляемую или обновляемую строку.

Построчный триггер BEFORE, который не предназначен вызывать ни одно из этих поведений, должен быть осторожен, чтобы вернуть в качестве своего результата ту же строку, что была передана (то есть строку NEW для триггеров INSERT и UPDATE, строку OLD для триггеров DELETE).

Триггер уровня строки должен либо возвращать INSTEAD OF для указания того, что он не изменял никаких данных из базовых таблиц представления, либо он должен вернуть строку представления, которая была передана (строка NEW для операций INSERT и UPDATE, или строка OLD для операции DELETE). Ненулевое значение возврата используется для сигнала о том, что триггер выполнил необходимые изменения данных в представлении. Это приведет к увеличению количества строк, затронутых командой. Только для операций INSERT и UPDATE триггер может изменить строку NEW перед ее возвратом. Это изменит данные, возвращаемые INSERT RETURNING или UPDATE RETURNING, и полезно, когда представление не будет отображать точно те же данные, которые были предоставлены.

Возвращаемое значение игнорируется для триггеров уровня строки, срабатывающих после операции, поэтому они могут возвращать NULL.

Некоторые соображения применимы для сгенерированных столбцов. Сохраненные сгенерированные столбцы вычисляются после триггеров BEFORE и до триггеров AFTER. Поэтому сгенерированное значение можно проверить в триггерах AFTER. В триггерах BEFORE строка OLD содержит старое сгенерированное значение, как можно было бы ожидать, но строка NEW еще не содержит новое сгенерированное значение и не должна быть доступна. В интерфейсе языка C содержимое столбца в этот момент неопределенно; язык программирования более высокого уровня должен предотвратить доступ к сохраненному сгенерированному столбцу в строке NEW в триггере BEFORE. Изменения значения сгенерированного столбца в триггере BEFORE игнорируются и будут перезаписаны.

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

Определение триггера также может указывать логическое WHEN условие, которое будет проверено для определения того, следует ли запускать триггер. В триггерах уровня строки WHEN условие может проверять старые и/или новые значения столбцов строки. (Триггеры уровня оператора также могут иметь WHEN условия, хотя эта функция не так полезна для них.) В триггере BEFORE условие WHEN оценивается непосредственно перед выполнением функции или ее потенциальным выполнением, поэтому использование WHEN практически не отличается от проверки того же условия в начале функции триггера. Однако в триггере AFTER условие WHEN оценивается сразу после обновления строки и определяет, нужно ли поставить событие в очередь для запуска триггера в конце оператора. Таким образом, когда условие AFTER триггера WHEN не возвращает истинное значение, нет необходимости ставить событие в очередь или повторно извлекать строку в конце предложения. Это может привести к значительному увеличению скорости выполнения операторов, которые изменяют множество строк, если триггер необходимо запустить только для нескольких из этих строк. INSTEAD OF триггеры не поддерживают WHEN условий.

Обычно триггеры уровня строки BEFORE используются для проверки или изменения данных, которые будут вставлены или обновлены. Например, триггер BEFORE может использоваться для вставки текущего времени в timestamp столбец или для проверки согласованности двух элементов строки. Триггеры уровня строки AFTER наиболее целесообразно использовать для распространения обновлений на другие таблицы или проведения проверок согласованности с другими таблицами. Причина такого разделения труда заключается в том, что триггер AFTER может быть уверен, что видит окончательное значение строки, тогда как триггер BEFORE не может; после него могут срабатывать другие BEFORE триггеры. Если нет конкретной причины сделать триггер BEFORE или AFTER, то случай BEFORE более эффективен, поскольку информация об операции не должна сохраняться до конца оператора.

Если функция триггера выполняет команды SQL, эти команды могут снова вызвать срабатывание триггеров. Это известно как каскадные триггеры. На количество уровней каскадов нет прямого ограничения. Возможно, каскады вызовут рекурсивный вызов одного и того же триггера; например, триггер INSERT может выполнить команду, которая вставляет дополнительную строку в ту же таблицу, вызывая повторное срабатывание триггера INSERT. Ответственность за предотвращение бесконечной рекурсии в таких сценариях лежит на программисте триггеров.

Если функция триггера выполняет SQL-команды, то эти команды снова могут запустить триггеры. Это известно как каскадирующие триггеры. Нет прямого ограничения на количество уровней каскада. Возможно рекурсивное обращение одной и той же функции триггера; например, триггер INSERT может выполнить команду, которая вставляет дополнительную строку в ту же таблицу, вызывая повторный вызов триггера INSERT. Ответственность за избежание бесконечной рекурсии в таких сценариях несет программист триггера.

Когда определяется триггер, можно указать аргументы для него. Цель включения аргументов в определение триггера состоит в том, чтобы разрешить различным триггерам с аналогичными требованиями вызывать одну и ту же функцию. В качестве примера можно привести обобщенную функцию триггера, которая принимает в качестве своих аргументов два имени столбца и помещает текущего пользователя в один из них и текущую временную метку в другой. Правильно написанная функция триггера была бы независимой от конкретной таблицы, на которую она воздействует. Поэтому одна и та же функция могла бы использоваться для событий INSERT в любой таблице с подходящими столбцами, чтобы автоматически отслеживать создание записей в транзакционной таблице, например. Она также могла бы использоваться для отслеживания последних событий обновления, если определена как триггер UPDATE.

Каждый язык программирования, поддерживающий триггеры, имеет свой метод для предоставления входных данных триггера функции триггера. Эти входные данные включают тип события триггера (например, INSERT или UPDATE) и любые аргументы, которые были перечислены в CREATE TRIGGER. Для триггера уровня строки входные данные также включают строку NEW для INSERT и UPDATE триггеров, а также/или строку OLD для UPDATE и DELETE триггеров.

По умолчанию у триггеров уровня оператора нет способа анализировать отдельные измененные оператором строки. Но триггер AFTER STATEMENT может запросить создание таблиц перехода, чтобы сделать наборы затронутых строк доступными для триггера. Триггеры AFTER ROW также могут запрашивать таблицы переходов, чтобы они могли видеть общие изменения в таблице, а также изменение в отдельной строке, для которой они в данный момент срабатывают. Метод проверки таблиц перехода снова зависит от используемого языка программирования, но типичный подход заключается в том, чтобы заставить таблицы перехода действовать как временные таблицы только для чтения, к которым можно получить доступ с помощью команд SQL, выдаваемых внутри функции триггера.