05.11.2015 Views

Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

CHAPTER 7 ■ CONCURRENCY AND MULTI-VERSIONING 251<br />

As you can see, that row trigger saw two versions of that row here. The row trigger was<br />

fired two times: once with the original version of the row <strong>and</strong> what we tried to modify that<br />

original version to, <strong>and</strong> again with the final row that was actually updated. Since this was a<br />

BEFORE FOR EACH ROW trigger, <strong>Oracle</strong> saw the read-consistent version of the record <strong>and</strong> the<br />

modifications we would like to have made to it. However, <strong>Oracle</strong> retrieved the block in current<br />

mode to actually perform the update after the BEFORE FOR EACH ROW trigger fired. It waits until<br />

after this trigger fires to get the block in current mode, because the trigger can modify the<br />

:NEW values. So <strong>Oracle</strong> cannot modify the block until after this trigger executes, <strong>and</strong> the trigger<br />

could take a very long time to execute. Since only one session at a time can hold a block in<br />

current mode, <strong>Oracle</strong> needs to limit the time we have it in that mode.<br />

After this trigger fired, <strong>Oracle</strong> retrieved the block in current mode <strong>and</strong> noticed that the<br />

column used to find this row, X, had been modified. Since X was used to locate this record <strong>and</strong><br />

X was modified, the database decided to restart our query. Notice that the update of X from<br />

1 to 2 did not put this row out of scope; we’ll still be updating it with this UPDATE statement.<br />

Rather, it is the fact that X was used to locate the row, <strong>and</strong> the consistent read value of X (1 in<br />

this case) differs from the current mode read of X (2). Now, upon restart, the trigger sees the<br />

value of X=2 (following modification by the other session) as the :OLD value <strong>and</strong> X=3 as the<br />

:NEW value.<br />

So, that shows that these restarts happen. It takes a trigger to see them in action; otherwise,<br />

they are generally “undetectable.” That does not mean you cannot see other symptoms—such<br />

as a large UPDATE statement rolling back work after updating many rows <strong>and</strong> then discovering a<br />

row that causes it to restart—just that it is hard to definitively say, “This symptom is caused by<br />

a restart.”<br />

An interesting observation is that triggers themselves may cause restarts to occur even<br />

when the statement itself doesn’t warrant them. Normally, the columns referenced in the<br />

WHERE clause of the UPDATE or DELETE statement are used to determine whether or not the modification<br />

needs to restart. <strong>Oracle</strong> will perform a consistent read using these columns <strong>and</strong>, upon<br />

retrieving the block in current mode, it will restart the statement if it detects that any of them<br />

have changed. Normally, the other columns in the row are not inspected. For example, let’s<br />

simply rerun the previous example <strong>and</strong> use WHERE Y>0 to find the rows:<br />

ops$tkyte@ORA10G> update t set x = x+1 where y > 0;<br />

old.x = 1, old.y = 1<br />

new.x = 2, new.y = 1<br />

old.x = 2, old.y = 1<br />

new.x = 3, new.y = 1<br />

1 row updated.<br />

You might at first wonder, “Why did <strong>Oracle</strong> fire the trigger twice when it was looking at the<br />

Y value? Does it examine the whole row?” As you can see from the output, the update was in<br />

fact restarted <strong>and</strong> the trigger again fired twice, even though we were searching on Y>0 <strong>and</strong> did<br />

not modify Y at all. But, if we re-create the trigger to simply print out the fact that it fired,<br />

rather than reference the :OLD <strong>and</strong> :NEW values<br />

ops$tkyte@ORA10G> create or replace trigger t_bufer<br />

2 before update on t for each row<br />

3 begin<br />

4 dbms_output.put_line( 'fired' );

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!