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

时间:2019-03-27 13:45:14

标签: postgresql stored-procedures plpgsql

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

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

  • 第一个应该可以完成(即,我已经编写了代码 这样它就不会遇到任何错误,因此可以到达COMMIT语句)。

  • 第二笔交易应该失败,因为我故意 引入错误(通过此处的cast或导致PK违反的INSERT)。

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

当我运行此存储过程时,我期望更新和日志记录由 即使第二笔交易失败,第一笔交易仍要保留在数据库中。

但这不是发生的情况,两个事务似乎都已回滚。

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

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

  1. 当发生错误(例如,在标记为***的行上)并且控件到达/跳到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$;

1 个答案:

答案 0 :(得分:1)

该错误恰好在您的过程开始时在语句中出现

START TRANSACTION;

the documentation说:

  

使用这些命令结束交易后,新的交易会自动开始,因此没有单独的START TRANSACTION命令。

那应该回答您的第一个问题。

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

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

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