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

简介: 使用Oracle的用户,在函数中如果使用了自治事务的话,如果要转到PostgreSQL会遇到很棘手的问题。 因为PostgreSQL的函数是作为一个事务来处理的,要么全部提交,要么全部回滚,除了exception,每个exception是一个子事务。 使用exception确实可以达到类似自治事

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

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
SQL Oracle 算法
|
SQL Oracle 关系型数据库
【赵渝强老师】Oracle的闪回事务查询
Oracle数据库的闪回事务查询(Flashback Transaction Query)是闪回版本查询的扩充,可用于审计或撤销已提交的事务。通过`flashback_transaction_query`视图,可生成还原特定事务的SQL语句。本文介绍了其基本概念,并通过实战演示如何使用该功能:从授权、开启UNDO数据增强,到创建测试表和事务,最后利用闪回查询撤销已提交的事务,验证数据恢复效果。附带视频讲解,帮助深入理解。
562 3
|
SQL 存储 Oracle
【YashanDB知识库】Oracle pipelined函数在YashanDB中的改写
【YashanDB知识库】Oracle pipelined函数在YashanDB中的改写
|
SQL 存储 Oracle
【YashanDB观点】论Oracle兼容性,我们需要做什么
我们经常发现,部分国产数据库声称与 Oracle兼容性高达90%,但在实际迁移过程中,仍需要频繁地修改业务应用的代码。为何实现与Oracle高兼容度的数据库产品如此困难?其中一个重要原因是Oracle兼容性不仅是模仿,而是一个非常复杂和工程量庞大的逆向工程。其技术实现的复杂性以及多如牛毛的细节,足以让多数“年轻”的数据库团队望洋兴叹。YashanDB作为一款从核心理论到关键系统均为原创的数据库产品,从构建初期就具备了技术优势,在Oracle兼容性实现上,敢于亮剑并充分发挥工匠精神,不断打磨,努力构筑一个真正形神兼备的数据库产品。以下将从YashanDB SQL引擎技术、Oracle兼容性的开发
|
SQL 存储 Oracle
【YashanDB观点】论Oracle兼容性,我们需要做什么
Oracle兼容性是目前国产数据库的关键任务之一,其直接影响到商业迁移的成本和竞争力。
432 8
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
4240 18
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
SQL Oracle 关系型数据库
|
SQL Oracle 关系型数据库
|
Oracle 关系型数据库 数据挖掘
|
存储 关系型数据库 测试技术
拯救海量数据:PostgreSQL分区表性能优化实战手册(附压测对比)
本文深入解析PostgreSQL分区表的核心原理与优化策略,涵盖性能痛点、实战案例及压测对比。首先阐述分区表作为继承表+路由规则的逻辑封装,分析分区裁剪失效、全局索引膨胀和VACUUM堆积三大性能杀手,并通过电商订单表崩溃事件说明旧分区维护的重要性。接着提出四维设计法优化分区策略,包括时间范围分区黄金法则与自动化维护体系。同时对比局部索引与全局索引性能,展示后者在特定场景下的优势。进一步探讨并行查询优化、冷热数据分层存储及故障复盘,解决分区锁竞争问题。

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版
  • 推荐镜像

    更多