Re: Is the PL/pgSQL refcursor useful in a modern three-tier app? - Mailing list pgsql-general

From Bryn Llewellyn
Subject Re: Is the PL/pgSQL refcursor useful in a modern three-tier app?
Date
Msg-id 8659357C-1DC6-4EB7-9451-3ECB04E6E67D@yugabyte.com
Whole thread Raw
In response to Re: Is the PL/pgSQL refcursor useful in a modern three-tier app?  (Adrian Klaver <adrian.klaver@aklaver.com>)
Responses Re: Is the PL/pgSQL refcursor useful in a modern three-tier app?  (Adrian Klaver <adrian.klaver@aklaver.com>)
Re: Is the PL/pgSQL refcursor useful in a modern three-tier app?  (Laurenz Albe <laurenz.albe@cybertec.at>)
List pgsql-general
> adrian.klaver@aklaver.com wrote:
>
>> bryn@yugabyte.com wrote:
>>
>>> adrian.klaver@aklaver.com wrote:
>>>
>>> I have a hard time fathoming why someone who writes documentation does not actually read documentation.
>>
>>
>> Ouch. In fact, I had read the whole of the "43.7. Cursors" section in the "PL/pgSQL" chapter. And the sections in
the"SQL Commands" chapter for "declare", "fetch" and "close". But several of the key concepts didn't sink in and this
preventedme not only from understanding what some of the examples showed but, worse, from being able to use the right
vocabularyto express what confused me. 
>
> Given this from your original question:
>
> « Anyway, without anything like Oracle PL/SQL's packages, you have no mechanism to hold the opened cursor variable
betweensuccessive server calls. » 
>
> What part of this [a particular code example] did not make sense in that context?

First off, despite the fact that I've clearly annoyed you (for which I apologize), I have found these exchanges very
helpful.So thank you very much. 

Your questions can be summarized as "Why couldn't you understand the doc? And why did you mix questions about use-cases
withquestions about the mechanics?" The answer has to do with psychology. I probably can't explain this convincingly.
That'swhy it's taken me a long time to respond. I also had to do lots of testing before responding to make sure that
themental model that I've formed for myself is consistent with these. 

You may not be interested in what follows. But, anyway, here goes.

— I happen to have many years of experience with Oracle Database and PL/SQL. The latter has analogous features to
PL/pgSQL's"refcursor". But the differences between the notions in the two environments are enormous. My attempt to
understandthe latter was hindered by my understanding of the former. I accept that this is *my* problem and that I
couldnever expect that the PG doc would cater for such a reader. 

— Oracle Database doesn't expose scrollability for PL/SQL's equivalent of "refcursor". So I had never come across use
caseswhere this was beneficial. I wanted, therefore, to hear about some. I thought that insights here would help me
understandthe mechanics. But I didn't get anything beyond "Scrollability is what it is. If you don't need it, don't use
it."

Anyway, never mind all that now. Here's what I now (think that) I understand—with some comments on what made it hard
forme to grasp. 

— The key notion is what is referred to sometimes as "portal" and sometimes as "cursor". This is the thing that's
globalwithin, and private to, a session, that's uniquely identified by a bare name, that, and that's listed in
"pg_cursors".I believe that in typical use, a cursor has only transaction duration. But (and only when you use the SQL
API)you can create a cursor with (up to) session duration 

— The doc pages for the "declare", "fetch", and "close" SQL statements don't mention "portal" and use only "cursor".
Theyuse the term to mean the underlying phenomenon and use wording like: "DECLARE allows a user to create cursors";
"Youcan see all available cursors by querying the pg_cursors system view"; "FETCH retrieves rows using a
previously-createdcursor"; "CLOSE frees the resources associated with an open cursor. After the cursor is closed, no
subsequentoperations are allowed on it. A cursor should be closed when it is no longer needed." However, these pages
leavethe term "open" undefined, though it's used. It seems that it has no meaning. Rather, a cursor with a particular
nameeither exists or not. You create it with "declare" and drop it with "close". And that's it. If "open" means
anything,it's just another word for "exists". (The fact that "pg_cursors" doesn't have a boolean column called "open"
supportsthis understanding.) The sentence "After the cursor is closed, no subsequent operations are allowed on it." is
equivalentto "After a table is dropped, no subsequent operations are allowed on it." But who would bother to say that?
Noticethat "pg_cursors" has a column called "creation_time" — and not "declaration time". 

— On the other hand, the doc page "43.7. Cursors" uses "portal" a lot—and never says that it means exactly the same as
"cursor"qua term of art (and not qua keyword). It does say "...a so-called portal containing the active query for the
cursor."This suggests a subtle difference in meaning between "portal" and "cursor" and a notion of containment. I can't
makeany sense of that. It says things like "Before a cursor can be used to retrieve rows, it must be opened. (This is
theequivalent action to the SQL command DECLARE CURSOR.)" This is the closest that it comes to saying that the SQL API
andthe PL/pgSQL API both manipulate the same thing—what you see in "pg_cursors". The sentence that I quoted is
equivalentto saying "Before you can insert a row into a table, the table has to exist." In other words, an unhelpful
tautology.I believe that the sense is this: « A variable with the data type "refcursor" holds a bare name (which is
governedby the usual rules for a SQL name). The name might be found in "pg_cursors" or it might not be. When, and only
when,it is found in "pg_cursors", the refcursor variable acts as a handle to the denoted cursor and supports operations
uponit using various PL/pgSQL statements that use the identifier for the refcursor variable's name. 

— I (but maybe only I) would have appreciated being able to read a single generic account that explained the underlying
concepts.This would have prepared me for understanding the operations that the SQL and PL/pgSQL APIs expose—and
especiallythat they are interoperable. So I'd've liked to see a note at the start of the four relevant sections ("43.7.
Cursors"and the "declare", "fetch", and "close" SQL statements) that x-ref'd to the generic account and said "read this
first".

Here's some more detail of how I'd state the mental model that I've deduced. Please tell me if you think that some, or
all,of my account is wrong. (When I say "cursor", I always mean what's listed in "pg_cursors". And I'll never mention
"portal"because the term seems to means exactly the same as "cursor".) 

(1) A cursor must have a defining "select" statement. It also always has a pointer to the next-to-be-fetched row in the
resultset that the "select" defines. The rows are (implicitly) numbered from 1 through N where N is the number of rows
thatthe cursor's "select" defines. However (as you can see from "fetch :x" in SQL, where :x is less than 1 or more than
N)the pointer can point outside of the result set and not cause an error. 

(2) A cursor defines a read-consistent snapshot, as of its "pg_cursors.creation_time". (From the "declare" doc, « In
PostgreSQL,all cursors are insensitive. ») The complete set of rows that the "select" defines may not all be
concurrentlymaterialized in the cursor. This implies some kind of aging out and replacement implementation. The details
aren'tdescribed because they have no semantic significance. 

(3) In top-level SQL, you create a cursor with the "declare" statement. This lets you name it, specify its “select”,
andspecify a few other boolean attributes like "[ no ] scroll" and "{ with | without } hold". 

(4) In PL/pgSQL, you create a cursor with "open". The operand is the identifier for the refcursor variable that holds
thecursor's name. You can test your mental model by using the equivalent SQL statements with the "execute" PL/SQL
statement.

(5) In top-level SQL, you drop a cursor with "close" where the operand is the identifier for the cursor's name. In
PL/pgSQL,you drop a cursor with "close" where the operand is the identifier for the refcursor variable that holds the
cursor'sname. 

(6) In PL/pgSQL, the value of a refcursor variable is an ordinary "text" value. It might be null. It you assign the
nameof a cursor that's listed in "pg_cursors" to a refcursor variable, then you can fetch from it or close it. And as
longas the name isn't currently found in "pg_cursors", you can create a new row with that name with the "open"
statement,specifying any "select" that you want. 

(7) I found the terms "bound cursor" and "unbound cursor" (as in the section  "43.7.2.3. Opening A Bound Cursor")
initiallyvery confusing because the wording connotes a property of a cursor—and "pg_cursors" has no column for such a
notion.But I presently came to understand that this was a careless shorthand for "[un]bound cursor variable" — which
phrasesare also used on the same page. 

(8) I found it initially hard to understand that the "bound" property of a refcursor variable is not part of its value.
(Andnor, for that matter, is the SQL statement that you specify with the "open" statement.) I reasoned, eventually,
thatthe "bound" property must be an annotation of the variable in the AST for the block statement where the variable is
declared.(Here, "declare" is used in the PL/pgSQL sense, and not the SQL sense, of the term). This explains why, when a
functionreturns a refcursor value where the variable was declared as "bound", it can only be seen as "unbound" in a
subprogramthat has a refcursor formal argument. The same reasoning applies if you assign a bound refcursor variable to
anunbound refcursor variable. (But I can't see that you'd have a reason to do that unless, like I was, you were testing
yourmental model.) 

It's the fact that the value that a refcursor variable holds is nothing other than the text of a (potential) cursor's
name(and that the SQL text and "bound" status are represented elsewhere) that lead me to write « without anything like
OraclePL/SQL's packages, you have no mechanism to hold the opened cursor variable between successive server calls ».
WhenI wrote that, I thought, wrongly as I now see, that a refcursor variable held a composite, opaque value (or an
opaquepointer to such) like it does in Oracle. 

(9) The upshot of #8 is that the "FOR recordvar IN bound_cursorvar" construct can be used only in the block statement
thatdeclares the bound cursor variable. And this seems to defeat the point. You may just as well use an ordinary "for"
loopthat has the SQL statement right after the "in" keyword. 

(10) I discovered that this construct:

for ... in select ... from pg_cursors order by name loop
  ...
end loop;

sees a cursor with an automatically generated name like "<unnamed portal N>" for the loop itself. I suppose that this
makesgood sense. But it does seem to undermine the value of declaring and using a bound cursor variable—esp as the
nominalvalue of the "cursor" concept is the scrollability and the ability to fetch a smallish set of rows from anywhere
ina huge result set. 




pgsql-general by date:

Previous
From: Ron Johnson
Date:
Subject: Re: Schema/user/role
Next
From: Adrian Klaver
Date:
Subject: Re: Is the PL/pgSQL refcursor useful in a modern three-tier app?