Plsql 通过dblink保存在clob变量中的ddl语句

Plsql 通过dblink保存在clob变量中的ddl语句,plsql,ddl,oracle12c,dblink,Plsql,Ddl,Oracle12c,Dblink,我在通过db链接执行ddl语句时遇到问题。 下面PL/SQL负责通过db链接在远程db上执行语句,但我需要将ddl_语句变量设置为clob,因为最大varchar2不够。目前正在筹集: PLS-00564:对远程服务器的调用中不允许使用Lob参数 DECLARE vi_handler INTEGER; vi_numberOfRows INTEGER; BEGIN vi_handler := DBMS_SQL.open_cursor%s; DBMS_SQL.par

我在通过db链接执行ddl语句时遇到问题。 下面PL/SQL负责通过db链接在远程db上执行语句,但我需要将ddl_语句变量设置为clob,因为最大varchar2不够。目前正在筹集:

PLS-00564:对远程服务器的调用中不允许使用Lob参数

DECLARE
  vi_handler        INTEGER;
  vi_numberOfRows   INTEGER;
BEGIN
  vi_handler := DBMS_SQL.open_cursor%s;
  DBMS_SQL.parse%s (vi_handler, :ddl_statement, DBMS_SQL.native);
  vi_numberOfRows := DBMS_SQL.execute%s (vi_handler);
  DBMS_SQL.close_cursor%s (vi_handler);
END;

提前感谢您提供的任何帮助

好吧,这很痛苦,如果您可以在远程端调用一个过程(我知道您不能!),这会更简单;但只要稍加努力,您就可以远程运行匿名块,并让匿名块运行DDL

这是令人困惑的,因为您需要通过在远端运行的匿名块对远程数据库执行
dbms\u sql
调用您需要在本地数据库上执行
dbms\u sql
调用,以便远程运行匿名块

现在,我已经在远程端用
r\uu
作为变量等的前缀,在本地端用
l\uu
作为变量等的前缀。我不确定这是否足以让事情变得清楚,但这只是一个开始

让我们来看一个您正在尝试的版本,使用带有简单包调用的本地CLOB,而不是真正的DDL:

declare
  l_stmt clob;
  l_c pls_integer;
  l_rc pls_integer;
begin
  l_stmt := 'begin dbms_stats.gather_schema_stats(user); end;';

  l_c := dbms_sql.open_cursor@dblink;
  dbms_sql.parse@dblink(l_c, l_stmt, dbms_sql.native);
  l_rc := dbms_sql.execute@dblink(l_c);
  dbms_sql.close_cursor@dblink(l_c);
end;
/
正如预期的那样,这将得到:

ORA-06550: line 9, column 31:
PLS-00564: lob arguments are not permitted in calls to remote server
正如在一篇评论中所建议的,也许我可以将CLOB分解成块并重新组装。如果我真的在远程服务器上,我可以这样做,现在手动切碎我的语句:

var b1 varchar2(20);
var b2 varchar2(20);
var b3 varchar2(20);

exec :b1 := 'begin dbms_stats.gat';
exec :b2 := 'her_schema_stats(use';
exec :b3 := 'r); end;';

declare
  r_stmt clob := :b1 || :b2 || :b3;
  r_c pls_integer;
  r_rc pls_integer;
begin
  r_c := dbms_sql.open_cursor;
  dbms_sql.parse(r_c, r_stmt, dbms_sql.native);
  r_rc := dbms_sql.execute(r_c);
  dbms_sql.close_cursor(r_c);
end;
/

PL/SQL procedure successfully completed.
所以现在我想远程运行同样的东西。该匿名块成为本地解析的语句:

declare
  l_stmt clob;
  l_remote_stmt varchar2(32767);
  l_c integer;
  l_rc integer;
begin
  l_stmt := 'begin dbms_stats.gather_schema_stats(user); end;';

  l_remote_stmt := q'[
declare
  r_stmt clob := :b1 || :b2 || :b3;
  r_c pls_integer;
  r_rc pls_integer;
  pragma autonomous_transaction;
begin
  r_c := dbms_sql.open_cursor;
  dbms_sql.parse(r_c, r_stmt, dbms_sql.native);
  r_rc := dbms_sql.execute(r_c);
  dbms_sql.close_cursor(r_c);
end;
]';

  l_c := dbms_sql.open_cursor@dblink;
  dbms_sql.parse@dblink(l_c, l_remote_stmt, dbms_sql.native);
  -- bind the chunks of CLOB
  dbms_sql.bind_variable@dblink(l_c, 'B1', dbms_lob.substr(l_stmt, 20, 1));
  dbms_sql.bind_variable@dblink(l_c, 'B2', dbms_lob.substr(l_stmt, 20, 21));
  dbms_sql.bind_variable@dblink(l_c, 'B3', dbms_lob.substr(l_stmt, 20, 41));
  -- execute the anonymous block (which makes its own dbms_sql calls) remotely
  l_rc := dbms_sql.execute@dblink(l_c);
  dbms_sql.close_cursor@dblink(l_c);
end;
/

PL/SQL procedure successfully completed.
我已经使用了20个字符块来模拟我之前手动执行的操作。对于真正的DDL,您应该:

  dbms_sql.bind_variable@dblink(l_c, 'B1', dbms_lob.substr(l_stmt, 32767, 1));
  dbms_sql.bind_variable@dblink(l_c, 'B2', dbms_lob.substr(l_stmt, 32767, 32768));
  dbms_sql.bind_variable@dblink(l_c, 'B3', dbms_lob.substr(l_stmt, 32767, 65535));
。。。可能是计算偏移量,而不是手动输入偏移量。对于大型CLOB,向远程语句添加更多连接的bind变量,并匹配
bind_variable
调用

此版本确定CLOB需要拆分成多少块,并创建适当的远程语句,并以相同的方式进行绑定:

set serveroutput on
declare
  l_stmt clob;
  l_remote_stmt varchar2(32767);
  l_remote_args varchar2(4000);
  l_c integer;
  l_rc integer;
begin
  -- create valid dummy statement > 32k via comments
  l_stmt := 'begin dbms_stats.gather_schema_stats(user); /* ';
  -- random garbage as comments
  for i in 1..20 loop
    l_stmt := l_stmt || dbms_random.string('a', 4000);
  end loop;
  l_stmt := l_stmt || ' */ end;';
  -- for debug only
  dbms_output.put_line('l_stmt size: ' || length(l_stmt)
    || ' chunks ' || ceil(length(l_stmt)/32767));

  -- build remote bind list dynamically
  l_remote_args := ' to_clob(:b1)';
  for i in 2..ceil(length(l_stmt)/32767) loop
    l_remote_args := l_remote_args || ' || to_clob(:b' || i || ')';
  end loop;
  -- for debug only
  dbms_output.put_line('l_remote_args: ' || l_remote_args);

  -- build remote statement, including constructed list of binds
  l_remote_stmt := q'[
declare
  r_stmt clob := ]' || l_remote_args || q'[;
  r_c pls_integer;
  r_rc pls_integer;
  pragma autonomous_transaction;
begin
  r_c := dbms_sql.open_cursor;
  dbms_sql.parse(r_c, r_stmt, dbms_sql.native);
  r_rc := dbms_sql.execute(r_c);
  dbms_sql.close_cursor(r_c);
end;
]';

  l_c := dbms_sql.open_cursor@dblink3;

  -- parse full remote statement including generated binds  
  dbms_sql.parse@dblink3(l_c, l_remote_stmt, dbms_sql.native);

  -- bind variables using chunks of original CLOB
  for i in 1..ceil(length(l_stmt)/32767) loop
    dbms_sql.bind_variable@dblink3(l_c, 'B' || i,
      dbms_lob.substr(l_stmt, 32767, ((i-1) * 32767) + 1));
    l_remote_args := l_remote_args || ' || :b' || i;
  end loop;
  l_rc := dbms_sql.execute@dblink3(l_c);

  dbms_sql.close_cursor@dblink3(l_c);
end;
/

l_stmt size: 80055 chunks 3
l_remote_args:  to_clob(:b1) || to_clob(:b2) || to_clob(:b3)

PL/SQL procedure successfully completed.

为此,我将DDL写入一个文件,然后使用sqlplus包含该文件。下面是我向所有Oracle实例分发管理包的示例:

c:\temp\distribute.sql保存我的DDL

sqlplus /nolog
connect /@db1 as sysdba
@c:\temp\distribute.sql
connect /@db2 as sysdba
@c:\temp\distribute.sql
...
connect /@dbN as sysdba
@c:\temp\distribute.sql

谢谢你的帮助。我已经发送了负责执行DDL的包
通过dblink(该包适合varchar2)连接到远程服务器,然后我从1通过dblink从所有源获取包sourec。db。它正在工作,因为视图中记录了一行程序包

看看这个,可能会有帮助。

谢谢@Phonolog,明白了。 你是对的,它实际上并没有回答这个问题,但它是一个可能有帮助的方向

那么这个怎么样。
能否将dbms_sql parse重载与dbms_sql.varchar2a类型(一个varchar2(32767)表)一起使用?
根据这个例子:

DECLARE  
  vi_handler        INTEGER;  
  vi_numberOfRows   INTEGER;  
  ddl_statement     DBMS_SQL.varchar2a@some_dblink;  
BEGIN  
  ddl_statement(1) := 'create or replace function get_dual return varchar2 as ';  
  ddl_statement(2) := 'v_dual varchar2(1);';  
  ddl_statement(3) := 'begin ';  
  ddl_statement(4) := '    select * into v_dual from dual;';  
  ddl_statement(5) := '    return v_dual;';  
  ddl_statement(6) := 'end get_dual;';  
  vi_handler := DBMS_SQL.open_cursor@some_dblink;  
  DBMS_SQL.parse@some_dblink (vi_handler, ddl_statement, 1, ddl_statement.count, true, DBMS_SQL.native);  
  vi_numberOfRows := DBMS_SQL.execute@some_dblink (vi_handler);  
  DBMS_SQL.close_cursor@some_dblink (vi_handler);  
END; 

亚历克斯,是的。我试过VARCHAR2(32767)似乎是一个奇怪的设置。是否有合理的最大尺寸?您能够在远程数据库上创建过程吗?如果是这样,您可以在那里创建一个包装过程,在本地(远端)调用
dbms\u sql
,然后可以使用多个varchar2参数(数量取决于您期望的最大大小),并将CLOB拆分为32K个块,用于远程调用,然后重新组装成CLOB,用于另一端的动态调用?我无法在远程上创建过程。我不知道语句的最大大小,这就是为什么我要使用clob。这些DDL是包,可能是使用dblink创建包的某种方式?您可以在远程节点上创建包,但不能创建过程?我知道您不知道实际的固定最大值,但您可以非常肯定它不会是4G,并且可以接受较低的限制;例如,如果您可以合理地说它永远不会超过320KB,那么您可以将其分成10个块,并让您的过程使用10个参数重新组合。但是,如果您不能在远程数据库上创建过程(或包!?),这是一个没有意义的问题。谢谢您,Alex!我会试试类似的,但是明天。我会让你know@vis-添加了一个更灵活的版本,您可以将真正的DDL插入其中(而不是我正在生成的伪DDL)。您好。虽然链接通常是有用的,但答案应该包含更多的解释,请参见。