Running PLSQL ( Oracle ) Stored Procedure in Liquibase error : Package or function X is in an invalid state - oracle

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

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"

Oracle Database - EXECUTE IMMEDIATE inside dbms_parallel_execute

I've faced the following task: to parallel some process on Oracle 12c database. As always, I've started making this using dbms_parallel_execute package. Created a new task using dbms_parallel_execute.run_task procedure and passed my anonymous block with stored procedure call as sql_stmt parameter.
l_sql := 'BEGIN my_procedure(:start_id, :end_id); END;';
dbms_parallel_execute.run_task
(
task_name => l_task_name,
sql_stmt => l_sql,
language_flag => dbms_sql.native,
parallel_level => l_parallel_level
);
This stored procedure calls a couple of other procedures.
As a result, my "parent" procedure fails with the following exception:
PLS-00103: Encountered the symbol "." when expecting one of the following:
( begin case declare 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
The symbol "<an identifier>" was substituted for "." to continue.
As I found out, this exception throws because of EXECUTE IMMEDIATE call inside on of the procedures:
l_sql := 'BEGIN ' || some_another_pkg.schema_name || '.some_pkg.some_procedure(:1, :2); END;'
EXECUTE IMMEDIATE l_sql
USING l_param_1, l_param_2l
Where schema_name is a global variable of VARCHAR2(30) inside some_another_pkg package.
When I changed it to a forward procedure call, exception disappeared. But I can't replace this EXECUTE IMMEDIATE on a target environment.
Are there any workarounds to fix this issue without replacing EXECUTE IMMEDIATE call?
Thanks in advance.
The problem, as it was mentioned in comments, was in some_another_pkg.schema_name code. In my case this value was NULL.

Oracle: Using EXECUTE IMMEDIATE with RETURNING INTO

I have a procedure that does a validation and inserts a record in a table. The procedure is breaking right after the INSERT statement when I try the following code:
EXECUTE IMMEDIATE V_SOME_STRNG || ' returning SOME_ID into :NEW_ID' returning into V_TRGT_ID;
I am trying to execute my INSERT statement which is stored in V_SOME_STRNG and assign the new record's ID to V_TRGT_ID. However, I am running into the following error:
ORA-00933: SQL command not properly ended
Any thoughts?
You don't need to repeat the returning into part, you need a using clause for your bind variable:
EXECUTE IMMEDIATE V_SOME_STRNG || ' returning SOME_ID into :NEW_ID' using out V_TRGT_ID;
Demo using a basic trigger to provide the ID:
create table t42 (some_id number, dummy varchar2(1));
create sequence s42 start with 42;
create trigger tr42 before insert on t42 for each row
begin
:new.some_id := s42.nextval;
end;
/
set serveroutput on
declare
v_some_strng varchar2(200) := 'insert into t42 (dummy) values (''X'')';
v_trgt_id number;
begin
EXECUTE IMMEDIATE V_SOME_STRNG || ' returning SOME_ID into :NEW_ID' using out V_TRGT_ID;
dbms_output.put_line('Returned ID: ' || v_trgt_id);
end;
/
which shows:
Returned ID: 42
PL/SQL procedure successfully completed.
You can only use returning into with the insert .. values ... pattern, not with insert ... select ...; so for instance changing the code above to use;
v_some_strng varchar2(200) := 'insert into t42 (dummy) select ''X'' from dual';
will get the error you originally reported:
ORA-00933: SQL command not properly ended
ORA-06512: at line 6
While you don't need to use returning into part, the OP problem most likely results from an error in the not shown content of the V_SOME_STRNG variable. Because you definitely can use returning into with execute immediate. Here is an example strait from the documentation:
sql_stmt := 'UPDATE emp SET sal = 2000 WHERE empno = :1 RETURNING sal INTO :2';
EXECUTE IMMEDIATE sql_stmt USING emp_id RETURNING INTO salary;
I stress the point again: it works. So if you have any troubles here check you dynamically generated SQL statement more thoroughly.
My test_queries table consist of 2 columns:fid and query_text.I want insert new row. And I return fid I inserted because I use it next question. But the code give me error .
select max(a.fid) into max_fid from test_queries a;
execute immediate 'insert into test_queries values (:1,:2) returning fid into :a' using max_fid+1,query_text,c;

ORA-00903:invalid table name

I have the following code in PLSQL:
Declare
tablename varchar2(20):='emp';
drop_stmt varchar2(2000);
begin
drop_stmt:='drop table :1 ;';
--dbms_output.put_line(drop_stmt);
execute immediate drop_stmt using tablename;
end;
Results in:
ORA-00903:invalid table name
ORA-06512: at line 8
However when I run:
drop table emp ;
it just successfully runs. What may be the cause of this error?
You must use this one:
drop_stmt:='drop table '||tablename; -- without ";" at the end of string
--dbms_output.put_line(drop_stmt);
execute immediate drop_stmt;

how to create dynamic table in oracle with dynamic column name and dynamic data type with out any views or any other table type

Thanks for all,We can create a table dynamically with the help of execute immediate query. But when we create a table it is been created but if i wanted to create table dynamically with dynamic no of columns then question was raised. Actually I had created a table but when I created no of columns along with the table then a lots of errors raised. The following is the code which I had written in the Oracle in a procedure.
declare
no_of_cols number:=&no_of_cols;
colname varchar2(20);
coldata varchar2(20);
i number;
begin
execute immediate 'create table smap1(nam varchar2(10))';
age:='age';
datf:='number'
if(no_of_cols>=2) then
for i in 2..no_of_cols loop
colname:=age;
coldata:=datf;
execute immediate 'alter table smapl add '||colname||' '||coldata;
end loop;
end if;
end;
then this code executing with four columns with same type if no_of_cols is 5.Then i had modified the code and run the plsql program.the program is as follows
declare
no_of_cols number:=&no_of_cols;
colname varchar2(20);
age varchar2(20);
datf varchar2(20);
coldata varchar2(20);
i number;
begin
execute immediate 'create table smap1(nam varchar2(10))';
if(no_of_cols>=2) then
for i in 2..no_of_cols loop
age :=&age;
datf:=&datf;
colname:=age;
coldata:=datf;
execute immediate 'alter table smapl add '||colname||' '||coldata;
end loop;
end if;
end;
The following are the errors which are generated when the above procedure is created
[Error] Execution (13: 19): ORA-06550: line 13, column 19:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
( - + case mod new not null <an identifier>
<a double-quoted delimited-identifier> <a bind variable>
continue avg count current exists max min prior sql stddev
sum variance execute forall merge time timestamp interval
date <a string literal with character set specification>
<a number> <a single-quoted SQL string> pipe
<an alternatively-quoted string literal with character set specification>
<an alternatively
i had done some modifications for the above plsql,then the plsql code will as follows
declare
no_of_cols number:=&no_of_cols;
colname varchar2(20):='&colname';
coldata varchar2(20):='&coldata';
i number;
begin
execute immediate 'create table smap1(nam varchar2(10))';
if(no_of_cols>=2) then
for i in 2..no_of_cols loop
execute immediate 'alter table smapl add '||colname||' '||coldata;
end loop;
end if;
end;
then after executing i am getting the following error and it doenot read column name dynamically
[Error] Execution (1: 1): ORA-02263: need to specify the datatype for this column
ORA-06512: at line 10
You cannot use semicolons in EXECUTE IMMEDIATE for single statements
Here's a quote from the documentation:
Except for multi-row queries, the dynamic string can contain any SQL statement (without the final semicolon) or any PL/SQL block (with the final semicolon).
Remove the semicolon from EXECUTE IMMEDIATE.
execute immediate 'create table smap1(nam varchar2(10));'; -- this is your code
execute immediate 'create table smap1(nam varchar2(10))'; -- correct code, no semicolon at end
But there's another problem.
You need to understand how substitution variables (&variable) works
SQL*Plus will prompt for substituion variables only once: just before the script compiles, before running it. And then the variables are replaced in the script verbatim, after which it will get compiled and executed.
For example, when you run your script, SQL*Plus recognizes that there are two unknown literals (&colname and &coldata), and will prompt for you. If you supply the values 'age', and 'number' for them, SQL*Plus will rewrite the script like this:
declare
-- omitted to add clarity
begin
execute immediate 'create table smap1(nam varchar2(10));';
if(no_of_cols>=2) then
for i in 2..no_of_cols loop
colname:=age;
coldata:=number;
execute immediate 'alter table smapl add '||colname||' '||coldata;
end loop;
end if;
end;
So if you want to assign a string literal to a variable, and you want to get that string from a substitution variable, you need to do this:
colname varchar2(30) := '&colname'; -- notice the single quotes
Assuming you provided 'age' for colname SQL*Plus will happily convert this to:
colname varchar2(30) := 'age';
So, placing a substitution variable inside a loop will not make SQL*Plus repeatedly prompt you for it's value.
For better understanding and analyze you should do it like this:
sqlcmd varhchar(30000);
begin
sqlcmd := 'create table smap1(nam varchar2(10))';
DBMS_OUTPUT.PUT_LINE(sqlcmd);
execute immediate sqlcmd;
if(no_of_cols>=2) then
for i in 2..no_of_cols loop
age :=&age;
datf:=&datf;
colname:=age;
coldata:=datf;
sqlcmd := 'alter table smapl add '||colname||' '||coldata;
DBMS_OUTPUT.PUT_LINE(sqlcmd);
execute immediate sqlcmd;
end loop;
end if;

Resources