Инвалидация пакетной табличной функции при вызове через линк к удалённой БД
Есть пакетная табличная функция.
Локально работает нормально. С другого сервера подключаются под владельцем схемы функции. Одиночные значения другими функциями возвращаются нормально. При вызове табличной функции ошибка - Функция в несовместимом состоянии.
То есть, каждый вызов ее инвалидирует пакет. В чем может быть причина?
Ответы (3 шт):
Функция в несовместимом состоянии
Нет, причина не в этом. Причина кроется тут:
Одиночные значения другими функциями возвращаются нормально.
Одиночные значения, это встроенные типы данных. Однако, табличные функции возвращают пользовательские типы данных. Обьявленные на удалённой БД пользовательские типы данных, неизвестны на локальной БД. Даже если объявить полностью идентичный тип данных локально, с точки зрения 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
Предложенное в ранее данном ответе решение, несмотря на простоту, имеет один серьёзный недостаток - не так просто передать в представление параметры.
Поэтому, более сложное, но достаточно эффективное решение - остаться в 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
На основе комментариев ТС ещё один пример для реализации.
Пакет на удалённой БД:
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