I have the below PL SQL Block:
WHENEVER SQLERROR EXIT 1
SET SERVEROUTPUT ON
DECLARE
v_sql VARCHAR2(500);
f1 VARCHAR2(20) := \'abc\';
p_procname VARCHAR2 (30)
As Amarillo said you can't execute a locally-defined procedure dynamically, as it doesn't exist in the SQL scope the dynamic section will be using.
The situation you describe is that all the procedures are defined in the anonymous block's DECLARE
section and you are running a query that tells you which of them to execute - and presumably which also gives you the arguments to pass. You can just use an if
/else
construct or a case
statement to execute the appropriate procedures, something like:
DECLARE
...
BEGIN
FOR data IN (SELECT procname, arg1, arg2, ... from <your_query>) LOOP
CASE data.procname
WHEN 'OPENLOG' THEN
openlog(data.arg1);
WHEN 'WRITELOG' THEN
writelog(data.arg1, data.arg2);
WHEN ...
...
ELSE
-- handle/report an invalid procedure name
-- or skip the `ELSE` and let CASE_NOT_FOUND be thrown
END CASE;
END LOOP;
END;
/
You just need one WHEN
condition and appropriate procedure call for each procedure. You can also either have an ELSE
to catch any unexpected procedure names or let the CASE_NOT_FOUND
exception (ORA-06592) be thrown, depending on what you need to happen if that ever occurs.
Use it like this:
DECLARE
v_sql VARCHAR2(500);
f1 VARCHAR2(20) := 'abc';
p_procname VARCHAR2 (30) := 'OPENLOG';
PROCEDURE OPENLOG (file_name IN VARCHAR2)
IS
BEGIN
NULL;
END;
BEGIN
DBMS_OUTPUT.PUT_LINE('Begin');
openlog(f1);
END;
You don't need to use execute immediate with begin end in this case, because you have the procedure in the declare section.
The other way is create the procedure as a database object like this:
CREATE PROCEDURE OPENLOG (file_name IN VARCHAR2)
IS
BEGIN
NULL;
END;
And the you can use execute immediate:
DECLARE
v_sql VARCHAR2(500);
f1 VARCHAR2(20) := 'abc';
p_procname VARCHAR2 (30) := 'OPENLOG';
BEGIN
DBMS_OUTPUT.PUT_LINE('Begin');
v_sql := 'BEGIN ' || p_procname || '(:a); END;';
EXECUTE IMMEDIATE v_sql USING IN f1;
END;