Remove the Query structure from the executor's API. This allows us to stop
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 20 Feb 2007 17:32:18 +0000 (17:32 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 20 Feb 2007 17:32:18 +0000 (17:32 +0000)
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.

39 files changed:
src/backend/commands/copy.c
src/backend/commands/explain.c
src/backend/commands/portalcmds.c
src/backend/commands/prepare.c
src/backend/executor/execMain.c
src/backend/executor/execUtils.c
src/backend/executor/functions.c
src/backend/executor/spi.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/plan/planagg.c
src/backend/optimizer/plan/planner.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/tcop/postgres.c
src/backend/tcop/pquery.c
src/backend/tcop/utility.c
src/backend/utils/mmgr/portalmem.c
src/include/catalog/catversion.h
src/include/commands/portalcmds.h
src/include/commands/prepare.h
src/include/executor/execdesc.h
src/include/executor/executor.h
src/include/executor/spi_priv.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/plannodes.h
src/include/nodes/primnodes.h
src/include/nodes/relation.h
src/include/optimizer/planner.h
src/include/tcop/pquery.h
src/include/tcop/tcopprot.h
src/include/tcop/utility.h
src/include/utils/portal.h
src/pl/plpgsql/src/pl_exec.c

index e61384beec85ba96c2138ffdd112c78e54e13d02..30118d5237b587733b64541cf58b11b440351b92 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.275 2007/01/25 02:17:26 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.276 2007/02/20 17:32:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -986,10 +986,11 @@ DoCopy(const CopyStmt *stmt)
    {
        Query      *query = stmt->query;
        List       *rewritten;
-       Plan       *plan;
+       PlannedStmt *plan;
        DestReceiver *dest;
 
        Assert(query);
+       Assert(query->commandType == CMD_SELECT);
        Assert(!is_from);
        cstate->rel = NULL;
 
@@ -999,6 +1000,7 @@ DoCopy(const CopyStmt *stmt)
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("COPY (SELECT) WITH OIDS is not supported")));
 
+       /* Query mustn't use INTO, either */
        if (query->into)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -1016,7 +1018,6 @@ DoCopy(const CopyStmt *stmt)
         * shouldn't modify its input ... FIXME someday.
         */
        query = copyObject(query);
-       Assert(query->commandType == CMD_SELECT);
 
        /*
         * Must acquire locks in case we didn't come fresh from the parser.
@@ -1051,7 +1052,7 @@ DoCopy(const CopyStmt *stmt)
        ((DR_copy *) dest)->cstate = cstate;
 
        /* Create a QueryDesc requesting no output */
-       cstate->queryDesc = CreateQueryDesc(query, plan,
+       cstate->queryDesc = CreateQueryDesc(plan,
                                            ActiveSnapshot, InvalidSnapshot,
                                            dest, NULL, false);
 
index 7b2c521a3535dd833cee3fccb4868b62f1468ab5..58b7e6ded9a48b1a74b344a45022fba02159e298 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.155 2007/02/19 02:23:11 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.156 2007/02/20 17:32:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -149,7 +149,7 @@ static void
 ExplainOneQuery(Query *query, ExplainStmt *stmt, ParamListInfo params,
                TupOutputState *tstate)
 {
-   Plan       *plan;
+   PlannedStmt *plan;
    QueryDesc  *queryDesc;
    bool        isCursor = false;
    int         cursorOptions = 0;
@@ -203,7 +203,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, ParamListInfo params,
    ActiveSnapshot->curcid = GetCurrentCommandId();
 
    /* Create a QueryDesc requesting no output */
-   queryDesc = CreateQueryDesc(query, plan,
+   queryDesc = CreateQueryDesc(plan,
                                ActiveSnapshot, InvalidSnapshot,
                                None_Receiver, params,
                                stmt->analyze);
@@ -260,14 +260,14 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
 
    es->printNodes = stmt->verbose;
    es->printAnalyze = stmt->analyze;
-   es->rtable = queryDesc->parsetree->rtable;
+   es->rtable = queryDesc->plannedstmt->rtable;
 
    if (es->printNodes)
    {
        char       *s;
        char       *f;
 
-       s = nodeToString(queryDesc->plantree);
+       s = nodeToString(queryDesc->plannedstmt->planTree);
        if (s)
        {
            if (Explain_pretty_print)
@@ -282,7 +282,8 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
    }
 
    initStringInfo(&buf);
-   explain_outNode(&buf, queryDesc->plantree, queryDesc->planstate,
+   explain_outNode(&buf,
+                   queryDesc->plannedstmt->planTree, queryDesc->planstate,
                    NULL, 0, es);
 
    /*
index d1c119ca08f753cdf601cbaa68d87cb23c42e9a5..0219650c069fc30210397499c2ab8dccdb0e7db1 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.60 2007/02/06 22:49:24 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.61 2007/02/20 17:32:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,7 +42,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
 {
    List       *rewritten;
    Query      *query;
-   Plan       *plan;
+   PlannedStmt *plan;
    Portal      portal;
    MemoryContext oldContext;
 
@@ -98,13 +98,12 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
    plan = planner(query, true, stmt->options, params);
 
    /*
-    * Create a portal and copy the query and plan into its memory context.
+    * Create a portal and copy the plan into its memory context.
     */
    portal = CreatePortal(stmt->portalname, false, false);
 
    oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
-   query = copyObject(query);
    plan = copyObject(plan);
 
    /*
@@ -115,7 +114,6 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
                      NULL,
                      debug_query_string ? pstrdup(debug_query_string) : NULL,
                      "SELECT", /* cursor's query is always a SELECT */
-                     list_make1(query),
                      list_make1(plan),
                      PortalGetHeapMemory(portal));
 
@@ -140,7 +138,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
    portal->cursorOptions = stmt->options;
    if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
    {
-       if (ExecSupportsBackwardScan(plan))
+       if (ExecSupportsBackwardScan(plan->planTree))
            portal->cursorOptions |= CURSOR_OPT_SCROLL;
        else
            portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
index ecd5074211e917b93a4f08a93730032398833e2c..8a5382c737815f975818d43b3c564cb9bd8a0759 100644 (file)
@@ -10,7 +10,7 @@
  * Copyright (c) 2002-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.68 2007/01/28 19:05:35 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.69 2007/02/20 17:32:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -114,9 +114,9 @@ PrepareQuery(PrepareStmt *stmt)
    StorePreparedStatement(stmt->name,
                           debug_query_string,
                           commandTag,
-                          query_list,
                           plan_list,
                           stmt->argtype_oids,
+                          true,
                           true);
 }
 
@@ -129,8 +129,7 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
 {
    PreparedStatement *entry;
    char       *query_string;
-   List       *query_list,
-              *plan_list;
+   List       *plan_list;
    MemoryContext qcontext;
    ParamListInfo paramLI = NULL;
    EState     *estate = NULL;
@@ -139,13 +138,18 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
    /* Look it up in the hash table */
    entry = FetchPreparedStatement(stmt->name, true);
 
+   /*
+    * Punt if not fully planned.  (Currently, that only happens for the
+    * protocol-level unnamed statement, which can't be accessed from SQL;
+    * so there's no point in doing more than a quick check here.)
+    */
+   if (!entry->fully_planned)
+       elog(ERROR, "EXECUTE does not support unplanned prepared statements");
+
    query_string = entry->query_string;
-   query_list = entry->query_list;
-   plan_list = entry->plan_list;
+   plan_list = entry->stmt_list;
    qcontext = entry->context;
 
-   Assert(list_length(query_list) == list_length(plan_list));
-
    /* Evaluate parameters, if any */
    if (entry->argtype_list != NIL)
    {
@@ -172,30 +176,26 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
    if (stmt->into)
    {
        MemoryContext oldContext;
-       Query      *query;
+       PlannedStmt *pstmt;
 
-       oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+       qcontext = PortalGetHeapMemory(portal);
+       oldContext = MemoryContextSwitchTo(qcontext);
 
        if (query_string)
            query_string = pstrdup(query_string);
-       query_list = copyObject(query_list);
        plan_list = copyObject(plan_list);
-       qcontext = PortalGetHeapMemory(portal);
 
-       if (list_length(query_list) != 1)
+       if (list_length(plan_list) != 1)
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                     errmsg("prepared statement is not a SELECT")));
-       query = (Query *) linitial(query_list);
-       if (query->commandType != CMD_SELECT)
+       pstmt = (PlannedStmt *) linitial(plan_list);
+       if (!IsA(pstmt, PlannedStmt) ||
+           pstmt->commandType != CMD_SELECT)
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                     errmsg("prepared statement is not a SELECT")));
-       query->into = copyObject(stmt->into);
-       query->intoOptions = copyObject(stmt->intoOptions);
-       query->intoOnCommit = stmt->into_on_commit;
-       if (stmt->into_tbl_space)
-           query->intoTableSpaceName = pstrdup(stmt->into_tbl_space);
+       pstmt->into = copyObject(stmt->into);
 
        MemoryContextSwitchTo(oldContext);
    }
@@ -204,7 +204,6 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
                      NULL,
                      query_string,
                      entry->commandTag,
-                     query_list,
                      plan_list,
                      qcontext);
 
@@ -305,9 +304,9 @@ void
 StorePreparedStatement(const char *stmt_name,
                       const char *query_string,
                       const char *commandTag,
-                      List *query_list,
-                      List *plan_list,
+                      List *stmt_list,
                       List *argtype_list,
+                      bool fully_planned,
                       bool from_sql)
 {
    PreparedStatement *entry;
@@ -345,8 +344,7 @@ StorePreparedStatement(const char *stmt_name,
     * incomplete (ie corrupt) hashtable entry.
     */
    qstring = query_string ? pstrdup(query_string) : NULL;
-   query_list = (List *) copyObject(query_list);
-   plan_list = (List *) copyObject(plan_list);
+   stmt_list = (List *) copyObject(stmt_list);
    argtype_list = list_copy(argtype_list);
 
    /* Now we can add entry to hash table */
@@ -363,12 +361,12 @@ StorePreparedStatement(const char *stmt_name,
    /* Fill in the hash table entry with copied data */
    entry->query_string = qstring;
    entry->commandTag = commandTag;
-   entry->query_list = query_list;
-   entry->plan_list = plan_list;
+   entry->stmt_list = stmt_list;
    entry->argtype_list = argtype_list;
+   entry->fully_planned = fully_planned;
+   entry->from_sql = from_sql;
    entry->context = entrycxt;
    entry->prepare_time = GetCurrentStatementStartTimestamp();
-   entry->from_sql = from_sql;
 
    MemoryContextSwitchTo(oldcxt);
 }
@@ -426,21 +424,54 @@ FetchPreparedStatementParams(const char *stmt_name)
 TupleDesc
 FetchPreparedStatementResultDesc(PreparedStatement *stmt)
 {
+   Node       *node;
    Query      *query;
+   PlannedStmt *pstmt;
 
-   switch (ChoosePortalStrategy(stmt->query_list))
+   switch (ChoosePortalStrategy(stmt->stmt_list))
    {
        case PORTAL_ONE_SELECT:
-           query = (Query *) linitial(stmt->query_list);
-           return ExecCleanTypeFromTL(query->targetList, false);
+           node = (Node *) linitial(stmt->stmt_list);
+           if (IsA(node, Query))
+           {
+               query = (Query *) node;
+               return ExecCleanTypeFromTL(query->targetList, false);
+           }
+           if (IsA(node, PlannedStmt))
+           {
+               pstmt = (PlannedStmt *) node;
+               return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
+           }
+           /* other cases shouldn't happen, but return NULL */
+           break;
 
        case PORTAL_ONE_RETURNING:
-           query = PortalListGetPrimaryQuery(stmt->query_list);
-           return ExecCleanTypeFromTL(query->returningList, false);
+           node = PortalListGetPrimaryStmt(stmt->stmt_list);
+           if (IsA(node, Query))
+           {
+               query = (Query *) node;
+               Assert(query->returningList);
+               return ExecCleanTypeFromTL(query->returningList, false);
+           }
+           if (IsA(node, PlannedStmt))
+           {
+               pstmt = (PlannedStmt *) node;
+               Assert(pstmt->returningLists);
+               return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
+           }
+           /* other cases shouldn't happen, but return NULL */
+           break;
 
        case PORTAL_UTIL_SELECT:
-           query = (Query *) linitial(stmt->query_list);
-           return UtilityTupleDescriptor(query->utilityStmt);
+           node = (Node *) linitial(stmt->stmt_list);
+           if (IsA(node, Query))
+           {
+               query = (Query *) node;
+               Assert(query->utilityStmt);
+               return UtilityTupleDescriptor(query->utilityStmt);
+           }
+           /* else it's a bare utility statement */
+           return UtilityTupleDescriptor(node);
 
        case PORTAL_MULTI_QUERY:
            /* will not return tuples */
@@ -460,7 +491,7 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
 bool
 PreparedStatementReturnsTuples(PreparedStatement *stmt)
 {
-   switch (ChoosePortalStrategy(stmt->query_list))
+   switch (ChoosePortalStrategy(stmt->stmt_list))
    {
        case PORTAL_ONE_SELECT:
        case PORTAL_ONE_RETURNING:
@@ -480,52 +511,15 @@ PreparedStatementReturnsTuples(PreparedStatement *stmt)
  * targetlist.
  *
  * Note: do not modify the result.
- *
- * XXX be careful to keep this in sync with FetchPortalTargetList,
- * and with UtilityReturnsTuples.
  */
 List *
 FetchPreparedStatementTargetList(PreparedStatement *stmt)
 {
-   PortalStrategy strategy = ChoosePortalStrategy(stmt->query_list);
-
-   if (strategy == PORTAL_ONE_SELECT)
-       return ((Query *) linitial(stmt->query_list))->targetList;
-   if (strategy == PORTAL_ONE_RETURNING)
-       return (PortalListGetPrimaryQuery(stmt->query_list))->returningList;
-   if (strategy == PORTAL_UTIL_SELECT)
-   {
-       Node       *utilityStmt;
-
-       utilityStmt = ((Query *) linitial(stmt->query_list))->utilityStmt;
-       switch (nodeTag(utilityStmt))
-       {
-           case T_FetchStmt:
-               {
-                   FetchStmt  *substmt = (FetchStmt *) utilityStmt;
-                   Portal      subportal;
-
-                   Assert(!substmt->ismove);
-                   subportal = GetPortalByName(substmt->portalname);
-                   Assert(PortalIsValid(subportal));
-                   return FetchPortalTargetList(subportal);
-               }
-
-           case T_ExecuteStmt:
-               {
-                   ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
-                   PreparedStatement *entry;
-
-                   Assert(!substmt->into);
-                   entry = FetchPreparedStatement(substmt->name, true);
-                   return FetchPreparedStatementTargetList(entry);
-               }
-
-           default:
-               break;
-       }
-   }
-   return NIL;
+   /* no point in looking if it doesn't return tuples */
+   if (ChoosePortalStrategy(stmt->stmt_list) == PORTAL_MULTI_QUERY)
+       return NIL;
+   /* get the primary statement and find out what it returns */
+   return FetchStatementTargetList(PortalListGetPrimaryStmt(stmt->stmt_list));
 }
 
 /*
@@ -574,10 +568,8 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
 {
    ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
    PreparedStatement *entry;
-   ListCell   *q,
-              *p;
-   List       *query_list,
-              *plan_list;
+   List       *plan_list;
+   ListCell   *p;
    ParamListInfo paramLI = NULL;
    EState     *estate = NULL;
 
@@ -587,10 +579,15 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
    /* Look it up in the hash table */
    entry = FetchPreparedStatement(execstmt->name, true);
 
-   query_list = entry->query_list;
-   plan_list = entry->plan_list;
+   /*
+    * Punt if not fully planned.  (Currently, that only happens for the
+    * protocol-level unnamed statement, which can't be accessed from SQL;
+    * so there's no point in doing more than a quick check here.)
+    */
+   if (!entry->fully_planned)
+       elog(ERROR, "EXPLAIN EXECUTE does not support unplanned prepared statements");
 
-   Assert(list_length(query_list) == list_length(plan_list));
+   plan_list = entry->stmt_list;
 
    /* Evaluate parameters, if any */
    if (entry->argtype_list != NIL)
@@ -606,17 +603,16 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
    }
 
    /* Explain each query */
-   forboth(q, query_list, p, plan_list)
+   foreach(p, plan_list)
    {
-       Query      *query = (Query *) lfirst(q);
-       Plan       *plan = (Plan *) lfirst(p);
+       PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
        bool        is_last_query;
 
        is_last_query = (lnext(p) == NULL);
 
-       if (query->commandType == CMD_UTILITY)
+       if (!IsA(pstmt, PlannedStmt))
        {
-           if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
+           if (IsA(pstmt, NotifyStmt))
                do_text_output_oneline(tstate, "NOTIFY");
            else
                do_text_output_oneline(tstate, "UTILITY");
@@ -627,15 +623,15 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
 
            if (execstmt->into)
            {
-               if (query->commandType != CMD_SELECT)
+               if (pstmt->commandType != CMD_SELECT)
                    ereport(ERROR,
                            (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                             errmsg("prepared statement is not a SELECT")));
 
-               /* Copy the query so we can modify it */
-               query = copyObject(query);
+               /* Copy the stmt so we can modify it */
+               pstmt = copyObject(pstmt);
 
-               query->into = execstmt->into;
+               pstmt->into = execstmt->into;
            }
 
            /*
@@ -648,7 +644,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
            ActiveSnapshot->curcid = GetCurrentCommandId();
 
            /* Create a QueryDesc requesting no output */
-           qdesc = CreateQueryDesc(query, plan,
+           qdesc = CreateQueryDesc(pstmt,
                                    ActiveSnapshot, InvalidSnapshot,
                                    None_Receiver,
                                    paramLI, stmt->analyze);
index 91a76bb0779b15c8c8e084e4693254f8290ed1a7..405b58f9fd6b087ec18ecda7541f7b1a98e63c9c 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.286 2007/02/02 00:07:02 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.287 2007/02/20 17:32:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -93,7 +93,8 @@ static void ExecProcessReturning(ProjectionInfo *projectReturning,
 static TupleTableSlot *EvalPlanQualNext(EState *estate);
 static void EndEvalPlanQual(EState *estate);
 static void ExecCheckRTEPerms(RangeTblEntry *rte);
-static void ExecCheckXactReadOnly(Query *parsetree);
+static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
+static void ExecCheckRangeTblReadOnly(List *rtable);
 static void EvalPlanQualStart(evalPlanQual *epq, EState *estate,
                  evalPlanQual *priorepq);
 static void EvalPlanQualStop(evalPlanQual *epq);
@@ -139,7 +140,7 @@ ExecutorStart(QueryDesc *queryDesc, int eflags)
     * planned to non-temporary tables.  EXPLAIN is considered read-only.
     */
    if (XactReadOnly && !(eflags & EXEC_FLAG_EXPLAIN_ONLY))
-       ExecCheckXactReadOnly(queryDesc->parsetree);
+       ExecCheckXactReadOnly(queryDesc->plannedstmt);
 
    /*
     * Build EState, switch into per-query memory context for startup.
@@ -154,9 +155,9 @@ ExecutorStart(QueryDesc *queryDesc, int eflags)
     */
    estate->es_param_list_info = queryDesc->params;
 
-   if (queryDesc->plantree->nParamExec > 0)
+   if (queryDesc->plannedstmt->nParamExec > 0)
        estate->es_param_exec_vals = (ParamExecData *)
-           palloc0(queryDesc->plantree->nParamExec * sizeof(ParamExecData));
+           palloc0(queryDesc->plannedstmt->nParamExec * sizeof(ParamExecData));
 
    /*
     * Copy other important information into the EState
@@ -227,7 +228,7 @@ ExecutorRun(QueryDesc *queryDesc,
    estate->es_lastoid = InvalidOid;
 
    sendTuples = (operation == CMD_SELECT ||
-                 queryDesc->parsetree->returningList);
+                 queryDesc->plannedstmt->returningLists);
 
    if (sendTuples)
        (*dest->rStartup) (dest, operation, queryDesc->tupDesc);
@@ -414,26 +415,41 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
  * Check that the query does not imply any writes to non-temp tables.
  */
 static void
-ExecCheckXactReadOnly(Query *parsetree)
+ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 {
-   ListCell   *l;
-
    /*
     * CREATE TABLE AS or SELECT INTO?
     *
     * XXX should we allow this if the destination is temp?
     */
-   if (parsetree->into != NULL)
+   if (plannedstmt->into != NULL)
        goto fail;
 
    /* Fail if write permissions are requested on any non-temp table */
-   foreach(l, parsetree->rtable)
+   ExecCheckRangeTblReadOnly(plannedstmt->rtable);
+
+   return;
+
+fail:
+   ereport(ERROR,
+           (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
+            errmsg("transaction is read-only")));
+}
+
+static void
+ExecCheckRangeTblReadOnly(List *rtable)
+{
+   ListCell   *l;
+
+   /* Fail if write permissions are requested on any non-temp table */
+   foreach(l, rtable)
    {
        RangeTblEntry *rte = lfirst(l);
 
        if (rte->rtekind == RTE_SUBQUERY)
        {
-           ExecCheckXactReadOnly(rte->subquery);
+           Assert(!rte->subquery->into);
+           ExecCheckRangeTblReadOnly(rte->subquery->rtable);
            continue;
        }
 
@@ -469,11 +485,11 @@ static void
 InitPlan(QueryDesc *queryDesc, int eflags)
 {
    CmdType     operation = queryDesc->operation;
-   Query      *parseTree = queryDesc->parsetree;
-   Plan       *plan = queryDesc->plantree;
+   PlannedStmt *plannedstmt = queryDesc->plannedstmt;
+   Plan       *plan = plannedstmt->planTree;
+   List       *rangeTable = plannedstmt->rtable;
    EState     *estate = queryDesc->estate;
    PlanState  *planstate;
-   List       *rangeTable;
    TupleDesc   tupType;
    ListCell   *l;
 
@@ -482,12 +498,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
     * rangetable here --- subplan RTEs will be checked during
     * ExecInitSubPlan().
     */
-   ExecCheckRTPerms(parseTree->rtable);
-
-   /*
-    * get information from query descriptor
-    */
-   rangeTable = parseTree->rtable;
+   ExecCheckRTPerms(rangeTable);
 
    /*
     * initialize the node's execution state
@@ -495,50 +506,27 @@ InitPlan(QueryDesc *queryDesc, int eflags)
    estate->es_range_table = rangeTable;
 
    /*
-    * if there is a result relation, initialize result relation stuff
+    * initialize result relation stuff
     */
-   if (parseTree->resultRelation)
+   if (plannedstmt->resultRelations)
    {
-       List       *resultRelations = parseTree->resultRelations;
-       int         numResultRelations;
+       List       *resultRelations = plannedstmt->resultRelations;
+       int         numResultRelations = list_length(resultRelations);
        ResultRelInfo *resultRelInfos;
+       ResultRelInfo *resultRelInfo;
 
-       if (resultRelations != NIL)
-       {
-           /*
-            * Multiple result relations (due to inheritance)
-            * parseTree->resultRelations identifies them all
-            */
-           ResultRelInfo *resultRelInfo;
-
-           numResultRelations = list_length(resultRelations);
-           resultRelInfos = (ResultRelInfo *)
-               palloc(numResultRelations * sizeof(ResultRelInfo));
-           resultRelInfo = resultRelInfos;
-           foreach(l, resultRelations)
-           {
-               initResultRelInfo(resultRelInfo,
-                                 lfirst_int(l),
-                                 rangeTable,
-                                 operation,
-                                 estate->es_instrument);
-               resultRelInfo++;
-           }
-       }
-       else
+       resultRelInfos = (ResultRelInfo *)
+           palloc(numResultRelations * sizeof(ResultRelInfo));
+       resultRelInfo = resultRelInfos;
+       foreach(l, resultRelations)
        {
-           /*
-            * Single result relation identified by parseTree->resultRelation
-            */
-           numResultRelations = 1;
-           resultRelInfos = (ResultRelInfo *) palloc(sizeof(ResultRelInfo));
-           initResultRelInfo(resultRelInfos,
-                             parseTree->resultRelation,
+           initResultRelInfo(resultRelInfo,
+                             lfirst_int(l),
                              rangeTable,
                              operation,
                              estate->es_instrument);
+           resultRelInfo++;
        }
-
        estate->es_result_relations = resultRelInfos;
        estate->es_num_result_relations = numResultRelations;
        /* Initialize to first or only result rel */
@@ -560,10 +548,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
     * correct tuple descriptors.  (Other SELECT INTO stuff comes later.)
     */
    estate->es_select_into = false;
-   if (operation == CMD_SELECT && parseTree->into != NULL)
+   if (operation == CMD_SELECT && plannedstmt->into != NULL)
    {
        estate->es_select_into = true;
-       estate->es_into_oids = interpretOidsOption(parseTree->intoOptions);
+       estate->es_into_oids = interpretOidsOption(plannedstmt->into->options);
    }
 
    /*
@@ -572,7 +560,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
     * While we are at it, build the ExecRowMark list.
     */
    estate->es_rowMarks = NIL;
-   foreach(l, parseTree->rowMarks)
+   foreach(l, plannedstmt->rowMarks)
    {
        RowMarkClause *rc = (RowMarkClause *) lfirst(l);
        Oid         relid = getrelid(rc->rti, rangeTable);
@@ -600,13 +588,13 @@ InitPlan(QueryDesc *queryDesc, int eflags)
    {
        int         nSlots = ExecCountSlotsNode(plan);
 
-       if (parseTree->resultRelations != NIL)
-           nSlots += list_length(parseTree->resultRelations);
+       if (plannedstmt->resultRelations != NIL)
+           nSlots += list_length(plannedstmt->resultRelations);
        else
            nSlots += 1;
        if (operation != CMD_SELECT)
            nSlots++;           /* for es_trig_tuple_slot */
-       if (parseTree->returningLists)
+       if (plannedstmt->returningLists)
            nSlots++;           /* for RETURNING projection */
 
        estate->es_tupleTable = ExecCreateTupleTable(nSlots);
@@ -617,7 +605,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
    }
 
    /* mark EvalPlanQual not active */
-   estate->es_topPlan = plan;
+   estate->es_plannedstmt = plannedstmt;
    estate->es_evalPlanQual = NULL;
    estate->es_evTupleNull = NULL;
    estate->es_evTuple = NULL;
@@ -683,7 +671,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
             * junk filter.  Note this is only possible for UPDATE/DELETE, so
             * we can't be fooled by some needing a filter and some not.
             */
-           if (parseTree->resultRelations != NIL)
+           if (list_length(plannedstmt->resultRelations) > 1)
            {
                PlanState **appendplans;
                int         as_nplans;
@@ -772,7 +760,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
    /*
     * Initialize RETURNING projections if needed.
     */
-   if (parseTree->returningLists)
+   if (plannedstmt->returningLists)
    {
        TupleTableSlot *slot;
        ExprContext *econtext;
@@ -782,7 +770,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
         * We set QueryDesc.tupDesc to be the RETURNING rowtype in this case.
         * We assume all the sublists will generate the same output tupdesc.
         */
-       tupType = ExecTypeFromTL((List *) linitial(parseTree->returningLists),
+       tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists),
                                 false);
 
        /* Set up a slot for the output of the RETURNING projection(s) */
@@ -795,9 +783,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
         * Build a projection for each result rel.  Note that any SubPlans in
         * the RETURNING lists get attached to the topmost plan node.
         */
-       Assert(list_length(parseTree->returningLists) == estate->es_num_result_relations);
+       Assert(list_length(plannedstmt->returningLists) == estate->es_num_result_relations);
        resultRelInfo = estate->es_result_relations;
-       foreach(l, parseTree->returningLists)
+       foreach(l, plannedstmt->returningLists)
        {
            List       *rlist = (List *) lfirst(l);
            List       *rliststate;
@@ -2273,14 +2261,14 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
    epqstate->es_into_relation_descriptor = estate->es_into_relation_descriptor;
    epqstate->es_into_relation_use_wal = estate->es_into_relation_use_wal;
    epqstate->es_param_list_info = estate->es_param_list_info;
-   if (estate->es_topPlan->nParamExec > 0)
+   if (estate->es_plannedstmt->nParamExec > 0)
        epqstate->es_param_exec_vals = (ParamExecData *)
-           palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData));
+           palloc0(estate->es_plannedstmt->nParamExec * sizeof(ParamExecData));
    epqstate->es_rowMarks = estate->es_rowMarks;
    epqstate->es_instrument = estate->es_instrument;
    epqstate->es_select_into = estate->es_select_into;
    epqstate->es_into_oids = estate->es_into_oids;
-   epqstate->es_topPlan = estate->es_topPlan;
+   epqstate->es_plannedstmt = estate->es_plannedstmt;
 
    /*
     * Each epqstate must have its own es_evTupleNull state, but all the stack
@@ -2299,7 +2287,7 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
    epqstate->es_tupleTable =
        ExecCreateTupleTable(estate->es_tupleTable->size);
 
-   epq->planstate = ExecInitNode(estate->es_topPlan, epqstate, 0);
+   epq->planstate = ExecInitNode(estate->es_plannedstmt->planTree, epqstate, 0);
 
    MemoryContextSwitchTo(oldcontext);
 }
@@ -2365,7 +2353,7 @@ typedef struct
 static void
 OpenIntoRel(QueryDesc *queryDesc)
 {
-   Query      *parseTree = queryDesc->parsetree;
+   IntoClause *into = queryDesc->plannedstmt->into;
    EState     *estate = queryDesc->estate;
    Relation    intoRelationDesc;
    char       *intoName;
@@ -2377,10 +2365,12 @@ OpenIntoRel(QueryDesc *queryDesc)
    TupleDesc   tupdesc;
    DR_intorel *myState;
 
+   Assert(into);
+
    /*
     * Check consistency of arguments
     */
-   if (parseTree->intoOnCommit != ONCOMMIT_NOOP && !parseTree->into->istemp)
+   if (into->onCommit != ONCOMMIT_NOOP && !into->rel->istemp)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                 errmsg("ON COMMIT can only be used on temporary tables")));
@@ -2388,8 +2378,8 @@ OpenIntoRel(QueryDesc *queryDesc)
    /*
     * Find namespace to create in, check its permissions
     */
-   intoName = parseTree->into->relname;
-   namespaceId = RangeVarGetCreationNamespace(parseTree->into);
+   intoName = into->rel->relname;
+   namespaceId = RangeVarGetCreationNamespace(into->rel);
 
    aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
                                      ACL_CREATE);
@@ -2401,16 +2391,16 @@ OpenIntoRel(QueryDesc *queryDesc)
     * Select tablespace to use.  If not specified, use default_tablespace
     * (which may in turn default to database's default).
     */
-   if (parseTree->intoTableSpaceName)
+   if (into->tableSpaceName)
    {
-       tablespaceId = get_tablespace_oid(parseTree->intoTableSpaceName);
+       tablespaceId = get_tablespace_oid(into->tableSpaceName);
        if (!OidIsValid(tablespaceId))
            ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_OBJECT),
                     errmsg("tablespace \"%s\" does not exist",
-                           parseTree->intoTableSpaceName)));
+                           into->tableSpaceName)));
    }
-   else if (parseTree->into->istemp)
+   else if (into->rel->istemp)
    {
        tablespaceId = GetTempTablespace();
    }
@@ -2435,7 +2425,7 @@ OpenIntoRel(QueryDesc *queryDesc)
 
    /* Parse and validate any reloptions */
    reloptions = transformRelOptions((Datum) 0,
-                                    parseTree->intoOptions,
+                                    into->options,
                                     true,
                                     false);
    (void) heap_reloptions(RELKIND_RELATION, reloptions, true);
@@ -2454,7 +2444,7 @@ OpenIntoRel(QueryDesc *queryDesc)
                                              false,
                                              true,
                                              0,
-                                             parseTree->intoOnCommit,
+                                             into->onCommit,
                                              reloptions,
                                              allowSystemTableMods);
 
index 91efba011ca139f7b9e353ec193b028c1633c3ae..d9957573883b3bf909c44f2a5ef775feec3f5b77 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.144 2007/02/06 17:35:20 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.145 2007/02/20 17:32:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -242,7 +242,7 @@ InternalCreateExecutorState(MemoryContext qcontext, bool is_subquery)
 
    estate->es_per_tuple_exprcontext = NULL;
 
-   estate->es_topPlan = NULL;
+   estate->es_plannedstmt = NULL;
    estate->es_evalPlanQual = NULL;
    estate->es_evTupleNull = NULL;
    estate->es_evTuple = NULL;
index 78044b2d614139f2e655dbbe35a943f53c9f9449..596a482fa137b39a7df020451e12ad45284173f3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.110 2007/02/02 00:02:55 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.111 2007/02/20 17:32:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,8 +33,8 @@
 
 /*
  * We have an execution_state record for each query in a function. Each
- * record contains a querytree and plantree for its query. If the query
- * is currently in F_EXEC_RUN state then there's a QueryDesc too.
+ * record contains a plantree for its query.  If the query is currently in
+ * F_EXEC_RUN state then there's a QueryDesc too.
  */
 typedef enum
 {
@@ -45,8 +45,7 @@ typedef struct local_es
 {
    struct local_es *next;
    ExecStatus  status;
-   Query      *query;
-   Plan       *plan;
+   Node       *stmt;           /* PlannedStmt or utility statement */
    QueryDesc  *qd;             /* null unless status == RUN */
 } execution_state;
 
@@ -105,26 +104,30 @@ init_execution_state(List *queryTree_list, bool readonly_func)
    foreach(qtl_item, queryTree_list)
    {
        Query      *queryTree = lfirst(qtl_item);
-       Plan       *planTree;
+       Node       *stmt;
        execution_state *newes;
 
+       Assert(IsA(queryTree, Query));
+
+       if (queryTree->commandType == CMD_UTILITY)
+           stmt = queryTree->utilityStmt;
+       else
+           stmt = (Node *) pg_plan_query(queryTree, NULL);
+
        /* Precheck all commands for validity in a function */
-       if (queryTree->commandType == CMD_UTILITY &&
-           IsA(queryTree->utilityStmt, TransactionStmt))
+       if (IsA(stmt, TransactionStmt))
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
            /* translator: %s is a SQL statement name */
                     errmsg("%s is not allowed in a SQL function",
-                           CreateQueryTag(queryTree))));
+                           CreateCommandTag(stmt))));
 
-       if (readonly_func && !QueryIsReadOnly(queryTree))
+       if (readonly_func && !CommandIsReadOnly(stmt))
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
            /* translator: %s is a SQL statement name */
                     errmsg("%s is not allowed in a non-volatile function",
-                           CreateQueryTag(queryTree))));
-
-       planTree = pg_plan_query(queryTree, NULL);
+                           CreateCommandTag(stmt))));
 
        newes = (execution_state *) palloc(sizeof(execution_state));
        if (preves)
@@ -134,8 +137,7 @@ init_execution_state(List *queryTree_list, bool readonly_func)
 
        newes->next = NULL;
        newes->status = F_EXEC_START;
-       newes->query = queryTree;
-       newes->plan = planTree;
+       newes->stmt = stmt;
        newes->qd = NULL;
 
        preves = newes;
@@ -298,10 +300,16 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
        snapshot = CopySnapshot(GetTransactionSnapshot());
    }
 
-   es->qd = CreateQueryDesc(es->query, es->plan,
-                            snapshot, InvalidSnapshot,
-                            None_Receiver,
-                            fcache->paramLI, false);
+   if (IsA(es->stmt, PlannedStmt))
+       es->qd = CreateQueryDesc((PlannedStmt *) es->stmt,
+                                snapshot, InvalidSnapshot,
+                                None_Receiver,
+                                fcache->paramLI, false);
+   else
+       es->qd = CreateUtilityQueryDesc(es->stmt,
+                                       snapshot,
+                                       None_Receiver,
+                                       fcache->paramLI);
 
    /* We assume we don't need to set up ActiveSnapshot for ExecutorStart */
 
@@ -337,7 +345,7 @@ postquel_getnext(execution_state *es)
 
        if (es->qd->operation == CMD_UTILITY)
        {
-           ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->params,
+           ProcessUtility(es->qd->utilitystmt, es->qd->params,
                           es->qd->dest, NULL);
            result = NULL;
        }
@@ -351,7 +359,7 @@ postquel_getnext(execution_state *es)
             */
            if (LAST_POSTQUEL_COMMAND(es) &&
                es->qd->operation == CMD_SELECT &&
-               es->qd->parsetree->into == NULL)
+               es->qd->plannedstmt->into == NULL)
                count = 1L;
            else
                count = 0L;
index a565ba3cd2b9bac9c501780b910da6dbadcb29e9..33ec21286d96d291502a3617992bcac5477cf9d1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.169 2007/01/09 22:00:59 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.170 2007/02/20 17:32:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -831,8 +831,7 @@ SPI_cursor_open(const char *name, void *plan,
                bool read_only)
 {
    _SPI_plan  *spiplan = (_SPI_plan *) plan;
-   List       *qtlist;
-   List       *ptlist;
+   List       *stmt_list;
    ParamListInfo paramLI;
    Snapshot    snapshot;
    MemoryContext oldcontext;
@@ -846,29 +845,22 @@ SPI_cursor_open(const char *name, void *plan,
    if (!SPI_is_cursor_plan(spiplan))
    {
        /* try to give a good error message */
-       Query      *queryTree;
+       Node       *stmt;
 
-       if (list_length(spiplan->qtlist) != 1)
+       if (list_length(spiplan->stmt_list_list) != 1)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
                     errmsg("cannot open multi-query plan as cursor")));
-       queryTree = PortalListGetPrimaryQuery((List *) linitial(spiplan->qtlist));
-       if (queryTree == NULL)
-           ereport(ERROR,
-                   (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
-                    errmsg("cannot open empty query as cursor")));
+       stmt = PortalListGetPrimaryStmt((List *) linitial(spiplan->stmt_list_list));
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
        /* translator: %s is name of a SQL command, eg INSERT */
                 errmsg("cannot open %s query as cursor",
-                       CreateQueryTag(queryTree))));
+                       CreateCommandTag(stmt))));
    }
 
-   Assert(list_length(spiplan->qtlist) == 1);
-   qtlist = (List *) linitial(spiplan->qtlist);
-   ptlist = spiplan->ptlist;
-   if (list_length(qtlist) != list_length(ptlist))
-       elog(ERROR, "corrupted SPI plan lists");
+   Assert(list_length(spiplan->stmt_list_list) == 1);
+   stmt_list = (List *) linitial(spiplan->stmt_list_list);
 
    /* Reset SPI result (note we deliberately don't touch lastoid) */
    SPI_processed = 0;
@@ -888,10 +880,9 @@ SPI_cursor_open(const char *name, void *plan,
        portal = CreatePortal(name, false, false);
    }
 
-   /* Switch to portal's memory and copy the parsetrees and plans to there */
+   /* Switch to portal's memory and copy the plans to there */
    oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-   qtlist = copyObject(qtlist);
-   ptlist = copyObject(ptlist);
+   stmt_list = copyObject(stmt_list);
 
    /* If the plan has parameters, set them up */
    if (spiplan->nargs > 0)
@@ -934,9 +925,8 @@ SPI_cursor_open(const char *name, void *plan,
    PortalDefineQuery(portal,
                      NULL,     /* no statement name */
                      spiplan->query,
-                     CreateQueryTag(PortalListGetPrimaryQuery(qtlist)),
-                     qtlist,
-                     ptlist,
+                     CreateCommandTag(PortalListGetPrimaryStmt(stmt_list)),
+                     stmt_list,
                      PortalGetHeapMemory(portal));
 
    MemoryContextSwitchTo(oldcontext);
@@ -945,8 +935,9 @@ SPI_cursor_open(const char *name, void *plan,
     * Set up options for portal.
     */
    portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
-   if (list_length(ptlist) == 1 &&
-       ExecSupportsBackwardScan((Plan *) linitial(ptlist)))
+   if (list_length(stmt_list) == 1 &&
+       IsA((Node *) linitial(stmt_list), PlannedStmt) &&
+       ExecSupportsBackwardScan(((PlannedStmt *) linitial(stmt_list))->planTree))
        portal->cursorOptions |= CURSOR_OPT_SCROLL;
    else
        portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
@@ -1076,10 +1067,10 @@ SPI_is_cursor_plan(void *plan)
        return false;
    }
 
-   if (list_length(spiplan->qtlist) != 1)
+   if (list_length(spiplan->stmt_list_list) != 1)
        return false;           /* not exactly 1 pre-rewrite command */
 
-   switch (ChoosePortalStrategy((List *) linitial(spiplan->qtlist)))
+   switch (ChoosePortalStrategy((List *) linitial(spiplan->stmt_list_list)))
    {
        case PORTAL_ONE_SELECT:
        case PORTAL_ONE_RETURNING:
@@ -1257,14 +1248,13 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
  *
  * At entry, plan->argtypes and plan->nargs must be valid.
  *
- * Query and plan lists are stored into *plan.
+ * Result lists are stored into *plan.
  */
 static void
 _SPI_prepare_plan(const char *src, _SPI_plan *plan)
 {
    List       *raw_parsetree_list;
-   List       *query_list_list;
-   List       *plan_list;
+   List       *stmt_list_list;
    ListCell   *list_item;
    ErrorContextCallback spierrcontext;
    Oid        *argtypes = plan->argtypes;
@@ -1294,12 +1284,11 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
    /*
     * Do parse analysis and rule rewrite for each raw parsetree.
     *
-    * We save the querytrees from each raw parsetree as a separate sublist.
+    * We save the results from each raw parsetree as a separate sublist.
     * This allows _SPI_execute_plan() to know where the boundaries between
     * original queries fall.
     */
-   query_list_list = NIL;
-   plan_list = NIL;
+   stmt_list_list = NIL;
 
    foreach(list_item, raw_parsetree_list)
    {
@@ -1308,14 +1297,11 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
 
        query_list = pg_analyze_and_rewrite(parsetree, src, argtypes, nargs);
 
-       query_list_list = lappend(query_list_list, query_list);
-
-       plan_list = list_concat(plan_list,
-                               pg_plan_queries(query_list, NULL, false));
+       stmt_list_list = lappend(stmt_list_list,
+                                pg_plan_queries(query_list, NULL, false));
    }
 
-   plan->qtlist = query_list_list;
-   plan->ptlist = plan_list;
+   plan->stmt_list_list = stmt_list_list;
 
    /*
     * Pop the error context stack
@@ -1348,9 +1334,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
    saveActiveSnapshot = ActiveSnapshot;
    PG_TRY();
    {
-       List       *query_list_list = plan->qtlist;
-       ListCell   *plan_list_item = list_head(plan->ptlist);
-       ListCell   *query_list_list_item;
+       ListCell   *stmt_list_list_item;
        ErrorContextCallback spierrcontext;
        int         nargs = plan->nargs;
        ParamListInfo paramLI;
@@ -1386,57 +1370,61 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
        spierrcontext.previous = error_context_stack;
        error_context_stack = &spierrcontext;
 
-       foreach(query_list_list_item, query_list_list)
+       foreach(stmt_list_list_item, plan->stmt_list_list)
        {
-           List       *query_list = lfirst(query_list_list_item);
-           ListCell   *query_list_item;
+           List       *stmt_list = (List *) lfirst(stmt_list_list_item);
+           ListCell   *stmt_list_item;
 
-           foreach(query_list_item, query_list)
+           foreach(stmt_list_item, stmt_list)
            {
-               Query      *queryTree = (Query *) lfirst(query_list_item);
-               Plan       *planTree;
+               Node       *stmt = (Node *) lfirst(stmt_list_item);
+               bool        canSetTag;
                QueryDesc  *qdesc;
                DestReceiver *dest;
 
-               planTree = lfirst(plan_list_item);
-               plan_list_item = lnext(plan_list_item);
-
                _SPI_current->processed = 0;
                _SPI_current->lastoid = InvalidOid;
                _SPI_current->tuptable = NULL;
 
-               if (queryTree->commandType == CMD_UTILITY)
+               if (IsA(stmt, PlannedStmt))
                {
-                   if (IsA(queryTree->utilityStmt, CopyStmt))
+                   canSetTag = ((PlannedStmt *) stmt)->canSetTag;
+               }
+               else
+               {
+                   /* utilities are canSetTag if only thing in list */
+                   canSetTag = (list_length(stmt_list) == 1);
+
+                   if (IsA(stmt, CopyStmt))
                    {
-                       CopyStmt   *stmt = (CopyStmt *) queryTree->utilityStmt;
+                       CopyStmt   *cstmt = (CopyStmt *) stmt;
 
-                       if (stmt->filename == NULL)
+                       if (cstmt->filename == NULL)
                        {
                            my_res = SPI_ERROR_COPY;
                            goto fail;
                        }
                    }
-                   else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
-                            IsA(queryTree->utilityStmt, ClosePortalStmt) ||
-                            IsA(queryTree->utilityStmt, FetchStmt))
+                   else if (IsA(stmt, DeclareCursorStmt) ||
+                            IsA(stmt, ClosePortalStmt) ||
+                            IsA(stmt, FetchStmt))
                    {
                        my_res = SPI_ERROR_CURSOR;
                        goto fail;
                    }
-                   else if (IsA(queryTree->utilityStmt, TransactionStmt))
+                   else if (IsA(stmt, TransactionStmt))
                    {
                        my_res = SPI_ERROR_TRANSACTION;
                        goto fail;
                    }
                }
 
-               if (read_only && !QueryIsReadOnly(queryTree))
+               if (read_only && !CommandIsReadOnly(stmt))
                    ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    /* translator: %s is a SQL statement name */
                       errmsg("%s is not allowed in a non-volatile function",
-                             CreateQueryTag(queryTree))));
+                             CreateCommandTag(stmt))));
 
                /*
                 * If not read-only mode, advance the command counter before
@@ -1445,7 +1433,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                if (!read_only)
                    CommandCounterIncrement();
 
-               dest = CreateDestReceiver(queryTree->canSetTag ? DestSPI : DestNone,
+               dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone,
                                          NULL);
 
                if (snapshot == InvalidSnapshot)
@@ -1471,26 +1459,24 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                        ActiveSnapshot->curcid = GetCurrentCommandId();
                }
 
-               if (queryTree->commandType == CMD_UTILITY)
-               {
-                   ProcessUtility(queryTree->utilityStmt, paramLI,
-                                  dest, NULL);
-                   /* Update "processed" if stmt returned tuples */
-                   if (_SPI_current->tuptable)
-                       _SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free;
-                   res = SPI_OK_UTILITY;
-               }
-               else
+               if (IsA(stmt, PlannedStmt))
                {
-                   qdesc = CreateQueryDesc(queryTree, planTree,
+                   qdesc = CreateQueryDesc((PlannedStmt *) stmt,
                                            ActiveSnapshot,
                                            crosscheck_snapshot,
                                            dest,
                                            paramLI, false);
-                   res = _SPI_pquery(qdesc,
-                                     queryTree->canSetTag ? tcount : 0);
+                   res = _SPI_pquery(qdesc, canSetTag ? tcount : 0);
                    FreeQueryDesc(qdesc);
                }
+               else
+               {
+                   ProcessUtility(stmt, paramLI, dest, NULL);
+                   /* Update "processed" if stmt returned tuples */
+                   if (_SPI_current->tuptable)
+                       _SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free;
+                   res = SPI_OK_UTILITY;
+               }
                FreeSnapshot(ActiveSnapshot);
                ActiveSnapshot = NULL;
 
@@ -1499,7 +1485,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                 * the caller.  Be careful to free any tuptables not returned,
                 * to avoid intratransaction memory leak.
                 */
-               if (queryTree->canSetTag)
+               if (canSetTag)
                {
                    my_processed = _SPI_current->processed;
                    my_lastoid = _SPI_current->lastoid;
@@ -1565,7 +1551,7 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
    switch (operation)
    {
        case CMD_SELECT:
-           if (queryDesc->parsetree->into)     /* select into table? */
+           if (queryDesc->plannedstmt->into)       /* select into table? */
                res = SPI_OK_SELINTO;
            else if (queryDesc->dest->mydest != DestSPI)
            {
@@ -1576,19 +1562,19 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
                res = SPI_OK_SELECT;
            break;
        case CMD_INSERT:
-           if (queryDesc->parsetree->returningList)
+           if (queryDesc->plannedstmt->returningLists)
                res = SPI_OK_INSERT_RETURNING;
            else
                res = SPI_OK_INSERT;
            break;
        case CMD_DELETE:
-           if (queryDesc->parsetree->returningList)
+           if (queryDesc->plannedstmt->returningLists)
                res = SPI_OK_DELETE_RETURNING;
            else
                res = SPI_OK_DELETE;
            break;
        case CMD_UPDATE:
-           if (queryDesc->parsetree->returningList)
+           if (queryDesc->plannedstmt->returningLists)
                res = SPI_OK_UPDATE_RETURNING;
            else
                res = SPI_OK_UPDATE;
@@ -1611,7 +1597,7 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
    _SPI_current->processed = queryDesc->estate->es_processed;
    _SPI_current->lastoid = queryDesc->estate->es_lastoid;
 
-   if ((res == SPI_OK_SELECT || queryDesc->parsetree->returningList) &&
+   if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->returningLists) &&
        queryDesc->dest->mydest == DestSPI)
    {
        if (_SPI_checktuples())
@@ -1813,8 +1799,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
    newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
    newplan->plancxt = plancxt;
    newplan->query = pstrdup(plan->query);
-   newplan->qtlist = (List *) copyObject(plan->qtlist);
-   newplan->ptlist = (List *) copyObject(plan->ptlist);
+   newplan->stmt_list_list = (List *) copyObject(plan->stmt_list_list);
    newplan->nargs = plan->nargs;
    if (plan->nargs > 0)
    {
index cdf98de568a6e158c0e496ccff8a2280c77ebe04..4d10afc022db95447f238c1fc51894d048fb9bf0 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.366 2007/02/19 02:23:11 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.367 2007/02/20 17:32:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * ****************************************************************
  */
 
+/*
+ * _copyPlannedStmt
+ */
+static PlannedStmt *
+_copyPlannedStmt(PlannedStmt *from)
+{
+   PlannedStmt    *newnode = makeNode(PlannedStmt);
+
+   COPY_SCALAR_FIELD(commandType);
+   COPY_SCALAR_FIELD(canSetTag);
+   COPY_NODE_FIELD(planTree);
+   COPY_NODE_FIELD(rtable);
+   COPY_NODE_FIELD(resultRelations);
+   COPY_NODE_FIELD(into);
+   COPY_NODE_FIELD(returningLists);
+   COPY_NODE_FIELD(rowMarks);
+   COPY_SCALAR_FIELD(nParamExec);
+
+   return newnode;
+}
+
 /*
  * CopyPlanFields
  *
@@ -84,7 +105,6 @@ CopyPlanFields(Plan *from, Plan *newnode)
    COPY_NODE_FIELD(initPlan);
    COPY_BITMAPSET_FIELD(extParam);
    COPY_BITMAPSET_FIELD(allParam);
-   COPY_SCALAR_FIELD(nParamExec);
 }
 
 /*
@@ -698,6 +718,23 @@ _copyRangeVar(RangeVar *from)
    return newnode;
 }
 
+/*
+ * _copyIntoClause
+ */
+static IntoClause *
+_copyIntoClause(IntoClause *from)
+{
+   IntoClause   *newnode = makeNode(IntoClause);
+
+   COPY_NODE_FIELD(rel);
+   COPY_NODE_FIELD(colNames);
+   COPY_NODE_FIELD(options);
+   COPY_SCALAR_FIELD(onCommit);
+   COPY_STRING_FIELD(tableSpaceName);
+
+   return newnode;
+}
+
 /*
  * We don't need a _copyExpr because Expr is an abstract supertype which
  * should never actually get instantiated. Also, since it has no common
@@ -1762,9 +1799,6 @@ _copyQuery(Query *from)
    COPY_NODE_FIELD(utilityStmt);
    COPY_SCALAR_FIELD(resultRelation);
    COPY_NODE_FIELD(into);
-   COPY_NODE_FIELD(intoOptions);
-   COPY_SCALAR_FIELD(intoOnCommit);
-   COPY_STRING_FIELD(intoTableSpaceName);
    COPY_SCALAR_FIELD(hasAggs);
    COPY_SCALAR_FIELD(hasSubLinks);
    COPY_NODE_FIELD(rtable);
@@ -1779,8 +1813,6 @@ _copyQuery(Query *from)
    COPY_NODE_FIELD(limitCount);
    COPY_NODE_FIELD(rowMarks);
    COPY_NODE_FIELD(setOperations);
-   COPY_NODE_FIELD(resultRelations);
-   COPY_NODE_FIELD(returningLists);
 
    return newnode;
 }
@@ -1832,10 +1864,6 @@ _copySelectStmt(SelectStmt *from)
 
    COPY_NODE_FIELD(distinctClause);
    COPY_NODE_FIELD(into);
-   COPY_NODE_FIELD(intoColNames);
-   COPY_NODE_FIELD(intoOptions);
-   COPY_SCALAR_FIELD(intoOnCommit);
-   COPY_STRING_FIELD(intoTableSpaceName);
    COPY_NODE_FIELD(targetList);
    COPY_NODE_FIELD(fromClause);
    COPY_NODE_FIELD(whereClause);
@@ -2768,9 +2796,6 @@ _copyExecuteStmt(ExecuteStmt *from)
 
    COPY_STRING_FIELD(name);
    COPY_NODE_FIELD(into);
-   COPY_NODE_FIELD(intoOptions);
-   COPY_SCALAR_FIELD(into_on_commit);
-   COPY_STRING_FIELD(into_tbl_space);
    COPY_NODE_FIELD(params);
 
    return newnode;
@@ -2902,6 +2927,9 @@ copyObject(void *from)
            /*
             * PLAN NODES
             */
+       case T_PlannedStmt:
+           retval = _copyPlannedStmt(from);
+           break;
        case T_Plan:
            retval = _copyPlan(from);
            break;
@@ -2990,6 +3018,9 @@ copyObject(void *from)
        case T_RangeVar:
            retval = _copyRangeVar(from);
            break;
+       case T_IntoClause:
+           retval = _copyIntoClause(from);
+           break;
        case T_Var:
            retval = _copyVar(from);
            break;
index c17a40bbbb246013f89a3c66be2771a1404667d4..dafa15f0287dcd849e36d1a8e3222a1de9acd5b6 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.298 2007/02/03 14:06:54 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.299 2007/02/20 17:32:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -102,6 +102,18 @@ _equalRangeVar(RangeVar *a, RangeVar *b)
    return true;
 }
 
+static bool
+_equalIntoClause(IntoClause *a, IntoClause *b)
+{
+   COMPARE_NODE_FIELD(rel);
+   COMPARE_NODE_FIELD(colNames);
+   COMPARE_NODE_FIELD(options);
+   COMPARE_SCALAR_FIELD(onCommit);
+   COMPARE_STRING_FIELD(tableSpaceName);
+
+   return true;
+}
+
 /*
  * We don't need an _equalExpr because Expr is an abstract supertype which
  * should never actually get instantiated. Also, since it has no common
@@ -690,9 +702,6 @@ _equalQuery(Query *a, Query *b)
    COMPARE_NODE_FIELD(utilityStmt);
    COMPARE_SCALAR_FIELD(resultRelation);
    COMPARE_NODE_FIELD(into);
-   COMPARE_NODE_FIELD(intoOptions);
-   COMPARE_SCALAR_FIELD(intoOnCommit);
-   COMPARE_STRING_FIELD(intoTableSpaceName);
    COMPARE_SCALAR_FIELD(hasAggs);
    COMPARE_SCALAR_FIELD(hasSubLinks);
    COMPARE_NODE_FIELD(rtable);
@@ -707,8 +716,6 @@ _equalQuery(Query *a, Query *b)
    COMPARE_NODE_FIELD(limitCount);
    COMPARE_NODE_FIELD(rowMarks);
    COMPARE_NODE_FIELD(setOperations);
-   COMPARE_NODE_FIELD(resultRelations);
-   COMPARE_NODE_FIELD(returningLists);
 
    return true;
 }
@@ -752,10 +759,6 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b)
 {
    COMPARE_NODE_FIELD(distinctClause);
    COMPARE_NODE_FIELD(into);
-   COMPARE_NODE_FIELD(intoColNames);
-   COMPARE_NODE_FIELD(intoOptions);
-   COMPARE_SCALAR_FIELD(intoOnCommit);
-   COMPARE_STRING_FIELD(intoTableSpaceName);
    COMPARE_NODE_FIELD(targetList);
    COMPARE_NODE_FIELD(fromClause);
    COMPARE_NODE_FIELD(whereClause);
@@ -1545,9 +1548,6 @@ _equalExecuteStmt(ExecuteStmt *a, ExecuteStmt *b)
 {
    COMPARE_STRING_FIELD(name);
    COMPARE_NODE_FIELD(into);
-   COMPARE_NODE_FIELD(intoOptions);
-   COMPARE_SCALAR_FIELD(into_on_commit);
-   COMPARE_STRING_FIELD(into_tbl_space);
    COMPARE_NODE_FIELD(params);
 
    return true;
@@ -1967,6 +1967,9 @@ equal(void *a, void *b)
        case T_RangeVar:
            retval = _equalRangeVar(a, b);
            break;
+       case T_IntoClause:
+           retval = _equalIntoClause(a, b);
+           break;
        case T_Var:
            retval = _equalVar(a, b);
            break;
index 0600f63b5567616427dab633f0f4c3d5793d5623..c8cc25abb1330ce9c8cc25d8dd90375bafbf0f6c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.299 2007/02/19 07:03:27 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.300 2007/02/20 17:32:15 tgl Exp $
  *
  * NOTES
  *   Every node type that can appear in stored rules' parsetrees *must*
@@ -234,6 +234,22 @@ _outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
  * Stuff from plannodes.h
  */
 
+static void
+_outPlannedStmt(StringInfo str, PlannedStmt *node)
+{
+   WRITE_NODE_TYPE("PLANNEDSTMT");
+
+   WRITE_ENUM_FIELD(commandType, CmdType);
+   WRITE_BOOL_FIELD(canSetTag);
+   WRITE_NODE_FIELD(planTree);
+   WRITE_NODE_FIELD(rtable);
+   WRITE_NODE_FIELD(resultRelations);
+   WRITE_NODE_FIELD(into);
+   WRITE_NODE_FIELD(returningLists);
+   WRITE_NODE_FIELD(rowMarks);
+   WRITE_INT_FIELD(nParamExec);
+}
+
 /*
  * print the basic stuff of all nodes that inherit from Plan
  */
@@ -251,7 +267,6 @@ _outPlanInfo(StringInfo str, Plan *node)
    WRITE_NODE_FIELD(initPlan);
    WRITE_BITMAPSET_FIELD(extParam);
    WRITE_BITMAPSET_FIELD(allParam);
-   WRITE_INT_FIELD(nParamExec);
 }
 
 /*
@@ -635,6 +650,18 @@ _outRangeVar(StringInfo str, RangeVar *node)
    WRITE_NODE_FIELD(alias);
 }
 
+static void
+_outIntoClause(StringInfo str, IntoClause *node)
+{
+   WRITE_NODE_TYPE("INTOCLAUSE");
+
+   WRITE_NODE_FIELD(rel);
+   WRITE_NODE_FIELD(colNames);
+   WRITE_NODE_FIELD(options);
+   WRITE_ENUM_FIELD(onCommit, OnCommitAction);
+   WRITE_STRING_FIELD(tableSpaceName);
+}
+
 static void
 _outVar(StringInfo str, Var *node)
 {
@@ -1245,6 +1272,8 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
    WRITE_NODE_FIELD(glob);
    WRITE_UINT_FIELD(query_level);
    WRITE_NODE_FIELD(join_rel_list);
+   WRITE_NODE_FIELD(resultRelations);
+   WRITE_NODE_FIELD(returningLists);
    WRITE_NODE_FIELD(init_plans);
    WRITE_NODE_FIELD(eq_classes);
    WRITE_NODE_FIELD(canon_pathkeys);
@@ -1502,10 +1531,6 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
 
    WRITE_NODE_FIELD(distinctClause);
    WRITE_NODE_FIELD(into);
-   WRITE_NODE_FIELD(intoColNames);
-   WRITE_NODE_FIELD(intoOptions);
-   WRITE_ENUM_FIELD(intoOnCommit, OnCommitAction);
-   WRITE_STRING_FIELD(intoTableSpaceName);
    WRITE_NODE_FIELD(targetList);
    WRITE_NODE_FIELD(fromClause);
    WRITE_NODE_FIELD(whereClause);
@@ -1651,9 +1676,6 @@ _outQuery(StringInfo str, Query *node)
 
    WRITE_INT_FIELD(resultRelation);
    WRITE_NODE_FIELD(into);
-   WRITE_NODE_FIELD(intoOptions);
-   WRITE_ENUM_FIELD(intoOnCommit, OnCommitAction);
-   WRITE_STRING_FIELD(intoTableSpaceName);
    WRITE_BOOL_FIELD(hasAggs);
    WRITE_BOOL_FIELD(hasSubLinks);
    WRITE_NODE_FIELD(rtable);
@@ -1668,8 +1690,6 @@ _outQuery(StringInfo str, Query *node)
    WRITE_NODE_FIELD(limitCount);
    WRITE_NODE_FIELD(rowMarks);
    WRITE_NODE_FIELD(setOperations);
-   WRITE_NODE_FIELD(resultRelations);
-   WRITE_NODE_FIELD(returningLists);
 }
 
 static void
@@ -1988,6 +2008,9 @@ _outNode(StringInfo str, void *obj)
        appendStringInfoChar(str, '{');
        switch (nodeTag(obj))
        {
+           case T_PlannedStmt:
+               _outPlannedStmt(str, obj);
+               break;
            case T_Plan:
                _outPlan(str, obj);
                break;
@@ -2072,6 +2095,9 @@ _outNode(StringInfo str, void *obj)
            case T_RangeVar:
                _outRangeVar(str, obj);
                break;
+           case T_IntoClause:
+               _outIntoClause(str, obj);
+               break;
            case T_Var:
                _outVar(str, obj);
                break;
index 17d36b4efe6db40a92b054b9bf9a6ad988755fe2..6478dce4b1c14e5a0b7a24379d6d07f08b4f97ac 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.202 2007/02/03 14:06:54 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.203 2007/02/20 17:32:15 tgl Exp $
  *
  * NOTES
  *   Path and Plan nodes do not have any readfuncs support, because we
@@ -140,9 +140,6 @@ _readQuery(void)
    READ_NODE_FIELD(utilityStmt);
    READ_INT_FIELD(resultRelation);
    READ_NODE_FIELD(into);
-   READ_NODE_FIELD(intoOptions);
-   READ_ENUM_FIELD(intoOnCommit, OnCommitAction);
-   READ_STRING_FIELD(intoTableSpaceName);
    READ_BOOL_FIELD(hasAggs);
    READ_BOOL_FIELD(hasSubLinks);
    READ_NODE_FIELD(rtable);
@@ -157,8 +154,6 @@ _readQuery(void)
    READ_NODE_FIELD(limitCount);
    READ_NODE_FIELD(rowMarks);
    READ_NODE_FIELD(setOperations);
-   READ_NODE_FIELD(resultRelations);
-   READ_NODE_FIELD(returningLists);
 
    READ_DONE();
 }
@@ -287,6 +282,20 @@ _readRangeVar(void)
    READ_DONE();
 }
 
+static IntoClause *
+_readIntoClause(void)
+{
+   READ_LOCALS(IntoClause);
+
+   READ_NODE_FIELD(rel);
+   READ_NODE_FIELD(colNames);
+   READ_NODE_FIELD(options);
+   READ_ENUM_FIELD(onCommit, OnCommitAction);
+   READ_STRING_FIELD(tableSpaceName);
+
+   READ_DONE();
+}
+
 /*
  * _readVar
  */
@@ -984,6 +993,8 @@ parseNodeString(void)
        return_value = _readAlias();
    else if (MATCH("RANGEVAR", 8))
        return_value = _readRangeVar();
+   else if (MATCH("INTOCLAUSE", 10))
+       return_value = _readIntoClause();
    else if (MATCH("VAR", 3))
        return_value = _readVar();
    else if (MATCH("CONST", 5))
index 1cb1d86be1b58374dc2c0f94a328e29e94f58dc9..6c6e80071c1ccfc12bbeed1a9d0c92373a750bb4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.159 2007/02/19 07:03:28 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.160 2007/02/20 17:32:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -444,8 +444,8 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
    Query      *subquery = rte->subquery;
    bool       *differentTypes;
    double      tuple_fraction;
+   PlannerInfo *subroot;
    List       *pathkeys;
-   List       *subquery_pathkeys;
 
    /* We need a workspace for keeping track of set-op type coercions */
    differentTypes = (bool *)
@@ -520,7 +520,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
    rel->subplan = subquery_planner(root->glob, subquery,
                                    root->query_level + 1,
                                    tuple_fraction,
-                                   &subquery_pathkeys);
+                                   &subroot);
 
    /* Copy number of output rows from subplan */
    rel->tuples = rel->subplan->plan_rows;
@@ -529,7 +529,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
    set_baserel_size_estimates(root, rel);
 
    /* Convert subquery pathkeys to outer representation */
-   pathkeys = convert_subquery_pathkeys(root, rel, subquery_pathkeys);
+   pathkeys = convert_subquery_pathkeys(root, rel, subroot->query_pathkeys);
 
    /* Generate appropriate path */
    add_path(rel, create_subqueryscan_path(rel, pathkeys));
index be26d17a28a2bca78bffb6a63e3a483e2e5677fe..406cc9dd49665845ef9d889565dbd1226c72b236 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.27 2007/02/19 07:03:29 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.28 2007/02/20 17:32:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -453,8 +453,7 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
    subroot.init_plans = NIL;
    subparse->commandType = CMD_SELECT;
    subparse->resultRelation = 0;
-   subparse->resultRelations = NIL;
-   subparse->returningLists = NIL;
+   subparse->returningList = NIL;
    subparse->into = NULL;
    subparse->hasAggs = false;
    subparse->groupClause = NIL;
index f4a940175ea5c89c08ed9c8bd2e703072fcd585a..3bb603a0f614bb53ff4cbb0eebefee770afbd819 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.213 2007/02/19 07:03:29 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.214 2007/02/20 17:32:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -79,13 +79,15 @@ static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
  *    Query optimizer entry point
  *
  *****************************************************************************/
-Plan *
+PlannedStmt *
 planner(Query *parse, bool isCursor, int cursorOptions,
        ParamListInfo boundParams)
 {
+   PlannedStmt *result;
    PlannerGlobal *glob;
    double      tuple_fraction;
-   Plan       *result_plan;
+   PlannerInfo *root;
+   Plan       *top_plan;
 
    /*
     * Set up global state for this planner invocation.  This data is needed
@@ -117,7 +119,7 @@ planner(Query *parse, bool isCursor, int cursorOptions,
    }
 
    /* primary planning entry point (may recurse for subqueries) */
-   result_plan = subquery_planner(glob, parse, 1, tuple_fraction, NULL);
+   top_plan = subquery_planner(glob, parse, 1, tuple_fraction, &root);
 
    /*
     * If creating a plan for a scrollable cursor, make sure it can run
@@ -125,17 +127,27 @@ planner(Query *parse, bool isCursor, int cursorOptions,
     */
    if (isCursor && (cursorOptions & CURSOR_OPT_SCROLL))
    {
-       if (!ExecSupportsBackwardScan(result_plan))
-           result_plan = materialize_finished_plan(result_plan);
+       if (!ExecSupportsBackwardScan(top_plan))
+           top_plan = materialize_finished_plan(top_plan);
    }
 
    /* final cleanup of the plan */
-   result_plan = set_plan_references(result_plan, parse->rtable);
-
-   /* executor wants to know total number of Params used overall */
-   result_plan->nParamExec = list_length(glob->paramlist);
-
-   return result_plan;
+   top_plan = set_plan_references(top_plan, parse->rtable);
+
+   /* build the PlannedStmt result */
+   result = makeNode(PlannedStmt);
+
+   result->commandType = parse->commandType;
+   result->canSetTag = parse->canSetTag;
+   result->planTree = top_plan;
+   result->rtable = parse->rtable;
+   result->resultRelations = root->resultRelations;
+   result->into = parse->into;
+   result->returningLists = root->returningLists;
+   result->rowMarks = parse->rowMarks;
+   result->nParamExec = list_length(glob->paramlist);
+
+   return result;
 }
 
 
@@ -150,8 +162,8 @@ planner(Query *parse, bool isCursor, int cursorOptions,
  * tuple_fraction is the fraction of tuples we expect will be retrieved.
  * tuple_fraction is interpreted as explained for grouping_planner, below.
  *
- * If subquery_pathkeys isn't NULL, it receives a list of pathkeys indicating
- * the output sort ordering of the completed plan.
+ * If subroot isn't NULL, we pass back the query's final PlannerInfo struct;
+ * among other things this tells the output sort ordering of the plan.
  *
  * Basically, this routine does the stuff that should only be done once
  * per Query object.  It then calls grouping_planner.  At one time,
@@ -168,7 +180,7 @@ planner(Query *parse, bool isCursor, int cursorOptions,
 Plan *
 subquery_planner(PlannerGlobal *glob, Query *parse,
                 Index level, double tuple_fraction,
-                List **subquery_pathkeys)
+                PlannerInfo **subroot)
 {
    int         saved_plan_id = glob->next_plan_id;
    PlannerInfo *root;
@@ -375,9 +387,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
    if (root->glob->next_plan_id != saved_plan_id || root->query_level > 1)
        SS_finalize_plan(root, plan);
 
-   /* Return sort ordering info if caller wants it */
-   if (subquery_pathkeys)
-       *subquery_pathkeys = root->query_pathkeys;
+   /* Return internal info if caller wants it */
+   if (subroot)
+       *subroot = root;
 
    return plan;
 }
@@ -593,14 +605,14 @@ inheritance_planner(PlannerInfo *root)
        /* Build list of per-relation RETURNING targetlists */
        if (parse->returningList)
        {
-           Assert(list_length(subroot.parse->returningLists) == 1);
+           Assert(list_length(subroot.returningLists) == 1);
            returningLists = list_concat(returningLists,
-                                        subroot.parse->returningLists);
+                                        subroot.returningLists);
        }
    }
 
-   parse->resultRelations = resultRelations;
-   parse->returningLists = returningLists;
+   root->resultRelations = resultRelations;
+   root->returningLists = returningLists;
 
    /* Mark result as unordered (probably unnecessary) */
    root->query_pathkeys = NIL;
@@ -1101,8 +1113,16 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
        rlist = set_returning_clause_references(parse->returningList,
                                                result_plan,
                                                parse->resultRelation);
-       parse->returningLists = list_make1(rlist);
+       root->returningLists = list_make1(rlist);
    }
+   else
+       root->returningLists = NIL;
+
+   /* Compute result-relations list if needed */
+   if (parse->resultRelation)
+       root->resultRelations = list_make1_int(parse->resultRelation);
+   else
+       root->resultRelations = NIL;
 
    /*
     * Return the actual output ordering in query_pathkeys for possible use by
index 0eb81d6d8982d0dbc54e283bfd9efa2d5302e07d..63a5999a401d29cca1130518e198820d450c6f2d 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.360 2007/02/01 19:10:27 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.361 2007/02/20 17:32:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2143,11 +2143,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
    if (stmt->into)
    {
        qry->into = stmt->into;
-       if (stmt->intoColNames)
-           applyColumnNames(qry->targetList, stmt->intoColNames);
-       qry->intoOptions = copyObject(stmt->intoOptions);
-       qry->intoOnCommit = stmt->intoOnCommit;
-       qry->intoTableSpaceName = stmt->intoTableSpaceName;
+       if (stmt->into->colNames)
+           applyColumnNames(qry->targetList, stmt->into->colNames);
    }
 
    qry->rtable = pstate->p_rtable;
@@ -2315,11 +2312,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
    if (stmt->into)
    {
        qry->into = stmt->into;
-       if (stmt->intoColNames)
-           applyColumnNames(qry->targetList, stmt->intoColNames);
-       qry->intoOptions = copyObject(stmt->intoOptions);
-       qry->intoOnCommit = stmt->intoOnCommit;
-       qry->intoTableSpaceName = stmt->intoTableSpaceName;
+       if (stmt->into->colNames)
+           applyColumnNames(qry->targetList, stmt->into->colNames);
    }
 
    /*
@@ -2399,9 +2393,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 
    /*
     * Find leftmost leaf SelectStmt; extract the one-time-only items from it
-    * and from the top-level node.  (Most of the INTO options can be
-    * transferred to the Query immediately, but intoColNames has to be saved
-    * to apply below.)
+    * and from the top-level node.
     */
    leftmostSelect = stmt->larg;
    while (leftmostSelect && leftmostSelect->op != SETOP_NONE)
@@ -2411,10 +2403,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
    if (leftmostSelect->into)
    {
        qry->into = leftmostSelect->into;
-       intoColNames = leftmostSelect->intoColNames;
-       qry->intoOptions = copyObject(leftmostSelect->intoOptions);
-       qry->intoOnCommit = leftmostSelect->intoOnCommit;
-       qry->intoTableSpaceName = leftmostSelect->intoTableSpaceName;
+       intoColNames = leftmostSelect->into->colNames;
    }
 
    /* clear this to prevent complaints in transformSetOperationTree() */
index cf25c5607a64e439e3d9c427f0318fb324856676..3204d0a401ae2039fc807fc1fa33c5a6b9bce327 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.579 2007/02/03 14:06:54 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.580 2007/02/20 17:32:16 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -139,6 +139,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
    IndexElem           *ielem;
    Alias               *alias;
    RangeVar            *range;
+   IntoClause          *into;
    A_Indices           *aind;
    ResTarget           *target;
    PrivTarget          *privtarget;
@@ -248,7 +249,8 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
                prep_type_clause
                execute_param_clause using_clause returning_clause
 
-%type <range>  into_clause OptTempTableName
+%type <range>  OptTempTableName
+%type <into>   into_clause create_as_target
 
 %type <defelt> createfunc_opt_item common_func_opt_item
 %type <fun_param> func_arg
@@ -2253,8 +2255,7 @@ OptConsTableSpace:   USING INDEX TABLESPACE name  { $$ = $4; }
  */
 
 CreateAsStmt:
-       CREATE OptTemp TABLE qualified_name OptCreateAs
-           OptWith OnCommitOption OptTableSpace AS SelectStmt
+       CREATE OptTemp TABLE create_as_target AS SelectStmt
                {
                    /*
                     * When the SelectStmt is a set-operation tree, we must
@@ -2263,18 +2264,26 @@ CreateAsStmt:
                     * to find it.  Similarly, the output column names must
                     * be attached to that Select's target list.
                     */
-                   SelectStmt *n = findLeftmostSelect((SelectStmt *) $10);
+                   SelectStmt *n = findLeftmostSelect((SelectStmt *) $6);
                    if (n->into != NULL)
                        ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
                                 errmsg("CREATE TABLE AS cannot specify INTO")));
-                   $4->istemp = $2;
+                   $4->rel->istemp = $2;
                    n->into = $4;
-                   n->intoColNames = $5;
-                   n->intoOptions = $6;
-                   n->intoOnCommit = $7;
-                   n->intoTableSpaceName = $8;
-                   $$ = $10;
+                   $$ = $6;
+               }
+       ;
+
+create_as_target:
+           qualified_name OptCreateAs OptWith OnCommitOption OptTableSpace
+               {
+                   $$ = makeNode(IntoClause);
+                   $$->rel = $1;
+                   $$->colNames = $2;
+                   $$->options = $3;
+                   $$->onCommit = $4;
+                   $$->tableSpaceName = $5;
                }
        ;
 
@@ -5459,19 +5468,15 @@ ExecuteStmt: EXECUTE name execute_param_clause
                    n->into = NULL;
                    $$ = (Node *) n;
                }
-           | CREATE OptTemp TABLE qualified_name OptCreateAs
-               OptWith OnCommitOption OptTableSpace AS
+           | CREATE OptTemp TABLE create_as_target AS
                EXECUTE name execute_param_clause
                {
                    ExecuteStmt *n = makeNode(ExecuteStmt);
-                   n->name = $11;
-                   n->params = $12;
-                   $4->istemp = $2;
+                   n->name = $7;
+                   n->params = $8;
+                   $4->rel->istemp = $2;
                    n->into = $4;
-                   n->intoOptions = $6;
-                   n->into_on_commit = $7;
-                   n->into_tbl_space = $8;
-                   if ($5)
+                   if ($4->colNames)
                        ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("column name list not allowed in CREATE TABLE / AS EXECUTE")));
@@ -5854,7 +5859,6 @@ simple_select:
                    n->distinctClause = $2;
                    n->targetList = $3;
                    n->into = $4;
-                   n->intoColNames = NIL;
                    n->fromClause = $5;
                    n->whereClause = $6;
                    n->groupClause = $7;
@@ -5877,8 +5881,17 @@ simple_select:
        ;
 
 into_clause:
-           INTO OptTempTableName                   { $$ = $2; }
-           | /*EMPTY*/                             { $$ = NULL; }
+           INTO OptTempTableName
+               {
+                   $$ = makeNode(IntoClause);
+                   $$->rel = $2;
+                   $$->colNames = NIL;
+                   $$->options = NIL;
+                   $$->onCommit = ONCOMMIT_NOOP;
+                   $$->tableSpaceName = NULL;
+               }
+           | /*EMPTY*/
+               { $$ = NULL; }
        ;
 
 /*
index f00897ee6222e0a4c2353638acbd9ccc5d614bbb..1c40a8752e953e34e2f23924b679676ebf4bbacf 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.524 2007/02/17 19:33:32 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.525 2007/02/20 17:32:16 tgl Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -165,8 +165,7 @@ static int  InteractiveBackend(StringInfo inBuf);
 static int SocketBackend(StringInfo inBuf);
 static int ReadCommand(StringInfo inBuf);
 static List *pg_rewrite_queries(List *querytree_list);
-static bool check_log_statement_raw(List *raw_parsetree_list);
-static bool check_log_statement_cooked(List *parsetree_list);
+static bool check_log_statement(List *stmt_list);
 static int errdetail_execute(List *raw_parsetree_list);
 static int errdetail_params(ParamListInfo params);
 static void start_xact_command(void);
@@ -659,10 +658,10 @@ pg_rewrite_queries(List *querytree_list)
 
 
 /* Generate a plan for a single already-rewritten query. */
-Plan *
+PlannedStmt *
 pg_plan_query(Query *querytree, ParamListInfo boundParams)
 {
-   Plan       *plan;
+   PlannedStmt *plan;
 
    /* Utility commands have no plans. */
    if (querytree->commandType == CMD_UTILITY)
@@ -680,7 +679,7 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
 #ifdef COPY_PARSE_PLAN_TREES
    /* Optional debugging check: pass plan output through copyObject() */
    {
-       Plan       *new_plan = (Plan *) copyObject(plan);
+       PlannedStmt *new_plan = (PlannedStmt *) copyObject(plan);
 
        /*
         * equal() currently does not have routines to compare Plan nodes, so
@@ -715,23 +714,26 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
  * utility statements depend on not having frozen the snapshot yet.
  * (We assume that such statements cannot appear together with plannable
  * statements in the rewriter's output.)
+ *
+ * Normal optimizable statements generate PlannedStmt entries in the result
+ * list.  Utility statements are simply represented by their statement nodes.
  */
 List *
 pg_plan_queries(List *querytrees, ParamListInfo boundParams,
                bool needSnapshot)
 {
-   List       *plan_list = NIL;
+   List       *stmt_list = NIL;
    ListCell   *query_list;
 
    foreach(query_list, querytrees)
    {
        Query      *query = (Query *) lfirst(query_list);
-       Plan       *plan;
+       Node       *stmt;
 
        if (query->commandType == CMD_UTILITY)
        {
            /* Utility commands have no plans. */
-           plan = NULL;
+           stmt = query->utilityStmt;
        }
        else
        {
@@ -740,13 +742,13 @@ pg_plan_queries(List *querytrees, ParamListInfo boundParams,
                ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
                needSnapshot = false;
            }
-           plan = pg_plan_query(query, boundParams);
+           stmt = (Node *) pg_plan_query(query, boundParams);
        }
 
-       plan_list = lappend(plan_list, plan);
+       stmt_list = lappend(stmt_list, stmt);
    }
 
-   return plan_list;
+   return stmt_list;
 }
 
 
@@ -817,7 +819,7 @@ exec_simple_query(const char *query_string)
    parsetree_list = pg_parse_query(query_string);
 
    /* Log immediately if dictated by log_statement */
-   if (check_log_statement_raw(parsetree_list))
+   if (check_log_statement(parsetree_list))
    {
        ereport(LOG,
                (errmsg("statement: %s", query_string),
@@ -905,7 +907,6 @@ exec_simple_query(const char *query_string)
                          NULL,
                          query_string,
                          commandTag,
-                         querytree_list,
                          plantree_list,
                          MessageContext);
 
@@ -1050,9 +1051,10 @@ exec_parse_message(const char *query_string, /* string to execute */
    List       *parsetree_list;
    const char *commandTag;
    List       *querytree_list,
-              *plantree_list,
+              *stmt_list,
               *param_list;
    bool        is_named;
+   bool        fully_planned;
    bool        save_log_statement_stats = log_statement_stats;
    char        msec_str[32];
 
@@ -1202,17 +1204,23 @@ exec_parse_message(const char *query_string,    /* string to execute */
         * planning until Bind.  Otherwise do it now.
         */
        if (!is_named && numParams > 0)
-           plantree_list = NIL;
+       {
+           stmt_list = querytree_list;
+           fully_planned = false;
+       }
        else
-           plantree_list = pg_plan_queries(querytree_list, NULL, true);
+       {
+           stmt_list = pg_plan_queries(querytree_list, NULL, true);
+           fully_planned = true;
+       }
    }
    else
    {
        /* Empty input string.  This is legal. */
        commandTag = NULL;
-       querytree_list = NIL;
-       plantree_list = NIL;
+       stmt_list = NIL;
        param_list = NIL;
+       fully_planned = true;
    }
 
    /* If we got a cancel signal in analysis or planning, quit */
@@ -1226,9 +1234,9 @@ exec_parse_message(const char *query_string,  /* string to execute */
        StorePreparedStatement(stmt_name,
                               query_string,
                               commandTag,
-                              querytree_list,
-                              plantree_list,
+                              stmt_list,
                               param_list,
+                              fully_planned,
                               false);
    }
    else
@@ -1240,9 +1248,9 @@ exec_parse_message(const char *query_string,  /* string to execute */
        pstmt->query_string = pstrdup(query_string);
        /* the rest is there already */
        pstmt->commandTag = commandTag;
-       pstmt->query_list = querytree_list;
-       pstmt->plan_list = plantree_list;
+       pstmt->stmt_list = stmt_list;
        pstmt->argtype_list = param_list;
+       pstmt->fully_planned = fully_planned;
        pstmt->from_sql = false;
        pstmt->context = unnamed_stmt_context;
        /* Now the unnamed statement is complete and valid */
@@ -1393,7 +1401,7 @@ exec_bind_message(StringInfo input_message)
     * functions.
     */
    if (IsAbortedTransactionBlockState() &&
-       (!IsTransactionExitStmtList(pstmt->query_list) ||
+       (!IsTransactionExitStmtList(pstmt->stmt_list) ||
         numParams != 0))
        ereport(ERROR,
                (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
@@ -1581,22 +1589,21 @@ exec_bind_message(StringInfo input_message)
     * portal's queryContext becomes its own heap context rather than the
     * prepared statement's context.  FIXME someday
     */
-   if (pstmt->plan_list == NIL && pstmt->query_list != NIL)
+   if (pstmt->fully_planned)
+   {
+       plan_list = pstmt->stmt_list;
+       qContext = pstmt->context;
+   }
+   else
    {
        MemoryContext oldContext;
 
        qContext = PortalGetHeapMemory(portal);
        oldContext = MemoryContextSwitchTo(qContext);
-       query_list = copyObject(pstmt->query_list);
+       query_list = copyObject(pstmt->stmt_list);
        plan_list = pg_plan_queries(query_list, params, true);
        MemoryContextSwitchTo(oldContext);
    }
-   else
-   {
-       query_list = pstmt->query_list;
-       plan_list = pstmt->plan_list;
-       qContext = pstmt->context;
-   }
 
    /*
     * Define portal and start execution.
@@ -1605,7 +1612,6 @@ exec_bind_message(StringInfo input_message)
                      *pstmt->stmt_name ? pstmt->stmt_name : NULL,
                      pstmt->query_string,
                      pstmt->commandTag,
-                     query_list,
                      plan_list,
                      qContext);
 
@@ -1688,13 +1694,13 @@ exec_execute_message(const char *portal_name, long max_rows)
     */
    if (portal->commandTag == NULL)
    {
-       Assert(portal->parseTrees == NIL);
+       Assert(portal->stmts == NIL);
        NullCommand(dest);
        return;
    }
 
    /* Does the portal contain a transaction command? */
-   is_xact_command = IsTransactionStmtList(portal->parseTrees);
+   is_xact_command = IsTransactionStmtList(portal->stmts);
 
    /*
     * We must copy the sourceText and prepStmtName into MessageContext in
@@ -1760,7 +1766,7 @@ exec_execute_message(const char *portal_name, long max_rows)
    execute_is_fetch = !portal->atStart;
 
    /* Log immediately if dictated by log_statement */
-   if (check_log_statement_cooked(portal->parseTrees))
+   if (check_log_statement(portal->stmts))
    {
        ereport(LOG,
                (errmsg("%s %s%s%s%s%s",
@@ -1781,7 +1787,7 @@ exec_execute_message(const char *portal_name, long max_rows)
     * actually run are those containing COMMIT or ROLLBACK commands.
     */
    if (IsAbortedTransactionBlockState() &&
-       !IsTransactionExitStmtList(portal->parseTrees))
+       !IsTransactionExitStmtList(portal->stmts))
        ereport(ERROR,
                (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
                 errmsg("current transaction is aborted, "
@@ -1865,15 +1871,16 @@ exec_execute_message(const char *portal_name, long max_rows)
 }
 
 /*
- * check_log_statement_raw
+ * check_log_statement
  *     Determine whether command should be logged because of log_statement
  *
- * raw_parsetree_list is the raw grammar output
+ * parsetree_list can be either raw grammar output or a list of planned
+ * statements
  */
 static bool
-check_log_statement_raw(List *raw_parsetree_list)
+check_log_statement(List *stmt_list)
 {
-   ListCell   *parsetree_item;
+   ListCell   *stmt_item;
 
    if (log_statement == LOGSTMT_NONE)
        return false;
@@ -1881,37 +1888,11 @@ check_log_statement_raw(List *raw_parsetree_list)
        return true;
 
    /* Else we have to inspect the statement(s) to see whether to log */
-   foreach(parsetree_item, raw_parsetree_list)
+   foreach(stmt_item, stmt_list)
    {
-       Node       *parsetree = (Node *) lfirst(parsetree_item);
+       Node       *stmt = (Node *) lfirst(stmt_item);
 
-       if (GetCommandLogLevel(parsetree) <= log_statement)
-           return true;
-   }
-
-   return false;
-}
-
-/*
- * check_log_statement_cooked
- *     As above, but work from already-analyzed querytrees
- */
-static bool
-check_log_statement_cooked(List *parsetree_list)
-{
-   ListCell   *parsetree_item;
-
-   if (log_statement == LOGSTMT_NONE)
-       return false;
-   if (log_statement == LOGSTMT_ALL)
-       return true;
-
-   /* Else we have to inspect the statement(s) to see whether to log */
-   foreach(parsetree_item, parsetree_list)
-   {
-       Query      *parsetree = (Query *) lfirst(parsetree_item);
-
-       if (GetQueryLogLevel(parsetree) <= log_statement)
+       if (GetCommandLogLevel(stmt) <= log_statement)
            return true;
    }
 
@@ -2259,6 +2240,7 @@ finish_xact_command(void)
  * ones that we allow in transaction-aborted state.
  */
 
+/* Test a bare parsetree */
 static bool
 IsTransactionExitStmt(Node *parsetree)
 {
@@ -2275,29 +2257,45 @@ IsTransactionExitStmt(Node *parsetree)
    return false;
 }
 
+/* Test a list that might contain Query nodes or bare parsetrees */
 static bool
 IsTransactionExitStmtList(List *parseTrees)
 {
    if (list_length(parseTrees) == 1)
    {
-       Query      *query = (Query *) linitial(parseTrees);
+       Node       *stmt = (Node *) linitial(parseTrees);
+
+       if (IsA(stmt, Query))
+       {
+           Query      *query = (Query *) stmt;
 
-       if (query->commandType == CMD_UTILITY &&
-           IsTransactionExitStmt(query->utilityStmt))
+           if (query->commandType == CMD_UTILITY &&
+               IsTransactionExitStmt(query->utilityStmt))
+               return true;
+       }
+       else if (IsTransactionExitStmt(stmt))
            return true;
    }
    return false;
 }
 
+/* Test a list that might contain Query nodes or bare parsetrees */
 static bool
 IsTransactionStmtList(List *parseTrees)
 {
    if (list_length(parseTrees) == 1)
    {
-       Query      *query = (Query *) linitial(parseTrees);
+       Node       *stmt = (Node *) linitial(parseTrees);
 
-       if (query->commandType == CMD_UTILITY &&
-           query->utilityStmt && IsA(query->utilityStmt, TransactionStmt))
+       if (IsA(stmt, Query))
+       {
+           Query      *query = (Query *) stmt;
+
+           if (query->commandType == CMD_UTILITY &&
+               IsA(query->utilityStmt, TransactionStmt))
+               return true;
+       }
+       else if (IsA(stmt, TransactionStmt))
            return true;
    }
    return false;
index f6f157e0cbbcdb30b42861e4e67c132767ac919e..97a003ac89da9246ea909589f3488cae748fdc61 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.113 2007/02/18 19:49:25 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.114 2007/02/20 17:32:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,8 +32,7 @@
 Portal     ActivePortal = NULL;
 
 
-static void ProcessQuery(Query *parsetree,
-            Plan *plan,
+static void ProcessQuery(PlannedStmt *plan,
             ParamListInfo params,
             DestReceiver *dest,
             char *completionTag);
@@ -42,7 +41,7 @@ static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
             DestReceiver *dest);
 static long PortalRunSelect(Portal portal, bool forward, long count,
                DestReceiver *dest);
-static void PortalRunUtility(Portal portal, Query *query,
+static void PortalRunUtility(Portal portal, Node *utilityStmt,
                 DestReceiver *dest, char *completionTag);
 static void PortalRunMulti(Portal portal,
               DestReceiver *dest, DestReceiver *altdest,
@@ -58,8 +57,7 @@ static void DoPortalRewind(Portal portal);
  * CreateQueryDesc
  */
 QueryDesc *
-CreateQueryDesc(Query *parsetree,
-               Plan *plantree,
+CreateQueryDesc(PlannedStmt *plannedstmt,
                Snapshot snapshot,
                Snapshot crosscheck_snapshot,
                DestReceiver *dest,
@@ -68,10 +66,10 @@ CreateQueryDesc(Query *parsetree,
 {
    QueryDesc  *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
 
-   qd->operation = parsetree->commandType;     /* operation */
-   qd->parsetree = parsetree;  /* parse tree */
-   qd->plantree = plantree;    /* plan */
-   qd->snapshot = snapshot;    /* snapshot */
+   qd->operation = plannedstmt->commandType;   /* operation */
+   qd->plannedstmt = plannedstmt;              /* plan */
+   qd->utilitystmt = NULL;
+   qd->snapshot = snapshot;                    /* snapshot */
    qd->crosscheck_snapshot = crosscheck_snapshot;      /* RI check snapshot */
    qd->dest = dest;            /* output dest */
    qd->params = params;        /* parameter values passed into query */
@@ -85,6 +83,34 @@ CreateQueryDesc(Query *parsetree,
    return qd;
 }
 
+/*
+ * CreateUtilityQueryDesc
+ */
+QueryDesc *
+CreateUtilityQueryDesc(Node *utilitystmt,
+                      Snapshot snapshot,
+                      DestReceiver *dest,
+                      ParamListInfo params)
+{
+   QueryDesc  *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
+
+   qd->operation = CMD_UTILITY;                /* operation */
+   qd->plannedstmt = NULL;
+   qd->utilitystmt = utilitystmt;              /* utility command */
+   qd->snapshot = snapshot;                    /* snapshot */
+   qd->crosscheck_snapshot = InvalidSnapshot;  /* RI check snapshot */
+   qd->dest = dest;            /* output dest */
+   qd->params = params;        /* parameter values passed into query */
+   qd->doInstrument = false;   /* uninteresting for utilities */
+
+   /* null these fields until set by ExecutorStart */
+   qd->tupDesc = NULL;
+   qd->estate = NULL;
+   qd->planstate = NULL;
+
+   return qd;
+}
+
 /*
  * FreeQueryDesc
  */
@@ -103,7 +129,6 @@ FreeQueryDesc(QueryDesc *qdesc)
  *     Execute a single plannable query within a PORTAL_MULTI_QUERY
  *     or PORTAL_ONE_RETURNING portal
  *
- * parsetree: the query tree
  * plan: the plan tree for the query
  * params: any parameters needed
  * dest: where to send results
@@ -116,13 +141,11 @@ FreeQueryDesc(QueryDesc *qdesc)
  * error; otherwise the executor's memory usage will be leaked.
  */
 static void
-ProcessQuery(Query *parsetree,
-            Plan *plan,
+ProcessQuery(PlannedStmt *plan,
             ParamListInfo params,
             DestReceiver *dest,
             char *completionTag)
 {
-   int         operation = parsetree->commandType;
    QueryDesc  *queryDesc;
 
    ereport(DEBUG3,
@@ -137,7 +160,7 @@ ProcessQuery(Query *parsetree,
    /*
     * Create the QueryDesc object
     */
-   queryDesc = CreateQueryDesc(parsetree, plan,
+   queryDesc = CreateQueryDesc(plan,
                                ActiveSnapshot, InvalidSnapshot,
                                dest, params, false);
 
@@ -163,7 +186,7 @@ ProcessQuery(Query *parsetree,
    {
        Oid         lastOid;
 
-       switch (operation)
+       switch (queryDesc->operation)
        {
            case CMD_SELECT:
                strcpy(completionTag, "SELECT");
@@ -206,40 +229,67 @@ ProcessQuery(Query *parsetree,
 
 /*
  * ChoosePortalStrategy
- *     Select portal execution strategy given the intended query list.
+ *     Select portal execution strategy given the intended statement list.
+ *
+ * The list elements can be Querys, PlannedStmts, or utility statements.
+ * That's more general than portals need, but we use this for prepared
+ * statements as well.
  *
  * See the comments in portal.h.
  */
 PortalStrategy
-ChoosePortalStrategy(List *parseTrees)
+ChoosePortalStrategy(List *stmts)
 {
    int         nSetTag;
    ListCell   *lc;
 
    /*
     * PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the
-    * single-Query-struct case, since there are no rewrite rules that can add
+    * single-statement case, since there are no rewrite rules that can add
     * auxiliary queries to a SELECT or a utility command.
     */
-   if (list_length(parseTrees) == 1)
+   if (list_length(stmts) == 1)
    {
-       Query      *query = (Query *) linitial(parseTrees);
+       Node       *stmt = (Node *) linitial(stmts);
 
-       Assert(IsA(query, Query));
-       if (query->canSetTag)
+       if (IsA(stmt, Query))
        {
-           if (query->commandType == CMD_SELECT &&
-               query->into == NULL)
-               return PORTAL_ONE_SELECT;
-           if (query->commandType == CMD_UTILITY &&
-               query->utilityStmt != NULL)
+           Query      *query = (Query *) stmt;
+
+           if (query->canSetTag)
            {
-               if (UtilityReturnsTuples(query->utilityStmt))
-                   return PORTAL_UTIL_SELECT;
-               /* it can't be ONE_RETURNING, so give up */
-               return PORTAL_MULTI_QUERY;
+               if (query->commandType == CMD_SELECT &&
+                   query->into == NULL)
+                   return PORTAL_ONE_SELECT;
+               if (query->commandType == CMD_UTILITY &&
+                   query->utilityStmt != NULL)
+               {
+                   if (UtilityReturnsTuples(query->utilityStmt))
+                       return PORTAL_UTIL_SELECT;
+                   /* it can't be ONE_RETURNING, so give up */
+                   return PORTAL_MULTI_QUERY;
+               }
            }
        }
+       else if (IsA(stmt, PlannedStmt))
+       {
+           PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
+           if (pstmt->canSetTag)
+           {
+               if (pstmt->commandType == CMD_SELECT &&
+                   pstmt->into == NULL)
+                   return PORTAL_ONE_SELECT;
+           }
+       }
+       else
+       {
+           /* must be a utility command; assume it's canSetTag */
+           if (UtilityReturnsTuples(stmt))
+               return PORTAL_UTIL_SELECT;
+           /* it can't be ONE_RETURNING, so give up */
+           return PORTAL_MULTI_QUERY;
+       }
    }
 
    /*
@@ -248,18 +298,35 @@ ChoosePortalStrategy(List *parseTrees)
     * it has a RETURNING list.
     */
    nSetTag = 0;
-   foreach(lc, parseTrees)
+   foreach(lc, stmts)
    {
-       Query      *query = (Query *) lfirst(lc);
+       Node       *stmt = (Node *) lfirst(lc);
 
-       Assert(IsA(query, Query));
-       if (query->canSetTag)
+       if (IsA(stmt, Query))
        {
-           if (++nSetTag > 1)
-               return PORTAL_MULTI_QUERY;      /* no need to look further */
-           if (query->returningList == NIL)
-               return PORTAL_MULTI_QUERY;      /* no need to look further */
+           Query      *query = (Query *) stmt;
+
+           if (query->canSetTag)
+           {
+               if (++nSetTag > 1)
+                   return PORTAL_MULTI_QUERY;  /* no need to look further */
+               if (query->returningList == NIL)
+                   return PORTAL_MULTI_QUERY;  /* no need to look further */
+           }
        }
+       else if (IsA(stmt, PlannedStmt))
+       {
+           PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
+           if (pstmt->canSetTag)
+           {
+               if (++nSetTag > 1)
+                   return PORTAL_MULTI_QUERY;  /* no need to look further */
+               if (pstmt->returningLists == NIL)
+                   return PORTAL_MULTI_QUERY;  /* no need to look further */
+           }
+       }
+       /* otherwise, utility command, assumed not canSetTag */
    }
    if (nSetTag == 1)
        return PORTAL_ONE_RETURNING;
@@ -274,48 +341,84 @@ ChoosePortalStrategy(List *parseTrees)
  *     Returns NIL if the portal doesn't have a determinable targetlist.
  *
  * Note: do not modify the result.
- *
- * XXX be careful to keep this in sync with FetchPreparedStatementTargetList,
- * and with UtilityReturnsTuples.
  */
 List *
 FetchPortalTargetList(Portal portal)
 {
-   if (portal->strategy == PORTAL_ONE_SELECT)
-       return ((Query *) linitial(portal->parseTrees))->targetList;
-   if (portal->strategy == PORTAL_ONE_RETURNING)
-       return (PortalGetPrimaryQuery(portal))->returningList;
-   if (portal->strategy == PORTAL_UTIL_SELECT)
+   /* no point in looking if we determined it doesn't return tuples */
+   if (portal->strategy == PORTAL_MULTI_QUERY)
+       return NIL;
+   /* get the primary statement and find out what it returns */
+   return FetchStatementTargetList(PortalGetPrimaryStmt(portal));
+}
+
+/*
+ * FetchStatementTargetList
+ *     Given a statement that returns tuples, extract the query targetlist.
+ *     Returns NIL if the statement doesn't have a determinable targetlist.
+ *
+ * This can be applied to a Query, a PlannedStmt, or a utility statement.
+ * That's more general than portals need, but we use this for prepared
+ * statements as well.
+ *
+ * Note: do not modify the result.
+ *
+ * XXX be careful to keep this in sync with UtilityReturnsTuples.
+ */
+List *
+FetchStatementTargetList(Node *stmt)
+{
+   if (stmt == NULL)
+       return NIL;
+   if (IsA(stmt, Query))
    {
-       Node       *utilityStmt;
+       Query      *query = (Query *) stmt;
 
-       utilityStmt = ((Query *) linitial(portal->parseTrees))->utilityStmt;
-       switch (nodeTag(utilityStmt))
+       if (query->commandType == CMD_UTILITY &&
+           query->utilityStmt != NULL)
        {
-           case T_FetchStmt:
-               {
-                   FetchStmt  *substmt = (FetchStmt *) utilityStmt;
-                   Portal      subportal;
-
-                   Assert(!substmt->ismove);
-                   subportal = GetPortalByName(substmt->portalname);
-                   Assert(PortalIsValid(subportal));
-                   return FetchPortalTargetList(subportal);
-               }
-
-           case T_ExecuteStmt:
-               {
-                   ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
-                   PreparedStatement *entry;
+           /* transfer attention to utility statement */
+           stmt = query->utilityStmt;
+       }
+       else
+       {
+           if (query->commandType == CMD_SELECT &&
+               query->into == NULL)
+               return query->targetList;
+           if (query->returningList)
+               return query->returningList;
+           return NIL;
+       }
+   }
+   if (IsA(stmt, PlannedStmt))
+   {
+       PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
+       if (pstmt->commandType == CMD_SELECT &&
+           pstmt->into == NULL)
+           return pstmt->planTree->targetlist;
+       if (pstmt->returningLists)
+           return (List *) linitial(pstmt->returningLists);
+       return NIL;
+   }
+   if (IsA(stmt, FetchStmt))
+   {
+       FetchStmt  *fstmt = (FetchStmt *) stmt;
+       Portal      subportal;
 
-                   Assert(!substmt->into);
-                   entry = FetchPreparedStatement(substmt->name, true);
-                   return FetchPreparedStatementTargetList(entry);
-               }
+       Assert(!fstmt->ismove);
+       subportal = GetPortalByName(fstmt->portalname);
+       Assert(PortalIsValid(subportal));
+       return FetchPortalTargetList(subportal);
+   }
+   if (IsA(stmt, ExecuteStmt))
+   {
+       ExecuteStmt *estmt = (ExecuteStmt *) stmt;
+       PreparedStatement *entry;
 
-           default:
-               break;
-       }
+       Assert(!estmt->into);
+       entry = FetchPreparedStatement(estmt->name, true);
+       return FetchPreparedStatementTargetList(entry);
    }
    return NIL;
 }
@@ -374,7 +477,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
        /*
         * Determine the portal execution strategy
         */
-       portal->strategy = ChoosePortalStrategy(portal->parseTrees);
+       portal->strategy = ChoosePortalStrategy(portal->stmts);
 
        /*
         * Fire her up according to the strategy
@@ -396,8 +499,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
                 * Create QueryDesc in portal's context; for the moment, set
                 * the destination to DestNone.
                 */
-               queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees),
-                                       (Plan *) linitial(portal->planTrees),
+               queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts),
                                            ActiveSnapshot,
                                            InvalidSnapshot,
                                            None_Receiver,
@@ -450,8 +552,16 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
                 * We don't start the executor until we are told to run the
                 * portal.  We do need to set up the result tupdesc.
                 */
-               portal->tupDesc =
-                   ExecCleanTypeFromTL((PortalGetPrimaryQuery(portal))->returningList, false);
+               {
+                   PlannedStmt *pstmt;
+
+                   pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal);
+                   Assert(IsA(pstmt, PlannedStmt));
+                   Assert(pstmt->returningLists);
+                   portal->tupDesc =
+                       ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists),
+                                           false);
+               }
 
                /*
                 * Reset cursor position data to "start of query"
@@ -468,8 +578,12 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
                 * We don't set snapshot here, because PortalRunUtility will
                 * take care of it if needed.
                 */
-               portal->tupDesc =
-                   UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt);
+               {
+                   Node *ustmt = PortalGetPrimaryStmt(portal);
+
+                   Assert(!IsA(ustmt, PlannedStmt));
+                   portal->tupDesc = UtilityTupleDescriptor(ustmt);
+               }
 
                /*
                 * Reset cursor position data to "start of query"
@@ -934,7 +1048,7 @@ FillPortalStore(Portal portal)
            break;
 
        case PORTAL_UTIL_SELECT:
-           PortalRunUtility(portal, linitial(portal->parseTrees),
+           PortalRunUtility(portal, (Node *) linitial(portal->stmts),
                             treceiver, completionTag);
            break;
 
@@ -1023,11 +1137,9 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
  *     Execute a utility statement inside a portal.
  */
 static void
-PortalRunUtility(Portal portal, Query *query,
+PortalRunUtility(Portal portal, Node *utilityStmt,
                 DestReceiver *dest, char *completionTag)
 {
-   Node       *utilityStmt = query->utilityStmt;
-
    ereport(DEBUG3,
            (errmsg_internal("ProcessUtility")));
 
@@ -1061,18 +1173,7 @@ PortalRunUtility(Portal portal, Query *query,
    else
        ActiveSnapshot = NULL;
 
-   if (query->canSetTag)
-   {
-       /* utility statement can override default tag string */
-       ProcessUtility(utilityStmt, portal->portalParams, dest, completionTag);
-       if (completionTag && completionTag[0] == '\0' && portal->commandTag)
-           strcpy(completionTag, portal->commandTag);  /* use the default */
-   }
-   else
-   {
-       /* utility added by rewrite cannot set tag */
-       ProcessUtility(utilityStmt, portal->portalParams, dest, NULL);
-   }
+   ProcessUtility(utilityStmt, portal->portalParams, dest, completionTag);
 
    /* Some utility statements may change context on us */
    MemoryContextSwitchTo(PortalGetHeapMemory(portal));
@@ -1092,8 +1193,7 @@ PortalRunMulti(Portal portal,
               DestReceiver *dest, DestReceiver *altdest,
               char *completionTag)
 {
-   ListCell   *querylist_item;
-   ListCell   *planlist_item;
+   ListCell   *stmtlist_item;
 
    /*
     * If the destination is DestRemoteExecute, change to DestNone.  The
@@ -1114,47 +1214,36 @@ PortalRunMulti(Portal portal,
     * Loop to handle the individual queries generated from a single parsetree
     * by analysis and rewrite.
     */
-   forboth(querylist_item, portal->parseTrees,
-           planlist_item, portal->planTrees)
+   foreach(stmtlist_item, portal->stmts)
    {
-       Query      *query = (Query *) lfirst(querylist_item);
-       Plan       *plan = (Plan *) lfirst(planlist_item);
+       Node   *stmt = (Node *) lfirst(stmtlist_item);
 
        /*
         * If we got a cancel signal in prior command, quit
         */
        CHECK_FOR_INTERRUPTS();
 
-       if (query->commandType == CMD_UTILITY)
-       {
-           /*
-            * process utility functions (create, destroy, etc..)
-            */
-           Assert(plan == NULL);
-
-           PortalRunUtility(portal, query,
-                            query->canSetTag ? dest : altdest,
-                            completionTag);
-       }
-       else
+       if (IsA(stmt, PlannedStmt))
        {
            /*
             * process a plannable query.
             */
+           PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
            if (log_executor_stats)
                ResetUsage();
 
-           if (query->canSetTag)
+           if (pstmt->canSetTag)
            {
                /* statement can set tag string */
-               ProcessQuery(query, plan,
+               ProcessQuery(pstmt,
                             portal->portalParams,
                             dest, completionTag);
            }
            else
            {
                /* stmt added by rewrite cannot set tag */
-               ProcessQuery(query, plan,
+               ProcessQuery(pstmt,
                             portal->portalParams,
                             altdest, NULL);
            }
@@ -1162,12 +1251,25 @@ PortalRunMulti(Portal portal,
            if (log_executor_stats)
                ShowUsage("EXECUTOR STATISTICS");
        }
+       else
+       {
+           /*
+            * process utility functions (create, destroy, etc..)
+            *
+            * These are assumed canSetTag if they're the only stmt in the
+            * portal.
+            */
+           if (list_length(portal->stmts) == 1)
+               PortalRunUtility(portal, stmt, dest, completionTag);
+           else
+               PortalRunUtility(portal, stmt, altdest, NULL);
+       }
 
        /*
         * Increment command counter between queries, but not after the last
         * one.
         */
-       if (lnext(planlist_item) != NULL)
+       if (lnext(stmtlist_item) != NULL)
            CommandCounterIncrement();
 
        /*
index 5cabec2970b27cb48ff43d500707f24e22109852..47051ad1ed2c9bb24794ac45f2bff2f6e07a0b6c 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.272 2007/02/14 01:58:57 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.273 2007/02/20 17:32:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -248,36 +248,41 @@ CheckRelationOwnership(RangeVar *rel, bool noCatalogs)
 
 
 /*
- * QueryIsReadOnly: is an analyzed/rewritten query read-only?
+ * CommandIsReadOnly: is an executable query read-only?
  *
  * This is a much stricter test than we apply for XactReadOnly mode;
  * the query must be *in truth* read-only, because the caller wishes
  * not to do CommandCounterIncrement for it.
+ *
+ * Note: currently no need to support Query nodes here
  */
 bool
-QueryIsReadOnly(Query *parsetree)
+CommandIsReadOnly(Node *parsetree)
 {
-   switch (parsetree->commandType)
+   if (IsA(parsetree, PlannedStmt))
    {
-       case CMD_SELECT:
-           if (parsetree->into != NULL)
-               return false;   /* SELECT INTO */
-           else if (parsetree->rowMarks != NIL)
-               return false;   /* SELECT FOR UPDATE/SHARE */
-           else
-               return true;
-       case CMD_UPDATE:
-       case CMD_INSERT:
-       case CMD_DELETE:
-           return false;
-       case CMD_UTILITY:
-           /* For now, treat all utility commands as read/write */
-           return false;
-       default:
-           elog(WARNING, "unrecognized commandType: %d",
-                (int) parsetree->commandType);
-           break;
+       PlannedStmt *stmt = (PlannedStmt *) parsetree;
+
+       switch (stmt->commandType)
+       {
+           case CMD_SELECT:
+               if (stmt->into != NULL)
+                   return false;   /* SELECT INTO */
+               else if (stmt->rowMarks != NIL)
+                   return false;   /* SELECT FOR UPDATE/SHARE */
+               else
+                   return true;
+           case CMD_UPDATE:
+           case CMD_INSERT:
+           case CMD_DELETE:
+               return false;
+           default:
+               elog(WARNING, "unrecognized commandType: %d",
+                    (int) stmt->commandType);
+               break;
+       }
    }
+   /* For now, treat all utility commands as read/write */
    return false;
 }
 
@@ -1161,7 +1166,7 @@ UtilityReturnsTuples(Node *parsetree)
                entry = FetchPreparedStatement(stmt->name, false);
                if (!entry)
                    return false;       /* not our business to raise error */
-               switch (ChoosePortalStrategy(entry->query_list))
+               switch (ChoosePortalStrategy(entry->stmt_list))
                {
                    case PORTAL_ONE_SELECT:
                    case PORTAL_ONE_RETURNING:
@@ -1244,6 +1249,7 @@ UtilityTupleDescriptor(Node *parsetree)
  * QueryReturnsTuples
  *     Return "true" if this Query will send output to the destination.
  */
+#ifdef NOT_USED
 bool
 QueryReturnsTuples(Query *parsetree)
 {
@@ -1270,14 +1276,15 @@ QueryReturnsTuples(Query *parsetree)
    }
    return false;               /* default */
 }
+#endif
 
 
 /*
  * CreateCommandTag
- *     utility to get a string representation of the
- *     command operation, given a raw (un-analyzed) parsetree.
+ *     utility to get a string representation of the command operation,
+ *     given either a raw (un-analyzed) parsetree or a planned query.
  *
- * This must handle all raw command types, but since the vast majority
+ * This must handle all command types, but since the vast majority
  * of 'em are utility commands, it seems sensible to keep it here.
  *
  * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
@@ -1290,6 +1297,7 @@ CreateCommandTag(Node *parsetree)
 
    switch (nodeTag(parsetree))
    {
+       /* raw plannable queries */
        case T_InsertStmt:
            tag = "INSERT";
            break;
@@ -1306,6 +1314,7 @@ CreateCommandTag(Node *parsetree)
            tag = "SELECT";
            break;
 
+       /* utility statements --- same whether raw or cooked */
        case T_TransactionStmt:
            {
                TransactionStmt *stmt = (TransactionStmt *) parsetree;
@@ -1826,65 +1835,98 @@ CreateCommandTag(Node *parsetree)
            tag = "DEALLOCATE";
            break;
 
-       default:
-           elog(WARNING, "unrecognized node type: %d",
-                (int) nodeTag(parsetree));
-           tag = "???";
-           break;
-   }
-
-   return tag;
-}
-
-/*
- * CreateQueryTag
- *     utility to get a string representation of a Query operation.
- *
- * This is exactly like CreateCommandTag, except it works on a Query
- * that has already been through parse analysis (and possibly further).
- */
-const char *
-CreateQueryTag(Query *parsetree)
-{
-   const char *tag;
-
-   Assert(IsA(parsetree, Query));
+       /* already-planned queries */
+       case T_PlannedStmt:
+           {
+               PlannedStmt *stmt = (PlannedStmt *) parsetree;
 
-   switch (parsetree->commandType)
-   {
-       case CMD_SELECT:
+               switch (stmt->commandType)
+               {
+                   case CMD_SELECT:
+                       /*
+                        * We take a little extra care here so that the result
+                        * will be useful for complaints about read-only
+                        * statements
+                        */
+                       if (stmt->into != NULL)
+                           tag = "SELECT INTO";
+                       else if (stmt->rowMarks != NIL)
+                       {
+                           if (((RowMarkClause *) linitial(stmt->rowMarks))->forUpdate)
+                               tag = "SELECT FOR UPDATE";
+                           else
+                               tag = "SELECT FOR SHARE";
+                       }
+                       else
+                           tag = "SELECT";
+                       break;
+                   case CMD_UPDATE:
+                       tag = "UPDATE";
+                       break;
+                   case CMD_INSERT:
+                       tag = "INSERT";
+                       break;
+                   case CMD_DELETE:
+                       tag = "DELETE";
+                       break;
+                   default:
+                       elog(WARNING, "unrecognized commandType: %d",
+                            (int) stmt->commandType);
+                       tag = "???";
+                       break;
+               }
+           }
+           break;
 
-           /*
-            * We take a little extra care here so that the result will be
-            * useful for complaints about read-only statements
-            */
-           if (parsetree->into != NULL)
-               tag = "SELECT INTO";
-           else if (parsetree->rowMarks != NIL)
+       /* parsed-and-rewritten-but-not-planned queries */
+       case T_Query:
            {
-               if (((RowMarkClause *) linitial(parsetree->rowMarks))->forUpdate)
-                   tag = "SELECT FOR UPDATE";
-               else
-                   tag = "SELECT FOR SHARE";
+               Query *stmt = (Query *) parsetree;
+
+               switch (stmt->commandType)
+               {
+                   case CMD_SELECT:
+                       /*
+                        * We take a little extra care here so that the result
+                        * will be useful for complaints about read-only
+                        * statements
+                        */
+                       if (stmt->into != NULL)
+                           tag = "SELECT INTO";
+                       else if (stmt->rowMarks != NIL)
+                       {
+                           if (((RowMarkClause *) linitial(stmt->rowMarks))->forUpdate)
+                               tag = "SELECT FOR UPDATE";
+                           else
+                               tag = "SELECT FOR SHARE";
+                       }
+                       else
+                           tag = "SELECT";
+                       break;
+                   case CMD_UPDATE:
+                       tag = "UPDATE";
+                       break;
+                   case CMD_INSERT:
+                       tag = "INSERT";
+                       break;
+                   case CMD_DELETE:
+                       tag = "DELETE";
+                       break;
+                   case CMD_UTILITY:
+                       tag = CreateCommandTag(stmt->utilityStmt);
+                       break;
+                   default:
+                       elog(WARNING, "unrecognized commandType: %d",
+                            (int) stmt->commandType);
+                       tag = "???";
+                       break;
+               }
            }
-           else
-               tag = "SELECT";
-           break;
-       case CMD_UPDATE:
-           tag = "UPDATE";
-           break;
-       case CMD_INSERT:
-           tag = "INSERT";
-           break;
-       case CMD_DELETE:
-           tag = "DELETE";
-           break;
-       case CMD_UTILITY:
-           tag = CreateCommandTag(parsetree->utilityStmt);
            break;
+
        default:
-           elog(WARNING, "unrecognized commandType: %d",
-                (int) parsetree->commandType);
+           elog(WARNING, "unrecognized node type: %d",
+                (int) nodeTag(parsetree));
            tag = "???";
            break;
    }
@@ -1896,9 +1938,9 @@ CreateQueryTag(Query *parsetree)
 /*
  * GetCommandLogLevel
  *     utility to get the minimum log_statement level for a command,
- *     given a raw (un-analyzed) parsetree.
+ *     given either a raw (un-analyzed) parsetree or a planned query.
  *
- * This must handle all raw command types, but since the vast majority
+ * This must handle all command types, but since the vast majority
  * of 'em are utility commands, it seems sensible to keep it here.
  */
 LogStmtLevel
@@ -1908,6 +1950,7 @@ GetCommandLogLevel(Node *parsetree)
 
    switch (nodeTag(parsetree))
    {
+       /* raw plannable queries */
        case T_InsertStmt:
        case T_DeleteStmt:
        case T_UpdateStmt:
@@ -1921,6 +1964,7 @@ GetCommandLogLevel(Node *parsetree)
                lev = LOGSTMT_ALL;
            break;
 
+       /* utility statements --- same whether raw or cooked */
        case T_TransactionStmt:
            lev = LOGSTMT_ALL;
            break;
@@ -2216,12 +2260,12 @@ GetCommandLogLevel(Node *parsetree)
                pstmt = FetchPreparedStatement(stmt->name, false);
                if (pstmt)
                {
-                   foreach(l, pstmt->query_list)
+                   foreach(l, pstmt->stmt_list)
                    {
-                       Query      *query = (Query *) lfirst(l);
+                       Node       *substmt = (Node *) lfirst(l);
                        LogStmtLevel stmt_lev;
 
-                       stmt_lev = GetQueryLogLevel(query);
+                       stmt_lev = GetCommandLogLevel(substmt);
                        lev = Min(lev, stmt_lev);
                    }
                }
@@ -2232,62 +2276,72 @@ GetCommandLogLevel(Node *parsetree)
            lev = LOGSTMT_ALL;
            break;
 
-       case T_Query:
+       /* already-planned queries */
+       case T_PlannedStmt:
+           {
+               PlannedStmt *stmt = (PlannedStmt *) parsetree;
 
-           /*
-            * In complicated situations (eg, EXPLAIN ANALYZE in an extended
-            * Query protocol), we might find an already-analyzed query within
-            * a utility statement.  Cope.
-            */
-           lev = GetQueryLogLevel((Query *) parsetree);
-           break;
+               switch (stmt->commandType)
+               {
+                   case CMD_SELECT:
+                       if (stmt->into != NULL)
+                           lev = LOGSTMT_DDL;  /* CREATE AS, SELECT INTO */
+                       else
+                           lev = LOGSTMT_ALL;
+                       break;
 
-       default:
-           elog(WARNING, "unrecognized node type: %d",
-                (int) nodeTag(parsetree));
-           lev = LOGSTMT_ALL;
+                   case CMD_UPDATE:
+                   case CMD_INSERT:
+                   case CMD_DELETE:
+                       lev = LOGSTMT_MOD;
+                       break;
+
+                   default:
+                       elog(WARNING, "unrecognized commandType: %d",
+                            (int) stmt->commandType);
+                       lev = LOGSTMT_ALL;
+                       break;
+               }
+           }
            break;
-   }
 
-   return lev;
-}
+       /* parsed-and-rewritten-but-not-planned queries */
+       case T_Query:
+           {
+               Query *stmt = (Query *) parsetree;
 
-/*
- * GetQueryLogLevel
- *     utility to get the minimum log_statement level for a Query operation.
- *
- * This is exactly like GetCommandLogLevel, except it works on a Query
- * that has already been through parse analysis (and possibly further).
- */
-LogStmtLevel
-GetQueryLogLevel(Query *parsetree)
-{
-   LogStmtLevel lev;
+               switch (stmt->commandType)
+               {
+                   case CMD_SELECT:
+                       if (stmt->into != NULL)
+                           lev = LOGSTMT_DDL;  /* CREATE AS, SELECT INTO */
+                       else
+                           lev = LOGSTMT_ALL;
+                       break;
 
-   Assert(IsA(parsetree, Query));
+                   case CMD_UPDATE:
+                   case CMD_INSERT:
+                   case CMD_DELETE:
+                       lev = LOGSTMT_MOD;
+                       break;
 
-   switch (parsetree->commandType)
-   {
-       case CMD_SELECT:
-           if (parsetree->into != NULL)
-               lev = LOGSTMT_DDL;      /* CREATE AS, SELECT INTO */
-           else
-               lev = LOGSTMT_ALL;
-           break;
+                   case CMD_UTILITY:
+                       lev = GetCommandLogLevel(stmt->utilityStmt);
+                       break;
 
-       case CMD_UPDATE:
-       case CMD_INSERT:
-       case CMD_DELETE:
-           lev = LOGSTMT_MOD;
-           break;
+                   default:
+                       elog(WARNING, "unrecognized commandType: %d",
+                            (int) stmt->commandType);
+                       lev = LOGSTMT_ALL;
+                       break;
+               }
 
-       case CMD_UTILITY:
-           lev = GetCommandLogLevel(parsetree->utilityStmt);
+           }
            break;
 
        default:
-           elog(WARNING, "unrecognized commandType: %d",
-                (int) parsetree->commandType);
+           elog(WARNING, "unrecognized node type: %d",
+                (int) nodeTag(parsetree));
            lev = LOGSTMT_ALL;
            break;
    }
index 8c9d58422ef538438758dd482cf5f8b65cfa8a03..3bd2ee6397f5daa84456c7bfaf1648d10a70334a 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.98 2007/01/05 22:19:47 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.99 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -141,29 +141,45 @@ GetPortalByName(const char *name)
 }
 
 /*
- * PortalListGetPrimaryQuery
- *     Get the "primary" Query within a portal, ie, the one marked canSetTag.
+ * PortalListGetPrimaryStmt
+ *     Get the "primary" stmt within a portal, ie, the one marked canSetTag.
  *
- * Returns NULL if no such Query.  If multiple Query structs within the
+ * Returns NULL if no such stmt.  If multiple PlannedStmt structs within the
  * portal are marked canSetTag, returns the first one. Neither of these
  * cases should occur in present usages of this function.
  *
+ * Copes if given a list of Querys --- can't happen in a portal, but this
+ * code also supports prepared statements, which need both cases.
+ *
  * Note: the reason this is just handed a List is so that prepared statements
- * can share the code. For use with a portal, use PortalGetPrimaryQuery
+ * can share the code. For use with a portal, use PortalGetPrimaryStmt
  * rather than calling this directly.
  */
-Query *
-PortalListGetPrimaryQuery(List *parseTrees)
+Node *
+PortalListGetPrimaryStmt(List *stmts)
 {
    ListCell   *lc;
 
-   foreach(lc, parseTrees)
+   foreach(lc, stmts)
    {
-       Query      *query = (Query *) lfirst(lc);
+       Node   *stmt = (Node *) lfirst(lc);
 
-       Assert(IsA(query, Query));
-       if (query->canSetTag)
-           return query;
+       if (IsA(stmt, PlannedStmt))
+       {
+           if (((PlannedStmt *) stmt)->canSetTag)
+               return stmt;
+       }
+       else if (IsA(stmt, Query))
+       {
+           if (((Query *) stmt)->canSetTag)
+               return stmt;
+       }
+       else
+       {
+           /* Utility stmts are assumed canSetTag if they're the only stmt */
+           if (list_length(stmts) == 1)
+               return stmt;
+       }
    }
    return NULL;
 }
@@ -261,30 +277,25 @@ CreateNewPortal(void)
  * (before rewriting) was an empty string. Also, the passed commandTag must
  * be a pointer to a constant string, since it is not copied.  The caller is
  * responsible for ensuring that the passed prepStmtName (if any), sourceText
- * (if any), parse and plan trees have adequate lifetime.  Also, queryContext
- * must accurately describe the location of the parse trees.
+ * (if any), and plan trees have adequate lifetime.
  */
 void
 PortalDefineQuery(Portal portal,
                  const char *prepStmtName,
                  const char *sourceText,
                  const char *commandTag,
-                 List *parseTrees,
-                 List *planTrees,
+                 List *stmts,
                  MemoryContext queryContext)
 {
    AssertArg(PortalIsValid(portal));
    AssertState(portal->queryContext == NULL);  /* else defined already */
 
-   Assert(list_length(parseTrees) == list_length(planTrees));
-
-   Assert(commandTag != NULL || parseTrees == NIL);
+   Assert(commandTag != NULL || stmts == NIL);
 
    portal->prepStmtName = prepStmtName;
    portal->sourceText = sourceText;
    portal->commandTag = commandTag;
-   portal->parseTrees = parseTrees;
-   portal->planTrees = planTrees;
+   portal->stmts = stmts;
    portal->queryContext = queryContext;
 }
 
index 20d6e24e053e03713f49545fbebd3a17ef2e8f3a..8c53a2df2ac7be441278ef63288094beb31cdacc 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.387 2007/02/20 10:00:25 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.388 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200702201
+#define CATALOG_VERSION_NO 200702202
 
 #endif
index 4a17ddb767a45c1ca117822928d5becba934ff61..50fe2f132843ec65d8be2cc2d111c3db63292d5e 100644 (file)
@@ -7,13 +7,14 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.20 2007/01/05 22:19:53 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.21 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef PORTALCMDS_H
 #define PORTALCMDS_H
 
+#include "nodes/parsenodes.h"
 #include "utils/portal.h"
 
 
index c1ee47fe7e887391ab0e8d4a2d0451a45e98490d..a921bf1b0451bb52866a3c31f5d233adeb5e71f2 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 2002-2007, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.23 2007/01/05 22:19:53 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.24 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 /*
  * The data structure representing a prepared statement
  *
+ * A prepared statement might be fully planned, or only parsed-and-rewritten.
+ * If fully planned, stmt_list contains PlannedStmts and/or utility statements;
+ * if not, it contains Query nodes.
+ *
  * Note: all subsidiary storage lives in the context denoted by the context
  * field.  However, the string referenced by commandTag is not subsidiary
  * storage; it is assumed to be a compile-time-constant string.  As with
@@ -31,11 +35,11 @@ typedef struct
    char        stmt_name[NAMEDATALEN];
    char       *query_string;   /* text of query, or NULL */
    const char *commandTag;     /* command tag (a constant!), or NULL */
-   List       *query_list;     /* list of queries, rewritten */
-   List       *plan_list;      /* list of plans */
+   List       *stmt_list;      /* list of statement or Query nodes */
    List       *argtype_list;   /* list of parameter type OIDs */
+   bool        fully_planned;  /* what is in stmt_list, exactly? */
+   bool        from_sql;       /* prepared via SQL, not FE/BE protocol? */
    TimestampTz prepare_time;   /* the time when the stmt was prepared */
-   bool        from_sql;       /* stmt prepared via SQL, not FE/BE protocol? */
    MemoryContext context;      /* context containing this query */
 } PreparedStatement;
 
@@ -52,9 +56,9 @@ extern void ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
 extern void StorePreparedStatement(const char *stmt_name,
                       const char *query_string,
                       const char *commandTag,
-                      List *query_list,
-                      List *plan_list,
+                      List *stmt_list,
                       List *argtype_list,
+                      bool fully_planned,
                       bool from_sql);
 extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
                       bool throwError);
index cf991125d4beda9db8d4c554a103ee199e382614..d5ae745a296a62b03adc9ddbf01c85b03732f81d 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/execdesc.h,v 1.33 2007/01/05 22:19:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/execdesc.h,v 1.34 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,7 +16,7 @@
 #define EXECDESC_H
 
 #include "nodes/execnodes.h"
-#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
 #include "tcop/dest.h"
 
 
  *     query descriptor:
  *
  * a QueryDesc encapsulates everything that the executor
- * needs to execute the query
+ * needs to execute the query.
+ *
+ * For the convenience of SQL-language functions, we also support QueryDescs
+ * containing utility statements; these must not be passed to the executor
+ * however.
  * ---------------------
  */
 typedef struct QueryDesc
 {
    /* These fields are provided by CreateQueryDesc */
    CmdType     operation;      /* CMD_SELECT, CMD_UPDATE, etc. */
-   Query      *parsetree;      /* rewritten parsetree */
-   Plan       *plantree;       /* planner's output */
+   PlannedStmt *plannedstmt;   /* planner's output, or null if utility */
+   Node       *utilitystmt;    /* utility statement, or null */
    Snapshot    snapshot;       /* snapshot to use for query */
    Snapshot    crosscheck_snapshot;    /* crosscheck for RI update/delete */
    DestReceiver *dest;         /* the destination for tuple output */
@@ -46,13 +50,18 @@ typedef struct QueryDesc
 } QueryDesc;
 
 /* in pquery.c */
-extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
+extern QueryDesc *CreateQueryDesc(PlannedStmt *plannedstmt,
                Snapshot snapshot,
                Snapshot crosscheck_snapshot,
                DestReceiver *dest,
                ParamListInfo params,
                bool doInstrument);
 
+extern QueryDesc *CreateUtilityQueryDesc(Node *utilitystmt,
+               Snapshot snapshot,
+               DestReceiver *dest,
+               ParamListInfo params);
+
 extern void FreeQueryDesc(QueryDesc *qdesc);
 
 #endif   /* EXECDESC_H  */
index bfbe1ba2f3886739d872c6258c0d2bb5a3c62ab9..38260b5ecd6c211dcd9b18ed590f688b0d04e1ee 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.136 2007/02/06 02:59:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.137 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,7 @@
 #define EXECUTOR_H
 
 #include "executor/execdesc.h"
+#include "nodes/parsenodes.h"
 
 
 /*
index 3bfc870159fca697e9171c524f3f0306f91106c8..5e65bd750aeaaeba4dbc8639b4a4398a92603751 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.26 2007/01/05 22:19:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.27 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,10 +35,11 @@ typedef struct
    MemoryContext plancxt;
    /* Original query string (used for error reporting) */
    const char *query;
-   /* List of List of querytrees; one sublist per original parsetree */
-   List       *qtlist;
-   /* List of plan trees --- length == # of querytrees, but flat list */
-   List       *ptlist;
+   /*
+    * List of List of PlannedStmts and utility stmts; one sublist per
+    * original parsetree
+    */
+   List       *stmt_list_list;
    /* Argument types, if a prepared plan */
    int         nargs;
    Oid        *argtypes;
index 35a0ab3a6071408f8073411577ce83a9f7e44ae9..b576f4610e3e75e7b7e84de5bb6b52b676db18ee 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.167 2007/02/06 02:59:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.168 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -344,7 +344,7 @@ typedef struct EState
    ExprContext *es_per_tuple_exprcontext;
 
    /* Below is to re-evaluate plan qual in READ COMMITTED mode */
-   Plan       *es_topPlan;     /* link to top of plan tree */
+   PlannedStmt *es_plannedstmt;    /* link to top of plan tree */
    struct evalPlanQual *es_evalPlanQual;       /* chain of PlanQual states */
    bool       *es_evTupleNull; /* local array of EPQ status */
    HeapTuple  *es_evTuple;     /* shared array of EPQ substitute tuples */
index 44175591e7502c38910a2286bfecff1bd57a8940..53bd13b5fbe58271eee4f1e824b0c99ecc87eac2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.195 2007/02/19 07:03:31 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.196 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -141,6 +141,7 @@ typedef enum NodeTag
    T_RangeTblRef,
    T_JoinExpr,
    T_FromExpr,
+   T_IntoClause,
 
    /*
     * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
@@ -225,9 +226,10 @@ typedef enum NodeTag
    T_OidList,
 
    /*
-    * TAGS FOR PARSE TREE NODES (parsenodes.h)
+    * TAGS FOR STATEMENT NODES (mostly in parsenodes.h)
     */
    T_Query = 700,
+   T_PlannedStmt,
    T_InsertStmt,
    T_DeleteStmt,
    T_UpdateStmt,
@@ -302,8 +304,12 @@ typedef enum NodeTag
    T_AlterOwnerStmt,
    T_DropOwnedStmt,
    T_ReassignOwnedStmt,
+   T_CompositeTypeStmt,
 
-   T_A_Expr = 800,
+   /*
+    * TAGS FOR PARSE TREE NODES (parsenodes.h)
+    */
+   T_A_Expr = 900,
    T_ColumnRef,
    T_ParamRef,
    T_A_Const,
@@ -328,7 +334,6 @@ typedef enum NodeTag
    T_FuncWithArgs,
    T_PrivTarget,
    T_CreateOpClassItem,
-   T_CompositeTypeStmt,
    T_InhRelation,
    T_FunctionParameter,
    T_LockingClause,
@@ -343,7 +348,7 @@ typedef enum NodeTag
     * purposes (usually because they are involved in APIs where we want to
     * pass multiple object types through the same pointer).
     */
-   T_TriggerData = 900,        /* in commands/trigger.h */
+   T_TriggerData = 950,        /* in commands/trigger.h */
    T_ReturnSetInfo,            /* in nodes/execnodes.h */
    T_TIDBitmap                 /* in nodes/tidbitmap.h */
 } NodeTag;
@@ -430,10 +435,9 @@ typedef double Cost;           /* execution cost (in page-access units) */
 
 /*
  * CmdType -
- *   enums for type of operation represented by a Query
+ *   enums for type of operation represented by a Query or PlannedStmt
  *
- * ??? could have put this in parsenodes.h but many files not in the
- *   optimizer also need this...
+ * This is needed in both parsenodes.h and plannodes.h, so put it here...
  */
 typedef enum CmdType
 {
index 0db72763021e2e23f5806bfcdc1d6b9ee2f0f7df..ec9ccb6ce30c15827e8c4671b5b77cd4c0718370 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.340 2007/02/03 14:06:55 petere Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.341 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,15 +27,6 @@ typedef enum QuerySource
    QSRC_NON_INSTEAD_RULE       /* added by non-INSTEAD rule */
 } QuerySource;
 
-/* What to do at commit time for temporary relations */
-typedef enum OnCommitAction
-{
-   ONCOMMIT_NOOP,              /* No ON COMMIT clause (do nothing) */
-   ONCOMMIT_PRESERVE_ROWS,     /* ON COMMIT PRESERVE ROWS (do nothing) */
-   ONCOMMIT_DELETE_ROWS,       /* ON COMMIT DELETE ROWS */
-   ONCOMMIT_DROP               /* ON COMMIT DROP */
-} OnCommitAction;
-
 /* Sort ordering options for ORDER BY and CREATE INDEX */
 typedef enum SortByDir
 {
@@ -86,11 +77,14 @@ typedef uint32 AclMode;         /* a bitmask of privilege bits */
 
 /*
  * Query -
- *   all statements are turned into a Query tree (via transformStmt)
- *   for further processing by the optimizer
+ *   Parse analysis turns all statements into a Query tree (via transformStmt)
+ *   for further processing by the rewriter and planner.
  *
- *   utility statements (i.e. non-optimizable statements) have the
+ *   Utility statements (i.e. non-optimizable statements) have the
  *   utilityStmt field set, and the Query itself is mostly dummy.
+ *
+ *   Planning converts a Query tree into a Plan tree headed by a PlannedStmt
+ *   noded --- the Query structure is not used by the executor.
  */
 typedef struct Query
 {
@@ -108,10 +102,7 @@ typedef struct Query
    int         resultRelation; /* rtable index of target relation for
                                 * INSERT/UPDATE/DELETE; 0 for SELECT */
 
-   RangeVar   *into;           /* target relation for SELECT INTO */
-   List       *intoOptions;    /* options from WITH clause */
-   OnCommitAction intoOnCommit;    /* what do we do at COMMIT? */
-   char       *intoTableSpaceName;     /* table space to use, or NULL */
+   IntoClause *into;           /* target for SELECT INTO / CREATE TABLE AS */
 
    bool        hasAggs;        /* has aggregates in tlist or havingQual */
    bool        hasSubLinks;    /* has subquery SubLink */
@@ -138,29 +129,6 @@ typedef struct Query
 
    Node       *setOperations;  /* set-operation tree if this is top level of
                                 * a UNION/INTERSECT/EXCEPT query */
-
-   /*
-    * If the resultRelation turns out to be the parent of an inheritance
-    * tree, the planner will add all the child tables to the rtable and store
-    * a list of the rtindexes of all the result relations here. This is done
-    * at plan time, not parse time, since we don't want to commit to the
-    * exact set of child tables at parse time.  XXX This field ought to go in
-    * some sort of TopPlan plan node, not in the Query.
-    */
-   List       *resultRelations;    /* integer list of RT indexes, or NIL */
-
-   /*
-    * If the query has a returningList then the planner will store a list of
-    * processed targetlists (one per result relation) here.  We must have a
-    * separate RETURNING targetlist for each result rel because column
-    * numbers may vary within an inheritance tree.  In the targetlists, Vars
-    * referencing the result relation will have their original varno and
-    * varattno, while Vars referencing other rels will be converted to have
-    * varno OUTER and varattno referencing a resjunk entry in the top plan
-    * node's targetlist.  XXX This field ought to go in some sort of TopPlan
-    * plan node, not in the Query.
-    */
-   List       *returningLists; /* list of lists of TargetEntry, or NIL */
 } Query;
 
 
@@ -761,17 +729,10 @@ typedef struct SelectStmt
 
    /*
     * These fields are used only in "leaf" SelectStmts.
-    *
-    * into, intoColNames, intoOptions, intoOnCommit, and intoTableSpaceName
-    * are a kluge; they belong somewhere else...
     */
    List       *distinctClause; /* NULL, list of DISTINCT ON exprs, or
                                 * lcons(NIL,NIL) for all (SELECT DISTINCT) */
-   RangeVar   *into;           /* target table (for select into table) */
-   List       *intoColNames;   /* column names for into table */
-   List       *intoOptions;    /* options from WITH clause */
-   OnCommitAction intoOnCommit;    /* what do we do at COMMIT? */
-   char       *intoTableSpaceName;     /* table space to use, or NULL */
+   IntoClause *into;           /* target for SELECT INTO / CREATE TABLE AS */
    List       *targetList;     /* the target list (of ResTarget) */
    List       *fromClause;     /* the FROM clause */
    Node       *whereClause;    /* WHERE qualification */
@@ -1994,10 +1955,7 @@ typedef struct ExecuteStmt
 {
    NodeTag     type;
    char       *name;           /* The name of the plan to execute */
-   RangeVar   *into;           /* Optional table to store results in */
-   List       *intoOptions;    /* Options from WITH clause */
-   OnCommitAction into_on_commit;      /* What do we do at COMMIT? */
-   char       *into_tbl_space; /* Tablespace to use, or NULL */
+   IntoClause *into;           /* Optional table to store results in */
    List       *params;         /* Values to assign to parameters */
 } ExecuteStmt;
 
index cdd7b4d2e43707fb574fb0ae2e7c040a2117d2f3..537981462b23e7b51585f63ab97e9116e8c37106 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.90 2007/02/19 02:23:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.91 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * ----------------------------------------------------------------
  */
 
+/* ----------------
+ *     PlannedStmt node
+ *
+ * The output of the planner is a Plan tree headed by a PlannedStmt node.
+ * PlannedStmt holds the "one time" information needed by the executor.
+ * ----------------
+ */
+typedef struct PlannedStmt
+{
+   NodeTag     type;
+
+   CmdType     commandType;    /* select|insert|update|delete */
+
+   bool        canSetTag;      /* do I set the command result tag? */
+
+   struct Plan *planTree;      /* tree of Plan nodes */
+
+   List       *rtable;         /* list of RangeTblEntry nodes */
+
+   /* rtable indexes of target relations for INSERT/UPDATE/DELETE */
+   List       *resultRelations;    /* integer list of RT indexes, or NIL */
+
+   IntoClause *into;           /* target for SELECT INTO / CREATE TABLE AS */
+
+   /*
+    * If the query has a returningList then the planner will store a list of
+    * processed targetlists (one per result relation) here.  We must have a
+    * separate RETURNING targetlist for each result rel because column
+    * numbers may vary within an inheritance tree.  In the targetlists, Vars
+    * referencing the result relation will have their original varno and
+    * varattno, while Vars referencing other rels will be converted to have
+    * varno OUTER and varattno referencing a resjunk entry in the top plan
+    * node's targetlist.
+    */
+   List       *returningLists; /* list of lists of TargetEntry, or NIL */
+
+   List       *rowMarks;       /* a list of RowMarkClause's */
+
+   int         nParamExec;     /* number of PARAM_EXEC Params used */
+} PlannedStmt;
+
+
 /* ----------------
  *     Plan node
  *
@@ -75,15 +117,6 @@ typedef struct Plan
     */
    Bitmapset  *extParam;
    Bitmapset  *allParam;
-
-   /*
-    * We really need in some TopPlan node to store range table and
-    * resultRelation from Query there and get rid of Query itself from
-    * Executor. Some other stuff like below could be put there, too.
-    */
-   int         nParamExec;     /* Number of them in entire query. This is to
-                                * get Executor know about how many PARAM_EXEC
-                                * there are in query plan. */
 } Plan;
 
 /* ----------------
index caa689e262379074e18b09fd35dcd769fe068b29..185673f729a85f90750eff29f7e30ef2c720181b 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.125 2007/02/19 07:03:31 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.126 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,6 +49,15 @@ typedef enum InhOption
    INH_DEFAULT                 /* Use current SQL_inheritance option */
 } InhOption;
 
+/* What to do at commit time for temporary relations */
+typedef enum OnCommitAction
+{
+   ONCOMMIT_NOOP,              /* No ON COMMIT clause (do nothing) */
+   ONCOMMIT_PRESERVE_ROWS,     /* ON COMMIT PRESERVE ROWS (do nothing) */
+   ONCOMMIT_DELETE_ROWS,       /* ON COMMIT DELETE ROWS */
+   ONCOMMIT_DROP               /* ON COMMIT DROP */
+} OnCommitAction;
+
 /*
  * RangeVar - range variable, used in FROM clauses
  *
@@ -69,6 +78,20 @@ typedef struct RangeVar
    Alias      *alias;          /* table alias & optional column aliases */
 } RangeVar;
 
+/*
+ * IntoClause - target information for SELECT INTO and CREATE TABLE AS
+ */
+typedef struct IntoClause
+{
+   NodeTag     type;
+
+   RangeVar   *rel;            /* target relation name */
+   List       *colNames;       /* column names to assign, or NIL */
+   List       *options;        /* options from WITH clause */
+   OnCommitAction onCommit;    /* what do we do at COMMIT? */
+   char       *tableSpaceName; /* table space to use, or NULL */
+} IntoClause;
+
 
 /* ----------------------------------------------------------------
  *                 node types for executable expressions
index 6de06ebc918f769327cf32ca075b3ab26ff9262a..59ec830f3ff6a23d1c065d7715a773d38bdb4037 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.136 2007/02/19 07:03:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.137 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -110,6 +110,10 @@ typedef struct PlannerInfo
    List       *join_rel_list;  /* list of join-relation RelOptInfos */
    struct HTAB *join_rel_hash; /* optional hashtable for join relations */
 
+   List       *resultRelations;    /* integer list of RT indexes, or NIL */
+
+   List       *returningLists;     /* list of lists of TargetEntry, or NIL */
+
    List       *init_plans;             /* init subplans for query */
 
    List       *eq_classes;             /* list of active EquivalenceClasses */
index 44d03602694120f59dd557b3328ea1f2005f6420..c243cdbc356aa2206bf81484a905549943213fe4 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.37 2007/02/19 07:03:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.38 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #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 */
index 353135302711e06c60d43e5939fc1f0451d638ea..5cab498c13a579076741440c6b4f1fcbf7961428 100644 (file)
@@ -7,23 +7,26 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.40 2007/01/05 22:19:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.41 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef PQUERY_H
 #define PQUERY_H
 
+#include "nodes/parsenodes.h"
 #include "utils/portal.h"
 
 
 extern DLLIMPORT Portal ActivePortal;
 
 
-extern PortalStrategy ChoosePortalStrategy(List *parseTrees);
+extern PortalStrategy ChoosePortalStrategy(List *stmts);
 
 extern List *FetchPortalTargetList(Portal portal);
 
+extern List *FetchStatementTargetList(Node *stmt);
+
 extern void PortalStart(Portal portal, ParamListInfo params,
            Snapshot snapshot);
 
index ad66a61dace10752540cb634f59e71540a457143..2a5512557669e04b69f50ecb4b990181ae1c89c9 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.86 2007/01/05 22:19:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.87 2007/02/20 17:32:17 tgl Exp $
  *
  * OLD COMMENTS
  *   This file was created so that other c files could get the two
@@ -20,6 +20,7 @@
 #define TCOPPROT_H
 
 #include "executor/execdesc.h"
+#include "nodes/parsenodes.h"
 #include "utils/guc.h"
 
 
@@ -50,7 +51,7 @@ extern List *pg_parse_and_rewrite(const char *query_string,
 extern List *pg_parse_query(const char *query_string);
 extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
                       Oid *paramTypes, int numParams);
-extern Plan *pg_plan_query(Query *querytree, ParamListInfo boundParams);
+extern PlannedStmt *pg_plan_query(Query *querytree, ParamListInfo boundParams);
 extern List *pg_plan_queries(List *querytrees, ParamListInfo boundParams,
                bool needSnapshot);
 
index ebcaf5977290f62088d8002662c80e2675a0c454..52c02253068c5acdba60a6507658716662738cb3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.30 2007/01/05 22:19:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.31 2007/02/20 17:32:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,15 +26,9 @@ extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
 
 extern const char *CreateCommandTag(Node *parsetree);
 
-extern const char *CreateQueryTag(Query *parsetree);
-
 extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
 
-extern LogStmtLevel GetQueryLogLevel(Query *parsetree);
-
-extern bool QueryReturnsTuples(Query *parsetree);
-
-extern bool QueryIsReadOnly(Query *parsetree);
+extern bool CommandIsReadOnly(Node *parsetree);
 
 extern void CheckRelationOwnership(RangeVar *rel, bool noCatalogs);
 
index 5eb2c715e30b36a7971bac9840490ff921c11104..aa432abb8768adb0a32a12db24fcb6c295d5bf3d 100644 (file)
@@ -12,7 +12,7 @@
  * to let the client suspend an update-type query partway through! Because
  * the query rewriter does not allow arbitrary ON SELECT rewrite rules,
  * only queries that were originally update-type could produce multiple
- * parse/plan trees; so the restriction to a single query is not a problem
+ * plan trees; so the restriction to a single query is not a problem
  * in practice.
  *
  * For SQL cursors, we support three kinds of scroll behavior:
@@ -39,7 +39,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.72 2007/01/05 22:19:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.73 2007/02/20 17:32:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -124,9 +124,8 @@ typedef struct PortalData
    /* The query or queries the portal will execute */
    const char *sourceText;     /* text of query, if known (may be NULL) */
    const char *commandTag;     /* command tag for original query */
-   List       *parseTrees;     /* parse tree(s) */
-   List       *planTrees;      /* plan tree(s) */
-   MemoryContext queryContext; /* where the parse trees live */
+   List       *stmts;          /* PlannedStmts and/or utility statements */
+   MemoryContext queryContext; /* where the plan trees live */
 
    /*
     * Note: queryContext effectively identifies which prepared statement the
@@ -191,7 +190,7 @@ typedef struct PortalData
  */
 #define PortalGetQueryDesc(portal) ((portal)->queryDesc)
 #define PortalGetHeapMemory(portal) ((portal)->heap)
-#define PortalGetPrimaryQuery(portal) PortalListGetPrimaryQuery((portal)->parseTrees)
+#define PortalGetPrimaryStmt(portal) PortalListGetPrimaryStmt((portal)->stmts)
 
 
 /* Prototypes for functions in utils/mmgr/portalmem.c */
@@ -217,10 +216,9 @@ extern void PortalDefineQuery(Portal portal,
                  const char *prepStmtName,
                  const char *sourceText,
                  const char *commandTag,
-                 List *parseTrees,
-                 List *planTrees,
+                 List *stmts,
                  MemoryContext queryContext);
-extern Query *PortalListGetPrimaryQuery(List *parseTrees);
+extern Node *PortalListGetPrimaryStmt(List *stmts);
 extern void PortalCreateHoldStore(Portal portal);
 
 #endif   /* PORTAL_H */
index 09f7a99097dda5c9ac2b06accb18b4dc8a016646..68a68a6634cdfea1a3558057245ec4378adda010 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.188 2007/02/08 18:37:30 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.189 2007/02/20 17:32:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2380,20 +2380,20 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
        exec_prepare_plan(estate, expr);
        stmt->mod_stmt = false;
        spi_plan = (_SPI_plan *) expr->plan;
-       foreach(l, spi_plan->qtlist)
+       foreach(l, spi_plan->stmt_list_list)
        {
            ListCell   *l2;
 
            foreach(l2, (List *) lfirst(l))
            {
-               Query      *q = (Query *) lfirst(l2);
+               PlannedStmt *p = (PlannedStmt *) lfirst(l2);
 
-               Assert(IsA(q, Query));
-               if (q->canSetTag)
+               if (IsA(p, PlannedStmt) &&
+                   p->canSetTag)
                {
-                   if (q->commandType == CMD_INSERT ||
-                       q->commandType == CMD_UPDATE ||
-                       q->commandType == CMD_DELETE)
+                   if (p->commandType == CMD_INSERT ||
+                       p->commandType == CMD_UPDATE ||
+                       p->commandType == CMD_DELETE)
                        stmt->mod_stmt = true;
                }
            }
@@ -4674,6 +4674,8 @@ static void
 exec_simple_check_plan(PLpgSQL_expr *expr)
 {
    _SPI_plan  *spi_plan = (_SPI_plan *) expr->plan;
+   List       *sublist;
+   PlannedStmt *stmt;
    Plan       *plan;
    TargetEntry *tle;
 
@@ -4683,17 +4685,20 @@ exec_simple_check_plan(PLpgSQL_expr *expr)
     * 1. We can only evaluate queries that resulted in one single execution
     * plan
     */
-   if (list_length(spi_plan->ptlist) != 1)
+   if (list_length(spi_plan->stmt_list_list) != 1)
+       return;
+   sublist = (List *) linitial(spi_plan->stmt_list_list);
+   if (list_length(sublist) != 1)
        return;
 
-   plan = (Plan *) linitial(spi_plan->ptlist);
+   stmt = (PlannedStmt *) linitial(sublist);
 
    /*
     * 2. It must be a RESULT plan --> no scan's required
     */
-   if (plan == NULL)           /* utility statement produces this */
+   if (!IsA(stmt, PlannedStmt))
        return;
-
+   plan = stmt->planTree;
    if (!IsA(plan, Result))
        return;