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

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;

Related

I want to create a table and insert a row in same table during run time using dynamic sql

I have created a procedure that accepts table name, 2 column names and 2 values that needed to be inserted on create table.
create or replace procedure SP_TABLE(P_TAB IN VARCHAR2,P_COL_1 IN VARCHAR2,P_COL_2 VARCHAR2,P_ID IN NUMBER,P_NAME IN VARCHAR2)
AS
v_sql varchar2(2000);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE '||P_TAB||' ('||P_COL_1||' NUMBER, '||P_COL_2 ||' VARCHAR2(20))';
v_sql := 'insert into ' ||P_TAB||'values (:1,:2)';
EXECUTE IMMEDIATE v_sql USING P_ID,P_NAME;
END;
/
The procedure was successfully created without any errors. However when i ran the below script i got error like 'ORA-00928: missing SELECT keyword
ORA-06512: at "SQL_PXADYXDREZPVPJSALLEGOZJOB.SP_TABLE", line 7'.
DECLARE
V_TAB VARCHAR2(20) := 'TABLE1';
V_COL1 VARCHAR2(20) := 'ID';
V_COL2 VARCHAR2(20) := 'NAME';
V_ID NUMBER := 1;
V_NAME VARCHAR2(20):= 'RAJA';
BEGIN
SP_TABLE(V_TAB,V_COL1,V_COL2,V_ID,V_NAME);
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_STACK);
END;
/
some one help me with this.
You can add space before values
create or replace procedure SP_TABLE(P_TAB IN VARCHAR2,P_COL_1 IN VARCHAR2,P_COL_2 VARCHAR2,P_ID IN NUMBER,P_NAME IN VARCHAR2)
AS
v_sql varchar2(2000);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE '||P_TAB||' ('||P_COL_1||' NUMBER, '||P_COL_2 ||' VARCHAR2(20))';
v_sql := 'insert into ' ||P_TAB||' values (:1,:2)';
EXECUTE IMMEDIATE v_sql USING P_ID,P_NAME;
END;
/

EXECUTE IMMEDIATE PL/SQL Block return type

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;

Passing table name and column name dynamically to PL/SQL Stored procedure

I am trying to pass table name and column name to a stored procedure in oracle , but it gives me following error: table or view does not exist
Below is the code:
create or replace procedure jz_dynamic_sql_statement
(p_table_name in varchar2,
p_col1_name in varchar2,
p_check_result out integer)
as
v_error_cd est_runtime_error_log.error_cd%type;
v_error_msg est_runtime_error_log.error_msg%type;
v_sql varchar2(1024);
v_result number(10);
begin
v_result := 0;
v_sql := 'select count(*) from ' || p_table_name ||' WHERE COLUMNNAME=' || p_col1_name;
execute immediate v_sql into v_result;
p_check_result := v_result;
end;
If the error coming back says the table does not exist then that means the table you pass in does not exist or the user that the procedure runs under cannot access it.
You could add a dbms_output.put_line statement to display the query that you are building and then try running it yourself, before you attempt the execute immediate. Then you know what errors you need to fix.
dbms_output.put_line('query : '||v_sql);
Be sure to turn on dbms_output.
Also, from what it looks like you are trying to do, you will need to pass the column name AND column value. Unless the tables you are querying will ALWAYS have the column name "COLUMNNAME".
Try this:
v_sql := 'select count(*) from ' || p_table_name ||' WHERE COLUMNNAME=''' || p_col1_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;