事务在 PostgreSQL 11 存储过程中无法正常工作

Transactions not working OK in a PostgreSQL 11 stored procedure

Postgres 版本:
x86_64-pc-linux-gnu、
上的 PostgreSQL 11.0 由 gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9) 编译,64 位

我有如下所示的存储过程。这只是一个测试。 在该过程中,我有 2 笔交易:

此外,yb.print_now 是一个简单的函数,它只是将消息记录(插入)到另一个 table。

当我 运行 这个存储过程时,我期待由 第一个事务要保存在数据库中,即使第二个事务失败。

但事实并非如此,两个事务似乎都被回滚了。

  1. 这是为什么?难道我做错了什么?

还有 2 个对我来说非常重要的问题。 :

  1. 当发生错误时(比如在标记为***的行上)并且当控制reaches/jumps到EXCEPTION块时,我的感觉是交易在我到达 EXCEPTION 块之前,我已经回滚了。 所以在异常块中我不能做 ROLLBACKCOMMIT 或任何事情 与交易有关。这种感觉对吗?

  2. 假设我想提交所有已完成的事情,尽管有错误,有什么办法可以做到吗?
    这正是我想要的。错误是错误...好吧,但我想要一切 这发生在我得到提交错误之前。

如何在 Postgres 11 中执行此操作?

    CREATE OR REPLACE PROCEDURE yb.test123()
     LANGUAGE plpgsql
    AS $procedure$ 

    DECLARE
        var_cnt int;
        c int;
    BEGIN

        START TRANSACTION; --- 1 ---

        raise notice '001.';

        PERFORM yb.print_now('===> 0010.');

        var_cnt = 0;

        update yb.mbb
        set the_price = the_price + 1   
        where 
        the_id = 23164;

        raise notice '002.';
        PERFORM yb.print_now('===> 0020.');

        raise notice '003.';
        PERFORM yb.print_now('===> 0030.');

        update yb.mbb
        set the_price = the_price + 1 
        where 
        the_id = 23164;

        COMMIT; --- 1 ---

        START TRANSACTION; --- 2 --- 

        c = cast('###a1e3Z' as int);  --- *** --- 

        raise notice '004.';
        PERFORM yb.print_now('===> 0040.');

        update yb.mbb
        set the_price = the_price + 1 
        where 
        the_id = 23164;

        -- insert into yb.mbb(the_id)
        -- values (23164); -- this will throw duplicate PK error

        raise notice '005.';
        PERFORM yb.print_now('===> 0050.');

        COMMIT; --- 2 ---



    EXCEPTION
        WHEN OTHERS THEN

        raise notice 'We are in the exception block now.';
        -- ROLLBACK;
        -- COMMIT;

        RETURN;

    END

    $procedure$;

错误发生在程序开始时,在语句中

START TRANSACTION;

正如the documentation所说:

A new transaction is started automatically after a transaction is ended using these commands, so there is no separate START TRANSACTION command.

这应该回答了你的第一个问题。

关于第二个,当你在异常分支中时,你已经有效地回滚了以属于 EXCEPTION 子句的 BEGIN 开始的子事务(或在最后一个 COMMIT).不过您仍在交易中,因此您可以发出 COMMITROLLBACK.

对于你的第三个问题:不,没有办法提交“直到最后一个例外的一切”。您只能通过将 每个 语句包装在 BEGIN ... EXCEPTION ... END 块中来实现它,但这会严重损害您的性能(除了使您的代码不可读之外)。

每当您预计语句可能会失败时,请明智地使用 BEGIN ... EXCEPTION ... END 块。