PostgreSQL存储过程中怎么利用BEGIN块处理事务,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
1、用BEGIN块模拟
随便建个表:
CREATE TABLE a(col1 int);
存储过程如下:
CREATE OR REPLACE FUNCTION ins_t() RETURNS void AS $$ BEGIN INSERT INTO a VALUES(100); BEGIN INSERT INTO a VALUES(200); RAISE 'any error'; EXCEPTION WHEN others THEN null; END; END; $$ LANGUAGE plpgsql;
测试:
flying=# SELECT ins_t(); ins_t ------- (1 row) flying=# SELECT * FROM a; col1 ------ 100 (1 row)
可以看到,内嵌BEGIN块中的INSERT操作并没有生效。
2、PL/pgSQL如何实现
块定义在pl_gram.y中,有兴趣可以自己去看,块的执行代码位于 src/pl/plpgsql/src/pl_exec.c
。
static int exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) { ... if (block->exceptions) { ... BeginInternalSubTransaction(NULL); ... PG_TRY(); ... PG_CATCH(); ... RollbackAndReleaseCurrentSubTransaction(); ...
只有当块定义有EXCEPTION部分,才会出现创建子事务的动作,否则(它对应的else分支内)只会当做普通语句处理。抛出例外时,就把这个子事务会滚掉。
而如果不抛出例外,而是WARNING或者NOTICE之类,也不会引起它的回滚,尝试一下就知道。PG怎么知道要不要回滚,关键在PG_TRY,这个说起来话长,暂时了解到这里足够。
3、没有EXCEPTION处理会怎样
去掉例外处理部分
CREATE OR REPLACE FUNCTION ins_t() RETURNS void AS $$ BEGIN INSERT INTO a VALUES(100); BEGIN INSERT INTO a VALUES(200); RAISE 'any error'; END; END; $$ LANGUAGE plpgsql;
结果什么都没有,因为它没有启用子事务,所以会全部回滚。
flying=# select ins_t(); ERROR: any error CONTEXT: PL/pgSQL function ins_t() line 6 at RAISE flying=# select * from a; col1 ------ (0 rows) flying=#
4、神奇的PG_TRY和PG_CATCH
它们用到的是sigsetjmp,PG中很多地方用到它,了解这个可能需要一定的汇编语言知识,也需要了解错误抛出机制,有机会再详细讲。
看完上述内容,你们掌握PostgreSQL存储过程中怎么利用BEGIN块处理事务的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。