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

时间: 2024-12-22 18:01:09

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

PostgreSQL Oracle兼容性之 - plpgsql 自治事务(autonomous_transaction)补丁

PostgreSQL Oracle兼容性之 - plpgsql 自治事务(autonomous_transaction)补丁 作者 digoal 日期 2016-11-04 标签 PostgreSQL , autonomous_transaction , 自治事务 , Oracle兼容性 , plpgsql 背景 PostgreSQL的plpgsql服务端编程语言与Oracle数据库的pl/sql编程语言非常类似,但是对于自治事务一直没有语法层面的支持. 以往如果要支持自治事务,可以使用exce

PostgreSQL Oracle 兼容性 之 TABLE、PIPELINED函数

标签 PostgreSQL , 返回表 , 返回复合类型 , 返回游标 背景 Oracle 通过table, pipelined函数,用于格式化返回类型为table的函数的结果. Table function concepts There a couple of steps to take when you are working with table functions. Like when you are working with normal tables you have to desc

PostgreSQL Oracle兼容性 之 - parser SQL保留|关键字(keywrods)大全

标签 PostgreSQL , keywords , 关键字 , Oracle 兼容性 背景 PostgreSQL数据库中有哪些关键字,这些关键字的使用限制如何? https://www.postgresql.org/docs/10/static/sql-keywords-appendix.html 文档中的说明并不是特别清晰,(并且KEYWORDS与版本强相关),所以使用pg_get_keywords这个系统函数得到的,更加准确可读. https://www.postgresql.org/do

PostgreSQL Oracle 兼容性之 - timestamp 与 numeric 的运算

标签 PostgreSQL , Oracle , 兼容性 , timestamp , numeric 背景 Oracle里面支持时间戳与数字的加减,数字默认单位为天. PostgreSQL 支持时间戳与interval类型进行加减.日期支持与整型做加减. 为了兼容Oracle(时间戳与数字加减),我们可以复写操作符来实现时间戳与数字的加减. 复写操作符 1.自定义几个函数,用于时间戳与数字的加减. postgres=# create or replace function timestamp_a

PostgreSQL Oracle 兼容性之 - 系统列(ctid, oid, cmin, cmax, xmin, xmax)

标签 PostgreSQL , Oracle , 兼容性 , ctid , cmin , cmax , xmin , xmax , oid 背景 PostgreSQL中有一些系统列(即行的头部信息的列),例如物理行号,COMMAND ID,事务号,以及OID. 当我们建表时,不能使用冲突的列名,否则会报错: postgres=# create table a(ctid int); 错误: 42701: 字段名 "ctid" 与系统字段名冲突 LOCATION: CheckAttribu

PostgreSQL Oracle 兼容性之 - rowid (CREATE TABLE WITH OIDS)

标签 PostgreSQL , Oracle , 兼容性 , 行号 , rowid , oid , ctid 背景 Oracle的数据中,通过ROWID可以定位到一条记录,当记录没有发生行迁移时,ROWID是不变的,因此即使不使用PK,也能很好的定位到一条记录. PostgreSQL中,也有行号,CTID,由BLOCK_ID和ITEM_ID组成,即哪个数据块的哪条记录. 但是PostgreSQL的引擎为多版本引擎,因此一条记录在被更新后CTID会发生变化(代表了新的版本). 不管是Oracle还

Oracle中怎样用自治事务保存日志表

数据库事务是一种单元操作,要么是全部操作都成功,要么全部失败.在Oracle中,一个事务是从执行第一个数据治理语言(DML)语句开始,直到执行一个COMMIT语句,提交保存这个事务,或者执行一个ROLLBACK语句,放弃此次操作结束. 事务的"要么全部完成,要么什么都没完成"的本性会使将错误信息记入数据库表中变得很困难,因为当事务失败重新运行时,用来编写日志条目的INSERT语句还未完成. 针对这种困境,Oracle提供了一种便捷的方法,即自治事务.自治事务从当前事务开始,在其自身的语

PostgreSQL Oracle 兼容性 之 - PL/SQL record, table类型定义

背景 Oracle PL/SQL是非常强大的一门SQL编程语言,许多Oracle用户也使用它来处理一些要求延迟低且数据一致性或可靠性要求很高的业务逻辑. PostgreSQL也有一门非常高级的内置SQL编程语言,plpgsql.与Oracle PL/SQL语法极其类似,但是还是有一些不一样的地方.(PS:除了plpgsql,PostgreSQL还支持C,java,python,perl等流行的语言作为数据库的函数编程语言) 本文是针对有Oracle用户遇到的一些函数语法与PostgreSQL不兼

PostgreSQL Oracle 兼容性系列之 - orafce

PostgreSQL是和Oracle最接近的企业数据库,包括数据类型,功能,架构和语法等几个方面.甚至大多数的日常应用的性能也不会输给Oracle. 但是Oracle有些函数或者包,默认PostgreSQL是没有的,需要安装orafce包来实现这些兼容性. 例如现在orafce已经包含了如下内容. 1. 类型 date, varchar2 and nvarchar2 2. 函数 concat, nvl, nvl2, lnnvl, decode, bitand, nanvl, sinh, cosh