Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Postgresql 具有异常块的过程中是否存在Postgres提交?_Postgresql_Stored Procedures_Plpgsql_Postgresql 11 - Fatal编程技术网

Postgresql 具有异常块的过程中是否存在Postgres提交?

Postgresql 具有异常块的过程中是否存在Postgres提交?,postgresql,stored-procedures,plpgsql,postgresql-11,Postgresql,Stored Procedures,Plpgsql,Postgresql 11,我很难理解博士后的交易。我有一个程序可能会遇到异常。在这个过程的某些部分中,我可能希望提交到目前为止的工作,以便在出现异常时不会回滚 我希望在过程的末尾有一个异常处理块,在该块中捕获异常并将异常中的信息插入到日志表中 我将问题归结为一个简单的过程,如下所示,它在PostgreSQL 11.2上失败,并且 2D000 cannot commit while a subtransaction is active PL/pgSQL function x_transaction_try() line 6

我很难理解博士后的交易。我有一个程序可能会遇到异常。在这个过程的某些部分中,我可能希望提交到目前为止的工作,以便在出现异常时不会回滚

我希望在过程的末尾有一个异常处理块,在该块中捕获异常并将异常中的信息插入到日志表中

我将问题归结为一个简单的过程,如下所示,它在PostgreSQL 11.2上失败,并且

2D000 cannot commit while a subtransaction is active
PL/pgSQL function x_transaction_try() line 6 at COMMIT
如果存在删除过程x\u事务\u尝试;
创建或替换过程x_事务_try()
语言plpgsql
作为$$
声明
开始
提出通知‘A’;
--TODO A:执行一些插入或更新,无论发生什么,我都要提交
犯罪
提出通知‘B’;
--TODO B:执行其他可能引发异常的操作,而不进行滚动
--支持我们在“待办事项A”中所做的工作。
当其他人
声明
我的前州文本;
我的短信;
我的前详细文本;
我的提示文本;
我的前ctx文本;
开始
提出通知‘C’;
获取堆叠诊断
my_ex_state=返回的_SQLSTATE,
my_ex_message=消息文本,
my_ex_detail=PG_EXCEPTION_detail,
my_ex_hint=PG_EXCEPTION_hint,
my_ex_ctx=PG_异常\u上下文
;
提出通知“%%%”、我的离开状态、我的离开消息、我的离开细节、我的离开提示、我的离开ctx;
--TODO C:在日志记录表中插入此异常信息并提交
结束;
结束;
$$;
调用x_transaction_try();
为什么这个存储过程不工作?为什么我们从未看到
引发通知“B”的输出,而是进入异常块?是否可以使用Postgres 11存储过程执行我上面描述的操作


编辑:这是一个完整的代码示例。将上述完整的代码示例(包括
create procedure
call
语句)粘贴到一个sql文件中,并在Postgres 11.2数据库中运行,以重新创建。所需的输出将用于打印
A
然后
B
,但它会打印
A
然后
C
以及异常信息


还请注意,如果注释掉所有异常处理块,使函数根本不捕获异常,则函数将在不发生异常的情况下输出“A”和“B”。这就是为什么我用“Postgres提交是否存在于具有异常块的过程中?”的方式来命名这个问题的原因。

问题在于
Exception
子句

这在PL/pgSQL中作为子事务实现(与SQL中的
保存点
相同),在到达异常块时回滚

当子事务处于活动状态时,您不能提交

请参阅中的此评论:

/*
*该限制是在SPI之上实施的PLs所要求的。他们
*使用子事务建立异常块,这些异常块应该
*如果出现错误,将一起回滚。终止
*这样一个块中的顶级事务违反了这个想法。未来的PL
*在这种情况下,实现可能对此有不同的想法
*必须完善这一限制,否则可能会取消检查
*从SPI搬到PLs。
*/
if(IsSubTransaction())
电子报告(错误,
(错误代码(错误代码无效交易终止),
errmsg(“子事务处于活动状态时无法提交”);

PL/pgSQL的语义规定:

当异常子句捕获错误时。。。块内对持久数据库状态的所有更改都将回滚

这是使用与基本相同的子事务实现的。换句话说,当您运行以下PL/pgSQL代码时:

BEGIN
  PERFORM foo();
EXCEPTION WHEN others THEN
  PERFORM handle_error();
END
…实际发生的事情是这样的:

BEGIN
  SAVEPOINT a;
  PERFORM foo();
  RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
  ROLLBACK TO SAVEPOINT a;
  PERFORM handle_error();
END
块内的
COMMIT
将完全破坏这一点;您的更改将成为永久性的,保存点将被丢弃,异常处理程序将无法回滚。因此,在此上下文中不允许提交,尝试执行
COMMIT
将导致“子事务处于活动状态时无法提交”错误

这就是为什么您看到您的过程跳转到异常处理程序,而不是运行
引发通知“B”
:当它到达
提交时,它抛出一个错误,处理程序捕获它

不过,解决这个问题相当简单<代码>开始。。。END
块可以嵌套,并且只有带有
EXCEPTION
子句的块才涉及设置保存点,因此您可以将提交前后的命令包装到它们自己的异常处理程序中:

create or replace procedure x_transaction_try() language plpgsql
as $$
declare
  my_ex_state text;
  my_ex_message text;
  my_ex_detail text;
  my_ex_hint text;
  my_ex_ctx text;
begin
  begin
    raise notice 'A';
  exception when others then
    raise notice 'C';
    GET STACKED DIAGNOSTICS
      my_ex_state   = RETURNED_SQLSTATE,
      my_ex_message = MESSAGE_TEXT,
      my_ex_detail  = PG_EXCEPTION_DETAIL,
      my_ex_hint    = PG_EXCEPTION_HINT,
      my_ex_ctx     = PG_EXCEPTION_CONTEXT
    ;
    raise notice '% % % % %', my_ex_state, my_ex_message, my_ex_detail, my_ex_hint, my_ex_ctx;
  end;

  commit;

  begin
    raise notice 'B';
  exception when others then
    raise notice 'C';
    GET STACKED DIAGNOSTICS
      my_ex_state   = RETURNED_SQLSTATE,
      my_ex_message = MESSAGE_TEXT,
      my_ex_detail  = PG_EXCEPTION_DETAIL,
      my_ex_hint    = PG_EXCEPTION_HINT,
      my_ex_ctx     = PG_EXCEPTION_CONTEXT
    ;
    raise notice '% % % % %', my_ex_state, my_ex_message, my_ex_detail, my_ex_hint, my_ex_ctx;
  end;      
end;
$$;

不幸的是,这确实会导致错误处理程序中出现大量重复,但我想不出一个好办法来避免它。

问题一定出在调用该过程的代码中。您能描述一下调用堆栈和事务管理吗?这是一个引发意外异常的完整代码示例。e、 g.即使TODO位置中没有任何代码,此完整的“脚本”也无法提供所需的输出。只需将整个代码示例粘贴到一个sql文件中并运行它。嗯,你说得对。我来看看。