2017-02-03 9 views
1

У меня есть рекурсивная функция PL/PgSQL, который использует связанный параметризированный курсор, как это:функция Рекурсивного PostgreSQL завершается с «курсором уже используется» сообщение

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, которые в значительной степени используют рекурсию и параметризованные курсоры. Конверсия казалась простой, пока я не ударил эту проблему с помощью курсоров с глобальным охватом.

ответ

2

Я только что нашел обходное решение, которое выглядит довольно странно для меня, но все еще, похоже, работает. Я не уверен, есть ли у него какие-то недостатки, но в моем случае единственной альтернативой является переписать довольно много кода вручную, поэтому, я думаю, я попробую.

Решение не ограничивать сферу открытого курсора, но рандомизации его общественное видимое имя (портальное имя), так что каждый раз, когда я повторно ввести свою функцию я получить свежее имя портала:

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; 

 Смежные вопросы

  • Нет связанных вопросов^_^