EXECUTE IMMEDIATE PL/SQL Block return type - sql

As part of a test I want to run a PL/SQL block using EXECUTE IMMEDIATE but when I try to fetch the result with INTO it always returns the same error regardless the content of the PL/SQL block I want to run.
DECLARE
l_output VARCHAR2(10);
BEGIN
EXECUTE IMMEDIATE 'BEGIN COMMIT; END;' INTO l_output;
END;
/
And the error is
ORA-01007: variable not in select list
I know this error has to with l_output not being the same type as the returning type by EXECUTE IMMEDIATE, but I don't know the type. I already tried to change l_output to CLOB, BLOB, NUMBER and nothing changes. Any idea?
OK, this is another example, same result.
DECLARE
l_output VARCHAR2(10);
BEGIN
EXECUTE IMMEDIATE 'BEGIN DBMS_OUTPUT.PUT_LINE(''TEST''); END;' INTO l_output;
END;
/

Oracle is complaining because your PL/SQL does not return anything for it to store in l_output. What are you expecting the value of l_output to be?
One would use EXECUTE IMMEDIATE...INTO with something like this to return a value from a PL/SQL block.
DECLARE
l_output VARCHAR2(10);
BEGIN
EXECUTE IMMEDIATE 'SELECT ''ABC'' FROM DUAL' INTO l_output;
dbms_output.put_line('l_output = ' || l_output);
END;
/
UPDATE
If you want, you can do this:
DECLARE
l_output VARCHAR2(10);
BEGIN
EXECUTE IMMEDIATE 'BEGIN :1 := 5; END;' USING IN OUT l_output;
dbms_output.put_line('l_output = ' || l_output);
END;

Related

How to properly call a PL/SQL function with dynamic SQL in it?

I wrote a PL/SQL function :-
CREATE OR REPLACE FUNCTION register_driver1(driver_name IN VARCHAR, pass_word IN VARCHAR) RETURN NUMBER AS
sql_stmt VARCHAR2(200);
driver_id NUMBER;
new_view_name VARCHAR(50);
BEGIN
sql_stmt := 'CREATE USER '||driver_name||' identified by '||pass_word;
EXECUTE IMMEDIATE sql_stmt;
sql_stmt := 'grant create session to '||driver_name;
EXECUTE IMMEDIATE sql_stmt;
driver_id := driver_ids.nextval;
new_view_name := 'vehicle_'||driver_name;
sql_stmt := 'CREATE VIEW '||new_view_name|| ' AS SELECT Model, Seats, reg_no FROM Vehicle WHERE(d_id='||driver_id||')';
EXECUTE IMMEDIATE sql_stmt;
sql_stmt := 'CREATE OR REPLACE TRIGGER reg_vehicle
INSTEAD OF INSERT ON '||new_view_name||
' FOR EACH ROW
DECLARE
vehicle_id NUMBER;
BEGIN
vehicle_id := vehicle_ids.nextval
INSERT INTO Vehicles VALUES(:NEW.Model, :NEW.Seats, :NEW.reg_no, vehicle_id, '||driver_id||');
END;';
EXECUTE IMMEDIATE sql_stmt;
sql_stmt := 'grant insert, update, select, delete on '||new_view_name||' to '||driver_name;
EXECUTE IMMEDIATE sql_stmt;
sql_stmt := 'grant select on PENDING_REQUESTS to '||driver_name;
EXECUTE IMMEDIATE sql_stmt;
RETURN driver_id;
END register_driver1;
/
But, I am not able to figure how to call it? Calling within a select query doesn't work because the function has DML and DDL statements.
Calling using the following :-
BEGIN
register_driver1('RoCK', 'wt893fdg$');
END;
/
shows the following error :-
Function REGISTER_DRIVER1 compiled
Elapsed: 00:00:00.009
ORA-06550: line 2, column 3: PLS-00221: 'REGISTER_DRIVER1' is not a procedure or is undefined ORA-06550: line 2, column 3: PL/SQL: Statement ignored
Both the call and function are executed in same worksheet and as ADMIN.. Yet, it shows that the procedure is undefined. Please help. SQL Developer Web of Oracle Cloud is used.
Your function returns a number. Call it in a pl/sql block where you assign the function to a variable of type number. The example below is a anonymous pl/sql block but you can also put this in a procedure of function.
DECLARE
l_driver_id NUMBER;
BEGIN
l_driver_id := register_driver1('RoCK', 'wt893fdg$');
-- rest of code ... --
END;

How to convert to procedure

I need help in creating this anonymous block into a procedure.
I am new to PLSQL. Any help will be much appreciated. Thank you in advance.
I would like this query to run just by calling a procedure.
TRUNCATE TABLE dblink_status_tbl;
set serveroutput on;
-- or equivalent for your client
declare
-- l_dummy dual.dummy%type;
l_dummy VARCHAR2(20);
l_status VARCHAR2(100);
begin
for r in
(select db_link from all_db_links where db_link in
( 'WP1',
'6P1',
'OP3',
'LP1',
'ODS')
and owner = 'CAMPER')
loop
begin
execute immediate 'select 1 from dual#' || r.db_link into l_dummy;
l_status:= 'ACTIVE';
dbms_output.put_line(r.db_link|| ',' || l_status);
rollback work;
execute immediate 'alter session close database link ' || r.db_link;
exception
when others then
l_status:= sqlerrm;
l_status := replace(replace(l_status,CHR(13), ' '),CHR(10),' ');
l_status := '"' || l_status || '"';
dbms_output.put_line(r.db_link|| ',' || l_status);
end;
insert into dblink_status_tbl
values(r.db_link,l_status);
commit;
end loop;
end;
Basically, you need only the first line in my example:
create or replace procedure p_your_proc as
-- from now on, it is your original code
l_dummy VARCHAR2(20);
l_status VARCHAR2(100);
begin
...
end;
/
Once it is created, run it as
begin
p_your_proc;
end;
/
P.S.
At the beginning, you're truncating a table - if it is necessary within the procedure, you'd use dynamic SQL (as it is a DDL):
begin
execute immediate ('TRUNCATE TABLE dblink_status_tbl');
...
end;
Or, simply delete its contents as
begin
delete from dblink_status_tbl;
...
end;
Make it like something:
create or replace procedure proc_name as
l_dummy VARCHAR2(20);
l_status VARCHAR2(100);
begin
...
Using l_dummy,l_status
end;
And run this like :
"Exec proc_name" or "execute proc_name"

How to execute a local procedure using execute immedate?

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) := 'OPENLOG';
PROCEDURE OPENLOG (file_name IN VARCHAR2)
IS
BEGIN
NULL;
END;
BEGIN
DBMS_OUTPUT.PUT_LINE('Begin');
v_sql := 'BEGIN ' || p_procname || '(:a); END;';
EXECUTE IMMEDIATE v_sql USING IN f1;
END;
/
When I execute the above block, I get the error:
DECLARE
*
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00201: identifier 'OPENLOG' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
ORA-06512: at line 19
However, if the procedure OPENLOG is part of a package, then it works fine.
Please advise how to execute a local procedure using dynamic SQL.
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;

Set an out parameter value from a pl/sql block stored in a clob column

I have the following table and relevant column..
TABLEA
SQL_SCRIPT CLOB
Here is a procedure that executes the pl/sql block in the clob..
Create procedure (p_exit_code IN OUT NUMBER)
AS
V_sql_val sql_script%TYPE;
…
BEGIN
Select sql_script into v_sql_val from tablea;
EXECUTE IMMEDIATE sql_script;
END;
In SQL_SCRIPT I am trying to do following in the exception handler:
EXCEPTION
WHEN EXCP_STOP_PROCESS THEN
p_exit_code := 1;
END;
I need to set the value of p_exit_code parameter in procedure from the clob script. How can I do this?
If I understand correctly, you need to set the value of a variable within sql_script and return this to the calling procedure. This can be achieved using bind variables. A very simple test case is below:
declare
-- ':return_code' is the bind variable
sql_script varchar2(1000) := 'begin :return_code := 1; end;';
vn_result number;
begin
execute immediate sql_script using out vn_result;
dbms_output.put_line(vn_result);
end;

Oracle function dynamic column

I have a function, and I want to determine the name of the column in run time. For this I am passing one variable as an argument, like column_name.
Below is the code with the function:
l_column_name as varchar2(100)
Begin
If(column_name='emp_name')
Then
l_column_name:=EMPLOYEE.EMP_NAME
End If;
begin
select l_column_name from employee
end;
In above code, l_column_name:=EMPLOYEE.EMP_NAME is giving the error
Not allowed in this context.
Any help is much appreicated.
Regards,
Chaitu
As the error says, you can not do this.
You need to look into using the PL/SQL Execute Immediate: http://docs.oracle.com/cd/B14117_01/appdev.101/b10807/13_elems017.htm
declare
l_column_name as varchar2(100);
l_column_results as VARCHAR2(100);
begin
if (column_name = 'emp_name') then
l_column_name := 'EMPLOYEE.EMP_NAME';
end if;
query := 'SELECT ' || l_column_name || ' from employeee';
EXECUTE IMMEDIATE query INTO l_column_results;
end;