Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutating Table in Oracle 11 caused by a function

We've recently upgraded from Oracle 10 to Oracle 11.2. After upgrading, I started seeing a mutating table error caused by a function rather than a trigger (which I've never come across before). It's old code that worked in prior versions of Oracle.

Here's a scenario that will cause the error:

create table mutate (
    x NUMBER,
    y NUMBER
);

insert into mutate (x, y)
values (1,2);

insert into mutate (x, y)
values (3,4);

I've created two rows. Now, I'll double my rows by calling this statement:

insert into mutate (x, y)
select x + 1, y + 1 
from mutate;

This isn't strictly necessary to duplicate the error, but it helps with my demonstration later. So the contents of the table now look like this:

X,Y
1,2
3,4
2,3
4,5

All is well. Now for the fun part:

create or replace function mutate_count
return PLS_INTEGER
is
    v_dummy PLS_INTEGER;
begin
    select count(*) 
    into v_dummy
    from mutate;

    return v_dummy;
end mutate_count;
/

I've created a function to query my table and return a count. Now, I'll combine that with an INSERT statement:

insert into mutate (x, y)
select x + 2, y + 2
from mutate
where mutate_count() = 4;

The result? This error:

ORA-04091: table MUTATE is mutating, trigger/function may not see it
ORA-06512: at "MUTATE_COUNT", line 6

So I know what causes the error, but I am curious as to the why. Isn't Oracle performing the SELECT, retrieving the result set, and then performing a bulk insert of those results? I would only expect a mutating table error if records were already being inserted before the query finished. But if Oracle did that, wouldn't the earlier statement:

insert into mutate (x, y)
select x + 1, y + 1 
from mutate;

start an infinite loop?

UPDATE:

Through Jeffrey's link I found this in the Oracle docs:

By default, Oracle guarantees statement-level read consistency. The set of data returned by a single query is consistent with respect to a single point in time.

There's also a comment from the author in his post:

One could argue why Oracle doesn't ensure this 'statement-level read consistency' for repeated function calls that appear inside a SQL statement. It could be considered a bug as far as I'm concerned. But this is the way it currently works.

Am I correct in assuming that this behavior has changed between Oracle versions 10 and 11?

like image 876
Dan A. Avatar asked Mar 29 '12 23:03

Dan A.


People also ask

How do you resolve a mutating table error?

If a trigger does result in a mutating table error, the only real option is to rewrite the trigger as a statement-level trigger. Mutating table errors only impact row level triggers. But to use a statement level trigger, some data may need to be preserved from each row, to be used by the statement level trigger.

How can a mutating table error be avoided?

First, declare an array of customer record that includes customer id and credit limit. Second, collect affected rows into the array in the row-level trigger. Third, update each affected row in the statement-level trigger.

What is a mutating table error and how can you get around it?

A mutating table error (ORA-04091) occurs when a row-level trigger tries to examine or change a table that is already undergoing change (via an INSERT, UPDATE, or DELETE statement). In particular, this error occurs when a row-level trigger attempts to read or write the table from which the trigger was fired.

What is mutating in trigger?

if the trigger attempts to select or modify the table while the trigger has not completed (ie. table is in transition). then mutating trigger error occurs. because the table is in middle of a transaction so it causes the trigger to mutate. you can change the trigger to statement level and apply the logic there.


1 Answers

Firstly,

insert into mutate (x, y)
select x + 1, y + 1 
from mutate;

Does not start an infinite loop, because the query will not see the data that was inserted - only data that existed as of the start of the statement. The new rows will only be visible to subsequent statements.

This explains it quite well:

When Oracle steps out of the SQL-engine that's currently executing the update statement, and invokes the function, then this function -- just like an after row update trigger would -- sees the intermediate states of EMP as they exist during execution of the update statement. This implies that the return value of our function invocations heavily depend on the order in which the rows happen to be updated.

like image 65
Jeffrey Kemp Avatar answered Oct 20 '22 20:10

Jeffrey Kemp