Skip to Main Content
  • Questions
  • ORA-01002 when using rollback to savepoint

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Martin.

Asked: May 06, 2004 - 10:28 am UTC

Last updated: July 02, 2012 - 2:12 pm UTC

Version: 8.1.7

Viewed 10K+ times! This question is

You Asked

Hello Tom,

we've got a problem concerning ORA-01002 in a PL/SQL block.
As we learned this error sometimes happens under special circumstances when updating selected data, i.e. when using a select for update. But this is not the case here!
The strange thing is that we're getting this error when only reading data from a cursor, without any updates on the cursor data.
The error occurs when executing a rollback to savepoint statement:


SAVEPOINT my_savepoint;

FOR my_record IN my_cursor LOOP

IF my_condition THEN
ROLLBACK TO SAVEPOINT my_savepoint;
ELSE
do_something;
--> no updates on my_cursor; this data is only read
--> but: update some other data (without any commit)

IF another_condition THEN
SAVEPOINT my_savepoint;
END IF;
END IF;

END LOOP;

COMMIT;

Normally this works fine: if there is still at least one row left to read in my_cursor, there are no problems executing the rollback to savepoint statement.
But: If my_record is the last record to be read within my_cursor, and if my_condition equals to true, then the rollback to savepoint statement fails with error ORA-01002!

We are using Oracle 8.1.7.

Up to now we couldn't find a similar problem in any forum, so any hints are welcome. Thanks.

martin


Followup:

Hello Tom,

it took some hours of work to find out what exactly the problem is, but now I've got a little example to reproduce the error:

/*
--create two tables and insert one row:
create table test (a integer);
insert into test values (1);
create table test2 (b integer);
insert into test2 values (11);
*/

declare
cursor c is select * from test;
begin
update test2 set b = 22; --this one's new!
savepoint my_savepoint;
for x in c
loop
if (true)
then
rollback to savepoint my_savepoint;
else
null;
end if;
end loop;
end;
/

...leads to "ORA-01002".
Seems to be legal PL/SQL for me, so I hope you can help me.

Kind regards
martin


and Tom said...

Ahh, now i see and it makes sense (hopefully you will agree).


Ok

At time t0, you did an update -- started a transaction

At time t1, you claimed "save point" <<<== important

At time t1+, you *could have* modified the database. <<<=== this is the reason

At time t2, you opened the result set. <<<== important

This result set can see the database "as of time t2 -- including any and all changes you made in your session -- everything that happened from t0 on to t2" you could have modified the TEST for example. You didn't -- but hey, there is nothing saying "you could not have"


At time t3, you say "put the data back the way it was as time t1. It is as if t1+ did not happen (in your case, it didn't -- but it COULD HAVE in general, we don't know, we don't keep track)

So, the rollback to savepoint would have wiped out records your cursor in theory should be able to see, would be able to see. It is like flashing back in a way. You invalidated that cursor. If that cursor remained open, it could well see records that don't, didn't and won't ever exist.

The 'general' rule is -- any rollback to savepoint that goes back to a savepoint that happened BEFORE THE OPEN of the cursor will cause this. Else, inconsistent results would abound.




Rating

  (25 ratings)

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

Comments

Bug:2401670

Guoge, May 09, 2004 - 9:42 pm UTC

The problem doesn;t happen in Oracle10g, It is maybe Bug:2401670, I think.
</code> http://metalink.oracle.com/metalink/plsql/ml2_documents.showDocument?p_database_id=NOT&p_id=202261.1 <code>

Tom Kyte
May 10, 2004 - 7:55 am UTC

Actually.......

that is not true.  that note states:


 Sessions which perform ROLLBACK TO SAVEPOINT operations may encounter an unexpected ORA-1002  error for cursors opened <b>before the SAVEPOINT. </b>


keyword = before.  

But.... in 10g, if you run the testcase as is, you might believe the behaviour to have changed, reasonably enough since:


ops$tkyte@ORA10G> create table test (a integer);
 
Table created.
 
ops$tkyte@ORA10G> insert into test values ( 1 );
 
1 row created.
 
ops$tkyte@ORA10G> create table test2 (b integer);
 
Table created.
 
ops$tkyte@ORA10G> insert into test2 values (11);
 
1 row created.
 
ops$tkyte@ORA10G> commit;
 
Commit complete.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> declare
  2            cursor c is select * from test;
  3  begin
  4            update test2 set b = 22; --this one's new!
  5            savepoint my_savepoint;
  6            for x in c
  7            loop
  8                    if (true)
  9                    then
 10                            rollback to savepoint my_savepoint;
 11                    else
 12                            null;
 13                    end if;
 14            end loop;
 15  end;
 16  /
 
PL/SQL procedure successfully completed.


<b>It would appear the behaviour has changed -- however, this is simply a side effect of plsql transparently array fetching 100 rows at a time.  Consider:</b>

ops$tkyte@ORA10G> insert into test select rownum from all_objects where rownum <= 100;
 
100 rows created.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> declare
  2            cursor c is select * from test;
  3  begin
  4            update test2 set b = 22; --this one's new!
  5            savepoint my_savepoint;
  6            for x in c
  7            loop
  8                    if (true)
  9                    then
 10                            rollback to savepoint my_savepoint;
 11                    else
 12                            null;
 13                    end if;
 14            end loop;
 15  end;
 16  /
declare
*
ERROR at line 1:
ORA-01002: fetch out of sequence
ORA-06512: at line 6
 
<b>the second true fetch does hit the expected ORA-01002</b>


 

Not that bug, I think.....

Mark J. Bobak, May 10, 2004 - 2:33 am UTC

I looked at that bug report. It seems to refer to ORA-1002
on rollback to savepoint for cursors opened BEFORE the save-
point. Clearly, that's a bug. However, in this case the
ORA-1002 is on a cursor opened AFTER the savepoint. For the
reasons Tom already clearly stated, this seems like
reasonable behavior. BUT, Guoge is correct, the above code
WORKS in 10g. Any idea why that is, Tom?

I tried the following versions with the following results:
8.1.7.4: Failure
9.2.0.4: Failure
10.1.0.2: Success!

(all the above on Sun Solaris 8)

Any idea, Tom?

Tom Kyte
May 10, 2004 - 7:58 am UTC

see above...

Thanks Tom!

Mark J. Bobak, May 10, 2004 - 11:08 am UTC

You da man! ;-)

I like this

Christo Kutrovsky, May 11, 2004 - 9:00 am UTC

This is a cute one. I like it :)

It shows how changes can have side effects in the most strange places.


Still some questions...

Martin Ruprecht, May 12, 2004 - 5:07 am UTC

Thanks for your explanation - now I see the point!

But...
1. The error doesn't occur without the update before the first savepoint - that's not consistent.
2. Unfortunately I have to use a construct like this, so the only way I see is to modify the code like this:

declare
cursor c is select * from test;
begin
update test2 set b = 22; --this one's new!
for x in c
loop
if c%ROWCOUNT = 1 THEN
savepoint my_savepoint;
END IF;

if (true)
then
rollback to savepoint my_savepoint;
else
null;
end if;
end loop;
end;
/

...but that really doesn't look nice. Any other ideas?

Tom Kyte
May 12, 2004 - 7:46 am UTC

1) sure it does.

a) when in transaction
b) and you open a cursor AFTER a savepoint
c) and you rollback to savepoint
d) cursor is closed

2) why don't you tell me what you are doing, doesn't look like a well formed transction. it seems to say:

loop over records
try something
if condition undo all of the something's
end


so that you could end up doing a little tiny bit of work at the end and keeping it but all of the unsuccessful stuff rolls back its "something" as well as all preceding "somethings"

seems the transactional logic is "funky" here.

ORA-01002 when using rollback to savepoint

ramesh, May 13, 2004 - 6:07 am UTC


I still have a question

A reader, June 28, 2004 - 6:06 pm UTC

Tom:

assume i have a table t1(a name, b description);
also I have an outside source which gives me "name" and "new description".
I want to use this outside source to modify my table.
i want to update as many as possible. this means that if error happens, I can simply rollback that specific row and keep with other rows.(the logic used may be complicate than a simple update).

so what I am goint to do here is

1.

for each row in the outside source.
update mytable.
if error occur, rollback , otherwise commit;

however, this one may not give us good performance. so I am thinking modify it to this.

for each row in the outside source.
savepoint mysavepoint.
update mytable.
if error occur, rollback to savepoint;
end loop;
commit;

could you please comment on this?

================================================
Here is simplified example, assume around 10% of record will fail, and i simple ignore those 10%.
================================================
create table t1 ( a number);

begin
for I in 1..1000 loop
savepoint a;
insert into t1 values(i);
if (I mod 10)=0 then
rollback to a;
end if;
end loop;
commit;
end;


begin
for I in 1..1000 loop
insert into t1 values(i);
if (I mod 10)=0 then
rollback;
end if;
commit;
end loop;
end;


plsql cursor prefetch

Depreter Frederic, July 07, 2004 - 7:32 am UTC

Hello Tom,

In your answer to Guogue on May 9 2004, you stated:
"It would appear the behaviour has changed -- however, this is simply a side
effect of plsql transparently array fetching 100 rows at a time."

I have 2 questions:
1.Is there a system parameter (like plsql_prefetch_rows) that is specifying the number of records that will be prefecthed ?
2. Is it possible to have a similar effect in Oracle 9.x
by setting any system parameter ?




Tom Kyte
July 07, 2004 - 8:47 am UTC

1) not that I am aware of, don't see one

2) no, not automagically anyway. You would have to:

fetch c bulk collect into ... limit 100;


explicitly do it.

Related to this thread.

Ramasamy, September 22, 2004 - 10:08 am UTC

Tom ,

As always thanks a lot for your great service.
I have same kind of error and almost same situation but not exactly similar.

Let me explain breifly what is happening.

1.) A pro*c program (call it A.PC) which uses dynamic sql which uses the query and select records and put it into flat file. That all!!. NO UPDATE DELETE INSERT COMMIT or whatsoever.
SIMPLE READ and PUT IT IN A FILE.

2.) This Pro*C works great ( Running thru appworx scheduler)
when running independently.

3.) When running in parallel with other programs (different pro*c programs which does have CRUD , savepoint  , rollback to a savepoint etc) this is failing with ORA-01002 randomly.
SOmedays it is going fine and some other days it is failing.

4.) After looking at every possiblities , i have proposed the following reason to management(got of your site).

This issue is vague and not related to the program A.PC. But it is related to the programs which are running in parallel while  A.PC running.
The following is my reasoning for this Bug. It may not be right, Rather it is a well educated guess.

Reason.

Any Rollback to a Savepoint that happened before the open of the cursor will invalidate the cursor and cause this error.

Proof of my test case;

--create two tables and insert one row:
create table test (a integer);
insert into test values (1);
create table test2 (b integer);
insert into test2 values (11);

  1  declare
  2     cursor c is select * from test;
  3     l1 NUMBER;
  4  begin
  5     update test2 set b = 22;
  6     savepoint my_savepoint;
  7     OPEN c;
  8        rollback to savepoint my_savepoint;
  9        FETCH c INTO l1;
 10        dbms_output.put_line(l1);
 11     CLOSE c;
 12* end;

-- Run the above PL/SQL Block
SQL> /
declare
*
ERROR at line 1:
ORA-01002: fetch out of sequence
ORA-06512: at line 9

SQL> 

I am almost certain this is the reason for this .
My only question is , 

step 1)     savepoint my_savepoint;
step 2)     OPEN c;
stemp 3)    rollback to savepoint my_savepoint;
step 4)     FETCH c INTO l1;

during step 2 there are atleast 30 programs might have opened the cursor since lots are in parallel and why only this job is failing. Supposed to be all jobs OPENED the cursor and did a fetch.

Note : A.PC select statement runs for 80 minutes.

II) Do you think this could be a memory problem since atleast more than 30 jobs are in parallel (which i don't think).

III) DO you think of any other possibilities for this "fetch out of sequence error".

I have tried to fill in as much as possible to clarify this issue. Hope you understand my issue and hope you have time to look at it. ONe more time , A.PC just have ONLY SELECT and dump it into a file. Nothing else like Savepoint , rollback , commit , insert , update , delete etc...

Thanks a lot.

 

Tom Kyte
September 22, 2004 - 10:25 am UTC

are the other programs running in their own session? transactional stuff is only related to "a session"

No , It is not the same session

ramasamy, September 22, 2004 - 10:48 am UTC

In our production environment everything runs thru appworx.
Either is is a Pro*c Program or Shell script.
But each one get connected thru' a session because we pass LOGIN parameter for each as userid/Password@SID.

Now userid/Password@SID password is same for all batch programs.

In your answer "transactional stuff is specfic to a session".

Which means ,A.PC (Which doesn't have any Transaction) which fails with ora-01002 is not related to this savepoint , rollback (eventhough other sessions have it). Is that right?. If that is true can you think any other possibilites which cause this error with the information i have provided to you earlier.

Thanks a lot for looking at it.



Tom Kyte
September 22, 2004 - 11:02 am UTC

it is not related -- you can contact support, they can show you how to enable a trace event to get more information -- but it would seem to be either

a) a proc issue
b) an issue with your developed code.

my final beg

Ramasamy, September 22, 2004 - 11:17 am UTC

I just noticed A.PC contains meaningless commit work at the very end(before termination of program). Do you think this would have caused the problem.

EXEC SQL CLOSE C;

EXEC SQL COMMIT WORK;

My second choice is ,

Since A.PC is a complex global program which accept n different SQL select statement and product output file for each. For example A.PC has code like below
XEC SQL INCLUDE sqlca;
EXEC SQL INCLUDE sqlda;

extern SQLDA *sqlald();
extern void sqlclu();
EXEC SQL PREPARE S FROM :sqlstmt;
EXEC SQL DECLARE C CURSOR FOR S;
EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;
EXEC SQL OPEN C;
EXEC SQL FOR :array_size FETCH C
USING DESCRIPTOR select_dp;

It would have had some implicit oracle transactions, which would have caused this problem?.

Thanks a lot.
Ramasamy T



Tom Kyte
September 22, 2004 - 11:50 am UTC

no.

no.

(not given the information supplied here, no)

Explanation to tar ORA-01002 Fetch out of sequence in Pro*C (505712.995)

Ramasamy, September 22, 2004 - 4:44 pm UTC

Tom ,

I badly need your expertise to resolve this issue one more last time.
I did a search on metalink on "ORA-01002 Fetch out of sequence in Pro*C " and got this hit (505712.995).

There they are saying the issue was resolved for them and the reason was "The problem was the EXEC SQL OPEN cursor; was failing as the TEMP tablespace was full."

I am hoping this is what happening with me (Ramasamy - Please refer earlier feedbacks today) since within A.PC the query is running around 80 minutes. Just to confirm this reason, i need the following help.

For cursor we have
1.) OPEN
2.) FETCH
3.) CLOSE.

I know OPEN does parse and excuecute.
Meaning While opening and executing the cursor , will Oracle takes TEMP tablespace if my query is like the following and takes 80 minutes to produce result set?.



SELECT sum(c1) from (
select sum(x1) c1 from (
select y1 from table1)
)

Assume my table1 has 20 million rows. Don't worry above performance the way this query is looking.

Assuming the end result is only 5000 rows and the query runs for 80 minutes all the time has taken while OPEN phase of this select with negligible FETCH , Close times.
Is that TRUE?.

Your help on this is greatly appreciated.

Thanks,
Ramasamy



Tom Kyte
September 22, 2004 - 6:05 pm UTC

like I said, if you contact support (which you must have you queried metalink), they can instruct you as to how to set an event in your code to enable extra information to be dumped out on this error which will be useful in diagnosing "what is going wrong"

everything else is "guessing".


Please, initiate that course of action.

Is 10g right?

Ravi, October 24, 2004 - 12:10 pm UTC

Tom,
What I dont understand is when why 10g's implicit 100 record bulk collect matters here?

ops$tkyte@ORA10G> declare
2 cursor c is select * from test;
3 l_rec test%rowtype;
4 begin
5 update test2 set b = 22;
6 savepoint my_savepoint;
7 for x in c
8 loop
9 rollback to
10 savepoint my_savepoint;
11 end loop;
12 end;
13 /

I might be silly here, but I guess its the For loop at 7 opening the cursor AFTER the savepoint has been created. So, assuming its collects 100 records from the DATABASE at this point, its still going to output one by one between lines 8 and 11.

On the first output record, its going to rollback to savepoint at line 10. Going by

At time t0, you did an update—you started a transaction.
At time t1, you claimed a "savepoint."
At time t1+, you could have modified the database.
At time t2, you opened the result set.


For the NEXT record (record 2), I'd be at T2 which is STILL beyond the rollbacked time of T1.

So should 10g not abort with 1002 for the SECOND record, either it fetches 100 records or not?

Please explain.

Tom Kyte
October 24, 2004 - 1:27 pm UTC

it would error out with the ora-1002 IF we were actually going back to the database to fetch the record - but we are not.

That code in 10g is identical to:

declare
cursor c is select * from test;
...

begin
update test2 set b = 22;
savepoint my_savepoint;
open C;
loop
fetch c BULK COLLECT into array1, array2, ... limit 100;
rollback to savepoint my_savepoint;
for i in 1 .. array1.count
loop
process i'th record...
end loop;
exit when c%notfound;
end loop;


the first 100 records are all fetched on the very first fetch in 10g -- before the rollback to savepoint occurs.

Ravi, October 25, 2004 - 7:30 am UTC

Whew! Thats complicated.
Please correct if I am wrong!

So as PL/SQL is a client to SQL, its already GOT the 100 records with it and inside the For Loop, all it does is to push them out one by one?

But, when it RUNS OUT of 100 records and it needs to go back to the Database to get the next 100 and at this point the Transaction is validated to be at time T1.

And its SQL which says thats not quite right, I am at T1, while this cursor is asking me to fetch something at T2 (which does not exist as its been rolled back) and hence generate the ORA-1002?

On the other hand pre-10g, each fetch may have been a transport to the database between PLSQL and SQL and hence SQL throws out the error with its second fetch. Can you confirm this error is raised for the SECOND record, pre-10g?

Cheers

Ravi

Tom Kyte
October 25, 2004 - 7:50 am UTC

in 10g -- yes, that is exactly what is happening.

Ravi, October 25, 2004 - 12:52 pm UTC

Tom,

Does this mean, in 10G, the first 100 records, kinda violate what you said in your first post
Quote

"So, the rollback to savepoint
would have wiped out records your cursor in theory should be able to see, would be able to see. It is like flashing back in a way. You invalidated that cursor. If that cursor remained open, it could well see records that don't,
didn't and won't ever exist."


Tom Kyte
October 25, 2004 - 1:18 pm UTC

i'm pointing out that this optimization in 10g changes that to a degree. "things change". as i said, in 10g, it is as if the code were:

declare
cursor c is select * from test;
...

begin
update test2 set b = 22;
savepoint my_savepoint;
open C;
loop
fetch c BULK COLLECT into array1, array2, ... limit 100;
rollback to savepoint my_savepoint;
for i in 1 .. array1.count
loop
process i'th record...
end loop;
exit when c%notfound;
end loop;

ORA-01002 after rollback statement

JLU, November 14, 2005 - 9:49 am UTC

Hi Tom,

We are using Pro*C in 8.1.7.4 database.

Our pb is :

We have error : ORA-01002 when we trying to fetch cursor 500 by 500 because of rollback statement ...


Pro*c executes the following things :


1. first procedure permits to declare cursor

select col1, col2 from table1,table2 etc ...;

2. Second permits to open cursor
and fetch 500 records into tab[i] with "while (i <=500)" statement ..

3. Then we call another procedure which tries to insert in another table
insert into tableA (colx, coly) values (tab[i] value , 'y');

=> If insert statement failed : we executed : rollback statement


4. Go back to the second procedure, then :
Re-init tab[i] (remove values),
fetch the 500 following records into tab[i] ... etc etc ...

5. close cursor, commit;


When Insert failed ==> rollback is executed and ORA-01002 appends !
If i disable rollback statement, second fetch is ok ...

Is cursor closed implicitly by rollback statement ?
How can i solve this pb ?


thank in advance.

Tom Kyte
November 14, 2005 - 1:24 pm UTC

(you should array fetch 500 records, not slow by slow fetch 500 records - you can do that in one call).


do you select for update?
do you modify the table before you open the cursor on it?


RE : ORA-01002 after rollback statement

JLU, November 15, 2005 - 3:47 am UTC

Hi Tom,

In our Pro*C we must realise treatment line by line not in one time.

1.We don't user "select for update" just a "select"

2. We don't modify anything before .. error appened at the beginning of program.

Thank a lot for your time.


Tom Kyte
November 15, 2005 - 8:38 am UTC

I'd need to see an example.

no 50,000 lines of code, a small example that mimicks what you are doing.

RE:RE : ORA-01002 after rollback statement

JLU, November 15, 2005 - 8:10 am UTC

Tom,

I have made some tests with following SQL exemple :

1. first procedure permits to declare cursor

select col1, col2 from table1,table2 etc ...;

2. Second permits to open cursor
and fetch 500 records into tab[i] with "while (i <=500)" statement ..
and juste before fetching cursor, i 've add a savepoint.


3. Then we call another procedure which tries to insert in another table
insert into tableA (colx, coly) values (tab[i] value , 'y');

=> If insert statement failed : we executed : rollback to savepoint ..


4. Go back to the second procedure, then :
Re-init tab[i] (remove values),
fetch the 500 following records into tab[i] ... etc etc ...

5. close cursor, commit;



With this programm, i obtained the following result :

1.
first 500 fetch + (insert and update) ko
second 500 fetch (501 to 1001) + update ok so commit !

==> result ok , the savepoint seemed solves problem


2.
first 500 fetch + (insert + update) ok so commit my 500 first records!
second 500 fetch (501 to 1001) + (insert and update) ko
third 500 fetch (1002 to 1502) + (insert and update) ok

==>result is ORA-01002 ...fetch out of sequence ...


IS first commit create a "fetch accross commit" ?

How can i rollback or commit on treatment for each record fetched ?

thank you one more time.

Tom Kyte
November 15, 2005 - 11:37 am UTC

need example. code example. I don't see anything obvious here.

savepoint every record versus commit every record for say one million records

Paul, October 24, 2006 - 4:00 pm UTC

I have five Million Records in a staging table. I am calling a standard published public API by Oracle Applications ( Ebusineess 11.5.10) to create instances in Oracle Install base Product using the above data. every record in the staging table should be converted to an instance in Oracle Install base.
So the process is
1. read the staging table and loop
2. for every record, call the public API
3. I also need to log errors into a error table incase if of any failure to the API
4. I also need to update a status column in the staging table to say that its success,error or needs to reprocess

I have 2 options
Option 1. This is what i like to do
a) Loop through the records. Inside the loop,before making the standard API call, have a savepoint. If the standard API call fails,capture it in exception and rollback to the savepoint set before the public API call and make a call to a pragma autonomous transaction procedure( that has a commit) which will log the error into my error tables and also update
the staging table record that this errored out or instaead of updating, i could insert into another table that captures the primary key and the status of the record
b) Then if its a success, update/insert the staging table/temp table that its a success and do nothing untill the no of records processed is say 1000 records or 5000 records and then commit.

Option 2 is
1. Loop through and if there is an success or error update the staging table with status and commit after processing evey record in staging table

What would be your recommendation to increase performance?

If it is Option1, what are the potential issues or errors that we might face since the volums of data is huge.
When we had our team meeting,developers are kind of scared to take Option1, since they are afraid or has a firm belief that they will hit with several issues.
In Option 1,another concern was, Since we have savepoints for every records, will it affect performance.

I am trying to benchmark option1 and option2 with my test programs. I started with 10,100,200,300 records. Right now our dev instance is having tablespace issues and dba is allocating and reallocating space and i am getting inconsistent results. I will keep trying to benchmark

But in between, if you could share your views on this, it would be of great help.







Tom Kyte
October 25, 2006 - 9:23 am UTC

what language are you writing this process in

if plsql - option 2 is likely just fine (we use an async commit in plsql for you, you don't have to wait for the log file sync)

if not plsql, and not in 10gr2, option 1 is likely something you want to seriously consider - as every commit will wait for log file sync and you might find you spend as much time "comitting" as you do "working". In 10gr2, you have an async commit - which since your process is designed as restartable - can be safely used.

Follow Up

Paul Ukken, October 25, 2006 - 10:58 am UTC

I am using pl/sql. Datbase is 9.2.0.5.0.
So, just to confirm, your recommendation here would be explicitly committing after processing every record from the staging table ,(which was my option No2), right?


Tom Kyte
October 25, 2006 - 1:49 pm UTC

I said option 2 is fine - since you are making it perfectly restartable (most people do not, they just commit, and that would be a bug)



somehow not convinced myself.

Paul, October 25, 2006 - 2:36 pm UTC

sorry to trouble you again on this.

In one of your articles below
</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:4951966319022 <code>

You have adviced that frequent commits is not recommended.
Why are you then recommending commit after every record?
Maybe i am not conveying properly what i want to do. I have 2 pseudo codes below.
which of the approaches you recommend

Option1 Pseudo Code ( without committing after every record
---

declare
counter NUMBER:=0;
l_batch_id NUMBER;
i NUMBER:=0;
err_count NUMBER:=0;
tot NUMBER:=0;
x_return_status VARCHAR2(1):=null;
CURSOR get_primary_key_id_csr IS
SELECT primary_key_id
FROM xx_ask_tom_pj
WHERE status='R';
begin
SELECT xx_ask_tom_pj_S.nextval INTO l_batch_id FROM dual;
dbms_output.put_line('start' || to_char(sysdate,'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line( 'batch id' || l_batch_id );
for x in get_primary_key_id_csr loop
x_return_status := 'S';
l_primary_key_id:=x.primary_key_id;
savepoint xxx;
begin
-- say the routine below is a standard Oracle APPS public API published by Oracle Applications
-- this routuine could be a complex routine that involves validtaions and inserts into several tables
-- say for example creating an Instance in Oracle Install base using csi_item_instances_pub.create_item_instance
csi_item_instances_pub.create_item_instance( );
exception
when others then
x_return_status := 'U';
end ;
-- explicitly erroring for some records to do a test on rollback and prgma autonomous procedure
if i=2 or i=4 or i=8 or i =12 or i=22 or i=24 or i=32 or i =42 or i=52 or i=62 or i=72 or i=82 or i=92 or i=112 or i=114 or i=118 or i =112 or i=122 or i=124 or i=132 or i =142 or i=152 or i=162 or i=172 or i=182 or i=192 then
x_return_status := 'U';
end if;
IF x_return_status = 'U' THEN
-- log the errror through an pragma autonomous procedure and update status'
err_count:=err_count+1;
rollback to xxx;
Write_apiErrOR -- this is a pragma autonomous procedure with a commit;
(
p_error_msg => 'Error in Creating Instance'
, p_primary_key_id=> l_p.primary_key_id
, p_sql_code => null
, p_sql_error => SQLERRM
, p_creation_date => sysdate
, p_batch_id => to_char(l_batch_id)
);
ELSE
counter:=counter+1;
update xx_ask_tom_pj
set status='S'
,batch_id=l_batch_id
where primary_key_id= l_p.primary_key_id;
END IF;
i:=i+1;
if i=1000 then
commit;
i:=1;
end if;
tot:=tot+1;
end loop;
commit;
dbms_output.put_line( 'total records processed ' || tot );
dbms_output.put_line( 'total records success ' || counter );
dbms_output.put_line( 'total records fail' || err_count);
dbms_output.put_line('end' || to_char(sysdate,'DD-MON-YYYY HH24:MI:SS'));
end;
/

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

-- Option 2 Psuedo Code with commit after record
declare
counter NUMBER:=0;
l_batch_id NUMBER;
i NUMBER:=0;
err_count NUMBER:=0;
tot NUMBER:=0;
x_return_status VARCHAR2(1):=null;
CURSOR get_primary_key_id_csr IS
SELECT primary_key_id
FROM xx_ask_tom_pj
WHERE status='R';
begin
SELECT xx_ask_tom_pj_S.nextval INTO l_batch_id FROM dual;
dbms_output.put_line('start' || to_char(sysdate,'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line( 'batch id' || l_batch_id );
for x in get_primary_key_id_csr loop
x_return_status := 'S';
l_primary_key_id:=x.primary_key_id;

begin
-- say the routine below is a standard Oracle APPS public API published by Oracle Applications
-- this routuine could be a complex routine that involves validtaions and inserts into several tables
-- say for example creating an Instance in Oracle Install base using csi_item_instances_pub.create_item_instance
csi_item_instances_pub.create_item_instance( );
exception
when others then
x_return_status := 'U';
end ;
-- explicitly erroring for some records to do a test on rollback and prgma autonomous procedure
if i=2 or i=4 or i=8 or i =12 or i=22 or i=24 or i=32 or i =42 or i=52 or i=62 or i=72 or i=82 or i=92 or i=112 or i=114 or i=118 or i =112 or i=122 or i=124 or i=132 or i =142 or i=152 or i=162 or i=172 or i=182 or i=192 then
x_return_status := 'U';
end if;
IF x_return_status = 'U' THEN
-- log the errror to an pragma autonomous transaction'
err_count:=err_count+1;

Write_apiErrOR -- this is a regular procedure to the log error and update status column
(
p_error_msg => 'Error in Creating Instance'
, p_primary_key_id=> l_p.primary_key_id
, p_sql_code => null
, p_sql_error => SQLERRM
, p_creation_date => sysdate
, p_batch_id => to_char(l_batch_id)
);
ELSE
counter:=counter+1;
update xx_ask_tom_pj
set status='S'
,batch_id=l_batch_id
where primary_key_id= l_p.primary_key_id;
END IF;
i:=i+1;
commit;
tot:=tot+1;
end loop;

dbms_output.put_line( 'total records processed ' || tot );
dbms_output.put_line( 'total records success ' || counter );
dbms_output.put_line( 'total records fail' || err_count);
dbms_output.put_line('end' || to_char(sysdate,'DD-MON-YYYY HH24:MI:SS'));
end;
/

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

Tom Kyte
October 25, 2006 - 4:25 pm UTC

your api call - I am assuming - takes "a measurable amount of time, does a lot of work"

you are NOT committing a single sql statement as far as I could tell, you are committing a long complex involved API call.

That was my assumption.


I said in plsql, you could probably get away with, be fine with, the approach. It is your decision.

thanks a lot

Paul, October 25, 2006 - 5:14 pm UTC

thanks a lot forclarifying. its a correct assumption that its an involved API call. We have gone with Option2. I was debating about it though with my team. Again sincere thanks for your help and time.


ORA-01086: savepoint 'your savepoint' never established

Mohamed From France, November 10, 2006 - 9:57 am UTC

Hi Tom,
I recently encoutered the above error. I searched this site and read chapter 15 (autonomous transactions) of your book.

All explanations I have found do not answer my situation. Let me explain you what I pointed out (may be it is published in this site in this case sory I have not find it)

If you implement the following procedures

CREATE OR REPLACE PROCEDURE P_2
IS
-- pragma autonomous_transaction; -- put in comment
BEGIN
insert into T2 values (25);
commit;

END P_2;
/

CREATE OR REPLACE PROCEDURE p_never_esta IS

BEGIN

-- set a savepoint here
savepoint s_a_savep;


insert into T1 values (155);

P_2;

rollback to s_a_savep;

END;
/
Table T1 and T2 are
create table t1(x number); create table t2(y number);

and then execute this
begin
p_never_esta;
end;

You will get
ORA-01086: savepoint s_a_savep never established

For me it is very normal than you can not commit and then ask to rollback to your savepoint. The problem is that this situation is not explained as one of the reason causing the ORA-01086



Tom Kyte
November 10, 2006 - 2:39 pm UTC

it is obvious though that by committing, you have wiped out all existing savepoints, it just "is" (it seems rather natural and perhaps even obvious that this would happen)

ORA 1002 when selecting from a DB link to a table on DB2

Anie, January 23, 2009 - 1:47 am UTC

Hi Tom,
This is an SOS call from a budding DBA who is working on testing applications after we have migrated our databases from 9i to 10g R2, all of which are in VB. Read on your forum regarding various solutions to the ORA 1002 errors and so thought of posting my query here. Earnest apologies if I would have posted it as a continuation to the thread instead of posting it as a new question. My query is as below:
I am executing a stored procedure containing a reference cursor in it through my VB application. This reference cursor in turn calls a table existing on DB2 using a DB link and stores the returned records into the ref cursor. When I try to execute the procedure through my application, it runs fine. However, on trying to parse through the contents of the recordset, I encounter an ORA 1002 error. The stored proc does not have any insert,update and delete statements in it. It simply contains a select statement which selects a row from the DB2 table. However, if I create the same table on Oracle and then run the stored proc, it runs fine and gets me the data into the recordset. Also, my app was running fine when I was running it on my 9i database, the stored proc as running fine and the recordset was also getting populated. Please advice.

Tom Kyte
January 23, 2009 - 8:50 am UTC

I'll have to refer you to support, I don't have the facilities to reproduce/test.

Works in 9i not in 11g

Dominic, July 02, 2012 - 11:11 am UTC

Tom,

Looking at the original question, I would have expected this to work in 11g but not in 9i, but the opposite is happening

create table domtest(msg varchar2(10));
insert into domtest values('a');
insert into domtest values('b');
insert into domtest values('c');

commit;

declare
  cursor c1 is
  select msg
  from domtest2
  for update;
begin
for crow in c1
loop
  dbms_alert.register('myalert'||crow.msg);
  update domtest2 set msg = 'x' where current of c1;
  dbms_alert.remove('myalert'||crow.msg);
end loop;
commit;
end;
/

In 9.2.0.7 it runs successfully but in 11.2.0.3 it fails with ora-01002.

SQL*Plus: Release 9.2.0.7.0 - Production on Mon Jul 2 17:04:00 2012

Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.


Connected to:
Oracle9i Enterprise Edition Release 9.2.0.7.0 - 64bit Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.7.0 - Production

SQL> declare
cursor c1 is
select msg
from domtest2
for update;
begin
for crow in c1
loop
dbms_alert.register('myalert'||crow.msg);
update domtest2 set msg = 'x' where current of c1;
dbms_alert.remove('myalert'||crow.msg);
end loop;
commit;
end; 2 3 4 5 6 7 8 9 10 11 12 13 14
15 /

PL/SQL procedure successfully completed.

SQL> select *
2 from domtest2;

MSG
----------
x
x
x


SQL*Plus: Release 11.2.0.3.0 Production on Mon Jul 2 17:07:59 2012

Copyright (c) 1982, 2011, Oracle. All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> declare
cursor c1 is
select msg
from domtest2
for update;
begin
for crow in c1
loop
dbms_alert.register('myalert'||crow.msg);
update domtest2 set msg = 'x' where current of c1;
dbms_alert.remove('myalert'||crow.msg);
end loop;
commit;
end; 2 3 4 5 6 7 8 9 10 11 12 13 14
15 /
declare
*
ERROR at line 1:
ORA-01002: fetch out of sequence
ORA-06512: at line 7

I would have expected the 11g instance to fetch all the rows in one go and not hit the fetch out of sequence while the 9i version should have hit it on the second row.

Thanks in advance
Tom Kyte
July 02, 2012 - 2:12 pm UTC

Two things at work here

the for update is causing the array fetch to go into row by row mode...

there was a bug fix for the broken 9i code that should have failed.


Since dbms_alert commits - the locks are lost and the for update has to fail, it was a bug in 9i that it worked sometimes.