Инвалидация пакетной табличной функции при вызове через линк к удалённой БД

Есть пакетная табличная функция.

Локально работает нормально. С другого сервера подключаются под владельцем схемы функции. Одиночные значения другими функциями возвращаются нормально. При вызове табличной функции ошибка - Функция в несовместимом состоянии.

То есть, каждый вызов ее инвалидирует пакет. В чем может быть причина?


Ответы (3 шт):

Автор решения: 0xdb

Функция в несовместимом состоянии

Нет, причина не в этом. Причина кроется тут:

Одиночные значения другими функциями возвращаются нормально.

Одиночные значения, это встроенные типы данных. Однако, табличные функции возвращают пользовательские типы данных. Обьявленные на удалённой БД пользовательские типы данных, неизвестны на локальной БД. Даже если объявить полностью идентичный тип данных локально, с точки зрения Oracle, это разные типы данных.

Посмотрим на примере:

create or replace package pack as
    type numtab is table of number;
    function gettab return numtab pipelined;
end;
/
create or replace package body pack as
    function gettab return numtab pipelined is
    begin
        for i in 1..3 loop pipe row (i*10);
        end loop;
        return; 
    end;
end;
/
select * from pack.gettab();

Result Sequence
---------------
             10
             20
             30

Локально работает. Но вызов этой же функции через линк к удалённой БД:

create database link loopback using 'localhost/pdb1';

select * from pack.gettab@loopback() 
/

Даст теперь соответсвующую ошибку:

ORA-30626: function/procedure parameters of remote object types are not supported

Обойти ошибку можно, например, создав на удалённой БД представление:

create or replace view viewtab as
    select * from pack.gettab()
/
select * from viewtab@loopback:

Result Sequence
---------------
             10
             20
             30
→ Ссылка
Автор решения: 0xdb

Предложенное в ранее данном ответе решение, несмотря на простоту, имеет один серьёзный недостаток - не так просто передать в представление параметры.

Поэтому, более сложное, но достаточно эффективное решение - остаться в PL/SQL контексте. В этом случае, возможно обратится через линк к типам данных, объявленным в пакете на удалённой БД. Однако, конвейерные табличные функции должны быть преобразованы в обычные табличные функции.

Пример для реализации:

create or replace package pack as
    type numtab is table of number;
    function gettab (val int) return numtab;
end;
/
create or replace package body pack as
    function gettab (val int) return numtab is
    begin
        return numtab (1*val,2*val,3*val); 
    end;
end;
/

Заметьте, в примере используется тот же самый пакет в той же самой схеме, но при обращении к нему через линк и локально, получим больше несвместимые типы данных. Поэтому, цикл для преобразования один в другой:

create database link loopback using 'localhost/pdb1';

var rc refcursor
declare
    tab pack.numtab@loopback := pack.gettab@loopback(10);
    ret pack.numtab := pack.numtab ();
    rc sys_refcursor;
begin null;
    ret.extend (tab.count);
    for i in 1..tab.count loop ret(i) := tab(i); end loop;
    open :rc for select * from table (ret);
end;
/

print rc

Result Sequence
---------------
             10
             20
             30
→ Ссылка
Автор решения: 0xdb

На основе комментариев ТС ещё один пример для реализации.

Пакет на удалённой БД:

create or replace package packrem as
    type rec is record (id int, memo varchar2 (32));
    type tab is table of rec;
    function gettab (val int) return tab;
end;
/
create or replace package body packrem as
    function gettab (val int) return tab is
    begin
        return tab (
            rec (1, 'memo'||1*val), 
            rec (2, 'memo'||2*val), 
            rec (3, 'memo'||3*val)); 
    end;
end;
/

Пакет на локальной БД:

create or replace package pack as
    type rec is record (id int, memo varchar2 (32));
    type tab is table of rec;
    function gettab (val int) return tab pipelined;
end;
/
create or replace package body pack as
    function gettab (val int) return tab pipelined is
        ret packrem.tab@loopback;
    begin 
        ret := packrem.gettab@loopback (val);
        for i in 1..ret.count loop
            pipe row (rec (ret(i).id, ret(i).memo));
        end loop;
        return;
    end;
end;
/

Запрос и его результат:

select * from pack.gettab (10);

        ID MEMO                            
---------- --------------------------------
         1 memo10                          
         2 memo20                          
         3 memo30                          
→ Ссылка