Каскадные триггеры PLPGSQL? - PullRequest
0 голосов
/ 07 марта 2012

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

У меня есть, например, простая таблица:

CREATE TABLE example
(
id      SERIAL PRIMARY KEY
,name   VARCHAR(100) NOT NULL
,session   INTEGER
,status VARCHAR(100)
);

Моя триггерная функция:

CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$

BEGIN

INSERT INTO example VALUES (NEW.id + 1, NEW.name, NEW.session+2, 'blocked');

RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';

Триггер:

CREATE TRIGGER add_block
AFTER INSERT OR UPDATE
ON example
FOR EACH ROW
EXECUTE PROCEDURE add_block();

Я получаю ошибку:

SQL statement "INSERT INTO example VALUES ( $1 +1,  $2 ,  $3 + 2, $4)"
PL/pgSQL function "add_block" line 37 at SQL statement

Эта ошибка повторяется столько раз, что я не вижу верх.

Как бы я решил это?

EDIT:

CREATE TABLE block_rules
(
id      SERIAL PRIMARY KEY
,session   INTEGER
,block_session   INTEGER
);

Эта таблица содержит правила блоков. Таким образом, если новая запись вставляется в таблицу EXAMPLE с сеансом 1, то она соответственно блокирует сеанс 3, вставляя новую запись с заблокированным состоянием в ту же (EXAMPLE) таблицу выше (не block_rules). То же самое для сеанса 2, но он блокирует сеанс 4.

Таблица block_rules содержит правила (или шаблоны) для блокировки сеанса. Он держит

id | session | block_session
------------------------------
1  | 1       | 3
2  | 2       | 4
3  | 3       | 2

Как бы я изложил это в выражении WHEN о триггере, идущем с ответом Эрвина Бранстеттера ниже?

Спасибо

Ответы [ 3 ]

1 голос
/ 07 марта 2012

ОК, поэтому вы не можете просто добавить еще один столбец, что-то вроде этого:

ALTER TABLE example ADD COLUMN trig INTEGER DEFAULT 0;

CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
    IF NEW.trig = 0 THEN
        INSERT INTO example VALUES (NEXTVAL('example_id_seq'::regclass), NEW.name, NEW.session+2, 'blocked', 1);
    END IF;
    RETURN NULL;
 END;
 $$ LANGUAGE 'plpgsql';

это не здорово, но работает: -)

1 голос
/ 08 марта 2012

Новый ответ на отредактированный вопрос

Эта триггерная функция добавляет заблокированные сеансы в соответствии с информацией в таблице block_rules.

I предполагает , что таблицысвязаны id - информация отсутствует в вопросе.
Теперь я предполагаю, что правила блоков являются общими правилами для всех сессий, и связываются с помощью session.Триггер вызывается только для неблокированных сеансов и вставляет соответствующий заблокированный сеанс.

Функция триггера:

CREATE OR REPLACE FUNCTION add_block()
  RETURNS TRIGGER AS
$BODY$
BEGIN
    INSERT INTO example (name, session, status)
    VALUES (NEW.name
           ,(SELECT block_session
             FROM   block_rules
             WHERE  session = NEW.session)
           ,'blocked');

    RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;

Триггер:

CREATE TRIGGER add_block
AFTER INSERT -- OR UPDATE
ON example
FOR EACH ROW
WHEN (NEW.status IS DISTINCT FROM 'blocked')
EXECUTE PROCEDURE add_block();

Ответна оригинальный вопрос

Есть еще возможности для улучшения.Рассмотрим эту настройку:

CREATE OR REPLACE FUNCTION add_block()
  RETURNS TRIGGER AS
$BODY$
BEGIN

INSERT INTO example (name, session, status)
VALUES (NEW.name, NEW.session + 2, 'blocked');

RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;


CREATE TRIGGER add_block
AFTER INSERT -- OR UPDATE
ON example
FOR EACH ROW
WHEN (NEW.session < 3)
-- WHEN (status IS DISTINCT FROM 'blocked') -- alternative guess at filter
EXECUTE PROCEDURE add_block();

Основные точки:

  • Для PostgreSQL 9.0 или новее вы можете использовать WHENусловие в определении триггера .Это было бы наиболее эффективным.Для более старых версий вы используете то же условие внутри функции триггера.

  • Нет необходимости добавлять столбец , если вы можете определить критерии для распознавания автоматически вставляемыхстрок.Вы не сказали, поэтому я предполагаю, что только автоматически вставленные строки имеют session > 2 в моем примере.Я добавил альтернативное WHEN условие для status = 'blocked' в качестве комментария.

  • Вы должны всегда предоставить список столбцов для INSERT.Если вы этого не сделаете, более поздние изменения в таблице могут иметь неожиданные побочные эффекты!

  • Не не вставьте NEW.id + 1 в триггер вручную.Это не будет увеличивать последовательность , а следующее INSERT завершится с ошибкой с дублированием ключа.
    id - это столбец serial, поэтому ничего не делайте.Значение по умолчанию nextval() из последовательности вставляется автоматически.

  • В вашем описании упоминается только INSERT, но у вас есть триггер AFTER INSERT OR UPDATE.Я вырезал UPDATE часть.

  • Ключевое слово plpgsql не нужно заключать в кавычки.

0 голосов
/ 07 марта 2012
CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
    SET SESSION session_replication_role = replica;
    INSERT INTO example VALUES (NEXTVAL('example_id_seq'::regclass), NEW.name, NEW.session+2, 'blocked');
    SET SESSION session_replication_role = origin;
    RETURN NULL;
 END;
 $$ LANGUAGE 'plpgsql';
...