On Sun, 2006-01-15 at 17:57 -0500, Neil Conway wrote: > I think the view should include the portals created by DECLARE CURSOR > and "Bind" protocol messages, but should not include the unnamed portal > or any other portals that are created internally as part of the > implementation of other commands (e.g. EXECUTE). I'm not sure how to > handle SPI: developers using SPI would expect to find their portals in > the view, but those using SPI indirectly (e.g. via PL/foo) would > probably find the clutter surprising. I'd say we need to include SPI > portals in the view as well.
Attached is a revised version of Joachim's patch that implements this. Cursors created via SPI are part of the view, which produces somewhat unexpected results when querying the view from a procedural language as noted above, but I suppose it's the best compromise. The documentation still needs some work. Barring any objections, I'll fix that and a few other minor issues and then apply the patch tomorrow. -Neil
============================================================ *** doc/src/sgml/catalogs.sgml 4eec167450469237ea89094c7bc90511b4d0ebdf --- doc/src/sgml/catalogs.sgml 5d0f61713e050a48b4f6217d4e20db444ffb20b1 *************** *** 4360,4365 **** --- 4360,4370 ---- <tbody> <row> + <entry><link linkend="view-pg-cursors"><structname>pg_cursors</structname></link></entry> + <entry>open cursors</entry> + </row> + + <row> <entry><link linkend="view-pg-group"><structname>pg_group</structname></link></entry> <entry>groups of database users</entry> </row> *************** *** 4429,4434 **** --- 4434,4533 ---- </table> </sect1> + <sect1 id="view-pg-cursors"> + <title><structname>pg_cursors</structname></title> + + <indexterm zone="view-pg-cursors"> + <primary>pg_cursors</primary> + </indexterm> + + <para> + The view <structname>pg_cursors</structname> lists all declared cursors for + the current session and transaction. Note that cursors that have been + declared <literal>WITHOUT HOLD</literal> are only valid within the current + transaction whereas <literal>WITH HOLD</literal> cursors can exist during + the whole session. Cursors can be created using <xref linkend="sql-declare" + endterm="sql-declare-title"> and removed with <xref linkend="sql-close" + endterm="sql-close-title">. + </para> + + <table> + <title><structname>pg_cursors</> Columns</title> + + <tgroup cols=4> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>References</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>name</structfield></entry> + <entry><type>text</type></entry> + <entry></entry> + <entry>The name of the cursor</entry> + </row> + + <row> + <entry><structfield>statement</structfield></entry> + <entry><type>text</type></entry> + <entry></entry> + <entry>The verbatim query string submitted to declare this cursor</entry> + </row> + + <row> + <entry><structfield>is_holdable</structfield></entry> + <entry><type>boolean</type></entry> + <entry></entry> + <entry> + <literal>true</literal> if the cursor is holdable (that is, it + can be accessed after the transaction that declared the cursor + has committed); <literal>false</literal> otherwise + </entry> + </row> + + <row> + <entry><structfield>is_binary</structfield></entry> + <entry><type>boolean</type></entry> + <entry></entry> + <entry> + <literal>true</literal> if the cursor was declared + <literal>BINARY</literal>; <literal>false</literal> + otherwise <!-- XXX: discuss fe/be protocol --> + </entry> + </row> + + <row> + <entry><structfield>is_scrollable</structfield></entry> + <entry><type>boolean</type></entry> + <entry></entry> + <entry> + <literal>true</> if the cursor is scrollable (that is, it + allows rows to be retrieved in a nonsequential manner); + <literal>false</literal> otherwise + </entry> + </row> + + <row> + <entry><structfield>creation_time</structfield></entry> + <entry><type>timestamptz</type></entry> + <entry></entry> + <entry>The time at which the cursor was declared</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + The <structname>pg_cursors</structname> view is read only. + </para> + + </sect1> + <sect1 id="view-pg-group"> <title><structname>pg_group</structname></title> ============================================================ *** doc/src/sgml/ref/close.sgml 8892a94e8d7e93d729efc7f45f5f65fa47e26854 --- doc/src/sgml/ref/close.sgml e430bd034f8fd5b8a97f81063f2f9f5a7edf3891 *************** *** 76,81 **** --- 76,86 ---- <xref linkend="sql-declare" endterm="sql-declare-title"> statement to declare a cursor. </para> + + <para> + You can see all available cursors by querying the + <structname>pg_cursors</structname> system view. + </para> </refsect1> <refsect1> ============================================================ *** doc/src/sgml/ref/declare.sgml 6875bafecc3a25bb5d9c8117b430afb6d0cd2474 --- doc/src/sgml/ref/declare.sgml 4566aed3538b627eb804e67913c75a8d4aebf100 *************** *** 253,258 **** --- 253,263 ---- the standard SQL cursor conventions, including those involving <command>DECLARE</command> and <command>OPEN</command> statements. </para> + + <para> + You can see all available cursors by querying the + <structname>pg_cursors</structname> system view. + </para> </refsect1> <refsect1> ============================================================ *** src/backend/catalog/system_views.sql 103ab494a8ed13f44f6c8ba5d00028c70a2a7abb --- src/backend/catalog/system_views.sql 56327ceb9618cb9c211f4c85162721957202f949 *************** *** 148,153 **** --- 148,160 ---- transactionid xid, classid oid, objid oid, objsubid int2, transaction xid, pid int4, mode text, granted boolean); + CREATE VIEW pg_cursors AS + SELECT C.name, C.statement, C.is_holdable, C.is_binary, + C.is_scrollable, C.creation_time + FROM pg_cursor() AS C + (name text, statement text, is_holdable boolean, is_binary boolean, + is_scrollable boolean, creation_time timestamptz); + CREATE VIEW pg_prepared_xacts AS SELECT P.transaction, P.gid, P.prepared, U.rolname AS owner, D.datname AS database ============================================================ *** src/backend/commands/portalcmds.c ba8e42896ec9179f64b4b1841199ca5b087456a9 --- src/backend/commands/portalcmds.c db3146dd3f642a6882d0bfdcb90be5ead6270dd4 *************** *** 28,33 **** --- 28,34 ---- #include "optimizer/planner.h" #include "rewrite/rewriteHandler.h" #include "tcop/pquery.h" + #include "tcop/tcopprot.h" #include "utils/memutils.h" *************** *** 106,112 **** plan = copyObject(plan); PortalDefineQuery(portal, ! NULL, /* unfortunately don't have sourceText */ "SELECT", /* cursor's query is always a SELECT */ list_make1(query), list_make1(plan), --- 107,113 ---- plan = copyObject(plan); PortalDefineQuery(portal, ! pstrdup(debug_query_string), "SELECT", /* cursor's query is always a SELECT */ list_make1(query), list_make1(plan), ============================================================ *** src/backend/commands/prepare.c de4ce2755737e05dd3c763b759c7f0234ddceb22 --- src/backend/commands/prepare.c f8504f84b1188c8277944f7a083505bba7f1e7c7 *************** *** 162,172 **** paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list); } ! /* ! * Create a new portal to run the query in ! */ portal = CreateNewPortal(); ! /* * For CREATE TABLE / AS EXECUTE, make a copy of the stored query so that * we can modify its destination (yech, but this has always been ugly). --- 162,172 ---- paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list); } ! /* Create a new portal to run the query in */ portal = CreateNewPortal(); ! /* Don't display the portal in pg_cursors, it is for internal use only */ ! portal->visible = false; ! /* * For CREATE TABLE / AS EXECUTE, make a copy of the stored query so that * we can modify its destination (yech, but this has always been ugly). ============================================================ *** src/backend/tcop/postgres.c c1b95dafdc4eca86cadc2d1fcc1ca9e56be29a6f --- src/backend/tcop/postgres.c 46535289caf3ff630d2d41c207a91059aee38bae *************** *** 956,961 **** --- 956,963 ---- * already is one, silently drop it. */ portal = CreatePortal("", true, true); + /* Don't display the portal in pg_cursors */ + portal->visible = false; PortalDefineQuery(portal, query_string, ============================================================ *** src/backend/utils/mmgr/portalmem.c 8591a97deb86749a2a0b98fc469288d474010003 --- src/backend/utils/mmgr/portalmem.c 7a160e7ba26930007ae971565179b08684d1a954 *************** *** 18,26 **** */ #include "postgres.h" ! #include "miscadmin.h" #include "commands/portalcmds.h" #include "executor/executor.h" #include "utils/hsearch.h" #include "utils/memutils.h" #include "utils/portal.h" --- 18,30 ---- */ #include "postgres.h" ! #include "access/heapam.h" ! #include "catalog/pg_type.h" #include "commands/portalcmds.h" #include "executor/executor.h" + #include "funcapi.h" + #include "miscadmin.h" + #include "utils/builtins.h" #include "utils/hsearch.h" #include "utils/memutils.h" #include "utils/portal.h" *************** *** 190,201 **** --- 194,208 ---- "Portal"); /* initialize portal fields that don't start off zero */ + portal->status = PORTAL_NEW; portal->cleanup = PortalCleanup; portal->createSubid = GetCurrentSubTransactionId(); portal->strategy = PORTAL_MULTI_QUERY; portal->cursorOptions = CURSOR_OPT_NO_SCROLL; portal->atStart = true; portal->atEnd = true; /* disallow fetches until query is set */ + portal->visible = true; + portal->creation_time = GetCurrentTimestamp(); /* put portal in table (sets portal->name) */ PortalHashTableInsert(portal, name); *************** *** 756,758 **** --- 763,868 ---- PortalDrop(portal, false); } } + + /* Find all available cursors */ + Datum + pg_cursor(PG_FUNCTION_ARGS) + { + FuncCallContext *funcctx; + HASH_SEQ_STATUS *hash_seq; + PortalHashEnt *hentry; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + TupleDesc tupdesc; + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* + * switch to memory context appropriate for multiple function + * calls + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + if (PortalHashTable) + { + hash_seq = (HASH_SEQ_STATUS *) palloc(sizeof(HASH_SEQ_STATUS)); + hash_seq_init(hash_seq, PortalHashTable); + funcctx->user_fctx = (void *) hash_seq; + } + else + funcctx->user_fctx = NULL; + + /* + * build tupdesc for result tuples. This must match the + * definition of the pg_cursors view in system_views.sql + */ + tupdesc = CreateTemplateTupleDesc(6, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "is_holdable", + BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_binary", + BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_scrollable", + BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "creation_time", + TIMESTAMPTZOID, -1, 0); + + funcctx->tuple_desc = BlessTupleDesc(tupdesc); + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + hash_seq = (HASH_SEQ_STATUS *) funcctx->user_fctx; + + /* if the hash table is uninitialized, we're done */ + if (hash_seq == NULL) + SRF_RETURN_DONE(funcctx); + + /* loop until we find a visible portal or hit the end of the list */ + while ((hentry = hash_seq_search(hash_seq)) != NULL) + { + if (hentry->portal->visible) + break; + } + + if (hentry) + { + Portal portal; + Datum result; + HeapTuple tuple; + Datum values[6]; + bool nulls[6]; + + portal = hentry->portal; + MemSet(nulls, 0, sizeof(nulls)); + + values[0] = DirectFunctionCall1(textin, CStringGetDatum(portal->name)); + /* XXX can this happen? */ + if (!portal->sourceText) + nulls[1] = true; + else + values[1] = DirectFunctionCall1(textin, + CStringGetDatum(portal->sourceText)); + values[2] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_HOLD); + values[3] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_BINARY); + /* that should be sufficient, since if the cursor is not scrollable, + * the CURSOR_OPT_SCROLL bit won't be set */ + values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL); + values[5] = TimestampTzGetDatum(portal->creation_time); + + tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); + result = HeapTupleGetDatum(tuple); + SRF_RETURN_NEXT(funcctx, result); + } + + SRF_RETURN_DONE(funcctx); + } + ============================================================ *** src/include/catalog/pg_proc.h 3986a9b84ebba188349a63e6f0b7d62ea70c74e1 --- src/include/catalog/pg_proc.h 7e4d05e3247ae6c2ec5b2a904abd101b194e15cb *************** *** 3621,3626 **** --- 3621,3628 ---- DESCR("deparse an encoded expression with pretty-print option"); DATA(insert OID = 2510 ( pg_prepared_statement PGNSP PGUID 12 f f t t s 0 2249 "" _null_ _null_ _null_ pg_prepared_statement - _null_ )); DESCR("get the prepared statements for this session"); + DATA(insert OID = 2511 ( pg_cursor PGNSP PGUID 12 f f t t s 0 2249 "" _null_ _null_ _null_ pg_cursor - _null_ )); + DESCR("get the open cursors for this session"); /* non-persistent series generator */ DATA(insert OID = 1066 ( generate_series PGNSP PGUID 12 f f t t v 3 23 "23 23 23" _null_ _null_ _null_ generate_series_step_int4 - _null_ )); ============================================================ *** src/include/utils/builtins.h c696e31dc8dee5eb01f4dca58febefc9836bffc1 --- src/include/utils/builtins.h f8881733a8275dae08876c154b51701e58d61fed *************** *** 865,868 **** --- 865,871 ---- /* commands/prepare.c */ extern Datum pg_prepared_statement(PG_FUNCTION_ARGS); + /* utils/mmgr/portalmem.c */ + extern Datum pg_cursor(PG_FUNCTION_ARGS); + #endif /* BUILTINS_H */ ============================================================ *** src/include/utils/portal.h 9448c7e015885a27c6d82cac4a07574c81b5c6d4 --- src/include/utils/portal.h d81d4d9d0013ce25a1bc7b25ed2d19654361d1fb *************** *** 72,78 **** * PORTAL_MULTI_QUERY: all other cases. Here, we do not support partial * execution: the portal's queries will be run to completion on first call. */ - typedef enum PortalStrategy { PORTAL_ONE_SELECT, --- 72,77 ---- *************** *** 166,171 **** --- 165,174 ---- bool atEnd; bool posOverflow; long portalPos; + + /* Presentation data, primarily used by the pg_cursors system view */ + TimestampTz creation_time; /* time at which this portal was defined */ + bool visible; /* include this portal in pg_cursors? */ } PortalData; /* ============================================================ *** src/test/regress/expected/portals.out 568d43dd96ea5d7e041ac763261553cce806327e --- src/test/regress/expected/portals.out 57839151bce53b43bce63a269c1fd21338c5f03e *************** *** 676,682 **** --- 676,705 ---- CLOSE foo11; CLOSE foo12; -- leave some cursors open, to test that auto-close works. + -- record this in the system view as well (don't query the time field there + -- however) + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + name | statement | is_holdable | is_binary | is_scrollable + -------+------------------------------------------------------+-------------+-----------+--------------- + foo13 | DECLARE foo13 SCROLL CURSOR FOR SELECT * FROM tenk1; | f | f | t + foo15 | DECLARE foo15 SCROLL CURSOR FOR SELECT * FROM tenk1; | f | f | t + foo19 | DECLARE foo19 SCROLL CURSOR FOR SELECT * FROM tenk1; | f | f | t + foo17 | DECLARE foo17 SCROLL CURSOR FOR SELECT * FROM tenk1; | f | f | t + foo14 | DECLARE foo14 SCROLL CURSOR FOR SELECT * FROM tenk2; | f | f | t + foo21 | DECLARE foo21 SCROLL CURSOR FOR SELECT * FROM tenk1; | f | f | t + foo23 | DECLARE foo23 SCROLL CURSOR FOR SELECT * FROM tenk1; | f | f | t + foo18 | DECLARE foo18 SCROLL CURSOR FOR SELECT * FROM tenk2; | f | f | t + foo20 | DECLARE foo20 SCROLL CURSOR FOR SELECT * FROM tenk2; | f | f | t + foo22 | DECLARE foo22 SCROLL CURSOR FOR SELECT * FROM tenk2; | f | f | t + foo16 | DECLARE foo16 SCROLL CURSOR FOR SELECT * FROM tenk2; | f | f | t + (11 rows) + END; + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + name | statement | is_holdable | is_binary | is_scrollable + ------+-----------+-------------+-----------+--------------- + (0 rows) + -- -- NO SCROLL disallows backward fetching -- *************** *** 695,700 **** --- 718,728 ---- -- -- Cursors outside transaction blocks -- + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + name | statement | is_holdable | is_binary | is_scrollable + ------+-----------+-------------+-----------+--------------- + (0 rows) + BEGIN; DECLARE foo25 SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2; FETCH FROM foo25; *************** *** 728,733 **** --- 756,767 ---- 2968 | 9999 | 0 | 0 | 8 | 8 | 68 | 968 | 968 | 2968 | 2968 | 136 | 137 | EKAAAA | PUOAAA | VVVVxx (1 row) + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + name | statement | is_holdable | is_binary | is_scrollable + -------+----------------------------------------------------------------+-------------+-----------+--------------- + foo25 | DECLARE foo25 SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2; | t | f | t + (1 row) + CLOSE foo25; -- -- ROLLBACK should close holdable cursors *************** *** 808,810 **** --- 842,871 ---- drop function count_tt1_v(); drop function count_tt1_s(); + -- Create a cursor with the BINARY option and check the pg_cursors view + BEGIN; + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + name | statement | is_holdable | is_binary | is_scrollable + ------+----------------------------------------------------------------------+-------------+-----------+--------------- + c2 | declare c2 cursor with hold for select count_tt1_v(), count_tt1_s(); | t | f | f + (1 row) + + DECLARE bc BINARY CURSOR FOR SELECT * FROM tenk1; + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + name | statement | is_holdable | is_binary | is_scrollable + ------+----------------------------------------------------------------------+-------------+-----------+--------------- + c2 | declare c2 cursor with hold for select count_tt1_v(), count_tt1_s(); | t | f | f + bc | DECLARE bc BINARY CURSOR FOR SELECT * FROM tenk1; | f | t | t + (2 rows) + + ROLLBACK; + -- We should not see the portal that is created internally to + -- implement EXECUTE in pg_cursors + PREPARE cprep AS + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + EXECUTE cprep; + name | statement | is_holdable | is_binary | is_scrollable + ------+----------------------------------------------------------------------+-------------+-----------+--------------- + c2 | declare c2 cursor with hold for select count_tt1_v(), count_tt1_s(); | t | f | f + (1 row) + ============================================================ *** src/test/regress/expected/rules.out b34e70eeb88ed7655fb82d371c119c6249cf473c --- src/test/regress/expected/rules.out a03bc3b5ce39ec7afbd45d65e27d2c2ef69a26a4 *************** *** 1277,1282 **** --- 1277,1283 ---- viewname | definition --------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); + pg_cursors | SELECT c.name, c."statement", c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name text, "statement" text, is_holdable boolean, is_binary boolean, is_scrollable boolean, creation_time timestamp with time zone); pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin); pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS "tablespace", pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")); pg_locks | SELECT l.locktype, l."database", l.relation, l.page, l.tuple, l.transactionid, l.classid, l.objid, l.objsubid, l."transaction", l.pid, l."mode", l."granted" FROM pg_lock_status() l(locktype text, "database" oid, relation oid, page integer, tuple smallint, transactionid xid, classid oid, objid oid, objsubid smallint, "transaction" xid, pid integer, "mode" text, "granted" boolean); *************** *** 1321,1327 **** shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); toyemp | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp; ! (45 rows) SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename; --- 1322,1328 ---- shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); toyemp | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp; ! (46 rows) SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename; ============================================================ *** src/test/regress/sql/portals.sql c5f9f0c2dedbec4d51afa62f781221e80460b613 --- src/test/regress/sql/portals.sql f993e71778eed98dce1e56d901b1a58597479e91 *************** *** 168,175 **** --- 168,181 ---- -- leave some cursors open, to test that auto-close works. + -- record this in the system view as well (don't query the time field there + -- however) + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + END; + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + -- -- NO SCROLL disallows backward fetching -- *************** *** 188,193 **** --- 194,202 ---- -- Cursors outside transaction blocks -- + + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + BEGIN; DECLARE foo25 SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2; *************** *** 204,209 **** --- 213,220 ---- FETCH ABSOLUTE -1 FROM foo25; + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + CLOSE foo25; -- *************** *** 278,280 **** --- 289,305 ---- drop function count_tt1_v(); drop function count_tt1_s(); + + + -- Create a cursor with the BINARY option and check the pg_cursors view + BEGIN; + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + DECLARE bc BINARY CURSOR FOR SELECT * FROM tenk1; + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + ROLLBACK; + + -- We should not see the portal that is created internally to + -- implement EXECUTE in pg_cursors + PREPARE cprep AS + SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; + EXECUTE cprep;
---------------------------(end of broadcast)--------------------------- TIP 5: don't forget to increase your free space map settings