ORA-04068: existing state of packages has been discarded - oracle

I've wrapper procedure which looks like this:
Procedure A
BEGIN;
Procedure B
Procedure C
END;
/
Procedure B drops and re-creates a table used in Procedure C. Due to this, I get the below error:
ERROR at line 1:
ORA-04068: existing state of packages has been discarded ORA-04065: not executed, altered or dropped stored procedure "SCHEMA.PROCEDURE C"
ORA-06508: PL/SQL: could not find program unit being called: "SCHEMA.PROCEDURE C"
ORA-06512: at "SCHEMA.PROCEDURE A", line 4
ORA-06512: at line 1
How do I go about getting this procedure to work?

So your proc get invalid during the execution, that is why you need recompile Procedure C. To achive this, you can call a dynamic SQL statement:
EXECUTE IMMEDIATE 'ALTER PROCEDURE my_procedure COMPILE';
Procedure_A IS
BEGIN;
Procedure_B;
EXECUTE IMMEDIATE 'ALTER PROCEDURE Procedure_C COMPILE';
Procedure_C;
END;
/

I recommend using dynamic SQL to call the DML in procedure C. By removing the dependency nothing gets invalidated. This would likely be more reliable than dynamically re-compiling code or dynamically running the code and catching all exceptions.
You can see this in the below sample code. Comment out the static SQL call on line 10 in procedure C to replicate the error.
drop table drop_me;
create table drop_me(id number);
create or replace procedure C is
v_count number;
begin
--Static SQL would fail with this error:
-- ORA-04068: existing state of packages has been discarded
-- ORA-04065: not executed, altered or dropped stored procedure "JHELLER.C"
-- ORA-06508: PL/SQL: could not find program unit being called: "JHELLER.C"
-- ORA-06512: at "JHELLER.A", line 4
-- ORA-06512: at line 2
--select count(*) into v_count from drop_me;
--Dynamic SQL runs correctly:
execute immediate 'select count(*) from drop_me' into v_count;
end;
/
create or replace procedure B is
begin
execute immediate 'drop table drop_me purge';
execute immediate 'create table drop_me(id number)';
end;
/
create or replace procedure A is
begin
B;
C;
end;
/
begin
A;
end;
/
But I agree with Alex Poole that there is almost certainly a better way to do this without dropping and re-creating objects. I can think of a few reasons why a DROP and CREATE might run faster than a TRUNCATE and INSERT, but only because of some weird side-effect, like dropping bad table statistics. Investigate the difference more and you might find the real reason.

You can do something like it.
begin
procedureB;
begin
execute immediate 'begin procedureC; end;';
exception when others then
execute immediate 'begin procedureC; end;';
end;
end;

Related

Error while executing a stored procedure to drop a table

Getting error while executing a stored procedure that drops a table.
I have compiled the procedure successfully (used dynamic SQL for the code).
Tool used is SQL Developer.
CREATE OR REPLACE PROCEDURE sp_DROP(P_VAR IN VARCHAR2)
IS
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE'||P_VAR; -- passing table via parameter
END;
but I'm getting error while executing this procedure:
EXECUTE sp_DROP('CON1'); -- i have made sure CON1 is a legit table.
Error :
ORA-00950: invalid DROP option
ORA-06512: at "HR.SP_DROP", line 4
ORA-06512: at line 3
00950. 00000 - "invalid DROP option"
*Cause:
*Action:
Thanks for the help.
Add a space to the end of drop table.
CREATE OR REPLACE PROCEDURE sp_DROP(P_VAR IN VARCHAR2)
IS
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE '||P_VAR; -- passing table via parameter
END;

How to catch the event where in PL/SQL code the first commit occurs in Oracle?

Is the way in Oracle automatically catch the event where in PL/SQL code the first commit occurs?
I have a big problem with PL/SQL procedure which should not do any commits but it does a commit somewhere.
This procedure runs a huge amount of pl/sql code, a lot of dynamic pl/sql code, some packages are wrapped and some packages are not granted for debug.
Maybe someone had it a similar problem and can help me?
Your PL/SQL block will never COMMIT the changes until and unless you have explicitly mentioned COMMIT.
However, you might have DDL statements executed as dynamic SQL which might be going for an implicit commit. Search for the commit in the code.
You could look into USER_SOURCE.
SELECT *
FROM USER_SOURCE
WHERE NAME = '<PROCEDURE NAME IN UPPER CASE>'
AND TYPE = 'PROCEDURE'
AND UPPER(TEXT) LIKE '%COMMIT%'
Update
To find the DDL statements, you could look for keywords like CREATE, DROP, ALTER
SELECT *
FROM USER_SOURCE
WHERE NAME = '<PROCEDURE NAME IN UPPER CASE>'
AND TYPE = 'PROCEDURE'
AND UPPER(TEXT) LIKE '%CREATE%'
OR UPPER(TEXT) LIKE '%DROP%'
OR UPPER(TEXT) LIKE '%ALTER%'
I found the answer:
ALTER SESSION DISABLE COMMIT IN PROCEDURE
This command disables the possibility of commit and, what is most
important for me the oracle error message shows the exact place where
in pl/sql code the commit is.
SQL> create or replace procedure tst_commit is
2 begin
3 dbms_output.put_line('before commit');
4 COMMIT;
5 dbms_output.put_line('after commit');
6 end tst_commit;
7 /
Procedure created
SQL> BEGIN
2 EXECUTE IMMEDIATE 'ALTER SESSION DISABLE COMMIT IN PROCEDURE';
3 INSERT INTO viliusg.log VALUES(SYSDATE,'test commit');
4 tst_commit;
5 dbms_output.put_line('THE END');
6 END;
7 /
BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION DISABLE COMMIT IN PROCEDURE';
INSERT INTO viliusg.log VALUES(SYSDATE,'test commit');
tst_commit;
dbms_output.put_line('THE END');
END;
ORA-00034: cannot COMMIT in current PL/SQL session
ORA-06512: at "FORPOST.TST_COMMIT", line 4
ORA-06512: at line 4

Testing native dynamic SQL in Oracle before execution

I have implemented a certain feature in my application where the user can compose queries dynamically from the user interface by pushing around buttons and inserting some values here and there.
The user will not see the generated SQL statement at all.
I was wondering if there is a way to perhaps check the syntax and grammar (e.g. he opened a parantheses '(' and forgot to close it ) of the dynamically generated SQL to ensure that no run-time compilation errors would happen before actually executing the statement using EXECUTE IMMEDIATE.
You could use the dbms_sql.parse procedure to parse the SQL statement assuming the statement is DML not DDL. It would be rather unusual to parse a dynamic SQL statement using the dbms_sql package and then use EXECUTE IMMEDIATE to execute it rather than using dbms_sql.execute but nothing prevents you from mixing dbms_sql and execute immediate.
The code to just parse the SQL statement would be something like
DECLARE
l_cursor integer;
l_sql_stmt varchar2(1000) := <<some SQL statement>>;
BEGIN
l_cursor := dbms_sql.open_cursor;
dbms_sql.parse( l_cursor, l_sql_stmt, dbms_sql.native );
dbms_sql.close_cursor( l_cursor );
END;
You can also use explain plan, this is basically doing the first step of the execution.
SQL> explain plan for select * from dual;
Explained.
SQL is valid, you can also use the explain tables to get the tables, views etc, maybe even estimated run time ...
SQL> explain plan for select * from duall;
explain plan for select * from duall
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL is invalid, this is why ...
you can also use it in a dynamic statement
SQL> begin execute immediate 'explain plan for ' || ' select * from dual'; end;
2 /
PL/SQL procedure successfully completed.
As always use error-handling, e.g. write a little error log creater package or procedure what u call when it's needed.
DECLARE
my_stmt VARCHAR2(4000):='this will contain my statements';
BEGIN
'begin' || my_stmt || 'end';
EXCEPTION
WHEN OTHERS THEN
prc_my_error_log_creator();
END;
in the log_creator use for example the DBMS_UTILITY.FORMAT_ERROR_BACKTRACE() func to get the stack's contain.
Regards

How to create and use temporary table in oracle stored procedure?

I want to create temporary table in stored procedure and access it in the same but I got error that ORA-00942:Table or view does not exists.
Following is the procedure that i tried,
Create procedure myproc
IS
stmt varchar2(1000);
BEGIN
stmt:='CREATE GLOBAL TEMPORARY table temp(list if columns) ON COMMIT DELETE ROWS';
execute immediate stmt;
insert into temp values('list of column values');
END;
This is the way I used to create temporary table but I got error, is there any other way to perform this task?
Just create it first (once, outside of your procedure), and then use it in your procedure. You don't want to (try to) create it on every call of the procedure.
create global temporary table tmp(x clob)
on commit delete rows;
create or replace procedure...
-- use tmp here
end;
Create or replace procedure myprocedure
is
stmt varchar2(1000);
stmt2 varchar2(1000);
begin
stmt := 'create global temporary table temp(id number(10))';
execute immediate stmt;
stmt2 := 'insert into temp(id) values (10)';
execute immediate stmt2;
end;
I have edited this answer as it was wrong. I am a recent MSSQL convert and because of the way oracle implements global temp tables, if you really DO need to use temp tables, creating them once and leaving them there is the way to go. Unless you use dynamic sql in your procs exclusively (have fun debugging), you will not be able to successfully compile your package unless the tables referenced already exist. Oracle validates any objects referenced in methods that you attempt to compile, which is why you got the 942 error. I love the way Oracle manages scope with these global temp tables. That, alone, sold me on the idea.
Use this
Create of replace procedure myprocedure
is
stmt varchar2(1000);
stmt2 varchar2(1000);
begin
stmt := 'create global temporary table temp(id number(10))';
execute immediate stmt;
stmt2 := 'insert into temp(id) values (10)';
execute immediate stmt2;
end;
CREATE OR REPLACE PROCEDURE myproc IS
BEGIN
CREATE GLOBAL TEMPORARY TABLE temp (id NUMBER(10)) ON COMMIT DELETE ROWS AS
SELECT 10 FROM dual;
END;
/

Oracle merge partition from procedure giving error

CREATE OR REPLACE PROCEDURE test
AS
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'ALTER TABLE daily_table PARTITIONS p1 , p2 into PARTITION p2';
EXECUTE IMMEDIATE sql_stmt;
END ;
/
The above procedure is giving me the following error -
ORA-01031: insufficient privileges
ORA-06512: at "test", line 8
ORA-06512: at line 6
But if I run the ALTER Command directly on the sql prompt, I am not receiving any error..
I am wondering what permission I need to provide the user to perform the merge from the procedure.
I fixed the issue by using AUTHID CURRENT_USER
CREATE OR REPLACE PROCEDURE test AUTHID CURRENT_USER
AS
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'ALTER TABLE daily_table PARTITIONS p1 , p2 into PARTITION p2';
EXECUTE IMMEDIATE sql_stmt;
END ;
/
Note: you should never use DDL in stored procedures. Below is the sample of very, very badly designed code which should be avoided.
CREATE OR REPLACE PROCEDURE test
AS
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'GRANT ALTER ON daily_table TO your_user';
EXECUTE IMMEDIATE sql_stmt;
sql_stmt := 'ALTER TABLE daily_table PARTITIONS p1 , p2 into PARTITION p2';
EXECUTE IMMEDIATE sql_stmt;
END ;
/
I'm sure that if someone creates a stored proc SPECIFICALLY for DDL, then they realize that it is commited automatically.
I do use DDL in stored procedures: most commonly to truncate summary tables that the procedure will then re-populate; now and then for DDLish tasks such as renaming columns of an imported table to conform to Oracle's standard rules for an identifier, or for creating primary keys and sequences for named tables. Generally I use
dbms_utility.exec_ddl_statement(blah);
rather than
EXECUTE IMMEDIATE blah;
a prejudice I won't attempt to justify. I will say, that having packaged procedure supplied and documented by Oracle suggests that it is not to be prohibited across the board.

Resources