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