2013-05-08 8 views
1

после миграции с сервера PostgreSQL версии 9 на 8.4 Я столкнулся с очень странной ошибкой.Триггер PostgreSQL вызывает ошибку 55000

Краткое описание:
Если есть триггер на данной таблице для каждой строки перед тем вставки или обновления и один использует в условном операторе (если-то еще) Контрольное значение TG_OP и OLD объект, следующий ошибка возникает, когда doinng ВСТАВКУ:

ERROR: record "old" is not assigned yet 
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate. 

Подробное описание:
Существует следующая структура БД:

CREATE TABLE table1 
(
    id serial NOT NULL, 
    name character varying(256), 
    CONSTRAINT table1_pkey PRIMARY KEY (id) 
) 
WITH (OIDS=FALSE); 

CREATE OR REPLACE FUNCTION exemplary_function() 
RETURNS trigger AS 
$BODY$ BEGIN 
IF TG_OP = 'INSERT' OR OLD.name <> NEW.name THEN 
    NEW.name = 'someName'; 
    END IF; 

RETURN NEW; 
END; 
$BODY$ 
LANGUAGE plpgsql VOLATILE COST 100; 

CREATE TRIGGER trigger1 
    BEFORE INSERT OR UPDATE 
    ON table1 
FOR EACH ROW EXECUTE PROCEDURE exemplary_function(); 

и после SQL-запрос, который вызывает ошибку:

INSERT INTO table1 (name) VALUES ('other name') 

Похоже, анализатор не останавливается на TG_OP = 'INSERT' состоянии (и это должно, потому что это правда), но проверяет другую и вызывает ошибку. Что интересно, я смог воспроизвести его только на версии 8.4.

+0

OLD не существует для инструкций INSERT. (Вы уже это знали, другие, посетившие здесь, не могли). Для тестирования может потребоваться переписать, чтобы сделать отдельные условные инструкции 'TG_OP = 'INSERT' и' OLD.name <> NEW.name'. То есть, переместите 'OLD.name <> NEW.name' в предложение ELSE или в предложение ELSEIF. –

+0

@ MikeSherrill'Catcall ', вы правы, но в этом все дело. Ввод его в отдельные условности делает работу, но дело в том, чтобы все это было в одном. В реальном живом примере я сделал это как временное решение, но мне пришлось скопировать весь блок кода ... – Wiktor

+0

Если это сработало, мой следующий тест может заключаться в том, чтобы добавить parens, чтобы гарантировать правильный приоритет: 'IF (TG_OP = 'INSERT ') ИЛИ (OLD.name <> NEW.name) THEN'. Сказав это, я не знаю о приоритетных ошибках в 8.4, но раньше я не знал о таких вещах. –

ответ

1

Postgres не officially делать короткие надрезы на логических утверждений (в отличие от C, например)

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

В основном он рассматривает, насколько сложны выражения на каждой стороне, прежде чем принимать решение о порядке оценки. Тогда, если это ИСТИНА, он может решить не беспокоиться о другой стороне.

В этом случае это похоже на попытку интерпретировать OLD, пока он все еще пытается решить лучший порядок оценки выражения.

Вы можете обойти это, используя CASE для разделения выражений, например.

IF (CASE WHEN TG_OP = 'INSERT' THEN TRUE ELSE OLD.name <> NEW.name END) THEN 
+0

Очень интересно и полезно! Спасибо, Гэри :) – Wiktor

Смежные вопросы