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: