diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2007-02-20 17:32:18 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2007-02-20 17:32:18 +0000 |
commit | 9cbd0c155d1602aad879f510256b626c58942080 (patch) | |
tree | 6d5504a4e841313d3a29cead80067006dc408e39 /src/backend/executor | |
parent | 71b0cf2f6bec3129f2c3f574d4e47408c2dc2516 (diff) | |
download | postgresql-9cbd0c155d1602aad879f510256b626c58942080.tar.gz postgresql-9cbd0c155d1602aad879f510256b626c58942080.zip |
Remove the Query structure from the executor's API. This allows us to stop
storing mostly-redundant Query trees in prepared statements, portals, etc.
To replace Query, a new node type called PlannedStmt is inserted by the
planner at the top of a completed plan tree; this carries just the fields of
Query that are still needed at runtime. The statement lists kept in portals
etc. now consist of intermixed PlannedStmt and bare utility-statement nodes
--- no Query. This incidentally allows us to remove some fields from Query
and Plan nodes that shouldn't have been there in the first place.
Still to do: simplify the execution-time range table; at the moment the
range table passed to the executor still contains Query trees for subqueries.
initdb forced due to change of stored rules.
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execMain.c | 154 | ||||
-rw-r--r-- | src/backend/executor/execUtils.c | 4 | ||||
-rw-r--r-- | src/backend/executor/functions.c | 50 | ||||
-rw-r--r-- | src/backend/executor/spi.c | 149 |
4 files changed, 170 insertions, 187 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 91a76bb0779..405b58f9fd6 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.286 2007/02/02 00:07:02 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.287 2007/02/20 17:32:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -93,7 +93,8 @@ static void ExecProcessReturning(ProjectionInfo *projectReturning, static TupleTableSlot *EvalPlanQualNext(EState *estate); static void EndEvalPlanQual(EState *estate); static void ExecCheckRTEPerms(RangeTblEntry *rte); -static void ExecCheckXactReadOnly(Query *parsetree); +static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); +static void ExecCheckRangeTblReadOnly(List *rtable); static void EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq); static void EvalPlanQualStop(evalPlanQual *epq); @@ -139,7 +140,7 @@ ExecutorStart(QueryDesc *queryDesc, int eflags) * planned to non-temporary tables. EXPLAIN is considered read-only. */ if (XactReadOnly && !(eflags & EXEC_FLAG_EXPLAIN_ONLY)) - ExecCheckXactReadOnly(queryDesc->parsetree); + ExecCheckXactReadOnly(queryDesc->plannedstmt); /* * Build EState, switch into per-query memory context for startup. @@ -154,9 +155,9 @@ ExecutorStart(QueryDesc *queryDesc, int eflags) */ estate->es_param_list_info = queryDesc->params; - if (queryDesc->plantree->nParamExec > 0) + if (queryDesc->plannedstmt->nParamExec > 0) estate->es_param_exec_vals = (ParamExecData *) - palloc0(queryDesc->plantree->nParamExec * sizeof(ParamExecData)); + palloc0(queryDesc->plannedstmt->nParamExec * sizeof(ParamExecData)); /* * Copy other important information into the EState @@ -227,7 +228,7 @@ ExecutorRun(QueryDesc *queryDesc, estate->es_lastoid = InvalidOid; sendTuples = (operation == CMD_SELECT || - queryDesc->parsetree->returningList); + queryDesc->plannedstmt->returningLists); if (sendTuples) (*dest->rStartup) (dest, operation, queryDesc->tupDesc); @@ -414,26 +415,41 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Check that the query does not imply any writes to non-temp tables. */ static void -ExecCheckXactReadOnly(Query *parsetree) +ExecCheckXactReadOnly(PlannedStmt *plannedstmt) { - ListCell *l; - /* * CREATE TABLE AS or SELECT INTO? * * XXX should we allow this if the destination is temp? */ - if (parsetree->into != NULL) + if (plannedstmt->into != NULL) goto fail; /* Fail if write permissions are requested on any non-temp table */ - foreach(l, parsetree->rtable) + ExecCheckRangeTblReadOnly(plannedstmt->rtable); + + return; + +fail: + ereport(ERROR, + (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), + errmsg("transaction is read-only"))); +} + +static void +ExecCheckRangeTblReadOnly(List *rtable) +{ + ListCell *l; + + /* Fail if write permissions are requested on any non-temp table */ + foreach(l, rtable) { RangeTblEntry *rte = lfirst(l); if (rte->rtekind == RTE_SUBQUERY) { - ExecCheckXactReadOnly(rte->subquery); + Assert(!rte->subquery->into); + ExecCheckRangeTblReadOnly(rte->subquery->rtable); continue; } @@ -469,11 +485,11 @@ static void InitPlan(QueryDesc *queryDesc, int eflags) { CmdType operation = queryDesc->operation; - Query *parseTree = queryDesc->parsetree; - Plan *plan = queryDesc->plantree; + PlannedStmt *plannedstmt = queryDesc->plannedstmt; + Plan *plan = plannedstmt->planTree; + List *rangeTable = plannedstmt->rtable; EState *estate = queryDesc->estate; PlanState *planstate; - List *rangeTable; TupleDesc tupType; ListCell *l; @@ -482,12 +498,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) * rangetable here --- subplan RTEs will be checked during * ExecInitSubPlan(). */ - ExecCheckRTPerms(parseTree->rtable); - - /* - * get information from query descriptor - */ - rangeTable = parseTree->rtable; + ExecCheckRTPerms(rangeTable); /* * initialize the node's execution state @@ -495,50 +506,27 @@ InitPlan(QueryDesc *queryDesc, int eflags) estate->es_range_table = rangeTable; /* - * if there is a result relation, initialize result relation stuff + * initialize result relation stuff */ - if (parseTree->resultRelation) + if (plannedstmt->resultRelations) { - List *resultRelations = parseTree->resultRelations; - int numResultRelations; + List *resultRelations = plannedstmt->resultRelations; + int numResultRelations = list_length(resultRelations); ResultRelInfo *resultRelInfos; + ResultRelInfo *resultRelInfo; - if (resultRelations != NIL) - { - /* - * Multiple result relations (due to inheritance) - * parseTree->resultRelations identifies them all - */ - ResultRelInfo *resultRelInfo; - - numResultRelations = list_length(resultRelations); - resultRelInfos = (ResultRelInfo *) - palloc(numResultRelations * sizeof(ResultRelInfo)); - resultRelInfo = resultRelInfos; - foreach(l, resultRelations) - { - initResultRelInfo(resultRelInfo, - lfirst_int(l), - rangeTable, - operation, - estate->es_instrument); - resultRelInfo++; - } - } - else + resultRelInfos = (ResultRelInfo *) + palloc(numResultRelations * sizeof(ResultRelInfo)); + resultRelInfo = resultRelInfos; + foreach(l, resultRelations) { - /* - * Single result relation identified by parseTree->resultRelation - */ - numResultRelations = 1; - resultRelInfos = (ResultRelInfo *) palloc(sizeof(ResultRelInfo)); - initResultRelInfo(resultRelInfos, - parseTree->resultRelation, + initResultRelInfo(resultRelInfo, + lfirst_int(l), rangeTable, operation, estate->es_instrument); + resultRelInfo++; } - estate->es_result_relations = resultRelInfos; estate->es_num_result_relations = numResultRelations; /* Initialize to first or only result rel */ @@ -560,10 +548,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) * correct tuple descriptors. (Other SELECT INTO stuff comes later.) */ estate->es_select_into = false; - if (operation == CMD_SELECT && parseTree->into != NULL) + if (operation == CMD_SELECT && plannedstmt->into != NULL) { estate->es_select_into = true; - estate->es_into_oids = interpretOidsOption(parseTree->intoOptions); + estate->es_into_oids = interpretOidsOption(plannedstmt->into->options); } /* @@ -572,7 +560,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) * While we are at it, build the ExecRowMark list. */ estate->es_rowMarks = NIL; - foreach(l, parseTree->rowMarks) + foreach(l, plannedstmt->rowMarks) { RowMarkClause *rc = (RowMarkClause *) lfirst(l); Oid relid = getrelid(rc->rti, rangeTable); @@ -600,13 +588,13 @@ InitPlan(QueryDesc *queryDesc, int eflags) { int nSlots = ExecCountSlotsNode(plan); - if (parseTree->resultRelations != NIL) - nSlots += list_length(parseTree->resultRelations); + if (plannedstmt->resultRelations != NIL) + nSlots += list_length(plannedstmt->resultRelations); else nSlots += 1; if (operation != CMD_SELECT) nSlots++; /* for es_trig_tuple_slot */ - if (parseTree->returningLists) + if (plannedstmt->returningLists) nSlots++; /* for RETURNING projection */ estate->es_tupleTable = ExecCreateTupleTable(nSlots); @@ -617,7 +605,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) } /* mark EvalPlanQual not active */ - estate->es_topPlan = plan; + estate->es_plannedstmt = plannedstmt; estate->es_evalPlanQual = NULL; estate->es_evTupleNull = NULL; estate->es_evTuple = NULL; @@ -683,7 +671,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) * junk filter. Note this is only possible for UPDATE/DELETE, so * we can't be fooled by some needing a filter and some not. */ - if (parseTree->resultRelations != NIL) + if (list_length(plannedstmt->resultRelations) > 1) { PlanState **appendplans; int as_nplans; @@ -772,7 +760,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) /* * Initialize RETURNING projections if needed. */ - if (parseTree->returningLists) + if (plannedstmt->returningLists) { TupleTableSlot *slot; ExprContext *econtext; @@ -782,7 +770,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) * We set QueryDesc.tupDesc to be the RETURNING rowtype in this case. * We assume all the sublists will generate the same output tupdesc. */ - tupType = ExecTypeFromTL((List *) linitial(parseTree->returningLists), + tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists), false); /* Set up a slot for the output of the RETURNING projection(s) */ @@ -795,9 +783,9 @@ InitPlan(QueryDesc *queryDesc, int eflags) * Build a projection for each result rel. Note that any SubPlans in * the RETURNING lists get attached to the topmost plan node. */ - Assert(list_length(parseTree->returningLists) == estate->es_num_result_relations); + Assert(list_length(plannedstmt->returningLists) == estate->es_num_result_relations); resultRelInfo = estate->es_result_relations; - foreach(l, parseTree->returningLists) + foreach(l, plannedstmt->returningLists) { List *rlist = (List *) lfirst(l); List *rliststate; @@ -2273,14 +2261,14 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq) epqstate->es_into_relation_descriptor = estate->es_into_relation_descriptor; epqstate->es_into_relation_use_wal = estate->es_into_relation_use_wal; epqstate->es_param_list_info = estate->es_param_list_info; - if (estate->es_topPlan->nParamExec > 0) + if (estate->es_plannedstmt->nParamExec > 0) epqstate->es_param_exec_vals = (ParamExecData *) - palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData)); + palloc0(estate->es_plannedstmt->nParamExec * sizeof(ParamExecData)); epqstate->es_rowMarks = estate->es_rowMarks; epqstate->es_instrument = estate->es_instrument; epqstate->es_select_into = estate->es_select_into; epqstate->es_into_oids = estate->es_into_oids; - epqstate->es_topPlan = estate->es_topPlan; + epqstate->es_plannedstmt = estate->es_plannedstmt; /* * Each epqstate must have its own es_evTupleNull state, but all the stack @@ -2299,7 +2287,7 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq) epqstate->es_tupleTable = ExecCreateTupleTable(estate->es_tupleTable->size); - epq->planstate = ExecInitNode(estate->es_topPlan, epqstate, 0); + epq->planstate = ExecInitNode(estate->es_plannedstmt->planTree, epqstate, 0); MemoryContextSwitchTo(oldcontext); } @@ -2365,7 +2353,7 @@ typedef struct static void OpenIntoRel(QueryDesc *queryDesc) { - Query *parseTree = queryDesc->parsetree; + IntoClause *into = queryDesc->plannedstmt->into; EState *estate = queryDesc->estate; Relation intoRelationDesc; char *intoName; @@ -2377,10 +2365,12 @@ OpenIntoRel(QueryDesc *queryDesc) TupleDesc tupdesc; DR_intorel *myState; + Assert(into); + /* * Check consistency of arguments */ - if (parseTree->intoOnCommit != ONCOMMIT_NOOP && !parseTree->into->istemp) + if (into->onCommit != ONCOMMIT_NOOP && !into->rel->istemp) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("ON COMMIT can only be used on temporary tables"))); @@ -2388,8 +2378,8 @@ OpenIntoRel(QueryDesc *queryDesc) /* * Find namespace to create in, check its permissions */ - intoName = parseTree->into->relname; - namespaceId = RangeVarGetCreationNamespace(parseTree->into); + intoName = into->rel->relname; + namespaceId = RangeVarGetCreationNamespace(into->rel); aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); @@ -2401,16 +2391,16 @@ OpenIntoRel(QueryDesc *queryDesc) * Select tablespace to use. If not specified, use default_tablespace * (which may in turn default to database's default). */ - if (parseTree->intoTableSpaceName) + if (into->tableSpaceName) { - tablespaceId = get_tablespace_oid(parseTree->intoTableSpaceName); + tablespaceId = get_tablespace_oid(into->tableSpaceName); if (!OidIsValid(tablespaceId)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace \"%s\" does not exist", - parseTree->intoTableSpaceName))); + into->tableSpaceName))); } - else if (parseTree->into->istemp) + else if (into->rel->istemp) { tablespaceId = GetTempTablespace(); } @@ -2435,7 +2425,7 @@ OpenIntoRel(QueryDesc *queryDesc) /* Parse and validate any reloptions */ reloptions = transformRelOptions((Datum) 0, - parseTree->intoOptions, + into->options, true, false); (void) heap_reloptions(RELKIND_RELATION, reloptions, true); @@ -2454,7 +2444,7 @@ OpenIntoRel(QueryDesc *queryDesc) false, true, 0, - parseTree->intoOnCommit, + into->onCommit, reloptions, allowSystemTableMods); diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 91efba011ca..d9957573883 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.144 2007/02/06 17:35:20 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.145 2007/02/20 17:32:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -242,7 +242,7 @@ InternalCreateExecutorState(MemoryContext qcontext, bool is_subquery) estate->es_per_tuple_exprcontext = NULL; - estate->es_topPlan = NULL; + estate->es_plannedstmt = NULL; estate->es_evalPlanQual = NULL; estate->es_evTupleNull = NULL; estate->es_evTuple = NULL; diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 78044b2d614..596a482fa13 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.110 2007/02/02 00:02:55 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.111 2007/02/20 17:32:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -33,8 +33,8 @@ /* * We have an execution_state record for each query in a function. Each - * record contains a querytree and plantree for its query. If the query - * is currently in F_EXEC_RUN state then there's a QueryDesc too. + * record contains a plantree for its query. If the query is currently in + * F_EXEC_RUN state then there's a QueryDesc too. */ typedef enum { @@ -45,8 +45,7 @@ typedef struct local_es { struct local_es *next; ExecStatus status; - Query *query; - Plan *plan; + Node *stmt; /* PlannedStmt or utility statement */ QueryDesc *qd; /* null unless status == RUN */ } execution_state; @@ -105,26 +104,30 @@ init_execution_state(List *queryTree_list, bool readonly_func) foreach(qtl_item, queryTree_list) { Query *queryTree = lfirst(qtl_item); - Plan *planTree; + Node *stmt; execution_state *newes; + Assert(IsA(queryTree, Query)); + + if (queryTree->commandType == CMD_UTILITY) + stmt = queryTree->utilityStmt; + else + stmt = (Node *) pg_plan_query(queryTree, NULL); + /* Precheck all commands for validity in a function */ - if (queryTree->commandType == CMD_UTILITY && - IsA(queryTree->utilityStmt, TransactionStmt)) + if (IsA(stmt, TransactionStmt)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /* translator: %s is a SQL statement name */ errmsg("%s is not allowed in a SQL function", - CreateQueryTag(queryTree)))); + CreateCommandTag(stmt)))); - if (readonly_func && !QueryIsReadOnly(queryTree)) + if (readonly_func && !CommandIsReadOnly(stmt)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /* translator: %s is a SQL statement name */ errmsg("%s is not allowed in a non-volatile function", - CreateQueryTag(queryTree)))); - - planTree = pg_plan_query(queryTree, NULL); + CreateCommandTag(stmt)))); newes = (execution_state *) palloc(sizeof(execution_state)); if (preves) @@ -134,8 +137,7 @@ init_execution_state(List *queryTree_list, bool readonly_func) newes->next = NULL; newes->status = F_EXEC_START; - newes->query = queryTree; - newes->plan = planTree; + newes->stmt = stmt; newes->qd = NULL; preves = newes; @@ -298,10 +300,16 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache) snapshot = CopySnapshot(GetTransactionSnapshot()); } - es->qd = CreateQueryDesc(es->query, es->plan, - snapshot, InvalidSnapshot, - None_Receiver, - fcache->paramLI, false); + if (IsA(es->stmt, PlannedStmt)) + es->qd = CreateQueryDesc((PlannedStmt *) es->stmt, + snapshot, InvalidSnapshot, + None_Receiver, + fcache->paramLI, false); + else + es->qd = CreateUtilityQueryDesc(es->stmt, + snapshot, + None_Receiver, + fcache->paramLI); /* We assume we don't need to set up ActiveSnapshot for ExecutorStart */ @@ -337,7 +345,7 @@ postquel_getnext(execution_state *es) if (es->qd->operation == CMD_UTILITY) { - ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->params, + ProcessUtility(es->qd->utilitystmt, es->qd->params, es->qd->dest, NULL); result = NULL; } @@ -351,7 +359,7 @@ postquel_getnext(execution_state *es) */ if (LAST_POSTQUEL_COMMAND(es) && es->qd->operation == CMD_SELECT && - es->qd->parsetree->into == NULL) + es->qd->plannedstmt->into == NULL) count = 1L; else count = 0L; diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index a565ba3cd2b..33ec21286d9 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.169 2007/01/09 22:00:59 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.170 2007/02/20 17:32:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -831,8 +831,7 @@ SPI_cursor_open(const char *name, void *plan, bool read_only) { _SPI_plan *spiplan = (_SPI_plan *) plan; - List *qtlist; - List *ptlist; + List *stmt_list; ParamListInfo paramLI; Snapshot snapshot; MemoryContext oldcontext; @@ -846,29 +845,22 @@ SPI_cursor_open(const char *name, void *plan, if (!SPI_is_cursor_plan(spiplan)) { /* try to give a good error message */ - Query *queryTree; + Node *stmt; - if (list_length(spiplan->qtlist) != 1) + if (list_length(spiplan->stmt_list_list) != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open multi-query plan as cursor"))); - queryTree = PortalListGetPrimaryQuery((List *) linitial(spiplan->qtlist)); - if (queryTree == NULL) - ereport(ERROR, - (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), - errmsg("cannot open empty query as cursor"))); + stmt = PortalListGetPrimaryStmt((List *) linitial(spiplan->stmt_list_list)); ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), /* translator: %s is name of a SQL command, eg INSERT */ errmsg("cannot open %s query as cursor", - CreateQueryTag(queryTree)))); + CreateCommandTag(stmt)))); } - Assert(list_length(spiplan->qtlist) == 1); - qtlist = (List *) linitial(spiplan->qtlist); - ptlist = spiplan->ptlist; - if (list_length(qtlist) != list_length(ptlist)) - elog(ERROR, "corrupted SPI plan lists"); + Assert(list_length(spiplan->stmt_list_list) == 1); + stmt_list = (List *) linitial(spiplan->stmt_list_list); /* Reset SPI result (note we deliberately don't touch lastoid) */ SPI_processed = 0; @@ -888,10 +880,9 @@ SPI_cursor_open(const char *name, void *plan, portal = CreatePortal(name, false, false); } - /* Switch to portal's memory and copy the parsetrees and plans to there */ + /* Switch to portal's memory and copy the plans to there */ oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); - qtlist = copyObject(qtlist); - ptlist = copyObject(ptlist); + stmt_list = copyObject(stmt_list); /* If the plan has parameters, set them up */ if (spiplan->nargs > 0) @@ -934,9 +925,8 @@ SPI_cursor_open(const char *name, void *plan, PortalDefineQuery(portal, NULL, /* no statement name */ spiplan->query, - CreateQueryTag(PortalListGetPrimaryQuery(qtlist)), - qtlist, - ptlist, + CreateCommandTag(PortalListGetPrimaryStmt(stmt_list)), + stmt_list, PortalGetHeapMemory(portal)); MemoryContextSwitchTo(oldcontext); @@ -945,8 +935,9 @@ SPI_cursor_open(const char *name, void *plan, * Set up options for portal. */ portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL); - if (list_length(ptlist) == 1 && - ExecSupportsBackwardScan((Plan *) linitial(ptlist))) + if (list_length(stmt_list) == 1 && + IsA((Node *) linitial(stmt_list), PlannedStmt) && + ExecSupportsBackwardScan(((PlannedStmt *) linitial(stmt_list))->planTree)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; @@ -1076,10 +1067,10 @@ SPI_is_cursor_plan(void *plan) return false; } - if (list_length(spiplan->qtlist) != 1) + if (list_length(spiplan->stmt_list_list) != 1) return false; /* not exactly 1 pre-rewrite command */ - switch (ChoosePortalStrategy((List *) linitial(spiplan->qtlist))) + switch (ChoosePortalStrategy((List *) linitial(spiplan->stmt_list_list))) { case PORTAL_ONE_SELECT: case PORTAL_ONE_RETURNING: @@ -1257,14 +1248,13 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self) * * At entry, plan->argtypes and plan->nargs must be valid. * - * Query and plan lists are stored into *plan. + * Result lists are stored into *plan. */ static void _SPI_prepare_plan(const char *src, _SPI_plan *plan) { List *raw_parsetree_list; - List *query_list_list; - List *plan_list; + List *stmt_list_list; ListCell *list_item; ErrorContextCallback spierrcontext; Oid *argtypes = plan->argtypes; @@ -1294,12 +1284,11 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan) /* * Do parse analysis and rule rewrite for each raw parsetree. * - * We save the querytrees from each raw parsetree as a separate sublist. + * We save the results 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; + stmt_list_list = NIL; foreach(list_item, raw_parsetree_list) { @@ -1308,14 +1297,11 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan) query_list = pg_analyze_and_rewrite(parsetree, src, argtypes, nargs); - query_list_list = lappend(query_list_list, query_list); - - plan_list = list_concat(plan_list, - pg_plan_queries(query_list, NULL, false)); + stmt_list_list = lappend(stmt_list_list, + pg_plan_queries(query_list, NULL, false)); } - plan->qtlist = query_list_list; - plan->ptlist = plan_list; + plan->stmt_list_list = stmt_list_list; /* * Pop the error context stack @@ -1348,9 +1334,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, saveActiveSnapshot = ActiveSnapshot; PG_TRY(); { - List *query_list_list = plan->qtlist; - ListCell *plan_list_item = list_head(plan->ptlist); - ListCell *query_list_list_item; + ListCell *stmt_list_list_item; ErrorContextCallback spierrcontext; int nargs = plan->nargs; ParamListInfo paramLI; @@ -1386,57 +1370,61 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, spierrcontext.previous = error_context_stack; error_context_stack = &spierrcontext; - foreach(query_list_list_item, query_list_list) + foreach(stmt_list_list_item, plan->stmt_list_list) { - List *query_list = lfirst(query_list_list_item); - ListCell *query_list_item; + List *stmt_list = (List *) lfirst(stmt_list_list_item); + ListCell *stmt_list_item; - foreach(query_list_item, query_list) + foreach(stmt_list_item, stmt_list) { - Query *queryTree = (Query *) lfirst(query_list_item); - Plan *planTree; + Node *stmt = (Node *) lfirst(stmt_list_item); + bool canSetTag; QueryDesc *qdesc; DestReceiver *dest; - planTree = lfirst(plan_list_item); - plan_list_item = lnext(plan_list_item); - _SPI_current->processed = 0; _SPI_current->lastoid = InvalidOid; _SPI_current->tuptable = NULL; - if (queryTree->commandType == CMD_UTILITY) + if (IsA(stmt, PlannedStmt)) { - if (IsA(queryTree->utilityStmt, CopyStmt)) + canSetTag = ((PlannedStmt *) stmt)->canSetTag; + } + else + { + /* utilities are canSetTag if only thing in list */ + canSetTag = (list_length(stmt_list) == 1); + + if (IsA(stmt, CopyStmt)) { - CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt; + CopyStmt *cstmt = (CopyStmt *) stmt; - if (stmt->filename == NULL) + if (cstmt->filename == NULL) { my_res = SPI_ERROR_COPY; goto fail; } } - else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) || - IsA(queryTree->utilityStmt, ClosePortalStmt) || - IsA(queryTree->utilityStmt, FetchStmt)) + else if (IsA(stmt, DeclareCursorStmt) || + IsA(stmt, ClosePortalStmt) || + IsA(stmt, FetchStmt)) { my_res = SPI_ERROR_CURSOR; goto fail; } - else if (IsA(queryTree->utilityStmt, TransactionStmt)) + else if (IsA(stmt, TransactionStmt)) { my_res = SPI_ERROR_TRANSACTION; goto fail; } } - if (read_only && !QueryIsReadOnly(queryTree)) + if (read_only && !CommandIsReadOnly(stmt)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /* translator: %s is a SQL statement name */ errmsg("%s is not allowed in a non-volatile function", - CreateQueryTag(queryTree)))); + CreateCommandTag(stmt)))); /* * If not read-only mode, advance the command counter before @@ -1445,7 +1433,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, if (!read_only) CommandCounterIncrement(); - dest = CreateDestReceiver(queryTree->canSetTag ? DestSPI : DestNone, + dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone, NULL); if (snapshot == InvalidSnapshot) @@ -1471,26 +1459,24 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, ActiveSnapshot->curcid = GetCurrentCommandId(); } - if (queryTree->commandType == CMD_UTILITY) - { - ProcessUtility(queryTree->utilityStmt, paramLI, - dest, NULL); - /* Update "processed" if stmt returned tuples */ - if (_SPI_current->tuptable) - _SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free; - res = SPI_OK_UTILITY; - } - else + if (IsA(stmt, PlannedStmt)) { - qdesc = CreateQueryDesc(queryTree, planTree, + qdesc = CreateQueryDesc((PlannedStmt *) stmt, ActiveSnapshot, crosscheck_snapshot, dest, paramLI, false); - res = _SPI_pquery(qdesc, - queryTree->canSetTag ? tcount : 0); + res = _SPI_pquery(qdesc, canSetTag ? tcount : 0); FreeQueryDesc(qdesc); } + else + { + ProcessUtility(stmt, paramLI, dest, NULL); + /* Update "processed" if stmt returned tuples */ + if (_SPI_current->tuptable) + _SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free; + res = SPI_OK_UTILITY; + } FreeSnapshot(ActiveSnapshot); ActiveSnapshot = NULL; @@ -1499,7 +1485,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, * the caller. Be careful to free any tuptables not returned, * to avoid intratransaction memory leak. */ - if (queryTree->canSetTag) + if (canSetTag) { my_processed = _SPI_current->processed; my_lastoid = _SPI_current->lastoid; @@ -1565,7 +1551,7 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount) switch (operation) { case CMD_SELECT: - if (queryDesc->parsetree->into) /* select into table? */ + if (queryDesc->plannedstmt->into) /* select into table? */ res = SPI_OK_SELINTO; else if (queryDesc->dest->mydest != DestSPI) { @@ -1576,19 +1562,19 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount) res = SPI_OK_SELECT; break; case CMD_INSERT: - if (queryDesc->parsetree->returningList) + if (queryDesc->plannedstmt->returningLists) res = SPI_OK_INSERT_RETURNING; else res = SPI_OK_INSERT; break; case CMD_DELETE: - if (queryDesc->parsetree->returningList) + if (queryDesc->plannedstmt->returningLists) res = SPI_OK_DELETE_RETURNING; else res = SPI_OK_DELETE; break; case CMD_UPDATE: - if (queryDesc->parsetree->returningList) + if (queryDesc->plannedstmt->returningLists) res = SPI_OK_UPDATE_RETURNING; else res = SPI_OK_UPDATE; @@ -1611,7 +1597,7 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount) _SPI_current->processed = queryDesc->estate->es_processed; _SPI_current->lastoid = queryDesc->estate->es_lastoid; - if ((res == SPI_OK_SELECT || queryDesc->parsetree->returningList) && + if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->returningLists) && queryDesc->dest->mydest == DestSPI) { if (_SPI_checktuples()) @@ -1813,8 +1799,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location) newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); newplan->plancxt = plancxt; newplan->query = pstrdup(plan->query); - newplan->qtlist = (List *) copyObject(plan->qtlist); - newplan->ptlist = (List *) copyObject(plan->ptlist); + newplan->stmt_list_list = (List *) copyObject(plan->stmt_list_list); newplan->nargs = plan->nargs; if (plan->nargs > 0) { |