2016-12-05 5 views
0

фонКак я могу создать весь блок кода PL/SQL с переменными привязки?

Я пытаюсь сделать PL процедуру/SQL многоразовой для перемещения данных из одной базы данных в другую.

Для этой цели я использую динамический SQL.

Процедура выполняется отлично, если я использую REPLACE с заполнителями. Однако из соображений безопасности я хочу использовать переменные связывания.


Вопрос

Как я могу сделать весь PL/SQL блок кода динамический (с связываемых переменных)? Если я использую REPLACE вместо переменных привязки, он отлично работает .


Как реплицировать

Чтобы повторить это в вашей базе данных, создайте следующую процедуру, как это:

create or replace procedure move_data(i_schema_name in varchar2, i_table_name in varchar2, i_destination in varchar2) as 
l_sql varchar2(32767); 
l_cursor_limit pls_integer := 500; 
l_values_list varchar2(32767); 

begin 

select listagg('l_to_be_moved(i).' || column_name, ', ') within group (order by column_id) 
into l_values_list 
from all_tab_cols 
where owner = i_schema_name and 
     table_name = i_table_name and 
     virtual_column = 'NO'; 

l_sql := q'[ 
declare 
l_cur_limit pls_integer := :l_cursor_limit; 

cursor c_get_to_be_moved is 
select :i_table_name.*, :i_table_name.rowid 
from :i_table_name; 

type tab_to_be_moved is table of c_get_to_be_moved%rowtype; 

l_to_be_moved tab_to_be_moved; 

begin 

open c_get_to_be_moved; 
loop 
    fetch c_get_to_be_moved 
    bulk collect into l_to_be_moved limit l_cur_limit; 
    exit when l_to_be_moved.count = 0;  

    for i in 1.. l_to_be_moved.count loop 
     begin 
      insert into :[email protected]:i_destination values (:l_values_list); 
     exception 
     when others then 
      dbms_output.put_line(sqlerrm); 
      l_to_be_moved.delete(i); 
     end;  
    end loop; 
    forall i in 1.. l_to_be_moved.count 
    delete 
    from :i_table_name 
    where rowid = l_to_be_moved(i).rowid;  

    for i in 1..l_to_be_moved.count loop 
     if (sql%bulk_rowcount(i) = 0) then 
      raise_application_error(-20001, 'Could not find ROWID to delete. Rolling back...');   
     end if; 
    end loop;  
    commit; 
end loop;   
close c_get_to_be_moved; 

exception 
when others then 
    rollback; 
    dbms_output.put_line(sqlerrm); 
end;]'; 
execute immediate l_sql using l_cursor_limit, i_table_name, i_destination, l_values_list; 
exception 
when others then 
    rollback; 
    dbms_output.put_line(sqlerrm); 
end; 
/

И тогда вы можете выполнить процедура со следующим:

begin 
    move_data('MySchemaName', 'MyTableName', 'MyDatabaseLinkName'); 
end; 
/
+2

Идентификаторы (имя таблицы, имя схемы и т. Д.) Не могут быть связаны. –

+0

@NicholasKrasnov Спасибо! Наверное, я просто проверю входные переменные. Не могли бы вы поместить то же самое, что и ответ, чтобы я мог закрыть это? – Zesty

ответ

3

Из-за многих причин (невозможность создания соответствующего плана выполнения, проверки безопасности и т. Д.) Oracle не позволяет привязывать идентификаторы (имена таблиц, имена схем, имена столбцов и т. Д.). Поэтому, если это действительно необходимо, единственный способ - жестко закодировать эти идентификаторы после некоторой проверки (для предотвращения внедрения SQL).

1

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

установки:

create table tab100 as select level l from dual connect by level <= 100; 
create table tab200 as select level l from dual connect by level <= 200; 
create table tabDest as select * from tab100 where 1 = 2; 

Это не будет работать:

create or replace procedure testBind (pTableName in varchar2) is 
    vSQL varchar2(32000); 
begin 
    vSQL := 'insert into tabDest select * from :tableName'; 
    execute immediate vSQL using pTableName; 
end; 

Но это будет делать трюк:

create or replace procedure testBind2 (pTableName in varchar2) is 
    vSQL varchar2(32000); 
begin 
    vSQL := q'[declare 
       vTab varchar2(30) := :tableName; 
       vSQL2 varchar2(32000) := 'insert into tabDest select * from ' || vTab; 
       begin 
       execute immediate vSQL2; 
       end; 
       ]'; 
    execute immediate vSQL using pTableName; 
end; 
+0

+1 для усилий. Однако цель побеждена, так как нам все равно придется конкатенировать строку в конце, которая до сих пор оставляет нас открытой для SQL-инъекции. – Zesty

+0

Почему бы не прямо 'vSQL: = 'вставить в tabDest выбрать * from' || tableName;'? –

+0

@WernfriedDomscheit См. Таблицу Name: = 'MyTable; drop MyTable '; – Zesty

1

Я думаю, что вы можете сделать это проще.

create or replace procedure move_data(i_schema_name in varchar2, i_table_name in varchar2, i_destination in varchar2) as 
l_sql varchar2(32767); 

begin 



select listagg('l_to_be_moved(i).' || column_name, ', ') within group (order by column_id) 
into l_values_list 
from all_tab_cols 
where owner = i_schema_name and 
     table_name = i_table_name and 
     virtual_column = 'NO'; 

l_sql := 'insert into '||i_destination||'.'||i_table_name||' select * from '||i_schema_name||'.'||i_table_name; 

execute immediate l_sql; 

end; 

Если вы обеспокоены SQL-инъекций, взглянуть на пакет DBMS_ASSERT. Этот пакет PL/SQL предоставляет функцию проверки свойств входных значений.

+0

+1 для DBMS_ASSERT. Причина, по которой я делаю это по X строк за раз, заключается в том, чтобы избежать массового уничтожения. Это делается для перемещения очень больших таблиц аудита. – Zesty