Skip to Main Content
  • Questions
  • Can I "query rewrite" or block a specific DELETE statement

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Kim Berg.

Asked: September 03, 2015 - 9:23 am UTC

Last updated: September 18, 2015 - 3:26 am UTC

Version: 11.2.0.3.0

Viewed 1000+ times

You Asked

I have an ancient application environment called XAL.

It has own data dictionary, own programming language, own development and runtime environment (kernel). From its own data dictionary the kernel auto-creates tables and indexes in Oracle. From its own programming language the kernel auto-generates SQL that runtime is executed on the database.

The XAL data dictionary has the concept of temporary tables, but the kernel creates normal tables, just with a DATASET column that runtime is populated with a unique session identifier so each session has own "slice" of data in these "temporary" tables. (We've changed these tables to GTTs, but the XAL kernel still believes they are normal tables.)

So the developers of the XAL kernel (which we cannot change) has implemented clean-up code whenever a session ends. The clean-up code loops over the temporary tables and issue a "DELETE FROM {table} WHERE DATASET={session identifier}" to remove the session "slice" of data in the "temporary" tables.

This is acceptable. Since we changed the tables to GTTs, these deletes do no harm anyway.

But... We've hit a bug in this kernel (and it won't be fixed), so once in a while (maybe a couple times a year), the clean-up code mistakenly deletes from a real table (some random real table, seldom the same one) instead of the temporary table. We have had cases where suddenly we had no employees or all prices went missing :-(

We are monitoring v$sql for when it happens:

select *
from (
   select distinct
   parsing_schema_name,
   substr(sql_text,13,instr(sql_text,' ',13)-13) table_name
   from v$sql
   where executions > 0
   and sql_fulltext like 'DELETE FROM % WHERE DATASET=:255 AND ROWNUM<=5000'
) bad
join dba_tables t
   on t.owner = 'XAL_SUPERVISOR'
   and t.table_name = bad.table_name
   and t.temporary != 'Y'
/


The kernel clean-up code deletes 5000 rows at a time, committing and looping until SQL%ROWCOUNT is less than 5000. That way we can identify that the text above is SQL generated by that clean-up code and not SQL from somewhere else. We then check if this delete happened to a non-temporary table.

When the monitor discovers it has happened, we can use flashback to get the data back.


But is there instead a way to block these particular DELETE statements instead so data won't be deleted?

Can advanced query rewrite somehow be made to work for DELETE rather than just SELECT?

Can some FGAC block a particular sql_id? (I might be able to isolate the about 900 individual DELETE statements that can happen.)

Other ideas?


Thanks in advance
Kim Berg Hansen

and Chris said...

Yes, you can use FGAC to prevent deletes with a certain where clause.

The sys_context variable CURRENT_SQL gives you the statement that triggered a policy. By inspecting this you can prevent deletes that match your criteria.

create table t (id not null, dataset not null) as
  select rownum, 'dataset' from dual connect by level <= 10000;

create or replace function restrict_deletes ( 
  schema_var in varchar2,
  table_var  in varchar2
) return varchar2
is
  where_clause varchar2 (100);
begin

  if upper(sys_context('USERENV', 'CURRENT_SQL')) like 
       '%ROWNUM<=5000%' then
    where_clause := '1=0';
  else
    where_clause := '1=1';
  end if;
  
  return where_clause;
  
end restrict_deletes;
/
show errors

set serveroutput on

var v255 varchar2(10);
exec :v255 := 'dataset';

PRO Policy not applied, should delete 5,000 rows 
delete t WHERE DATASET=:v255 AND ROWNUM<=5000;

5,000 rows deleted.

begin
  dbms_rls.add_policy (
    object_schema    => 'chris',
    object_name      => 't',
    policy_name      => 'delete_policy',
    function_schema  => 'chris',
    policy_function  => 'restrict_deletes',
    statement_types  => 'delete'
   );
 end;
/

PRO Policy applied, delete 0 rows
delete t WHERE DATASET=:v255 AND ROWNUM<=5000;

0 rows deleted.

PRO "normal" deletes still allowed
delete t ;

5,000 rows deleted.

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Interesting, thanks

Kim Berg Hansen, September 03, 2015 - 10:45 am UTC

That looks just like something I could use.
I've got to go and test it for how much overhead that'll give to all DELETE statements...

Thanks :-)

More to Explore

Security

All of the vital components for a secure database are covered in the Security guide.