aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-10-14 23:49:20 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-10-14 23:49:20 +0000
commit9ff695c944e7a60792f38302076826eaf54d4031 (patch)
tree6e8cc2f7a857b9cc05e6c7101b3e4d178d8afa2b
parentba0edcf451b992d064f311337bea2ddfbe7333db (diff)
downloadpostgresql-9ff695c944e7a60792f38302076826eaf54d4031.tar.gz
postgresql-9ff695c944e7a60792f38302076826eaf54d4031.zip
Make SPI's execution of querystrings follow the rules agreed to for
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.
-rw-r--r--src/backend/executor/spi.c329
-rw-r--r--src/backend/tcop/postgres.c10
-rw-r--r--src/include/executor/spi_priv.h16
-rw-r--r--src/include/tcop/tcopprot.h5
4 files changed, 233 insertions, 127 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 273fe6fee18..fe26df84670 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -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);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 9538b34a4e9..ea1b0cb3714 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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");
}
/*
diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h
index c780c3656fa..6a2acd7b21c 100644
--- a/src/include/executor/spi_priv.h
+++ b/src/include/executor/spi_priv.h
@@ -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
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 7123f493a96..ae909eb8af5 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -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);