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
是一个简单的函数,它只是将消息记录(插入)到另一个表中。
当我运行此存储过程时,我期望更新和日志记录由 即使第二笔交易失败,第一笔交易仍要保留在数据库中。
但这不是发生的情况,两个事务似乎都已回滚。
还有2个对我非常重要的问题。 :
当发生错误(例如,在标记为***
的行上)并且控件到达/跳到EXCEPTION
块时,我感觉到我所在的事务已经被滚动在我到达EXCEPTION
区块之前返回。
因此,在异常块中,我无法执行ROLLBACK
或COMMIT
或其他任何操作
与交易有关。感觉正确吗?
说我想将所有完成的工作都提交,尽管有错误,我有办法吗?
那正是我想要的。错误是错误...可以,但是我想要一切
这是在我犯下要提交的错误之前发生的。
如何在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$;
答案 0 :(得分:1)
该错误恰好在您的过程开始时在语句中出现
START TRANSACTION;
使用这些命令结束交易后,新的交易会自动开始,因此没有单独的
START TRANSACTION
命令。
那应该回答您的第一个问题。
关于第二个,当您在异常分支中时,您已经有效地回滚了从属于BEGIN
子句的EXCEPTION
开始的子事务(或在最后一个{{1之后) }})。不过,您仍在交易中,因此可以发出COMMIT
和COMMIT
。
第三个问题:不,没有办法提交“直到最后一个例外的一切”。您只能通过将每个语句包装在ROLLBACK
块中来实现此目的,但这会严重损害您的性能(除了使代码不可读之外)。
每当您希望语句可能失败时,请明智地使用BEGIN ... EXCEPTION ... END
块。