Quantcast
Channel: PostgreSQL research
Viewing all articles
Browse latest Browse all 253

PostgreSQL Oracle兼容性之 - 函数 自治事务 的实现

$
0
0

使用Oracle的用户,在函数中如果使用了自治事务的话,如果要转到PostgreSQL会遇到很棘手的问题。
因为PostgreSQL的函数是作为一个事务来处理的,要么全部提交,要么全部回滚,除了exception,每个exception是一个子事务。
使用并行或嵌套block和exception可以达到自治事务的目的。

建议使用例子2的用法。

例子1 : 

单个block的用法:  
你可以将原来函数中的block拆解成一个个小的函数,当然不拆解成小函数也可以的,就是这里的代码量会更大一点。

例如 :

declare
  trace int;
  其他变量定义;
begin
-- block 1,需要流转的变量通过参数传入下面的函数
  select func1(...) into ...;
  trace = 1;  -- 说明block 1的部分已经成功执行了。
-- block 2,需要流转的变量通过参数传入下面的函数
  select func2(...) into ...;
  trace = 2;  -- 说明block 2的部分已经成功执行了。
......
-- block n,需要流转的变量通过参数传入下面的函数
  select funcn(...) into ...;
  trace = n;  -- 说明block n的部分已经成功执行了。
......
-- 如果在接下来的block执行失败,会跳到异常处理部分,在异常处理中,通过trace的值来判断哪些是可以成功执行的。

exception when others then
  case trace
    case when 1 then
      call func1(...);
    case when 2 then
      call func1(...);
      call func2(...);
......
    case when n then
      call funcn(...);
......
end;

这个方法并不能cover所有的场景,因为在exception执行的时候,还是可能出错的。如果exception出错,那么后面的代码就不能被执行。


例子2,

并行加嵌套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)


比例子2更直观的例子:

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)


例子3:
使用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
http://postgresql.nabble.com/Autonomous-Transaction-WIP-td5798928.html
https://lwn.net/Articles/648973/

Flag Counter

Viewing all articles
Browse latest Browse all 253

Trending Articles