*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
_SPI_connected++;
_SPI_current = &(_SPI_stack[_SPI_connected]);
- _SPI_current->qtlist = NULL;
_SPI_current->processed = 0;
_SPI_current->tuptable = NULL;
_SPI_end_call(true);
return (void *) plan;
-
}
void *
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)
* 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;
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();
}
}
return SPI_ERROR_OPUNKNOWN;
}
- if (state == NULL) /* plan preparation */
+ if (state == NULL) /* plan preparation, don't execute */
return res;
#ifdef SPI_EXECUTOR_STATS
_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();
}
else
newplan->argtypes = NULL;
+ newplan->origCmdType = plan->origCmdType;
MemoryContextSwitchTo(oldcxt);