Add new return codes SPI_OK_INSERT_RETURNING etc to the SPI API.
Fix all the standard PLs to be able to return tuples from FOO_RETURNING statements as well as utility statements that return tuples. Also, fix oversight that SPI_processed wasn't set for a utility statement returning tuples. Per recent discussion.
This commit is contained in:
parent
7a2fe85b03
commit
ea2e263539
|
@ -1,4 +1,4 @@
|
||||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.46 2006/08/12 20:05:54 tgl Exp $ -->
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.47 2006/08/27 23:47:57 tgl Exp $ -->
|
||||||
|
|
||||||
<chapter id="spi">
|
<chapter id="spi">
|
||||||
<title>Server Programming Interface</title>
|
<title>Server Programming Interface</title>
|
||||||
|
@ -361,12 +361,16 @@ SPI_execute("INSERT INTO foo SELECT * FROM bar", false, 5);
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The actual number of rows for which the (last) command was executed
|
The actual number of rows for which the (last) command was executed
|
||||||
is returned in the global variable <varname>SPI_processed</varname>
|
is returned in the global variable <varname>SPI_processed</varname>.
|
||||||
(unless the return value of the function is
|
If the return value of the function is <symbol>SPI_OK_SELECT</symbol>,
|
||||||
<symbol>SPI_OK_UTILITY</symbol>). If the return value of the
|
<symbol>SPI_OK_INSERT_RETURNING</symbol>,
|
||||||
function is <symbol>SPI_OK_SELECT</symbol> then you may use the
|
<symbol>SPI_OK_DELETE_RETURNING</symbol>, or
|
||||||
|
<symbol>SPI_OK_UPDATE_RETURNING</symbol>,
|
||||||
|
then you may use the
|
||||||
global pointer <literal>SPITupleTable *SPI_tuptable</literal> to
|
global pointer <literal>SPITupleTable *SPI_tuptable</literal> to
|
||||||
access the result rows.
|
access the result rows. Some utility commands (such as
|
||||||
|
<command>EXPLAIN</>) also return rowsets, and <literal>SPI_tuptable</>
|
||||||
|
will contain the result in these cases too.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -458,15 +462,6 @@ typedef struct
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><symbol>SPI_OK_DELETE</symbol></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
if a <command>DELETE</command> was executed
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><symbol>SPI_OK_INSERT</symbol></term>
|
<term><symbol>SPI_OK_INSERT</symbol></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -476,6 +471,15 @@ typedef struct
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><symbol>SPI_OK_DELETE</symbol></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
if a <command>DELETE</command> was executed
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><symbol>SPI_OK_UPDATE</symbol></term>
|
<term><symbol>SPI_OK_UPDATE</symbol></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -485,6 +489,33 @@ typedef struct
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><symbol>SPI_OK_INSERT_RETURNING</symbol></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
if an <command>INSERT RETURNING</command> was executed
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><symbol>SPI_OK_DELETE_RETURNING</symbol></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
if a <command>DELETE RETURNING</command> was executed
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><symbol>SPI_OK_UPDATE_RETURNING</symbol></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
if an <command>UPDATE RETURNING</command> was executed
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><symbol>SPI_OK_UTILITY</symbol></term>
|
<term><symbol>SPI_OK_UTILITY</symbol></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -2987,10 +3018,9 @@ execq(text *sql, int cnt)
|
||||||
|
|
||||||
proc = SPI_processed;
|
proc = SPI_processed;
|
||||||
/*
|
/*
|
||||||
* If this is a SELECT and some rows were fetched,
|
* If some rows were fetched, print them via elog(INFO).
|
||||||
* then the rows are printed via elog(INFO).
|
|
||||||
*/
|
*/
|
||||||
if (ret == SPI_OK_SELECT && SPI_processed > 0)
|
if (ret > 0 && SPI_tuptable != NULL)
|
||||||
{
|
{
|
||||||
TupleDesc tupdesc = SPI_tuptable->tupdesc;
|
TupleDesc tupdesc = SPI_tuptable->tupdesc;
|
||||||
SPITupleTable *tuptable = SPI_tuptable;
|
SPITupleTable *tuptable = SPI_tuptable;
|
||||||
|
@ -3005,7 +3035,7 @@ execq(text *sql, int cnt)
|
||||||
snprintf(buf + strlen (buf), sizeof(buf) - strlen(buf), " %s%s",
|
snprintf(buf + strlen (buf), sizeof(buf) - strlen(buf), " %s%s",
|
||||||
SPI_getvalue(tuple, tupdesc, i),
|
SPI_getvalue(tuple, tupdesc, i),
|
||||||
(i == tupdesc->natts) ? " " : " |");
|
(i == tupdesc->natts) ? " " : " |");
|
||||||
elog (INFO, "EXECQ: %s", buf);
|
elog(INFO, "EXECQ: %s", buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.157 2006/08/14 22:57:15 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.158 2006/08/27 23:47:57 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1136,6 +1136,12 @@ SPI_result_code_string(int code)
|
||||||
return "SPI_OK_UPDATE";
|
return "SPI_OK_UPDATE";
|
||||||
case SPI_OK_CURSOR:
|
case SPI_OK_CURSOR:
|
||||||
return "SPI_OK_CURSOR";
|
return "SPI_OK_CURSOR";
|
||||||
|
case SPI_OK_INSERT_RETURNING:
|
||||||
|
return "SPI_OK_INSERT_RETURNING";
|
||||||
|
case SPI_OK_DELETE_RETURNING:
|
||||||
|
return "SPI_OK_DELETE_RETURNING";
|
||||||
|
case SPI_OK_UPDATE_RETURNING:
|
||||||
|
return "SPI_OK_UPDATE_RETURNING";
|
||||||
}
|
}
|
||||||
/* Unrecognized code ... return something useful ... */
|
/* Unrecognized code ... return something useful ... */
|
||||||
sprintf(buf, "Unrecognized SPI code %d", code);
|
sprintf(buf, "Unrecognized SPI code %d", code);
|
||||||
|
@ -1454,6 +1460,9 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
||||||
{
|
{
|
||||||
ProcessUtility(queryTree->utilityStmt, paramLI,
|
ProcessUtility(queryTree->utilityStmt, paramLI,
|
||||||
dest, NULL);
|
dest, NULL);
|
||||||
|
/* Update "processed" if stmt returned tuples */
|
||||||
|
if (_SPI_current->tuptable)
|
||||||
|
_SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free;
|
||||||
res = SPI_OK_UTILITY;
|
res = SPI_OK_UTILITY;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1542,13 +1551,22 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
|
||||||
res = SPI_OK_SELECT;
|
res = SPI_OK_SELECT;
|
||||||
break;
|
break;
|
||||||
case CMD_INSERT:
|
case CMD_INSERT:
|
||||||
res = SPI_OK_INSERT;
|
if (queryDesc->parsetree->returningList)
|
||||||
|
res = SPI_OK_INSERT_RETURNING;
|
||||||
|
else
|
||||||
|
res = SPI_OK_INSERT;
|
||||||
break;
|
break;
|
||||||
case CMD_DELETE:
|
case CMD_DELETE:
|
||||||
res = SPI_OK_DELETE;
|
if (queryDesc->parsetree->returningList)
|
||||||
|
res = SPI_OK_DELETE_RETURNING;
|
||||||
|
else
|
||||||
|
res = SPI_OK_DELETE;
|
||||||
break;
|
break;
|
||||||
case CMD_UPDATE:
|
case CMD_UPDATE:
|
||||||
res = SPI_OK_UPDATE;
|
if (queryDesc->parsetree->returningList)
|
||||||
|
res = SPI_OK_UPDATE_RETURNING;
|
||||||
|
else
|
||||||
|
res = SPI_OK_UPDATE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return SPI_ERROR_OPUNKNOWN;
|
return SPI_ERROR_OPUNKNOWN;
|
||||||
|
@ -1568,7 +1586,8 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
|
||||||
_SPI_current->processed = queryDesc->estate->es_processed;
|
_SPI_current->processed = queryDesc->estate->es_processed;
|
||||||
_SPI_current->lastoid = queryDesc->estate->es_lastoid;
|
_SPI_current->lastoid = queryDesc->estate->es_lastoid;
|
||||||
|
|
||||||
if (operation == CMD_SELECT && queryDesc->dest->mydest == DestSPI)
|
if ((res == SPI_OK_SELECT || queryDesc->parsetree->returningList) &&
|
||||||
|
queryDesc->dest->mydest == DestSPI)
|
||||||
{
|
{
|
||||||
if (_SPI_checktuples())
|
if (_SPI_checktuples())
|
||||||
elog(ERROR, "consistency check on SPI tuple count failed");
|
elog(ERROR, "consistency check on SPI tuple count failed");
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* spi.h
|
* spi.h
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.54 2006/07/11 18:26:11 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.55 2006/08/27 23:47:58 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -71,6 +71,9 @@ typedef struct
|
||||||
#define SPI_OK_DELETE 8
|
#define SPI_OK_DELETE 8
|
||||||
#define SPI_OK_UPDATE 9
|
#define SPI_OK_UPDATE 9
|
||||||
#define SPI_OK_CURSOR 10
|
#define SPI_OK_CURSOR 10
|
||||||
|
#define SPI_OK_INSERT_RETURNING 11
|
||||||
|
#define SPI_OK_DELETE_RETURNING 12
|
||||||
|
#define SPI_OK_UPDATE_RETURNING 13
|
||||||
|
|
||||||
extern DLLIMPORT uint32 SPI_processed;
|
extern DLLIMPORT uint32 SPI_processed;
|
||||||
extern DLLIMPORT Oid SPI_lastoid;
|
extern DLLIMPORT Oid SPI_lastoid;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* plperl.c - perl as a procedural language for PostgreSQL
|
* plperl.c - perl as a procedural language for PostgreSQL
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.117 2006/08/13 17:31:10 momjian Exp $
|
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.118 2006/08/27 23:47:58 tgl Exp $
|
||||||
*
|
*
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
|
||||||
|
@ -1630,7 +1630,7 @@ plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed,
|
||||||
hv_store(result, "processed", strlen("processed"),
|
hv_store(result, "processed", strlen("processed"),
|
||||||
newSViv(processed), 0);
|
newSViv(processed), 0);
|
||||||
|
|
||||||
if (status == SPI_OK_SELECT)
|
if (status > 0 && tuptable)
|
||||||
{
|
{
|
||||||
AV *rows;
|
AV *rows;
|
||||||
SV *row;
|
SV *row;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.176 2006/08/15 19:01:17 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.177 2006/08/27 23:47:58 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -2370,23 +2370,16 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
|
||||||
case SPI_OK_INSERT:
|
case SPI_OK_INSERT:
|
||||||
case SPI_OK_UPDATE:
|
case SPI_OK_UPDATE:
|
||||||
case SPI_OK_DELETE:
|
case SPI_OK_DELETE:
|
||||||
|
case SPI_OK_INSERT_RETURNING:
|
||||||
|
case SPI_OK_UPDATE_RETURNING:
|
||||||
|
case SPI_OK_DELETE_RETURNING:
|
||||||
Assert(stmt->mod_stmt);
|
Assert(stmt->mod_stmt);
|
||||||
exec_set_found(estate, (SPI_processed != 0));
|
exec_set_found(estate, (SPI_processed != 0));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SPI_OK_SELINTO:
|
case SPI_OK_SELINTO:
|
||||||
Assert(!stmt->mod_stmt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SPI_OK_UTILITY:
|
case SPI_OK_UTILITY:
|
||||||
Assert(!stmt->mod_stmt);
|
Assert(!stmt->mod_stmt);
|
||||||
/*
|
|
||||||
* spi.c currently does not update SPI_processed for utility
|
|
||||||
* commands. Not clear if this should be considered a bug;
|
|
||||||
* for the moment, work around it here.
|
|
||||||
*/
|
|
||||||
if (SPI_tuptable)
|
|
||||||
SPI_processed = (SPI_tuptable->alloced - SPI_tuptable->free);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -2505,16 +2498,10 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
|
||||||
case SPI_OK_INSERT:
|
case SPI_OK_INSERT:
|
||||||
case SPI_OK_UPDATE:
|
case SPI_OK_UPDATE:
|
||||||
case SPI_OK_DELETE:
|
case SPI_OK_DELETE:
|
||||||
break;
|
case SPI_OK_INSERT_RETURNING:
|
||||||
|
case SPI_OK_UPDATE_RETURNING:
|
||||||
|
case SPI_OK_DELETE_RETURNING:
|
||||||
case SPI_OK_UTILITY:
|
case SPI_OK_UTILITY:
|
||||||
/*
|
|
||||||
* spi.c currently does not update SPI_processed for utility
|
|
||||||
* commands. Not clear if this should be considered a bug;
|
|
||||||
* for the moment, work around it here.
|
|
||||||
*/
|
|
||||||
if (SPI_tuptable)
|
|
||||||
SPI_processed = (SPI_tuptable->alloced - SPI_tuptable->free);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* plpython.c - python as a procedural language for PostgreSQL
|
* plpython.c - python as a procedural language for PostgreSQL
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.85 2006/08/08 19:15:09 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.86 2006/08/27 23:47:58 tgl Exp $
|
||||||
*
|
*
|
||||||
*********************************************************************
|
*********************************************************************
|
||||||
*/
|
*/
|
||||||
|
@ -2193,24 +2193,19 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
|
||||||
Py_DECREF(result->status);
|
Py_DECREF(result->status);
|
||||||
result->status = PyInt_FromLong(status);
|
result->status = PyInt_FromLong(status);
|
||||||
|
|
||||||
if (status == SPI_OK_UTILITY)
|
if (status > 0 && tuptable == NULL)
|
||||||
{
|
|
||||||
Py_DECREF(result->nrows);
|
|
||||||
result->nrows = PyInt_FromLong(0);
|
|
||||||
}
|
|
||||||
else if (status != SPI_OK_SELECT)
|
|
||||||
{
|
{
|
||||||
Py_DECREF(result->nrows);
|
Py_DECREF(result->nrows);
|
||||||
result->nrows = PyInt_FromLong(rows);
|
result->nrows = PyInt_FromLong(rows);
|
||||||
}
|
}
|
||||||
else
|
else if (status > 0 && tuptable != NULL)
|
||||||
{
|
{
|
||||||
PLyTypeInfo args;
|
PLyTypeInfo args;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
PLy_typeinfo_init(&args);
|
|
||||||
Py_DECREF(result->nrows);
|
Py_DECREF(result->nrows);
|
||||||
result->nrows = PyInt_FromLong(rows);
|
result->nrows = PyInt_FromLong(rows);
|
||||||
|
PLy_typeinfo_init(&args);
|
||||||
|
|
||||||
oldcontext = CurrentMemoryContext;
|
oldcontext = CurrentMemoryContext;
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* pltcl.c - PostgreSQL support for Tcl as
|
* pltcl.c - PostgreSQL support for Tcl as
|
||||||
* procedural language (PL)
|
* procedural language (PL)
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.106 2006/08/08 19:15:09 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.107 2006/08/27 23:47:58 tgl Exp $
|
||||||
*
|
*
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
|
||||||
|
@ -1663,10 +1663,6 @@ pltcl_process_SPI_result(Tcl_Interp *interp,
|
||||||
|
|
||||||
switch (spi_rc)
|
switch (spi_rc)
|
||||||
{
|
{
|
||||||
case SPI_OK_UTILITY:
|
|
||||||
Tcl_SetResult(interp, "0", TCL_VOLATILE);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SPI_OK_SELINTO:
|
case SPI_OK_SELINTO:
|
||||||
case SPI_OK_INSERT:
|
case SPI_OK_INSERT:
|
||||||
case SPI_OK_DELETE:
|
case SPI_OK_DELETE:
|
||||||
|
@ -1675,7 +1671,18 @@ pltcl_process_SPI_result(Tcl_Interp *interp,
|
||||||
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SPI_OK_UTILITY:
|
||||||
|
if (tuptable == NULL)
|
||||||
|
{
|
||||||
|
Tcl_SetResult(interp, "0", TCL_VOLATILE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* FALL THRU for utility returning tuples */
|
||||||
|
|
||||||
case SPI_OK_SELECT:
|
case SPI_OK_SELECT:
|
||||||
|
case SPI_OK_INSERT_RETURNING:
|
||||||
|
case SPI_OK_DELETE_RETURNING:
|
||||||
|
case SPI_OK_UPDATE_RETURNING:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process the tuples we got
|
* Process the tuples we got
|
||||||
|
|
Loading…
Reference in New Issue