[comp.databases] Oracle CURSORS useless ?

nigel@cnw01.storesys.coles.oz.au (Nigel Harwood) (06/25/91)

Okay, useless may be a bit strong, but not much.  At least if
this is true at any rate.

I am beginning with PRO*C and my first program needs to read the rows
from a table and modify each with values from a flat file before
writing them back to the table.

My first step was to set up a loop to display a test table to
see that I had the basics correct.  I did this by declaring a cursor,
opening it, using a loop with a fetch to read through the rows
selected.

This worked as expected.

My next step was to modify the fields with the information from the
flat file, do an update to the table, and then a commit to complete
the transaction etc.

This failed with a "fetch out of sequence" error on the second row.

On asking Oracle they said that you cannot do a commit in the middle
of fetching ?  In other words in doing what I want I can only do one
commit and that's right at the end.  How can I do that if I don't
know how many records I might have in the table ?

I've got something horribly wrong, right ?
This can't be real, can it ?

If I am forced to accept that this is the way of things, how do all
you other Oracle programmers deal with this everyday processing
loop ?

Any help appreciated.

Regards
-- 
<<<<<<<<<<<<<<<<<<<<<<<<<  Nigel Harwood  >>>>>>>>>>>>>>>>>>>>>>>>>>>
<< Post:  Coles Myer Ltd, PO Box 2000 Tooronga 3146, Australia     >>
<< Phone: +61 3 829 6090  E-mail: nigel@cnw01.storesys.coles.oz.au >>
<<   FAX: +61 3 829 6886                                           >>

par@sirius.Aus.Sun.COM (Paul Riethmuller SE Brisbane) (06/26/91)

nigel@cnw01.storesys.coles.oz.au (Nigel Harwood) writes:
> Okay, useless may be a bit strong, but not much.  At least if
> this is true at any rate.
> 
> ... deleted
> 
> On asking Oracle they said that you cannot do a commit in the middle
> of fetching ?  In other words in doing what I want I can only do one
> commit and that's right at the end.  How can I do that if I don't
> know how many records I might have in the table ?
> 
> I've got something horribly wrong, right ?
> This can't be real, can it ?

Well, it does pose a problem for the developer - but you can some consolation
in knowing that this limitation isn't just Oracle's.  Every commercial SQL
DBMS I've ever seen closes all open cursors [SQL/DS, DB2, Ingres ...] when
a commit is issued.

(Although their forms languages may behave differently)

In your case, you could drive the transaction from the ASCII update file
and just issue UPDATE commands.  BTW, doing multiple fetch,update,commit
operations is intensive.  The alternative would be to do ~100 fetch,update
operations, record your position in the cursor scan (and possibly the file
offset - for recovery purposes) then issue 1 COMMIT.

Good Luck

Paul Riethmuller
Sun Microsystems, Brisbane.

Disclaimer:  these are the personal opinions of the author.

elaps@lut.ac.uk (Alan Schwarzenberger) (06/26/91)

I had a long running discussion with the Oracle help desk in the uk
about this a few months back. A commit definitely closes the cursor.
However, in version 6 of oracle, if you make a change to a column that
is in your cursor, you'll see it immediately. For example

If test_table has columns a, b, reject.

EXEC SQL DECLARE C1 CURSOR FOR
	select a, b from test_table for update of reject;
EXEC SQL OPEN C1;
do {
	EXEC SQL FETCH C1 INTO :a, :b;
	/* other code */
	if (condition) 
		EXEC SQL UPDATE test_table SET reject=1 WHERE CURRENT OF C1;
} while (more_rows);
EXEC SQL CLOSE C1;
EXEC SQL COMMIT;

In this example, changes you make to the column reject are visible in
the cursor C1 th next time you go round the do{}while loop. I use this
because I need to know what other rows of data have already been rejected
when I llok at the current row of data.

I use Oracle RDBMS v6.0.30.3.1 running on HP9000 under HPUX, ProC
v1.3.15.1.2

-- 

============================================================================
Alan Schwarzenberger    tel +44 509 222849     A.P.Schwarzenberger@lut.ac.uk
International Electronics Reliability Institute, Loughborough University, UK

jfr@locus.com (Jon Rosen) (06/27/91)

In article <1991Jun26.033043.22948@sunaus.oz> par@sirius.Aus.Sun.COM writes:
>nigel@cnw01.storesys.coles.oz.au (Nigel Harwood) writes:
>> ... deleted
>> On asking Oracle they said that you cannot do a commit in the middle
>> of fetching ?  In other words in doing what I want I can only do one
>> commit and that's right at the end.  How can I do that if I don't
>> know how many records I might have in the table ?
>> I've got something horribly wrong, right ?
>> This can't be real, can it ?
>Well, it does pose a problem for the developer - but you can some consolation
>in knowing that this limitation isn't just Oracle's.  Every commercial SQL
>DBMS I've ever seen closes all open cursors [SQL/DS, DB2, Ingres ...] when
>a commit is issued.
 
  Tis true, tis true... If the SELECT is only ordered on an index
  and you are not doing anything else that requires prefetching the
  entire answer set (like aggregate selection (HAVING) or something),
  most DBMSs will not actually fetch the entire answer set but will
  in fact retrieve it row-by-row for you.  In this case, after each
  commit of the update (if you need to do this), you can issue a new
  SELECT cursor request with a WHERE predicate that asks for the next
  sorted column(s) of the index greater than the one just updated.
  This actually works pretty well in DB2, particularly if you are
  using static SQL (dynamic SQL sucks on this problem for the most
  part since it has to reoptimize the query each time you open the
  cursor).  DB2 Release 2.3 has actualy sort of fixed this with a
  COMMIT HOLD option which will commit but keep the cursor position
  available.  However, I have no idea yet what kind of locks will
  be held (the purpose of COMMIT afterall is also to release locks
  and if this doesn't happen, the cursor approach will still suck).
  What happens depends on the DBMS and your mileage may vary.

>(Although their forms languages may behave differently)
 
  If a DBMS's forms language (or for that matter any tool) behaves
  differently, than it is using something other than SQL as its
  access method (or at least is augmenting its SQL with some kind
  of under-the-covers scheme for locking and navigation).  This is
  not only not good but it can be very dangerous, particularly if
  you mix applications that use the tool with regular SQL applications
  and the DBMS doesn't integrate the usage properly.  Oracle was 
  certainly guilty of this in early SQL*Forms... I have no idea it
  that is still true.  It was supposed to have changed with Forms 4.0
  but I am not sure whether that has ever been released.

  Jon Rosen

markj@informix.com (Mark Jeske(Chicago Consultan)t) (06/27/91)

In article <1991Jun26.033043.22948@sunaus.oz> par@sirius.Aus.Sun.COM writes:
>
>nigel@cnw01.storesys.coles.oz.au (Nigel Harwood) writes:
>
>Well, it does pose a problem for the developer - but you can some consolation
>in knowing that this limitation isn't just Oracle's.  Every commercial SQL
>DBMS I've ever seen closes all open cursors [SQL/DS, DB2, Ingres ...] when
>a commit is issued.
>

Not Informix.  In our 4.0 product you can declare a cursor with hold
and it won't be closed during a commit.

mark

jfr@locus.com (Jon Rosen) (06/27/91)

In article <1991Jun26.100717.17267@lut.ac.uk> elaps@lut.ac.uk (Alan Schwarzenberger) writes:
>I had a long running discussion with the Oracle help desk in the uk
>about this a few months back. A commit definitely closes the cursor.
>However, in version 6 of oracle, if you make a change to a column that
>is in your cursor, you'll see it immediately. For example
>If test_table has columns a, b, reject.
>
>EXEC SQL DECLARE C1 CURSOR FOR
>	select a, b from test_table for update of reject;
>EXEC SQL OPEN C1;
>do {
>	EXEC SQL FETCH C1 INTO :a, :b;
>	/* other code */
>	if (condition) 
>		EXEC SQL UPDATE test_table SET reject=1 WHERE CURRENT OF C1;
>} while (more_rows);
>EXEC SQL CLOSE C1;
>EXEC SQL COMMIT;
>
>In this example, changes you make to the column reject are visible in
>the cursor C1 th next time you go round the do{}while loop. I use this
>because I need to know what other rows of data have already been rejected
>when I llok at the current row of data.

 Be careful about generalizing this theory.  It will only work when 
 Oracle traverses the table through the cursor on a row-by-row basis
 as each FETCH is processed.  This will in fact occur quite often, 
 particularly when you have no ORDER BYs and no aggregates or GROUP BY
 expressions.  In addition, evem when you have ORDER BYs, this may still
 happen because either an index is available on the ORDER BY column(s)
 and/or Oracle chooses to build and sort a list of ORDER BY columns only
 and then retrieve by ROWIDs that it keeps with the sorted list.  
 
 This is fraught with peril, however, since you can not be sure this
 will always work.  If an ORDER BY on an unindexed column is used and
 the entire SELECT list is retrieved, sorted and held as an answer
 set, the result will be completely different and any updates or deletes
 you do while the cursor is open will no work.  In fact, you may block
 due to locks held on your rows (depending on the kind of cursor SELECT
 you are doing) and you might even find your update being cancelled for
 deadlock reasons.  In addition, you should realize that such behavior
 is totally inconsistent with any definition of the relational model
 since the cursor/FETCH mechanism is an artifact of procedural language
 requirements... In relational data bases and SQL, the SELECT should
 retrieve a complete set of rows at the instant in time when it is
 executed.  Any subsequent changes to those rows (if any are allowed)
 should not be reflected until a reSELECT is done.
 
 Jon Rosen

gnh@cci632.cci.com (George Hillenbrand) (06/27/91)

In article <25762@oolong.la.locus.com> jfr@locus.com (Jon Rosen) writes:
>  ...  If the SELECT is only ordered on an index
>  and you are not doing anything else that requires prefetching the
>  entire answer set (like aggregate selection (HAVING) or something),
>  most DBMSs will not actually fetch the entire answer set but will
>  in fact retrieve it row-by-row for you.  ...

Which among the major relational databases (Oracle, Informix, Ingress,
Sybase...) avoid the prefetch for a select of a set of records having a
particular indexed field value?  

gatynen@well.sf.ca.us (Gerard Tynen) (06/27/91)

Having last year built an embedded SQL precompiler for cobol and
SQL Server, we came across this "commit closes all cursors" issue.

We couldn't think of any reason to force a close on all cursors just
because you do a commit.  I think that what happened is that early 
versions of DB2 did this for some reason related to DB2 and MVS and so
everybody else who did a precompiler did it also.

Now this kind of behaviour has become standard.  The NIST verification
suites test for it, and I think also it is being assumed by the SAG 
commitee as well.

We ended up leaving our cursors open after a commit which is nice, 
because you don't lose your place in the result set and can commit as
often as you feel you need to.  However, I just got a call from the
customer I did the precompiler for and they want to change it back to
having commits close all open cursors.  I assume they want this in order
to remain "standard" since there is no real reason I can think of to
force this.

--GT

dlm@hermes.dlogics.com (06/28/91)

nigel@cnw01.storesys.coles.oz.au (Nigel Harwood) writes:
> On asking Oracle they said that you cannot do a commit in the middle
> of fetching ?  In other words in doing what I want I can only do one
> commit and that's right at the end.  How can I do that if I don't
> know how many records I might have in the table ?

There is no law that says "Thou shalt commit after every update". In fact,
after you fetch a row, you can use UPDATE with the CURRENT OF CURSOR clause
specifically to make use of the cursor information at hand. When you run out
of rows in the result set, THEN you close the cursor, and THEN you commit.

Committing after each update is inadvisable for performance reasons; knowing 
the number of rows in the result set is not especially important. Oracle will
jump out of the FETCH loop when the result set is emptied. Refer to EXEC SQL
WHEN SQLERROR ... in the manual.
-- 
Dave Mausner, Sr Tech Consultant / Datalogics Inc / Chicago IL / 312-266-4450
                        dlm@hermes.dlogics.com
            "Don't talk about it -- just show me the code!"

mao@eden.Berkeley.EDU (Mike Olson) (06/28/91)

In <25731@well.sf.ca.us>, gatynen@well.sf.ca.us (Gerard Tynen) writes

> We couldn't think of any reason to force a close on all cursors just
> because you do a commit.

if you're doing two-phase locking, and you release locks at commit time,
you'd better close your cursors.  changes by concurrent transactions will
become visible at that time, too, which means that the set of prefetched
values cached in the cursor may be wrong if you keep it open.

					mike olson
					postgres research group
					uc berkeley
					mao@postgres.berkeley.edu

epstein@world.std.com (Richard W Epstein) (06/28/91)

In article <25731@well.sf.ca.us> gatynen@well.sf.ca.us (Gerard Tynen) writes:
>We couldn't think of any reason to force a close on all cursors just
>because you do a commit.  I think that what happened is that early 
[deleted...]

A commit by definition makes any updates,inserts, and deleted permanent
and releases locks. After this happens, where would your cursor point?
Since you released your locks, you cannot be sure the records or
even the table is still there or that you have access to them.

Ingres has a command called 'savepoint' and allows you to rollback
to the last savepoint without aborting the entire transaction.
I don't think ANSI SQL has any provision for this.

The real need for this seems to be in syncing with external devices/files
which are not under the DBMS transaction control. Transactions loose
some of their usefulness when all parts of the update cannot rollback.

--Richard W. Epstein		epstein@world.std.com

IAN@SLACVM.SLAC.STANFORD.EDU (06/29/91)

Oracle has SAVEPOINT as well.  An Informix employee mentioned using the
hold option.  Oracle has a hold cursor and a release cursor option.  I
don't know whether they will solve the commit problem.  I thought they
were used so that after a cursor was closed you could open the cursor
again with a different value in the predicate.  You didn't need to
redeclare the cursor.  In fact Oracle is not ansi standard in that it
allows you to re-open a cursor without closing it first, (with the
default option settings).  What exactly does hold cursor mean to Informix


                  Ian MacGregor [Stanford Linear Accelerator Center]

aland@informix.com (Colonel Panic) (06/29/91)

In article <1991Jun26.033043.22948@sunaus.oz> par@sirius.Aus.Sun.COM writes:
>
>nigel@cnw01.storesys.coles.oz.au (Nigel Harwood) writes:
>> 
>> On asking Oracle they said that you cannot do a commit in the middle
>> of fetching ?  In other words in doing what I want I can only do one
>> commit and that's right at the end.  How can I do that if I don't
>> know how many records I might have in the table ?
>> 
>> I've got something horribly wrong, right ?
>> This can't be real, can it ?
>
>Well, it does pose a problem for the developer - but you can some consolation
>in knowing that this limitation isn't just Oracle's.  Every commercial SQL
>DBMS I've ever seen closes all open cursors [SQL/DS, DB2, Ingres ...] when
>a commit is issued.

This is an ANSI requirement; terminating a transaction (COMMIT or
ROLLBACK) releases all locks and closes all cursors.

Informix (at least) provides an optional cursor extension known as
a persistent cursor or "hold" cursor.  A cursor declared WITH HOLD,
once opened, remains open (regardless of transactions) until explicitly
closed (or until the database itself is closed).  Commit/Rollback 
still releases all locks, so the developer must be careful in her/his
integrity assumptions (any rows updated in the hold cursor will not
necessarily remain locked, even under explicitly higher Isolation
Levels).
 
>Paul Riethmuller
>Sun Microsystems, Brisbane.

--
Alan Denney  #  aland@informix.com  #  {pyramid|uunet}!infmx!aland
  We'll sue your drummer       in perfect four/four time
  We'll sue your manager       for his last thin dime
  We'll sue your record label, hey now ain't this fun
  We'll sue your best friend   if you still have one
  It ain't no crime,    'cause I'm     a rock and roll lawyer
                    -- The Austin Lounge Lizards

gatynen@well.sf.ca.us (Gerard Tynen) (06/30/91)

In article <1991Jun27.225555.27108@agate.berkeley.edu> mao@eden.Berkeley.EDU (Mike Olson) writes:
>In <25731@well.sf.ca.us>, gatynen@well.sf.ca.us (Gerard Tynen) writes
>
>> We couldn't think of any reason to force a close on all cursors just
>> because you do a commit.
>
>if you're doing two-phase locking, and you release locks at commit time,
>you'd better close your cursors.  changes by concurrent transactions will
>become visible at that time, too, which means that the set of prefetched
>values cached in the cursor may be wrong if you keep it open.

I agree.  Our problem was that we were building our cursors on top of Sybase
browse mode, which doesn't lock anything. You are really working on a copy
of the data.  When you update on the cursor, it compares timestamps to see
if anybody modified that row under your nose.  Also, as you indicate, updates
that change the result set are not reflected when you fetch unless you 
reopen the cursor.  

I think we just documented that updatable cursors only fetch data as it 
existed in the database at the time the select statement executed, and if
the user wanted current data, they would have to close and reopen the cursor.

This is bogus, I know, but I feel I can blame Sybase for this bogusicity.  
They are the only database I know of that allows database connections opened
by the same process to deadlock each other.  That means you can't update a
table you are reading without blowing away the result set unless you use their
lame browse mode, but then you really just get a copy of the data as it 
existed at some point in time.  Hey. I'm ready to switch to Postgres! 

So anyway, back to the original point, I now understand that real cursors 
ought to be closed upon commit.  My cursors weren't what I would call real
cursors, after all.  I would call them Sears Cursors.  Hopefully Sybase 
5.0 will not implement Sears Cursors...  

--GT

gatynen@well.sf.ca.us (Gerard Tynen) (06/30/91)

In article <1991Jun27.230131.2358@world.std.com> epstein@world.std.com (Richard W Epstein) writes:
>In article <25731@well.sf.ca.us> gatynen@well.sf.ca.us (Gerard Tynen) writes:
>>We couldn't think of any reason to force a close on all cursors just
>>because you do a commit.  I think that what happened is that early 
>[deleted...]
>
>A commit by definition makes any updates,inserts, and deleted permanent
>and releases locks. After this happens, where would your cursor point?
>Since you released your locks, you cannot be sure the records or
>even the table is still there or that you have access to them.

I believe that Sybase uses a different defintion of "commit".  You can
open a number of connections and if you commit on one connection, the
other connections are not affected; their changes are still outstanding.
Each connection is actually considered a separate user.

So we were able to use one connection for reading, and another for DML.
We didn't close the cursor, although if an update or insert affected 
the result set, the cursor would have to be reopened to see those changes.
Also we didn't lock what we were reading, so yes, I agree with you that
drastic changes could take place in the database and the cursor would not
know anything about it. 

--GT

gatynen@well.sf.ca.us (Gerard Tynen) (06/30/91)

>>DBMS I've ever seen closes all open cursors [SQL/DS, DB2, Ingres ...] when
>>a commit is issued.
>>
>
>Not Informix.  In our 4.0 product you can declare a cursor with hold
>and it won't be closed during a commit.

Also IBM Database Manager can declare cursors "with hold", 
and their positioning is maintained across commits...


-GT

dlm@hermes.dlogics.com (06/30/91)

I still think this thread is missing the point. 

The original question was: "how do i update while i am in a fetch loop; if i 
update and commit, the cursor closes!".  So everyone gets into a discussion
of when cursors close or don't close.

The answer is: "There is no need to commit until you are done with the fetch".
That way, the problem with unexpectedly closed cursors becomes moot, and the
guy can fix his code and get on with his life.

I am pretty sure the folks at oracle will agree with me on this. ;-)
-- 
Dave Mausner, Sr Tech Consultant / Datalogics Inc / Chicago IL / 312-266-4450
                        dlm@hermes.dlogics.com
            "Don't talk about it -- just show me the code!"