Postgresql中不支持事务块中调用plpgsql回滚(多层exception、事务块有检查点)
在PostgreSQL中,如果你在PL/pgSQL的事务块中遇到了这样的问题,即在一个嵌套的事务块中,外层事务块设置了SAVEPOINT,并且在内层事务块中使用了EXCEPTION来处理异常,并且在内层事务块中也有ROLLBACK操作,那么可能会遇到一个问题,即内层的ROLLBACK可能会影响到外层事务块的状态。
这是因为在PostgreSQL中,一旦你执行了ROLLBACK,它会回滚到最近的SAVEPOINT,而不仅仅是内层事务块的SAVEPOINT。这就导致了所谓的“不支持事务块中调用plpgsql回滚”的问题。
解决方案:
- 使用PL/pgSQL中的EXCEPTION来捕获内层事务块中的异常,并在内层事务块结束后再做ROLLBACK操作。
- 使用PL/pgSQL中的控制结构,如LOOP或者BEGIN ... END来代替事务块的嵌套。
- 使用PL/pgSQL中的动态SQL,在EXECUTE命令中写入ROLLBACK,并在内层事务块中捕获SQLSTATE异常。
示例代码:
DO $$
DECLARE
outer_savepoint_name TEXT := 'outer_savepoint';
inner_savepoint_name TEXT := 'inner_savepoint';
BEGIN
-- 设置外层SAVEPOINT
SAVEPOINT outer_savepoint_name;
-- 内层事务块
BEGIN
-- 设置内层SAVEPOINT
SAVEPOINT inner_savepoint_name;
-- 这里是可能会抛出异常的代码
-- ...
-- 如果没有异常发生,则正常COMMIT
-- COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- 处理异常
RAISE NOTICE 'Caught an exception: %', SQLERRM;
-- 如果发生异常,则内层ROLLBACK
ROLLBACK TO inner_savepoint_name;
END;
-- 外层事务块结束,如果需要的话,可以再做一次COMMIT
-- COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- 处理外层事务块中的异常
RAISE NOTICE 'Caught an exception in outer block: %', SQLERRM;
-- 如果发生异常,则外层ROLLBACK
ROLLBACK TO outer_savepoint_name;
END $$;
在这个示例中,我们首先设置了一个外层的SAVEPOINT,然后开始了一个内层的事务块。在内层事务块中,我们捕获所有的异常,并在异常处理代码中执行ROLLBACK TO inner\_savepoint\_name,这样只会回滚到内层的SAVEPOINT,而不会影响到外层的SAVEPOINT。最后,在外层事务块中捕获任何可能发生的异常,并执行相应的ROLLBACK。这样就可以在嵌套事务中正确地使用ROLLBACK,而不会影响到外层的事务。
评论已关闭