summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2005-02-10 20:36:49 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2005-02-10 20:36:49 +0000
commit9843862b50b3ce3c56d10116df782167e6da016e (patch)
treea63f273fd9d8d37a65b7cf6dc74e1edca48de1a2
parent95b40b7f7736fba60a34dc143bea3f86ea0cf8e4 (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..dd1fcf8ec11 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.133.4.1 2005/02/10 20:36:48 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 cc048d811a6..e44ab59db2a 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.89.4.1 2005/02/01 23:29:13 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.89.4.2 2005/02/10 20:36:49 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)
{