繁体   English   中英

触发错误时回滚事务

[英]Rollback Transaction on Trigger ERROR

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

 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 到现在已经 2 年了,plsql 和 plpgsql 之间的变化让我很抓狂。

您的触发器功能有几个问题:

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

  • INSERT OR UPDATE AFTER的触发器函数可以只返回NULL RETURN NEW仅与称为BEFORE触发器相关。 手册

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

  • 不平衡的单引号。

  • 正如@Pavel 解释的那样,您无法从 plpgsql 函数中控制事务。 任何未处理的异常都会强制您的整个事务自动回滚。 因此,只需删除EXCEPTION块。

你的假设触发器重写:

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触发器更有意义。

但是UNIQUE INDEX ON ("Room_Name", "Date")会做同样的事情,而且效率更高。 然后,任何违反的行都会引发重复键异常并回滚事务(除非被捕获和处理)。 在现代 Postgres 中,您可以使用INSERT ... ON CONFLICT ...跳过或转移此类INSERT尝试。 看:

高级用法:

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 不同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM