回滚外部功能如果嵌套功能抛出(被困!)异常

发布于 2025-02-04 09:33:06 字数 753 浏览 1 评论 0原文

考虑PLPGSQL中的以下示例函数:

create or replace function fn_delete(p_id int)
  returns SETOF result_transaction as
$$
declare 
   related_rows_affected int :=0;
begin
   --some previous code that alters some tables--
   .
   .
   .
   select count(id1) into related_rows_affected from td_other,
   lateral fn_delete_secondary(id1)
   where id = p_id;
end
$$ language 'plpgsql';

考虑fn_delete_secondary只是删除一些记录。如果FN_DELETE_SECONDARY会抛出一个例外,以防止其删除被置换的行。如何达到上述结果?

我以为在FN_DELETE_SECONDARY中抛出一个例外也会回滚FN_DELETE,但不是这样...请注意,

EXCEPTION
WHEN OTHERS THEN

如果我不处理fn_delete_secondary中的异常,请注意fn_delete_secondary在表单中处理异常吗?我希望现在像现在这样离开它,因为我也直接使用此功能。

另一个问题是:如何在横向语句中使用何时在内部使用FN_DELETE_SECONDARY的结果?

Consider the following example function in plpgsql:

create or replace function fn_delete(p_id int)
  returns SETOF result_transaction as
$
declare 
   related_rows_affected int :=0;
begin
   --some previous code that alters some tables--
   .
   .
   .
   select count(id1) into related_rows_affected from td_other,
   lateral fn_delete_secondary(id1)
   where id = p_id;
end
$ language 'plpgsql';

Consider fn_delete_secondary just deletes some records. I want to rollback any previous changes in fn_delete if fn_delete_secondary throws an exception that prevents it from deleting the appropriated rows. How can I achieve the aforementioned result?

I thought that throwing an exception inside the fn_delete_secondary would rollback fn_delete too, but isn't the case... Note that fn_delete_secondary handles exceptions in the form

EXCEPTION
WHEN OTHERS THEN

If I didn't handle the exceptions inside fn_delete_secondary, would that work? I would prefer to leave it as is right now because I also use this function directly.

Other question is: How can I get the results of fn_delete_secondary when is used inside in a lateral statement?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

末が日狂欢 2025-02-11 09:33:06

我以为在fn_delete_secondary()滚动 fn_delete()也要丢入fn_delete_secondary(),...

...扔。因此,是的,如果您不处理它,一切都会回滚。

否则,您需要从fn_delete_secondary()返回一些信息,以指示被困的异常。然后fn_delete()可以识别并提出异常本身。

这样,您可以在fn_delete_secondary()中继续错误处理 - 如果需要。具有异常的代码块子句要贵得多,因此这很昂贵。

要回答您的问题,假设fn_delete_secondary()像这样:

CREATE OR REPLACE FUNCTION fn_delete_secondary(_id int, OUT _deleted_rows int)
  LANGUAGE plpgsql AS
$func$
BEGIN
   DELETE FROM tbl2 t WHERE t.id = _id;
   
   GET DIAGNOSTICS _deleted_rows = ROW_COUNT;   

EXCEPTION WHEN OTHERS THEN
   _deleted_rows := -1;

END
$func$;

它返回已删除行的数量 - 或-1如果有处理错误。
然后,您的主要功能可以这样工作:

CREATE OR REPLACE FUNCTION fn_delete(p_id int)
  RETURNS SETOF result_transaction
  LANGUAGE plpgsql AS
$func$
DECLARE
   related_rows_affected int := 0;
   _error_reported bool;
BEGIN
   -- put this first!
   SELECT sum(f._deleted_rows) , bool_or(f._deleted_rows < 0)
   INTO   related_rows_affected, _error_reported
   FROM   td_other, fn_delete_secondary(id1) f
   WHERE  id = p_id;

   IF _error_reported THEN
      RAISE EXCEPTION 'fn_delete_secondary() reported an error'; 
   END IF;
   
   -- some code that alters some tables, etc.
END
$func$;

将可能失败的呼叫移动到顶部。那便宜。无论哪种方式,一切都会回滚。

I thought that throwing an exception inside the fn_delete_secondary() would rollback fn_delete() too, ...

You thought correctly, but the exception in the second function is trapped instead of thrown. So, yes, if you don't handle it, everything is rolled back.

Else you need to return some information from fn_delete_secondary() indicating a trapped exception. Then fn_delete() can identify that and raise an exception itself.

This way you can keep error handling in fn_delete_secondary() - if you must. Code blocks with an EXCEPTION clause are considerably more expensive, so that's expensive complication.

To answer your question, assuming fn_delete_secondary() like this:

CREATE OR REPLACE FUNCTION fn_delete_secondary(_id int, OUT _deleted_rows int)
  LANGUAGE plpgsql AS
$func$
BEGIN
   DELETE FROM tbl2 t WHERE t.id = _id;
   
   GET DIAGNOSTICS _deleted_rows = ROW_COUNT;   

EXCEPTION WHEN OTHERS THEN
   _deleted_rows := -1;

END
$func$;

It returns the number of deleted rows - or -1 if there was a handled error.
Then your main function could work like this:

CREATE OR REPLACE FUNCTION fn_delete(p_id int)
  RETURNS SETOF result_transaction
  LANGUAGE plpgsql AS
$func$
DECLARE
   related_rows_affected int := 0;
   _error_reported bool;
BEGIN
   -- put this first!
   SELECT sum(f._deleted_rows) , bool_or(f._deleted_rows < 0)
   INTO   related_rows_affected, _error_reported
   FROM   td_other, fn_delete_secondary(id1) f
   WHERE  id = p_id;

   IF _error_reported THEN
      RAISE EXCEPTION 'fn_delete_secondary() reported an error'; 
   END IF;
   
   -- some code that alters some tables, etc.
END
$func$;

Move the call that might fail to the top. That's cheaper. Everything will be rolled back either way.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文