We help IT Professionals succeed at work.

Trigger to record SQL when constraint error encountered

118 Views
Last Modified: 2018-11-21
I am trying to get a trigger to work when a user gets a constraint error.    It is supposed to record the SQL being executed by the user.  

The trigger is working fine until I try to combine the multiple lines of the  SQL statement into a variable so that it's all one line.

But when I put in the  line to do the combine, only the first two utl_file.put_line commands performed prior to the loop will work.  All others fail.  

Here is my combine command:      sql_stmt_text := sql_stmt_text || l_text(i);

Here is the trigger:

create or replace trigger test_trigger
after servererror on database

declare

l_text ora_name_list_t;
l_n number;
sql_stmt_text varchar2(32767);
v_output_file1 utl_file.file_type;    

begin

v_output_file1 := UTL_FILE.FOPEN('SQL_STMT_OUTPUT','failing_SQL_Stmt.txt','W');                  


if ( is_servererror(0001) )
then
  utl_file.put_line(v_output_file1,'--------------------');
  utl_file.put_line(v_output_file1,'statement causing error: ');
 
  l_n := ora_sql_txt( l_text );

  for i in 1 .. nvl(l_n,0) loop

    -- this statement works when it is the only one in the loop.
    -- also all following utl_file.put_line commands are successful.
    utl_file.put_line(v_output_file1,l_text(i));

    -- when I include this statement, none of the utl_file.put_line commands work except
    --  for the two just before the loop.  
    sql_stmt_text := sql_stmt_text || l_text(i);

  end loop;

  utl_file.put_line(v_output_file1,sql_stmt_text);

  utl_file.put_line(v_output_file1,'error text: ');

  for i in 1 .. ora_server_error_depth
  loop
    utl_file.put_line(v_output_file1,ora_server_error_msg(i));
  end loop;
    utl_file.put_line(v_output_file1,'--------------------');

end if;

utl_file.fclose_all;

end;
/
Comment
Watch Question

CERTIFIED EXPERT
Most Valuable Expert 2012
Distinguished Expert 2019

Commented:
What version of Oracle are you using?

Works for me using 18.0.

I modified your PUT_LINES to add location1 and location2 tothem so I know what wrote what.

create or replace trigger test_trigger
after servererror on database

declare

l_text ora_name_list_t;
l_n number;
sql_stmt_text varchar2(32767);
v_output_file1 utl_file.file_type;    

begin

v_output_file1 := UTL_FILE.FOPEN('SQL_STMT_OUTPUT','failing_SQL_Stmt.txt','W');                  

if ( is_servererror(0001) )
then
  utl_file.put_line(v_output_file1,'--------------------');
  utl_file.put_line(v_output_file1,'statement causing error: ');
 
  l_n := ora_sql_txt( l_text );


  for i in 1 .. nvl(l_n,0) loop

    -- this statement works when it is the only one in the loop.
    -- also all following utl_file.put_line commands are successful.
    utl_file.put_line(v_output_file1,'Location1: ' || l_text(i));

    -- when I include this statement, none of the utl_file.put_line commands work except
    --  for the two just before the loop.  
    sql_stmt_text := sql_stmt_text || l_text(i);

  end loop;

  utl_file.put_line(v_output_file1,'Location2: ' || sql_stmt_text);

  utl_file.put_line(v_output_file1,'error text: ');

  for i in 1 .. ora_server_error_depth
  loop
    utl_file.put_line(v_output_file1,ora_server_error_msg(i));
  end loop;
    utl_file.put_line(v_output_file1,'--------------------');

end if;

utl_file.fclose_all;

end;
/

show errors

create or replace directory SQL_STMT_OUTPUT as 'C:\TEMP';

drop table tab1 purge;
create table tab1(col1 char(1) primary key);

insert into tab1 values('a');
insert into tab1 values('a');

Open in new window


My test run output:
SQL> @q

Trigger created.

No errors.

Directory created.


Table dropped.


Table created.


1 row created.

insert into tab1 values('a')
*
ERROR at line 1:
ORA-00001: unique constraint (BUD.SYS_C0016150) violated

Open in new window


My file contents:
C:\TEMP>type failing_SQL_Stmt.txt
--------------------
statement causing error:
Location1: insert into tab1 values('a')
Location2: insert into tab1 values('a')
error text:
ORA-00001: unique constraint (MYUSER.SYS_C0016150) violated

--------------------

Open in new window

Julie KurpaSr. Systems Programmer

Author

Commented:
Thanks Slightwv.  :)

I'm using Oracle 11g running on Oracle LInux.  

Perhaps it would help if I included the output without the "offending" line and then with it so you see what I see.   Am putting it together now.
Julie KurpaSr. Systems Programmer

Author

Commented:
In breaking down the changes, I see that it's actually the "utl_file.put_line(v_output_file1,sql_stmt_text);"  that is causing the problem, not the "sql_stmt_text := sql_stmt_text || l_text(i);".

Here is the output WITHOUT the "utl_file.put_line(v_output_file1,sql_stmt_text);"

--------------------
statement causing error:
INSERT INTO myuser.tab1(column1,column2,column3,column4,column5,
column6,column7,column8,column9,column10,column11,column12,colum
m13) VALUES (1111,2222,3333,4444,5555,6666,7777,8888,9999,1010,1
111,1212,1313)
error text:
ORA-00001: unique constraint (MYUSER.PK_TAB1) violated


This is the output when  I include the "utl_file.put_line(v_output_file1,sql_stmt_text);" statement. Notice it doesn't write anything after it's supposed to writethe contents of varable sql_stmt_text:

--------------------
statement causing error:
INSERT INTO myuser.tab1(column1,column2,column3,column4,column5,
column6,column7,column8,column9,column10,column11,column12,colum
m13) VALUES (1111,2222,3333,4444,5555,6666,7777,8888,9999,1010,1
111,1212,1313)


I tried sending the contents of variable sql_stmt_text to different file altogether.  
When doing that, I get the full constraint information written to the first location but never the content of the sql_stmt_text variable in the 2nd file. The 2nd location stays blank.
CERTIFIED EXPERT
Most Valuable Expert 2012
Distinguished Expert 2019

Commented:
I can only go with what I can personally test.  The code I posted worked for me.

If you want me to run some other test, post the code you wish me to test.  I don't have access to an 11g database any more but can test on 12.2 right now and 18c later today.

>>Here is the output WITHOUT the "utl_file.put_line(v_output_file1,sql_stmt_text);

If you are getting the output you want, what are you trying to accomplish by changing it?  I'm not understanding why you need the SQL on a single line.  What happens when you get SQL larger than 32k?
Julie KurpaSr. Systems Programmer

Author

Commented:
It seems your code is exactly like mine so I don't really have any changes to make.  If it runs in 12.2 then I'm not sure what else to try.


I wanted to get the SQL into a single line because I plan to extract certain elements from it and use it in a procedure.  If it's wrapping like it is, then certain elements I need may be split between lines.
CERTIFIED EXPERT
Most Valuable Expert 2012
Distinguished Expert 2019

Commented:
OK, I think I've been able to reproduce this using longer SQL.  Let me dig into this a little more.
CERTIFIED EXPERT
Most Valuable Expert 2012
Distinguished Expert 2019
Commented:
This problem has been solved!
(Unlock this solution with a 7-day Free Trial)
UNLOCK SOLUTION
Julie KurpaSr. Systems Programmer

Author

Commented:
I really appreciate your help.

On a whim, I directed that variable to a table instead of to a file and it seems to work.  The entire SQL is being written into one column.  This is where I eventually wanted to end up but was just writting to the file so I could see what I was getting.

Here's the adjusted code.  

create or replace trigger test_trigger
after servererror on database

declare

l_text ora_name_list_t;
l_n number;
sql_stmt_text varchar2(32767);
v_output_file1 utl_file.file_type;    

begin

v_output_file1 := UTL_FILE.FOPEN('SQL_STMT_OUTPUT','failing_SQL_Stmt.txt','W');                  


if ( is_servererror(0001) )
then
  utl_file.put_line(v_output_file1,'--------------------');
  utl_file.put_line(v_output_file1,'statement causing error: ');
 
  l_n := ora_sql_txt( l_text );

  for i in 1 .. nvl(l_n,0) loop

    utl_file.put_line(v_output_file1,l_text(i));

    sql_stmt_text := sql_stmt_text || l_text(i);

  end loop;

  INSERT INTO myuser.sql_event VALUES (sql_stmt_text);

  utl_file.put_line(v_output_file1,'error text: ');

  for i in 1 .. ora_server_error_depth
  loop
    utl_file.put_line(v_output_file1,ora_server_error_msg(i));
  end loop;
    utl_file.put_line(v_output_file1,'--------------------');

end if;

utl_file.fclose_all;

end;
/
Julie KurpaSr. Systems Programmer

Author

Commented:
oh I just saw your post.  am reading it now.
Julie KurpaSr. Systems Programmer

Author

Commented:
That worked great slighwv!  You are wonderful!

I set the FOPEN size to 32767 and I'm getting the full SQL text on one line in the file.
Now I have many options on how to handle the rest of my project.
Julie KurpaSr. Systems Programmer

Author

Commented:
Here's the working product with the writing of the SQL on all one line to the output file outside the loop.

create or replace trigger test_trigger
after servererror on database

declare

l_text ora_name_list_t;
l_n number;
sql_stmt_text varchar2(10000);
v_output_file1 utl_file.file_type;    

begin

v_output_file1 := UTL_FILE.FOPEN('SQL_STMT_OUTPUT','failing_SQL_Stmt.txt','W',32767);                  


if ( is_servererror(0001) )
then
  utl_file.put_line(v_output_file1,'--------------------');
  utl_file.put_line(v_output_file1,'statement causing error: ');
 
  l_n := ora_sql_txt( l_text );

  for i in 1 .. nvl(l_n,0) loop

    utl_file.put_line(v_output_file1,l_text(i));

    sql_stmt_text := sql_stmt_text || l_text(i);

  end loop;

  -- write the combined SQL  to a table
  INSERT INTO myuser.sql_event VALUES (sql_stmt_text);

  -- write the combined SQL to the file
  utl_file.put_line(v_output_file1,'error text: ');

  for i in 1 .. ora_server_error_depth
  loop
    utl_file.put_line(v_output_file1,ora_server_error_msg(i));
  end loop;
    utl_file.put_line(v_output_file1,'--------------------');

end if;

utl_file.fclose_all;

end;
/
CERTIFIED EXPERT
Most Valuable Expert 2012
Distinguished Expert 2019

Commented:
>>SQL on all one line to the output file outside the loop.

Why when it isn't necessary?
Julie KurpaSr. Systems Programmer

Author

Commented:
Just to show it as part of the solution.  I won't continue to do it now that I'm writing it to a table.
Julie KurpaSr. Systems Programmer

Author

Commented:
slightwv didn't have to do further research when his SQL worked fine.  Went the extra mile to reproduce the problem and found the solution.

Gain unlimited access to on-demand training courses with an Experts Exchange subscription.

Get Access
Why Experts Exchange?

Experts Exchange always has the answer, or at the least points me in the correct direction! It is like having another employee that is extremely experienced.

Jim Murphy
Programmer at Smart IT Solutions

When asked, what has been your best career decision?

Deciding to stick with EE.

Mohamed Asif
Technical Department Head

Being involved with EE helped me to grow personally and professionally.

Carl Webster
CTP, Sr Infrastructure Consultant
Empower Your Career
Did You Know?

We've partnered with two important charities to provide clean water and computer science education to those who need it most. READ MORE

Ask ANY Question

Connect with Certified Experts to gain insight and support on specific technology challenges including:

  • Troubleshooting
  • Research
  • Professional Opinions