From: Tom Lane Date: Mon, 14 Oct 2002 23:49:20 +0000 (+0000) Subject: Make SPI's execution of querystrings follow the rules agreed to for X-Git-Tag: REL7_3~285 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9ff695c944e7a60792f38302076826eaf54d4031;p=thirdparty%2Fpostgresql.git 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. --- 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);