1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-30 21:42:05 +03:00

Tweak SPI_cursor_open to allow INSERT/UPDATE/DELETE RETURNING; this was

merely a matter of fixing the error check, since the underlying Portal
infrastructure already handles it.  This in turn allows these statements
to be used in some existing plpgsql and plperl contexts, such as a
plpgsql FOR loop.  Also, do some marginal code cleanup in places that
were being sloppy about distinguishing SELECT from SELECT INTO.
This commit is contained in:
Tom Lane
2006-08-12 20:05:56 +00:00
parent 883f4b42d7
commit 3f8db37c2f
16 changed files with 152 additions and 114 deletions

View File

@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.278 2006/08/12 02:52:04 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.279 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -497,7 +497,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
/*
* if there is a result relation, initialize result relation stuff
*/
if (parseTree->resultRelation != 0 && operation != CMD_SELECT)
if (parseTree->resultRelation)
{
List *resultRelations = parseTree->resultRelations;
int numResultRelations;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.104 2006/07/14 14:52:19 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.105 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -361,7 +361,9 @@ postquel_getnext(execution_state *es)
* run it to completion. (If we run to completion then
* ExecutorRun is guaranteed to return NULL.)
*/
if (LAST_POSTQUEL_COMMAND(es) && es->qd->operation == CMD_SELECT)
if (LAST_POSTQUEL_COMMAND(es) &&
es->qd->operation == CMD_SELECT &&
es->qd->parsetree->into == NULL)
count = 1L;
else
count = 0L;
@ -868,7 +870,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
JunkFilter **junkFilter)
{
Query *parse;
int cmd;
bool isSelect;
List *tlist;
ListCell *tlistitem;
int tlistlen;
@ -893,15 +895,18 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
/* find the final query */
parse = (Query *) lfirst(list_tail(queryTreeList));
cmd = parse->commandType;
tlist = parse->targetList;
/*
* Note: eventually replace this with QueryReturnsTuples? We'd need
* a more general method of determining the output type, though.
*/
isSelect = (parse->commandType == CMD_SELECT && parse->into == NULL);
/*
* The last query must be a SELECT if and only if return type isn't VOID.
*/
if (rettype == VOIDOID)
{
if (cmd == CMD_SELECT)
if (isSelect)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("return type mismatch in function declared to return %s",
@ -911,7 +916,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
}
/* by here, the function is declared to return some type */
if (cmd != CMD_SELECT)
if (!isSelect)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("return type mismatch in function declared to return %s",
@ -921,6 +926,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
/*
* Count the non-junk entries in the result targetlist.
*/
tlist = parse->targetList;
tlistlen = ExecCleanTargetListLength(tlist);
fn_typtype = get_typtype(rettype);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.154 2006/08/12 02:52:04 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.155 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -837,26 +837,12 @@ SPI_cursor_open(const char *name, void *plan,
planTree = (Plan *) linitial(ptlist);
/* 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;
}
if (!QueryReturnsTuples(queryTree))
ereport(ERROR,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
/* translator: %s is name of a SQL command, eg INSERT */
errmsg("cannot open %s query as cursor",
CreateQueryTag(queryTree))));
/* Reset SPI result (note we deliberately don't touch lastoid) */
SPI_processed = 0;
@ -876,7 +862,7 @@ SPI_cursor_open(const char *name, void *plan,
portal = CreatePortal(name, false, false);
}
/* Switch to portals memory and copy the parsetree and plan to there */
/* Switch to portal's memory and copy the parsetree and plan to there */
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
queryTree = copyObject(queryTree);
planTree = copyObject(planTree);
@ -919,9 +905,9 @@ SPI_cursor_open(const char *name, void *plan,
* Set up the portal.
*/
PortalDefineQuery(portal,
NULL,
NULL, /* no statement name */
spiplan->query,
"SELECT", /* don't have the raw parse tree... */
CreateQueryTag(queryTree),
list_make1(queryTree),
list_make1(planTree),
PortalGetHeapMemory(portal));
@ -954,9 +940,16 @@ SPI_cursor_open(const char *name, void *plan,
*/
PortalStart(portal, paramLI, snapshot);
Assert(portal->strategy == PORTAL_ONE_SELECT ||
portal->strategy == PORTAL_ONE_RETURNING ||
portal->strategy == PORTAL_UTIL_SELECT);
/*
* If this test fails then we're out of sync with pquery.c about
* which queries can return tuples...
*/
if (portal->strategy == PORTAL_MULTI_QUERY)
ereport(ERROR,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
/* translator: %s is name of a SQL command, eg INSERT */
errmsg("cannot open %s query as cursor",
CreateQueryTag(queryTree))));
/* Return the created portal */
return portal;
@ -1046,12 +1039,12 @@ SPI_getargcount(void *plan)
/*
* Returns true if the plan contains exactly one command
* and that command originates from normal SELECT (i.e.
* *not* a SELECT ... INTO). In essence, the result indicates
* if the command can be used with SPI_cursor_open
* and that command returns tuples to the caller (eg, SELECT or
* INSERT ... RETURNING, but not SELECT ... INTO). In essence,
* the result indicates if the command can be used with SPI_cursor_open
*
* Parameters
* plan A plan previously prepared using SPI_prepare
* plan: A plan previously prepared using SPI_prepare
*/
bool
SPI_is_cursor_plan(void *plan)
@ -1070,7 +1063,7 @@ SPI_is_cursor_plan(void *plan)
{
Query *queryTree = (Query *) linitial((List *) linitial(qtlist));
if (queryTree->commandType == CMD_SELECT && queryTree->into == NULL)
if (QueryReturnsTuples(queryTree))
return true;
}
return false;