Oracle EXECUTE IMMEDIATE error - oracle

There is UserA and UserB in my oracle.
This is the package from UserA:
CREATE OR REPLACE PACKAGE BODY pkgA AS
PROCEDURE procA
AS
l_sql = 'BEGIN ' || UserB.procB || (:l_v1,:l_v2) END;';
EXECUTE IMMEDIATE l_sql USING IN l_v1,IN l_v2;
END;
Thie procB is come from UserB;
When I run this, I get the error:
PLS-00201:IDENTIFIER 'UserB.procB' must be declared;

User A need the EXECUTE right on the userb.procB.
grant the right as User B:
grant execute on UserB.procB to userA;

Unless it's supplied as a parameter, the procedure name needs to be inside the string as a fixed value; you have it outside so it's trying to be interpreted as a variable name, which doesn't exist:
l_sql = 'BEGIN UserB.procB(:l_v1,:l_v2) END;';
But then you wouldn't need to execut it dynamically anyway, you could just do:
PROCEDURE ProcA AS
BEGIN
UserB.procB(l_v1, l_v2);
END;
If you were passing the procedure as a variable, which would be a little odd, you'd have something like:
PROCEDURE procA (proc_name in varchar2) AS
BEGIN
l_sql = 'BEGIN ' || proc_name || '(:l_v1,:l_v2) END;';
EXECUTE IMMEDIATE l_sql USING IN l_v1,IN l_v2;
END;
... and you'd call that as procA('UserB.procB'). I don't think that's what you're trying to do, but it isn't entirely clear.
In both cases you don't seem to have l_v1 or l_v2 defined, so I guess you've just missed that part of the code out.

Related

Create Oracle sequence via execute immediate without pipe || operator

create sequence s1 ;
declare
v_value number;
v_sql_stmt varchar2(4000);
v_seq_name varchar2(30);
BEGIN
v_seq_name:='S1'; -- **this is dynamic and the sequence will be passed in the proc as input parameter at runtime**
v_sql_stmt:= 'select :v_seq_name'||'.nextval from dual' ;
EXECUTE IMMEDIATE v_sql_stmt INTO v_value USING v_seq_name ;
--**below is working but I dont want to do in this way because of sql injection issue, let me know how to fix the above**
--EXECUTE IMMEDIATE 'select ' || v_seq_name || '.nextval from dual' INTO v_value;
dbms_output.put_line(v_value);
end;
/
the above code is throwing error, please help to fix.
If you run the commented code then it will run but I dont want to use || in execute immediate. I want to use colon : only.
the sequence name will be passed at run time. The above code will be converted to a proc later.
I understand your concern about SQL injection. To my knowledge, table/column/sequence names cannot be specified with bind variables. However, you could do a simple check before executing the unsafe code:
CREATE SEQUENCE s1;
CREATE SEQUENCE s2;
CREATE OR REPLACE FUNCTION p(seq_name VARCHAR2) RETURN NUMBER AS
v_value number;
v_sql_stmt varchar2(4000);
v_seq_name varchar2(128 BYTE);
BEGIN
v_seq_name:= DBMS_ASSERT.SIMPLE_SQL_NAME(seq_name);
v_sql_stmt:= 'select '||v_seq_name||'.nextval from dual';
EXECUTE IMMEDIATE v_sql_stmt INTO v_value;
RETURN v_value;
END p;
/
If a valid name is used, everything works as expected:
select p('s1') from dual;
1
select p('s2') from dual;
2
However, if seq_name is not a valid Oracle name, DBMS_ASSERT throws an exception:
select p('1; DROP TABLE x') from dual;
ORA-44003: invalid SQL name
ORA-06512: at "SYS.DBMS_ASSERT", line 215
ORA-06512: at "WFL.P", line 6
44003. 0000 - "invalid SQL name"

bind variable does not exist

create or replace
PROCEDURE TEST3(CREATED_BY_IN IN VARCHAR2,SRC_NAME IN VARCHAR2,TGT_NAME IN VARCHAR2,OUTPUT OUT VARCHAR2)
IS
CHECK_STATUS INTEGER;
SRCSQL VARCHAR(1000);
BEGIN
SRCSQL:= 'INSERT INTO TBLCNT SELECT '||''''||CREATED_BY_IN||''''||','|| ' COUNT(*),' ||''''||SRC_NAME||''''||' FROM '||SRC_NAME ;
EXECUTE IMMEDIATE SRCSQL USING CREATED_BY_IN,SRC_NAME;
OUTPUT:=SRCSQL;
END;
Iam getting the error stating 'Bind variable does not exist'.
Can anyone please help me with this
You are performing a query with concatenated text and variable, so you can't use execute immediate with binded variables since they are not used in this case.
the right syntaxe is :
SRCSQL:= 'INSERT INTO TBLCNT SELECT :p1, COUNT(*), :p2 FROM :p2';
EXECUTE IMMEDIATE SRCSQL USING CREATED_BY_IN, SRC_NAME, SRC_NAME;
See documentation https://docs.oracle.com/cd/B13789_01/appdev.101/b10807/13_elems017.htm
You can use this without the help of bind variables. Hope below snippet helps .
CREATE OR REPLACE PROCEDURE TEST3(
CREATED_BY_IN IN VARCHAR2,
SRC_NAME IN VARCHAR2,
TGT_NAME IN VARCHAR2,
OUTPUT OUT VARCHAR2)
IS
CHECK_STATUS INTEGER;
SRCSQL VARCHAR(1000);
BEGIN
SRCSQL:= 'INSERT INTO TBLCNT SELECT '||''''||CREATED_BY_IN||''''||','|| ' COUNT(*),' ||''''||SRC_NAME||''''||' FROM '||SRC_NAME ;
EXECUTE IMMEDIATE SRCSQL;
OUTPUT:=SRCSQL;
END;

ORA-01031: insufficient privileges when executing rebuild index from Stored Procedure [duplicate]

Here is the definition of the stored procedure:
CREATE OR REPLACE PROCEDURE usp_dropTable(schema VARCHAR, tblToDrop VARCHAR) IS
BEGIN
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_cnt
FROM all_tables
WHERE owner = schema
AND table_name = tblToDrop;
IF v_cnt > 0 THEN
EXECUTE IMMEDIATE('DROP TABLE someschema.some_table PURGE');
END IF;
END;
END;
Here is the call:
CALL usp_dropTable('SOMESCHEMA', 'SOME_TABLE');
For some reason, I keep getting insufficient privileges error for the EXECUTE IMMEDIATE command. I looked online and found out that the insufficient privileges error usually means the oracle user account does not have privileges for the command used in the query that is passes, which in this case is DROP. However, I have drop privileges. I am really confused and I can't seem to find a solution that works for me.
Thanks to you in advance.
SOLUTION:
As Steve mentioned below, Oracle security model is weird in that it needs to know explicitly somewhere in the procedure what kind of privileges to use. The way to let Oracle know that is to use AUTHID keyword in the CREATE OR REPLACE statement. If you want the same level of privileges as the creator of the procedure, you use AUTHID DEFINER. If you want Oracle to use the privileges of the user currently running the stored procedure, you want to use AUTHID CURRENT_USER. The procedure declaration looks as follows:
CREATE OR REPLACE PROCEDURE usp_dropTable(schema VARCHAR, tblToDrop VARCHAR)
AUTHID CURRENT_USER IS
BEGIN
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_cnt
FROM all_tables
WHERE owner = schema
AND table_name = tblToDrop;
IF v_cnt > 0 THEN
EXECUTE IMMEDIATE('DROP TABLE someschema.some_table PURGE');
END IF;
END;
END;
Thank you everyone for responding. This was definitely very annoying problem to get to the solution.
Oracle's security model is such that when executing dynamic SQL using Execute Immediate (inside the context of a PL/SQL block or procedure), the user does not have privileges to objects or commands that are granted via role membership. Your user likely has "DBA" role or something similar. You must explicitly grant "drop table" permissions to this user. The same would apply if you were trying to select from tables in another schema (such as sys or system) - you would need to grant explicit SELECT privileges on that table to this user.
You should use this example with AUTHID CURRENT_USER :
CREATE OR REPLACE PROCEDURE Create_sequence_for_tab (VAR_TAB_NAME IN VARCHAR2)
AUTHID CURRENT_USER
IS
SEQ_NAME VARCHAR2 (100);
FINAL_QUERY VARCHAR2 (100);
COUNT_NUMBER NUMBER := 0;
cur_id NUMBER;
BEGIN
SEQ_NAME := 'SEQ_' || VAR_TAB_NAME;
SELECT COUNT (*)
INTO COUNT_NUMBER
FROM USER_SEQUENCES
WHERE SEQUENCE_NAME = SEQ_NAME;
DBMS_OUTPUT.PUT_LINE (SEQ_NAME || '>' || COUNT_NUMBER);
IF COUNT_NUMBER = 0
THEN
--DBMS_OUTPUT.PUT_LINE('DROP SEQUENCE ' || SEQ_NAME);
-- EXECUTE IMMEDIATE 'DROP SEQUENCE ' || SEQ_NAME;
-- ELSE
SELECT 'CREATE SEQUENCE COMPTABILITE.' || SEQ_NAME || ' START WITH ' || ROUND (DBMS_RANDOM.VALUE (100000000000, 999999999999), 0) || ' INCREMENT BY 1'
INTO FINAL_QUERY
FROM DUAL;
DBMS_OUTPUT.PUT_LINE (FINAL_QUERY);
cur_id := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.parse (cur_id, FINAL_QUERY, DBMS_SQL.v7);
DBMS_SQL.CLOSE_CURSOR (cur_id);
-- EXECUTE IMMEDIATE FINAL_QUERY;
END IF;
COMMIT;
END;
/
you could use "AUTHID CURRENT_USER" in body of your procedure definition for your requirements.
Alternatively you can grant the user DROP_ANY_TABLE privilege if need be and the procedure will run as is without the need for any alteration. Dangerous maybe but depends what you're doing :)

Get input from user in PL/SQL procedure

I'm building a procedure which would require to get an input from user to print few details. But when I use & to get values it fails with errors. the logic is as follows..
DBMS_OUTPUT.PUT_LINE('Enter Y to display Unauthorized records OR N to skip the display');
--SELECT &1 INTO lv_choice FROM DUAL;
IF NOT ('&lv_choice'='Y') THEN
DBMS_OUTPUT.PUT_LINE ('RECORDS WILL NOT BE DISPLAYED');
ELSE
DBMS_OUTPUT.PUT_LINE ('RECORDS TO BE DISPLAYED ARE:');
......
I have tried using &1 into dual or directly calling &lv_choice which is failing with PLSQL internal errors.
Any methods to get input from user to proceed further in the procedure?
This isn't possible in PL/SQL - PL/SQL doesn't have access to the terminal(unless you do something like plug in Java or call your program from a something like SQL*Plus(in which you can use commands like ACCEPT/PROMPT before you run the procedure).
The & variables are substitution variables, and are specific to SQL*Plus, not PL/SQL
If you are using some UI Terminal like SQLDeveloper or TOAD, you can achieve it using below code:
CREATE OR REPLACE INPUTPROCEDURE (LV_CHOICE IN VARCHAR2)
AS
BEGIN
DBMS_OUTPUT.PUT_LINE('Enter Y to display Unauthorized records OR N to skip the display');
--SELECT &1 INTO lv_choice FROM DUAL;
IF lv_choice <> 'Y' THEN
DBMS_OUTPUT.PUT_LINE ('RECORDS WILL NOT BE DISPLAYED');
ELSE
DBMS_OUTPUT.PUT_LINE ('RECORDS TO BE DISPLAYED ARE:');
END INPUTPROCEDURE;
And Invoke the above Procedure like below:
DECLARE
dyn_stmt VARCHAR2(200);
b BOOLEAN := TRUE;
BEGIN
dyn_stmt := 'BEGIN INPUTPROCEDURE(:LV_CHOICE); END;';
EXECUTE IMMEDIATE dyn_stmt USING b;
END;

Calling preocedure by passing DB Link Dynamically

How to pass db link dynamically while calling a procedure?
Execute immediate will work or we need to use dbms_sql?
For DBMS_SQL i seen it used mostly with curosrs :(
Can any one help me ?
You can use EXECUTE IMMEDIATE. Something like
DECLARE
l_dblink_name VARCHAR2(30) := 'YourDBLink';
l_sql_stmt VARCHAR2(1000);
BEGIN
l_sql_stmt := 'BEGIN procedure_name#' || l_dblink_name || ' (:1, :2); END;';
EXECUTE IMMEDIATE l_sql_stmt
USING 17, 42;
END;
assuming that your procedure takes two parameters and you want to call it with parameter values 17 and 42.

Resources