OSDN Git Service

Add a new system view, pg_cursors, that displays the currently available
authorNeil Conway <neilc@samurai.com>
Wed, 18 Jan 2006 06:49:30 +0000 (06:49 +0000)
committerNeil Conway <neilc@samurai.com>
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 c346005..aee68c2 100644 (file)
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.117 2006/01/16 18:15:30 neilc Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.118 2006/01/18 06:49:25 neilc Exp $
  -->
 
 <chapter id="catalogs">
 
     <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>
   </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 <structname>pg_cursors</structname> view lists the cursors that
+   are currently available. Cursors can be defined in several ways:
+   <itemizedlist>
+    <listitem>
+     <para>
+      via the <xref linkend="sql-declare" endterm="sql-declare-title">
+      statement in SQL
+     </para>
+    </listitem>
+
+    <listitem>
+     <para>
+      via the Bind message in the frontend/backend protocol, as
+      described in <xref linkend="protocol-flow-ext-query">
+     </para>
+    </listitem>
+
+    <listitem>
+     <para>
+      via the Server Programming Interface (SPI), as described in
+      <xref linkend="spi-interface">
+   </itemizedlist>
+
+   The <structname>pg_cursors</structname> 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 <literal>WITH HOLD</literal>. Therefore non-holdable
+   cursors are only present in the view until the end of their
+   creating transaction.
+
+   <note>
+    <para>
+     Cursors are used internally to implement some of the components
+     of <productname>PostgreSQL</>, such as procedural languages.
+     Therefore, the <structname>pg_cursors</> view may include cursors
+     that have not been explicitly created by the user.
+    </para>
+   </note>
+  </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
+       </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>
 
index 07c9c64..689460e 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/protocol.sgml,v 1.62 2005/08/14 22:19:49 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/protocol.sgml,v 1.63 2006/01/18 06:49:25 neilc Exp $ -->
 
 <chapter id="protocol">
  <title>Frontend/Backend Protocol</title>
    </para>
   </sect2>
 
-  <sect2>
+  <sect2 id="protocol-flow-ext-query">
    <title>Extended Query</title>
 
    <para>
index 3376476..35f0993 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/close.sgml,v 1.22 2005/01/04 00:39:53 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/close.sgml,v 1.23 2006/01/18 06:49:26 neilc Exp $
 PostgreSQL documentation
 -->
 
@@ -76,6 +76,11 @@ CLOSE <replaceable class="PARAMETER">name</replaceable>
    <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>
index 0263145..f43e08f 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/declare.sgml,v 1.33 2005/01/04 00:39:53 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/declare.sgml,v 1.34 2006/01/18 06:49:26 neilc Exp $
 PostgreSQL documentation
 -->
 
@@ -253,6 +253,11 @@ DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITI
     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>
index c44e9ed..a6d8155 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 8246b25..b2dab9d 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 f523984..f0afdbb 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 21a9a90..2788606 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 0fe8ee0..ca08849 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 0402005..1bd9cc6 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 b844196..2742b70 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 6f5ec17..5c54d83 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 8ca2128..e8ad4bd 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 7585925..dca2ce8 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 3f0e0cd..cbff0b4 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 63c06ec..90e38d0 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 da4e3b0..c030787 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;