今天在PostgreSQL遇到一个奇怪的现象,简而言之,是想用函数(存储过程)实现插入记录,整个过程没报错但事后却没找到记录!忙活半天,才发现原因是PostgreSQL函数(存储过程)有自动COMMIT或ROLLBACK的特殊规定。

二、问题重现

以下用示例表和示例代码来重现该问题。

create table t1 
  ID int not null primary key,
  name varchar(20)
 

涉及的存储过程是从oracle那边直接拷贝过来后再修改过的,原先是动态SQL,这里简化为静态SQL。注意其中有个commit;根据PostgreSQL的要求,对事务增加begin...exception...end,否则会有错误或警告。示例脚本代码为:

create or replace function p1(pid int, pname varchar)
returns void as $$
begin
  begin             --pg对事务的要求
  insert into t1 values(pid, pname);
  commit;
  exception      
  when others then
  end;              --pg对事务的要求
end;
$$ language plpgsql;

依次执行脚本创建存储过程、调用存储过程、查找示例表,结果如下: 

postgres=# \i test1.sql
CREATE FUNCTION
postgres=# select p1(1, 'abc');
(1 行记录)
postgres=# select * from t1;

  id | name
  ----+------
  (0 行记录)

要插入的记录并不存在!惊喜不惊喜?意外不意外?

三、原因分析及解决

仔细查找有关资料,发现有这么一个解释:

Functions and trigger procedures are always executed within a transaction established by an outer 
query — they cannot start or commit that transaction, since there would be no context for them to
execute in. However, a block containing an EXCEPTION clause effectively forms a subtransaction that
can be rolled back without affecting the outer transaction.

其意义是PostgreSQL的函数总是默认为一个事务,总是自动Commit或Rollback。

其实一开始没增加begin...exception...end时,PostgreSQL报错“can't begin/end transaction  in pl/pgsql”,已经隐含了这层信息。只是脑子里还是延续Oracle的习惯,而画蛇添足了。

于是,修改存储过程的脚本,按最简单的法子来:

create or replace function p2(pid int, pname varchar)
returns void as $$
begin
  insert into t1 values(pid, pname);
end;
$$ language plpgsql;

为验证此说法是否正确,在再次创建函数、调用函数后,增加一个回滚(事先已设置AutoCommit为false)的操作,然后再查询记录:

postgres=# \i test1.sql
CREATE FUNCTION
postgres=# select p2(1, 'abc');
----
(1 行记录)
postgres=# rollback;
WARNING:  there is no transaction in progress
ROLLBACK
postgres=# select * from t1;
 id | name
----+------
  1 | abc
(1 行记录)

可见,这次记录已成功插入,且外部的回滚操作对其无影响。

Oracle是可以在存储过程或函数里指定Commit/Rollback的,如果没有,则外部调用者可以回滚存储过程内部的操作。

但在PostgreSQL,函数(存储过程)总是自动将其所有操作当作一个事务,外部无法对内部操作提交或回滚。

问题好像已经解决,但留有一个疑问没弄明白,为什么PostgreSQL允许在函数体中加关于事务的begin...exception...end,但结果却好像是没提交?

转载于:https://www.cnblogs.com/wggj/p/7792312.html

一、综述今天在PostgreSQL遇到一个奇怪的现象,简而言之,是想用函数(存储过程)实现插入记录,整个过程没报错但事后却没找到记录!忙活半天,才发现原因是PostgreSQL函数(存储过程)有自动COMMIT或ROLLBACK的特殊规定。二、问题重现以下用示例表和示例代码来重现该问题。create table t1 ( ID int not null primary...
对于熟悉Oracle数据库的SQL * Plus的人来说,当将其引入PostgreSQL数据库的psql时,一个潜在的惊喜可能就是psql的默认启用的autocommit 。 这篇文章概述了psql对autocommit的处理以及一些相关的细微差别 。 默认情况下, Oracle的SQL * Plus命令行工具不会自动提交DML语句 ,并且操作员必须作为事务的一部分显式提交这些语句(或退...
事务:确保一系列操作必须全部成功后,才算成功。一旦其中一个操作出错,那么会回到初始状态,哪怕前几个操作已经成功了。 比如某人要在商店使用电子货币购买100元的东西,当中至少包括两个操作: 该人账户减少 100 元。 商店账户增加100元。 支持事务的数据库管理系统就是要确保以上两个操作(整个"事务")都能完成,或一起取消,否则就会出现 100 元平白消失或出现的情况。 事务有三个命令,如下: begin 开始事务。开始事务后,后面的操作并不会直接修改数据库,除非提交事务 commit 提交事..
PostgreSQL 正则表达式 常用函数的总结 对那些需要进行复杂数据处理的程序来说,正则表达式无疑是一个非常有用的工具。本文重点在于阐述 PostgreSQL 的一些常用正则表达式函数以及源码中的一些函数。 正则相关部分的目录结构 [root@localhost regex]# pwd /opt/hgdb-core/src/include/regex [root@localhost regex]# ll total 40 -rw-r--r--. 1 postgres postgres 3490 Mar 19 19:00 regcustom.h -rw-r--r--. 1 postgres
spring mvc 项目在执行查询功能的时候 后台报错如下: javax.persistence.TransactionRequiredException:no transaction is in progress ............ 后来找到原因是因为链接的资源有问题也就是sessionFactory所映射到配置资源链接的资源库有问题,修改之后没有问题,后来进行原因查... public T merge(T entity) { T returnObj = this.getJpaTemplate().merge(entity); this.getJpaTemplate().flush(); return r 21-05-23 11:56:17.869 ERROR [taskScheduler-8 ] com.xxx.CashoutService :743 - [xxx] Process Unconfirm App Cash Record Error = {} org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.pe
\l 列出数据库 \c testdb 切换数据库 SELECT tablename FROM pg_tables WHERE tablename NOT LIKE 'pg%' AND tablename NOT LIKE 'sql_%' ORDER BY tablename; 查看所有的表列表 \d tab...
项目中虽然配置了事务,但是总感觉没起作用。 尤其是我在测试service方法时,总是报出一个异常  transaction not successfully started . 我开始以为是事务配置的问题,经过反复的检查,发现没错。 开始在网上漫无目的的搜索解决方案, 在一片帖子中看到,有人说 使用SessionFactory.getCurrentSession爆出了这个异常,解决办法...
private C c; @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT) public...
可以使用 PostgreSQL 内置的 uuid-ossp 模块来生成 UUID。使用函数 uuid-ossp.uuid_generate_v4() 可以生成一个随机的 UUID。例如: SELECT uuid-ossp.uuid_generate_v4(); 这将返回一个类似于 "3f2504e-4f89-11d3-9ac-0305e82c3301" 的 UUID。