A unhandled exception in a "after create" trigger preventing create - oracle

I have an AFTER CREATE trigger on my schema that raises an exception; I'm trying to create a table. I understand that an unhandled exception could cause the query/operation/transaction (I couldn't find suitable word for this case) to terminate. But, when the exception is raised after creating the table, shouldn't the table already be created by then?
When creating a table after successful triggering it throws error an error:
create table test(testcol number);
Error:
SQL Error: ORA-00604: error occurred at recursive SQL level 1
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at line 4
Trigger:
create or replace trigger ddl_trig_2812
after create
on schema
declare
eirit exception;
begin
raise eirit;
end;
/

From the documentation:
In most cases, if a trigger runs a statement that raises an exception, and the exception is not handled by an exception handler, then the database rolls back the effects of both the trigger and its triggering statement.
It doesn't just cause it to terminate, as your question suggests. Your unhandled exception is causing the triggering statement - i.e. the create table - to be rolled back. When talking about DDL statements:
An implicit COMMIT occurs immediately before the database executes a DDL statement and a COMMIT or ROLLBACK occurs immediately afterward. In the preceding example, ... [if] the ALTER TABLE statement succeeds, then the database commits this statement; otherwise, the database rolls back this statement. ...
So this is expected behaviour.

The transaction begins with CREATE TABLE; then the AFTER CREATE trigger fires and raises an exception, i.e. table definition isn't "committed" into data dictionary tables (such as USER_TABLES, USER_TAB_COLUMNS, ...) but "rolled-back".
So - no, CREATE TABLE process hasn't been finished properly and you can't use that table as it isn't created because its creation was prevented by a trigger.

One good illustration to verify this would be to check if the table actually exists within the trigger.
Lets say you RAISE from the EXCEPTION block and query DBA_OBJECTS to see if object exists.
SQL> create or replace trigger ddl_trig_2812
2 after create
3 on schema
4 declare
5 v_table_name DBA_OBJECTS.OBJECT_NAME%TYPE;
6 eirit exception;
7 begin
8 select object_name into v_table_name from DBA_OBJECTS where object_name = 'ATEST';
9 raise eirit;
10 EXCEPTION
11 WHEN OTHERS THEN
12 DBMS_OUTPUT.PUT_LINE( 'TABLE CREATED '|| v_table_name );
13 RAISE;
14 END;
15
16 /
Trigger created.
Now, Lets create the table.
SQL> set serveroutput on;
SQL> create table atest ( a number);
TABLE CREATED ATEST
create table atest ( a number)
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at line 10
Although exception was raised, the table exists in the data dictionary as you can see above the message displayed from dbms_output.
Now, if we check whether the table exists,
SQL> select * from atest;
select * from atest
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> select object_name from DBA_OBJECTS where object_name = 'ATEST'
2 ;
no rows selected
It means, Oracle changes the data dictionary in the DDL triggers but rollbacks the transaction if error occurs in the trigger.

Related

Error while creating trigger on table in Oracle

I am creating a trigger for inserting incremental ID's in my table. But while creating I am getting below error
An error occurred while processing a recursive SQL statement
(a statement applying to internal dictionary tables).
ORA-00604: error occurred at recursive SQL level 1
ORA-01654: unable to extend index SYS.I_TRIGGERCOL1 by 64 in tablespace SYSTEM
Here is my trigger query.
create or replace TRIGGER TGR_IPCOLO_BIL
BEFORE INSERT ON ipcolo_ipfee_calc_bil
for each row
begin
IF INSERTING THEN
IF :NEW."ID" IS NULL THEN
select SEQ_IPCOLO_IPFEE_BIL.nextval into :NEW."ID" from dual;
end if;
END IF;
end;
That error sounds pretty bad (I never saw it before) ... internal dictionary tables?! What is error code? ORA-xxxxx?
Meanwhile, trigger can be simplified to this:
create or replace trigger trg_ipcolo_bil
before insert on ipcolo_ipfee_calc_bil
for each row
begin
:new.id := nvl(:new.id, seq_ipolo_ipfee_bil.nextval);
end;
/
You don't have to check if inserting; what else could it be, if it fires before insert? Also, you don't need select ... into - use sequence directly. nvl makes sure you won't overwrite id if you provided it.
Also, consider using identity column instead, if your database version supports it.

Nhibernate TooManyRowsAffectedException Oracle

There is a trigger on a table that periodically creates an insert and throws the TooManyRowsAffectedException. In Sql server, we can set the trigger to NoCount to solve the issue. Any ideas in Oracle?
FluentNHibernate 2.12
.net 4.7.2
Oracle 11g
I think you are talking about two different things:
SET NOCOUNT ON
Stops the message that shows the count of the number of rows affected
by a Transact-SQL statement or stored procedure from being returned as
part of the result set. When SET NOCOUNT is ON, the count is not returned. When SET NOCOUNT is OFF, the count is returned.
I guess you are talking about the exception Too_Many_Rows in Oracle, as the TooManyRowsAffectedException of Hibernate indicates that more rows were affected then we were expecting to be. Typically indicates presence of duplicate "PK" values in the given table.
The TOO_MANY_ROWS Exception (ORA-01422) occurs when a SELECT INTO
statement returns more than one row.
An exception in Oracle is handled within a PL/SQL program into the exception section, which its counterpart in SQL Server would be the TRY CATCH.
When a program in Oracle contains an exception block, you can control the output of one of many specific errors by changing the outcome of them, thereby you can control what the program should do when an error happens.
An exception is basically a logical expression that answers a simple question: when an error happens what do you do with it.
exception
when ... then ...
Let me show you an example
SQL> create table t ( c1 number , c2 number ) ;
Table created.
SQL> alter table t add primary key (c1) ;
Table altered.
SQL> set timing off
SQL> declare
begin
insert into t values ( 1 , 1 );
commit ;
insert into t values ( 1 , 2 );
commit;
exception
when dup_val_on_index then null;
when others then raise;
end;
/
PL/SQL procedure successfully completed.
SQL> select * from t ;
C1 C2
---------- ----------
1 1
As you can see above in that example, I just controlled the exception dup_val_on_index to prevent the program to throw an error. I could have done the same for a too_many_rows exception.
Basically, if you want to ignore the exception, you can change the code in your trigger to avoid that when the exception happens an error is raised. You can also disable the trigger, but I guess that might be not an option.
You only need to realise that both things are different. SET NOCOUNT ON is preventing message of affected rows to be delivered to the client program. An exception in Oracle PL/SQL is used to control and handling a specific error.

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

Oracle DB - identifier too long

I have created a connection in SQL Developer and added several tables to database. Also, I have defined some triggers. They all work well except for one that gives "identifier too long" error. I am aware of 30 character limit, but i can't see what causes this particular error. With this code, I'm trying to implement the Short-circuit keys.
Tables:
Izvestaj (IzvestajID, Datum, Opis, Tekst, PredmerID, NarucilacID, OsobaID, IzvrsilacID)
Predmer (PredmerID, Datum, Naziv, IzvrsilacID, LokacijaID)
Izvrsilac (IzvrsilacID, Naziv)
Italic values represent the primary keys of relations (tables).
Triggers:
create or replace TRIGGER "T_IZM_IZV"
AFTER UPDATE OF IZVRSILACID ON PREDMER
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'DISABLE');
UPDATE IZVESTAJ SET IZVRSILACID=:NEW.IZVRSILACID
WHERE PREDMERID=:NEW.PREDMERID;
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'ENABLE');
END;
create or replace TRIGGER "T_ZABRANA_IZM_IZV"
BEFORE UPDATE OF IzvrsilacID ON Izvestaj
FOR EACH ROW
BEGIN
RAISE_APPLICATION_ERROR(-20000, 'Direct altering of IzvrsilacID is forbidden');
END;
I have added some rows to table "Predmer" (Izvrsilac and Izvestaj also have some values inserted) and when I try to change value of column "IzvrsilacID" (foreign key), i get an error:
UPDATE "KORISNIK"."PREDMER" SET IZVRSILACID = '1' WHERE ROWID = 'AAAFBRAABAAALDxAAB' AND ORA_ROWSCN = '675526'
ORA-00972: identifier is too long
ORA-06512: at "KORISNIK.T_IZM_IZV", line 4
ORA-04088: error during execution of trigger 'KORISNIK.T_IZM_IZV'
One error saving changes to table "KORISNIK"."PREDMER":
Row 2: ORA-00972: identifier is too long
ORA-06512: at "KORISNIK.T_IZM_IZV", line 4
ORA-04088: error during execution of trigger 'KORISNIK.T_IZM_IZV'
What could cause this error? I've tried several things, even renaming triggers, tables and columns to one-letter names, but without any success.
P.S. Sorry about naming. I didn't want to translate table and column names to English because it would differ their length, which seems to cause an error in the first place.
These lines:
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'DISABLE');
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'ENABLE');
Should look like this:
EXECUTE IMMEDIATE ('ALTER TRIGGER '||'T_ZABRANA_IZM_IZV'||' DISABLE');
EXECUTE IMMEDIATE ('ALTER TRIGGER '||'T_ZABRANA_IZM_IZV'||' ENABLE');
Note the added spaces.
Without the spaces added, you were trying execute:
ALTER TRIGGERT_ZABRANA_IZM_IZVDISABLE
and
ALTER TRIGGERT_ZABRANA_IZM_IZVENABLE
which clearly isn't going to work.
Hope that helps.

Resources