Fix SPI cursor support to allow scanning the results of utility commands
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 10 Feb 2005 20:36:28 +0000 (20:36 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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)
        {