PostgreSQL Oracle兼容性:函数 自治事务 的实现
发布日期:2016-4-24 21:4:13
使用Oracle的用户,在函数中如果使用了自治事务的话,如果要转到PostgreSQL会遇到很棘手的问题。 因为PostgreSQL的函数是作为一个事务来处理的,要么全部提交,要么全部回滚, 除了exception,每个exception是一个子事务。 因此使用exception可以达到自治事务的目的。 例子1: 使用并行block和嵌套block,来控制子事务层级。 输入参数为block1, block2.1, block2.2, block3.1 。 这些参数代表执行在哪个block出错,出错时对应层级的block的exception会捕获错误,同时处理,然后跳到下一个block继续执行。 如果是外层的block出错,内层还没有被执行的block就没机会执行了。 根据业务需求,调整block层级或嵌套层级,达到目的。 这种用法可以完美的支撑业务的需求。 (除了一种情况不能满足,就是被提交的子事务立刻可以被其他事务可见。这种需求建本文下面的方法,用dblink来满足这种需求即可。) create or replace function ft(err_level text) returns void as $$ declare begin -- block level 1 raise notice 'block level 1'; if (err_level='block1') then raise exception '%', err_level; end if; begin -- block level 2.1 raise notice 'block level 2.1'; -- 请用业务处理SQL代替 if (err_level='block2.1') then raise exception '%', err_level; end if; begin -- block level 3.1 raise notice 'block level 3.1'; if (err_level='block3.1') then raise exception '%', err_level; end if; exception when others then -- you can write catchup any ERROR CODE or ERROR STATE. raise notice 'end block level 3.1'; end; -- end block level 3.1 exception when others then -- you can write catchup any ERROR CODE or ERROR STATE. 回滚block 2.1的业务处理SQL raise notice 'end block level 2.1'; end; -- end block level 2.1 begin -- block level 2.2 raise notice 'block level 2.2'; if (err_level='block2.2') then raise exception '%', err_level; end if; exception when others then -- you can write catchup any ERROR CODE or ERROR STATE. raise notice 'end block level 2.2'; end; -- end block level 2.2 exception when others then -- you can write catchup any ERROR CODE or ERROR STATE. raise notice 'end block level 1'; end; -- end block level 1 $$ language plpgsql; 测试: 在block 1出错,出错代码后面的代码都不会被执行,如下所示: postgres=# select ft('block1'); NOTICE: block level 1 NOTICE: end block level 1 ft ---- (1 row) 在block2.1出错,block 2.1内部出错代码后面的代码都不会被执行。但是同级代码如2.2会被执行。 postgres=# select ft('block2.1'); NOTICE: block level 1 NOTICE: block level 2.1 NOTICE: end block level 2.1 NOTICE: block level 2.2 ft ---- (1 row) 在block2.2出错。 postgres=# select ft('block2.2'); NOTICE: block level 1 NOTICE: block level 2.1 NOTICE: block level 3.1 NOTICE: block level 2.2 NOTICE: end block level 2.2 ft ---- (1 row) 在block3.1出错,如下所示: postgres=# select ft('block3.1'); NOTICE: block level 1 NOTICE: block level 2.1 NOTICE: block level 3.1 NOTICE: end block level 3.1 NOTICE: block level 2.2 ft ---- (1 row) 更直观的例子: drop table tt; create table tt(id int primary key, info text); insert into tt values(5,'test'); create or replace function ft() returns void as $$ declare begin -- block level 1 begin -- block level 2.1 insert into tt values (1,'test'),(2,'test'),(3,'test'); exception when others then raise notice 'rollback block level 2.1'; end; -- end block level 2.1 begin -- block level 2.2 insert into tt values (4,'test'),(5,'test'),(6,'test'); -- 主键冲突, 插入失败, 但是不影响后面的block继续执行. exception when others then raise notice 'rollback block level 2.2'; end; -- end block level 2.2 begin -- block level 2.3 insert into tt values (7,'test'),(8,'test'),(9,'test'); exception when others then raise notice 'rollback block level 2.3'; end; -- end block level 2.3 exception when others then -- you can write catchup any ERROR CODE or ERROR STATE. raise notice 'rollback block level 1'; end; -- end block level 1 $$ language plpgsql; postgres=# select ft(); NOTICE: rollback block level 2.2 ft ---- (1 row) postgres=# select * from tt; id | info ----+------ 5 | test 1 | test 2 | test 3 | test 7 | test 8 | test 9 | test (7 rows) 例子2: 使用dblink,同样需要将需要批量提交的部分写成子函数先。 例如 : create extension dblink; CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr '127.0.0.1', dbname '函数所在的库名'); CREATE USER MAPPING FOR 需要调用函数的用户名 SERVER fdtest OPTIONS (user '需要调用函数的用户名', password '用户密码'); GRANT USAGE ON FOREIGN SERVER fdtest TO 需要调用函数的用户名; 函数体: declare dblink_block_res1 record; dblink_block_res2 record; ... dblink_block_resn record; ... 其他变量定义; begin -- 建立连接 if ( dblink_connect('myconn', 'fdtest') <> 'OK' ) then raise notice '连接失败'; return; end if; -- block 1,需要流转的变量通过参数传入下面的函数 select dblink('myconn', 'select func1($1,$2,...)') into dblink_block_res1; -- $1,$2,...使用常数替代, 或动态SQL -- 中间结果判断 if not found then -- dblink调用异常 -- 异常处理 else -- dblink对应子事务已提交 end if; -- block 2,需要流转的变量通过参数传入下面的函数 select dblink('myconn', 'select func2($1,$2,...)') into dblink_block_res2; -- $1,$2,...使用常数替代, 或动态SQL -- 中间结果判断 if not found then -- 远程调用异常 -- 异常处理 else -- dblink对应子事务已提交 end if; ...... -- block n,需要流转的变量通过参数传入下面的函数 select dblink('myconn', 'select funcn($1,$2,...)') into dblink_block_resn; -- $1,$2,...使用常数替代, 或动态SQL -- 中间结果判断 if not found then -- 远程调用异常 -- 异常处理 else -- dblink对应子事务已提交 end if; ...... exception when others then ... end; 其他参考地址: http://www.postgresql.org/docs/9.5/static/contrib-dblink-function.html https://lwn.net/Articles/648973/ http://postgresql.nabble.com/Autonomous-Transaction-WIP-td5798928.html 上一条: 关于sql语句的总结 下一条: mysql死锁问题
|