mysql自治事务_PostgreSQL Oracle兼容性:函数 自治事务 的实现

使用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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值