触发错误时回滚事务

问题描述 投票:2回答:2

我正在尝试检查要插入系统的房间在那个日期是否已经出租。我已经计算过与房间号和日期都匹配的行,然后回滚了事务。但是,即使我更改了代码以引发用户定义的异常,也遇到了以下错误:

ERROR:  cannot begin/end transactions in PL/pgSQL
HINT: Use a BEGIN block with an EXCEPTION clause instead.
CONTEXT: PL/pgSQL function "checkRoom"() line 17 at SQL statement
CREATE OR REPLACE FUNCTION "checkRoom"() RETURNS TRIGGER AS
$BODY$
DECLARE 
    counter integer;
  BEGIN
    SELECT COUNT("num_sesion")
    FROM "Sesion"
    INTO counter
    WHERE "Room_Name"=NEW."Room_Name" AND "Date"=NEW."Date";

    IF (counter> 0) THEN -- Probably counter>1 as it's triggered after the transaction..
        raise notice 'THERE'S A ROOM ALREADY!!';
        raise exception 'The room is rented at that date';
    END IF;
    RETURN new;
EXCEPTION
    WHEN raise_exception THEN
        ROLLBACK TRANSACTION;
        RETURN new;
END;$BODY$
LANGUAGE plpgsql VOLATILE NOT LEAKPROOF;

然后创建触发器:

CREATE TRIGGER "roomOcupied" AFTER INSERT OR UPDATE OF "Room_Name", "Date"
ON "Sesion" FOR EACH ROW
EXECUTE PROCEDURE "checkRoom"();

距离我上一次使用SQL的方法已经两年了,plsql和plpgsql之间的变化使我发疯。

sql postgresql triggers plpgsql unique-constraint
2个回答
3
投票

PostgreSQL处理错误的方式与其他数据库明显不同。任何未处理的错误都会引发给用户。在PL / pgSQL中,您可以捕获任何异常,也可以引发任何异常,但是您不能显式控制事务。任何PostgreSQL语句都在事务内部执行(也有功能)。当任何未处理的异常到达顶部时,最外部的事务将自动中断。

您可以做什么:

  • 引发异常(通常在触发器中)

BEGIN
  IF CURRENT_USER <> 'Admin' THEN
    RAISE EXCEPTION 'missing admin rights';
  END IF;
  RETURN NEW;
END;
  • 陷阱例外
BEGIN
  BEGIN -- start of protected section
    -- do some, what can be stopped by exception
  EXCEPTION WHEN divide_by_zero THEN
    -- exception handler;
    RAISE WARNING 'I was here';
    -- should ignore
  EXCEPTION WHEN others THEN
    -- any unexpected exception
    RAISE WARNING 'some unexpected issue';
    RAISE; -- forward exception'
  END;

没有其他可能性-因此在PL / pgSQL中编写应用程序非常简单,但是与PL / SQL或TSQL不同。


3
投票

触发功能有几个问题:

  • 使用IF EXISTS (...) THEN,而不是计算所有出现的次数。更快,更简单。参见:

    • IF EXISTS (...) THEN
  • 触发函数PL/pgSQL checking if a row exists AFTER只能返回INSERT OR UPDATENULL仅与称为RETURN NEW的触发器有关。 BEFORE

    对于操作后触发的行级触发器,返回值将被忽略,因此它们可以返回The manual

  • 不平衡单引号。

  • 作为NULL,您无法在plpgsql函数中控制事务。任何未处理的异常都将强制您的整个事务自动回滚。因此,只需删除@Pavel explained块。

您的假设触发器被重写:

EXCEPTION

A CREATE OR REPLACE FUNCTION check_room() RETURNS TRIGGER AS $func$ BEGIN IF EXISTS ( SELECT FROM "Sesion" -- are you sure it's not "Session"? WHERE "Room_Name" = NEW."Room_Name" AND "Date" = NEW."Date") THEN RAISE EXCEPTION 'The room is rented at that date'; END IF; RETURN NULL; END $func$ LANGUAGE plpgsql; 触发器更有意义。

但是 BEFORE将更有效地执行相同操作。然后,任何违反的行都会引发重复的键异常并回滚该事务(除非被捕获并处理)。在现代的Postgres中,您可以选择使用UNIQUE INDEX ON ("Room_Name", "Date")跳过或转移此类INSERT尝试。参见:

  • INSERT ... ON CONFLICT ...

高级用法:

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.