Make SPI's execution of querystrings follow the rules agreed to for
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 14 Oct 2002 23:49:20 +0000 (23:49 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 14 Oct 2002 23:49:20 +0000 (23:49 +0000)
command status at the interactive level.  SPI_processed, etc are set
in the same way as the returned command status would have been set if
the same querystring were issued interactively.  Per gripe from
Michael Paesold 25-Sep-02.

src/backend/executor/spi.c
src/backend/tcop/postgres.c
src/include/executor/spi_priv.h
src/include/tcop/tcopprot.h

index 273fe6fee18122e8df5ee94cdea7a24df658f92b..fe26df84670b3f0b46b4a2c30818dea9e8a92e5e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.74 2002/09/04 20:31:18 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.75 2002/10/14 23:49:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,7 +88,6 @@ SPI_connect(void)
        _SPI_connected++;
 
        _SPI_current = &(_SPI_stack[_SPI_connected]);
-       _SPI_current->qtlist = NULL;
        _SPI_current->processed = 0;
        _SPI_current->tuptable = NULL;
 
@@ -258,7 +257,6 @@ SPI_prepare(char *src, int nargs, Oid *argtypes)
        _SPI_end_call(true);
 
        return (void *) plan;
-
 }
 
 void *
@@ -716,9 +714,9 @@ SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls)
        int                     k;
 
        /* Ensure that the plan contains only one regular SELECT query */
-       if (length(ptlist) != 1)
+       if (length(ptlist) != 1 || length(qtlist) != 1)
                elog(ERROR, "cannot open multi-query plan as cursor");
-       queryTree = (Query *) lfirst(qtlist);
+       queryTree = (Query *) lfirst((List *) lfirst(qtlist));
        planTree = (Plan *) lfirst(ptlist);
 
        if (queryTree->commandType != CMD_SELECT)
@@ -948,102 +946,172 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
  * Static functions
  */
 
+/*
+ * Plan and optionally execute a querystring.
+ *
+ * If plan != NULL, just prepare plan tree, else execute immediately.
+ */
 static int
 _SPI_execute(char *src, int tcount, _SPI_plan *plan)
 {
-       List       *queryTree_list;
-       List       *planTree_list;
-       List       *queryTree_list_item;
-       QueryDesc  *qdesc;
-       Query      *queryTree;
-       Plan       *planTree;
-       EState     *state;
+       StringInfoData stri;
+       List       *raw_parsetree_list;
+       List       *query_list_list;
+       List       *plan_list;
+       List       *list_item;
        int                     nargs = 0;
        Oid                *argtypes = NULL;
        int                     res = 0;
-       bool            islastquery;
+
+       if (plan)
+       {
+               nargs = plan->nargs;
+               argtypes = plan->argtypes;
+       }
 
        /* Increment CommandCounter to see changes made by now */
        CommandCounterIncrement();
 
+       /* Reset state (only needed in case string is empty) */
        SPI_processed = 0;
        SPI_lastoid = InvalidOid;
        SPI_tuptable = NULL;
        _SPI_current->tuptable = NULL;
-       _SPI_current->qtlist = NULL;
 
-       if (plan)
+       /*
+        * Parse the request string into a list of raw parse trees.
+        */
+       initStringInfo(&stri);
+       appendStringInfo(&stri, "%s", src);
+
+       raw_parsetree_list = pg_parse_query(&stri, argtypes, nargs);
+
+       /*
+        * 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.
+        */
+       query_list_list = NIL;
+       plan_list = NIL;
+
+       foreach(list_item, raw_parsetree_list)
        {
-               nargs = plan->nargs;
-               argtypes = plan->argtypes;
-       }
+               Node       *parsetree = (Node *) lfirst(list_item);
+               CmdType         origCmdType;
+               bool            foundOriginalQuery = false;
+               List       *query_list;
+               List       *query_list_item;
 
-       queryTree_list = pg_parse_and_rewrite(src, argtypes, nargs);
+               switch (nodeTag(parsetree))
+               {
+                       case T_InsertStmt:
+                               origCmdType = CMD_INSERT;
+                               break;
+                       case T_DeleteStmt:
+                               origCmdType = CMD_DELETE;
+                               break;
+                       case T_UpdateStmt:
+                               origCmdType = CMD_UPDATE;
+                               break;
+                       case T_SelectStmt:
+                               origCmdType = CMD_SELECT;
+                               break;
+                       default:
+                               /* Otherwise, never match commandType */
+                               origCmdType = CMD_UNKNOWN;
+                               break;
+               }
 
-       _SPI_current->qtlist = queryTree_list;
+               if (plan)
+                       plan->origCmdType = origCmdType;
 
-       planTree_list = NIL;
+               query_list = pg_analyze_and_rewrite(parsetree);
 
-       foreach(queryTree_list_item, queryTree_list)
-       {
-               queryTree = (Query *) lfirst(queryTree_list_item);
-               islastquery = (lnext(queryTree_list_item) == NIL);
+               query_list_list = lappend(query_list_list, query_list);
 
-               planTree = pg_plan_query(queryTree);
-               planTree_list = lappend(planTree_list, planTree);
+               /* Reset state for each original parsetree */
+               SPI_processed = 0;
+               SPI_lastoid = InvalidOid;
+               SPI_tuptable = NULL;
+               _SPI_current->tuptable = NULL;
 
-               if (queryTree->commandType == CMD_UTILITY)
+               foreach(query_list_item, query_list)
                {
-                       if (nodeTag(queryTree->utilityStmt) == T_CopyStmt)
+                       Query      *queryTree = (Query *) lfirst(query_list_item);
+                       Plan       *planTree;
+                       bool            canSetResult;
+                       QueryDesc  *qdesc;
+                       EState     *state;
+
+                       planTree = pg_plan_query(queryTree);
+                       plan_list = lappend(plan_list, planTree);
+
+                       /*
+                        * This query can set the SPI result if it is the original
+                        * query, or if it is an INSTEAD query of the same kind as the
+                        * original and we haven't yet seen the original query.
+                        */
+                       if (queryTree->querySource == QSRC_ORIGINAL)
                        {
-                               CopyStmt   *stmt = (CopyStmt *) (queryTree->utilityStmt);
-
-                               if (stmt->filename == NULL)
-                                       return SPI_ERROR_COPY;
+                               canSetResult = true;
+                               foundOriginalQuery = true;
                        }
-                       else if (nodeTag(queryTree->utilityStmt) == T_ClosePortalStmt ||
-                                        nodeTag(queryTree->utilityStmt) == T_FetchStmt)
-                               return SPI_ERROR_CURSOR;
-                       else if (nodeTag(queryTree->utilityStmt) == T_TransactionStmt)
-                               return SPI_ERROR_TRANSACTION;
-                       res = SPI_OK_UTILITY;
-                       if (plan == NULL)
+                       else if (!foundOriginalQuery &&
+                                        queryTree->commandType == origCmdType &&
+                                        (queryTree->querySource == QSRC_INSTEAD_RULE ||
+                                         queryTree->querySource == QSRC_QUAL_INSTEAD_RULE))
+                               canSetResult = true;
+                       else
+                               canSetResult = false;
+
+                       if (queryTree->commandType == CMD_UTILITY)
                        {
-                               ProcessUtility(queryTree->utilityStmt, None, NULL);
-                               if (!islastquery)
+                               if (IsA(queryTree->utilityStmt, CopyStmt))
+                               {
+                                       CopyStmt   *stmt = (CopyStmt *) queryTree->utilityStmt;
+
+                                       if (stmt->filename == NULL)
+                                               return SPI_ERROR_COPY;
+                               }
+                               else if (IsA(queryTree->utilityStmt, ClosePortalStmt) ||
+                                                IsA(queryTree->utilityStmt, FetchStmt))
+                                       return SPI_ERROR_CURSOR;
+                               else if (IsA(queryTree->utilityStmt, TransactionStmt))
+                                       return SPI_ERROR_TRANSACTION;
+                               res = SPI_OK_UTILITY;
+                               if (plan == NULL)
+                               {
+                                       ProcessUtility(queryTree->utilityStmt, None, NULL);
                                        CommandCounterIncrement();
-                               else
+                               }
+                       }
+                       else if (plan == NULL)
+                       {
+                               qdesc = CreateQueryDesc(queryTree, planTree,
+                                                                               canSetResult ? SPI : None, NULL);
+                               state = CreateExecutorState();
+                               res = _SPI_pquery(qdesc, state, canSetResult ? tcount : 0);
+                               if (res < 0)
+                                       return res;
+                               CommandCounterIncrement();
+                       }
+                       else
+                       {
+                               qdesc = CreateQueryDesc(queryTree, planTree,
+                                                                               canSetResult ? SPI : None, NULL);
+                               res = _SPI_pquery(qdesc, NULL, 0);
+                               if (res < 0)
                                        return res;
                        }
-                       else if (islastquery)
-                               break;
-               }
-               else if (plan == NULL)
-               {
-                       qdesc = CreateQueryDesc(queryTree, planTree,
-                                                                       islastquery ? SPI : None, NULL);
-                       state = CreateExecutorState();
-                       res = _SPI_pquery(qdesc, state, islastquery ? tcount : 0);
-                       if (res < 0 || islastquery)
-                               return res;
-                       CommandCounterIncrement();
-               }
-               else
-               {
-                       qdesc = CreateQueryDesc(queryTree, planTree,
-                                                                       islastquery ? SPI : None, NULL);
-                       res = _SPI_pquery(qdesc, NULL, islastquery ? tcount : 0);
-                       if (res < 0)
-                               return res;
-                       if (islastquery)
-                               break;
                }
        }
 
        if (plan)
        {
-               plan->qtlist = queryTree_list;
-               plan->ptlist = planTree_list;
+               plan->qtlist = query_list_list;
+               plan->ptlist = plan_list;
        }
 
        return res;
@@ -1052,72 +1120,100 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
 static int
 _SPI_execute_plan(_SPI_plan *plan, Datum *Values, char *Nulls, int tcount)
 {
-       List       *queryTree_list = plan->qtlist;
-       List       *planTree_list = plan->ptlist;
-       List       *queryTree_list_item;
-       QueryDesc  *qdesc;
-       Query      *queryTree;
-       Plan       *planTree;
-       EState     *state;
+       List       *query_list_list = plan->qtlist;
+       List       *plan_list = plan->ptlist;
+       List       *query_list_list_item;
        int                     nargs = plan->nargs;
        int                     res = 0;
-       bool            islastquery;
-       int                     k;
 
        /* Increment CommandCounter to see changes made by now */
        CommandCounterIncrement();
 
+       /* Reset state (only needed in case string is empty) */
        SPI_processed = 0;
        SPI_lastoid = InvalidOid;
        SPI_tuptable = NULL;
        _SPI_current->tuptable = NULL;
-       _SPI_current->qtlist = NULL;
 
-       foreach(queryTree_list_item, queryTree_list)
+       foreach(query_list_list_item, query_list_list)
        {
-               queryTree = (Query *) lfirst(queryTree_list_item);
-               planTree = lfirst(planTree_list);
-               planTree_list = lnext(planTree_list);
-               islastquery = (planTree_list == NIL);   /* assume lists are same
-                                                                                                * len */
+               List   *query_list = lfirst(query_list_list_item);
+               List   *query_list_item;
+               bool    foundOriginalQuery = false;
 
-               if (queryTree->commandType == CMD_UTILITY)
+               /* Reset state for each original parsetree */
+               SPI_processed = 0;
+               SPI_lastoid = InvalidOid;
+               SPI_tuptable = NULL;
+               _SPI_current->tuptable = NULL;
+
+               foreach(query_list_item, query_list)
                {
-                       ProcessUtility(queryTree->utilityStmt, None, NULL);
-                       if (!islastquery)
+                       Query  *queryTree = (Query *) lfirst(query_list_item);
+                       Plan       *planTree;
+                       bool            canSetResult;
+                       QueryDesc  *qdesc;
+                       EState     *state;
+
+                       planTree = lfirst(plan_list);
+                       plan_list = lnext(plan_list);
+
+                       /*
+                        * This query can set the SPI result if it is the original
+                        * query, or if it is an INSTEAD query of the same kind as the
+                        * original and we haven't yet seen the original query.
+                        */
+                       if (queryTree->querySource == QSRC_ORIGINAL)
+                       {
+                               canSetResult = true;
+                               foundOriginalQuery = true;
+                       }
+                       else if (!foundOriginalQuery &&
+                                        queryTree->commandType == plan->origCmdType &&
+                                        (queryTree->querySource == QSRC_INSTEAD_RULE ||
+                                         queryTree->querySource == QSRC_QUAL_INSTEAD_RULE))
+                               canSetResult = true;
+                       else
+                               canSetResult = false;
+
+                       if (queryTree->commandType == CMD_UTILITY)
+                       {
+                               res = SPI_OK_UTILITY;
+                               ProcessUtility(queryTree->utilityStmt, None, NULL);
                                CommandCounterIncrement();
+                       }
                        else
-                               return SPI_OK_UTILITY;
-               }
-               else
-               {
-                       qdesc = CreateQueryDesc(queryTree, planTree,
-                                                                       islastquery ? SPI : None, NULL);
-                       state = CreateExecutorState();
-                       if (nargs > 0)
                        {
-                               ParamListInfo paramLI;
-
-                               paramLI = (ParamListInfo) palloc((nargs + 1) *
-                                                                                         sizeof(ParamListInfoData));
-                               MemSet(paramLI, 0, (nargs + 1) * sizeof(ParamListInfoData));
-
-                               state->es_param_list_info = paramLI;
-                               for (k = 0; k < plan->nargs; paramLI++, k++)
+                               qdesc = CreateQueryDesc(queryTree, planTree,
+                                                                               canSetResult ? SPI : None, NULL);
+                               state = CreateExecutorState();
+                               if (nargs > 0)
                                {
-                                       paramLI->kind = PARAM_NUM;
-                                       paramLI->id = k + 1;
-                                       paramLI->isnull = (Nulls && Nulls[k] == 'n');
-                                       paramLI->value = Values[k];
+                                       ParamListInfo paramLI;
+                                       int                     k;
+
+                                       paramLI = (ParamListInfo)
+                                               palloc((nargs + 1) * sizeof(ParamListInfoData));
+                                       MemSet(paramLI, 0,
+                                                  (nargs + 1) * sizeof(ParamListInfoData));
+
+                                       state->es_param_list_info = paramLI;
+                                       for (k = 0; k < plan->nargs; paramLI++, k++)
+                                       {
+                                               paramLI->kind = PARAM_NUM;
+                                               paramLI->id = k + 1;
+                                               paramLI->isnull = (Nulls && Nulls[k] == 'n');
+                                               paramLI->value = Values[k];
+                                       }
+                                       paramLI->kind = PARAM_INVALID;
                                }
-                               paramLI->kind = PARAM_INVALID;
+                               else
+                                       state->es_param_list_info = NULL;
+                               res = _SPI_pquery(qdesc, state, canSetResult ? tcount : 0);
+                               if (res < 0)
+                                       return res;
+                               CommandCounterIncrement();
                        }
-                       else
-                               state->es_param_list_info = NULL;
-                       res = _SPI_pquery(qdesc, state, islastquery ? tcount : 0);
-                       if (res < 0 || islastquery)
-                               return res;
-                       CommandCounterIncrement();
                }
        }
 
@@ -1169,7 +1265,7 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
                        return SPI_ERROR_OPUNKNOWN;
        }
 
-       if (state == NULL)                      /* plan preparation */
+       if (state == NULL)                      /* plan preparation, don't execute */
                return res;
 
 #ifdef SPI_EXECUTOR_STATS
@@ -1340,12 +1436,10 @@ static int
 _SPI_end_call(bool procmem)
 {
        /*
-        * We' returning to procedure where _SPI_curid == _SPI_connected - 1
+        * We're returning to procedure where _SPI_curid == _SPI_connected - 1
         */
        _SPI_curid--;
 
-       _SPI_current->qtlist = NULL;
-
        if (procmem)                            /* switch to the procedure memory context */
        {
                _SPI_procmem();
@@ -1420,6 +1514,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
        }
        else
                newplan->argtypes = NULL;
+       newplan->origCmdType = plan->origCmdType;
 
        MemoryContextSwitchTo(oldcxt);
 
index 9538b34a4e9e55266fd386f8f8a8b61c2d68123b..ea1b0cb3714105e8e003f953a07ebf0a5ba2340d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.302 2002/10/14 22:14:35 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.303 2002/10/14 23:49:20 tgl Exp $
  *
  * NOTES
  *       this is the "main" module of the postgres backend and
@@ -119,8 +119,6 @@ int                 XfuncMode = 0;
 static int     InteractiveBackend(StringInfo inBuf);
 static int     SocketBackend(StringInfo inBuf);
 static int     ReadCommand(StringInfo inBuf);
-static List *pg_parse_query(StringInfo query_string, Oid *typev, int nargs);
-static List *pg_analyze_and_rewrite(Node *parsetree);
 static void start_xact_command(void);
 static void finish_xact_command(bool forceCommit);
 static void SigHupHandler(SIGNAL_ARGS);
@@ -367,7 +365,7 @@ pg_parse_and_rewrite(char *query_string,            /* string to execute */
  * we've seen a COMMIT or ABORT command; when we are in abort state, other
  * commands are not processed any further than the raw parse stage.
  */
-static List *
+List *
 pg_parse_query(StringInfo query_string, Oid *typev, int nargs)
 {
        List       *raw_parsetree_list;
@@ -395,7 +393,7 @@ pg_parse_query(StringInfo query_string, Oid *typev, int nargs)
  *
  * NOTE: for reasons mentioned above, this must be separate from raw parsing.
  */
-static List *
+List *
 pg_analyze_and_rewrite(Node *parsetree)
 {
        List       *querytree_list;
@@ -1769,7 +1767,7 @@ PostgresMain(int argc, char *argv[], const char *username)
        if (!IsUnderPostmaster)
        {
                puts("\nPOSTGRES backend interactive interface ");
-               puts("$Revision: 1.302 $ $Date: 2002/10/14 22:14:35 $\n");
+               puts("$Revision: 1.303 $ $Date: 2002/10/14 23:49:20 $\n");
        }
 
        /*
index c780c3656fa9423f379ca489df37a8f716b80086..6a2acd7b21c680299eccdc816f2bad838e7beb56 100644 (file)
@@ -1,9 +1,12 @@
 /*-------------------------------------------------------------------------
  *
- * spi.c
+ * spi_priv.h
  *                             Server Programming Interface private declarations
  *
- * $Header: /cvsroot/pgsql/src/include/executor/spi_priv.h,v 1.12 2002/05/21 22:05:55 tgl Exp $
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: spi_priv.h,v 1.13 2002/10/14 23:49:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -12,9 +15,9 @@
 
 #include "executor/spi.h"
 
+
 typedef struct
 {
-       List       *qtlist;
        uint32          processed;              /* by Executor */
        SPITupleTable *tuptable;
        MemoryContext procCxt;          /* procedure context */
@@ -24,13 +27,20 @@ typedef struct
 
 typedef struct
 {
+       /* context containing _SPI_plan itself as well as subsidiary structures */
        MemoryContext plancxt;
+       /* List of List of querytrees; one sublist per original parsetree */
        List       *qtlist;
+       /* List of plan trees --- length == # of querytrees, but flat list */
        List       *ptlist;
+       /* Argument types, if a prepared plan */
        int                     nargs;
        Oid                *argtypes;
+       /* Command type of last original parsetree */
+       CmdType         origCmdType;
 } _SPI_plan;
 
+
 #define _SPI_CPLAN_CURCXT      0
 #define _SPI_CPLAN_PROCXT      1
 #define _SPI_CPLAN_TOPCXT      2
index 7123f493a9676f26d98fb23116386f635bd14062..ae909eb8af51ccbd41f2f124ef93941b127180fc 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tcopprot.h,v 1.49 2002/06/20 20:29:52 momjian Exp $
+ * $Id: tcopprot.h,v 1.50 2002/10/14 23:49:20 tgl Exp $
  *
  * OLD COMMENTS
  *       This file was created so that other c files could get the two
@@ -35,12 +35,15 @@ extern bool ShowPortNumber;
 
 #ifndef BOOTSTRAP_INCLUDE
 
+extern List *pg_parse_query(StringInfo query_string, Oid *typev, int nargs);
+extern List *pg_analyze_and_rewrite(Node *parsetree);
 extern List *pg_parse_and_rewrite(char *query_string,
                                         Oid *typev, int nargs);
 extern Plan *pg_plan_query(Query *querytree);
 extern void pg_exec_query_string(StringInfo query_string,
                                         CommandDest dest,
                                         MemoryContext parse_context);
+
 #endif   /* BOOTSTRAP_INCLUDE */
 
 extern void die(SIGNAL_ARGS);