]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/spi.c
Modify all callers of datatype input and receive functions so that if these
[postgresql] / src / backend / executor / spi.c
index f00a2c3c4116cf3e6c14b74dc711f9d98130d596..3563ba23d3ad2f7cbb8f2fed207402b3714936b9 100644 (file)
@@ -3,12 +3,12 @@
  * spi.c
  *                             Server Programming Interface
  *
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.141 2005/06/09 21:25:22 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.150 2006/04/04 19:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,11 +38,11 @@ static int  _SPI_curid = -1;
 static void _SPI_prepare_plan(const char *src, _SPI_plan *plan);
 
 static int _SPI_execute_plan(_SPI_plan *plan,
-                                                        Datum *Values, const char *Nulls,
-                                                        Snapshot snapshot, Snapshot crosscheck_snapshot,
-                                                        bool read_only, long tcount);
+                                 Datum *Values, const char *Nulls,
+                                 Snapshot snapshot, Snapshot crosscheck_snapshot,
+                                 bool read_only, long tcount);
 
-static int _SPI_pquery(QueryDesc *queryDesc, long tcount);
+static int     _SPI_pquery(QueryDesc *queryDesc, long tcount);
 
 static void _SPI_error_callback(void *arg);
 
@@ -66,8 +66,8 @@ SPI_connect(void)
        int                     newdepth;
 
        /*
-        * When procedure called by Executor _SPI_curid expected to be equal
-        * to _SPI_connected
+        * When procedure called by Executor _SPI_curid expected to be equal to
+        * _SPI_connected
         */
        if (_SPI_curid != _SPI_connected)
                return SPI_ERROR_CONNECT;
@@ -104,8 +104,9 @@ SPI_connect(void)
 
        _SPI_current = &(_SPI_stack[_SPI_connected]);
        _SPI_current->processed = 0;
+       _SPI_current->lastoid = InvalidOid;
        _SPI_current->tuptable = NULL;
-       _SPI_current->procCxt = NULL; /* in case we fail to create 'em */
+       _SPI_current->procCxt = NULL;           /* in case we fail to create 'em */
        _SPI_current->execCxt = NULL;
        _SPI_current->connectSubid = GetCurrentSubTransactionId();
 
@@ -113,20 +114,20 @@ SPI_connect(void)
         * Create memory contexts for this procedure
         *
         * XXX it would be better to use PortalContext as the parent context, but
-        * we may not be inside a portal (consider deferred-trigger
-        * execution).  Perhaps CurTransactionContext would do?  For now it
-        * doesn't matter because we clean up explicitly in AtEOSubXact_SPI().
+        * we may not be inside a portal (consider deferred-trigger execution).
+        * Perhaps CurTransactionContext would do?      For now it doesn't matter
+        * because we clean up explicitly in AtEOSubXact_SPI().
         */
        _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
                                                                                                  "SPI Proc",
-                                                                                               ALLOCSET_DEFAULT_MINSIZE,
-                                                                                          ALLOCSET_DEFAULT_INITSIZE,
-                                                                                          ALLOCSET_DEFAULT_MAXSIZE);
+                                                                                                 ALLOCSET_DEFAULT_MINSIZE,
+                                                                                                 ALLOCSET_DEFAULT_INITSIZE,
+                                                                                                 ALLOCSET_DEFAULT_MAXSIZE);
        _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
                                                                                                  "SPI Exec",
-                                                                                               ALLOCSET_DEFAULT_MINSIZE,
-                                                                                          ALLOCSET_DEFAULT_INITSIZE,
-                                                                                          ALLOCSET_DEFAULT_MAXSIZE);
+                                                                                                 ALLOCSET_DEFAULT_MINSIZE,
+                                                                                                 ALLOCSET_DEFAULT_INITSIZE,
+                                                                                                 ALLOCSET_DEFAULT_MAXSIZE);
        /* ... and switch to procedure's context */
        _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
 
@@ -160,9 +161,9 @@ SPI_finish(void)
        SPI_tuptable = NULL;
 
        /*
-        * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
-        * closing connection to SPI and returning to upper Executor and so
-        * _SPI_connected must be equal to _SPI_curid.
+        * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are closing
+        * connection to SPI and returning to upper Executor and so _SPI_connected
+        * must be equal to _SPI_curid.
         */
        _SPI_connected--;
        _SPI_curid--;
@@ -181,9 +182,9 @@ void
 AtEOXact_SPI(bool isCommit)
 {
        /*
-        * Note that memory contexts belonging to SPI stack entries will be
-        * freed automatically, so we can ignore them here.  We just need to
-        * restore our static variables to initial state.
+        * Note that memory contexts belonging to SPI stack entries will be freed
+        * automatically, so we can ignore them here.  We just need to restore our
+        * static variables to initial state.
         */
        if (isCommit && _SPI_connected != -1)
                ereport(WARNING,
@@ -235,8 +236,8 @@ AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
 
                /*
                 * Pop the stack entry and reset global variables.      Unlike
-                * SPI_finish(), we don't risk switching to memory contexts that
-                * might be already gone.
+                * SPI_finish(), we don't risk switching to memory contexts that might
+                * be already gone.
                 */
                _SPI_connected--;
                _SPI_curid = _SPI_connected;
@@ -559,8 +560,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
                mtuple = heap_formtuple(rel->rd_att, v, n);
 
                /*
-                * copy the identification info of the old tuple: t_ctid, t_self,
-                * and OID (if any)
+                * copy the identification info of the old tuple: t_ctid, t_self, and
+                * OID (if any)
                 */
                mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
                mtuple->t_self = tuple->t_self;
@@ -628,9 +629,9 @@ SPI_fname(TupleDesc tupdesc, int fnumber)
 char *
 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
 {
+       char       *result;
        Datum           origval,
-                               val,
-                               result;
+                               val;
        bool            isnull;
        Oid                     typoid,
                                foutoid;
@@ -657,22 +658,21 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
        getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
 
        /*
-        * If we have a toasted datum, forcibly detoast it here to avoid
-        * memory leakage inside the type's output routine.
+        * If we have a toasted datum, forcibly detoast it here to avoid memory
+        * leakage inside the type's output routine.
         */
        if (typisvarlena)
                val = PointerGetDatum(PG_DETOAST_DATUM(origval));
        else
                val = origval;
 
-       result = OidFunctionCall1(foutoid,
-                                                         val);
+       result = OidOutputFunctionCall(foutoid, val);
 
        /* Clean up detoasted copy, if any */
        if (val != origval)
                pfree(DatumGetPointer(val));
 
-       return DatumGetCString(result);
+       return result;
 }
 
 Datum
@@ -754,7 +754,7 @@ SPI_getrelname(Relation rel)
 char *
 SPI_getnspname(Relation rel)
 {
-    return get_namespace_name(RelationGetNamespace(rel));
+       return get_namespace_name(RelationGetNamespace(rel));
 }
 
 void *
@@ -859,7 +859,7 @@ SPI_cursor_open(const char *name, void *plan,
                        break;
        }
 
-       /* Reset SPI result */
+       /* Reset SPI result (note we deliberately don't touch lastoid) */
        SPI_processed = 0;
        SPI_tuptable = NULL;
        _SPI_current->processed = 0;
@@ -920,8 +920,8 @@ SPI_cursor_open(const char *name, void *plan,
         * Set up the portal.
         */
        PortalDefineQuery(portal,
-                                         NULL,         /* unfortunately don't have sourceText */
-                                         "SELECT", /* nor the raw parse tree... */
+                                         spiplan->query,
+                                         "SELECT", /* don't have the raw parse tree... */
                                          list_make1(queryTree),
                                          list_make1(planTree),
                                          PortalGetHeapMemory(portal));
@@ -938,8 +938,8 @@ SPI_cursor_open(const char *name, void *plan,
                portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
 
        /*
-        * Set up the snapshot to use.  (PortalStart will do CopySnapshot,
-        * so we skip that here.)
+        * Set up the snapshot to use.  (PortalStart will do CopySnapshot, so we
+        * skip that here.)
         */
        if (read_only)
                snapshot = ActiveSnapshot;
@@ -983,8 +983,8 @@ void
 SPI_cursor_fetch(Portal portal, bool forward, long count)
 {
        _SPI_cursor_operation(portal, forward, count,
-                                                 CreateDestReceiver(SPI, NULL));
-       /* we know that the SPI receiver doesn't need a destroy call */
+                                                 CreateDestReceiver(DestSPI, NULL));
+       /* we know that the DestSPI receiver doesn't need a destroy call */
 }
 
 
@@ -1213,7 +1213,7 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
                tuptable->free = 256;
                tuptable->alloced += tuptable->free;
                tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
-                                                                 tuptable->alloced * sizeof(HeapTuple));
+                                                                         tuptable->alloced * sizeof(HeapTuple));
        }
 
        tuptable->vals[tuptable->alloced - tuptable->free] =
@@ -1246,9 +1246,9 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
        int                     nargs = plan->nargs;
 
        /*
-        * Increment CommandCounter to see changes made by now.  We must do
-        * this to be sure of seeing any schema changes made by a just-preceding
-        * SPI command.  (But we don't bother advancing the snapshot, since the
+        * Increment CommandCounter to see changes made by now.  We must do this
+        * to be sure of seeing any schema changes made by a just-preceding SPI
+        * command.  (But we don't bother advancing the snapshot, since the
         * planner generally operates under SnapshotNow rules anyway.)
         */
        CommandCounterIncrement();
@@ -1269,9 +1269,9 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
        /*
         * Do parse analysis and rule rewrite for each raw parsetree.
         *
-        * We save the querytrees from each raw parsetree as a separate
-        * sublist.  This allows _SPI_execute_plan() to know where the
-        * boundaries between original queries fall.
+        * We save the querytrees from each raw parsetree as a separate sublist.
+        * This allows _SPI_execute_plan() to know where the boundaries between
+        * original queries fall.
         */
        query_list_list = NIL;
        plan_list = NIL;
@@ -1281,7 +1281,7 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
                Node       *parsetree = (Node *) lfirst(list_item);
                List       *query_list;
 
-               query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
+               query_list = pg_analyze_and_rewrite(parsetree, src, argtypes, nargs);
 
                query_list_list = lappend(query_list_list, query_list);
 
@@ -1313,6 +1313,9 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                                  bool read_only, long tcount)
 {
        volatile int res = 0;
+       volatile uint32 my_processed = 0;
+       volatile Oid my_lastoid = InvalidOid;
+       SPITupleTable *volatile my_tuptable = NULL;
        Snapshot        saveActiveSnapshot;
 
        /* Be sure to restore ActiveSnapshot on error exit */
@@ -1347,12 +1350,6 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                else
                        paramLI = NULL;
 
-               /* Reset state (only needed in case string is empty) */
-               SPI_processed = 0;
-               SPI_lastoid = InvalidOid;
-               SPI_tuptable = NULL;
-               _SPI_current->tuptable = NULL;
-
                /*
                 * Setup error traceback support for ereport()
                 */
@@ -1366,13 +1363,6 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                        List       *query_list = lfirst(query_list_list_item);
                        ListCell   *query_list_item;
 
-                       /* Reset state for each original parsetree */
-                       /* (at most one of its querytrees will be marked canSetTag) */
-                       SPI_processed = 0;
-                       SPI_lastoid = InvalidOid;
-                       SPI_tuptable = NULL;
-                       _SPI_current->tuptable = NULL;
-
                        foreach(query_list_item, query_list)
                        {
                                Query      *queryTree = (Query *) lfirst(query_list_item);
@@ -1383,6 +1373,10 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                                planTree = lfirst(plan_list_item);
                                plan_list_item = lnext(plan_list_item);
 
+                               _SPI_current->processed = 0;
+                               _SPI_current->lastoid = InvalidOid;
+                               _SPI_current->tuptable = NULL;
+
                                if (queryTree->commandType == CMD_UTILITY)
                                {
                                        if (IsA(queryTree->utilityStmt, CopyStmt))
@@ -1412,9 +1406,10 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                                if (read_only && !QueryIsReadOnly(queryTree))
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                        /* translator: %s is a SQL statement name */
-                                                        errmsg("%s is not allowed in a non-volatile function",
-                                                                       CreateQueryTag(queryTree))));
+                                       /* translator: %s is a SQL statement name */
+                                          errmsg("%s is not allowed in a non-volatile function",
+                                                         CreateQueryTag(queryTree))));
+
                                /*
                                 * If not read-only mode, advance the command counter before
                                 * each command.
@@ -1422,7 +1417,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                                if (!read_only)
                                        CommandCounterIncrement();
 
-                               dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None,
+                               dest = CreateDestReceiver(queryTree->canSetTag ? DestSPI : DestNone,
                                                                                  NULL);
 
                                if (snapshot == InvalidSnapshot)
@@ -1467,6 +1462,24 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                                }
                                FreeSnapshot(ActiveSnapshot);
                                ActiveSnapshot = NULL;
+
+                               /*
+                                * The last canSetTag query sets the auxiliary values returned
+                                * to the caller.  Be careful to free any tuptables not
+                                * returned, to avoid intratransaction memory leak.
+                                */
+                               if (queryTree->canSetTag)
+                               {
+                                       my_processed = _SPI_current->processed;
+                                       my_lastoid = _SPI_current->lastoid;
+                                       SPI_freetuptable(my_tuptable);
+                                       my_tuptable = _SPI_current->tuptable;
+                               }
+                               else
+                               {
+                                       SPI_freetuptable(_SPI_current->tuptable);
+                                       _SPI_current->tuptable = NULL;
+                               }
                                /* we know that the receiver doesn't need a destroy call */
                                if (res < 0)
                                        goto fail;
@@ -1490,6 +1503,11 @@ fail:
 
        ActiveSnapshot = saveActiveSnapshot;
 
+       /* Save results for caller */
+       SPI_processed = my_processed;
+       SPI_lastoid = my_lastoid;
+       SPI_tuptable = my_tuptable;
+
        return res;
 }
 
@@ -1497,18 +1515,21 @@ static int
 _SPI_pquery(QueryDesc *queryDesc, long tcount)
 {
        int                     operation = queryDesc->operation;
-       CommandDest     origDest = queryDesc->dest->mydest;
        int                     res;
-       Oid                     save_lastoid;
 
        switch (operation)
        {
                case CMD_SELECT:
                        res = SPI_OK_SELECT;
-                       if (queryDesc->parsetree->into)                 /* select into table? */
+                       if (queryDesc->parsetree->into)         /* select into table? */
                        {
                                res = SPI_OK_SELINTO;
-                               queryDesc->dest = None_Receiver;        /* don't output results */
+                               queryDesc->dest = None_Receiver;                /* don't output results */
+                       }
+                       else if (queryDesc->dest->mydest != DestSPI)
+                       {
+                               /* Don't return SPI_OK_SELECT if we're discarding result */
+                               res = SPI_OK_UTILITY;
                        }
                        break;
                case CMD_INSERT:
@@ -1531,14 +1552,14 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
 
        AfterTriggerBeginQuery();
 
-       ExecutorStart(queryDesc, false);
+       ExecutorStart(queryDesc, 0);
 
        ExecutorRun(queryDesc, ForwardScanDirection, tcount);
 
        _SPI_current->processed = queryDesc->estate->es_processed;
-       save_lastoid = queryDesc->estate->es_lastoid;
+       _SPI_current->lastoid = queryDesc->estate->es_lastoid;
 
-       if (operation == CMD_SELECT && queryDesc->dest->mydest == SPI)
+       if (operation == CMD_SELECT && queryDesc->dest->mydest == DestSPI)
        {
                if (_SPI_checktuples())
                        elog(ERROR, "consistency check on SPI tuple count failed");
@@ -1549,19 +1570,6 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
 
        ExecutorEnd(queryDesc);
 
-       /* Test origDest here so that SPI_processed gets set in SELINTO case */
-       if (origDest == SPI)
-       {
-               SPI_processed = _SPI_current->processed;
-               SPI_lastoid = save_lastoid;
-               SPI_tuptable = _SPI_current->tuptable;
-       }
-       else if (res == SPI_OK_SELECT)
-       {
-               /* Don't return SPI_OK_SELECT if we discarded the result */
-               res = SPI_OK_UTILITY;
-       }
-
 #ifdef SPI_EXECUTOR_STATS
        if (ShowExecutorStats)
                ShowUsage("SPI EXECUTOR STATS");
@@ -1582,8 +1590,8 @@ _SPI_error_callback(void *arg)
        int                     syntaxerrposition;
 
        /*
-        * If there is a syntax error position, convert to internal syntax
-        * error; otherwise treat the query as an item of context stack
+        * If there is a syntax error position, convert to internal syntax error;
+        * otherwise treat the query as an item of context stack
         */
        syntaxerrposition = geterrposition();
        if (syntaxerrposition > 0)
@@ -1615,7 +1623,7 @@ _SPI_cursor_operation(Portal portal, bool forward, long count,
        if (_SPI_begin_call(true) < 0)
                elog(ERROR, "SPI cursor operation called while not connected");
 
-       /* Reset the SPI result */
+       /* Reset the SPI result (note we deliberately don't touch lastoid) */
        SPI_processed = 0;
        SPI_tuptable = NULL;
        _SPI_current->processed = 0;
@@ -1628,17 +1636,16 @@ _SPI_cursor_operation(Portal portal, bool forward, long count,
                                                          dest);
 
        /*
-        * Think not to combine this store with the preceding function call.
-        * If the portal contains calls to functions that use SPI, then
-        * SPI_stack is likely to move around while the portal runs.  When
-        * control returns, _SPI_current will point to the correct stack
-        * entry... but the pointer may be different than it was beforehand.
-        * So we must be sure to re-fetch the pointer after the function call
-        * completes.
+        * Think not to combine this store with the preceding function call. If
+        * the portal contains calls to functions that use SPI, then SPI_stack is
+        * likely to move around while the portal runs.  When control returns,
+        * _SPI_current will point to the correct stack entry... but the pointer
+        * may be different than it was beforehand. So we must be sure to re-fetch
+        * the pointer after the function call completes.
         */
        _SPI_current->processed = nfetched;
 
-       if (dest->mydest == SPI && _SPI_checktuples())
+       if (dest->mydest == DestSPI && _SPI_checktuples())
                elog(ERROR, "consistency check on SPI tuple count failed");
 
        /* Put the result into place for access by caller */
@@ -1731,12 +1738,13 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
                parentcxt = _SPI_current->procCxt;
        else if (location == _SPI_CPLAN_TOPCXT)
                parentcxt = TopMemoryContext;
-       else    /* (this case not currently used) */
+       else
+               /* (this case not currently used) */
                parentcxt = CurrentMemoryContext;
 
        /*
-        * Create a memory context for the plan.  We don't expect the plan to
-        * be very large, so use smaller-than-default alloc parameters.
+        * Create a memory context for the plan.  We don't expect the plan to be
+        * very large, so use smaller-than-default alloc parameters.
         */
        plancxt = AllocSetContextCreate(parentcxt,
                                                                        "SPI Plan",