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

本文涉及的产品
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
简介: 使用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数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
6月前
|
SQL 关系型数据库 MySQL
MySQL锁机制:并发控制与事务隔离
本文深入解析了MySQL的锁机制与事务隔离级别,涵盖锁类型、兼容性、死锁处理及性能优化策略,助你掌握高并发场景下的数据库并发控制核心技巧。
|
7月前
|
存储 监控 Oracle
MySQL事务
MySQL事务具有ACID特性,包括原子性、一致性、隔离性和持久性。其默认隔离级别为可重复读,通过MVCC和间隙锁解决幻读问题,确保事务间数据的一致性和并发性。
MySQL事务
|
8月前
|
存储 SQL 关系型数据库
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
|
5月前
|
关系型数据库 MySQL 数据库
【赵渝强老师】MySQL的事务隔离级别
数据库并发访问时易引发数据不一致问题。如客户端读取到未提交的事务数据,可能导致“脏读”。MySQL通过四种事务隔离级别(读未提交、读已提交、可重复读、可序列化)控制并发行为,默认为“可重复读”,以平衡性能与数据一致性。
361 0
|
6月前
|
关系型数据库 MySQL 数据库
MySql事务以及事务的四大特性
事务是数据库操作的基本单元,具有ACID四大特性:原子性、一致性、隔离性、持久性。它确保数据的正确性与完整性。并发事务可能引发脏读、不可重复读、幻读等问题,数据库通过不同隔离级别(如读未提交、读已提交、可重复读、串行化)加以解决。MySQL默认使用可重复读级别。高隔离级别虽能更好处理并发问题,但会降低性能。
241 0
|
8月前
|
安全 关系型数据库 MySQL
mysql事务隔离级别
事务隔离级别用于解决脏读、不可重复读和幻读问题。不同级别在安全与性能间权衡,如SERIALIZABLE最安全但性能差,READ_UNCOMMITTED性能高但易导致数据不一致。了解各级别特性有助于合理选择以平衡并发性与数据一致性需求。
231 1
|
10月前
|
Oracle 关系型数据库 数据库
【赵渝强老师】在PostgreSQL中访问Oracle
本文介绍了如何在PostgreSQL中使用oracle_fdw扩展访问Oracle数据库数据。首先需从Oracle官网下载三个Instance Client安装包并解压,设置Oracle环境变量。接着从GitHub下载oracle_fdw扩展,配置pg_config环境变量后编译安装。之后启动PostgreSQL服务器,在数据库中创建oracle_fdw扩展及外部数据库服务,建立用户映射。最后通过创建外部表实现对Oracle数据的访问。文末附有具体操作步骤与示例代码。
816 6
【赵渝强老师】在PostgreSQL中访问Oracle
|
SQL 安全 关系型数据库
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
事务是MySQL中一组不可分割的操作集合,确保所有操作要么全部成功,要么全部失败。本文利用SQL演示并总结了事务操作、事务四大特性、并发事务问题、事务隔离级别。
5403 56
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
|
SQL 存储 Oracle
【YashanDB知识库】Oracle pipelined函数在YashanDB中的改写
【YashanDB知识库】Oracle pipelined函数在YashanDB中的改写
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
797 7
MySQL事务日志-Undo Log工作原理分析

相关产品

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

    更多