后记。超出 plpgsql 堆栈深度限制

Posted

技术标签:

【中文标题】后记。超出 plpgsql 堆栈深度限制【英文标题】:postgres. plpgsql stack depth limit exceeded 【发布时间】:2013-05-22 16:15:49 【问题描述】:

我正在开发一个简单的函数,它会自动更新表格中的内容。

create or replace function total() 
returns void as $$
declare
  sum int;
begin
  sum = (SELECT count(copy_id) FROM copies);
    update totalbooks
    set all_books = sum
    where num = 1;
  end;
$$ language plpgsql;

如果我执行“select total();”它工作得很好,所以我做了一个函数触发器,让它自动更新:

create or replace function total1() returns trigger as $$
begin
   perform (select total());
    return null;
end;
$$ language plpgsql;

但是在我执行这个之后:

create trigger total2
after update
on totalbooks
for each row
execute procedure total1();

它给了我一个错误信息:

ERROR:  stack depth limit exceeded
HINT:  Increase the configuration parameter "max_stack_depth" (currently 3072kB), after   ensuring the platform's stack depth limit is adequate.
CONTEXT:  SQL statement "SELECT (SELECT count(copy_id) FROM copies)"
PL/pgSQL function total() line 5 at assignment
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"

显然我的触发器有问题。请帮忙。

我使用的是 Postgres 9.2.4,由 Visual C++ build 1600 编译,64 位

编辑:

我尝试了 pg_trigger_depth(),但现在触发器不会自动更新??我仍然需要执行'select total()'

这是我的新代码:

create or replace function total() 
returns void as $$
declare
  sum int;
begin
   sum = (SELECT count(copy_id) FROM copies);
    update totalbooks
    set all_books = sum;
end;
$$ language plpgsql;


create or replace function total1() returns trigger as $$
begin
  perform (select total());
  return null;
end;
$$ language plpgsql;

create trigger total2
after update
on totalbooks
for each row
WHEN (pg_trigger_depth()=0)
execute procedure total1();

【问题讨论】:

您的触发器由totalbooks 上的更新触发,然后更新表totalbooks,进而触发totalbooks 上的触发器,更新表totalbook,在@ 上触发触发器987654330@ 然后更新表totalbooks 然后触发totalbooks 上的触发器,更新表totalbook 这将触发触发器......(你明白了) @a_horse_with_no_name 所以这就像一遍又一遍地做事??那我该如何纠正这个?有什么想法吗?? 【参考方案1】:

好的,如果您真的想要更新时的触发器,您可以将这个触发器设置为特定于列的触发器,这样它就不会在更新到 all_books 时触发,这会导致您的递归。像这样的 -

create trigger total2
after update of copy_id
on totalbooks
for each row
execute procedure total1();

当然,您可以更改触发该功能的列,我只是选择了copy_id,因为这就是您要数的。

但是

如果您使用count() 结果进行更新,您只需将触发器放在INSERTDELETE 操作上。这样,触发器将在计数更改时触发,但本身不会被更新触发。 // 编辑:由于您的 sum 只是 copies 中所有记录的计数,因此它只会在插入或更新记录时更改,因此在更新时运行此触发器无论如何都没有意义。

编辑:我认为添加指向CREATE TRIGGER Documentation 的链接会很有用。请参阅标有“事件”的部分,因为这详细说明了如何在事件中指定列。

编辑新信息:

考虑到您需要完成的任务,我认为您需要重新考虑您的数据设计,我建议您使用父子关系(任何时候您将共享数据缓存在表中的许多行上,因为它们在常见,这表明您可能需要一个父表)。

有一个books 表,其中每一行是关于一本书的信息(标题、作者等),然后有一个copies 表,其中每一行包含关于一本书的副本的信息(序列号,最后检查出等)。

这样,获取副本数就像SELECT COUNT(*) FROM copies WHERE book_id=[some book id] 一样简单。

如果您真的想在某处缓存计数,请在books 表上进行。

copies 上创建一个INSERT OR UPDATE 触发器,该触发器执行UPDATE books SET copy_count=(SELECT COUNT(*) FROM copies WHERE book_id=NEW.book_id) WHERE id=NEW.book_id

然后在执行UPDATE books SET copy_count=(SELECT COUNT(*) FROM copies WHERE book_id=OLD.book_id) WHERE id=OLD.book_id 的副本上创建DELETE 触发器

两个触发器的原因是NEW变量只在INSERTUPDATE触发器中可用,而OLD只在DELETE触发器中可用。您可以将所有这些作为一个触发器来完成,但这需要的代码比我想放在这里的要多。

确保您的所有触发器都是AFTER 触发器,否则新插入/删除的行将不会被计入计数。

【讨论】:

好的,我会尽快阅读。在我阅读了你给我的链接后会问一些问题。谢谢。 完成阅读并尝试一切。我指定了列,但它仍然没有自行更新。 您指定了哪些列,您使用什么 SQL 语句来测试触发器? after update of all_books 和插入触发器的sql语句:insert into copies(book_id) values('BI6'); 您的 SQL 是 INSERT 语句,但您的触发器设置为在 UPDATE 上触发。该 SQL 语句永远不会触发您的触发器,您必须更新现有行(类似于UPDATE copies SET book_id='BI6' WHERE ID=1)。另外,这就是我在回答的第二部分中所要表达的意思-您可能想要AFTER INSERT OR DELETE而不是AFTER UPDATE,因为您正在做一个没有限制的count,结果只能改变在插入或更新之后。【参考方案2】:

查看 pg_trigger_depth:

http://www.postgresql.org/docs/9.2/static/functions-info.html

(9.2 或更高版本)

【讨论】:

不能让它工作??如何将 pg_trigger_depth() 放入我的函数中?? 添加深度检查,如果是递归则返回。 我编辑了我的原始帖子,并使用 pg_trigger_depth() 发布了新代码,但我仍然不确定我是否制作了正确的代码。你可以检查吗?请?谢谢! 在你的函数内部,如果 depth 【参考方案3】:

在您的触发器内部:

IF pg_trigger_depth() < 2 THEN

  PERFORM total();

END IF;

【讨论】:

以上是关于后记。超出 plpgsql 堆栈深度限制的主要内容,如果未能解决你的问题,请参考以下文章

触发触发器时超出堆栈深度限制

Python递归限制与堆栈大小?

超出堆栈限制(0.2Gb)...可能无限递归(循环):

我可以限制通用堆栈的深度吗?

堆栈深度限制 INSERT 触发器

PostgresQL 中超出堆栈深度限制(删除触发器后)