Fix SPI cursor support to allow scanning the results of utility commands
authorTom Lane
Thu, 10 Feb 2005 20:36:28 +0000 (20:36 +0000)
committerTom Lane
Thu, 10 Feb 2005 20:36:28 +0000 (20:36 +0000)
that return tuples (such as EXPLAIN).  Per gripe from Michael Fuhr.
Side effect: fix an old bug that unintentionally disabled backward scans
for all SPI-created cursors.

src/backend/executor/spi.c
src/backend/tcop/pquery.c

index a0bfdf12bce895415f116fca9c3546dc117da26b..fd860fcb5514e69f7d91a353bfb41a5d058547d0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.133 2004/12/31 21:59:45 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.134 2005/02/10 20:36:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -832,7 +832,7 @@ SPI_cursor_open(const char *name, void *plan,
    Portal      portal;
    int         k;
 
-   /* Ensure that the plan contains only one regular SELECT query */
+   /* Ensure that the plan contains only one query */
    if (list_length(ptlist) != 1 || list_length(qtlist) != 1)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
@@ -840,14 +840,27 @@ SPI_cursor_open(const char *name, void *plan,
    queryTree = (Query *) linitial((List *) linitial(qtlist));
    planTree = (Plan *) linitial(ptlist);
 
-   if (queryTree->commandType != CMD_SELECT)
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
-                errmsg("cannot open non-SELECT query as cursor")));
-   if (queryTree->into != NULL)
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
-                errmsg("cannot open SELECT INTO query as cursor")));
+   /* Must be a query that returns tuples */
+   switch (queryTree->commandType)
+   {
+       case CMD_SELECT:
+           if (queryTree->into != NULL)
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+                        errmsg("cannot open SELECT INTO query as cursor")));
+           break;
+       case CMD_UTILITY:
+           if (!UtilityReturnsTuples(queryTree->utilityStmt))
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+                        errmsg("cannot open non-SELECT query as cursor")));
+           break;
+       default:
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+                    errmsg("cannot open non-SELECT query as cursor")));
+           break;
+   }
 
    /* Reset SPI result */
    SPI_processed = 0;
@@ -911,7 +924,7 @@ SPI_cursor_open(const char *name, void *plan,
     */
    PortalDefineQuery(portal,
                      NULL,     /* unfortunately don't have sourceText */
-                     "SELECT", /* cursor's query is always a SELECT */
+                     "SELECT", /* nor the raw parse tree... */
                      list_make1(queryTree),
                      list_make1(planTree),
                      PortalGetHeapMemory(portal));
@@ -922,7 +935,7 @@ SPI_cursor_open(const char *name, void *plan,
     * Set up options for portal.
     */
    portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
-   if (ExecSupportsBackwardScan(plan))
+   if (planTree == NULL || ExecSupportsBackwardScan(planTree))
        portal->cursorOptions |= CURSOR_OPT_SCROLL;
    else
        portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
@@ -944,7 +957,8 @@ SPI_cursor_open(const char *name, void *plan,
     */
    PortalStart(portal, paramLI, snapshot);
 
-   Assert(portal->strategy == PORTAL_ONE_SELECT);
+   Assert(portal->strategy == PORTAL_ONE_SELECT ||
+          portal->strategy == PORTAL_UTIL_SELECT);
 
    /* Return the created portal */
    return portal;
index 496cbb43f8a0a492833d774a8360a98550014a1b..d4d5b945946c8f4b00d7e774b8c2c8459138aee4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.90 2005/02/01 23:28:40 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.91 2005/02/10 20:36:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1120,6 +1120,30 @@ PortalRunFetch(Portal portal,
                result = DoPortalRunFetch(portal, fdirection, count, dest);
                break;
 
+           case PORTAL_UTIL_SELECT:
+
+               /*
+                * If we have not yet run the utility statement, do so,
+                * storing its results in the portal's tuplestore.
+                */
+               if (!portal->portalUtilReady)
+               {
+                   DestReceiver *treceiver;
+
+                   PortalCreateHoldStore(portal);
+                   treceiver = CreateDestReceiver(Tuplestore, portal);
+                   PortalRunUtility(portal, linitial(portal->parseTrees),
+                                    treceiver, NULL);
+                   (*treceiver->rDestroy) (treceiver);
+                   portal->portalUtilReady = true;
+               }
+
+               /*
+                * Now fetch desired portion of results.
+                */
+               result = DoPortalRunFetch(portal, fdirection, count, dest);
+               break;
+
            default:
                elog(ERROR, "unsupported portal strategy");
                result = 0;     /* keep compiler quiet */
@@ -1170,7 +1194,8 @@ DoPortalRunFetch(Portal portal,
 {
    bool        forward;
 
-   Assert(portal->strategy == PORTAL_ONE_SELECT);
+   Assert(portal->strategy == PORTAL_ONE_SELECT ||
+          portal->strategy == PORTAL_UTIL_SELECT);
 
    switch (fdirection)
    {