Add a new system view, pg_cursors, that displays the currently available
authorNeil Conway
Wed, 18 Jan 2006 06:49:30 +0000 (06:49 +0000)
committerNeil Conway
Wed, 18 Jan 2006 06:49:30 +0000 (06:49 +0000)
cursors. Patch from Joachim Wieland, review and ediorialization by Neil
Conway. The view lists cursors defined by DECLARE CURSOR, using SPI, or
via the Bind message of the frontend/backend protocol. This means the
view does not list the unnamed portal or the portal created to implement
EXECUTE. Because we do list SPI portals, there might be more rows in
this view than you might expect if you are using SPI implicitly (e.g.
via a procedural language).

Per recent discussion on -hackers, the query string included in the
view for cursors defined by DECLARE CURSOR is based on
debug_query_string. That means it is not accurate if multiple queries
separated by semicolons are submitted as one query string. However,
there doesn't seem a trivial fix for that: debug_query_string
is better than nothing. I also changed SPI_cursor_open() to include
the source text for the portal it creates: AFAICS there is no reason
not to do this.

Update the documentation and regression tests, bump the catversion.

17 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/protocol.sgml
doc/src/sgml/ref/close.sgml
doc/src/sgml/ref/declare.sgml
src/backend/catalog/system_views.sql
src/backend/commands/portalcmds.c
src/backend/commands/prepare.c
src/backend/executor/spi.c
src/backend/tcop/postgres.c
src/backend/utils/mmgr/portalmem.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/include/utils/portal.h
src/test/regress/expected/portals.out
src/test/regress/expected/rules.out
src/test/regress/sql/portals.sql

index c346005bd96c77842042c41bb6826b0c8d2f5a12..aee68c2f89364840f8c5f2eb5dfa83f4a6f0a011 100644 (file)
@@ -1,6 +1,6 @@
 
 
 
     
 
     
+     
+      pg_cursors
+      open cursors
+     
+
      
       pg_group
       groups of database users
   
  
 
+  <structname>pg_cursors</structname>
+
+  
+   pg_cursors
+  
+
+  
+   The pg_cursors view lists the cursors that
+   are currently available. Cursors can be defined in several ways:
+   
+    
+     
+      via the 
+      statement in SQL
+     
+    
+
+    
+     
+      via the Bind message in the frontend/backend protocol, as
+      described in 
+     
+    
+
+    
+     
+      via the Server Programming Interface (SPI), as described in
+      
+   
+
+   The pg_cursors view displays cursors
+   created by any of these means. Cursors only exist for the duration
+   of the transaction that defines them, unless they have been
+   declared WITH HOLD. Therefore non-holdable
+   cursors are only present in the view until the end of their
+   creating transaction.
+
+   
+    
+     Cursors are used internally to implement some of the components
+     of PostgreSQL, such as procedural languages.
+     Therefore, the pg_cursors view may include cursors
+     that have not been explicitly created by the user.
+    
+   
+  
+
+  
+   <structname>pg_cursors</> Columns
+
+   
+    
+     
+      Name
+      Type
+      References
+      Description
+     
+    
+
+    
+     
+      name
+      text
+      
+      The name of the cursor
+     
+
+     
+      statement
+      text
+      
+      The verbatim query string submitted to declare this cursor
+     
+
+     
+      is_holdable
+      boolean
+      
+      
+       true if the cursor is holdable (that is, it
+       can be accessed after the transaction that declared the cursor
+       has committed); false otherwise
+       
+     
+
+     
+      is_binary
+      boolean
+      
+      
+       true if the cursor was declared
+       BINARYfalse
+       otherwise
+       
+     
+
+     
+      is_scrollable
+      boolean
+      
+      
+       true if the cursor is scrollable (that is, it
+       allows rows to be retrieved in a nonsequential manner);
+       false otherwise
+       
+     
+
+     
+      creation_time
+      timestamptz
+      
+      The time at which the cursor was declared
+     
+    
+   
+  
+
+  
+   The pg_cursors view is read only.
+  
+
+
  
   <structname>pg_group</structname>
 
index 07c9c642c75287e578df3093739e7fc044aec375..689460ed92fd4bb76146a45f28d2dfb2b50fdeee 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
  Frontend/Backend Protocol
    
   
 
-  
+   id="protocol-flow-ext-query">
    Extended Query
 
    
index 3376476e26ddf5a19098f9e238bbb9ac8291e477..35f0993e6a2af81f84b9ce7682942fb2e594bfc8 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -76,6 +76,11 @@ CLOSE name
    
    statement to declare a cursor.
   
+
+  
+   You can see all available cursors by querying the
+   pg_cursors system view.
+  
  
 
  
index 0263145927df672d737f6584f5316b7fdb038624..f43e08f970df4390e922137805de3dd39aad2ce1 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -253,6 +253,11 @@ DECLARE name [ BINARY ] [ INSENSITI
     the standard SQL cursor conventions, including those involving
     DECLARE and OPEN statements.
    
+
+   
+    You can see all available cursors by querying the
+    pg_cursors system view.
+   
  
 
  
index c44e9ed72f1ca5946655a0936aed66b0b21cea17..a6d8155a0a44aeb020cdaf25b9292c9266581b02 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.24 2006/01/16 18:15:30 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.25 2006/01/18 06:49:26 neilc Exp $
  */
 
 CREATE VIEW pg_roles AS 
@@ -148,6 +148,13 @@ CREATE VIEW pg_locks AS
      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
index 8246b25774eb1e8821699062d746608cdb431b08..b2dab9d98de723ea43ad69d069805f327bd76a23 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.44 2005/11/03 17:11:35 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.45 2006/01/18 06:49:26 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@
 #include "optimizer/planner.h"
 #include "rewrite/rewriteHandler.h"
 #include "tcop/pquery.h"
+#include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 
 
@@ -105,8 +106,12 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
    query = copyObject(query);
    plan = copyObject(plan);
 
+   /*
+    * XXX: debug_query_string is wrong here: the user might have
+    * submitted more than one semicolon delimited queries.
+    */
    PortalDefineQuery(portal,
-                     NULL,     /* unfortunately don't have sourceText */
+                     pstrdup(debug_query_string),
                      "SELECT", /* cursor's query is always a SELECT */
                      list_make1(query),
                      list_make1(plan),
index f523984e5a8cef719054024c831ebfe626b6e6c9..f0afdbba3675d0cfbd7d7bfe99610d06be3e4821 100644 (file)
@@ -10,7 +10,7 @@
  * Copyright (c) 2002-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.46 2006/01/16 18:15:30 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.47 2006/01/18 06:49:26 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -162,11 +162,11 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
        paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list);
    }
 
-   /*
-    * Create a new portal to run the query in
-    */
+   /* 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).
index 21a9a901d623246a788cb45fe4c6a32e7962fbf4..278860600b46e08ae8a6436965bba26de298c534 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.145 2005/11/22 18:17:10 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.146 2006/01/18 06:49:27 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -921,8 +921,8 @@ SPI_cursor_open(const char *name, void *plan,
     * Set up the portal.
     */
    PortalDefineQuery(portal,
-                     NULL,     /* unfortunately don't have sourceText */
-                     "SELECT", /* nor the raw parse tree... */
+                     spiplan->query,
+                     "SELECT", /* don't have the raw parse tree... */
                      list_make1(queryTree),
                      list_make1(planTree),
                      PortalGetHeapMemory(portal));
index 0fe8ee057d5655c33f6593483c2f0b5760497476..ca08849afe9a106cb0467c979cf1e877ddc90a9a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.478 2006/01/08 07:00:25 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.479 2006/01/18 06:49:27 neilc Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -956,6 +956,8 @@ exec_simple_query(const char *query_string)
         * 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,
index 0402005a372c3fe6427c1585b452daebeecb6254..1bd9cc61d851e4227b57230cf75dde00d9ac9a6b 100644 (file)
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.83 2005/11/22 18:17:27 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.84 2006/01/18 06:49:27 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include "miscadmin.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"
@@ -56,8 +60,8 @@ do { \
    \
    MemSet(key, 0, MAX_PORTALNAME_LEN); \
    StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
-   hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
-                                        key, HASH_FIND, NULL); \
+   hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
+                                          key, HASH_FIND, NULL);   \
    if (hentry) \
        PORTAL = hentry->portal; \
    else \
@@ -70,8 +74,8 @@ do { \
    \
    MemSet(key, 0, MAX_PORTALNAME_LEN); \
    StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
-   hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
-                                        key, HASH_ENTER, &found); \
+   hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
+                                          key, HASH_ENTER, &found);    \
    if (found) \
        elog(ERROR, "duplicate portal name"); \
    hentry->portal = PORTAL; \
@@ -85,8 +89,8 @@ do { \
    \
    MemSet(key, 0, MAX_PORTALNAME_LEN); \
    StrNCpy(key, PORTAL->name, MAX_PORTALNAME_LEN); \
-   hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
-                                        key, HASH_REMOVE, NULL); \
+   hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
+                                          key, HASH_REMOVE, NULL); \
    if (hentry == NULL) \
        elog(WARNING, "trying to delete portal name that does not exist"); \
 } while(0)
@@ -190,12 +194,15 @@ CreatePortal(const char *name, bool allowDup, bool dupSilent)
                                           "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,3 +763,103 @@ AtSubCleanup_Portals(SubTransactionId mySubid)
        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));
+       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);
+       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);
+}
+
index b844196c4ff958bb3e52e3e42b9aa66adb3427ac..2742b700275a5fba788f8e10958f7bf216c916c3 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.311 2006/01/16 18:15:30 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.312 2006/01/18 06:49:27 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200601161
+#define CATALOG_VERSION_NO 200601181
 
 #endif
index 6f5ec17c45178ee281b5b0fc9bbb7e4b9b5ea8bd..5c54d831cdb5e89dc843e42bce0dceb870ec097d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.391 2006/01/11 20:12:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.392 2006/01/18 06:49:28 neilc Exp $
  *
  * NOTES
  *   The script catalog/genbki.sh reads this file and generates .bki
@@ -3621,6 +3621,8 @@ DATA(insert OID = 2509 (  pg_get_expr        PGNSP PGUID 12 f f t f s 3 25 "25 26 1
 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_ ));
index 8ca212852c2c1e9a41c102495c168f4021aa02c5..e8ad4bd0e21b05404429dc40f8af5bdee9b6e37a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.270 2006/01/11 20:12:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.271 2006/01/18 06:49:29 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -865,4 +865,7 @@ extern Datum pg_convert_using(PG_FUNCTION_ARGS);
 /* 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 */
index 758592525ffbf735aa31ee46d75048fc602be36d..dca2ce8af3ef6653c38f950a074f3e0f57cb01ad 100644 (file)
@@ -39,7 +39,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.57 2005/10/15 02:49:46 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.58 2006/01/18 06:49:29 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -72,7 +72,6 @@
  * 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,
@@ -166,6 +165,10 @@ typedef struct PortalData
    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;
 
 /*
index 3f0e0cdd265fdc1741285e75938c7380746c546f..cbff0b4245a2288e407893b7d24ee3260a268427 100644 (file)
@@ -676,7 +676,30 @@ CLOSE foo10;
 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,6 +718,11 @@ END;
 --
 -- 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,6 +756,12 @@ FETCH ABSOLUTE -1 FROM foo25;
     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,3 +842,30 @@ fetch all from c2;
 
 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)
+
index 63c06ec75ace9e09f988eb5dfa8e2a62175e5248..90e38d013f3b6d26196bb5dadd5a0335398bfb5b 100644 (file)
@@ -1277,6 +1277,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
          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,7 +1322,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
  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)
+(46 rows)
 
 SELECT tablename, rulename, definition FROM pg_rules 
    ORDER BY tablename, rulename;
index da4e3b0e3ae591863c2087575e2ff935f0882a76..c0307874e800fa7797ccfc70a0934e8508d51852 100644 (file)
@@ -168,8 +168,14 @@ 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;
+
 END;
 
+SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors;
+
 --
 -- NO SCROLL disallows backward fetching
 --
@@ -188,6 +194,9 @@ END;
 -- 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,6 +213,8 @@ FETCH BACKWARD FROM foo25;
 
 FETCH ABSOLUTE -1 FROM foo25;
 
+SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors;
+
 CLOSE foo25;
 
 --
@@ -278,3 +289,17 @@ fetch all from c2;
 
 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;