From 9cbd0c155d1602aad879f510256b626c58942080 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 20 Feb 2007 17:32:18 +0000 Subject: [PATCH] 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. --- src/backend/commands/copy.c | 9 +- src/backend/commands/explain.c | 13 +- src/backend/commands/portalcmds.c | 10 +- src/backend/commands/prepare.c | 188 +++++++------- src/backend/executor/execMain.c | 154 ++++++------ src/backend/executor/execUtils.c | 4 +- src/backend/executor/functions.c | 50 ++-- src/backend/executor/spi.c | 151 ++++++------ src/backend/nodes/copyfuncs.c | 59 +++-- src/backend/nodes/equalfuncs.c | 29 ++- src/backend/nodes/outfuncs.c | 48 +++- src/backend/nodes/readfuncs.c | 23 +- src/backend/optimizer/path/allpaths.c | 8 +- src/backend/optimizer/plan/planagg.c | 5 +- src/backend/optimizer/plan/planner.c | 62 +++-- src/backend/parser/analyze.c | 25 +- src/backend/parser/gram.y | 59 +++-- src/backend/tcop/postgres.c | 150 ++++++------ src/backend/tcop/pquery.c | 340 +++++++++++++++++--------- src/backend/tcop/utility.c | 328 ++++++++++++++----------- src/backend/utils/mmgr/portalmem.c | 53 ++-- src/include/catalog/catversion.h | 4 +- src/include/commands/portalcmds.h | 3 +- src/include/commands/prepare.h | 16 +- src/include/executor/execdesc.h | 21 +- src/include/executor/executor.h | 3 +- src/include/executor/spi_priv.h | 11 +- src/include/nodes/execnodes.h | 4 +- src/include/nodes/nodes.h | 20 +- src/include/nodes/parsenodes.h | 62 +---- src/include/nodes/plannodes.h | 53 +++- src/include/nodes/primnodes.h | 25 +- src/include/nodes/relation.h | 6 +- src/include/optimizer/planner.h | 6 +- src/include/tcop/pquery.h | 7 +- src/include/tcop/tcopprot.h | 5 +- src/include/tcop/utility.h | 10 +- src/include/utils/portal.h | 16 +- src/pl/plpgsql/src/pl_exec.c | 29 ++- 39 files changed, 1172 insertions(+), 897 deletions(-) diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index e61384beec..30118d5237 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.275 2007/01/25 02:17:26 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.276 2007/02/20 17:32:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -986,10 +986,11 @@ DoCopy(const CopyStmt *stmt) { Query *query = stmt->query; List *rewritten; - Plan *plan; + PlannedStmt *plan; DestReceiver *dest; Assert(query); + Assert(query->commandType == CMD_SELECT); Assert(!is_from); cstate->rel = NULL; @@ -999,6 +1000,7 @@ DoCopy(const CopyStmt *stmt) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY (SELECT) WITH OIDS is not supported"))); + /* Query mustn't use INTO, either */ if (query->into) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -1016,7 +1018,6 @@ DoCopy(const CopyStmt *stmt) * shouldn't modify its input ... FIXME someday. */ query = copyObject(query); - Assert(query->commandType == CMD_SELECT); /* * Must acquire locks in case we didn't come fresh from the parser. @@ -1051,7 +1052,7 @@ DoCopy(const CopyStmt *stmt) ((DR_copy *) dest)->cstate = cstate; /* Create a QueryDesc requesting no output */ - cstate->queryDesc = CreateQueryDesc(query, plan, + cstate->queryDesc = CreateQueryDesc(plan, ActiveSnapshot, InvalidSnapshot, dest, NULL, false); diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 7b2c521a35..58b7e6ded9 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994-5, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.155 2007/02/19 02:23:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.156 2007/02/20 17:32:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -149,7 +149,7 @@ static void ExplainOneQuery(Query *query, ExplainStmt *stmt, ParamListInfo params, TupOutputState *tstate) { - Plan *plan; + PlannedStmt *plan; QueryDesc *queryDesc; bool isCursor = false; int cursorOptions = 0; @@ -203,7 +203,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, ParamListInfo params, ActiveSnapshot->curcid = GetCurrentCommandId(); /* Create a QueryDesc requesting no output */ - queryDesc = CreateQueryDesc(query, plan, + queryDesc = CreateQueryDesc(plan, ActiveSnapshot, InvalidSnapshot, None_Receiver, params, stmt->analyze); @@ -260,14 +260,14 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt, es->printNodes = stmt->verbose; es->printAnalyze = stmt->analyze; - es->rtable = queryDesc->parsetree->rtable; + es->rtable = queryDesc->plannedstmt->rtable; if (es->printNodes) { char *s; char *f; - s = nodeToString(queryDesc->plantree); + s = nodeToString(queryDesc->plannedstmt->planTree); if (s) { if (Explain_pretty_print) @@ -282,7 +282,8 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt, } initStringInfo(&buf); - explain_outNode(&buf, queryDesc->plantree, queryDesc->planstate, + explain_outNode(&buf, + queryDesc->plannedstmt->planTree, queryDesc->planstate, NULL, 0, es); /* diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index d1c119ca08..0219650c06 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.60 2007/02/06 22:49:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.61 2007/02/20 17:32:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -42,7 +42,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params) { List *rewritten; Query *query; - Plan *plan; + PlannedStmt *plan; Portal portal; MemoryContext oldContext; @@ -98,13 +98,12 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params) plan = planner(query, true, stmt->options, params); /* - * Create a portal and copy the query and plan into its memory context. + * Create a portal and copy the plan into its memory context. */ portal = CreatePortal(stmt->portalname, false, false); oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); - query = copyObject(query); plan = copyObject(plan); /* @@ -115,7 +114,6 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params) NULL, debug_query_string ? pstrdup(debug_query_string) : NULL, "SELECT", /* cursor's query is always a SELECT */ - list_make1(query), list_make1(plan), PortalGetHeapMemory(portal)); @@ -140,7 +138,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params) portal->cursorOptions = stmt->options; if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL))) { - if (ExecSupportsBackwardScan(plan)) + if (ExecSupportsBackwardScan(plan->planTree)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index ecd5074211..8a5382c737 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -10,7 +10,7 @@ * Copyright (c) 2002-2007, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.68 2007/01/28 19:05:35 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.69 2007/02/20 17:32:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -114,9 +114,9 @@ PrepareQuery(PrepareStmt *stmt) StorePreparedStatement(stmt->name, debug_query_string, commandTag, - query_list, plan_list, stmt->argtype_oids, + true, true); } @@ -129,8 +129,7 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params, { PreparedStatement *entry; char *query_string; - List *query_list, - *plan_list; + List *plan_list; MemoryContext qcontext; ParamListInfo paramLI = NULL; EState *estate = NULL; @@ -139,12 +138,17 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params, /* Look it up in the hash table */ entry = FetchPreparedStatement(stmt->name, true); - query_string = entry->query_string; - query_list = entry->query_list; - plan_list = entry->plan_list; - qcontext = entry->context; + /* + * Punt if not fully planned. (Currently, that only happens for the + * protocol-level unnamed statement, which can't be accessed from SQL; + * so there's no point in doing more than a quick check here.) + */ + if (!entry->fully_planned) + elog(ERROR, "EXECUTE does not support unplanned prepared statements"); - Assert(list_length(query_list) == list_length(plan_list)); + query_string = entry->query_string; + plan_list = entry->stmt_list; + qcontext = entry->context; /* Evaluate parameters, if any */ if (entry->argtype_list != NIL) @@ -172,30 +176,26 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params, if (stmt->into) { MemoryContext oldContext; - Query *query; + PlannedStmt *pstmt; - oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); + qcontext = PortalGetHeapMemory(portal); + oldContext = MemoryContextSwitchTo(qcontext); if (query_string) query_string = pstrdup(query_string); - query_list = copyObject(query_list); plan_list = copyObject(plan_list); - qcontext = PortalGetHeapMemory(portal); - if (list_length(query_list) != 1) + if (list_length(plan_list) != 1) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("prepared statement is not a SELECT"))); - query = (Query *) linitial(query_list); - if (query->commandType != CMD_SELECT) + pstmt = (PlannedStmt *) linitial(plan_list); + if (!IsA(pstmt, PlannedStmt) || + pstmt->commandType != CMD_SELECT) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("prepared statement is not a SELECT"))); - query->into = copyObject(stmt->into); - query->intoOptions = copyObject(stmt->intoOptions); - query->intoOnCommit = stmt->into_on_commit; - if (stmt->into_tbl_space) - query->intoTableSpaceName = pstrdup(stmt->into_tbl_space); + pstmt->into = copyObject(stmt->into); MemoryContextSwitchTo(oldContext); } @@ -204,7 +204,6 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params, NULL, query_string, entry->commandTag, - query_list, plan_list, qcontext); @@ -305,9 +304,9 @@ void StorePreparedStatement(const char *stmt_name, const char *query_string, const char *commandTag, - List *query_list, - List *plan_list, + List *stmt_list, List *argtype_list, + bool fully_planned, bool from_sql) { PreparedStatement *entry; @@ -345,8 +344,7 @@ StorePreparedStatement(const char *stmt_name, * incomplete (ie corrupt) hashtable entry. */ qstring = query_string ? pstrdup(query_string) : NULL; - query_list = (List *) copyObject(query_list); - plan_list = (List *) copyObject(plan_list); + stmt_list = (List *) copyObject(stmt_list); argtype_list = list_copy(argtype_list); /* Now we can add entry to hash table */ @@ -363,12 +361,12 @@ StorePreparedStatement(const char *stmt_name, /* Fill in the hash table entry with copied data */ entry->query_string = qstring; entry->commandTag = commandTag; - entry->query_list = query_list; - entry->plan_list = plan_list; + entry->stmt_list = stmt_list; entry->argtype_list = argtype_list; + entry->fully_planned = fully_planned; + entry->from_sql = from_sql; entry->context = entrycxt; entry->prepare_time = GetCurrentStatementStartTimestamp(); - entry->from_sql = from_sql; MemoryContextSwitchTo(oldcxt); } @@ -426,21 +424,54 @@ FetchPreparedStatementParams(const char *stmt_name) TupleDesc FetchPreparedStatementResultDesc(PreparedStatement *stmt) { + Node *node; Query *query; + PlannedStmt *pstmt; - switch (ChoosePortalStrategy(stmt->query_list)) + switch (ChoosePortalStrategy(stmt->stmt_list)) { case PORTAL_ONE_SELECT: - query = (Query *) linitial(stmt->query_list); - return ExecCleanTypeFromTL(query->targetList, false); + node = (Node *) linitial(stmt->stmt_list); + if (IsA(node, Query)) + { + query = (Query *) node; + return ExecCleanTypeFromTL(query->targetList, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; + return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); + } + /* other cases shouldn't happen, but return NULL */ + break; case PORTAL_ONE_RETURNING: - query = PortalListGetPrimaryQuery(stmt->query_list); - return ExecCleanTypeFromTL(query->returningList, false); + node = PortalListGetPrimaryStmt(stmt->stmt_list); + if (IsA(node, Query)) + { + query = (Query *) node; + Assert(query->returningList); + return ExecCleanTypeFromTL(query->returningList, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; + Assert(pstmt->returningLists); + return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false); + } + /* other cases shouldn't happen, but return NULL */ + break; case PORTAL_UTIL_SELECT: - query = (Query *) linitial(stmt->query_list); - return UtilityTupleDescriptor(query->utilityStmt); + node = (Node *) linitial(stmt->stmt_list); + if (IsA(node, Query)) + { + query = (Query *) node; + Assert(query->utilityStmt); + return UtilityTupleDescriptor(query->utilityStmt); + } + /* else it's a bare utility statement */ + return UtilityTupleDescriptor(node); case PORTAL_MULTI_QUERY: /* will not return tuples */ @@ -460,7 +491,7 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt) bool PreparedStatementReturnsTuples(PreparedStatement *stmt) { - switch (ChoosePortalStrategy(stmt->query_list)) + switch (ChoosePortalStrategy(stmt->stmt_list)) { case PORTAL_ONE_SELECT: case PORTAL_ONE_RETURNING: @@ -480,52 +511,15 @@ PreparedStatementReturnsTuples(PreparedStatement *stmt) * targetlist. * * Note: do not modify the result. - * - * XXX be careful to keep this in sync with FetchPortalTargetList, - * and with UtilityReturnsTuples. */ List * FetchPreparedStatementTargetList(PreparedStatement *stmt) { - PortalStrategy strategy = ChoosePortalStrategy(stmt->query_list); - - if (strategy == PORTAL_ONE_SELECT) - return ((Query *) linitial(stmt->query_list))->targetList; - if (strategy == PORTAL_ONE_RETURNING) - return (PortalListGetPrimaryQuery(stmt->query_list))->returningList; - if (strategy == PORTAL_UTIL_SELECT) - { - Node *utilityStmt; - - utilityStmt = ((Query *) linitial(stmt->query_list))->utilityStmt; - switch (nodeTag(utilityStmt)) - { - case T_FetchStmt: - { - FetchStmt *substmt = (FetchStmt *) utilityStmt; - Portal subportal; - - Assert(!substmt->ismove); - subportal = GetPortalByName(substmt->portalname); - Assert(PortalIsValid(subportal)); - return FetchPortalTargetList(subportal); - } - - case T_ExecuteStmt: - { - ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt; - PreparedStatement *entry; - - Assert(!substmt->into); - entry = FetchPreparedStatement(substmt->name, true); - return FetchPreparedStatementTargetList(entry); - } - - default: - break; - } - } - return NIL; + /* no point in looking if it doesn't return tuples */ + if (ChoosePortalStrategy(stmt->stmt_list) == PORTAL_MULTI_QUERY) + return NIL; + /* get the primary statement and find out what it returns */ + return FetchStatementTargetList(PortalListGetPrimaryStmt(stmt->stmt_list)); } /* @@ -574,10 +568,8 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params, { ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt; PreparedStatement *entry; - ListCell *q, - *p; - List *query_list, - *plan_list; + List *plan_list; + ListCell *p; ParamListInfo paramLI = NULL; EState *estate = NULL; @@ -587,10 +579,15 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params, /* Look it up in the hash table */ entry = FetchPreparedStatement(execstmt->name, true); - query_list = entry->query_list; - plan_list = entry->plan_list; + /* + * Punt if not fully planned. (Currently, that only happens for the + * protocol-level unnamed statement, which can't be accessed from SQL; + * so there's no point in doing more than a quick check here.) + */ + if (!entry->fully_planned) + elog(ERROR, "EXPLAIN EXECUTE does not support unplanned prepared statements"); - Assert(list_length(query_list) == list_length(plan_list)); + plan_list = entry->stmt_list; /* Evaluate parameters, if any */ if (entry->argtype_list != NIL) @@ -606,17 +603,16 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params, } /* Explain each query */ - forboth(q, query_list, p, plan_list) + foreach(p, plan_list) { - Query *query = (Query *) lfirst(q); - Plan *plan = (Plan *) lfirst(p); + PlannedStmt *pstmt = (PlannedStmt *) lfirst(p); bool is_last_query; is_last_query = (lnext(p) == NULL); - if (query->commandType == CMD_UTILITY) + if (!IsA(pstmt, PlannedStmt)) { - if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt)) + if (IsA(pstmt, NotifyStmt)) do_text_output_oneline(tstate, "NOTIFY"); else do_text_output_oneline(tstate, "UTILITY"); @@ -627,15 +623,15 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params, if (execstmt->into) { - if (query->commandType != CMD_SELECT) + if (pstmt->commandType != CMD_SELECT) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("prepared statement is not a SELECT"))); - /* Copy the query so we can modify it */ - query = copyObject(query); + /* Copy the stmt so we can modify it */ + pstmt = copyObject(pstmt); - query->into = execstmt->into; + pstmt->into = execstmt->into; } /* @@ -648,7 +644,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params, ActiveSnapshot->curcid = GetCurrentCommandId(); /* Create a QueryDesc requesting no output */ - qdesc = CreateQueryDesc(query, plan, + qdesc = CreateQueryDesc(pstmt, ActiveSnapshot, InvalidSnapshot, None_Receiver, paramLI, stmt->analyze); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 91a76bb077..405b58f9fd 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) + 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); diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 91efba011c..d995757388 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 78044b2d61..596a482fa1 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 a565ba3cd2..33ec21286d 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)) - { - 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) { diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index cdf98de568..4d10afc022 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.366 2007/02/19 02:23:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.367 2007/02/20 17:32:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -64,6 +64,27 @@ * **************************************************************** */ +/* + * _copyPlannedStmt + */ +static PlannedStmt * +_copyPlannedStmt(PlannedStmt *from) +{ + PlannedStmt *newnode = makeNode(PlannedStmt); + + COPY_SCALAR_FIELD(commandType); + COPY_SCALAR_FIELD(canSetTag); + COPY_NODE_FIELD(planTree); + COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(resultRelations); + COPY_NODE_FIELD(into); + COPY_NODE_FIELD(returningLists); + COPY_NODE_FIELD(rowMarks); + COPY_SCALAR_FIELD(nParamExec); + + return newnode; +} + /* * CopyPlanFields * @@ -84,7 +105,6 @@ CopyPlanFields(Plan *from, Plan *newnode) COPY_NODE_FIELD(initPlan); COPY_BITMAPSET_FIELD(extParam); COPY_BITMAPSET_FIELD(allParam); - COPY_SCALAR_FIELD(nParamExec); } /* @@ -698,6 +718,23 @@ _copyRangeVar(RangeVar *from) return newnode; } +/* + * _copyIntoClause + */ +static IntoClause * +_copyIntoClause(IntoClause *from) +{ + IntoClause *newnode = makeNode(IntoClause); + + COPY_NODE_FIELD(rel); + COPY_NODE_FIELD(colNames); + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(onCommit); + COPY_STRING_FIELD(tableSpaceName); + + return newnode; +} + /* * We don't need a _copyExpr because Expr is an abstract supertype which * should never actually get instantiated. Also, since it has no common @@ -1762,9 +1799,6 @@ _copyQuery(Query *from) COPY_NODE_FIELD(utilityStmt); COPY_SCALAR_FIELD(resultRelation); COPY_NODE_FIELD(into); - COPY_NODE_FIELD(intoOptions); - COPY_SCALAR_FIELD(intoOnCommit); - COPY_STRING_FIELD(intoTableSpaceName); COPY_SCALAR_FIELD(hasAggs); COPY_SCALAR_FIELD(hasSubLinks); COPY_NODE_FIELD(rtable); @@ -1779,8 +1813,6 @@ _copyQuery(Query *from) COPY_NODE_FIELD(limitCount); COPY_NODE_FIELD(rowMarks); COPY_NODE_FIELD(setOperations); - COPY_NODE_FIELD(resultRelations); - COPY_NODE_FIELD(returningLists); return newnode; } @@ -1832,10 +1864,6 @@ _copySelectStmt(SelectStmt *from) COPY_NODE_FIELD(distinctClause); COPY_NODE_FIELD(into); - COPY_NODE_FIELD(intoColNames); - COPY_NODE_FIELD(intoOptions); - COPY_SCALAR_FIELD(intoOnCommit); - COPY_STRING_FIELD(intoTableSpaceName); COPY_NODE_FIELD(targetList); COPY_NODE_FIELD(fromClause); COPY_NODE_FIELD(whereClause); @@ -2768,9 +2796,6 @@ _copyExecuteStmt(ExecuteStmt *from) COPY_STRING_FIELD(name); COPY_NODE_FIELD(into); - COPY_NODE_FIELD(intoOptions); - COPY_SCALAR_FIELD(into_on_commit); - COPY_STRING_FIELD(into_tbl_space); COPY_NODE_FIELD(params); return newnode; @@ -2902,6 +2927,9 @@ copyObject(void *from) /* * PLAN NODES */ + case T_PlannedStmt: + retval = _copyPlannedStmt(from); + break; case T_Plan: retval = _copyPlan(from); break; @@ -2990,6 +3018,9 @@ copyObject(void *from) case T_RangeVar: retval = _copyRangeVar(from); break; + case T_IntoClause: + retval = _copyIntoClause(from); + break; case T_Var: retval = _copyVar(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index c17a40bbbb..dafa15f028 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.298 2007/02/03 14:06:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.299 2007/02/20 17:32:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -102,6 +102,18 @@ _equalRangeVar(RangeVar *a, RangeVar *b) return true; } +static bool +_equalIntoClause(IntoClause *a, IntoClause *b) +{ + COMPARE_NODE_FIELD(rel); + COMPARE_NODE_FIELD(colNames); + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(onCommit); + COMPARE_STRING_FIELD(tableSpaceName); + + return true; +} + /* * We don't need an _equalExpr because Expr is an abstract supertype which * should never actually get instantiated. Also, since it has no common @@ -690,9 +702,6 @@ _equalQuery(Query *a, Query *b) COMPARE_NODE_FIELD(utilityStmt); COMPARE_SCALAR_FIELD(resultRelation); COMPARE_NODE_FIELD(into); - COMPARE_NODE_FIELD(intoOptions); - COMPARE_SCALAR_FIELD(intoOnCommit); - COMPARE_STRING_FIELD(intoTableSpaceName); COMPARE_SCALAR_FIELD(hasAggs); COMPARE_SCALAR_FIELD(hasSubLinks); COMPARE_NODE_FIELD(rtable); @@ -707,8 +716,6 @@ _equalQuery(Query *a, Query *b) COMPARE_NODE_FIELD(limitCount); COMPARE_NODE_FIELD(rowMarks); COMPARE_NODE_FIELD(setOperations); - COMPARE_NODE_FIELD(resultRelations); - COMPARE_NODE_FIELD(returningLists); return true; } @@ -752,10 +759,6 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b) { COMPARE_NODE_FIELD(distinctClause); COMPARE_NODE_FIELD(into); - COMPARE_NODE_FIELD(intoColNames); - COMPARE_NODE_FIELD(intoOptions); - COMPARE_SCALAR_FIELD(intoOnCommit); - COMPARE_STRING_FIELD(intoTableSpaceName); COMPARE_NODE_FIELD(targetList); COMPARE_NODE_FIELD(fromClause); COMPARE_NODE_FIELD(whereClause); @@ -1545,9 +1548,6 @@ _equalExecuteStmt(ExecuteStmt *a, ExecuteStmt *b) { COMPARE_STRING_FIELD(name); COMPARE_NODE_FIELD(into); - COMPARE_NODE_FIELD(intoOptions); - COMPARE_SCALAR_FIELD(into_on_commit); - COMPARE_STRING_FIELD(into_tbl_space); COMPARE_NODE_FIELD(params); return true; @@ -1967,6 +1967,9 @@ equal(void *a, void *b) case T_RangeVar: retval = _equalRangeVar(a, b); break; + case T_IntoClause: + retval = _equalIntoClause(a, b); + break; case T_Var: retval = _equalVar(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 0600f63b55..c8cc25abb1 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.299 2007/02/19 07:03:27 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.300 2007/02/20 17:32:15 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -234,6 +234,22 @@ _outDatum(StringInfo str, Datum value, int typlen, bool typbyval) * Stuff from plannodes.h */ +static void +_outPlannedStmt(StringInfo str, PlannedStmt *node) +{ + WRITE_NODE_TYPE("PLANNEDSTMT"); + + WRITE_ENUM_FIELD(commandType, CmdType); + WRITE_BOOL_FIELD(canSetTag); + WRITE_NODE_FIELD(planTree); + WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(resultRelations); + WRITE_NODE_FIELD(into); + WRITE_NODE_FIELD(returningLists); + WRITE_NODE_FIELD(rowMarks); + WRITE_INT_FIELD(nParamExec); +} + /* * print the basic stuff of all nodes that inherit from Plan */ @@ -251,7 +267,6 @@ _outPlanInfo(StringInfo str, Plan *node) WRITE_NODE_FIELD(initPlan); WRITE_BITMAPSET_FIELD(extParam); WRITE_BITMAPSET_FIELD(allParam); - WRITE_INT_FIELD(nParamExec); } /* @@ -635,6 +650,18 @@ _outRangeVar(StringInfo str, RangeVar *node) WRITE_NODE_FIELD(alias); } +static void +_outIntoClause(StringInfo str, IntoClause *node) +{ + WRITE_NODE_TYPE("INTOCLAUSE"); + + WRITE_NODE_FIELD(rel); + WRITE_NODE_FIELD(colNames); + WRITE_NODE_FIELD(options); + WRITE_ENUM_FIELD(onCommit, OnCommitAction); + WRITE_STRING_FIELD(tableSpaceName); +} + static void _outVar(StringInfo str, Var *node) { @@ -1245,6 +1272,8 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node) WRITE_NODE_FIELD(glob); WRITE_UINT_FIELD(query_level); WRITE_NODE_FIELD(join_rel_list); + WRITE_NODE_FIELD(resultRelations); + WRITE_NODE_FIELD(returningLists); WRITE_NODE_FIELD(init_plans); WRITE_NODE_FIELD(eq_classes); WRITE_NODE_FIELD(canon_pathkeys); @@ -1502,10 +1531,6 @@ _outSelectStmt(StringInfo str, SelectStmt *node) WRITE_NODE_FIELD(distinctClause); WRITE_NODE_FIELD(into); - WRITE_NODE_FIELD(intoColNames); - WRITE_NODE_FIELD(intoOptions); - WRITE_ENUM_FIELD(intoOnCommit, OnCommitAction); - WRITE_STRING_FIELD(intoTableSpaceName); WRITE_NODE_FIELD(targetList); WRITE_NODE_FIELD(fromClause); WRITE_NODE_FIELD(whereClause); @@ -1651,9 +1676,6 @@ _outQuery(StringInfo str, Query *node) WRITE_INT_FIELD(resultRelation); WRITE_NODE_FIELD(into); - WRITE_NODE_FIELD(intoOptions); - WRITE_ENUM_FIELD(intoOnCommit, OnCommitAction); - WRITE_STRING_FIELD(intoTableSpaceName); WRITE_BOOL_FIELD(hasAggs); WRITE_BOOL_FIELD(hasSubLinks); WRITE_NODE_FIELD(rtable); @@ -1668,8 +1690,6 @@ _outQuery(StringInfo str, Query *node) WRITE_NODE_FIELD(limitCount); WRITE_NODE_FIELD(rowMarks); WRITE_NODE_FIELD(setOperations); - WRITE_NODE_FIELD(resultRelations); - WRITE_NODE_FIELD(returningLists); } static void @@ -1988,6 +2008,9 @@ _outNode(StringInfo str, void *obj) appendStringInfoChar(str, '{'); switch (nodeTag(obj)) { + case T_PlannedStmt: + _outPlannedStmt(str, obj); + break; case T_Plan: _outPlan(str, obj); break; @@ -2072,6 +2095,9 @@ _outNode(StringInfo str, void *obj) case T_RangeVar: _outRangeVar(str, obj); break; + case T_IntoClause: + _outIntoClause(str, obj); + break; case T_Var: _outVar(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 17d36b4efe..6478dce4b1 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.202 2007/02/03 14:06:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.203 2007/02/20 17:32:15 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -140,9 +140,6 @@ _readQuery(void) READ_NODE_FIELD(utilityStmt); READ_INT_FIELD(resultRelation); READ_NODE_FIELD(into); - READ_NODE_FIELD(intoOptions); - READ_ENUM_FIELD(intoOnCommit, OnCommitAction); - READ_STRING_FIELD(intoTableSpaceName); READ_BOOL_FIELD(hasAggs); READ_BOOL_FIELD(hasSubLinks); READ_NODE_FIELD(rtable); @@ -157,8 +154,6 @@ _readQuery(void) READ_NODE_FIELD(limitCount); READ_NODE_FIELD(rowMarks); READ_NODE_FIELD(setOperations); - READ_NODE_FIELD(resultRelations); - READ_NODE_FIELD(returningLists); READ_DONE(); } @@ -287,6 +282,20 @@ _readRangeVar(void) READ_DONE(); } +static IntoClause * +_readIntoClause(void) +{ + READ_LOCALS(IntoClause); + + READ_NODE_FIELD(rel); + READ_NODE_FIELD(colNames); + READ_NODE_FIELD(options); + READ_ENUM_FIELD(onCommit, OnCommitAction); + READ_STRING_FIELD(tableSpaceName); + + READ_DONE(); +} + /* * _readVar */ @@ -984,6 +993,8 @@ parseNodeString(void) return_value = _readAlias(); else if (MATCH("RANGEVAR", 8)) return_value = _readRangeVar(); + else if (MATCH("INTOCLAUSE", 10)) + return_value = _readIntoClause(); else if (MATCH("VAR", 3)) return_value = _readVar(); else if (MATCH("CONST", 5)) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 1cb1d86be1..6c6e80071c 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.159 2007/02/19 07:03:28 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.160 2007/02/20 17:32:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -444,8 +444,8 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, Query *subquery = rte->subquery; bool *differentTypes; double tuple_fraction; + PlannerInfo *subroot; List *pathkeys; - List *subquery_pathkeys; /* We need a workspace for keeping track of set-op type coercions */ differentTypes = (bool *) @@ -520,7 +520,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, rel->subplan = subquery_planner(root->glob, subquery, root->query_level + 1, tuple_fraction, - &subquery_pathkeys); + &subroot); /* Copy number of output rows from subplan */ rel->tuples = rel->subplan->plan_rows; @@ -529,7 +529,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, set_baserel_size_estimates(root, rel); /* Convert subquery pathkeys to outer representation */ - pathkeys = convert_subquery_pathkeys(root, rel, subquery_pathkeys); + pathkeys = convert_subquery_pathkeys(root, rel, subroot->query_pathkeys); /* Generate appropriate path */ add_path(rel, create_subqueryscan_path(rel, pathkeys)); diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c index be26d17a28..406cc9dd49 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.27 2007/02/19 07:03:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.28 2007/02/20 17:32:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -453,8 +453,7 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info) subroot.init_plans = NIL; subparse->commandType = CMD_SELECT; subparse->resultRelation = 0; - subparse->resultRelations = NIL; - subparse->returningLists = NIL; + subparse->returningList = NIL; subparse->into = NULL; subparse->hasAggs = false; subparse->groupClause = NIL; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index f4a940175e..3bb603a0f6 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.213 2007/02/19 07:03:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.214 2007/02/20 17:32:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -79,13 +79,15 @@ static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist); * Query optimizer entry point * *****************************************************************************/ -Plan * +PlannedStmt * planner(Query *parse, bool isCursor, int cursorOptions, ParamListInfo boundParams) { + PlannedStmt *result; PlannerGlobal *glob; double tuple_fraction; - Plan *result_plan; + PlannerInfo *root; + Plan *top_plan; /* * Set up global state for this planner invocation. This data is needed @@ -117,7 +119,7 @@ planner(Query *parse, bool isCursor, int cursorOptions, } /* primary planning entry point (may recurse for subqueries) */ - result_plan = subquery_planner(glob, parse, 1, tuple_fraction, NULL); + top_plan = subquery_planner(glob, parse, 1, tuple_fraction, &root); /* * If creating a plan for a scrollable cursor, make sure it can run @@ -125,17 +127,27 @@ planner(Query *parse, bool isCursor, int cursorOptions, */ if (isCursor && (cursorOptions & CURSOR_OPT_SCROLL)) { - if (!ExecSupportsBackwardScan(result_plan)) - result_plan = materialize_finished_plan(result_plan); + if (!ExecSupportsBackwardScan(top_plan)) + top_plan = materialize_finished_plan(top_plan); } /* final cleanup of the plan */ - result_plan = set_plan_references(result_plan, parse->rtable); + top_plan = set_plan_references(top_plan, parse->rtable); - /* executor wants to know total number of Params used overall */ - result_plan->nParamExec = list_length(glob->paramlist); + /* build the PlannedStmt result */ + result = makeNode(PlannedStmt); - return result_plan; + result->commandType = parse->commandType; + result->canSetTag = parse->canSetTag; + result->planTree = top_plan; + result->rtable = parse->rtable; + result->resultRelations = root->resultRelations; + result->into = parse->into; + result->returningLists = root->returningLists; + result->rowMarks = parse->rowMarks; + result->nParamExec = list_length(glob->paramlist); + + return result; } @@ -150,8 +162,8 @@ planner(Query *parse, bool isCursor, int cursorOptions, * tuple_fraction is the fraction of tuples we expect will be retrieved. * tuple_fraction is interpreted as explained for grouping_planner, below. * - * If subquery_pathkeys isn't NULL, it receives a list of pathkeys indicating - * the output sort ordering of the completed plan. + * If subroot isn't NULL, we pass back the query's final PlannerInfo struct; + * among other things this tells the output sort ordering of the plan. * * Basically, this routine does the stuff that should only be done once * per Query object. It then calls grouping_planner. At one time, @@ -168,7 +180,7 @@ planner(Query *parse, bool isCursor, int cursorOptions, Plan * subquery_planner(PlannerGlobal *glob, Query *parse, Index level, double tuple_fraction, - List **subquery_pathkeys) + PlannerInfo **subroot) { int saved_plan_id = glob->next_plan_id; PlannerInfo *root; @@ -375,9 +387,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse, if (root->glob->next_plan_id != saved_plan_id || root->query_level > 1) SS_finalize_plan(root, plan); - /* Return sort ordering info if caller wants it */ - if (subquery_pathkeys) - *subquery_pathkeys = root->query_pathkeys; + /* Return internal info if caller wants it */ + if (subroot) + *subroot = root; return plan; } @@ -593,14 +605,14 @@ inheritance_planner(PlannerInfo *root) /* Build list of per-relation RETURNING targetlists */ if (parse->returningList) { - Assert(list_length(subroot.parse->returningLists) == 1); + Assert(list_length(subroot.returningLists) == 1); returningLists = list_concat(returningLists, - subroot.parse->returningLists); + subroot.returningLists); } } - parse->resultRelations = resultRelations; - parse->returningLists = returningLists; + root->resultRelations = resultRelations; + root->returningLists = returningLists; /* Mark result as unordered (probably unnecessary) */ root->query_pathkeys = NIL; @@ -1101,8 +1113,16 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) rlist = set_returning_clause_references(parse->returningList, result_plan, parse->resultRelation); - parse->returningLists = list_make1(rlist); + root->returningLists = list_make1(rlist); } + else + root->returningLists = NIL; + + /* Compute result-relations list if needed */ + if (parse->resultRelation) + root->resultRelations = list_make1_int(parse->resultRelation); + else + root->resultRelations = NIL; /* * Return the actual output ordering in query_pathkeys for possible use by diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 0eb81d6d89..63a5999a40 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.360 2007/02/01 19:10:27 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.361 2007/02/20 17:32:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2143,11 +2143,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) if (stmt->into) { qry->into = stmt->into; - if (stmt->intoColNames) - applyColumnNames(qry->targetList, stmt->intoColNames); - qry->intoOptions = copyObject(stmt->intoOptions); - qry->intoOnCommit = stmt->intoOnCommit; - qry->intoTableSpaceName = stmt->intoTableSpaceName; + if (stmt->into->colNames) + applyColumnNames(qry->targetList, stmt->into->colNames); } qry->rtable = pstate->p_rtable; @@ -2315,11 +2312,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) if (stmt->into) { qry->into = stmt->into; - if (stmt->intoColNames) - applyColumnNames(qry->targetList, stmt->intoColNames); - qry->intoOptions = copyObject(stmt->intoOptions); - qry->intoOnCommit = stmt->intoOnCommit; - qry->intoTableSpaceName = stmt->intoTableSpaceName; + if (stmt->into->colNames) + applyColumnNames(qry->targetList, stmt->into->colNames); } /* @@ -2399,9 +2393,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) /* * Find leftmost leaf SelectStmt; extract the one-time-only items from it - * and from the top-level node. (Most of the INTO options can be - * transferred to the Query immediately, but intoColNames has to be saved - * to apply below.) + * and from the top-level node. */ leftmostSelect = stmt->larg; while (leftmostSelect && leftmostSelect->op != SETOP_NONE) @@ -2411,10 +2403,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) if (leftmostSelect->into) { qry->into = leftmostSelect->into; - intoColNames = leftmostSelect->intoColNames; - qry->intoOptions = copyObject(leftmostSelect->intoOptions); - qry->intoOnCommit = leftmostSelect->intoOnCommit; - qry->intoTableSpaceName = leftmostSelect->intoTableSpaceName; + intoColNames = leftmostSelect->into->colNames; } /* clear this to prevent complaints in transformSetOperationTree() */ diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index cf25c5607a..3204d0a401 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.579 2007/02/03 14:06:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.580 2007/02/20 17:32:16 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -139,6 +139,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) IndexElem *ielem; Alias *alias; RangeVar *range; + IntoClause *into; A_Indices *aind; ResTarget *target; PrivTarget *privtarget; @@ -248,7 +249,8 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) prep_type_clause execute_param_clause using_clause returning_clause -%type into_clause OptTempTableName +%type OptTempTableName +%type into_clause create_as_target %type createfunc_opt_item common_func_opt_item %type func_arg @@ -2253,8 +2255,7 @@ OptConsTableSpace: USING INDEX TABLESPACE name { $$ = $4; } */ CreateAsStmt: - CREATE OptTemp TABLE qualified_name OptCreateAs - OptWith OnCommitOption OptTableSpace AS SelectStmt + CREATE OptTemp TABLE create_as_target AS SelectStmt { /* * When the SelectStmt is a set-operation tree, we must @@ -2263,18 +2264,26 @@ CreateAsStmt: * to find it. Similarly, the output column names must * be attached to that Select's target list. */ - SelectStmt *n = findLeftmostSelect((SelectStmt *) $10); + SelectStmt *n = findLeftmostSelect((SelectStmt *) $6); if (n->into != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("CREATE TABLE AS cannot specify INTO"))); - $4->istemp = $2; + $4->rel->istemp = $2; n->into = $4; - n->intoColNames = $5; - n->intoOptions = $6; - n->intoOnCommit = $7; - n->intoTableSpaceName = $8; - $$ = $10; + $$ = $6; + } + ; + +create_as_target: + qualified_name OptCreateAs OptWith OnCommitOption OptTableSpace + { + $$ = makeNode(IntoClause); + $$->rel = $1; + $$->colNames = $2; + $$->options = $3; + $$->onCommit = $4; + $$->tableSpaceName = $5; } ; @@ -5459,19 +5468,15 @@ ExecuteStmt: EXECUTE name execute_param_clause n->into = NULL; $$ = (Node *) n; } - | CREATE OptTemp TABLE qualified_name OptCreateAs - OptWith OnCommitOption OptTableSpace AS + | CREATE OptTemp TABLE create_as_target AS EXECUTE name execute_param_clause { ExecuteStmt *n = makeNode(ExecuteStmt); - n->name = $11; - n->params = $12; - $4->istemp = $2; + n->name = $7; + n->params = $8; + $4->rel->istemp = $2; n->into = $4; - n->intoOptions = $6; - n->into_on_commit = $7; - n->into_tbl_space = $8; - if ($5) + if ($4->colNames) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("column name list not allowed in CREATE TABLE / AS EXECUTE"))); @@ -5854,7 +5859,6 @@ simple_select: n->distinctClause = $2; n->targetList = $3; n->into = $4; - n->intoColNames = NIL; n->fromClause = $5; n->whereClause = $6; n->groupClause = $7; @@ -5877,8 +5881,17 @@ simple_select: ; into_clause: - INTO OptTempTableName { $$ = $2; } - | /*EMPTY*/ { $$ = NULL; } + INTO OptTempTableName + { + $$ = makeNode(IntoClause); + $$->rel = $2; + $$->colNames = NIL; + $$->options = NIL; + $$->onCommit = ONCOMMIT_NOOP; + $$->tableSpaceName = NULL; + } + | /*EMPTY*/ + { $$ = NULL; } ; /* diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index f00897ee62..1c40a8752e 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.524 2007/02/17 19:33:32 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.525 2007/02/20 17:32:16 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -165,8 +165,7 @@ static int InteractiveBackend(StringInfo inBuf); static int SocketBackend(StringInfo inBuf); static int ReadCommand(StringInfo inBuf); static List *pg_rewrite_queries(List *querytree_list); -static bool check_log_statement_raw(List *raw_parsetree_list); -static bool check_log_statement_cooked(List *parsetree_list); +static bool check_log_statement(List *stmt_list); static int errdetail_execute(List *raw_parsetree_list); static int errdetail_params(ParamListInfo params); static void start_xact_command(void); @@ -659,10 +658,10 @@ pg_rewrite_queries(List *querytree_list) /* Generate a plan for a single already-rewritten query. */ -Plan * +PlannedStmt * pg_plan_query(Query *querytree, ParamListInfo boundParams) { - Plan *plan; + PlannedStmt *plan; /* Utility commands have no plans. */ if (querytree->commandType == CMD_UTILITY) @@ -680,7 +679,7 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams) #ifdef COPY_PARSE_PLAN_TREES /* Optional debugging check: pass plan output through copyObject() */ { - Plan *new_plan = (Plan *) copyObject(plan); + PlannedStmt *new_plan = (PlannedStmt *) copyObject(plan); /* * equal() currently does not have routines to compare Plan nodes, so @@ -715,23 +714,26 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams) * utility statements depend on not having frozen the snapshot yet. * (We assume that such statements cannot appear together with plannable * statements in the rewriter's output.) + * + * Normal optimizable statements generate PlannedStmt entries in the result + * list. Utility statements are simply represented by their statement nodes. */ List * pg_plan_queries(List *querytrees, ParamListInfo boundParams, bool needSnapshot) { - List *plan_list = NIL; + List *stmt_list = NIL; ListCell *query_list; foreach(query_list, querytrees) { Query *query = (Query *) lfirst(query_list); - Plan *plan; + Node *stmt; if (query->commandType == CMD_UTILITY) { /* Utility commands have no plans. */ - plan = NULL; + stmt = query->utilityStmt; } else { @@ -740,13 +742,13 @@ pg_plan_queries(List *querytrees, ParamListInfo boundParams, ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); needSnapshot = false; } - plan = pg_plan_query(query, boundParams); + stmt = (Node *) pg_plan_query(query, boundParams); } - plan_list = lappend(plan_list, plan); + stmt_list = lappend(stmt_list, stmt); } - return plan_list; + return stmt_list; } @@ -817,7 +819,7 @@ exec_simple_query(const char *query_string) parsetree_list = pg_parse_query(query_string); /* Log immediately if dictated by log_statement */ - if (check_log_statement_raw(parsetree_list)) + if (check_log_statement(parsetree_list)) { ereport(LOG, (errmsg("statement: %s", query_string), @@ -905,7 +907,6 @@ exec_simple_query(const char *query_string) NULL, query_string, commandTag, - querytree_list, plantree_list, MessageContext); @@ -1050,9 +1051,10 @@ exec_parse_message(const char *query_string, /* string to execute */ List *parsetree_list; const char *commandTag; List *querytree_list, - *plantree_list, + *stmt_list, *param_list; bool is_named; + bool fully_planned; bool save_log_statement_stats = log_statement_stats; char msec_str[32]; @@ -1202,17 +1204,23 @@ exec_parse_message(const char *query_string, /* string to execute */ * planning until Bind. Otherwise do it now. */ if (!is_named && numParams > 0) - plantree_list = NIL; + { + stmt_list = querytree_list; + fully_planned = false; + } else - plantree_list = pg_plan_queries(querytree_list, NULL, true); + { + stmt_list = pg_plan_queries(querytree_list, NULL, true); + fully_planned = true; + } } else { /* Empty input string. This is legal. */ commandTag = NULL; - querytree_list = NIL; - plantree_list = NIL; + stmt_list = NIL; param_list = NIL; + fully_planned = true; } /* If we got a cancel signal in analysis or planning, quit */ @@ -1226,9 +1234,9 @@ exec_parse_message(const char *query_string, /* string to execute */ StorePreparedStatement(stmt_name, query_string, commandTag, - querytree_list, - plantree_list, + stmt_list, param_list, + fully_planned, false); } else @@ -1240,9 +1248,9 @@ exec_parse_message(const char *query_string, /* string to execute */ pstmt->query_string = pstrdup(query_string); /* the rest is there already */ pstmt->commandTag = commandTag; - pstmt->query_list = querytree_list; - pstmt->plan_list = plantree_list; + pstmt->stmt_list = stmt_list; pstmt->argtype_list = param_list; + pstmt->fully_planned = fully_planned; pstmt->from_sql = false; pstmt->context = unnamed_stmt_context; /* Now the unnamed statement is complete and valid */ @@ -1393,7 +1401,7 @@ exec_bind_message(StringInfo input_message) * functions. */ if (IsAbortedTransactionBlockState() && - (!IsTransactionExitStmtList(pstmt->query_list) || + (!IsTransactionExitStmtList(pstmt->stmt_list) || numParams != 0)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), @@ -1581,22 +1589,21 @@ exec_bind_message(StringInfo input_message) * portal's queryContext becomes its own heap context rather than the * prepared statement's context. FIXME someday */ - if (pstmt->plan_list == NIL && pstmt->query_list != NIL) + if (pstmt->fully_planned) + { + plan_list = pstmt->stmt_list; + qContext = pstmt->context; + } + else { MemoryContext oldContext; qContext = PortalGetHeapMemory(portal); oldContext = MemoryContextSwitchTo(qContext); - query_list = copyObject(pstmt->query_list); + query_list = copyObject(pstmt->stmt_list); plan_list = pg_plan_queries(query_list, params, true); MemoryContextSwitchTo(oldContext); } - else - { - query_list = pstmt->query_list; - plan_list = pstmt->plan_list; - qContext = pstmt->context; - } /* * Define portal and start execution. @@ -1605,7 +1612,6 @@ exec_bind_message(StringInfo input_message) *pstmt->stmt_name ? pstmt->stmt_name : NULL, pstmt->query_string, pstmt->commandTag, - query_list, plan_list, qContext); @@ -1688,13 +1694,13 @@ exec_execute_message(const char *portal_name, long max_rows) */ if (portal->commandTag == NULL) { - Assert(portal->parseTrees == NIL); + Assert(portal->stmts == NIL); NullCommand(dest); return; } /* Does the portal contain a transaction command? */ - is_xact_command = IsTransactionStmtList(portal->parseTrees); + is_xact_command = IsTransactionStmtList(portal->stmts); /* * We must copy the sourceText and prepStmtName into MessageContext in @@ -1760,7 +1766,7 @@ exec_execute_message(const char *portal_name, long max_rows) execute_is_fetch = !portal->atStart; /* Log immediately if dictated by log_statement */ - if (check_log_statement_cooked(portal->parseTrees)) + if (check_log_statement(portal->stmts)) { ereport(LOG, (errmsg("%s %s%s%s%s%s", @@ -1781,7 +1787,7 @@ exec_execute_message(const char *portal_name, long max_rows) * actually run are those containing COMMIT or ROLLBACK commands. */ if (IsAbortedTransactionBlockState() && - !IsTransactionExitStmtList(portal->parseTrees)) + !IsTransactionExitStmtList(portal->stmts)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " @@ -1865,15 +1871,16 @@ exec_execute_message(const char *portal_name, long max_rows) } /* - * check_log_statement_raw + * check_log_statement * Determine whether command should be logged because of log_statement * - * raw_parsetree_list is the raw grammar output + * parsetree_list can be either raw grammar output or a list of planned + * statements */ static bool -check_log_statement_raw(List *raw_parsetree_list) +check_log_statement(List *stmt_list) { - ListCell *parsetree_item; + ListCell *stmt_item; if (log_statement == LOGSTMT_NONE) return false; @@ -1881,37 +1888,11 @@ check_log_statement_raw(List *raw_parsetree_list) return true; /* Else we have to inspect the statement(s) to see whether to log */ - foreach(parsetree_item, raw_parsetree_list) + foreach(stmt_item, stmt_list) { - Node *parsetree = (Node *) lfirst(parsetree_item); + Node *stmt = (Node *) lfirst(stmt_item); - if (GetCommandLogLevel(parsetree) <= log_statement) - return true; - } - - return false; -} - -/* - * check_log_statement_cooked - * As above, but work from already-analyzed querytrees - */ -static bool -check_log_statement_cooked(List *parsetree_list) -{ - ListCell *parsetree_item; - - if (log_statement == LOGSTMT_NONE) - return false; - if (log_statement == LOGSTMT_ALL) - return true; - - /* Else we have to inspect the statement(s) to see whether to log */ - foreach(parsetree_item, parsetree_list) - { - Query *parsetree = (Query *) lfirst(parsetree_item); - - if (GetQueryLogLevel(parsetree) <= log_statement) + if (GetCommandLogLevel(stmt) <= log_statement) return true; } @@ -2259,6 +2240,7 @@ finish_xact_command(void) * ones that we allow in transaction-aborted state. */ +/* Test a bare parsetree */ static bool IsTransactionExitStmt(Node *parsetree) { @@ -2275,29 +2257,45 @@ IsTransactionExitStmt(Node *parsetree) return false; } +/* Test a list that might contain Query nodes or bare parsetrees */ static bool IsTransactionExitStmtList(List *parseTrees) { if (list_length(parseTrees) == 1) { - Query *query = (Query *) linitial(parseTrees); + Node *stmt = (Node *) linitial(parseTrees); - if (query->commandType == CMD_UTILITY && - IsTransactionExitStmt(query->utilityStmt)) + if (IsA(stmt, Query)) + { + Query *query = (Query *) stmt; + + if (query->commandType == CMD_UTILITY && + IsTransactionExitStmt(query->utilityStmt)) + return true; + } + else if (IsTransactionExitStmt(stmt)) return true; } return false; } +/* Test a list that might contain Query nodes or bare parsetrees */ static bool IsTransactionStmtList(List *parseTrees) { if (list_length(parseTrees) == 1) { - Query *query = (Query *) linitial(parseTrees); + Node *stmt = (Node *) linitial(parseTrees); - if (query->commandType == CMD_UTILITY && - query->utilityStmt && IsA(query->utilityStmt, TransactionStmt)) + if (IsA(stmt, Query)) + { + Query *query = (Query *) stmt; + + if (query->commandType == CMD_UTILITY && + IsA(query->utilityStmt, TransactionStmt)) + return true; + } + else if (IsA(stmt, TransactionStmt)) return true; } return false; diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index f6f157e0cb..97a003ac89 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.113 2007/02/18 19:49:25 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.114 2007/02/20 17:32:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,8 +32,7 @@ Portal ActivePortal = NULL; -static void ProcessQuery(Query *parsetree, - Plan *plan, +static void ProcessQuery(PlannedStmt *plan, ParamListInfo params, DestReceiver *dest, char *completionTag); @@ -42,7 +41,7 @@ static uint32 RunFromStore(Portal portal, ScanDirection direction, long count, DestReceiver *dest); static long PortalRunSelect(Portal portal, bool forward, long count, DestReceiver *dest); -static void PortalRunUtility(Portal portal, Query *query, +static void PortalRunUtility(Portal portal, Node *utilityStmt, DestReceiver *dest, char *completionTag); static void PortalRunMulti(Portal portal, DestReceiver *dest, DestReceiver *altdest, @@ -58,8 +57,7 @@ static void DoPortalRewind(Portal portal); * CreateQueryDesc */ QueryDesc * -CreateQueryDesc(Query *parsetree, - Plan *plantree, +CreateQueryDesc(PlannedStmt *plannedstmt, Snapshot snapshot, Snapshot crosscheck_snapshot, DestReceiver *dest, @@ -68,10 +66,10 @@ CreateQueryDesc(Query *parsetree, { QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc)); - qd->operation = parsetree->commandType; /* operation */ - qd->parsetree = parsetree; /* parse tree */ - qd->plantree = plantree; /* plan */ - qd->snapshot = snapshot; /* snapshot */ + qd->operation = plannedstmt->commandType; /* operation */ + qd->plannedstmt = plannedstmt; /* plan */ + qd->utilitystmt = NULL; + qd->snapshot = snapshot; /* snapshot */ qd->crosscheck_snapshot = crosscheck_snapshot; /* RI check snapshot */ qd->dest = dest; /* output dest */ qd->params = params; /* parameter values passed into query */ @@ -85,6 +83,34 @@ CreateQueryDesc(Query *parsetree, return qd; } +/* + * CreateUtilityQueryDesc + */ +QueryDesc * +CreateUtilityQueryDesc(Node *utilitystmt, + Snapshot snapshot, + DestReceiver *dest, + ParamListInfo params) +{ + QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc)); + + qd->operation = CMD_UTILITY; /* operation */ + qd->plannedstmt = NULL; + qd->utilitystmt = utilitystmt; /* utility command */ + qd->snapshot = snapshot; /* snapshot */ + qd->crosscheck_snapshot = InvalidSnapshot; /* RI check snapshot */ + qd->dest = dest; /* output dest */ + qd->params = params; /* parameter values passed into query */ + qd->doInstrument = false; /* uninteresting for utilities */ + + /* null these fields until set by ExecutorStart */ + qd->tupDesc = NULL; + qd->estate = NULL; + qd->planstate = NULL; + + return qd; +} + /* * FreeQueryDesc */ @@ -103,7 +129,6 @@ FreeQueryDesc(QueryDesc *qdesc) * Execute a single plannable query within a PORTAL_MULTI_QUERY * or PORTAL_ONE_RETURNING portal * - * parsetree: the query tree * plan: the plan tree for the query * params: any parameters needed * dest: where to send results @@ -116,13 +141,11 @@ FreeQueryDesc(QueryDesc *qdesc) * error; otherwise the executor's memory usage will be leaked. */ static void -ProcessQuery(Query *parsetree, - Plan *plan, +ProcessQuery(PlannedStmt *plan, ParamListInfo params, DestReceiver *dest, char *completionTag) { - int operation = parsetree->commandType; QueryDesc *queryDesc; ereport(DEBUG3, @@ -137,7 +160,7 @@ ProcessQuery(Query *parsetree, /* * Create the QueryDesc object */ - queryDesc = CreateQueryDesc(parsetree, plan, + queryDesc = CreateQueryDesc(plan, ActiveSnapshot, InvalidSnapshot, dest, params, false); @@ -163,7 +186,7 @@ ProcessQuery(Query *parsetree, { Oid lastOid; - switch (operation) + switch (queryDesc->operation) { case CMD_SELECT: strcpy(completionTag, "SELECT"); @@ -206,40 +229,67 @@ ProcessQuery(Query *parsetree, /* * ChoosePortalStrategy - * Select portal execution strategy given the intended query list. + * Select portal execution strategy given the intended statement list. + * + * The list elements can be Querys, PlannedStmts, or utility statements. + * That's more general than portals need, but we use this for prepared + * statements as well. * * See the comments in portal.h. */ PortalStrategy -ChoosePortalStrategy(List *parseTrees) +ChoosePortalStrategy(List *stmts) { int nSetTag; ListCell *lc; /* * PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the - * single-Query-struct case, since there are no rewrite rules that can add + * single-statement case, since there are no rewrite rules that can add * auxiliary queries to a SELECT or a utility command. */ - if (list_length(parseTrees) == 1) + if (list_length(stmts) == 1) { - Query *query = (Query *) linitial(parseTrees); + Node *stmt = (Node *) linitial(stmts); - Assert(IsA(query, Query)); - if (query->canSetTag) + if (IsA(stmt, Query)) { - if (query->commandType == CMD_SELECT && - query->into == NULL) - return PORTAL_ONE_SELECT; - if (query->commandType == CMD_UTILITY && - query->utilityStmt != NULL) + Query *query = (Query *) stmt; + + if (query->canSetTag) { - if (UtilityReturnsTuples(query->utilityStmt)) - return PORTAL_UTIL_SELECT; - /* it can't be ONE_RETURNING, so give up */ - return PORTAL_MULTI_QUERY; + if (query->commandType == CMD_SELECT && + query->into == NULL) + return PORTAL_ONE_SELECT; + if (query->commandType == CMD_UTILITY && + query->utilityStmt != NULL) + { + if (UtilityReturnsTuples(query->utilityStmt)) + return PORTAL_UTIL_SELECT; + /* it can't be ONE_RETURNING, so give up */ + return PORTAL_MULTI_QUERY; + } } } + else if (IsA(stmt, PlannedStmt)) + { + PlannedStmt *pstmt = (PlannedStmt *) stmt; + + if (pstmt->canSetTag) + { + if (pstmt->commandType == CMD_SELECT && + pstmt->into == NULL) + return PORTAL_ONE_SELECT; + } + } + else + { + /* must be a utility command; assume it's canSetTag */ + if (UtilityReturnsTuples(stmt)) + return PORTAL_UTIL_SELECT; + /* it can't be ONE_RETURNING, so give up */ + return PORTAL_MULTI_QUERY; + } } /* @@ -248,18 +298,35 @@ ChoosePortalStrategy(List *parseTrees) * it has a RETURNING list. */ nSetTag = 0; - foreach(lc, parseTrees) + foreach(lc, stmts) { - Query *query = (Query *) lfirst(lc); + Node *stmt = (Node *) lfirst(lc); - Assert(IsA(query, Query)); - if (query->canSetTag) + if (IsA(stmt, Query)) { - if (++nSetTag > 1) - return PORTAL_MULTI_QUERY; /* no need to look further */ - if (query->returningList == NIL) - return PORTAL_MULTI_QUERY; /* no need to look further */ + Query *query = (Query *) stmt; + + if (query->canSetTag) + { + if (++nSetTag > 1) + return PORTAL_MULTI_QUERY; /* no need to look further */ + if (query->returningList == NIL) + return PORTAL_MULTI_QUERY; /* no need to look further */ + } } + else if (IsA(stmt, PlannedStmt)) + { + PlannedStmt *pstmt = (PlannedStmt *) stmt; + + if (pstmt->canSetTag) + { + if (++nSetTag > 1) + return PORTAL_MULTI_QUERY; /* no need to look further */ + if (pstmt->returningLists == NIL) + return PORTAL_MULTI_QUERY; /* no need to look further */ + } + } + /* otherwise, utility command, assumed not canSetTag */ } if (nSetTag == 1) return PORTAL_ONE_RETURNING; @@ -274,48 +341,84 @@ ChoosePortalStrategy(List *parseTrees) * Returns NIL if the portal doesn't have a determinable targetlist. * * Note: do not modify the result. - * - * XXX be careful to keep this in sync with FetchPreparedStatementTargetList, - * and with UtilityReturnsTuples. */ List * FetchPortalTargetList(Portal portal) { - if (portal->strategy == PORTAL_ONE_SELECT) - return ((Query *) linitial(portal->parseTrees))->targetList; - if (portal->strategy == PORTAL_ONE_RETURNING) - return (PortalGetPrimaryQuery(portal))->returningList; - if (portal->strategy == PORTAL_UTIL_SELECT) + /* no point in looking if we determined it doesn't return tuples */ + if (portal->strategy == PORTAL_MULTI_QUERY) + return NIL; + /* get the primary statement and find out what it returns */ + return FetchStatementTargetList(PortalGetPrimaryStmt(portal)); +} + +/* + * FetchStatementTargetList + * Given a statement that returns tuples, extract the query targetlist. + * Returns NIL if the statement doesn't have a determinable targetlist. + * + * This can be applied to a Query, a PlannedStmt, or a utility statement. + * That's more general than portals need, but we use this for prepared + * statements as well. + * + * Note: do not modify the result. + * + * XXX be careful to keep this in sync with UtilityReturnsTuples. + */ +List * +FetchStatementTargetList(Node *stmt) +{ + if (stmt == NULL) + return NIL; + if (IsA(stmt, Query)) { - Node *utilityStmt; + Query *query = (Query *) stmt; - utilityStmt = ((Query *) linitial(portal->parseTrees))->utilityStmt; - switch (nodeTag(utilityStmt)) + if (query->commandType == CMD_UTILITY && + query->utilityStmt != NULL) { - case T_FetchStmt: - { - FetchStmt *substmt = (FetchStmt *) utilityStmt; - Portal subportal; - - Assert(!substmt->ismove); - subportal = GetPortalByName(substmt->portalname); - Assert(PortalIsValid(subportal)); - return FetchPortalTargetList(subportal); - } - - case T_ExecuteStmt: - { - ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt; - PreparedStatement *entry; - - Assert(!substmt->into); - entry = FetchPreparedStatement(substmt->name, true); - return FetchPreparedStatementTargetList(entry); - } - - default: - break; + /* transfer attention to utility statement */ + stmt = query->utilityStmt; } + else + { + if (query->commandType == CMD_SELECT && + query->into == NULL) + return query->targetList; + if (query->returningList) + return query->returningList; + return NIL; + } + } + if (IsA(stmt, PlannedStmt)) + { + PlannedStmt *pstmt = (PlannedStmt *) stmt; + + if (pstmt->commandType == CMD_SELECT && + pstmt->into == NULL) + return pstmt->planTree->targetlist; + if (pstmt->returningLists) + return (List *) linitial(pstmt->returningLists); + return NIL; + } + if (IsA(stmt, FetchStmt)) + { + FetchStmt *fstmt = (FetchStmt *) stmt; + Portal subportal; + + Assert(!fstmt->ismove); + subportal = GetPortalByName(fstmt->portalname); + Assert(PortalIsValid(subportal)); + return FetchPortalTargetList(subportal); + } + if (IsA(stmt, ExecuteStmt)) + { + ExecuteStmt *estmt = (ExecuteStmt *) stmt; + PreparedStatement *entry; + + Assert(!estmt->into); + entry = FetchPreparedStatement(estmt->name, true); + return FetchPreparedStatementTargetList(entry); } return NIL; } @@ -374,7 +477,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) /* * Determine the portal execution strategy */ - portal->strategy = ChoosePortalStrategy(portal->parseTrees); + portal->strategy = ChoosePortalStrategy(portal->stmts); /* * Fire her up according to the strategy @@ -396,8 +499,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) * Create QueryDesc in portal's context; for the moment, set * the destination to DestNone. */ - queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees), - (Plan *) linitial(portal->planTrees), + queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts), ActiveSnapshot, InvalidSnapshot, None_Receiver, @@ -450,8 +552,16 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) * We don't start the executor until we are told to run the * portal. We do need to set up the result tupdesc. */ - portal->tupDesc = - ExecCleanTypeFromTL((PortalGetPrimaryQuery(portal))->returningList, false); + { + PlannedStmt *pstmt; + + pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal); + Assert(IsA(pstmt, PlannedStmt)); + Assert(pstmt->returningLists); + portal->tupDesc = + ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), + false); + } /* * Reset cursor position data to "start of query" @@ -468,8 +578,12 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) * We don't set snapshot here, because PortalRunUtility will * take care of it if needed. */ - portal->tupDesc = - UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt); + { + Node *ustmt = PortalGetPrimaryStmt(portal); + + Assert(!IsA(ustmt, PlannedStmt)); + portal->tupDesc = UtilityTupleDescriptor(ustmt); + } /* * Reset cursor position data to "start of query" @@ -934,7 +1048,7 @@ FillPortalStore(Portal portal) break; case PORTAL_UTIL_SELECT: - PortalRunUtility(portal, linitial(portal->parseTrees), + PortalRunUtility(portal, (Node *) linitial(portal->stmts), treceiver, completionTag); break; @@ -1023,11 +1137,9 @@ RunFromStore(Portal portal, ScanDirection direction, long count, * Execute a utility statement inside a portal. */ static void -PortalRunUtility(Portal portal, Query *query, +PortalRunUtility(Portal portal, Node *utilityStmt, DestReceiver *dest, char *completionTag) { - Node *utilityStmt = query->utilityStmt; - ereport(DEBUG3, (errmsg_internal("ProcessUtility"))); @@ -1061,18 +1173,7 @@ PortalRunUtility(Portal portal, Query *query, else ActiveSnapshot = NULL; - if (query->canSetTag) - { - /* utility statement can override default tag string */ - ProcessUtility(utilityStmt, portal->portalParams, dest, completionTag); - if (completionTag && completionTag[0] == '\0' && portal->commandTag) - strcpy(completionTag, portal->commandTag); /* use the default */ - } - else - { - /* utility added by rewrite cannot set tag */ - ProcessUtility(utilityStmt, portal->portalParams, dest, NULL); - } + ProcessUtility(utilityStmt, portal->portalParams, dest, completionTag); /* Some utility statements may change context on us */ MemoryContextSwitchTo(PortalGetHeapMemory(portal)); @@ -1092,8 +1193,7 @@ PortalRunMulti(Portal portal, DestReceiver *dest, DestReceiver *altdest, char *completionTag) { - ListCell *querylist_item; - ListCell *planlist_item; + ListCell *stmtlist_item; /* * If the destination is DestRemoteExecute, change to DestNone. The @@ -1114,47 +1214,36 @@ PortalRunMulti(Portal portal, * Loop to handle the individual queries generated from a single parsetree * by analysis and rewrite. */ - forboth(querylist_item, portal->parseTrees, - planlist_item, portal->planTrees) + foreach(stmtlist_item, portal->stmts) { - Query *query = (Query *) lfirst(querylist_item); - Plan *plan = (Plan *) lfirst(planlist_item); + Node *stmt = (Node *) lfirst(stmtlist_item); /* * If we got a cancel signal in prior command, quit */ CHECK_FOR_INTERRUPTS(); - if (query->commandType == CMD_UTILITY) - { - /* - * process utility functions (create, destroy, etc..) - */ - Assert(plan == NULL); - - PortalRunUtility(portal, query, - query->canSetTag ? dest : altdest, - completionTag); - } - else + if (IsA(stmt, PlannedStmt)) { /* * process a plannable query. */ + PlannedStmt *pstmt = (PlannedStmt *) stmt; + if (log_executor_stats) ResetUsage(); - if (query->canSetTag) + if (pstmt->canSetTag) { /* statement can set tag string */ - ProcessQuery(query, plan, + ProcessQuery(pstmt, portal->portalParams, dest, completionTag); } else { /* stmt added by rewrite cannot set tag */ - ProcessQuery(query, plan, + ProcessQuery(pstmt, portal->portalParams, altdest, NULL); } @@ -1162,12 +1251,25 @@ PortalRunMulti(Portal portal, if (log_executor_stats) ShowUsage("EXECUTOR STATISTICS"); } + else + { + /* + * process utility functions (create, destroy, etc..) + * + * These are assumed canSetTag if they're the only stmt in the + * portal. + */ + if (list_length(portal->stmts) == 1) + PortalRunUtility(portal, stmt, dest, completionTag); + else + PortalRunUtility(portal, stmt, altdest, NULL); + } /* * Increment command counter between queries, but not after the last * one. */ - if (lnext(planlist_item) != NULL) + if (lnext(stmtlist_item) != NULL) CommandCounterIncrement(); /* diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 5cabec2970..47051ad1ed 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.272 2007/02/14 01:58:57 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.273 2007/02/20 17:32:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -248,36 +248,41 @@ CheckRelationOwnership(RangeVar *rel, bool noCatalogs) /* - * QueryIsReadOnly: is an analyzed/rewritten query read-only? + * CommandIsReadOnly: is an executable query read-only? * * This is a much stricter test than we apply for XactReadOnly mode; * the query must be *in truth* read-only, because the caller wishes * not to do CommandCounterIncrement for it. + * + * Note: currently no need to support Query nodes here */ bool -QueryIsReadOnly(Query *parsetree) +CommandIsReadOnly(Node *parsetree) { - switch (parsetree->commandType) + if (IsA(parsetree, PlannedStmt)) { - case CMD_SELECT: - if (parsetree->into != NULL) - return false; /* SELECT INTO */ - else if (parsetree->rowMarks != NIL) - return false; /* SELECT FOR UPDATE/SHARE */ - else - return true; - case CMD_UPDATE: - case CMD_INSERT: - case CMD_DELETE: - return false; - case CMD_UTILITY: - /* For now, treat all utility commands as read/write */ - return false; - default: - elog(WARNING, "unrecognized commandType: %d", - (int) parsetree->commandType); - break; + PlannedStmt *stmt = (PlannedStmt *) parsetree; + + switch (stmt->commandType) + { + case CMD_SELECT: + if (stmt->into != NULL) + return false; /* SELECT INTO */ + else if (stmt->rowMarks != NIL) + return false; /* SELECT FOR UPDATE/SHARE */ + else + return true; + case CMD_UPDATE: + case CMD_INSERT: + case CMD_DELETE: + return false; + default: + elog(WARNING, "unrecognized commandType: %d", + (int) stmt->commandType); + break; + } } + /* For now, treat all utility commands as read/write */ return false; } @@ -1161,7 +1166,7 @@ UtilityReturnsTuples(Node *parsetree) entry = FetchPreparedStatement(stmt->name, false); if (!entry) return false; /* not our business to raise error */ - switch (ChoosePortalStrategy(entry->query_list)) + switch (ChoosePortalStrategy(entry->stmt_list)) { case PORTAL_ONE_SELECT: case PORTAL_ONE_RETURNING: @@ -1244,6 +1249,7 @@ UtilityTupleDescriptor(Node *parsetree) * QueryReturnsTuples * Return "true" if this Query will send output to the destination. */ +#ifdef NOT_USED bool QueryReturnsTuples(Query *parsetree) { @@ -1270,14 +1276,15 @@ QueryReturnsTuples(Query *parsetree) } return false; /* default */ } +#endif /* * CreateCommandTag - * utility to get a string representation of the - * command operation, given a raw (un-analyzed) parsetree. + * utility to get a string representation of the command operation, + * given either a raw (un-analyzed) parsetree or a planned query. * - * This must handle all raw command types, but since the vast majority + * This must handle all command types, but since the vast majority * of 'em are utility commands, it seems sensible to keep it here. * * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE. @@ -1290,6 +1297,7 @@ CreateCommandTag(Node *parsetree) switch (nodeTag(parsetree)) { + /* raw plannable queries */ case T_InsertStmt: tag = "INSERT"; break; @@ -1306,6 +1314,7 @@ CreateCommandTag(Node *parsetree) tag = "SELECT"; break; + /* utility statements --- same whether raw or cooked */ case T_TransactionStmt: { TransactionStmt *stmt = (TransactionStmt *) parsetree; @@ -1826,6 +1835,95 @@ CreateCommandTag(Node *parsetree) tag = "DEALLOCATE"; break; + /* already-planned queries */ + case T_PlannedStmt: + { + PlannedStmt *stmt = (PlannedStmt *) parsetree; + + switch (stmt->commandType) + { + case CMD_SELECT: + /* + * We take a little extra care here so that the result + * will be useful for complaints about read-only + * statements + */ + if (stmt->into != NULL) + tag = "SELECT INTO"; + else if (stmt->rowMarks != NIL) + { + if (((RowMarkClause *) linitial(stmt->rowMarks))->forUpdate) + tag = "SELECT FOR UPDATE"; + else + tag = "SELECT FOR SHARE"; + } + else + tag = "SELECT"; + break; + case CMD_UPDATE: + tag = "UPDATE"; + break; + case CMD_INSERT: + tag = "INSERT"; + break; + case CMD_DELETE: + tag = "DELETE"; + break; + default: + elog(WARNING, "unrecognized commandType: %d", + (int) stmt->commandType); + tag = "???"; + break; + } + } + break; + + /* parsed-and-rewritten-but-not-planned queries */ + case T_Query: + { + Query *stmt = (Query *) parsetree; + + switch (stmt->commandType) + { + case CMD_SELECT: + /* + * We take a little extra care here so that the result + * will be useful for complaints about read-only + * statements + */ + if (stmt->into != NULL) + tag = "SELECT INTO"; + else if (stmt->rowMarks != NIL) + { + if (((RowMarkClause *) linitial(stmt->rowMarks))->forUpdate) + tag = "SELECT FOR UPDATE"; + else + tag = "SELECT FOR SHARE"; + } + else + tag = "SELECT"; + break; + case CMD_UPDATE: + tag = "UPDATE"; + break; + case CMD_INSERT: + tag = "INSERT"; + break; + case CMD_DELETE: + tag = "DELETE"; + break; + case CMD_UTILITY: + tag = CreateCommandTag(stmt->utilityStmt); + break; + default: + elog(WARNING, "unrecognized commandType: %d", + (int) stmt->commandType); + tag = "???"; + break; + } + } + break; + default: elog(WARNING, "unrecognized node type: %d", (int) nodeTag(parsetree)); @@ -1836,69 +1934,13 @@ CreateCommandTag(Node *parsetree) return tag; } -/* - * CreateQueryTag - * utility to get a string representation of a Query operation. - * - * This is exactly like CreateCommandTag, except it works on a Query - * that has already been through parse analysis (and possibly further). - */ -const char * -CreateQueryTag(Query *parsetree) -{ - const char *tag; - - Assert(IsA(parsetree, Query)); - - switch (parsetree->commandType) - { - case CMD_SELECT: - - /* - * We take a little extra care here so that the result will be - * useful for complaints about read-only statements - */ - if (parsetree->into != NULL) - tag = "SELECT INTO"; - else if (parsetree->rowMarks != NIL) - { - if (((RowMarkClause *) linitial(parsetree->rowMarks))->forUpdate) - tag = "SELECT FOR UPDATE"; - else - tag = "SELECT FOR SHARE"; - } - else - tag = "SELECT"; - break; - case CMD_UPDATE: - tag = "UPDATE"; - break; - case CMD_INSERT: - tag = "INSERT"; - break; - case CMD_DELETE: - tag = "DELETE"; - break; - case CMD_UTILITY: - tag = CreateCommandTag(parsetree->utilityStmt); - break; - default: - elog(WARNING, "unrecognized commandType: %d", - (int) parsetree->commandType); - tag = "???"; - break; - } - - return tag; -} - /* * GetCommandLogLevel * utility to get the minimum log_statement level for a command, - * given a raw (un-analyzed) parsetree. + * given either a raw (un-analyzed) parsetree or a planned query. * - * This must handle all raw command types, but since the vast majority + * This must handle all command types, but since the vast majority * of 'em are utility commands, it seems sensible to keep it here. */ LogStmtLevel @@ -1908,6 +1950,7 @@ GetCommandLogLevel(Node *parsetree) switch (nodeTag(parsetree)) { + /* raw plannable queries */ case T_InsertStmt: case T_DeleteStmt: case T_UpdateStmt: @@ -1921,6 +1964,7 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_ALL; break; + /* utility statements --- same whether raw or cooked */ case T_TransactionStmt: lev = LOGSTMT_ALL; break; @@ -2216,12 +2260,12 @@ GetCommandLogLevel(Node *parsetree) pstmt = FetchPreparedStatement(stmt->name, false); if (pstmt) { - foreach(l, pstmt->query_list) + foreach(l, pstmt->stmt_list) { - Query *query = (Query *) lfirst(l); + Node *substmt = (Node *) lfirst(l); LogStmtLevel stmt_lev; - stmt_lev = GetQueryLogLevel(query); + stmt_lev = GetCommandLogLevel(substmt); lev = Min(lev, stmt_lev); } } @@ -2232,14 +2276,67 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_ALL; break; - case T_Query: + /* already-planned queries */ + case T_PlannedStmt: + { + PlannedStmt *stmt = (PlannedStmt *) parsetree; - /* - * In complicated situations (eg, EXPLAIN ANALYZE in an extended - * Query protocol), we might find an already-analyzed query within - * a utility statement. Cope. - */ - lev = GetQueryLogLevel((Query *) parsetree); + switch (stmt->commandType) + { + case CMD_SELECT: + if (stmt->into != NULL) + lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */ + else + lev = LOGSTMT_ALL; + break; + + case CMD_UPDATE: + case CMD_INSERT: + case CMD_DELETE: + lev = LOGSTMT_MOD; + break; + + default: + elog(WARNING, "unrecognized commandType: %d", + (int) stmt->commandType); + lev = LOGSTMT_ALL; + break; + } + } + break; + + /* parsed-and-rewritten-but-not-planned queries */ + case T_Query: + { + Query *stmt = (Query *) parsetree; + + switch (stmt->commandType) + { + case CMD_SELECT: + if (stmt->into != NULL) + lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */ + else + lev = LOGSTMT_ALL; + break; + + case CMD_UPDATE: + case CMD_INSERT: + case CMD_DELETE: + lev = LOGSTMT_MOD; + break; + + case CMD_UTILITY: + lev = GetCommandLogLevel(stmt->utilityStmt); + break; + + default: + elog(WARNING, "unrecognized commandType: %d", + (int) stmt->commandType); + lev = LOGSTMT_ALL; + break; + } + + } break; default: @@ -2251,46 +2348,3 @@ GetCommandLogLevel(Node *parsetree) return lev; } - -/* - * GetQueryLogLevel - * utility to get the minimum log_statement level for a Query operation. - * - * This is exactly like GetCommandLogLevel, except it works on a Query - * that has already been through parse analysis (and possibly further). - */ -LogStmtLevel -GetQueryLogLevel(Query *parsetree) -{ - LogStmtLevel lev; - - Assert(IsA(parsetree, Query)); - - switch (parsetree->commandType) - { - case CMD_SELECT: - if (parsetree->into != NULL) - lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */ - else - lev = LOGSTMT_ALL; - break; - - case CMD_UPDATE: - case CMD_INSERT: - case CMD_DELETE: - lev = LOGSTMT_MOD; - break; - - case CMD_UTILITY: - lev = GetCommandLogLevel(parsetree->utilityStmt); - break; - - default: - elog(WARNING, "unrecognized commandType: %d", - (int) parsetree->commandType); - lev = LOGSTMT_ALL; - break; - } - - return lev; -} diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c index 8c9d58422e..3bd2ee6397 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c @@ -12,7 +12,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.98 2007/01/05 22:19:47 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.99 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -141,29 +141,45 @@ GetPortalByName(const char *name) } /* - * PortalListGetPrimaryQuery - * Get the "primary" Query within a portal, ie, the one marked canSetTag. + * PortalListGetPrimaryStmt + * Get the "primary" stmt within a portal, ie, the one marked canSetTag. * - * Returns NULL if no such Query. If multiple Query structs within the + * Returns NULL if no such stmt. If multiple PlannedStmt structs within the * portal are marked canSetTag, returns the first one. Neither of these * cases should occur in present usages of this function. * + * Copes if given a list of Querys --- can't happen in a portal, but this + * code also supports prepared statements, which need both cases. + * * Note: the reason this is just handed a List is so that prepared statements - * can share the code. For use with a portal, use PortalGetPrimaryQuery + * can share the code. For use with a portal, use PortalGetPrimaryStmt * rather than calling this directly. */ -Query * -PortalListGetPrimaryQuery(List *parseTrees) +Node * +PortalListGetPrimaryStmt(List *stmts) { ListCell *lc; - foreach(lc, parseTrees) + foreach(lc, stmts) { - Query *query = (Query *) lfirst(lc); + Node *stmt = (Node *) lfirst(lc); - Assert(IsA(query, Query)); - if (query->canSetTag) - return query; + if (IsA(stmt, PlannedStmt)) + { + if (((PlannedStmt *) stmt)->canSetTag) + return stmt; + } + else if (IsA(stmt, Query)) + { + if (((Query *) stmt)->canSetTag) + return stmt; + } + else + { + /* Utility stmts are assumed canSetTag if they're the only stmt */ + if (list_length(stmts) == 1) + return stmt; + } } return NULL; } @@ -261,30 +277,25 @@ CreateNewPortal(void) * (before rewriting) was an empty string. Also, the passed commandTag must * be a pointer to a constant string, since it is not copied. The caller is * responsible for ensuring that the passed prepStmtName (if any), sourceText - * (if any), parse and plan trees have adequate lifetime. Also, queryContext - * must accurately describe the location of the parse trees. + * (if any), and plan trees have adequate lifetime. */ void PortalDefineQuery(Portal portal, const char *prepStmtName, const char *sourceText, const char *commandTag, - List *parseTrees, - List *planTrees, + List *stmts, MemoryContext queryContext) { AssertArg(PortalIsValid(portal)); AssertState(portal->queryContext == NULL); /* else defined already */ - Assert(list_length(parseTrees) == list_length(planTrees)); - - Assert(commandTag != NULL || parseTrees == NIL); + Assert(commandTag != NULL || stmts == NIL); portal->prepStmtName = prepStmtName; portal->sourceText = sourceText; portal->commandTag = commandTag; - portal->parseTrees = parseTrees; - portal->planTrees = planTrees; + portal->stmts = stmts; portal->queryContext = queryContext; } diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 20d6e24e05..8c53a2df2a 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.387 2007/02/20 10:00:25 petere Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.388 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200702201 +#define CATALOG_VERSION_NO 200702202 #endif diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h index 4a17ddb767..50fe2f1328 100644 --- a/src/include/commands/portalcmds.h +++ b/src/include/commands/portalcmds.h @@ -7,13 +7,14 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.20 2007/01/05 22:19:53 momjian Exp $ + * $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.21 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef PORTALCMDS_H #define PORTALCMDS_H +#include "nodes/parsenodes.h" #include "utils/portal.h" diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h index c1ee47fe7e..a921bf1b04 100644 --- a/src/include/commands/prepare.h +++ b/src/include/commands/prepare.h @@ -6,7 +6,7 @@ * * Copyright (c) 2002-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.23 2007/01/05 22:19:53 momjian Exp $ + * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.24 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,10 @@ /* * The data structure representing a prepared statement * + * A prepared statement might be fully planned, or only parsed-and-rewritten. + * If fully planned, stmt_list contains PlannedStmts and/or utility statements; + * if not, it contains Query nodes. + * * Note: all subsidiary storage lives in the context denoted by the context * field. However, the string referenced by commandTag is not subsidiary * storage; it is assumed to be a compile-time-constant string. As with @@ -31,11 +35,11 @@ typedef struct char stmt_name[NAMEDATALEN]; char *query_string; /* text of query, or NULL */ const char *commandTag; /* command tag (a constant!), or NULL */ - List *query_list; /* list of queries, rewritten */ - List *plan_list; /* list of plans */ + List *stmt_list; /* list of statement or Query nodes */ List *argtype_list; /* list of parameter type OIDs */ + bool fully_planned; /* what is in stmt_list, exactly? */ + bool from_sql; /* prepared via SQL, not FE/BE protocol? */ TimestampTz prepare_time; /* the time when the stmt was prepared */ - bool from_sql; /* stmt prepared via SQL, not FE/BE protocol? */ MemoryContext context; /* context containing this query */ } PreparedStatement; @@ -52,9 +56,9 @@ extern void ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params, extern void StorePreparedStatement(const char *stmt_name, const char *query_string, const char *commandTag, - List *query_list, - List *plan_list, + List *stmt_list, List *argtype_list, + bool fully_planned, bool from_sql); extern PreparedStatement *FetchPreparedStatement(const char *stmt_name, bool throwError); diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h index cf991125d4..d5ae745a29 100644 --- a/src/include/executor/execdesc.h +++ b/src/include/executor/execdesc.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/executor/execdesc.h,v 1.33 2007/01/05 22:19:54 momjian Exp $ + * $PostgreSQL: pgsql/src/include/executor/execdesc.h,v 1.34 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,7 +16,7 @@ #define EXECDESC_H #include "nodes/execnodes.h" -#include "nodes/parsenodes.h" +#include "nodes/plannodes.h" #include "tcop/dest.h" @@ -24,15 +24,19 @@ * query descriptor: * * a QueryDesc encapsulates everything that the executor - * needs to execute the query + * needs to execute the query. + * + * For the convenience of SQL-language functions, we also support QueryDescs + * containing utility statements; these must not be passed to the executor + * however. * --------------------- */ typedef struct QueryDesc { /* These fields are provided by CreateQueryDesc */ CmdType operation; /* CMD_SELECT, CMD_UPDATE, etc. */ - Query *parsetree; /* rewritten parsetree */ - Plan *plantree; /* planner's output */ + PlannedStmt *plannedstmt; /* planner's output, or null if utility */ + Node *utilitystmt; /* utility statement, or null */ Snapshot snapshot; /* snapshot to use for query */ Snapshot crosscheck_snapshot; /* crosscheck for RI update/delete */ DestReceiver *dest; /* the destination for tuple output */ @@ -46,13 +50,18 @@ typedef struct QueryDesc } QueryDesc; /* in pquery.c */ -extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree, +extern QueryDesc *CreateQueryDesc(PlannedStmt *plannedstmt, Snapshot snapshot, Snapshot crosscheck_snapshot, DestReceiver *dest, ParamListInfo params, bool doInstrument); +extern QueryDesc *CreateUtilityQueryDesc(Node *utilitystmt, + Snapshot snapshot, + DestReceiver *dest, + ParamListInfo params); + extern void FreeQueryDesc(QueryDesc *qdesc); #endif /* EXECDESC_H */ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index bfbe1ba2f3..38260b5ecd 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.136 2007/02/06 02:59:13 tgl Exp $ + * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.137 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,6 +15,7 @@ #define EXECUTOR_H #include "executor/execdesc.h" +#include "nodes/parsenodes.h" /* diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h index 3bfc870159..5e65bd750a 100644 --- a/src/include/executor/spi_priv.h +++ b/src/include/executor/spi_priv.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.26 2007/01/05 22:19:55 momjian Exp $ + * $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.27 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,10 +35,11 @@ typedef struct MemoryContext plancxt; /* Original query string (used for error reporting) */ const char *query; - /* List of List of querytrees; one sublist per original parsetree */ - List *qtlist; - /* List of plan trees --- length == # of querytrees, but flat list */ - List *ptlist; + /* + * List of List of PlannedStmts and utility stmts; one sublist per + * original parsetree + */ + List *stmt_list_list; /* Argument types, if a prepared plan */ int nargs; Oid *argtypes; diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 35a0ab3a60..b576f4610e 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.167 2007/02/06 02:59:13 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.168 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -344,7 +344,7 @@ typedef struct EState ExprContext *es_per_tuple_exprcontext; /* Below is to re-evaluate plan qual in READ COMMITTED mode */ - Plan *es_topPlan; /* link to top of plan tree */ + PlannedStmt *es_plannedstmt; /* link to top of plan tree */ struct evalPlanQual *es_evalPlanQual; /* chain of PlanQual states */ bool *es_evTupleNull; /* local array of EPQ status */ HeapTuple *es_evTuple; /* shared array of EPQ substitute tuples */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 44175591e7..53bd13b5fb 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.195 2007/02/19 07:03:31 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.196 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -141,6 +141,7 @@ typedef enum NodeTag T_RangeTblRef, T_JoinExpr, T_FromExpr, + T_IntoClause, /* * TAGS FOR EXPRESSION STATE NODES (execnodes.h) @@ -225,9 +226,10 @@ typedef enum NodeTag T_OidList, /* - * TAGS FOR PARSE TREE NODES (parsenodes.h) + * TAGS FOR STATEMENT NODES (mostly in parsenodes.h) */ T_Query = 700, + T_PlannedStmt, T_InsertStmt, T_DeleteStmt, T_UpdateStmt, @@ -302,8 +304,12 @@ typedef enum NodeTag T_AlterOwnerStmt, T_DropOwnedStmt, T_ReassignOwnedStmt, + T_CompositeTypeStmt, - T_A_Expr = 800, + /* + * TAGS FOR PARSE TREE NODES (parsenodes.h) + */ + T_A_Expr = 900, T_ColumnRef, T_ParamRef, T_A_Const, @@ -328,7 +334,6 @@ typedef enum NodeTag T_FuncWithArgs, T_PrivTarget, T_CreateOpClassItem, - T_CompositeTypeStmt, T_InhRelation, T_FunctionParameter, T_LockingClause, @@ -343,7 +348,7 @@ typedef enum NodeTag * purposes (usually because they are involved in APIs where we want to * pass multiple object types through the same pointer). */ - T_TriggerData = 900, /* in commands/trigger.h */ + T_TriggerData = 950, /* in commands/trigger.h */ T_ReturnSetInfo, /* in nodes/execnodes.h */ T_TIDBitmap /* in nodes/tidbitmap.h */ } NodeTag; @@ -430,10 +435,9 @@ typedef double Cost; /* execution cost (in page-access units) */ /* * CmdType - - * enums for type of operation represented by a Query + * enums for type of operation represented by a Query or PlannedStmt * - * ??? could have put this in parsenodes.h but many files not in the - * optimizer also need this... + * This is needed in both parsenodes.h and plannodes.h, so put it here... */ typedef enum CmdType { diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 0db7276302..ec9ccb6ce3 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.340 2007/02/03 14:06:55 petere Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.341 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,15 +27,6 @@ typedef enum QuerySource QSRC_NON_INSTEAD_RULE /* added by non-INSTEAD rule */ } QuerySource; -/* What to do at commit time for temporary relations */ -typedef enum OnCommitAction -{ - ONCOMMIT_NOOP, /* No ON COMMIT clause (do nothing) */ - ONCOMMIT_PRESERVE_ROWS, /* ON COMMIT PRESERVE ROWS (do nothing) */ - ONCOMMIT_DELETE_ROWS, /* ON COMMIT DELETE ROWS */ - ONCOMMIT_DROP /* ON COMMIT DROP */ -} OnCommitAction; - /* Sort ordering options for ORDER BY and CREATE INDEX */ typedef enum SortByDir { @@ -86,11 +77,14 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */ /* * Query - - * all statements are turned into a Query tree (via transformStmt) - * for further processing by the optimizer + * Parse analysis turns all statements into a Query tree (via transformStmt) + * for further processing by the rewriter and planner. * - * utility statements (i.e. non-optimizable statements) have the + * Utility statements (i.e. non-optimizable statements) have the * utilityStmt field set, and the Query itself is mostly dummy. + * + * Planning converts a Query tree into a Plan tree headed by a PlannedStmt + * noded --- the Query structure is not used by the executor. */ typedef struct Query { @@ -108,10 +102,7 @@ typedef struct Query int resultRelation; /* rtable index of target relation for * INSERT/UPDATE/DELETE; 0 for SELECT */ - RangeVar *into; /* target relation for SELECT INTO */ - List *intoOptions; /* options from WITH clause */ - OnCommitAction intoOnCommit; /* what do we do at COMMIT? */ - char *intoTableSpaceName; /* table space to use, or NULL */ + IntoClause *into; /* target for SELECT INTO / CREATE TABLE AS */ bool hasAggs; /* has aggregates in tlist or havingQual */ bool hasSubLinks; /* has subquery SubLink */ @@ -138,29 +129,6 @@ typedef struct Query Node *setOperations; /* set-operation tree if this is top level of * a UNION/INTERSECT/EXCEPT query */ - - /* - * If the resultRelation turns out to be the parent of an inheritance - * tree, the planner will add all the child tables to the rtable and store - * a list of the rtindexes of all the result relations here. This is done - * at plan time, not parse time, since we don't want to commit to the - * exact set of child tables at parse time. XXX This field ought to go in - * some sort of TopPlan plan node, not in the Query. - */ - List *resultRelations; /* integer list of RT indexes, or NIL */ - - /* - * If the query has a returningList then the planner will store a list of - * processed targetlists (one per result relation) here. We must have a - * separate RETURNING targetlist for each result rel because column - * numbers may vary within an inheritance tree. In the targetlists, Vars - * referencing the result relation will have their original varno and - * varattno, while Vars referencing other rels will be converted to have - * varno OUTER and varattno referencing a resjunk entry in the top plan - * node's targetlist. XXX This field ought to go in some sort of TopPlan - * plan node, not in the Query. - */ - List *returningLists; /* list of lists of TargetEntry, or NIL */ } Query; @@ -761,17 +729,10 @@ typedef struct SelectStmt /* * These fields are used only in "leaf" SelectStmts. - * - * into, intoColNames, intoOptions, intoOnCommit, and intoTableSpaceName - * are a kluge; they belong somewhere else... */ List *distinctClause; /* NULL, list of DISTINCT ON exprs, or * lcons(NIL,NIL) for all (SELECT DISTINCT) */ - RangeVar *into; /* target table (for select into table) */ - List *intoColNames; /* column names for into table */ - List *intoOptions; /* options from WITH clause */ - OnCommitAction intoOnCommit; /* what do we do at COMMIT? */ - char *intoTableSpaceName; /* table space to use, or NULL */ + IntoClause *into; /* target for SELECT INTO / CREATE TABLE AS */ List *targetList; /* the target list (of ResTarget) */ List *fromClause; /* the FROM clause */ Node *whereClause; /* WHERE qualification */ @@ -1994,10 +1955,7 @@ typedef struct ExecuteStmt { NodeTag type; char *name; /* The name of the plan to execute */ - RangeVar *into; /* Optional table to store results in */ - List *intoOptions; /* Options from WITH clause */ - OnCommitAction into_on_commit; /* What do we do at COMMIT? */ - char *into_tbl_space; /* Tablespace to use, or NULL */ + IntoClause *into; /* Optional table to store results in */ List *params; /* Values to assign to parameters */ } ExecuteStmt; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index cdd7b4d2e4..537981462b 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.90 2007/02/19 02:23:12 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.91 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,48 @@ * ---------------------------------------------------------------- */ +/* ---------------- + * PlannedStmt node + * + * The output of the planner is a Plan tree headed by a PlannedStmt node. + * PlannedStmt holds the "one time" information needed by the executor. + * ---------------- + */ +typedef struct PlannedStmt +{ + NodeTag type; + + CmdType commandType; /* select|insert|update|delete */ + + bool canSetTag; /* do I set the command result tag? */ + + struct Plan *planTree; /* tree of Plan nodes */ + + List *rtable; /* list of RangeTblEntry nodes */ + + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ + List *resultRelations; /* integer list of RT indexes, or NIL */ + + IntoClause *into; /* target for SELECT INTO / CREATE TABLE AS */ + + /* + * If the query has a returningList then the planner will store a list of + * processed targetlists (one per result relation) here. We must have a + * separate RETURNING targetlist for each result rel because column + * numbers may vary within an inheritance tree. In the targetlists, Vars + * referencing the result relation will have their original varno and + * varattno, while Vars referencing other rels will be converted to have + * varno OUTER and varattno referencing a resjunk entry in the top plan + * node's targetlist. + */ + List *returningLists; /* list of lists of TargetEntry, or NIL */ + + List *rowMarks; /* a list of RowMarkClause's */ + + int nParamExec; /* number of PARAM_EXEC Params used */ +} PlannedStmt; + + /* ---------------- * Plan node * @@ -75,15 +117,6 @@ typedef struct Plan */ Bitmapset *extParam; Bitmapset *allParam; - - /* - * We really need in some TopPlan node to store range table and - * resultRelation from Query there and get rid of Query itself from - * Executor. Some other stuff like below could be put there, too. - */ - int nParamExec; /* Number of them in entire query. This is to - * get Executor know about how many PARAM_EXEC - * there are in query plan. */ } Plan; /* ---------------- diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index caa689e262..185673f729 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.125 2007/02/19 07:03:31 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.126 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -49,6 +49,15 @@ typedef enum InhOption INH_DEFAULT /* Use current SQL_inheritance option */ } InhOption; +/* What to do at commit time for temporary relations */ +typedef enum OnCommitAction +{ + ONCOMMIT_NOOP, /* No ON COMMIT clause (do nothing) */ + ONCOMMIT_PRESERVE_ROWS, /* ON COMMIT PRESERVE ROWS (do nothing) */ + ONCOMMIT_DELETE_ROWS, /* ON COMMIT DELETE ROWS */ + ONCOMMIT_DROP /* ON COMMIT DROP */ +} OnCommitAction; + /* * RangeVar - range variable, used in FROM clauses * @@ -69,6 +78,20 @@ typedef struct RangeVar Alias *alias; /* table alias & optional column aliases */ } RangeVar; +/* + * IntoClause - target information for SELECT INTO and CREATE TABLE AS + */ +typedef struct IntoClause +{ + NodeTag type; + + RangeVar *rel; /* target relation name */ + List *colNames; /* column names to assign, or NIL */ + List *options; /* options from WITH clause */ + OnCommitAction onCommit; /* what do we do at COMMIT? */ + char *tableSpaceName; /* table space to use, or NULL */ +} IntoClause; + /* ---------------------------------------------------------------- * node types for executable expressions diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 6de06ebc91..59ec830f3f 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.136 2007/02/19 07:03:33 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.137 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -110,6 +110,10 @@ typedef struct PlannerInfo List *join_rel_list; /* list of join-relation RelOptInfos */ struct HTAB *join_rel_hash; /* optional hashtable for join relations */ + List *resultRelations; /* integer list of RT indexes, or NIL */ + + List *returningLists; /* list of lists of TargetEntry, or NIL */ + List *init_plans; /* init subplans for query */ List *eq_classes; /* list of active EquivalenceClasses */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 44d0360269..c243cdbc35 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.37 2007/02/19 07:03:34 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.38 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,10 +18,10 @@ #include "nodes/relation.h" -extern Plan *planner(Query *parse, bool isCursor, int cursorOptions, +extern PlannedStmt *planner(Query *parse, bool isCursor, int cursorOptions, ParamListInfo boundParams); extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse, Index level, double tuple_fraction, - List **subquery_pathkeys); + PlannerInfo **subroot); #endif /* PLANNER_H */ diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h index 3531353027..5cab498c13 100644 --- a/src/include/tcop/pquery.h +++ b/src/include/tcop/pquery.h @@ -7,23 +7,26 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.40 2007/01/05 22:19:58 momjian Exp $ + * $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.41 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef PQUERY_H #define PQUERY_H +#include "nodes/parsenodes.h" #include "utils/portal.h" extern DLLIMPORT Portal ActivePortal; -extern PortalStrategy ChoosePortalStrategy(List *parseTrees); +extern PortalStrategy ChoosePortalStrategy(List *stmts); extern List *FetchPortalTargetList(Portal portal); +extern List *FetchStatementTargetList(Node *stmt); + extern void PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot); diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index ad66a61dac..2a55125576 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.86 2007/01/05 22:19:58 momjian Exp $ + * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.87 2007/02/20 17:32:17 tgl Exp $ * * OLD COMMENTS * This file was created so that other c files could get the two @@ -20,6 +20,7 @@ #define TCOPPROT_H #include "executor/execdesc.h" +#include "nodes/parsenodes.h" #include "utils/guc.h" @@ -50,7 +51,7 @@ extern List *pg_parse_and_rewrite(const char *query_string, extern List *pg_parse_query(const char *query_string); extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string, Oid *paramTypes, int numParams); -extern Plan *pg_plan_query(Query *querytree, ParamListInfo boundParams); +extern PlannedStmt *pg_plan_query(Query *querytree, ParamListInfo boundParams); extern List *pg_plan_queries(List *querytrees, ParamListInfo boundParams, bool needSnapshot); diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h index ebcaf59772..52c0225306 100644 --- a/src/include/tcop/utility.h +++ b/src/include/tcop/utility.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.30 2007/01/05 22:19:58 momjian Exp $ + * $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.31 2007/02/20 17:32:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -26,15 +26,9 @@ extern TupleDesc UtilityTupleDescriptor(Node *parsetree); extern const char *CreateCommandTag(Node *parsetree); -extern const char *CreateQueryTag(Query *parsetree); - extern LogStmtLevel GetCommandLogLevel(Node *parsetree); -extern LogStmtLevel GetQueryLogLevel(Query *parsetree); - -extern bool QueryReturnsTuples(Query *parsetree); - -extern bool QueryIsReadOnly(Query *parsetree); +extern bool CommandIsReadOnly(Node *parsetree); extern void CheckRelationOwnership(RangeVar *rel, bool noCatalogs); diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h index 5eb2c715e3..aa432abb87 100644 --- a/src/include/utils/portal.h +++ b/src/include/utils/portal.h @@ -12,7 +12,7 @@ * to let the client suspend an update-type query partway through! Because * the query rewriter does not allow arbitrary ON SELECT rewrite rules, * only queries that were originally update-type could produce multiple - * parse/plan trees; so the restriction to a single query is not a problem + * plan trees; so the restriction to a single query is not a problem * in practice. * * For SQL cursors, we support three kinds of scroll behavior: @@ -39,7 +39,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.72 2007/01/05 22:19:59 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.73 2007/02/20 17:32:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -124,9 +124,8 @@ typedef struct PortalData /* The query or queries the portal will execute */ const char *sourceText; /* text of query, if known (may be NULL) */ const char *commandTag; /* command tag for original query */ - List *parseTrees; /* parse tree(s) */ - List *planTrees; /* plan tree(s) */ - MemoryContext queryContext; /* where the parse trees live */ + List *stmts; /* PlannedStmts and/or utility statements */ + MemoryContext queryContext; /* where the plan trees live */ /* * Note: queryContext effectively identifies which prepared statement the @@ -191,7 +190,7 @@ typedef struct PortalData */ #define PortalGetQueryDesc(portal) ((portal)->queryDesc) #define PortalGetHeapMemory(portal) ((portal)->heap) -#define PortalGetPrimaryQuery(portal) PortalListGetPrimaryQuery((portal)->parseTrees) +#define PortalGetPrimaryStmt(portal) PortalListGetPrimaryStmt((portal)->stmts) /* Prototypes for functions in utils/mmgr/portalmem.c */ @@ -217,10 +216,9 @@ extern void PortalDefineQuery(Portal portal, const char *prepStmtName, const char *sourceText, const char *commandTag, - List *parseTrees, - List *planTrees, + List *stmts, MemoryContext queryContext); -extern Query *PortalListGetPrimaryQuery(List *parseTrees); +extern Node *PortalListGetPrimaryStmt(List *stmts); extern void PortalCreateHoldStore(Portal portal); #endif /* PORTAL_H */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 09f7a99097..68a68a6634 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.188 2007/02/08 18:37:30 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.189 2007/02/20 17:32:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2380,20 +2380,20 @@ exec_stmt_execsql(PLpgSQL_execstate *estate, exec_prepare_plan(estate, expr); stmt->mod_stmt = false; spi_plan = (_SPI_plan *) expr->plan; - foreach(l, spi_plan->qtlist) + foreach(l, spi_plan->stmt_list_list) { ListCell *l2; foreach(l2, (List *) lfirst(l)) { - Query *q = (Query *) lfirst(l2); + PlannedStmt *p = (PlannedStmt *) lfirst(l2); - Assert(IsA(q, Query)); - if (q->canSetTag) + if (IsA(p, PlannedStmt) && + p->canSetTag) { - if (q->commandType == CMD_INSERT || - q->commandType == CMD_UPDATE || - q->commandType == CMD_DELETE) + if (p->commandType == CMD_INSERT || + p->commandType == CMD_UPDATE || + p->commandType == CMD_DELETE) stmt->mod_stmt = true; } } @@ -4674,6 +4674,8 @@ static void exec_simple_check_plan(PLpgSQL_expr *expr) { _SPI_plan *spi_plan = (_SPI_plan *) expr->plan; + List *sublist; + PlannedStmt *stmt; Plan *plan; TargetEntry *tle; @@ -4683,17 +4685,20 @@ exec_simple_check_plan(PLpgSQL_expr *expr) * 1. We can only evaluate queries that resulted in one single execution * plan */ - if (list_length(spi_plan->ptlist) != 1) + if (list_length(spi_plan->stmt_list_list) != 1) + return; + sublist = (List *) linitial(spi_plan->stmt_list_list); + if (list_length(sublist) != 1) return; - plan = (Plan *) linitial(spi_plan->ptlist); + stmt = (PlannedStmt *) linitial(sublist); /* * 2. It must be a RESULT plan --> no scan's required */ - if (plan == NULL) /* utility statement produces this */ + if (!IsA(stmt, PlannedStmt)) return; - + plan = stmt->planTree; if (!IsA(plan, Result)) return;