ИСКЛЮЧЕНИЕ, не вызванное в триггерной функции, запускаемой при УДАЛЕНИИ ИЗ (postgresql 13)

#sql #postgresql

#sql #postgresql

Вопрос:

У меня есть две таблицы:

 CREATE TABLE Registered(student TEXT, course CHAR(6))


CREATE TABLE WaitingList(student TEXT, course CHAR(6), position INT)
 

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

У меня также есть представление

 CREATE VIEW Registrations(student, course, status)
 

Которое представляет собой объединение двух таблиц со статусом либо ожидающий, либо зарегистрированный в зависимости от того, из какой таблицы происходит пара (студент, курс).

Затем я создаю триггер и функцию:

  CREATE FUNCTION unregistration_function() RETURNS TRIGGER AS $
    BEGIN
        IF (EXISTS(SELECT * FROM WaitingList WHERE student = OLD.student AND course = OLD.course)) THEN
              --delete OLD.student from WaitingList and then update WaitingList position;

        ELSIF (EXISTS(SELECT * FROM Registered WHERE student = OLD.student AND course = OLD.course)) THEN
              --delete OLD.student from Registered;
        
        ELSE 
              RAISE EXCEPTION 'Student % is not registered or on waitinglist for course %', OLD.student, OLD.course;

        END IF;
        RETURN OLD;
     END;
$ LANGUAGE plpgsql;

CREATE TRIGGER unregistration 
    INSTEAD OF DELETE FROM Registrations
    FOR EACH ROW 
    EXECUTE PROCEDURE unregistration_function();
 

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

Я не уверен, чего мне не хватает, так как условие WHERE должно возвращать false в предложениях IF и ELSIF в этом случае, и исключение должно быть вызвано. Насколько я понимаю из документации, создание ИСКЛЮЧЕНИЯ должно прервать текущую транзакцию?

Просто ли из-за того, что это оператор УДАЛЕНИЯ, прерывание транзакции приводит к тому, что DELETE просто завершается с 0 удаленными строками, которые по-прежнему считаются «успешными» и, следовательно, не отображают мое сообщение об ошибке?

Комментарии:

1. Попробуйте IS NOT DISTINCT FROM вместо = этого для сравнения.

Ответ №1:

Вызывается DELETE для каждой строки, которая будет удалена.

Если вы выполните:

 delete from registrations
    where student = XXX;
 

и student отсутствует в таблице или представлении, тогда триггер не вызывается.

Вам нужно посмотреть на количество удаленных строк, чтобы увидеть, было ли что-нибудь удалено.

Комментарии:

1. Конечно … так просто, и все же я пропустил это, спасибо.

2. @KnutKnutsson . , , я признаю, что потребовалось некоторое размышление, чтобы понять, что это проблема. Это тонкий момент (хотя и очевидный, когда вы его видите).