Add new return codes SPI_OK_INSERT_RETURNING etc to the SPI API.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 27 Aug 2006 23:47:58 +0000 (23:47 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 27 Aug 2006 23:47:58 +0000 (23:47 +0000)
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.

doc/src/sgml/spi.sgml
src/backend/executor/spi.c
src/include/executor/spi.h
src/pl/plperl/plperl.c
src/pl/plpgsql/src/pl_exec.c
src/pl/plpython/plpython.c
src/pl/tcl/pltcl.c

index 4a83aea9b804dbc91bf90501a362a5f75daf4461..bc2e5dc36878fce9826ad0a473d1b3d86c17d99e 100644 (file)
@@ -361,12 +361,16 @@ SPI_execute("INSERT INTO foo SELECT * FROM bar", false, 5);
 
   <para>
    The actual number of rows for which the (last) command was executed
-   is returned in the global variable <varname>SPI_processed</varname>
-   (unless the return value of the function is
-   <symbol>SPI_OK_UTILITY</symbol>).  If the return value of the
-   function is <symbol>SPI_OK_SELECT</symbol> then you may use the
+   is returned in the global variable <varname>SPI_processed</varname>.
+   If the return value of the function is <symbol>SPI_OK_SELECT</symbol>,
+   <symbol>SPI_OK_INSERT_RETURNING</symbol>,
+   <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
-   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>
@@ -459,19 +463,19 @@ typedef struct
     </varlistentry>
 
     <varlistentry>
-     <term><symbol>SPI_OK_DELETE</symbol></term>
+     <term><symbol>SPI_OK_INSERT</symbol></term>
      <listitem>
       <para>
-       if a <command>DELETE</command> was executed
+       if an <command>INSERT</command> was executed
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><symbol>SPI_OK_INSERT</symbol></term>
+     <term><symbol>SPI_OK_DELETE</symbol></term>
      <listitem>
       <para>
-       if an <command>INSERT</command> was executed
+       if a <command>DELETE</command> was executed
       </para>
      </listitem>
     </varlistentry>
@@ -485,6 +489,33 @@ typedef struct
      </listitem>
     </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>
      <term><symbol>SPI_OK_UTILITY</symbol></term>
      <listitem>
@@ -2987,10 +3018,9 @@ execq(text *sql, int cnt)
     
     proc = SPI_processed;
     /*
-     * If this is a SELECT and some rows were fetched,
-     * then the rows are printed via elog(INFO).
+     * If some rows were fetched, print them via elog(INFO).
      */
-    if (ret == SPI_OK_SELECT && SPI_processed &gt; 0)
+    if (ret &gt; 0 &amp;&amp; SPI_tuptable != NULL)
     {
         TupleDesc tupdesc = SPI_tuptable-&gt;tupdesc;
         SPITupleTable *tuptable = SPI_tuptable;
@@ -3005,7 +3035,7 @@ execq(text *sql, int cnt)
                 snprintf(buf + strlen (buf), sizeof(buf) - strlen(buf), " %s%s",
                         SPI_getvalue(tuple, tupdesc, i),
                         (i == tupdesc-&gt;natts) ? " " : " |");
-            elog (INFO, "EXECQ: %s", buf);
+            elog(INFO, "EXECQ: %s", buf);
         }
     }
 
index 5219e8949d19a9ebaafed7a4a8932b4ce0450015..910d68c959f9237b441465946ff917f730ae748e 100644 (file)
@@ -1136,6 +1136,12 @@ SPI_result_code_string(int code)
                        return "SPI_OK_UPDATE";
                case 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 ... */
        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,
                                                                   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;
                                }
                                else
@@ -1542,13 +1551,22 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
                                res = SPI_OK_SELECT;
                        break;
                case CMD_INSERT:
-                       res = SPI_OK_INSERT;
+                       if (queryDesc->parsetree->returningList)
+                               res = SPI_OK_INSERT_RETURNING;
+                       else
+                               res = SPI_OK_INSERT;
                        break;
                case CMD_DELETE:
-                       res = SPI_OK_DELETE;
+                       if (queryDesc->parsetree->returningList)
+                               res = SPI_OK_DELETE_RETURNING;
+                       else
+                               res = SPI_OK_DELETE;
                        break;
                case CMD_UPDATE:
-                       res = SPI_OK_UPDATE;
+                       if (queryDesc->parsetree->returningList)
+                               res = SPI_OK_UPDATE_RETURNING;
+                       else
+                               res = SPI_OK_UPDATE;
                        break;
                default:
                        return SPI_ERROR_OPUNKNOWN;
@@ -1568,7 +1586,8 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
        _SPI_current->processed = queryDesc->estate->es_processed;
        _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())
                        elog(ERROR, "consistency check on SPI tuple count failed");
index f3ee4647931562dee221f19fd8604628446045fb..e57329e53d77af6c20f33aba2a427fb9df712128 100644 (file)
@@ -71,6 +71,9 @@ typedef struct
 #define SPI_OK_DELETE                  8
 #define SPI_OK_UPDATE                  9
 #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 Oid SPI_lastoid;
index 8a6083ec31a68c453c7cc1a5b55561e718a35736..30b02ba8e2376207300370956d7f07a68bc18f1b 100644 (file)
@@ -1630,7 +1630,7 @@ plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed,
        hv_store(result, "processed", strlen("processed"),
                         newSViv(processed), 0);
 
-       if (status == SPI_OK_SELECT)
+       if (status > 0 && tuptable)
        {
                AV                 *rows;
                SV                 *row;
index 5b7c6b56e4635a961bffb58eacb0767e7cbcc725..f26a6ffebf7d3b825f59e499207c312980e525fa 100644 (file)
@@ -2370,23 +2370,16 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
                case SPI_OK_INSERT:
                case SPI_OK_UPDATE:
                case SPI_OK_DELETE:
+               case SPI_OK_INSERT_RETURNING:
+               case SPI_OK_UPDATE_RETURNING:
+               case SPI_OK_DELETE_RETURNING:
                        Assert(stmt->mod_stmt);
                        exec_set_found(estate, (SPI_processed != 0));
                        break;
 
                case SPI_OK_SELINTO:
-                       Assert(!stmt->mod_stmt);
-                       break;
-
                case SPI_OK_UTILITY:
                        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;
 
                default:
@@ -2505,16 +2498,10 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
                case SPI_OK_INSERT:
                case SPI_OK_UPDATE:
                case SPI_OK_DELETE:
-                       break;
-
+               case SPI_OK_INSERT_RETURNING:
+               case SPI_OK_UPDATE_RETURNING:
+               case SPI_OK_DELETE_RETURNING:
                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;
 
                case 0:
index 69a2e644a4e9e871e3d92b0a69e0c77a47602203..de314141856ff554aa4deed72b9116d9b503e867 100644 (file)
@@ -2193,24 +2193,19 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
        Py_DECREF(result->status);
        result->status = PyInt_FromLong(status);
 
-       if (status == SPI_OK_UTILITY)
-       {
-               Py_DECREF(result->nrows);
-               result->nrows = PyInt_FromLong(0);
-       }
-       else if (status != SPI_OK_SELECT)
+       if (status > 0 && tuptable == NULL)
        {
                Py_DECREF(result->nrows);
                result->nrows = PyInt_FromLong(rows);
        }
-       else
+       else if (status > 0 && tuptable != NULL)
        {
                PLyTypeInfo args;
                int                     i;
 
-               PLy_typeinfo_init(&args);
                Py_DECREF(result->nrows);
                result->nrows = PyInt_FromLong(rows);
+               PLy_typeinfo_init(&args);
 
                oldcontext = CurrentMemoryContext;
                PG_TRY();
index d96591f51d5f5b9d4604ea2a4e178343485eff61..57f19f692f1bb681a39fa2869b1e35a94ffeecb0 100644 (file)
@@ -1663,10 +1663,6 @@ pltcl_process_SPI_result(Tcl_Interp *interp,
 
        switch (spi_rc)
        {
-               case SPI_OK_UTILITY:
-                       Tcl_SetResult(interp, "0", TCL_VOLATILE);
-                       break;
-
                case SPI_OK_SELINTO:
                case SPI_OK_INSERT:
                case SPI_OK_DELETE:
@@ -1675,7 +1671,18 @@ pltcl_process_SPI_result(Tcl_Interp *interp,
                        Tcl_SetResult(interp, buf, TCL_VOLATILE);
                        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_INSERT_RETURNING:
+               case SPI_OK_DELETE_RETURNING:
+               case SPI_OK_UPDATE_RETURNING:
 
                        /*
                         * Process the tuples we got