Forum OpenACS Development: Cursors

Collapse
Posted by Gilbert Wong on

I encountered some pl/sql code which makes use of cursors. One of the functions uses the cursors to loop through the result set and to perform different actions based on some logic. However, there are several functions which use cursors to perform a simple select. They will open the cursor as a select, then in the body of the function it has something like this:

OPEN this_cursor;
FETCH this_cursor INTO this_variable;
CLOSE this_cursor;

OPEN that_cursor;
FETCH that_cursor INTO that_variable;
CLOSE that_cursor;

return this_variable + that_variable;

Would there be any reason to use a cursor instead of select statement? If not, would it make sense to remove the cursors and make the function call a select statement(s) instead? Thanks.

Collapse
2: Response to Cursors (response to 1)
Posted by Dan Wickstrom on
Usually when you see a single fetch from a cursor, they're worried about the the NOTFOUND case.  So after the fetch, you might see some action based on the notfound case.  I guess you can't do this in oracle without using a cursor.  For postgresql, you can replace this with select into followed by a if NOT FOUND then ... statement to get equivalent functionality.  If not found is not the issue here, you can also just convert it to a simple select.
Collapse
3: Response to Cursors (response to 1)
Posted by Gilbert Wong on
Would the NOTFOUND case be handled within the same function?  If so, these functions do not test for the NOTFOUND case.  So it looks like I can convert it to a simple select statement.  I'll leave the Oracle code as is and make the changes in the PostgreSQL code where applicable.
Collapse
4: Response to Cursors (response to 1)
Posted by Don Baccus on
I've always changed to the simple select case (or the FOR loop over the result set when the cursor is assigned more than one row) without problem.

The Oracle docs indicate that the NOT FOUND case *should* work just fine with simple selects, BTW, but in practice I found that not to be true and digging in ACS code a year or so ago found that they'd run into the same sort of problem, apparently (you can catch the exception  raised when no row's returned but the NOT FOUND example in the Oracle 8i doc doesn't work).

As Dan says, this isn't a problem in PG.  And as Gilbert's noted, the ACS use of cursors often doesn't check for the NOT FOUND case anyway.

Collapse
5: Response to Cursors (response to 1)
Posted by Tom Jackson on

"My rule of thumb is always to use an explicit cursor for all SELECT statements in my applications, even if an implicit cursor might run a little faster...

"By setting and following this clear-cut rule, I give myself one less thing to think about. I do not have to determine if a particular SELECT statement will return only one row...I do not have to wonder about the conditions under which a single-row query might suddenly return more than one row, thus requiring a TOO_MANY_ROWS exception handler. I am guaranteed to get vastly improved programmatic control over the data access and more finely-tuned exception handling for the cursor."

From page 166 of O'Reilly's _Oracle PL/SQL Programming_

Maybe the ACS team read the same, and put it into practice.

Collapse
6: Response to Cursors (response to 1)
Posted by Dan Wickstrom on
Well for one thing, postgresql doesn't yet support cursors, and even if it did, I wouldn't blindly use a cursor, just to save myself the trouble of thinking about whether a query would return more than one row.  Ususally, when you intend to get only one row back from a query, it's pretty easy to guarantee.  Cursors are kind of clunky to use, so in general, I wouldn't use one unless there were no other way to get the result that I wanted.
Collapse
7: Response to Cursors (response to 1)
Posted by Don Baccus on
I find the guy's comment rather amazing, frankly.  If your query returns more than one row and you've written the PL/SQL proc with the assumption that only one row will be returned, the use of a cursor will certainly avoid the exception.

But ... in doing so it will be masking what is very likely a buggy query!  This is not good!  The query writer in this case has either miswritten the query or didn't understand the datamodel or the data contained within it.

PG does support cursors, BTW - it is PL/pgSQL which doesn't.  The current, eventually-to-be 7.2, version of PL/pgSQL actually does but the version of PL/pgSQL contained in PG 7.1 and previous versions do not.

Collapse
8: Response to Cursors (response to 1)
Posted by Gilbert Wong on
As I was looking at the OpenACS 3.2.5 code for the ecommerce package, I noticed that case statements were used to replace Oracle's NVL.  For instance:

case when sum(price_charged) is null then 0::numeric else sum(price_charged) end

Is that the same as:

coalesce(sum(price_charged),0::numeric)

By the way, the Oracle code used cursors.  Thanks.

Collapse
9: Response to Cursors (response to 1)
Posted by Tom Jackson on

Seems like it could mask all sorts of errors. The query could be correct, but the data model could have been setup incorrectly allowing duplicate data to get into the system. Or a uniqueness constraint could be dropped by accident and go undiscovered for a very long time. This is similar to assuming a catch placed around your code is going to help you find a bug.

My purpose in posting the above quote was in hopes that someone with real world experience would explain what was wrong with this reasoning. Thanks Don!

Collapse
10: Response to Cursors (response to 1)
Posted by Don Baccus on
Gilbert: the coalesce() looks fine.  My guess is that whoever ported that old code didn't realize that coalesce() existed so rewrote it as a case (none of us had significant PG experience when we did the original port).
Collapse
11: Response to Cursors (response to 1)
Posted by Gilbert Wong on
Thanks Don.