Make SPI's execution of querystrings follow the rules agreed to for
authorTom Lane
Mon, 14 Oct 2002 23:49:20 +0000 (23:49 +0000)
committerTom Lane
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);