【问题标题】:Recursive PostgreSQL function fails with a «cursor already in use» message递归 PostgreSQL 函数失败并显示 «cursor already in use» 消息
【发布时间】:2017-06-20 06:41:22
【问题描述】:

我有一个递归 PL/PgSQL 函数,它使用这样的绑定参数化游标:

create or replace function test_cursor(rec boolean) returns void as $$
declare
    cur cursor(a int) for select * from generate_series(1,a);
begin
    for c in cur(3) loop
        if rec then
            perform test_cursor(false);
        end if;
    end loop;
end;
$$ language plpgsql;

函数递归调用自身时,报错:

select test_cursor(true)

Code: 42P03, ErrorMessage: cursor "cur" already in use

显然我的光标的范围不限于单个函数调用。在谷歌搜索解决方法后,我在邮件列表档案中发现了这个message,其中提到未绑定的游标没有这个限制,即:

declare
     mycursor refcursor;
begin
     open mycursor for ...;
end;

但我不知道如何参数化未绑定的游标。另外,我不能将for...loop 与未绑定的光标一起使用:

-- 42601: cursor FOR loop must use a bound cursor variable
create or replace function test_cursor(rec boolean) returns void as $$
declare
    cur refcursor;
begin
    open cur for select * from generate_series(1,3);
    for c in cur loop
        if rec then
            perform test_cursor(false);
        end if;
    end loop;
    close cur;
end;
$$ language plpgsql;

有人可以建议一种替代方法吗?

PS。我正在移植大量使用递归和参数化游标的 Oracle 存储过程。在我用全局范围的游标遇到这个问题之前,转换似乎很简单。

【问题讨论】:

    标签: sql postgresql recursion cursor postgresql-9.6


    【解决方案1】:

    我刚刚找到了一个对我来说看起来很奇怪但似乎仍然有效的解决方法。我不确定它是否有任何缺点,但就我而言,唯一的选择是手动重写相当多的代码,所以我想我会尝试一下。

    解决方案不是限制打开光标的范围,而是随机化其公开可见的名称(门户名称),这样每次我重新输入我的函数时,我都会得到一个新的门户名称:

    create or replace function test_cursor(rec boolean default true) returns void as $$
    declare
        cur cursor(a int) for select * from generate_series(1,a);
    begin
        -- assign a random string as a portal name
        -- before iterating over the cursor
        cur := random_portal_name();
    
        for c in cur(3) loop
            if rec then
                perform test_cursor(false);
            end if;
        end loop;
    end;
    $$ language plpgsql;
    

    有很多方法可以获取随机字符串:从序列中获取下一个值、生成 UUID 等等。我为自己编写了一个辅助函数,它为此目的创建了一个临时(会话范围)序列:

    create or replace function random_portal_name() returns varchar as $$
    begin
        create temp sequence if not exists portal_names;
        return 'portal$' || nextval('portal_names');
    end;
    $$ language plpgsql;
    

    【讨论】:

      猜你喜欢
      • 2021-02-20
      • 2022-12-26
      • 2022-12-01
      • 2017-08-19
      • 2017-08-12
      • 2016-12-05
      • 1970-01-01
      • 1970-01-01
      • 2021-12-22
      相关资源
      最近更新 更多