mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
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.
This commit is contained in:
@ -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)
|
||||
resultRelInfos = (ResultRelInfo *)
|
||||
palloc(numResultRelations * sizeof(ResultRelInfo));
|
||||
resultRelInfo = resultRelInfos;
|
||||
foreach(l, resultRelations)
|
||||
{
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
/*
|
||||
* 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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
{
|
||||
CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt;
|
||||
canSetTag = ((PlannedStmt *) stmt)->canSetTag;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* utilities are canSetTag if only thing in list */
|
||||
canSetTag = (list_length(stmt_list) == 1);
|
||||
|
||||
if (stmt->filename == NULL)
|
||||
if (IsA(stmt, CopyStmt))
|
||||
{
|
||||
CopyStmt *cstmt = (CopyStmt *) stmt;
|
||||
|
||||
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)
|
||||
if (IsA(stmt, PlannedStmt))
|
||||
{
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
Reference in New Issue
Block a user