Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 42599b3

Browse files
committed
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.
1 parent 5cc8884 commit 42599b3

File tree

2 files changed

+54
-15
lines changed

2 files changed

+54
-15
lines changed

src/backend/executor/spi.c

+27-13
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.133 2004/12/31 21:59:45 pgsql Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.134 2005/02/10 20:36:27 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -832,22 +832,35 @@ SPI_cursor_open(const char *name, void *plan,
832832
Portal portal;
833833
int k;
834834

835-
/* Ensure that the plan contains only one regular SELECT query */
835+
/* Ensure that the plan contains only one query */
836836
if (list_length(ptlist) != 1 || list_length(qtlist) != 1)
837837
ereport(ERROR,
838838
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
839839
errmsg("cannot open multi-query plan as cursor")));
840840
queryTree = (Query *) linitial((List *) linitial(qtlist));
841841
planTree = (Plan *) linitial(ptlist);
842842

843-
if (queryTree->commandType != CMD_SELECT)
844-
ereport(ERROR,
845-
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
846-
errmsg("cannot open non-SELECT query as cursor")));
847-
if (queryTree->into != NULL)
848-
ereport(ERROR,
849-
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
850-
errmsg("cannot open SELECT INTO query as cursor")));
843+
/* Must be a query that returns tuples */
844+
switch (queryTree->commandType)
845+
{
846+
case CMD_SELECT:
847+
if (queryTree->into != NULL)
848+
ereport(ERROR,
849+
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
850+
errmsg("cannot open SELECT INTO query as cursor")));
851+
break;
852+
case CMD_UTILITY:
853+
if (!UtilityReturnsTuples(queryTree->utilityStmt))
854+
ereport(ERROR,
855+
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
856+
errmsg("cannot open non-SELECT query as cursor")));
857+
break;
858+
default:
859+
ereport(ERROR,
860+
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
861+
errmsg("cannot open non-SELECT query as cursor")));
862+
break;
863+
}
851864

852865
/* Reset SPI result */
853866
SPI_processed = 0;
@@ -911,7 +924,7 @@ SPI_cursor_open(const char *name, void *plan,
911924
*/
912925
PortalDefineQuery(portal,
913926
NULL, /* unfortunately don't have sourceText */
914-
"SELECT", /* cursor's query is always a SELECT */
927+
"SELECT", /* nor the raw parse tree... */
915928
list_make1(queryTree),
916929
list_make1(planTree),
917930
PortalGetHeapMemory(portal));
@@ -922,7 +935,7 @@ SPI_cursor_open(const char *name, void *plan,
922935
* Set up options for portal.
923936
*/
924937
portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
925-
if (ExecSupportsBackwardScan(plan))
938+
if (planTree == NULL || ExecSupportsBackwardScan(planTree))
926939
portal->cursorOptions |= CURSOR_OPT_SCROLL;
927940
else
928941
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
@@ -944,7 +957,8 @@ SPI_cursor_open(const char *name, void *plan,
944957
*/
945958
PortalStart(portal, paramLI, snapshot);
946959

947-
Assert(portal->strategy == PORTAL_ONE_SELECT);
960+
Assert(portal->strategy == PORTAL_ONE_SELECT ||
961+
portal->strategy == PORTAL_UTIL_SELECT);
948962

949963
/* Return the created portal */
950964
return portal;

src/backend/tcop/pquery.c

+27-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.90 2005/02/01 23:28:40 neilc Exp $
11+
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.91 2005/02/10 20:36:28 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1120,6 +1120,30 @@ PortalRunFetch(Portal portal,
11201120
result = DoPortalRunFetch(portal, fdirection, count, dest);
11211121
break;
11221122

1123+
case PORTAL_UTIL_SELECT:
1124+
1125+
/*
1126+
* If we have not yet run the utility statement, do so,
1127+
* storing its results in the portal's tuplestore.
1128+
*/
1129+
if (!portal->portalUtilReady)
1130+
{
1131+
DestReceiver *treceiver;
1132+
1133+
PortalCreateHoldStore(portal);
1134+
treceiver = CreateDestReceiver(Tuplestore, portal);
1135+
PortalRunUtility(portal, linitial(portal->parseTrees),
1136+
treceiver, NULL);
1137+
(*treceiver->rDestroy) (treceiver);
1138+
portal->portalUtilReady = true;
1139+
}
1140+
1141+
/*
1142+
* Now fetch desired portion of results.
1143+
*/
1144+
result = DoPortalRunFetch(portal, fdirection, count, dest);
1145+
break;
1146+
11231147
default:
11241148
elog(ERROR, "unsupported portal strategy");
11251149
result = 0; /* keep compiler quiet */
@@ -1170,7 +1194,8 @@ DoPortalRunFetch(Portal portal,
11701194
{
11711195
bool forward;
11721196

1173-
Assert(portal->strategy == PORTAL_ONE_SELECT);
1197+
Assert(portal->strategy == PORTAL_ONE_SELECT ||
1198+
portal->strategy == PORTAL_UTIL_SELECT);
11741199

11751200
switch (fdirection)
11761201
{

0 commit comments

Comments
 (0)