It appears as though there's an implementation restriction that forbids the use of forall .. insert
on Oracle, when used over a database link. This is a simple example to demonstrate:
connect schema/password@db1
create table tmp_ben_test (
a number
, b number
, c date
, constraint pk_tmp_ben_test primary key (a, b)
);
Table created.
connect schema/password@db2
Connected.
declare
type r_test is record ( a number, b number, c date);
type t__test is table of r_test index by binary_integer;
t_test t__test;
cursor c_test is
select 1, level, sysdate
from dual
connect by level <= 10
;
begin
open c_test;
fetch c_test bulk collect into t_test;
forall i in t_test.first .. t_test.last
insert into tmp_ben_test@db1
values t_test(i)
;
close c_test;
end;
/
Very confusingly this fails in 9i with the following error:
ERROR at line 1: ORA-01400: cannot insert NULL into ("SCHEMA"."TMP_BEN_TEST"."A") ORA-02063: preceding line from DB1 ORA-06512: at line 18
If was only after checking in 11g that I realised this was an implementation restriction.
ERROR at line 18: ORA-06550: line 18, column 4: PLS-00739: FORALL INSERT/UPDATE/DELETE not supported on remote tables
The really obvious way round this is to change forall ..
to:
for i in t_test.first .. t_test.last loop
insert into tmp_ben_test@db1
values t_test(i);
end loop;
but, I'd rather keep it down to a single insert if at all possible. Tom Kyte suggests the use of a global temporary table. Inserting the data into a GTT and then over a DB link seems like massive overkill for a set of data that is already in a user-defined type.
Just to clarify this example is extremely simplistic compared to what is actually happening. There is no way we will be able to do a simple insert into
and there is no way all the operations could be done on a GTT. Large parts of the code have to be done in user-defined type.
Is there another, simpler or less DMLy, way around this restriction?