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.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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;
(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),
* 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.
((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);
* 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 $
*
*-------------------------------------------------------------------------
*/
ExplainOneQuery(Query *query, ExplainStmt *stmt, ParamListInfo params,
TupOutputState *tstate)
{
- Plan *plan;
+ PlannedStmt *plan;
QueryDesc *queryDesc;
bool isCursor = false;
int cursorOptions = 0;
ActiveSnapshot->curcid = GetCurrentCommandId();
/* Create a QueryDesc requesting no output */
- queryDesc = CreateQueryDesc(query, plan,
+ queryDesc = CreateQueryDesc(plan,
ActiveSnapshot, InvalidSnapshot,
None_Receiver, params,
stmt->analyze);
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)
}
initStringInfo(&buf);
- explain_outNode(&buf, queryDesc->plantree, queryDesc->planstate,
+ explain_outNode(&buf,
+ queryDesc->plannedstmt->planTree, queryDesc->planstate,
NULL, 0, es);
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
List *rewritten;
Query *query;
- Plan *plan;
+ PlannedStmt *plan;
Portal portal;
MemoryContext oldContext;
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);
/*
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));
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
StorePreparedStatement(stmt->name,
debug_query_string,
commandTag,
- query_list,
plan_list,
stmt->argtype_oids,
+ true,
true);
}
{
PreparedStatement *entry;
char *query_string;
- List *query_list,
- *plan_list;
+ List *plan_list;
MemoryContext qcontext;
ParamListInfo paramLI = NULL;
EState *estate = NULL;
/* 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)
{
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);
}
NULL,
query_string,
entry->commandTag,
- query_list,
plan_list,
qcontext);
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;
* 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 */
/* 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);
}
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 */
bool
PreparedStatementReturnsTuples(PreparedStatement *stmt)
{
- switch (ChoosePortalStrategy(stmt->query_list))
+ switch (ChoosePortalStrategy(stmt->stmt_list))
{
case PORTAL_ONE_SELECT:
case PORTAL_ONE_RETURNING:
* 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));
}
/*
{
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;
/* 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)
}
/* 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");
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;
}
/*
ActiveSnapshot->curcid = GetCurrentCommandId();
/* Create a QueryDesc requesting no output */
- qdesc = CreateQueryDesc(query, plan,
+ qdesc = CreateQueryDesc(pstmt,
ActiveSnapshot, InvalidSnapshot,
None_Receiver,
paramLI, stmt->analyze);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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.
*/
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
estate->es_lastoid = InvalidOid;
sendTuples = (operation == CMD_SELECT ||
- queryDesc->parsetree->returningList);
+ queryDesc->plannedstmt->returningLists);
if (sendTuples)
(*dest->rStartup) (dest, operation, queryDesc->tupDesc);
* 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;
}
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;
* 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
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 */
* 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);
}
/*
* 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);
{
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);
}
/* mark EvalPlanQual not active */
- estate->es_topPlan = plan;
+ estate->es_plannedstmt = plannedstmt;
estate->es_evalPlanQual = NULL;
estate->es_evTupleNull = NULL;
estate->es_evTuple = NULL;
* 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;
/*
* Initialize RETURNING projections if needed.
*/
- if (parseTree->returningLists)
+ if (plannedstmt->returningLists)
{
TupleTableSlot *slot;
ExprContext *econtext;
* 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) */
* 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;
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
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);
}
static void
OpenIntoRel(QueryDesc *queryDesc)
{
- Query *parseTree = queryDesc->parsetree;
+ IntoClause *into = queryDesc->plannedstmt->into;
EState *estate = queryDesc->estate;
Relation intoRelationDesc;
char *intoName;
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")));
/*
* 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);
* 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();
}
/* Parse and validate any reloptions */
reloptions = transformRelOptions((Datum) 0,
- parseTree->intoOptions,
+ into->options,
true,
false);
(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
false,
true,
0,
- parseTree->intoOnCommit,
+ into->onCommit,
reloptions,
allowSystemTableMods);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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
{
{
struct local_es *next;
ExecStatus status;
- Query *query;
- Plan *plan;
+ Node *stmt; /* PlannedStmt or utility statement */
QueryDesc *qd; /* null unless status == RUN */
} execution_state;
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)
newes->next = NULL;
newes->status = F_EXEC_START;
- newes->query = queryTree;
- newes->plan = planTree;
+ newes->stmt = stmt;
newes->qd = NULL;
preves = newes;
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 */
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;
}
*/
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
bool read_only)
{
_SPI_plan *spiplan = (_SPI_plan *) plan;
- List *qtlist;
- List *ptlist;
+ List *stmt_list;
ParamListInfo paramLI;
Snapshot snapshot;
MemoryContext oldcontext;
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;
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)
PortalDefineQuery(portal,
NULL, /* no statement name */
spiplan->query,
- CreateQueryTag(PortalListGetPrimaryQuery(qtlist)),
- qtlist,
- ptlist,
+ CreateCommandTag(PortalListGetPrimaryStmt(stmt_list)),
+ stmt_list,
PortalGetHeapMemory(portal));
MemoryContextSwitchTo(oldcontext);
* 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;
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:
*
* 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;
/*
* 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)
{
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
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;
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
if (!read_only)
CommandCounterIncrement();
- dest = CreateDestReceiver(queryTree->canSetTag ? DestSPI : DestNone,
+ dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone,
NULL);
if (snapshot == InvalidSnapshot)
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;
* 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;
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)
{
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;
_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())
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)
{
* 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 $
*
*-------------------------------------------------------------------------
*/
* ****************************************************************
*/
+/*
+ * _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
*
COPY_NODE_FIELD(initPlan);
COPY_BITMAPSET_FIELD(extParam);
COPY_BITMAPSET_FIELD(allParam);
- COPY_SCALAR_FIELD(nParamExec);
}
/*
return newnode;
}
+/*
+ * _copyIntoClause
+ */
+static IntoClause *
+_copyIntoClause(IntoClause *from)
+{
+ IntoClause *newnode = makeNode(IntoClause);
+
+ COPY_NODE_FIELD(rel);
+ COPY_NODE_FIELD(colNames);
+ COPY_NODE_FIELD(options);
+ COPY_SCALAR_FIELD(onCommit);
+ COPY_STRING_FIELD(tableSpaceName);
+
+ return newnode;
+}
+
/*
* We don't need a _copyExpr because Expr is an abstract supertype which
* should never actually get instantiated. Also, since it has no common
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);
COPY_NODE_FIELD(limitCount);
COPY_NODE_FIELD(rowMarks);
COPY_NODE_FIELD(setOperations);
- COPY_NODE_FIELD(resultRelations);
- COPY_NODE_FIELD(returningLists);
return newnode;
}
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);
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;
/*
* PLAN NODES
*/
+ case T_PlannedStmt:
+ retval = _copyPlannedStmt(from);
+ break;
case T_Plan:
retval = _copyPlan(from);
break;
case T_RangeVar:
retval = _copyRangeVar(from);
break;
+ case T_IntoClause:
+ retval = _copyIntoClause(from);
+ break;
case T_Var:
retval = _copyVar(from);
break;
* 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 $
*
*-------------------------------------------------------------------------
*/
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
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);
COMPARE_NODE_FIELD(limitCount);
COMPARE_NODE_FIELD(rowMarks);
COMPARE_NODE_FIELD(setOperations);
- COMPARE_NODE_FIELD(resultRelations);
- COMPARE_NODE_FIELD(returningLists);
return true;
}
{
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);
{
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;
case T_RangeVar:
retval = _equalRangeVar(a, b);
break;
+ case T_IntoClause:
+ retval = _equalIntoClause(a, b);
+ break;
case T_Var:
retval = _equalVar(a, b);
break;
*
*
* 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*
* 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
*/
WRITE_NODE_FIELD(initPlan);
WRITE_BITMAPSET_FIELD(extParam);
WRITE_BITMAPSET_FIELD(allParam);
- WRITE_INT_FIELD(nParamExec);
}
/*
WRITE_NODE_FIELD(alias);
}
+static void
+_outIntoClause(StringInfo str, IntoClause *node)
+{
+ WRITE_NODE_TYPE("INTOCLAUSE");
+
+ WRITE_NODE_FIELD(rel);
+ WRITE_NODE_FIELD(colNames);
+ WRITE_NODE_FIELD(options);
+ WRITE_ENUM_FIELD(onCommit, OnCommitAction);
+ WRITE_STRING_FIELD(tableSpaceName);
+}
+
static void
_outVar(StringInfo str, Var *node)
{
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);
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);
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);
WRITE_NODE_FIELD(limitCount);
WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(setOperations);
- WRITE_NODE_FIELD(resultRelations);
- WRITE_NODE_FIELD(returningLists);
}
static void
appendStringInfoChar(str, '{');
switch (nodeTag(obj))
{
+ case T_PlannedStmt:
+ _outPlannedStmt(str, obj);
+ break;
case T_Plan:
_outPlan(str, obj);
break;
case T_RangeVar:
_outRangeVar(str, obj);
break;
+ case T_IntoClause:
+ _outIntoClause(str, obj);
+ break;
case T_Var:
_outVar(str, obj);
break;
*
*
* 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
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);
READ_NODE_FIELD(limitCount);
READ_NODE_FIELD(rowMarks);
READ_NODE_FIELD(setOperations);
- READ_NODE_FIELD(resultRelations);
- READ_NODE_FIELD(returningLists);
READ_DONE();
}
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
*/
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))
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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 *)
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;
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));
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
}
/* 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
*/
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;
}
* 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,
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;
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;
}
/* 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;
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
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
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);
}
/*
/*
* 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)
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() */
*
*
* 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
IndexElem *ielem;
Alias *alias;
RangeVar *range;
+ IntoClause *into;
A_Indices *aind;
ResTarget *target;
PrivTarget *privtarget;
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
*/
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
* 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;
}
;
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")));
n->distinctClause = $2;
n->targetList = $3;
n->into = $4;
- n->intoColNames = NIL;
n->fromClause = $5;
n->whereClause = $6;
n->groupClause = $7;
;
into_clause:
- INTO OptTempTableName { $$ = $2; }
- | /*EMPTY*/ { $$ = NULL; }
+ INTO OptTempTableName
+ {
+ $$ = makeNode(IntoClause);
+ $$->rel = $2;
+ $$->colNames = NIL;
+ $$->options = NIL;
+ $$->onCommit = ONCOMMIT_NOOP;
+ $$->tableSpaceName = NULL;
+ }
+ | /*EMPTY*/
+ { $$ = NULL; }
;
/*
*
*
* 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
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);
/* 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)
#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
* 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
{
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;
}
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),
NULL,
query_string,
commandTag,
- querytree_list,
plantree_list,
MessageContext);
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];
* 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 */
StorePreparedStatement(stmt_name,
query_string,
commandTag,
- querytree_list,
- plantree_list,
+ stmt_list,
param_list,
+ fully_planned,
false);
}
else
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 */
* functions.
*/
if (IsAbortedTransactionBlockState() &&
- (!IsTransactionExitStmtList(pstmt->query_list) ||
+ (!IsTransactionExitStmtList(pstmt->stmt_list) ||
numParams != 0))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
* 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.
*pstmt->stmt_name ? pstmt->stmt_name : NULL,
pstmt->query_string,
pstmt->commandTag,
- query_list,
plan_list,
qContext);
*/
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
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",
* 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, "
}
/*
- * 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;
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;
}
* ones that we allow in transaction-aborted state.
*/
+/* Test a bare parsetree */
static bool
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
Portal ActivePortal = NULL;
-static void ProcessQuery(Query *parsetree,
- Plan *plan,
+static void ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
DestReceiver *dest,
char *completionTag);
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,
* CreateQueryDesc
*/
QueryDesc *
-CreateQueryDesc(Query *parsetree,
- Plan *plantree,
+CreateQueryDesc(PlannedStmt *plannedstmt,
Snapshot snapshot,
Snapshot crosscheck_snapshot,
DestReceiver *dest,
{
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 */
return qd;
}
+/*
+ * CreateUtilityQueryDesc
+ */
+QueryDesc *
+CreateUtilityQueryDesc(Node *utilitystmt,
+ Snapshot snapshot,
+ DestReceiver *dest,
+ ParamListInfo params)
+{
+ QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
+
+ qd->operation = CMD_UTILITY; /* operation */
+ qd->plannedstmt = NULL;
+ qd->utilitystmt = utilitystmt; /* utility command */
+ qd->snapshot = snapshot; /* snapshot */
+ qd->crosscheck_snapshot = InvalidSnapshot; /* RI check snapshot */
+ qd->dest = dest; /* output dest */
+ qd->params = params; /* parameter values passed into query */
+ qd->doInstrument = false; /* uninteresting for utilities */
+
+ /* null these fields until set by ExecutorStart */
+ qd->tupDesc = NULL;
+ qd->estate = NULL;
+ qd->planstate = NULL;
+
+ return qd;
+}
+
/*
* FreeQueryDesc
*/
* 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
* 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,
/*
* Create the QueryDesc object
*/
- queryDesc = CreateQueryDesc(parsetree, plan,
+ queryDesc = CreateQueryDesc(plan,
ActiveSnapshot, InvalidSnapshot,
dest, params, false);
{
Oid lastOid;
- switch (operation)
+ switch (queryDesc->operation)
{
case CMD_SELECT:
strcpy(completionTag, "SELECT");
/*
* 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;
+ }
}
/*
* 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;
* 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;
}
/*
* Determine the portal execution strategy
*/
- portal->strategy = ChoosePortalStrategy(portal->parseTrees);
+ portal->strategy = ChoosePortalStrategy(portal->stmts);
/*
* Fire her up according to the strategy
* 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,
* 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"
* 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"
break;
case PORTAL_UTIL_SELECT:
- PortalRunUtility(portal, linitial(portal->parseTrees),
+ PortalRunUtility(portal, (Node *) linitial(portal->stmts),
treceiver, completionTag);
break;
* 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")));
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));
DestReceiver *dest, DestReceiver *altdest,
char *completionTag)
{
- ListCell *querylist_item;
- ListCell *planlist_item;
+ ListCell *stmtlist_item;
/*
* If the destination is DestRemoteExecute, change to DestNone. The
* 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);
}
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();
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
- * 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;
}
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:
* QueryReturnsTuples
* Return "true" if this Query will send output to the destination.
*/
+#ifdef NOT_USED
bool
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.
switch (nodeTag(parsetree))
{
+ /* raw plannable queries */
case T_InsertStmt:
tag = "INSERT";
break;
tag = "SELECT";
break;
+ /* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
TransactionStmt *stmt = (TransactionStmt *) 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;
}
/*
* 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
switch (nodeTag(parsetree))
{
+ /* raw plannable queries */
case T_InsertStmt:
case T_DeleteStmt:
case T_UpdateStmt:
lev = LOGSTMT_ALL;
break;
+ /* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
break;
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);
}
}
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;
}
* 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 $
*
*-------------------------------------------------------------------------
*/
}
/*
- * 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;
}
* (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;
}
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200702201
+#define CATALOG_VERSION_NO 200702202
#endif
* 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"
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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
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;
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
#define EXECDESC_H
#include "nodes/execnodes.h"
-#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
#include "tcop/dest.h"
* 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 */
} 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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
#define EXECUTOR_H
#include "executor/execdesc.h"
+#include "nodes/parsenodes.h"
/*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
T_RangeTblRef,
T_JoinExpr,
T_FromExpr,
+ T_IntoClause,
/*
* TAGS FOR EXPRESSION STATE NODES (execnodes.h)
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,
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,
T_FuncWithArgs,
T_PrivTarget,
T_CreateOpClassItem,
- T_CompositeTypeStmt,
T_InhRelation,
T_FunctionParameter,
T_LockingClause,
* 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;
/*
* 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
{
* 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 $
*
*-------------------------------------------------------------------------
*/
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
{
/*
* 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
{
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 */
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;
/*
* 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 */
{
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
* ----------------------------------------------------------------
*/
+/* ----------------
+ * 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
*
*/
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;
/* ----------------
* 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 $
*
*-------------------------------------------------------------------------
*/
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
*
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
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
#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 */
* 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);
* 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
#define TCOPPROT_H
#include "executor/execdesc.h"
+#include "nodes/parsenodes.h"
#include "utils/guc.h"
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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:
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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
*/
#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 */
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 */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
}
}
exec_simple_check_plan(PLpgSQL_expr *expr)
{
_SPI_plan *spi_plan = (_SPI_plan *) expr->plan;
+ List *sublist;
+ PlannedStmt *stmt;
Plan *plan;
TargetEntry *tle;
* 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;