事务在 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 笔交易:
第一个应该可以完成(即我已经编写了代码
这样它就不会遇到任何错误,因此它会到达 COMMIT
语句)。
第二笔交易应该会失败,因为我是故意的
在其中引入错误(通过那里的 cast
,或通过导致 PK 违规的 INSERT
)。
此外,yb.print_now
是一个简单的函数,它只是将消息记录(插入)到另一个 table。
当我 运行 这个存储过程时,我期待由
第一个事务要保存在数据库中,即使第二个事务失败。
但事实并非如此,两个事务似乎都被回滚了。
- 这是为什么?难道我做错了什么?
还有 2 个对我来说非常重要的问题。 :
当发生错误时(比如在标记为***
的行上)并且当控制reaches/jumps到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$;
错误发生在程序开始时,在语句中
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
).不过您仍在交易中,因此您可以发出 COMMIT
和 ROLLBACK
.
对于你的第三个问题:不,没有办法提交“直到最后一个例外的一切”。您只能通过将 每个 语句包装在 BEGIN ... EXCEPTION ... END
块中来实现它,但这会严重损害您的性能(除了使您的代码不可读之外)。
每当您预计语句可能会失败时,请明智地使用 BEGIN ... EXCEPTION ... END
块。
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
是一个简单的函数,它只是将消息记录(插入)到另一个 table。
当我 运行 这个存储过程时,我期待由 第一个事务要保存在数据库中,即使第二个事务失败。
但事实并非如此,两个事务似乎都被回滚了。
- 这是为什么?难道我做错了什么?
还有 2 个对我来说非常重要的问题。 :
当发生错误时(比如在标记为
***
的行上)并且当控制reaches/jumps到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$;
错误发生在程序开始时,在语句中
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
).不过您仍在交易中,因此您可以发出 COMMIT
和 ROLLBACK
.
对于你的第三个问题:不,没有办法提交“直到最后一个例外的一切”。您只能通过将 每个 语句包装在 BEGIN ... EXCEPTION ... END
块中来实现它,但这会严重损害您的性能(除了使您的代码不可读之外)。
每当您预计语句可能会失败时,请明智地使用 BEGIN ... EXCEPTION ... END
块。