summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2005-02-10 20:36:28 +0000
committerTom Lane2005-02-10 20:36:28 +0000
commit42599b322db75b1cfdf4ab518532ddffd15066ac (patch)
tree5835e341265b3493ba2d72ab57655922bfac3b8d
parent5cc8884a5d9f1d238a2098ebe6024f5d748c1f25 (diff)
Fix SPI cursor support to allow scanning the results of utility commands
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.
-rw-r--r--src/backend/executor/spi.c40
-rw-r--r--src/backend/tcop/pquery.c29
2 files changed, 54 insertions, 15 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index a0bfdf12bce..fd860fcb551 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -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;
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 496cbb43f8a..d4d5b945946 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -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)
{