2017-02-03 1 views
1

Я пытаюсь удалить список процедур после того, как Все этих процедур выполнены успешно. Таким образом, если какая-либо из процедур не удалась, ее можно отладить без потери.Удаление списка процедур

Однако я получаю ORA-04021: тайм-аут произошел во время ожидания блокировки объекта

Вот код.

Пакет функция: тело

create or replace package remove_procedures AUTHID CURRENT_USER is 

procedure dropProcedures(p_schema varchar2, p_type varchar2, p_object_list varchar2); 
procedure dropProceduresName(p_schema varchar2, p_object_list varchar2); 

end remove_procedures; 

Пакета:

create or replace package body remove_procedures is 

-- type declaration for a collection of string 
type t_stringtab is table of varchar2(100); 

function listToTab (p_list in varchar2) 
    return t_stringtab is 
    l_str varchar2(32760) default p_list ||','; 
    l_n number; 
    l_retval t_stringtab := t_stringtab(); 
    l_item varchar2(100); 
begin 
    -- traverse the items in the comma seperated list into a nested table 
    loop 
    -- find the position of the first comma in l_str 
    l_n := instr(l_str, ','); 
    -- exit the loop if a comma was not found 
    exit when (nvl(l_n,0) = 0); 
    -- add an element to our collection to hold the next item parsed from the string 
    l_retval.extend; 
    -- parse the next item from the comma seperated string 
    l_item := ltrim(rtrim(substr(l_str,1,l_n-1))); 
    -- raise an exception if the length of the item exceeds 30 bytes (limit for oracle object names) 
    -- todo - add check for violations of other naming rules e.g. items beginning with a number 
    /* if lengthb(l_item) > 30 then 
     raise_application_error(-20000,'Error : 30 bytes limit exceeded for item : '|| l_item); --disabling this block for file names 
    end if; */ 
    -- otherwise add the item to the element in the collection 
    l_retval(l_retval.count) := l_item; 
    -- reset l_str variable to the remainder of the comma seperated list 
    l_str := substr(l_str, l_n+1); 
    end loop; 
    return l_retval; 
end listToTab; 

procedure dropProcedures(p_schema varchar2, p_type varchar2, p_object_list varchar2) is 
    l_objects t_stringtab := t_stringtab(); 
    l_object_found number; 
begin 
    l_objects := listToTab(p_object_list); 
    for i in l_objects.first .. l_objects.last loop 
    select count(*) into l_object_found 
    from user_objects 
    where upper(object_type) = upper(p_type) 
    and upper(object_name) = upper(l_objects(i)); 
    if l_object_found = 0 then 
     dbms_output.put_line('WARNING : Object '||upper(p_schema)||'.'||upper(l_objects(i))||' does not exist.'); 
    else 
     dbms_output.put_line('Dropping Object '||upper(p_type)||' named '||upper(p_schema)||'.'||upper(l_objects(i))||' now...'); 
     begin 
     execute immediate 'drop '||p_type||' '||upper(p_schema)||'.'||upper(l_objects(i)); 
     exception when others then 
     if sqlcode = -00942 then 
      null; 
     else 
      raise; 
     end if; 
     end; 
    end if; 
    end loop; 
end; 

Тогда вот код для выполнения всех процедур, и если все процедуры выполняются, то падение нескольких процедур.

create or replace procedure ref_master is 
begin 
    ref_categories; 
    ref_country; 
    ref_currency; 
    ref_currency_to_country; 
    promotions.remove_procedures.dropProcedures(
     p_schema => 'PROMOTIONS', 
     p_type => 'PROCEDURE', 
     p_object_list => 'ref_country,ref_currency,ref_currency_to_country'); 
end; 

А вот исполнение ref_master, и это, где я получаю ошибку упоминалось ранее:

begin 
    -- Call the procedure 
    ref_master; 
end; 

Удивительно, но когда я запускаю только определенные строки, то он работает котировка нормально:

begin 
promotions.remove_procedures.dropProcedures(
     p_schema => 'PROMOTIONS', 
     p_type => 'PROCEDURE', 
     p_object_list => 'ref_country,ref_currency,ref_currency_to_country'); 
end; 

Любые идеи о том, как я могу продолжить, я смогу отказаться от процедур, если все выполнено успешно, или немедленно прекратить работу, если какая-либо из процедур не была выполнена должным образом. Заранее спасибо :-)

+3

Почему вы хотите отказаться от процедур после их запуска? Процедуры не должны быть одноразовыми, – APC

+0

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

+0

По-прежнему звучит неправильно. Почему «изменение» означает «падение»? Почему бы не «создать или заменить»? – APC

ответ

1

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

Но есть проблема в одном из процедуры запускается до удаления, он занимает слишком много времени, и вы получите и исключение:

ORA-04021: timeout occurred while waiting to lock object 

Какой выход вы получаете от

set time on 

exec ref_categories; 
exec dbms_output.put_line('ref_categories:ok'); 
exec ref_country; 
exec dbms_output.put_line('ref_country:ok'); 
exec ref_currency; 
exec dbms_output.put_line('ref_currency:ok'); 
exec ref_currency_to_country; 
exec dbms_output.put_line('ref_currency_to_country:ok'); 
exec promotions.remove_procedures.dropProcedures(
    p_schema => 'PROMOTIONS', 
    p_type => 'PROCEDURE', 
    p_object_list => 'ref_country,ref_currency,ref_currency_to_country'); 

?

Мой анализ - одна из процедур занимает слишком много времени (и вы получаете этот «таймаут»), потому что таблица или что-то заблокировано другим действием Oracle (insert, update, ...). Но мы не можем догадываться.

Что вы можете сделать, это во время выполнения вашей процедуры, изучить сеансы базы данных (с помощью соответствующего инструмента, например TOAD, PL/SQL Developer), и посмотреть, какие объекты заблокированы.

Edit:

Мы могли бы ваш вопрос сводится на минимум как:

create or replace procedure test 
is 
begin 
    dbms_output.put_line('done'); 
end; 
/

begin 
    test; 
    execute immediate 'drop procedure test'; 
end; 

Вы получаете эту ошибку, потому что процедура не может быть удален из контекста они называются из.

Решение должно состоять из двух различных вызовов: запуска процедур, а затем запускать удаление.

Для sqlplus, чтобы избежать удаления, если процедуры не выполнялись правильно, используйте WHENEVER SQLERROR EXIT FAILURE ROLLBACK; как это (проверено)

WHENEVER SQLERROR EXIT FAILURE ROLLBACK 

create or replace procedure test 
is 
begin 
    dbms_output.put_line('done'); 
end; 
/

begin 
    test; 
end; 
/
begin 
    execute immediate 'drop procedure test'; 
end; 
/

из PLSQL Дев,

Я нашел то, что искал я думаю: execute immediate назвать свои процедуры:

begin 
    execute immediate 'begin test; end;'; 
    execute immediate 'drop procedure test'; 
end; 
/
+0

четкий способ вызова работал просто отлично. Благодаря :-) – Jaanna

Смежные вопросы