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"
Related
I have created a procedure and it requires Dynamic SQL block.
Procedure is as below which complies correctly but when I try to run it , it gives the below error.
I have tried to use cast() for all the variable which are used here. But this does not help.
Also, I am trying to run this procedure on Oracle using SQL - Developer tool.
Once the dynamic SQL is stored in sqlquery variable I would try and run the dynamic query and display its results!
**Output**
*Connecting to the database XXX.
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at "XXX.PROCEDURE2", line 28
ORA-06512: at line 6
Who are you ?
Process exited.
Disconnecting from the database XXX.*
Procedure
*create or replace PROCEDURE PROCEDURE2 (var_owner IN dba_tab_cols.owner%TYPE) AS
CURSOR col_ident IS
select col1_name,col2,col3,col4,nullable,'_NM' as s_type
from table_name
where col2 in var_owner
and col3 like 'exp%%'
and col4 like 'exp2%';
sample_record col_ident%ROWTYPE;
BEGIN
OPEN col_ident;
LOOP
FETCH col_ident INTO sample_record;
DECLARE
col_nm VARCHAR2(30);
tab_nm VARCHAR2(30);
schema_nm VARCHAR2(30);
--a1 VARCHAR2(30);
sqlquery VARCHAR2(400);
BEGIN
col_nm := sample_record.col2;
tab_nm := sample_record.col3;
schema_nm := sample_record.col1_name;
IF sample_record.col4 = 'VARCHAR2' THEN
DBMS_OUTPUT.PUT_LINE('Who are you ? ');
sqlquery:= ('select distinct ' +col_nm+ 'from' + schema_nm+'.'+tab_nm + 'where rownum < 1');
DBMS_OUTPUT.PUT_LINE('Do you know me ');
END IF;
EXIT WHEN col_ident%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(sample_record.col1_name||','||sample_record.col3
||','||sample_record.col2||','||sample_record.col4||','||
sample_record.col5||','||sample_record.nullable||','||
sample_record.s_type);
END;
END LOOP;
--Close CURSOR col_ident
CLOSE col_ident;
END PROCEDURE2;*
I am trying to call the following PL SQL procedure from ODI. The code is runnig perfectly in SQL Developer.
create or replace PROCEDURE OMS_UPD_CODE_STATO (myUID_ELAB IN NUMBER, myTABLE_NAME IN VARCHAR2, P_RESULT OUT VARCHAR2) AS
C_TREC NUMBER;
C_TOT NUMBER;
str1 varchar2(1000);
str2 varchar2(1000);
BEGIN
str1:='select to_number(rtrim(substr(utente, 18,23),''0'')) from '|| myTABLE_NAME || ' where TREC =''99'' AND UID_ELAB = '||myUID_ELAB;
EXECUTE IMMEDIATE STR1 INTO C_TREC;
str2:='select count(*) from '|| myTABLE_NAME || ' where TREC is not null';
EXECUTE IMMEDIATE STR2 INTO C_TOT;
IF C_TOT = C_TREC THEN
execute immediate 'update'|| myTABLE_NAME || 'set CODE_STATO = ''N'' WHERE UID_ELAB = '|| myUID_ELAB;
P_RESULT:='OK';
commit;
ELSE
P_RESULT:='KO';
END IF;
END OMS_UPD_CODE_STATO;
In ODI I wrote the procedure as follow:
declare
P_RESULT VARCHAR2(100);
begin
OMS_UPD_CODE_STATO( #OMS.UID_ELAB, #OMS.ATTR_NOME_TABELLA, P_RESULT);
end;
where the first two parameters are INPUT parm while the third one, P_RESULT, is the OUT param present inside the PL SQL Stored Procedure. By calling the procedure from ODI I get the following error:
ODI-1228: Task Procedure-omsUpdateStato-callStored fails on the target connection OMS_DEV.
Caused By: java.sql.SQLException: ORA-06550: line 5, column 27:
PLS-00357: Table,View Or Sequence reference 'OMS_S_PERSONE_FISICHE' not allowed in this context
ORA-06550: line 5, column 1:
PL/SQL: Statement ignored
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:466)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:407)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1113)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:546)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:269)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:603)
at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:218)
at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:31)
at oracle.jdbc.driver.T4CStatement.executeForRows(T4CStatement.java:1000)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1316)
at oracle.jdbc.driver.OracleStatement.executeInternal(OracleStatement.java:2168)
at oracle.jdbc.driver.OracleStatement.execute(OracleStatement.java:2100)
at oracle.jdbc.driver.OracleStatementWrapper.execute(OracleStatementWrapper.java:363)
at oracle.odi.runtime.agent.execution.sql.SQLCommand.execute(SQLCommand.java:205)
at oracle.odi.runtime.agent.execution.sql.SQLExecutor.execute(SQLExecutor.java:141)
at oracle.odi.runtime.agent.execution.sql.SQLExecutor.execute(SQLExecutor.java:28)
at oracle.odi.runtime.agent.execution.TaskExecutionHandler.handleTask(TaskExecutionHandler.java:52)
at oracle.odi.runtime.agent.execution.SessionTask.processTask(SessionTask.java:206)
at oracle.odi.runtime.agent.execution.SessionTask.doExecuteTask(SessionTask.java:117)
at oracle.odi.runtime.agent.execution.AbstractSessionTask.execute(AbstractSessionTask.java:886)
at oracle.odi.runtime.agent.execution.SessionExecutor$SerialTrain.runTasks(SessionExecutor.java:2227)
at oracle.odi.runtime.agent.execution.SessionExecutor.executeSession(SessionExecutor.java:611)
at oracle.odi.runtime.agent.processor.TaskExecutorAgentRequestProcessor$1.doAction(TaskExecutorAgentRequestProcessor.java:719)
at oracle.odi.runtime.agent.processor.TaskExecutorAgentRequestProcessor$1.doAction(TaskExecutorAgentRequestProcessor.java:611)
at oracle.odi.core.persistence.dwgobject.DwgObjectTemplate.execute(DwgObjectTemplate.java:203)
at oracle.odi.runtime.agent.processor.TaskExecutorAgentRequestProcessor.doProcessStartAgentTask(TaskExecutorAgentRequestProcessor.java:801)
at oracle.odi.runtime.agent.processor.impl.StartSessRequestProcessor.access$1400(StartSessRequestProcessor.java:74)
at oracle.odi.runtime.agent.processor.impl.StartSessRequestProcessor$StartSessTask.doExecute(StartSessRequestProcessor.java:702)
at oracle.odi.runtime.agent.processor.task.AgentTask.execute(AgentTask.java:180)
at oracle.odi.runtime.agent.support.DefaultAgentTaskExecutor$2.run(DefaultAgentTaskExecutor.java:108)
at java.lang.Thread.run(Thread.java:745)
Do you have any idea how to solve it? Am I doing any wrong call?
Thank you
I am trying to run the following stored procedure:
CREATE OR REPLACE PROCEDURE RETRY_TRANS_EXCEPTION
AS
BEGIN
FOR i IN 1..5 LOOP
DBMS_OUTPUT.PUT('Try #' || i);
ALTER TABLE CIS_CASE ADD TEST01 varchar2(1) NOT NULL;
END;
END;
/
and calling it in changelog.xml as:
<sql>CALL RETRY_TRANS_EXCEPTION();</sql>
i get error:
Liquibase update Failed: Migration failed for change set eldata-changelog.xml::2016-08-25-cn-01::Ch Will:Reason: liquibase.exception.DatabaseException: Error executing SQL CALL RETRY_TRANS_EXCEPTION(): ORA-06575: Package or function RETRY_TRANS_EXCEPTION is in an invalid state
What i am trying to achieve is to be able to run a stored procedure through Liquibase with a loop in it.
Thanks for your help Prashant. What worked in my case was your solution plus one change:
CREATE OR REPLACE PROCEDURE RETRY_TRANS_EXCEPTION
AS
v_query varchar2(100);
BEGIN
FOR i IN 1..500 LOOP
DBMS_OUTPUT.PUT('Try #' || i);
v_query := 'ALTER TABLE CIS_CASE ADD TEST01 varchar2(1) NULL';
execute immediate v_query;
END loop;
END;
/
and then calling the Stored Procedure from the changelog, as:
<changeSet id="2016-08-25-cw-01" author="Ch Will">
<comment>
Testing retry logic on liquibase
</comment>
<sql>CALL RETRY_TRANS_EXCEPTION();</sql>
</changeSet>
You can't call it because the procedure does not compile correctly. Go back and fix the compilation errors, then try again.
Here are a couple of errors that stand out to me:
the for loop should end with end loop;, not end;
You can't have DDL statements directly in the code. You need dynamic SQL to execute a DDL statement from a procedure: execute immediate 'ALTER TABLE CIS_CASE ADD TEST01 varchar2(1) NOT NULL';
Additional note: I don't understand why you are trying to execute the same DDL statement multiple times inside a loop. Obviously, you won't be able to add the same column with the same name over and over. You will get a runtime error.
SQL> CREATE OR REPLACE PROCEDURE RETRY_TRANS_EXCEPTION
2 AS
3 BEGIN
4 FOR i IN 1..5 LOOP
5 DBMS_OUTPUT.PUT('Try #' || i);
6 ALTER TABLE CIS_CASE ADD TEST01 varchar2(1) NOT NULL;
7 END;
8 END;
9 /
Warning: Procedure created with compilation errors
SQL> show err
Errors for PROCEDURE PRASHANT-MISHRA.RETRY_TRANS_EXCEPTION:
LINE/COL ERROR
-------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
6/4 PLS-00103: Encountered the symbol "ALTER" when expecting one of the following: ( begin case declare end exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge
Did required fixes :
CREATE OR REPLACE PROCEDURE RETRY_TRANS_EXCEPTION
AS
v_query varchar2(100);
BEGIN
FOR i IN 1..5 LOOP
DBMS_OUTPUT.PUT('Try #' || i);
v_query := 'ALTER TABLE CIS_CASE ADD TEST01 varchar2(1) NOT NULL' ;
execute immediate v_query;
END loop;
END;
PLSQL stored procedure can't use DDL statements, like
alter table ...
so the
execute immediate ("...")
statement is required because in fact it creates an autonomous implicit transition that can't be rollbacked
I am trying to execute the below plsql program, but facing expression of wrong type. Could anyone let me know what might be the error?
CREATE OR REPLACE PROCEDURE CLN_TBL (CTRLM IN VARCHAR2, CTG IN VARCHAR,SBCT IN NUMBER, RTDT IN NUMBER )
AS
V_SQL VARCHAR(2000);
V_TABLE VARCHAR(30);
CURSOR TBL_CUR
IS
SELECT TGT_TABLE_NAME FROM ODS_USER.CLNP WHERE CONTROLM=CTRLM AND APPL_CTGY=CTG AND APPL_SUB_CTGY= SBCT;
L_TGT_TABLE_NAME TBL_CUR%ROWTYPE;
BEGIN
OPEN TBL_CUR;
LOOP
FETCH TBL_CUR INTO L_TGT_TABLE_NAME;
V_TABLE:= L_TGT_TABLE_NAME ;
EXIT WHEN TBL_CUR%NOTFOUND;
V_SQL:='DELETE FROM '||V_TABLE||' WHERE RPT_DT_ID'||'=:1';
EXECUTE IMMEDIATE V_SQL using RTDT;
END LOOP;
COMMIT;
CLOSE TBL_CUR;
END;
As Exhausted said you cant assign row variable to varchar so You should take TGT_TABLE_NAME from row variable, like below should work;
CREATE OR REPLACE PROCEDURE CLN_TBL (CTRLM IN VARCHAR2, CTG IN VARCHAR,SBCT IN NUMBER, RTDT IN NUMBER )
AS
V_SQL VARCHAR(2000);
V_TABLE VARCHAR(30);
CURSOR TBL_CUR
IS
SELECT TGT_TABLE_NAME FROM ODS_USER.CLNP WHERE CONTROLM=CTRLM AND APPL_CTGY=CTG AND APPL_SUB_CTGY= SBCT;
L_TGT_TABLE_NAME TBL_CUR%ROWTYPE;
BEGIN
OPEN TBL_CUR;
LOOP
FETCH TBL_CUR INTO L_TGT_TABLE_NAME;
V_TABLE:= L_TGT_TABLE_NAME.TGT_TABLE_NAME ;
EXIT WHEN TBL_CUR%NOTFOUND;
V_SQL:='DELETE FROM '||V_TABLE||' WHERE RPT_DT_ID'||'=:1';
EXECUTE IMMEDIATE V_SQL using RTDT;
END LOOP;
COMMIT;
CLOSE TBL_CUR;
END;
When I create the following procedure
create or replace procedure check_exec_imm(
tab IN VARCHAR2,
col IN VARCHAR2,
col_name IN VARCHAR2
)
IS
cv SYS_REFCURSOR;
col_value VARCHAR2(32767);
lv_query VARCHAR2(32767);
BEGIN
lv_query := 'SELECT ' ||col||
' FROM ' ||tab||
' WHERE (:1 = ''EUR'' OR :1 = ''USD'') and rownum <=1';
EXECUTE IMMEDIATE lv_query INTO col_value USING col_name ;
DBMS_OUTPUT.PUT_LINE('COLUMN VALUE : ' || col_value);
END;
When the procedure is executed, I'm getting the following error
ORA-01008: not all variables bound
ORA-06512: at "GRM_IV.CHECK_EXEC_IMM", line 18
ORA-06512: at line 2
When I give the bind argument col_name again as below, the procedure is running fine.
EXECUTE IMMEDIATE lv_query INTO col_value USING col_name, col_name ;
Why oracle is behaving differently in this procedure. Since, it is the same bind variable, one bind argument should be sufficient right..!!? Please explain where I'm getting my logic wrong.
There is "special" behaviour in Oracle: Repeated Placeholder Names in Dynamic SQL Statements
In an Anonymous Block or CALL Statement it is not required to repeat the bind values if the names are equal.
For example this Anonymous Block is working:
DECLARE
a NUMBER := 4;
b NUMBER := 7;
plsql_block VARCHAR2(100);
BEGIN
plsql_block := 'BEGIN calc_stats(:x, :x, :y, :x); END;';
EXECUTE IMMEDIATE plsql_block USING a, b; -- calc_stats(a, a, b, a)
END;
/
But this EXECUTE IMMEDIATE plsql_block USING a, b; does not work inside a Procedure.
The way you have referenced the column name through bind variable is not a preferred method as Nichoas pointed out. What you tried is called as native dynamic SQL using 'cleverer' bind variables.
In this method, you need to bind every parameter X times since you use it X times because they are all treated as separate variables.
Read more on binding to dynamic SQL.
#ethan and #ManiSankar I too had a same problem in my scenario as well. I solved this using some kind of brute force techinque. What i have done is
Before this
EXECUTE IMMEDIATE lv_query INTO col_value USING col_name ;
I have added replace condition in my code by replacing parameter with the required value then called "Execute Immediate" without "using" clause
lv_query := replace(lv_query, ':1',col_name);
EXECUTE IMMEDIATE lv_query INTO col_value;
I don't know this is optimal one but served purpose for what i am expecting..
Please advice if this one recommended or not...