Commit 42599b32 authored by Tom Lane's avatar Tom Lane

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.
parent 5cc8884a
......@@ -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;
......
......@@ -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)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment