]> granicus.if.org Git - postgresql/commitdiff
Change representation of statement lists, and add statement location info.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 14 Jan 2017 21:02:35 +0000 (16:02 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 14 Jan 2017 21:02:35 +0000 (16:02 -0500)
This patch makes several changes that improve the consistency of
representation of lists of statements.  It's always been the case
that the output of parse analysis is a list of Query nodes, whatever
the types of the individual statements in the list.  This patch brings
similar consistency to the outputs of raw parsing and planning steps:

* The output of raw parsing is now always a list of RawStmt nodes;
the statement-type-dependent nodes are one level down from that.

* The output of pg_plan_queries() is now always a list of PlannedStmt
nodes, even for utility statements.  In the case of a utility statement,
"planning" just consists of wrapping a CMD_UTILITY PlannedStmt around
the utility node.  This list representation is now used in Portal and
CachedPlan plan lists, replacing the former convention of intermixing
PlannedStmts with bare utility-statement nodes.

Now, every list of statements has a consistent head-node type depending
on how far along it is in processing.  This allows changing many places
that formerly used generic "Node *" pointers to use a more specific
pointer type, thus reducing the number of IsA() tests and casts needed,
as well as improving code clarity.

Also, the post-parse-analysis representation of DECLARE CURSOR is changed
so that it looks more like EXPLAIN, PREPARE, etc.  That is, the contained
SELECT remains a child of the DeclareCursorStmt rather than getting flipped
around to be the other way.  It's now true for both Query and PlannedStmt
that utilityStmt is non-null if and only if commandType is CMD_UTILITY.
That allows simplifying a lot of places that were testing both fields.
(I think some of those were just defensive programming, but in many places,
it was actually necessary to avoid confusing DECLARE CURSOR with SELECT.)

Because PlannedStmt carries a canSetTag field, we're also able to get rid
of some ad-hoc rules about how to reconstruct canSetTag for a bare utility
statement; specifically, the assumption that a utility is canSetTag if and
only if it's the only one in its list.  While I see no near-term need for
relaxing that restriction, it's nice to get rid of the ad-hocery.

The API of ProcessUtility() is changed so that what it's passed is the
wrapper PlannedStmt not just the bare utility statement.  This will affect
all users of ProcessUtility_hook, but the changes are pretty trivial; see
the affected contrib modules for examples of the minimum change needed.
(Most compilers should give pointer-type-mismatch warnings for uncorrected
code.)

There's also a change in the API of ExplainOneQuery_hook, to pass through
cursorOptions instead of expecting hook functions to know what to pick.
This is needed because of the DECLARE CURSOR changes, but really should
have been done in 9.6; it's unlikely that any extant hook functions
know about using CURSOR_OPT_PARALLEL_OK.

Finally, teach gram.y to save statement boundary locations in RawStmt
nodes, and pass those through to Query and PlannedStmt nodes.  This allows
more intelligent handling of cases where a source query string contains
multiple statements.  This patch doesn't actually do anything with the
information, but a follow-on patch will.  (Passing this information through
cleanly is the true motivation for these changes; while I think this is all
good cleanup, it's unlikely we'd have bothered without this end goal.)

catversion bump because addition of location fields to struct Query
affects stored rules.

This patch is by me, but it owes a good deal to Fabien Coelho who did
a lot of preliminary work on the problem, and also reviewed the patch.

Discussion: https://postgr.es/m/alpine.DEB.2.20.1612200926310.29821@lancre

53 files changed:
contrib/pg_stat_statements/pg_stat_statements.c
contrib/sepgsql/hooks.c
src/backend/catalog/pg_proc.c
src/backend/commands/copy.c
src/backend/commands/createas.c
src/backend/commands/explain.c
src/backend/commands/extension.c
src/backend/commands/foreigncmds.c
src/backend/commands/portalcmds.c
src/backend/commands/prepare.c
src/backend/commands/schemacmds.c
src/backend/commands/tablecmds.c
src/backend/commands/trigger.c
src/backend/commands/view.c
src/backend/executor/execParallel.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/plan/planner.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/util/clauses.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_clause.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_type.c
src/backend/parser/parser.c
src/backend/rewrite/rewriteDefine.c
src/backend/tcop/postgres.c
src/backend/tcop/pquery.c
src/backend/tcop/utility.c
src/backend/utils/cache/plancache.c
src/backend/utils/mmgr/portalmem.c
src/include/catalog/catversion.h
src/include/commands/copy.h
src/include/commands/explain.h
src/include/commands/portalcmds.h
src/include/commands/prepare.h
src/include/commands/schemacmds.h
src/include/commands/view.h
src/include/executor/execdesc.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/plannodes.h
src/include/parser/analyze.h
src/include/tcop/tcopprot.h
src/include/tcop/utility.h
src/include/utils/plancache.h
src/include/utils/portal.h
src/pl/plpgsql/src/pl_exec.c

index 3d1b99f502d2da9bfd58cb90de7f2b30659a6ec4..7eec86fb289b4a3fbccb6b3bfbcedcb52d13d6d2 100644 (file)
@@ -292,7 +292,7 @@ static void pgss_ExecutorRun(QueryDesc *queryDesc,
                                 uint64 count);
 static void pgss_ExecutorFinish(QueryDesc *queryDesc);
 static void pgss_ExecutorEnd(QueryDesc *queryDesc);
-static void pgss_ProcessUtility(Node *parsetree, const char *queryString,
+static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                                        ProcessUtilityContext context, ParamListInfo params,
                                        DestReceiver *dest, char *completionTag);
 static uint32 pgss_hash_fn(const void *key, Size keysize);
@@ -942,10 +942,12 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
  * ProcessUtility hook
  */
 static void
-pgss_ProcessUtility(Node *parsetree, const char *queryString,
+pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                                        ProcessUtilityContext context, ParamListInfo params,
                                        DestReceiver *dest, char *completionTag)
 {
+       Node       *parsetree = pstmt->utilityStmt;
+
        /*
         * If it's an EXECUTE statement, we don't track it and don't increment the
         * nesting level.  This allows the cycles to be charged to the underlying
@@ -979,11 +981,11 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
                PG_TRY();
                {
                        if (prev_ProcessUtility)
-                               prev_ProcessUtility(parsetree, queryString,
+                               prev_ProcessUtility(pstmt, queryString,
                                                                        context, params,
                                                                        dest, completionTag);
                        else
-                               standard_ProcessUtility(parsetree, queryString,
+                               standard_ProcessUtility(pstmt, queryString,
                                                                                context, params,
                                                                                dest, completionTag);
                        nested_level--;
@@ -1044,11 +1046,11 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
        else
        {
                if (prev_ProcessUtility)
-                       prev_ProcessUtility(parsetree, queryString,
+                       prev_ProcessUtility(pstmt, queryString,
                                                                context, params,
                                                                dest, completionTag);
                else
-                       standard_ProcessUtility(parsetree, queryString,
+                       standard_ProcessUtility(pstmt, queryString,
                                                                        context, params,
                                                                        dest, completionTag);
        }
index 15f40d83f11521001e2b9fcd960f09bf1efc32e3..93cc8debaa7e6ce6a3f9f354749e668819d2e67e 100644 (file)
@@ -297,13 +297,14 @@ sepgsql_exec_check_perms(List *rangeTabls, bool abort)
  * break whole of the things if nefarious user would use.
  */
 static void
-sepgsql_utility_command(Node *parsetree,
+sepgsql_utility_command(PlannedStmt *pstmt,
                                                const char *queryString,
                                                ProcessUtilityContext context,
                                                ParamListInfo params,
                                                DestReceiver *dest,
                                                char *completionTag)
 {
+       Node       *parsetree = pstmt->utilityStmt;
        sepgsql_context_info_t saved_context_info = sepgsql_context_info;
        ListCell   *cell;
 
@@ -362,11 +363,11 @@ sepgsql_utility_command(Node *parsetree,
                }
 
                if (next_ProcessUtility_hook)
-                       (*next_ProcessUtility_hook) (parsetree, queryString,
+                       (*next_ProcessUtility_hook) (pstmt, queryString,
                                                                                 context, params,
                                                                                 dest, completionTag);
                else
-                       standard_ProcessUtility(parsetree, queryString,
+                       standard_ProcessUtility(pstmt, queryString,
                                                                        context, params,
                                                                        dest, completionTag);
        }
index 6d8f17db2d14641618ac1c52e6374cf45c37d4b0..85cec0cb1cbeb1770cb4f120a6bc6bb188421aa6 100644 (file)
@@ -934,7 +934,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
                        querytree_list = NIL;
                        foreach(lc, raw_parsetree_list)
                        {
-                               Node       *parsetree = (Node *) lfirst(lc);
+                               RawStmt    *parsetree = (RawStmt *) lfirst(lc);
                                List       *querytree_sublist;
 
                                querytree_sublist = pg_analyze_and_rewrite_params(parsetree,
index f56b2ac49b0c5c3fd4b7dccb7a0c4da33c2ec58c..1fd2162794832c76d8c2f0e275724d0cd7cf4b07 100644 (file)
@@ -287,13 +287,13 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
 
 
 /* non-export function prototypes */
-static CopyState BeginCopy(ParseState *pstate, bool is_from, Relation rel, Node *raw_query,
-                 const Oid queryRelId, List *attnamelist,
+static CopyState BeginCopy(ParseState *pstate, bool is_from, Relation rel,
+                 RawStmt *raw_query, Oid queryRelId, List *attnamelist,
                  List *options);
 static void EndCopy(CopyState cstate);
 static void ClosePipeToProgram(CopyState cstate);
-static CopyState BeginCopyTo(ParseState *pstate, Relation rel, Node *query,
-                       const Oid queryRelId, const char *filename, bool is_program,
+static CopyState BeginCopyTo(ParseState *pstate, Relation rel, RawStmt *query,
+                       Oid queryRelId, const char *filename, bool is_program,
                        List *attnamelist, List *options);
 static void EndCopyTo(CopyState cstate);
 static uint64 DoCopyTo(CopyState cstate);
@@ -770,15 +770,17 @@ CopyLoadRawBuf(CopyState cstate)
  * Do not allow the copy if user doesn't have proper permission to access
  * the table or the specifically requested columns.
  */
-Oid
-DoCopy(ParseState *pstate, const CopyStmt *stmt, uint64 *processed)
+void
+DoCopy(ParseState *pstate, const CopyStmt *stmt,
+          int stmt_location, int stmt_len,
+          uint64 *processed)
 {
        CopyState       cstate;
        bool            is_from = stmt->is_from;
        bool            pipe = (stmt->filename == NULL);
        Relation        rel;
        Oid                     relid;
-       Node       *query = NULL;
+       RawStmt    *query = NULL;
        List       *range_table = NIL;
 
        /* Disallow COPY to/from file or program except to superusers. */
@@ -929,7 +931,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, uint64 *processed)
                        select->targetList = targetList;
                        select->fromClause = list_make1(from);
 
-                       query = (Node *) select;
+                       query = makeNode(RawStmt);
+                       query->stmt = (Node *) select;
+                       query->stmt_location = stmt_location;
+                       query->stmt_len = stmt_len;
 
                        /*
                         * Close the relation for now, but keep the lock on it to prevent
@@ -945,7 +950,11 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, uint64 *processed)
        {
                Assert(stmt->query);
 
-               query = stmt->query;
+               query = makeNode(RawStmt);
+               query->stmt = stmt->query;
+               query->stmt_location = stmt_location;
+               query->stmt_len = stmt_len;
+
                relid = InvalidOid;
                rel = NULL;
        }
@@ -981,8 +990,6 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, uint64 *processed)
         */
        if (rel != NULL)
                heap_close(rel, (is_from ? NoLock : AccessShareLock));
-
-       return relid;
 }
 
 /*
@@ -1364,8 +1371,8 @@ static CopyState
 BeginCopy(ParseState *pstate,
                  bool is_from,
                  Relation rel,
-                 Node *raw_query,
-                 const Oid queryRelId,
+                 RawStmt *raw_query,
+                 Oid queryRelId,
                  List *attnamelist,
                  List *options)
 {
@@ -1456,7 +1463,7 @@ BeginCopy(ParseState *pstate,
                 * function and is executed repeatedly.  (See also the same hack in
                 * DECLARE CURSOR and PREPARE.)  XXX FIXME someday.
                 */
-               rewritten = pg_analyze_and_rewrite((Node *) copyObject(raw_query),
+               rewritten = pg_analyze_and_rewrite((RawStmt *) copyObject(raw_query),
                                                                                   pstate->p_sourcetext, NULL, 0);
 
                /* check that we got back something we can work with */
@@ -1747,8 +1754,8 @@ EndCopy(CopyState cstate)
 static CopyState
 BeginCopyTo(ParseState *pstate,
                        Relation rel,
-                       Node *query,
-                       const Oid queryRelId,
+                       RawStmt *query,
+                       Oid queryRelId,
                        const char *filename,
                        bool is_program,
                        List *attnamelist,
index 57ef9817653a1e9bed718c2b3053a5ee4115201c..cee3b4d50b5d5a906c24c000eec005bcd7ed202c 100644 (file)
@@ -326,7 +326,7 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
                query = (Query *) linitial(rewritten);
                Assert(query->commandType == CMD_SELECT);
 
-               /* plan the query */
+               /* plan the query --- note we disallow parallelism */
                plan = pg_plan_query(query, 0, params);
 
                /*
index c762fb07d4db0ee3c5f1984e20915e827813910e..ee7046c47b922e059b7d191dbaf6872ed82bea9a 100644 (file)
@@ -53,7 +53,8 @@ explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
 #define X_CLOSE_IMMEDIATE 2
 #define X_NOWHITESPACE 4
 
-static void ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
+static void ExplainOneQuery(Query *query, int cursorOptions,
+                               IntoClause *into, ExplainState *es,
                                const char *queryString, ParamListInfo params);
 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
                                ExplainState *es);
@@ -245,7 +246,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
                /* Explain every plan */
                foreach(l, rewritten)
                {
-                       ExplainOneQuery((Query *) lfirst(l), NULL, es,
+                       ExplainOneQuery((Query *) lfirst(l),
+                                                       CURSOR_OPT_PARALLEL_OK, NULL, es,
                                                        queryString, params);
 
                        /* Separate plans with an appropriate separator */
@@ -329,7 +331,8 @@ ExplainResultDesc(ExplainStmt *stmt)
  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
  */
 static void
-ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
+ExplainOneQuery(Query *query, int cursorOptions,
+                               IntoClause *into, ExplainState *es,
                                const char *queryString, ParamListInfo params)
 {
        /* planner will not cope with utility statements */
@@ -341,7 +344,8 @@ ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
 
        /* if an advisor plugin is present, let it manage things */
        if (ExplainOneQuery_hook)
-               (*ExplainOneQuery_hook) (query, into, es, queryString, params);
+               (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
+                                                                queryString, params);
        else
        {
                PlannedStmt *plan;
@@ -351,7 +355,7 @@ ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
                INSTR_TIME_SET_CURRENT(planstart);
 
                /* plan the query */
-               plan = pg_plan_query(query, into ? 0 : CURSOR_OPT_PARALLEL_OK, params);
+               plan = pg_plan_query(query, cursorOptions, params);
 
                INSTR_TIME_SET_CURRENT(planduration);
                INSTR_TIME_SUBTRACT(planduration, planstart);
@@ -385,6 +389,8 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
                 * We have to rewrite the contained SELECT and then pass it back to
                 * ExplainOneQuery.  It's probably not really necessary to copy the
                 * contained parsetree another time, but let's be safe.
+                *
+                * Like ExecCreateTableAs, disallow parallelism in the plan.
                 */
                CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
                List       *rewritten;
@@ -392,7 +398,28 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
                Assert(IsA(ctas->query, Query));
                rewritten = QueryRewrite((Query *) copyObject(ctas->query));
                Assert(list_length(rewritten) == 1);
-               ExplainOneQuery((Query *) linitial(rewritten), ctas->into, es,
+               ExplainOneQuery((Query *) linitial(rewritten),
+                                               0, ctas->into, es,
+                                               queryString, params);
+       }
+       else if (IsA(utilityStmt, DeclareCursorStmt))
+       {
+               /*
+                * Likewise for DECLARE CURSOR.
+                *
+                * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
+                * actually run the query.  This is different from pre-8.3 behavior
+                * but seems more useful than not running the query.  No cursor will
+                * be created, however.
+                */
+               DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
+               List       *rewritten;
+
+               Assert(IsA(dcs->query, Query));
+               rewritten = QueryRewrite((Query *) copyObject(dcs->query));
+               Assert(list_length(rewritten) == 1);
+               ExplainOneQuery((Query *) linitial(rewritten),
+                                               dcs->options, NULL, es,
                                                queryString, params);
        }
        else if (IsA(utilityStmt, ExecuteStmt))
@@ -423,11 +450,6 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt,
  * in which case executing the query should result in creating that table.
  *
- * Since we ignore any DeclareCursorStmt that might be attached to the query,
- * if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll actually run the
- * query.  This is different from pre-8.3 behavior but seems more useful than
- * not running the query.  No cursor will be created, however.
- *
  * This is exported because it's called back from prepare.c in the
  * EXPLAIN EXECUTE case, and because an index advisor plugin would need
  * to call it.
@@ -444,6 +466,8 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
        int                     eflags;
        int                     instrument_option = 0;
 
+       Assert(plannedstmt->commandType != CMD_UTILITY);
+
        if (es->analyze && es->timing)
                instrument_option |= INSTRUMENT_TIMER;
        else if (es->analyze)
index be521484d0895ee56ab610636cd0fe34da9b77a8..967b52a133fe3dfd4d91672ea043a35f8a0215d1 100644 (file)
@@ -712,7 +712,7 @@ execute_sql_string(const char *sql, const char *filename)
         */
        foreach(lc1, raw_parsetree_list)
        {
-               Node       *parsetree = (Node *) lfirst(lc1);
+               RawStmt    *parsetree = (RawStmt *) lfirst(lc1);
                List       *stmt_list;
                ListCell   *lc2;
 
@@ -724,23 +724,17 @@ execute_sql_string(const char *sql, const char *filename)
 
                foreach(lc2, stmt_list)
                {
-                       Node       *stmt = (Node *) lfirst(lc2);
-
-                       if (IsA(stmt, TransactionStmt))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                errmsg("transaction control statements are not allowed within an extension script")));
+                       PlannedStmt *stmt = (PlannedStmt *) lfirst(lc2);
 
                        CommandCounterIncrement();
 
                        PushActiveSnapshot(GetTransactionSnapshot());
 
-                       if (IsA(stmt, PlannedStmt) &&
-                               ((PlannedStmt *) stmt)->utilityStmt == NULL)
+                       if (stmt->utilityStmt == NULL)
                        {
                                QueryDesc  *qdesc;
 
-                               qdesc = CreateQueryDesc((PlannedStmt *) stmt,
+                               qdesc = CreateQueryDesc(stmt,
                                                                                sql,
                                                                                GetActiveSnapshot(), NULL,
                                                                                dest, NULL, 0);
@@ -754,6 +748,11 @@ execute_sql_string(const char *sql, const char *filename)
                        }
                        else
                        {
+                               if (IsA(stmt->utilityStmt, TransactionStmt))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                        errmsg("transaction control statements are not allowed within an extension script")));
+
                                ProcessUtility(stmt,
                                                           sql,
                                                           PROCESS_UTILITY_QUERY,
@@ -1434,7 +1433,8 @@ CreateExtensionInternal(char *extensionName,
                        csstmt->authrole = NULL;        /* will be created by current user */
                        csstmt->schemaElts = NIL;
                        csstmt->if_not_exists = false;
-                       CreateSchemaCommand(csstmt, NULL);
+                       CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
+                                                               -1, -1);
 
                        /*
                         * CreateSchemaCommand includes CommandCounterIncrement, so new
index 06b4bc3ba9a05d0f76fa8ed4ab9c659d906c5878..476a023ec54c91f4eec31a8b7c54692d4ff97f26 100644 (file)
@@ -1572,7 +1572,9 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
                 */
                foreach(lc2, raw_parsetree_list)
                {
-                       CreateForeignTableStmt *cstmt = lfirst(lc2);
+                       RawStmt    *rs = (RawStmt *) lfirst(lc2);
+                       CreateForeignTableStmt *cstmt = (CreateForeignTableStmt *) rs->stmt;
+                       PlannedStmt *pstmt;
 
                        /*
                         * Because we only allow CreateForeignTableStmt, we can skip parse
@@ -1593,8 +1595,16 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
                        /* Ensure creation schema is the one given in IMPORT statement */
                        cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
 
+                       /* No planning needed, just make a wrapper PlannedStmt */
+                       pstmt = makeNode(PlannedStmt);
+                       pstmt->commandType = CMD_UTILITY;
+                       pstmt->canSetTag = false;
+                       pstmt->utilityStmt = (Node *) cstmt;
+                       pstmt->stmt_location = rs->stmt_location;
+                       pstmt->stmt_len = rs->stmt_len;
+
                        /* Execute statement */
-                       ProcessUtility((Node *) cstmt,
+                       ProcessUtility(pstmt,
                                                   cmd,
                                                   PROCESS_UTILITY_SUBCOMMAND, NULL,
                                                   None_Receiver, NULL);
index 71640b7748f31908b4baf0f1992a8504ffa44f29..1d3e39299b9d405751a96286b6c22201e48a129d 100644 (file)
@@ -27,7 +27,9 @@
 #include "commands/portalcmds.h"
 #include "executor/executor.h"
 #include "executor/tstoreReceiver.h"
+#include "rewrite/rewriteHandler.h"
 #include "tcop/pquery.h"
+#include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 #include "utils/snapmgr.h"
 
 /*
  * PerformCursorOpen
  *             Execute SQL DECLARE CURSOR command.
- *
- * The query has already been through parse analysis, rewriting, and planning.
- * When it gets here, it looks like a SELECT PlannedStmt, except that the
- * utilityStmt field is set.
  */
 void
-PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
+PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params,
                                  const char *queryString, bool isTopLevel)
 {
-       DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt;
+       Query      *query = (Query *) cstmt->query;
+       List       *rewritten;
+       PlannedStmt *plan;
        Portal          portal;
        MemoryContext oldContext;
 
-       if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
-               elog(ERROR, "PerformCursorOpen called for non-cursor query");
+       Assert(IsA(query, Query));      /* else parse analysis wasn't done */
 
        /*
         * Disallow empty-string cursor name (conflicts with protocol-level
@@ -68,6 +67,32 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
        if (!(cstmt->options & CURSOR_OPT_HOLD))
                RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
 
+       /*
+        * Parse analysis was done already, but we still have to run the rule
+        * rewriter.  We do not do AcquireRewriteLocks: we assume the query either
+        * came straight from the parser, or suitable locks were acquired by
+        * plancache.c.
+        *
+        * Because the rewriter and planner tend to scribble on the input, we make
+        * a preliminary copy of the source querytree.  This prevents problems in
+        * the case that the DECLARE CURSOR is in a portal or plpgsql function and
+        * is executed repeatedly.  (See also the same hack in EXPLAIN and
+        * PREPARE.)  XXX FIXME someday.
+        */
+       rewritten = QueryRewrite((Query *) copyObject(query));
+
+       /* SELECT should never rewrite to more or less than one query */
+       if (list_length(rewritten) != 1)
+               elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
+
+       query = (Query *) linitial(rewritten);
+
+       if (query->commandType != CMD_SELECT)
+               elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
+
+       /* Plan the query, applying the specified options */
+       plan = pg_plan_query(query, cstmt->options, params);
+
        /*
         * Create a portal and copy the plan and queryString into its memory.
         */
@@ -75,8 +100,7 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
 
        oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
-       stmt = copyObject(stmt);
-       stmt->utilityStmt = NULL;       /* make it look like plain SELECT */
+       plan = copyObject(plan);
 
        queryString = pstrdup(queryString);
 
@@ -84,7 +108,7 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
                                          NULL,
                                          queryString,
                                          "SELECT", /* cursor's query is always a SELECT */
-                                         list_make1(stmt),
+                                         list_make1(plan),
                                          NULL);
 
        /*----------
@@ -111,8 +135,8 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
        portal->cursorOptions = cstmt->options;
        if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
        {
-               if (stmt->rowMarks == NIL &&
-                       ExecSupportsBackwardScan(stmt->planTree))
+               if (plan->rowMarks == NIL &&
+                       ExecSupportsBackwardScan(plan->planTree))
                        portal->cursorOptions |= CURSOR_OPT_SCROLL;
                else
                        portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
index d768cf8dda39a3418bba29094551eb15af449cba..1ff41661a551c586b4198e3dc14bf3deb317f384 100644 (file)
@@ -52,8 +52,10 @@ static Datum build_regtype_array(Oid *param_types, int num_params);
  * Implements the 'PREPARE' utility statement.
  */
 void
-PrepareQuery(PrepareStmt *stmt, const char *queryString)
+PrepareQuery(PrepareStmt *stmt, const char *queryString,
+                        int stmt_location, int stmt_len)
 {
+       RawStmt    *rawstmt;
        CachedPlanSource *plansource;
        Oid                *argtypes = NULL;
        int                     nargs;
@@ -70,11 +72,23 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
                                (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
                                 errmsg("invalid statement name: must not be empty")));
 
+       /*
+        * Need to wrap the contained statement in a RawStmt node to pass it to
+        * parse analysis.
+        *
+        * Because parse analysis scribbles on the raw querytree, we must make a
+        * copy to ensure we don't modify the passed-in tree.  FIXME someday.
+        */
+       rawstmt = makeNode(RawStmt);
+       rawstmt->stmt = (Node *) copyObject(stmt->query);
+       rawstmt->stmt_location = stmt_location;
+       rawstmt->stmt_len = stmt_len;
+
        /*
         * Create the CachedPlanSource before we do parse analysis, since it needs
         * to see the unmodified raw parse tree.
         */
-       plansource = CreateCachedPlan(stmt->query, queryString,
+       plansource = CreateCachedPlan(rawstmt, queryString,
                                                                  CreateCommandTag(stmt->query));
 
        /* Transform list of TypeNames to array of type OIDs */
@@ -108,12 +122,8 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
         * Analyze the statement using these parameter types (any parameters
         * passed in from above us will not be visible to it), allowing
         * information about unknown parameters to be deduced from context.
-        *
-        * Because parse analysis scribbles on the raw querytree, we must make a
-        * copy to ensure we don't modify the passed-in tree.  FIXME someday.
         */
-       query = parse_analyze_varparams((Node *) copyObject(stmt->query),
-                                                                       queryString,
+       query = parse_analyze_varparams(rawstmt, queryString,
                                                                        &argtypes, &nargs);
 
        /*
@@ -256,9 +266,8 @@ ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
                                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                         errmsg("prepared statement is not a SELECT")));
                pstmt = (PlannedStmt *) linitial(plan_list);
-               if (!IsA(pstmt, PlannedStmt) ||
-                       pstmt->commandType != CMD_SELECT ||
-                       pstmt->utilityStmt != NULL)
+               Assert(IsA(pstmt, PlannedStmt));
+               if (pstmt->commandType != CMD_SELECT)
                        ereport(ERROR,
                                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                         errmsg("prepared statement is not a SELECT")));
@@ -664,10 +673,11 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
        {
                PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
 
-               if (IsA(pstmt, PlannedStmt))
+               Assert(IsA(pstmt, PlannedStmt));
+               if (pstmt->commandType != CMD_UTILITY)
                        ExplainOnePlan(pstmt, into, es, query_string, paramLI, NULL);
                else
-                       ExplainOneUtility((Node *) pstmt, into, es, query_string, paramLI);
+                       ExplainOneUtility(pstmt->utilityStmt, into, es, query_string, paramLI);
 
                /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
 
index c475613b1c5cd1cfc6b311991798589e03a3ad06..c3b37b2625901f1a7756b4332c37d856d830b6fc 100644 (file)
@@ -40,9 +40,16 @@ static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerI
 
 /*
  * CREATE SCHEMA
+ *
+ * Note: caller should pass in location information for the whole
+ * CREATE SCHEMA statement, which in turn we pass down as the location
+ * of the component commands.  This comports with our general plan of
+ * reporting location/len for the whole command even when executing
+ * a subquery.
  */
 Oid
-CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
+CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
+                                       int stmt_location, int stmt_len)
 {
        const char *schemaName = stmt->schemaname;
        Oid                     namespaceId;
@@ -172,14 +179,24 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
        foreach(parsetree_item, parsetree_list)
        {
                Node       *stmt = (Node *) lfirst(parsetree_item);
+               PlannedStmt *wrapper;
+
+               /* need to make a wrapper PlannedStmt */
+               wrapper = makeNode(PlannedStmt);
+               wrapper->commandType = CMD_UTILITY;
+               wrapper->canSetTag = false;
+               wrapper->utilityStmt = stmt;
+               wrapper->stmt_location = stmt_location;
+               wrapper->stmt_len = stmt_len;
 
                /* do this step */
-               ProcessUtility(stmt,
+               ProcessUtility(wrapper,
                                           queryString,
                                           PROCESS_UTILITY_SUBCOMMAND,
                                           NULL,
                                           None_Receiver,
                                           NULL);
+
                /* make sure later steps can see the object created here */
                CommandCounterIncrement();
        }
index f913e87bc8b9d32d87e4b6ff28294fcbd2695568..e633a50dd2db4f08f3edb7b05273bd9648e4992b 100644 (file)
@@ -9285,7 +9285,8 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
        querytree_list = NIL;
        foreach(list_item, raw_parsetree_list)
        {
-               Node       *stmt = (Node *) lfirst(list_item);
+               RawStmt    *rs = (RawStmt *) lfirst(list_item);
+               Node       *stmt = rs->stmt;
 
                if (IsA(stmt, IndexStmt))
                        querytree_list = lappend(querytree_list,
index 61666ad19238cc4bc7f8e5a2658f0355471229da..b404d1ea16ff99ef1198eb208aab28f913fa00cc 100644 (file)
@@ -1078,6 +1078,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
                AlterTableStmt *atstmt = makeNode(AlterTableStmt);
                AlterTableCmd *atcmd = makeNode(AlterTableCmd);
                Constraint *fkcon = makeNode(Constraint);
+               PlannedStmt *wrapper = makeNode(PlannedStmt);
 
                ereport(NOTICE,
                                (errmsg("converting trigger group into constraint \"%s\" %s",
@@ -1167,8 +1168,15 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
                fkcon->skip_validation = false;
                fkcon->initially_valid = true;
 
+               /* finally, wrap it in a dummy PlannedStmt */
+               wrapper->commandType = CMD_UTILITY;
+               wrapper->canSetTag = false;
+               wrapper->utilityStmt = (Node *) atstmt;
+               wrapper->stmt_location = -1;
+               wrapper->stmt_len = -1;
+
                /* ... and execute it */
-               ProcessUtility((Node *) atstmt,
+               ProcessUtility(wrapper,
                                           "(generated ALTER TABLE ADD FOREIGN KEY command)",
                                           PROCESS_UTILITY_SUBCOMMAND, NULL,
                                           None_Receiver, NULL);
index e4be5c533205bfb2a080dd7401fd23a410ecf2ec..1f008b075666342c0cbae3486c3fbc4ea9080d3a 100644 (file)
@@ -414,8 +414,10 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
  *             Execute a CREATE VIEW command.
  */
 ObjectAddress
-DefineView(ViewStmt *stmt, const char *queryString)
+DefineView(ViewStmt *stmt, const char *queryString,
+                  int stmt_location, int stmt_len)
 {
+       RawStmt    *rawstmt;
        Query      *viewParse;
        RangeVar   *view;
        ListCell   *cell;
@@ -429,8 +431,12 @@ DefineView(ViewStmt *stmt, const char *queryString)
         * Since parse analysis scribbles on its input, copy the raw parse tree;
         * this ensures we don't corrupt a prepared statement, for example.
         */
-       viewParse = parse_analyze((Node *) copyObject(stmt->query),
-                                                         queryString, NULL, 0);
+       rawstmt = makeNode(RawStmt);
+       rawstmt->stmt = (Node *) copyObject(stmt->query);
+       rawstmt->stmt_location = stmt_location;
+       rawstmt->stmt_len = stmt_len;
+
+       viewParse = parse_analyze(rawstmt, queryString, NULL, 0);
 
        /*
         * The grammar should ensure that the result is a single SELECT Query.
@@ -443,8 +449,7 @@ DefineView(ViewStmt *stmt, const char *queryString)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("views must not contain SELECT INTO")));
-       if (viewParse->commandType != CMD_SELECT ||
-               viewParse->utilityStmt != NULL)
+       if (viewParse->commandType != CMD_SELECT)
                elog(ERROR, "unexpected parse analysis result");
 
        /*
index 6cf62daab8a58e384f88f53127de1fa5b3087ca6..e01fe6da96492f46d034c9522f88062646437d1c 100644 (file)
@@ -156,13 +156,15 @@ ExecSerializePlan(Plan *plan, EState *estate)
        pstmt->planTree = plan;
        pstmt->rtable = estate->es_range_table;
        pstmt->resultRelations = NIL;
-       pstmt->utilityStmt = NULL;
        pstmt->subplans = NIL;
        pstmt->rewindPlanIDs = NULL;
        pstmt->rowMarks = NIL;
        pstmt->relationOids = NIL;
        pstmt->invalItems = NIL;        /* workers can't replan anyway... */
        pstmt->nParamExec = estate->es_plannedstmt->nParamExec;
+       pstmt->utilityStmt = NULL;
+       pstmt->stmt_location = -1;
+       pstmt->stmt_len = -1;
 
        /* Return serialized copy of our dummy PlannedStmt. */
        return nodeToString(pstmt);
index 039defa7b85f681f5398838b7946fd9941ce9402..e4a1da4dbbf4b2be0475fedfcfe64be498f13094 100644 (file)
@@ -66,7 +66,7 @@ typedef struct execution_state
        ExecStatus      status;
        bool            setsResult;             /* true if this query produces func's result */
        bool            lazyEval;               /* true if should fetch one row at a time */
-       Node       *stmt;                       /* PlannedStmt or utility statement */
+       PlannedStmt *stmt;                      /* plan for this query */
        QueryDesc  *qd;                         /* null unless status == RUN */
 } execution_state;
 
@@ -487,45 +487,56 @@ init_execution_state(List *queryTree_list,
                foreach(lc2, qtlist)
                {
                        Query      *queryTree = (Query *) lfirst(lc2);
-                       Node       *stmt;
+                       PlannedStmt *stmt;
                        execution_state *newes;
 
                        Assert(IsA(queryTree, Query));
 
                        /* Plan the query if needed */
                        if (queryTree->commandType == CMD_UTILITY)
-                               stmt = queryTree->utilityStmt;
+                       {
+                               /* Utility commands require no planning. */
+                               stmt = makeNode(PlannedStmt);
+                               stmt->commandType = CMD_UTILITY;
+                               stmt->canSetTag = queryTree->canSetTag;
+                               stmt->utilityStmt = queryTree->utilityStmt;
+                               stmt->stmt_location = queryTree->stmt_location;
+                               stmt->stmt_len = queryTree->stmt_len;
+                       }
                        else
-                               stmt = (Node *) pg_plan_query(queryTree,
+                               stmt = pg_plan_query(queryTree,
                                                  fcache->readonly_func ? CURSOR_OPT_PARALLEL_OK : 0,
-                                                                                         NULL);
+                                                                        NULL);
 
                        /*
                         * Precheck all commands for validity in a function.  This should
                         * generally match the restrictions spi.c applies.
                         */
-                       if (IsA(stmt, CopyStmt) &&
-                               ((CopyStmt *) stmt)->filename == NULL)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                       if (stmt->commandType == CMD_UTILITY)
+                       {
+                               if (IsA(stmt->utilityStmt, CopyStmt) &&
+                                       ((CopyStmt *) stmt->utilityStmt)->filename == NULL)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                        errmsg("cannot COPY to/from client in a SQL function")));
 
-                       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",
-                                                               CreateCommandTag(stmt))));
+                               if (IsA(stmt->utilityStmt, TransactionStmt))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                       /* translator: %s is a SQL statement name */
+                                                        errmsg("%s is not allowed in a SQL function",
+                                                                       CreateCommandTag(stmt->utilityStmt))));
+                       }
 
                        if (fcache->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",
-                                                         CreateCommandTag(stmt))));
+                                                         CreateCommandTag((Node *) stmt))));
 
                        if (IsInParallelMode() && !CommandIsReadOnly(stmt))
-                               PreventCommandIfParallelMode(CreateCommandTag(stmt));
+                               PreventCommandIfParallelMode(CreateCommandTag((Node *) stmt));
 
                        /* OK, build the execution_state for this query */
                        newes = (execution_state *) palloc(sizeof(execution_state));
@@ -569,15 +580,9 @@ init_execution_state(List *queryTree_list,
        {
                lasttages->setsResult = true;
                if (lazyEvalOK &&
-                       IsA(lasttages->stmt, PlannedStmt))
-               {
-                       PlannedStmt *ps = (PlannedStmt *) lasttages->stmt;
-
-                       if (ps->commandType == CMD_SELECT &&
-                               ps->utilityStmt == NULL &&
-                               !ps->hasModifyingCTE)
-                               fcache->lazyEval = lasttages->lazyEval = true;
-               }
+                       lasttages->stmt->commandType == CMD_SELECT &&
+                       !lasttages->stmt->hasModifyingCTE)
+                       fcache->lazyEval = lasttages->lazyEval = true;
        }
 
        return eslist;
@@ -704,7 +709,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
        flat_query_list = NIL;
        foreach(lc, raw_parsetree_list)
        {
-               Node       *parsetree = (Node *) lfirst(lc);
+               RawStmt    *parsetree = (RawStmt *) lfirst(lc);
                List       *queryTree_sublist;
 
                queryTree_sublist = pg_analyze_and_rewrite_params(parsetree,
@@ -801,22 +806,15 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
        else
                dest = None_Receiver;
 
-       if (IsA(es->stmt, PlannedStmt))
-               es->qd = CreateQueryDesc((PlannedStmt *) es->stmt,
-                                                                fcache->src,
-                                                                GetActiveSnapshot(),
-                                                                InvalidSnapshot,
-                                                                dest,
-                                                                fcache->paramLI, 0);
-       else
-               es->qd = CreateUtilityQueryDesc(es->stmt,
-                                                                               fcache->src,
-                                                                               GetActiveSnapshot(),
-                                                                               dest,
-                                                                               fcache->paramLI);
+       es->qd = CreateQueryDesc(es->stmt,
+                                                        fcache->src,
+                                                        GetActiveSnapshot(),
+                                                        InvalidSnapshot,
+                                                        dest,
+                                                        fcache->paramLI, 0);
 
        /* Utility commands don't need Executor. */
-       if (es->qd->utilitystmt == NULL)
+       if (es->qd->operation != CMD_UTILITY)
        {
                /*
                 * In lazyEval mode, do not let the executor set up an AfterTrigger
@@ -844,12 +842,9 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
 {
        bool            result;
 
-       if (es->qd->utilitystmt)
+       if (es->qd->operation == CMD_UTILITY)
        {
-               /* ProcessUtility needs the PlannedStmt for DECLARE CURSOR */
-               ProcessUtility((es->qd->plannedstmt ?
-                                               (Node *) es->qd->plannedstmt :
-                                               es->qd->utilitystmt),
+               ProcessUtility(es->qd->plannedstmt,
                                           fcache->src,
                                           PROCESS_UTILITY_QUERY,
                                           es->qd->params,
@@ -882,7 +877,7 @@ postquel_end(execution_state *es)
        es->status = F_EXEC_DONE;
 
        /* Utility commands don't need Executor. */
-       if (es->qd->utilitystmt == NULL)
+       if (es->qd->operation != CMD_UTILITY)
        {
                ExecutorFinish(es->qd);
                ExecutorEnd(es->qd);
@@ -1576,8 +1571,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
         * entities.
         */
        if (parse &&
-               parse->commandType == CMD_SELECT &&
-               parse->utilityStmt == NULL)
+               parse->commandType == CMD_SELECT)
        {
                tlist_ptr = &parse->targetList;
                tlist = parse->targetList;
index ee7a7e2d78c2e4708ec507ec2bba1ef6c6dd5066..7bd37283b7f809560ed314078dabc4838520038c 100644 (file)
@@ -1232,7 +1232,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
        if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
        {
                if (list_length(stmt_list) == 1 &&
-                       IsA((Node *) linitial(stmt_list), PlannedStmt) &&
+                ((PlannedStmt *) linitial(stmt_list))->commandType != CMD_UTILITY &&
                        ((PlannedStmt *) linitial(stmt_list))->rowMarks == NIL &&
                        ExecSupportsBackwardScan(((PlannedStmt *) linitial(stmt_list))->planTree))
                        portal->cursorOptions |= CURSOR_OPT_SCROLL;
@@ -1248,7 +1248,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
        if (portal->cursorOptions & CURSOR_OPT_SCROLL)
        {
                if (list_length(stmt_list) == 1 &&
-                       IsA((Node *) linitial(stmt_list), PlannedStmt) &&
+                ((PlannedStmt *) linitial(stmt_list))->commandType != CMD_UTILITY &&
                        ((PlannedStmt *) linitial(stmt_list))->rowMarks != NIL)
                        ereport(ERROR,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -1270,7 +1270,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
 
                foreach(lc, stmt_list)
                {
-                       Node       *pstmt = (Node *) lfirst(lc);
+                       PlannedStmt *pstmt = (PlannedStmt *) lfirst(lc);
 
                        if (!CommandIsReadOnly(pstmt))
                        {
@@ -1279,9 +1279,9 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                        /* translator: %s is a SQL statement name */
                                           errmsg("%s is not allowed in a non-volatile function",
-                                                         CreateCommandTag(pstmt))));
+                                                         CreateCommandTag((Node *) pstmt))));
                                else
-                                       PreventCommandIfParallelMode(CreateCommandTag(pstmt));
+                                       PreventCommandIfParallelMode(CreateCommandTag((Node *) pstmt));
                        }
                }
        }
@@ -1757,7 +1757,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
 
        foreach(list_item, raw_parsetree_list)
        {
-               Node       *parsetree = (Node *) lfirst(list_item);
+               RawStmt    *parsetree = (RawStmt *) lfirst(list_item);
                List       *stmt_list;
                CachedPlanSource *plansource;
 
@@ -1767,7 +1767,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
                 */
                plansource = CreateCachedPlan(parsetree,
                                                                          src,
-                                                                         CreateCommandTag(parsetree));
+                                                                         CreateCommandTag(parsetree->stmt));
 
                /*
                 * Parameter datatypes are driven by parserSetup hook if provided,
@@ -1859,12 +1859,12 @@ _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan)
 
        foreach(list_item, raw_parsetree_list)
        {
-               Node       *parsetree = (Node *) lfirst(list_item);
+               RawStmt    *parsetree = (RawStmt *) lfirst(list_item);
                CachedPlanSource *plansource;
 
                plansource = CreateOneShotCachedPlan(parsetree,
                                                                                         src,
-                                                                                        CreateCommandTag(parsetree));
+                                                                                 CreateCommandTag(parsetree->stmt));
 
                plancache_list = lappend(plancache_list, plansource);
        }
@@ -1959,7 +1959,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                 */
                if (plan->oneshot)
                {
-                       Node       *parsetree = plansource->raw_parse_tree;
+                       RawStmt    *parsetree = plansource->raw_parse_tree;
                        const char *src = plansource->query_string;
                        List       *stmt_list;
 
@@ -2018,26 +2018,19 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 
                foreach(lc2, stmt_list)
                {
-                       Node       *stmt = (Node *) lfirst(lc2);
-                       bool            canSetTag;
+                       PlannedStmt *stmt = (PlannedStmt *) lfirst(lc2);
+                       bool            canSetTag = stmt->canSetTag;
                        DestReceiver *dest;
 
                        _SPI_current->processed = 0;
                        _SPI_current->lastoid = InvalidOid;
                        _SPI_current->tuptable = NULL;
 
-                       if (IsA(stmt, PlannedStmt))
+                       if (stmt->utilityStmt)
                        {
-                               canSetTag = ((PlannedStmt *) stmt)->canSetTag;
-                       }
-                       else
-                       {
-                               /* utilities are canSetTag if only thing in list */
-                               canSetTag = (list_length(stmt_list) == 1);
-
-                               if (IsA(stmt, CopyStmt))
+                               if (IsA(stmt->utilityStmt, CopyStmt))
                                {
-                                       CopyStmt   *cstmt = (CopyStmt *) stmt;
+                                       CopyStmt   *cstmt = (CopyStmt *) stmt->utilityStmt;
 
                                        if (cstmt->filename == NULL)
                                        {
@@ -2045,7 +2038,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                                                goto fail;
                                        }
                                }
-                               else if (IsA(stmt, TransactionStmt))
+                               else if (IsA(stmt->utilityStmt, TransactionStmt))
                                {
                                        my_res = SPI_ERROR_TRANSACTION;
                                        goto fail;
@@ -2057,10 +2050,10 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                /* translator: %s is a SQL statement name */
                                           errmsg("%s is not allowed in a non-volatile function",
-                                                         CreateCommandTag(stmt))));
+                                                         CreateCommandTag((Node *) stmt))));
 
                        if (IsInParallelMode() && !CommandIsReadOnly(stmt))
-                               PreventCommandIfParallelMode(CreateCommandTag(stmt));
+                               PreventCommandIfParallelMode(CreateCommandTag((Node *) stmt));
 
                        /*
                         * If not read-only mode, advance the command counter before each
@@ -2074,8 +2067,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 
                        dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone);
 
-                       if (IsA(stmt, PlannedStmt) &&
-                               ((PlannedStmt *) stmt)->utilityStmt == NULL)
+                       if (stmt->utilityStmt == NULL)
                        {
                                QueryDesc  *qdesc;
                                Snapshot        snap;
@@ -2085,7 +2077,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                                else
                                        snap = InvalidSnapshot;
 
-                               qdesc = CreateQueryDesc((PlannedStmt *) stmt,
+                               qdesc = CreateQueryDesc(stmt,
                                                                                plansource->query_string,
                                                                                snap, crosscheck_snapshot,
                                                                                dest,
@@ -2116,9 +2108,9 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                                 * Some utility statements return a row count, even though the
                                 * tuples are not returned to the caller.
                                 */
-                               if (IsA(stmt, CreateTableAsStmt))
+                               if (IsA(stmt->utilityStmt, CreateTableAsStmt))
                                {
-                                       CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt;
+                                       CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
 
                                        if (strncmp(completionTag, "SELECT ", 7) == 0)
                                                _SPI_current->processed =
@@ -2141,7 +2133,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                                        if (ctastmt->is_select_into)
                                                res = SPI_OK_SELINTO;
                                }
-                               else if (IsA(stmt, CopyStmt))
+                               else if (IsA(stmt->utilityStmt, CopyStmt))
                                {
                                        Assert(strncmp(completionTag, "COPY ", 5) == 0);
                                        _SPI_current->processed = pg_strtouint64(completionTag + 5,
@@ -2270,7 +2262,6 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
        switch (operation)
        {
                case CMD_SELECT:
-                       Assert(queryDesc->plannedstmt->utilityStmt == NULL);
                        if (queryDesc->dest->mydest != DestSPI)
                        {
                                /* Don't return SPI_OK_SELECT if we're discarding result */
index 930f2f1d5f40f6aaa7f40f1161dccb2022813c93..7107bbf16428feb860467ed05c91be97716f591e 100644 (file)
@@ -90,13 +90,15 @@ _copyPlannedStmt(const PlannedStmt *from)
        COPY_NODE_FIELD(planTree);
        COPY_NODE_FIELD(rtable);
        COPY_NODE_FIELD(resultRelations);
-       COPY_NODE_FIELD(utilityStmt);
        COPY_NODE_FIELD(subplans);
        COPY_BITMAPSET_FIELD(rewindPlanIDs);
        COPY_NODE_FIELD(rowMarks);
        COPY_NODE_FIELD(relationOids);
        COPY_NODE_FIELD(invalItems);
        COPY_SCALAR_FIELD(nParamExec);
+       COPY_NODE_FIELD(utilityStmt);
+       COPY_LOCATION_FIELD(stmt_location);
+       COPY_LOCATION_FIELD(stmt_len);
 
        return newnode;
 }
@@ -2767,6 +2769,20 @@ _copyQuery(const Query *from)
        COPY_NODE_FIELD(setOperations);
        COPY_NODE_FIELD(constraintDeps);
        COPY_NODE_FIELD(withCheckOptions);
+       COPY_LOCATION_FIELD(stmt_location);
+       COPY_LOCATION_FIELD(stmt_len);
+
+       return newnode;
+}
+
+static RawStmt *
+_copyRawStmt(const RawStmt *from)
+{
+       RawStmt    *newnode = makeNode(RawStmt);
+
+       COPY_NODE_FIELD(stmt);
+       COPY_LOCATION_FIELD(stmt_location);
+       COPY_LOCATION_FIELD(stmt_len);
 
        return newnode;
 }
@@ -4728,6 +4744,9 @@ copyObject(const void *from)
                case T_Query:
                        retval = _copyQuery(from);
                        break;
+               case T_RawStmt:
+                       retval = _copyRawStmt(from);
+                       break;
                case T_InsertStmt:
                        retval = _copyInsertStmt(from);
                        break;
index a27e5edf37233064b048c5441cc16a522bf1fe6e..ec4bbfc770b313bcbd5e539172472efe7fe751e6 100644 (file)
@@ -946,6 +946,18 @@ _equalQuery(const Query *a, const Query *b)
        COMPARE_NODE_FIELD(setOperations);
        COMPARE_NODE_FIELD(constraintDeps);
        COMPARE_NODE_FIELD(withCheckOptions);
+       COMPARE_LOCATION_FIELD(stmt_location);
+       COMPARE_LOCATION_FIELD(stmt_len);
+
+       return true;
+}
+
+static bool
+_equalRawStmt(const RawStmt *a, const RawStmt *b)
+{
+       COMPARE_NODE_FIELD(stmt);
+       COMPARE_LOCATION_FIELD(stmt_location);
+       COMPARE_LOCATION_FIELD(stmt_len);
 
        return true;
 }
@@ -3015,6 +3027,9 @@ equal(const void *a, const void *b)
                case T_Query:
                        retval = _equalQuery(a, b);
                        break;
+               case T_RawStmt:
+                       retval = _equalRawStmt(a, b);
+                       break;
                case T_InsertStmt:
                        retval = _equalInsertStmt(a, b);
                        break;
index 806d0a93aac639d4def0162be3d0f62842702e08..cf0a6059e91f280499946bfe51872423d09d5245 100644 (file)
@@ -252,13 +252,15 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
        WRITE_NODE_FIELD(planTree);
        WRITE_NODE_FIELD(rtable);
        WRITE_NODE_FIELD(resultRelations);
-       WRITE_NODE_FIELD(utilityStmt);
        WRITE_NODE_FIELD(subplans);
        WRITE_BITMAPSET_FIELD(rewindPlanIDs);
        WRITE_NODE_FIELD(rowMarks);
        WRITE_NODE_FIELD(relationOids);
        WRITE_NODE_FIELD(invalItems);
        WRITE_INT_FIELD(nParamExec);
+       WRITE_NODE_FIELD(utilityStmt);
+       WRITE_LOCATION_FIELD(stmt_location);
+       WRITE_LOCATION_FIELD(stmt_len);
 }
 
 /*
@@ -2705,6 +2707,9 @@ _outQuery(StringInfo str, const Query *node)
        WRITE_NODE_FIELD(rowMarks);
        WRITE_NODE_FIELD(setOperations);
        WRITE_NODE_FIELD(constraintDeps);
+       /* withCheckOptions intentionally omitted, see comment in parsenodes.h */
+       WRITE_LOCATION_FIELD(stmt_location);
+       WRITE_LOCATION_FIELD(stmt_len);
 }
 
 static void
index dc40d0181e00dfcf47258dcdba0a2e1f930bb7a0..e02dd94f055b2a1aa8177e715c7a004bd76fac76 100644 (file)
@@ -262,6 +262,9 @@ _readQuery(void)
        READ_NODE_FIELD(rowMarks);
        READ_NODE_FIELD(setOperations);
        READ_NODE_FIELD(constraintDeps);
+       /* withCheckOptions intentionally omitted, see comment in parsenodes.h */
+       READ_LOCATION_FIELD(stmt_location);
+       READ_LOCATION_FIELD(stmt_len);
 
        READ_DONE();
 }
@@ -1415,13 +1418,15 @@ _readPlannedStmt(void)
        READ_NODE_FIELD(planTree);
        READ_NODE_FIELD(rtable);
        READ_NODE_FIELD(resultRelations);
-       READ_NODE_FIELD(utilityStmt);
        READ_NODE_FIELD(subplans);
        READ_BITMAPSET_FIELD(rewindPlanIDs);
        READ_NODE_FIELD(rowMarks);
        READ_NODE_FIELD(relationOids);
        READ_NODE_FIELD(invalItems);
        READ_INT_FIELD(nParamExec);
+       READ_NODE_FIELD(utilityStmt);
+       READ_LOCATION_FIELD(stmt_location);
+       READ_LOCATION_FIELD(stmt_len);
 
        READ_DONE();
 }
index 207290fd2608e9d4a411f8ebea7386d90fb540f6..f936710171c7814dad813e64cc83b25b5b47e739 100644 (file)
@@ -193,11 +193,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
        ListCell   *lp,
                           *lr;
 
-       /* Cursor options may come from caller or from DECLARE CURSOR stmt */
-       if (parse->utilityStmt &&
-               IsA(parse->utilityStmt, DeclareCursorStmt))
-               cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options;
-
        /*
         * Set up global state for this planner invocation.  This data is needed
         * across all levels of sub-Query that might exist in the given command,
@@ -246,7 +241,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
                IsUnderPostmaster &&
                dynamic_shared_memory_type != DSM_IMPL_NONE &&
                parse->commandType == CMD_SELECT &&
-               parse->utilityStmt == NULL &&
                !parse->hasModifyingCTE &&
                max_parallel_workers_per_gather > 0 &&
                !IsParallelWorker() &&
@@ -421,13 +415,16 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
        result->planTree = top_plan;
        result->rtable = glob->finalrtable;
        result->resultRelations = glob->resultRelations;
-       result->utilityStmt = parse->utilityStmt;
        result->subplans = glob->subplans;
        result->rewindPlanIDs = glob->rewindPlanIDs;
        result->rowMarks = glob->finalrowmarks;
        result->relationOids = glob->relationOids;
        result->invalItems = glob->invalItems;
        result->nParamExec = glob->nParamExec;
+       /* utilityStmt should be null, but we might as well copy it */
+       result->utilityStmt = parse->utilityStmt;
+       result->stmt_location = parse->stmt_location;
+       result->stmt_len = parse->stmt_len;
 
        return result;
 }
index be5d3d1104bfa69bd9ade06c84d7d7180be56c56..7cb1bc9a62c5ed05eddd6a32ecd31a153462af29 100644 (file)
@@ -1409,8 +1409,7 @@ is_simple_subquery(Query *subquery, RangeTblEntry *rte,
         * Let's just make sure it's a valid subselect ...
         */
        if (!IsA(subquery, Query) ||
-               subquery->commandType != CMD_SELECT ||
-               subquery->utilityStmt != NULL)
+               subquery->commandType != CMD_SELECT)
                elog(ERROR, "subquery is bogus");
 
        /*
@@ -1744,8 +1743,7 @@ is_simple_union_all(Query *subquery)
 
        /* Let's just make sure it's a valid subselect ... */
        if (!IsA(subquery, Query) ||
-               subquery->commandType != CMD_SELECT ||
-               subquery->utilityStmt != NULL)
+               subquery->commandType != CMD_SELECT)
                elog(ERROR, "subquery is bogus");
 
        /* Is it a set-operation query at all? */
index 10abdc3aff136ff5dc4c347dfbc6984971418456..59ccdf43d4999f1dd7fb3a219e59017809458a40 100644 (file)
@@ -4479,7 +4479,6 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
         */
        if (!IsA(querytree, Query) ||
                querytree->commandType != CMD_SELECT ||
-               querytree->utilityStmt ||
                querytree->hasAggs ||
                querytree->hasWindowFuncs ||
                querytree->hasTargetSRFs ||
@@ -5006,8 +5005,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
         * The single command must be a plain SELECT.
         */
        if (!IsA(querytree, Query) ||
-               querytree->commandType != CMD_SELECT ||
-               querytree->utilityStmt)
+               querytree->commandType != CMD_SELECT)
                goto fail;
 
        /*
index 5116cbb71a27f7693113a601bc1db7d9d1dc5c8b..8278e742f8dcfd9b27d632d39230d808bd80a113 100644 (file)
@@ -48,6 +48,7 @@
 /* Hook for plugins to get control at end of parse analysis */
 post_parse_analyze_hook_type post_parse_analyze_hook = NULL;
 
+static Query *transformOptionalSelectInto(ParseState *pstate, Node *parseTree);
 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
 static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
 static List *transformInsertRow(ParseState *pstate, List *exprlist,
@@ -92,7 +93,7 @@ static bool test_raw_expression_coverage(Node *node, void *context);
  * a dummy CMD_UTILITY Query node.
  */
 Query *
-parse_analyze(Node *parseTree, const char *sourceText,
+parse_analyze(RawStmt *parseTree, const char *sourceText,
                          Oid *paramTypes, int numParams)
 {
        ParseState *pstate = make_parsestate(NULL);
@@ -123,7 +124,7 @@ parse_analyze(Node *parseTree, const char *sourceText,
  * be modified or enlarged (via repalloc).
  */
 Query *
-parse_analyze_varparams(Node *parseTree, const char *sourceText,
+parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
                                                Oid **paramTypes, int *numParams)
 {
        ParseState *pstate = make_parsestate(NULL);
@@ -174,14 +175,35 @@ parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
  * transformTopLevelStmt -
  *       transform a Parse tree into a Query tree.
  *
+ * This function is just responsible for transferring statement location data
+ * from the RawStmt into the finished Query.
+ */
+Query *
+transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree)
+{
+       Query      *result;
+
+       /* We're at top level, so allow SELECT INTO */
+       result = transformOptionalSelectInto(pstate, parseTree->stmt);
+
+       result->stmt_location = parseTree->stmt_location;
+       result->stmt_len = parseTree->stmt_len;
+
+       return result;
+}
+
+/*
+ * transformOptionalSelectInto -
+ *       If SELECT has INTO, convert it to CREATE TABLE AS.
+ *
  * The only thing we do here that we don't do in transformStmt() is to
  * convert SELECT ... INTO into CREATE TABLE AS.  Since utility statements
  * aren't allowed within larger statements, this is only allowed at the top
  * of the parse tree, and so we only try it before entering the recursive
  * transformStmt() processing.
  */
-Query *
-transformTopLevelStmt(ParseState *pstate, Node *parseTree)
+static Query *
+transformOptionalSelectInto(ParseState *pstate, Node *parseTree)
 {
        if (IsA(parseTree, SelectStmt))
        {
@@ -318,11 +340,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
  * Classification here should match transformStmt().
  */
 bool
-analyze_requires_snapshot(Node *parseTree)
+analyze_requires_snapshot(RawStmt *parseTree)
 {
        bool            result;
 
-       switch (nodeTag(parseTree))
+       switch (nodeTag(parseTree->stmt))
        {
                        /*
                         * Optimizable statements
@@ -338,10 +360,6 @@ analyze_requires_snapshot(Node *parseTree)
                         * Special cases
                         */
                case T_DeclareCursorStmt:
-                       /* yes, because it's analyzed just like SELECT */
-                       result = true;
-                       break;
-
                case T_ExplainStmt:
                case T_CreateTableAsStmt:
                        /* yes, because we must analyze the contained statement */
@@ -563,8 +581,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 
                /* The grammar should have produced a SELECT */
                if (!IsA(selectQuery, Query) ||
-                       selectQuery->commandType != CMD_SELECT ||
-                       selectQuery->utilityStmt != NULL)
+                       selectQuery->commandType != CMD_SELECT)
                        elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT");
 
                /*
@@ -2344,17 +2361,17 @@ transformReturningList(ParseState *pstate, List *returningList)
  * transformDeclareCursorStmt -
  *     transform a DECLARE CURSOR Statement
  *
- * DECLARE CURSOR is a hybrid case: it's an optimizable statement (in fact not
- * significantly different from a SELECT) as far as parsing/rewriting/planning
- * are concerned, but it's not passed to the executor and so in that sense is
- * a utility statement.  We transform it into a Query exactly as if it were
- * a SELECT, then stick the original DeclareCursorStmt into the utilityStmt
- * field to carry the cursor name and options.
+ * DECLARE CURSOR is like other utility statements in that we emit it as a
+ * CMD_UTILITY Query node; however, we must first transform the contained
+ * query.  We used to postpone that until execution, but it's really necessary
+ * to do it during the normal parse analysis phase to ensure that side effects
+ * of parser hooks happen at the expected time.
  */
 static Query *
 transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 {
        Query      *result;
+       Query      *query;
 
        /*
         * Don't allow both SCROLL and NO SCROLL to be specified
@@ -2365,12 +2382,13 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
                                (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
                                 errmsg("cannot specify both SCROLL and NO SCROLL")));
 
-       result = transformStmt(pstate, stmt->query);
+       /* Transform contained query, not allowing SELECT INTO */
+       query = transformStmt(pstate, stmt->query);
+       stmt->query = (Node *) query;
 
        /* Grammar should not have allowed anything but SELECT */
-       if (!IsA(result, Query) ||
-               result->commandType != CMD_SELECT ||
-               result->utilityStmt != NULL)
+       if (!IsA(query, Query) ||
+               query->commandType != CMD_SELECT)
                elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR");
 
        /*
@@ -2378,47 +2396,47 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
         * allowed, but the semantics of when the updates occur might be
         * surprising.)
         */
-       if (result->hasModifyingCTE)
+       if (query->hasModifyingCTE)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("DECLARE CURSOR must not contain data-modifying statements in WITH")));
 
        /* FOR UPDATE and WITH HOLD are not compatible */
-       if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD))
+       if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD))
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                /*------
                  translator: %s is a SQL row locking clause such as FOR UPDATE */
                                 errmsg("DECLARE CURSOR WITH HOLD ... %s is not supported",
                                                LCS_asString(((RowMarkClause *)
-                                                                         linitial(result->rowMarks))->strength)),
+                                                                         linitial(query->rowMarks))->strength)),
                                 errdetail("Holdable cursors must be READ ONLY.")));
 
        /* FOR UPDATE and SCROLL are not compatible */
-       if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL))
+       if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL))
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                /*------
                  translator: %s is a SQL row locking clause such as FOR UPDATE */
                                 errmsg("DECLARE SCROLL CURSOR ... %s is not supported",
                                                LCS_asString(((RowMarkClause *)
-                                                                         linitial(result->rowMarks))->strength)),
+                                                                         linitial(query->rowMarks))->strength)),
                                 errdetail("Scrollable cursors must be READ ONLY.")));
 
        /* FOR UPDATE and INSENSITIVE are not compatible */
-       if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE))
+       if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE))
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                /*------
                  translator: %s is a SQL row locking clause such as FOR UPDATE */
                                 errmsg("DECLARE INSENSITIVE CURSOR ... %s is not supported",
                                                LCS_asString(((RowMarkClause *)
-                                                                         linitial(result->rowMarks))->strength)),
+                                                                         linitial(query->rowMarks))->strength)),
                                 errdetail("Insensitive cursors must be READ ONLY.")));
 
-       /* We won't need the raw querytree any more */
-       stmt->query = NULL;
-
+       /* represent the command as a utility Query */
+       result = makeNode(Query);
+       result->commandType = CMD_UTILITY;
        result->utilityStmt = (Node *) stmt;
 
        return result;
@@ -2441,7 +2459,7 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
        Query      *result;
 
        /* transform contained query, allowing SELECT INTO */
-       stmt->query = (Node *) transformTopLevelStmt(pstate, stmt->query);
+       stmt->query = (Node *) transformOptionalSelectInto(pstate, stmt->query);
 
        /* represent the command as a utility Query */
        result = makeNode(Query);
@@ -2457,7 +2475,7 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
  *     transform a CREATE TABLE AS, SELECT ... INTO, or CREATE MATERIALIZED VIEW
  *     Statement
  *
- * As with EXPLAIN, transform the contained statement now.
+ * As with DECLARE CURSOR and EXPLAIN, transform the contained statement now.
  */
 static Query *
 transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
@@ -2465,7 +2483,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
        Query      *result;
        Query      *query;
 
-       /* transform contained query */
+       /* transform contained query, not allowing SELECT INTO */
        query = transformStmt(pstate, stmt->query);
        stmt->query = (Node *) query;
 
index 9eef550d2ab9c21747823e31e50ee7dd279ca7dc..e61ba06efe59088f358337fae9c5e85f3de58eef 100644 (file)
@@ -80,7 +80,8 @@
 
 /*
  * The above macro assigns -1 (unknown) as the parse location of any
- * nonterminal that was reduced from an empty rule.  This is problematic
+ * nonterminal that was reduced from an empty rule, or whose leftmost
+ * component was reduced from an empty rule.  This is problematic
  * for nonterminals defined like
  *             OptFooList: / * EMPTY * / { ... } | OptFooList Foo { ... } ;
  * because we'll set -1 as the location during the first reduction and then
  * (Although we have many nonterminals that follow this pattern, we only
  * bother with fixing @$ like this when the nonterminal's parse location
  * is actually referenced in some rule.)
+ *
+ * A cleaner answer would be to make YYLLOC_DEFAULT scan all the Rhs
+ * locations until it's found one that's not -1.  Then we'd get a correct
+ * location for any nonterminal that isn't entirely empty.  But this way
+ * would add overhead to every rule reduction, and so far there's not been
+ * a compelling reason to pay that overhead.
  */
 
 /*
@@ -133,6 +140,8 @@ typedef struct ImportQual
 
 static void base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner,
                                                 const char *msg);
+static RawStmt *makeRawStmt(Node *stmt, int stmt_location);
+static void updateRawStmtEnd(RawStmt *rs, int end_location);
 static Node *makeColumnRef(char *colname, List *indirection,
                                                   int location, core_yyscan_t yyscanner);
 static Node *makeTypeCast(Node *arg, TypeName *typename, int location);
@@ -758,18 +767,32 @@ stmtblock:        stmtmulti
                        }
                ;
 
-/* the thrashing around here is to discard "empty" statements... */
+/*
+ * At top level, we wrap each stmt with a RawStmt node carrying start location
+ * and length of the stmt's text.  Notice that the start loc/len are driven
+ * entirely from semicolon locations (@2).  It would seem natural to use
+ * @1 or @3 to get the true start location of a stmt, but that doesn't work
+ * for statements that can start with empty nonterminals (opt_with_clause is
+ * the main offender here); as noted in the comments for YYLLOC_DEFAULT,
+ * we'd get -1 for the location in such cases.
+ * We also take care to discard empty statements entirely.
+ */
 stmtmulti:     stmtmulti ';' stmt
                                {
+                                       if ($1 != NIL)
+                                       {
+                                               /* update length of previous stmt */
+                                               updateRawStmtEnd((RawStmt *) llast($1), @2);
+                                       }
                                        if ($3 != NULL)
-                                               $$ = lappend($1, $3);
+                                               $$ = lappend($1, makeRawStmt($3, @2 + 1));
                                        else
                                                $$ = $1;
                                }
                        | stmt
                                {
                                        if ($1 != NULL)
-                                               $$ = list_make1($1);
+                                               $$ = list_make1(makeRawStmt($1, 0));
                                        else
                                                $$ = NIL;
                                }
@@ -14474,6 +14497,33 @@ base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner, const char *msg)
        parser_yyerror(msg);
 }
 
+static RawStmt *
+makeRawStmt(Node *stmt, int stmt_location)
+{
+       RawStmt    *rs = makeNode(RawStmt);
+
+       rs->stmt = stmt;
+       rs->stmt_location = stmt_location;
+       rs->stmt_len = 0;                       /* might get changed later */
+       return rs;
+}
+
+/* Adjust a RawStmt to reflect that it doesn't run to the end of the string */
+static void
+updateRawStmtEnd(RawStmt *rs, int end_location)
+{
+       /*
+        * If we already set the length, don't change it.  This is for situations
+        * like "select foo ;; select bar" where the same statement will be last
+        * in the string for more than one semicolon.
+        */
+       if (rs->stmt_len > 0)
+               return;
+
+       /* OK, update length of RawStmt */
+       rs->stmt_len = end_location - rs->stmt_location;
+}
+
 static Node *
 makeColumnRef(char *colname, List *indirection,
                          int location, core_yyscan_t yyscanner)
index d788ffd7999daef293f9499f0e478fc6564a8fe7..624ab41371f97c78de11490e285f620d9e6c378d 100644 (file)
@@ -478,12 +478,11 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
        pstate->p_expr_kind = EXPR_KIND_NONE;
 
        /*
-        * Check that we got something reasonable.  Many of these conditions are
-        * impossible given restrictions of the grammar, but check 'em anyway.
+        * Check that we got a SELECT.  Anything else should be impossible given
+        * restrictions of the grammar, but check anyway.
         */
        if (!IsA(query, Query) ||
-               query->commandType != CMD_SELECT ||
-               query->utilityStmt != NULL)
+               query->commandType != CMD_SELECT)
                elog(ERROR, "unexpected non-SELECT command in subquery in FROM");
 
        /*
index f62e45f8ac8fbdc24c1379ae421336fd10c2bed5..73521b93fcab498b3ffb3149183047bff7315bd6 100644 (file)
@@ -1848,12 +1848,11 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
        qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false);
 
        /*
-        * Check that we got something reasonable.  Many of these conditions are
-        * impossible given restrictions of the grammar, but check 'em anyway.
+        * Check that we got a SELECT.  Anything else should be impossible given
+        * restrictions of the grammar, but check anyway.
         */
        if (!IsA(qtree, Query) ||
-               qtree->commandType != CMD_SELECT ||
-               qtree->utilityStmt != NULL)
+               qtree->commandType != CMD_SELECT)
                elog(ERROR, "unexpected non-SELECT command in SubLink");
 
        sublink->subselect = (Node *) qtree;
index e2c884caae0dce9fe23e3e01b2ac9df8a59f461a..6660929dec748dc4b8698af985e93f776b827e86 100644 (file)
@@ -720,7 +720,7 @@ typeStringToTypeName(const char *str)
         */
        if (list_length(raw_parsetree_list) != 1)
                goto fail;
-       stmt = (SelectStmt *) linitial(raw_parsetree_list);
+       stmt = (SelectStmt *) ((RawStmt *) linitial(raw_parsetree_list))->stmt;
        if (stmt == NULL ||
                !IsA(stmt, SelectStmt) ||
                stmt->distinctClause != NIL ||
index c870229817ccb8f06935df9812a0f77742abf0e9..245b4cda3b9b3a4ebe585cad639f5c1fe3b62d02 100644 (file)
@@ -29,7 +29,8 @@
  * raw_parser
  *             Given a query in string form, do lexical and grammatical analysis.
  *
- * Returns a list of raw (un-analyzed) parse trees.
+ * Returns a list of raw (un-analyzed) parse trees.  The immediate elements
+ * of the list are always RawStmt nodes.
  */
 List *
 raw_parser(const char *str)
index 55900d4b0e180805ab641d9952accfb0f48a5509..864d45ff1289699e73ba37533caade16d80b87a6 100644 (file)
@@ -330,8 +330,7 @@ DefineQueryRewrite(char *rulename,
                 */
                query = (Query *) linitial(action);
                if (!is_instead ||
-                       query->commandType != CMD_SELECT ||
-                       query->utilityStmt != NULL)
+                       query->commandType != CMD_SELECT)
                        ereport(ERROR,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("rules on SELECT must have action INSTEAD SELECT")));
index 05b2e57b71d07ffe685df86bbede6799b32579fe..bb89cce8cb9ff17d7ab7c93eb673d3d3c343e529 100644 (file)
@@ -183,8 +183,8 @@ static int  errdetail_recovery_conflict(void);
 static void start_xact_command(void);
 static void finish_xact_command(void);
 static bool IsTransactionExitStmt(Node *parsetree);
-static bool IsTransactionExitStmtList(List *parseTrees);
-static bool IsTransactionStmtList(List *parseTrees);
+static bool IsTransactionExitStmtList(List *pstmts);
+static bool IsTransactionStmtList(List *pstmts);
 static void drop_unnamed_stmt(void);
 static void SigHupHandler(SIGNAL_ARGS);
 static void log_disconnections(int code, Datum arg);
@@ -588,8 +588,8 @@ ProcessClientWriteInterrupt(bool blocked)
 /*
  * Do raw parsing (only).
  *
- * A list of parsetrees is returned, since there might be multiple
- * commands in the given string.
+ * A list of parsetrees (RawStmt nodes) is returned, since there might be
+ * multiple commands in the given string.
  *
  * NOTE: for interactive queries, it is important to keep this routine
  * separate from the analysis & rewrite stages.  Analysis and rewriting
@@ -641,7 +641,7 @@ pg_parse_query(const char *query_string)
  * NOTE: for reasons mentioned above, this must be separate from raw parsing.
  */
 List *
-pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
+pg_analyze_and_rewrite(RawStmt *parsetree, const char *query_string,
                                           Oid *paramTypes, int numParams)
 {
        Query      *query;
@@ -676,7 +676,7 @@ pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
  * hooks instead of a fixed list of parameter datatypes.
  */
 List *
-pg_analyze_and_rewrite_params(Node *parsetree,
+pg_analyze_and_rewrite_params(RawStmt *parsetree,
                                                          const char *query_string,
                                                          ParserSetupHook parserSetup,
                                                          void *parserSetupArg)
@@ -833,8 +833,10 @@ pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
 /*
  * Generate plans for a list of already-rewritten queries.
  *
- * Normal optimizable statements generate PlannedStmt entries in the result
- * list.  Utility statements are simply represented by their statement nodes.
+ * For normal optimizable statements, invoke the planner.  For utility
+ * statements, just make a wrapper PlannedStmt node.
+ *
+ * The result is a list of PlannedStmt nodes.
  */
 List *
 pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
@@ -845,16 +847,21 @@ pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
        foreach(query_list, querytrees)
        {
                Query      *query = (Query *) lfirst(query_list);
-               Node       *stmt;
+               PlannedStmt *stmt;
 
                if (query->commandType == CMD_UTILITY)
                {
-                       /* Utility commands have no plans. */
-                       stmt = query->utilityStmt;
+                       /* Utility commands require no planning. */
+                       stmt = makeNode(PlannedStmt);
+                       stmt->commandType = CMD_UTILITY;
+                       stmt->canSetTag = query->canSetTag;
+                       stmt->utilityStmt = query->utilityStmt;
+                       stmt->stmt_location = query->stmt_location;
+                       stmt->stmt_len = query->stmt_len;
                }
                else
                {
-                       stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams);
+                       stmt = pg_plan_query(query, cursorOptions, boundParams);
                }
 
                stmt_list = lappend(stmt_list, stmt);
@@ -955,7 +962,7 @@ exec_simple_query(const char *query_string)
         */
        foreach(parsetree_item, parsetree_list)
        {
-               Node       *parsetree = (Node *) lfirst(parsetree_item);
+               RawStmt    *parsetree = (RawStmt *) lfirst(parsetree_item);
                bool            snapshot_set = false;
                const char *commandTag;
                char            completionTag[COMPLETION_TAG_BUFSIZE];
@@ -971,7 +978,7 @@ exec_simple_query(const char *query_string)
                 * do any special start-of-SQL-command processing needed by the
                 * destination.
                 */
-               commandTag = CreateCommandTag(parsetree);
+               commandTag = CreateCommandTag(parsetree->stmt);
 
                set_ps_display(commandTag, false);
 
@@ -986,7 +993,7 @@ exec_simple_query(const char *query_string)
                 * state, but not many...)
                 */
                if (IsAbortedTransactionBlockState() &&
-                       !IsTransactionExitStmt(parsetree))
+                       !IsTransactionExitStmt(parsetree->stmt))
                        ereport(ERROR,
                                        (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
                                         errmsg("current transaction is aborted, "
@@ -1061,9 +1068,9 @@ exec_simple_query(const char *query_string)
                 * backward compatibility...)
                 */
                format = 0;                             /* TEXT is default */
-               if (IsA(parsetree, FetchStmt))
+               if (IsA(parsetree->stmt, FetchStmt))
                {
-                       FetchStmt  *stmt = (FetchStmt *) parsetree;
+                       FetchStmt  *stmt = (FetchStmt *) parsetree->stmt;
 
                        if (!stmt->ismove)
                        {
@@ -1102,7 +1109,7 @@ exec_simple_query(const char *query_string)
 
                PortalDrop(portal, false);
 
-               if (IsA(parsetree, TransactionStmt))
+               if (IsA(parsetree->stmt, TransactionStmt))
                {
                        /*
                         * If this was a transaction control statement, commit it. We will
@@ -1194,7 +1201,7 @@ exec_parse_message(const char *query_string,      /* string to execute */
        MemoryContext unnamed_stmt_context = NULL;
        MemoryContext oldcontext;
        List       *parsetree_list;
-       Node       *raw_parse_tree;
+       RawStmt    *raw_parse_tree;
        const char *commandTag;
        List       *querytree_list;
        CachedPlanSource *psrc;
@@ -1279,12 +1286,12 @@ exec_parse_message(const char *query_string,    /* string to execute */
                bool            snapshot_set = false;
                int                     i;
 
-               raw_parse_tree = (Node *) linitial(parsetree_list);
+               raw_parse_tree = (RawStmt *) linitial(parsetree_list);
 
                /*
                 * Get the command name for possible use in status display.
                 */
-               commandTag = CreateCommandTag(raw_parse_tree);
+               commandTag = CreateCommandTag(raw_parse_tree->stmt);
 
                /*
                 * If we are in an aborted transaction, reject all commands except
@@ -1295,7 +1302,7 @@ exec_parse_message(const char *query_string,      /* string to execute */
                 * state, but not many...)
                 */
                if (IsAbortedTransactionBlockState() &&
-                       !IsTransactionExitStmt(raw_parse_tree))
+                       !IsTransactionExitStmt(raw_parse_tree->stmt))
                        ereport(ERROR,
                                        (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
                                         errmsg("current transaction is aborted, "
@@ -1552,7 +1559,7 @@ exec_bind_message(StringInfo input_message)
         * functions.
         */
        if (IsAbortedTransactionBlockState() &&
-               (!IsTransactionExitStmt(psrc->raw_parse_tree) ||
+               (!IsTransactionExitStmt(psrc->raw_parse_tree->stmt) ||
                 numParams != 0))
                ereport(ERROR,
                                (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
@@ -2140,11 +2147,11 @@ errdetail_execute(List *raw_parsetree_list)
 
        foreach(parsetree_item, raw_parsetree_list)
        {
-               Node       *parsetree = (Node *) lfirst(parsetree_item);
+               RawStmt    *parsetree = (RawStmt *) lfirst(parsetree_item);
 
-               if (IsA(parsetree, ExecuteStmt))
+               if (IsA(parsetree->stmt, ExecuteStmt))
                {
-                       ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
+                       ExecuteStmt *stmt = (ExecuteStmt *) parsetree->stmt;
                        PreparedStatement *pstmt;
 
                        pstmt = FetchPreparedStatement(stmt->name, false);
@@ -2488,45 +2495,33 @@ IsTransactionExitStmt(Node *parsetree)
        return false;
 }
 
-/* Test a list that might contain Query nodes or bare parsetrees */
+/* Test a list that contains PlannedStmt nodes */
 static bool
-IsTransactionExitStmtList(List *parseTrees)
+IsTransactionExitStmtList(List *pstmts)
 {
-       if (list_length(parseTrees) == 1)
+       if (list_length(pstmts) == 1)
        {
-               Node       *stmt = (Node *) linitial(parseTrees);
-
-               if (IsA(stmt, Query))
-               {
-                       Query      *query = (Query *) stmt;
+               PlannedStmt *pstmt = (PlannedStmt *) linitial(pstmts);
 
-                       if (query->commandType == CMD_UTILITY &&
-                               IsTransactionExitStmt(query->utilityStmt))
-                               return true;
-               }
-               else if (IsTransactionExitStmt(stmt))
+               Assert(IsA(pstmt, PlannedStmt));
+               if (pstmt->commandType == CMD_UTILITY &&
+                       IsTransactionExitStmt(pstmt->utilityStmt))
                        return true;
        }
        return false;
 }
 
-/* Test a list that might contain Query nodes or bare parsetrees */
+/* Test a list that contains PlannedStmt nodes */
 static bool
-IsTransactionStmtList(List *parseTrees)
+IsTransactionStmtList(List *pstmts)
 {
-       if (list_length(parseTrees) == 1)
+       if (list_length(pstmts) == 1)
        {
-               Node       *stmt = (Node *) linitial(parseTrees);
+               PlannedStmt *pstmt = (PlannedStmt *) linitial(pstmts);
 
-               if (IsA(stmt, Query))
-               {
-                       Query      *query = (Query *) stmt;
-
-                       if (query->commandType == CMD_UTILITY &&
-                               IsA(query->utilityStmt, TransactionStmt))
-                               return true;
-               }
-               else if (IsA(stmt, TransactionStmt))
+               Assert(IsA(pstmt, PlannedStmt));
+               if (pstmt->commandType == CMD_UTILITY &&
+                       IsA(pstmt->utilityStmt, TransactionStmt))
                        return true;
        }
        return false;
index 3b5da73051d323134d27b661234211d2f8321e58..704be399cf7c0b435ddbd4362c65155e056797e5 100644 (file)
@@ -43,7 +43,7 @@ static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
                         DestReceiver *dest);
 static uint64 PortalRunSelect(Portal portal, bool forward, long count,
                                DestReceiver *dest);
-static void PortalRunUtility(Portal portal, Node *utilityStmt,
+static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
                                 bool isTopLevel, bool setHoldSnapshot,
                                 DestReceiver *dest, char *completionTag);
 static void PortalRunMulti(Portal portal,
@@ -73,7 +73,6 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
 
        qd->operation = plannedstmt->commandType;       /* operation */
        qd->plannedstmt = plannedstmt;          /* plan */
-       qd->utilitystmt = plannedstmt->utilityStmt; /* in case DECLARE CURSOR */
        qd->sourceText = sourceText;    /* query text */
        qd->snapshot = RegisterSnapshot(snapshot);      /* snapshot */
        /* RI check snapshot */
@@ -92,37 +91,6 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
        return qd;
 }
 
-/*
- * CreateUtilityQueryDesc
- */
-QueryDesc *
-CreateUtilityQueryDesc(Node *utilitystmt,
-                                          const char *sourceText,
-                                          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->sourceText = sourceText;    /* query text */
-       qd->snapshot = RegisterSnapshot(snapshot);      /* snapshot */
-       qd->crosscheck_snapshot = InvalidSnapshot;      /* RI check snapshot */
-       qd->dest = dest;                        /* output dest */
-       qd->params = params;            /* parameter values passed into query */
-       qd->instrument_options = false;         /* uninteresting for utilities */
-
-       /* null these fields until set by ExecutorStart */
-       qd->tupDesc = NULL;
-       qd->estate = NULL;
-       qd->planstate = NULL;
-       qd->totaltime = NULL;
-
-       return qd;
-}
-
 /*
  * FreeQueryDesc
  */
@@ -236,7 +204,7 @@ ProcessQuery(PlannedStmt *plan,
  * ChoosePortalStrategy
  *             Select portal execution strategy given the intended statement list.
  *
- * The list elements can be Querys, PlannedStmts, or utility statements.
+ * The list elements can be Querys or PlannedStmts.
  * That's more general than portals need, but plancache.c uses this too.
  *
  * See the comments in portal.h.
@@ -263,16 +231,14 @@ ChoosePortalStrategy(List *stmts)
 
                        if (query->canSetTag)
                        {
-                               if (query->commandType == CMD_SELECT &&
-                                       query->utilityStmt == NULL)
+                               if (query->commandType == CMD_SELECT)
                                {
                                        if (query->hasModifyingCTE)
                                                return PORTAL_ONE_MOD_WITH;
                                        else
                                                return PORTAL_ONE_SELECT;
                                }
-                               if (query->commandType == CMD_UTILITY &&
-                                       query->utilityStmt != NULL)
+                               if (query->commandType == CMD_UTILITY)
                                {
                                        if (UtilityReturnsTuples(query->utilityStmt))
                                                return PORTAL_UTIL_SELECT;
@@ -287,24 +253,24 @@ ChoosePortalStrategy(List *stmts)
 
                        if (pstmt->canSetTag)
                        {
-                               if (pstmt->commandType == CMD_SELECT &&
-                                       pstmt->utilityStmt == NULL)
+                               if (pstmt->commandType == CMD_SELECT)
                                {
                                        if (pstmt->hasModifyingCTE)
                                                return PORTAL_ONE_MOD_WITH;
                                        else
                                                return PORTAL_ONE_SELECT;
                                }
+                               if (pstmt->commandType == CMD_UTILITY)
+                               {
+                                       if (UtilityReturnsTuples(pstmt->utilityStmt))
+                                               return PORTAL_UTIL_SELECT;
+                                       /* it can't be ONE_RETURNING, so give up */
+                                       return PORTAL_MULTI_QUERY;
+                               }
                        }
                }
                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;
-               }
+                       elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
        }
 
        /*
@@ -325,7 +291,8 @@ ChoosePortalStrategy(List *stmts)
                        {
                                if (++nSetTag > 1)
                                        return PORTAL_MULTI_QUERY;      /* no need to look further */
-                               if (query->returningList == NIL)
+                               if (query->commandType == CMD_UTILITY ||
+                                       query->returningList == NIL)
                                        return PORTAL_MULTI_QUERY;      /* no need to look further */
                        }
                }
@@ -337,11 +304,13 @@ ChoosePortalStrategy(List *stmts)
                        {
                                if (++nSetTag > 1)
                                        return PORTAL_MULTI_QUERY;      /* no need to look further */
-                               if (!pstmt->hasReturning)
+                               if (pstmt->commandType == CMD_UTILITY ||
+                                       !pstmt->hasReturning)
                                        return PORTAL_MULTI_QUERY;      /* no need to look further */
                        }
                }
-               /* otherwise, utility command, assumed not canSetTag */
+               else
+                       elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
        }
        if (nSetTag == 1)
                return PORTAL_ONE_RETURNING;
@@ -364,7 +333,7 @@ FetchPortalTargetList(Portal portal)
        if (portal->strategy == PORTAL_MULTI_QUERY)
                return NIL;
        /* get the primary statement and find out what it returns */
-       return FetchStatementTargetList(PortalGetPrimaryStmt(portal));
+       return FetchStatementTargetList((Node *) PortalGetPrimaryStmt(portal));
 }
 
 /*
@@ -372,7 +341,7 @@ FetchPortalTargetList(Portal portal)
  *             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.
+ * This can be applied to a Query or a PlannedStmt.
  * That's more general than portals need, but plancache.c uses this too.
  *
  * Note: do not modify the result.
@@ -388,16 +357,14 @@ FetchStatementTargetList(Node *stmt)
        {
                Query      *query = (Query *) stmt;
 
-               if (query->commandType == CMD_UTILITY &&
-                       query->utilityStmt != NULL)
+               if (query->commandType == CMD_UTILITY)
                {
                        /* transfer attention to utility statement */
                        stmt = query->utilityStmt;
                }
                else
                {
-                       if (query->commandType == CMD_SELECT &&
-                               query->utilityStmt == NULL)
+                       if (query->commandType == CMD_SELECT)
                                return query->targetList;
                        if (query->returningList)
                                return query->returningList;
@@ -408,12 +375,19 @@ FetchStatementTargetList(Node *stmt)
        {
                PlannedStmt *pstmt = (PlannedStmt *) stmt;
 
-               if (pstmt->commandType == CMD_SELECT &&
-                       pstmt->utilityStmt == NULL)
-                       return pstmt->planTree->targetlist;
-               if (pstmt->hasReturning)
-                       return pstmt->planTree->targetlist;
-               return NIL;
+               if (pstmt->commandType == CMD_UTILITY)
+               {
+                       /* transfer attention to utility statement */
+                       stmt = pstmt->utilityStmt;
+               }
+               else
+               {
+                       if (pstmt->commandType == CMD_SELECT)
+                               return pstmt->planTree->targetlist;
+                       if (pstmt->hasReturning)
+                               return pstmt->planTree->targetlist;
+                       return NIL;
+               }
        }
        if (IsA(stmt, FetchStmt))
        {
@@ -566,8 +540,7 @@ PortalStart(Portal portal, ParamListInfo params,
                                {
                                        PlannedStmt *pstmt;
 
-                                       pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal);
-                                       Assert(IsA(pstmt, PlannedStmt));
+                                       pstmt = PortalGetPrimaryStmt(portal);
                                        portal->tupDesc =
                                                ExecCleanTypeFromTL(pstmt->planTree->targetlist,
                                                                                        false);
@@ -588,10 +561,10 @@ PortalStart(Portal portal, ParamListInfo params,
                                 * take care of it if needed.
                                 */
                                {
-                                       Node       *ustmt = PortalGetPrimaryStmt(portal);
+                                       PlannedStmt *pstmt = PortalGetPrimaryStmt(portal);
 
-                                       Assert(!IsA(ustmt, PlannedStmt));
-                                       portal->tupDesc = UtilityTupleDescriptor(ustmt);
+                                       Assert(pstmt->commandType == CMD_UTILITY);
+                                       portal->tupDesc = UtilityTupleDescriptor(pstmt->utilityStmt);
                                }
 
                                /*
@@ -1047,7 +1020,7 @@ FillPortalStore(Portal portal, bool isTopLevel)
                        break;
 
                case PORTAL_UTIL_SELECT:
-                       PortalRunUtility(portal, (Node *) linitial(portal->stmts),
+                       PortalRunUtility(portal, (PlannedStmt *) linitial(portal->stmts),
                                                         isTopLevel, true, treceiver, completionTag);
                        break;
 
@@ -1143,10 +1116,11 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count,
  *             Execute a utility statement inside a portal.
  */
 static void
-PortalRunUtility(Portal portal, Node *utilityStmt,
+PortalRunUtility(Portal portal, PlannedStmt *pstmt,
                                 bool isTopLevel, bool setHoldSnapshot,
                                 DestReceiver *dest, char *completionTag)
 {
+       Node       *utilityStmt = pstmt->utilityStmt;
        Snapshot        snapshot;
 
        /*
@@ -1186,7 +1160,7 @@ PortalRunUtility(Portal portal, Node *utilityStmt,
        else
                snapshot = NULL;
 
-       ProcessUtility(utilityStmt,
+       ProcessUtility(pstmt,
                                   portal->sourceText,
                           isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
                                   portal->portalParams,
@@ -1241,21 +1215,18 @@ PortalRunMulti(Portal portal,
         */
        foreach(stmtlist_item, portal->stmts)
        {
-               Node       *stmt = (Node *) lfirst(stmtlist_item);
+               PlannedStmt *pstmt = (PlannedStmt *) lfirst(stmtlist_item);
 
                /*
                 * If we got a cancel signal in prior command, quit
                 */
                CHECK_FOR_INTERRUPTS();
 
-               if (IsA(stmt, PlannedStmt) &&
-                       ((PlannedStmt *) stmt)->utilityStmt == NULL)
+               if (pstmt->utilityStmt == NULL)
                {
                        /*
                         * process a plannable query.
                         */
-                       PlannedStmt *pstmt = (PlannedStmt *) stmt;
-
                        TRACE_POSTGRESQL_QUERY_EXECUTE_START();
 
                        if (log_executor_stats)
@@ -1320,9 +1291,6 @@ PortalRunMulti(Portal portal,
                        /*
                         * process utility functions (create, destroy, etc..)
                         *
-                        * These are assumed canSetTag if they're the only stmt in the
-                        * portal.
-                        *
                         * We must not set a snapshot here for utility commands (if one is
                         * needed, PortalRunUtility will do it).  If a utility command is
                         * alone in a portal then everything's fine.  The only case where
@@ -1331,18 +1299,18 @@ PortalRunMulti(Portal portal,
                         * whether it has a snapshot or not, so we just leave the current
                         * snapshot alone if we have one.
                         */
-                       if (list_length(portal->stmts) == 1)
+                       if (pstmt->canSetTag)
                        {
                                Assert(!active_snapshot_set);
                                /* statement can set tag string */
-                               PortalRunUtility(portal, stmt, isTopLevel, false,
+                               PortalRunUtility(portal, pstmt, isTopLevel, false,
                                                                 dest, completionTag);
                        }
                        else
                        {
-                               Assert(IsA(stmt, NotifyStmt));
+                               Assert(IsA(pstmt->utilityStmt, NotifyStmt));
                                /* stmt added by rewrite cannot set tag */
-                               PortalRunUtility(portal, stmt, isTopLevel, false,
+                               PortalRunUtility(portal, pstmt, isTopLevel, false,
                                                                 altdest, NULL);
                        }
                }
index 127dc862e8d86b650d0036d82d6d4c8ac51522e9..149210133606443b3f3ba142f4df18836530bc4c 100644 (file)
@@ -72,7 +72,7 @@ ProcessUtility_hook_type ProcessUtility_hook = NULL;
 
 /* local function declarations */
 static void ProcessUtilitySlow(ParseState *pstate,
-                                                          Node *parsetree,
+                                  PlannedStmt *pstmt,
                                   const char *queryString,
                                   ProcessUtilityContext context,
                                   ParamListInfo params,
@@ -88,35 +88,33 @@ static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
  * 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
+ * Note: currently no need to support raw or analyzed queries here
  */
 bool
-CommandIsReadOnly(Node *parsetree)
+CommandIsReadOnly(PlannedStmt *pstmt)
 {
-       if (IsA(parsetree, PlannedStmt))
+       Assert(IsA(pstmt, PlannedStmt));
+       switch (pstmt->commandType)
        {
-               PlannedStmt *stmt = (PlannedStmt *) parsetree;
-
-               switch (stmt->commandType)
-               {
-                       case CMD_SELECT:
-                               if (stmt->rowMarks != NIL)
-                                       return false;           /* SELECT FOR [KEY] UPDATE/SHARE */
-                               else if (stmt->hasModifyingCTE)
-                                       return false;           /* data-modifying CTE */
-                               else
-                                       return true;
-                       case CMD_UPDATE:
-                       case CMD_INSERT:
-                       case CMD_DELETE:
-                               return false;
-                       default:
-                               elog(WARNING, "unrecognized commandType: %d",
-                                        (int) stmt->commandType);
-                               break;
-               }
+               case CMD_SELECT:
+                       if (pstmt->rowMarks != NIL)
+                               return false;   /* SELECT FOR [KEY] UPDATE/SHARE */
+                       else if (pstmt->hasModifyingCTE)
+                               return false;   /* data-modifying CTE */
+                       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) pstmt->commandType);
+                       break;
        }
-       /* For now, treat all utility commands as read/write */
        return false;
 }
 
@@ -297,7 +295,7 @@ CheckRestrictedOperation(const char *cmdname)
  * ProcessUtility
  *             general utility function invoker
  *
- *     parsetree: the parse tree for the utility statement
+ *     pstmt: PlannedStmt wrapper for the utility statement
  *     queryString: original source text of command
  *     context: identifies source of statement (toplevel client command,
  *             non-toplevel client command, subcommand of a larger utility command)
@@ -315,13 +313,15 @@ CheckRestrictedOperation(const char *cmdname)
  * completionTag may be NULL if caller doesn't want a status string.
  */
 void
-ProcessUtility(Node *parsetree,
+ProcessUtility(PlannedStmt *pstmt,
                           const char *queryString,
                           ProcessUtilityContext context,
                           ParamListInfo params,
                           DestReceiver *dest,
                           char *completionTag)
 {
+       Assert(IsA(pstmt, PlannedStmt));
+       Assert(pstmt->commandType == CMD_UTILITY);
        Assert(queryString != NULL);    /* required as of 8.4 */
 
        /*
@@ -330,11 +330,11 @@ ProcessUtility(Node *parsetree,
         * call standard_ProcessUtility().
         */
        if (ProcessUtility_hook)
-               (*ProcessUtility_hook) (parsetree, queryString,
+               (*ProcessUtility_hook) (pstmt, queryString,
                                                                context, params,
                                                                dest, completionTag);
        else
-               standard_ProcessUtility(parsetree, queryString,
+               standard_ProcessUtility(pstmt, queryString,
                                                                context, params,
                                                                dest, completionTag);
 }
@@ -351,13 +351,14 @@ ProcessUtility(Node *parsetree,
  * which requires being in a valid transaction.
  */
 void
-standard_ProcessUtility(Node *parsetree,
+standard_ProcessUtility(PlannedStmt *pstmt,
                                                const char *queryString,
                                                ProcessUtilityContext context,
                                                ParamListInfo params,
                                                DestReceiver *dest,
                                                char *completionTag)
 {
+       Node       *parsetree = pstmt->utilityStmt;
        bool            isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
        ParseState *pstate;
 
@@ -486,20 +487,10 @@ standard_ProcessUtility(Node *parsetree,
 
                        /*
                         * Portal (cursor) manipulation
-                        *
-                        * Note: DECLARE CURSOR is processed mostly as a SELECT, and
-                        * therefore what we will get here is a PlannedStmt not a bare
-                        * DeclareCursorStmt.
                         */
-               case T_PlannedStmt:
-                       {
-                               PlannedStmt *stmt = (PlannedStmt *) parsetree;
-
-                               if (stmt->utilityStmt == NULL ||
-                                       !IsA(stmt->utilityStmt, DeclareCursorStmt))
-                                       elog(ERROR, "non-DECLARE CURSOR PlannedStmt passed to ProcessUtility");
-                               PerformCursorOpen(stmt, params, queryString, isTopLevel);
-                       }
+               case T_DeclareCursorStmt:
+                       PerformCursorOpen((DeclareCursorStmt *) parsetree, params,
+                                                         queryString, isTopLevel);
                        break;
 
                case T_ClosePortalStmt:
@@ -545,7 +536,9 @@ standard_ProcessUtility(Node *parsetree,
                        {
                                uint64          processed;
 
-                               DoCopy(pstate, (CopyStmt *) parsetree, &processed);
+                               DoCopy(pstate, (CopyStmt *) parsetree,
+                                          pstmt->stmt_location, pstmt->stmt_len,
+                                          &processed);
                                if (completionTag)
                                        snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
                                                         "COPY " UINT64_FORMAT, processed);
@@ -554,7 +547,8 @@ standard_ProcessUtility(Node *parsetree,
 
                case T_PrepareStmt:
                        CheckRestrictedOperation("PREPARE");
-                       PrepareQuery((PrepareStmt *) parsetree, queryString);
+                       PrepareQuery((PrepareStmt *) parsetree, queryString,
+                                                pstmt->stmt_location, pstmt->stmt_len);
                        break;
 
                case T_ExecuteStmt:
@@ -808,11 +802,11 @@ standard_ProcessUtility(Node *parsetree,
                                GrantStmt  *stmt = (GrantStmt *) parsetree;
 
                                if (EventTriggerSupportsGrantObjectType(stmt->objtype))
-                                       ProcessUtilitySlow(pstate, parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
-                                       ExecuteGrantStmt((GrantStmt *) parsetree);
+                                       ExecuteGrantStmt(stmt);
                        }
                        break;
 
@@ -821,7 +815,7 @@ standard_ProcessUtility(Node *parsetree,
                                DropStmt   *stmt = (DropStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->removeType))
-                                       ProcessUtilitySlow(pstate, parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -834,7 +828,7 @@ standard_ProcessUtility(Node *parsetree,
                                RenameStmt *stmt = (RenameStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->renameType))
-                                       ProcessUtilitySlow(pstate, parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -847,7 +841,7 @@ standard_ProcessUtility(Node *parsetree,
                                AlterObjectDependsStmt *stmt = (AlterObjectDependsStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->objectType))
-                                       ProcessUtilitySlow(pstate, parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -860,7 +854,7 @@ standard_ProcessUtility(Node *parsetree,
                                AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->objectType))
-                                       ProcessUtilitySlow(pstate, parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -873,7 +867,7 @@ standard_ProcessUtility(Node *parsetree,
                                AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->objectType))
-                                       ProcessUtilitySlow(pstate, parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -886,11 +880,11 @@ standard_ProcessUtility(Node *parsetree,
                                CommentStmt *stmt = (CommentStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->objtype))
-                                       ProcessUtilitySlow(pstate, parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
-                                       CommentObject((CommentStmt *) parsetree);
+                                       CommentObject(stmt);
                                break;
                        }
 
@@ -899,7 +893,7 @@ standard_ProcessUtility(Node *parsetree,
                                SecLabelStmt *stmt = (SecLabelStmt *) parsetree;
 
                                if (EventTriggerSupportsObjectType(stmt->objtype))
-                                       ProcessUtilitySlow(pstate, parsetree, queryString,
+                                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                                           context, params,
                                                                           dest, completionTag);
                                else
@@ -909,7 +903,7 @@ standard_ProcessUtility(Node *parsetree,
 
                default:
                        /* All other statement types have event trigger support */
-                       ProcessUtilitySlow(pstate, parsetree, queryString,
+                       ProcessUtilitySlow(pstate, pstmt, queryString,
                                                           context, params,
                                                           dest, completionTag);
                        break;
@@ -925,13 +919,14 @@ standard_ProcessUtility(Node *parsetree,
  */
 static void
 ProcessUtilitySlow(ParseState *pstate,
-                                  Node *parsetree,
+                                  PlannedStmt *pstmt,
                                   const char *queryString,
                                   ProcessUtilityContext context,
                                   ParamListInfo params,
                                   DestReceiver *dest,
                                   char *completionTag)
 {
+       Node       *parsetree = pstmt->utilityStmt;
        bool            isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
        bool            isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
        bool            needCleanup;
@@ -955,7 +950,9 @@ ProcessUtilitySlow(ParseState *pstate,
                                 */
                        case T_CreateSchemaStmt:
                                CreateSchemaCommand((CreateSchemaStmt *) parsetree,
-                                                                       queryString);
+                                                                       queryString,
+                                                                       pstmt->stmt_location,
+                                                                       pstmt->stmt_len);
 
                                /*
                                 * EventTriggerCollectSimpleCommand called by
@@ -1036,7 +1033,16 @@ ProcessUtilitySlow(ParseState *pstate,
                                                         * call will stash the objects so created into our
                                                         * event trigger context.
                                                         */
-                                                       ProcessUtility(stmt,
+                                                       PlannedStmt *wrapper;
+
+                                                       wrapper = makeNode(PlannedStmt);
+                                                       wrapper->commandType = CMD_UTILITY;
+                                                       wrapper->canSetTag = false;
+                                                       wrapper->utilityStmt = stmt;
+                                                       wrapper->stmt_location = pstmt->stmt_location;
+                                                       wrapper->stmt_len = pstmt->stmt_len;
+
+                                                       ProcessUtility(wrapper,
                                                                                   queryString,
                                                                                   PROCESS_UTILITY_SUBCOMMAND,
                                                                                   params,
@@ -1105,8 +1111,16 @@ ProcessUtilitySlow(ParseState *pstate,
                                                                 * queued commands is consistent with the way
                                                                 * they are executed here.
                                                                 */
+                                                               PlannedStmt *wrapper;
+
                                                                EventTriggerAlterTableEnd();
-                                                               ProcessUtility(stmt,
+                                                               wrapper = makeNode(PlannedStmt);
+                                                               wrapper->commandType = CMD_UTILITY;
+                                                               wrapper->canSetTag = false;
+                                                               wrapper->utilityStmt = stmt;
+                                                               wrapper->stmt_location = pstmt->stmt_location;
+                                                               wrapper->stmt_len = pstmt->stmt_len;
+                                                               ProcessUtility(wrapper,
                                                                                           queryString,
                                                                                           PROCESS_UTILITY_SUBCOMMAND,
                                                                                           params,
@@ -1376,7 +1390,8 @@ ProcessUtilitySlow(ParseState *pstate,
 
                        case T_ViewStmt:        /* CREATE VIEW */
                                EventTriggerAlterTableStart(parsetree);
-                               address = DefineView((ViewStmt *) parsetree, queryString);
+                               address = DefineView((ViewStmt *) parsetree, queryString,
+                                                                        pstmt->stmt_location, pstmt->stmt_len);
                                EventTriggerCollectSimpleCommand(address, secondaryObject,
                                                                                                 parsetree);
                                /* stashed internally */
@@ -1480,6 +1495,7 @@ ProcessUtilitySlow(ParseState *pstate,
 
                        case T_AlterTSConfigurationStmt:
                                AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
+
                                /*
                                 * Commands are stashed in MakeConfigurationMapping and
                                 * DropConfigurationMapping, which are called from
@@ -1736,10 +1752,8 @@ QueryReturnsTuples(Query *parsetree)
        switch (parsetree->commandType)
        {
                case CMD_SELECT:
-                       /* returns tuples ... unless it's DECLARE CURSOR */
-                       if (parsetree->utilityStmt == NULL)
-                               return true;
-                       break;
+                       /* returns tuples */
+                       return true;
                case CMD_INSERT:
                case CMD_UPDATE:
                case CMD_DELETE:
@@ -1780,6 +1794,13 @@ UtilityContainsQuery(Node *parsetree)
 
        switch (nodeTag(parsetree))
        {
+               case T_DeclareCursorStmt:
+                       qry = (Query *) ((DeclareCursorStmt *) parsetree)->query;
+                       Assert(IsA(qry, Query));
+                       if (qry->commandType == CMD_UTILITY)
+                               return UtilityContainsQuery(qry->utilityStmt);
+                       return qry;
+
                case T_ExplainStmt:
                        qry = (Query *) ((ExplainStmt *) parsetree)->query;
                        Assert(IsA(qry, Query));
@@ -1931,7 +1952,8 @@ AlterObjectTypeCommandTag(ObjectType objtype)
 /*
  * CreateCommandTag
  *             utility to get a string representation of the command operation,
- *             given either a raw (un-analyzed) parsetree or a planned query.
+ *             given either a raw (un-analyzed) parsetree, an analyzed Query,
+ *             or a PlannedStmt.
  *
  * This must handle all command types, but since the vast majority
  * of 'em are utility commands, it seems sensible to keep it here.
@@ -1946,6 +1968,11 @@ CreateCommandTag(Node *parsetree)
 
        switch (nodeTag(parsetree))
        {
+                       /* recurse if we're given a RawStmt */
+               case T_RawStmt:
+                       tag = CreateCommandTag(((RawStmt *) parsetree)->stmt);
+                       break;
+
                        /* raw plannable queries */
                case T_InsertStmt:
                        tag = "INSERT";
@@ -2608,12 +2635,7 @@ CreateCommandTag(Node *parsetree)
                                                 * will be useful for complaints about read-only
                                                 * statements
                                                 */
-                                               if (stmt->utilityStmt != NULL)
-                                               {
-                                                       Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
-                                                       tag = "DECLARE CURSOR";
-                                               }
-                                               else if (stmt->rowMarks != NIL)
+                                               if (stmt->rowMarks != NIL)
                                                {
                                                        /* not 100% but probably close enough */
                                                        switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
@@ -2647,6 +2669,9 @@ CreateCommandTag(Node *parsetree)
                                        case CMD_DELETE:
                                                tag = "DELETE";
                                                break;
+                                       case CMD_UTILITY:
+                                               tag = CreateCommandTag(stmt->utilityStmt);
+                                               break;
                                        default:
                                                elog(WARNING, "unrecognized commandType: %d",
                                                         (int) stmt->commandType);
@@ -2670,12 +2695,7 @@ CreateCommandTag(Node *parsetree)
                                                 * will be useful for complaints about read-only
                                                 * statements
                                                 */
-                                               if (stmt->utilityStmt != NULL)
-                                               {
-                                                       Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
-                                                       tag = "DECLARE CURSOR";
-                                               }
-                                               else if (stmt->rowMarks != NIL)
+                                               if (stmt->rowMarks != NIL)
                                                {
                                                        /* not 100% but probably close enough */
                                                        switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
@@ -2735,7 +2755,8 @@ CreateCommandTag(Node *parsetree)
 /*
  * GetCommandLogLevel
  *             utility to get the minimum log_statement level for a command,
- *             given either a raw (un-analyzed) parsetree or a planned query.
+ *             given either a raw (un-analyzed) parsetree, an analyzed Query,
+ *             or a PlannedStmt.
  *
  * This must handle all command types, but since the vast majority
  * of 'em are utility commands, it seems sensible to keep it here.
@@ -2747,6 +2768,11 @@ GetCommandLogLevel(Node *parsetree)
 
        switch (nodeTag(parsetree))
        {
+                       /* recurse if we're given a RawStmt */
+               case T_RawStmt:
+                       lev = GetCommandLogLevel(((RawStmt *) parsetree)->stmt);
+                       break;
+
                        /* raw plannable queries */
                case T_InsertStmt:
                case T_DeleteStmt:
@@ -2850,7 +2876,7 @@ GetCommandLogLevel(Node *parsetree)
                                /* Look through an EXECUTE to the referenced stmt */
                                ps = FetchPreparedStatement(stmt->name, false);
                                if (ps && ps->plansource->raw_parse_tree)
-                                       lev = GetCommandLogLevel(ps->plansource->raw_parse_tree);
+                                       lev = GetCommandLogLevel(ps->plansource->raw_parse_tree->stmt);
                                else
                                        lev = LOGSTMT_ALL;
                        }
@@ -3157,6 +3183,10 @@ GetCommandLogLevel(Node *parsetree)
                                                lev = LOGSTMT_MOD;
                                                break;
 
+                                       case CMD_UTILITY:
+                                               lev = GetCommandLogLevel(stmt->utilityStmt);
+                                               break;
+
                                        default:
                                                elog(WARNING, "unrecognized commandType: %d",
                                                         (int) stmt->commandType);
index e48a878dc2a99eaa4b9e25d52f6f70de6d32b272..c31c603fbf5ad111e7bbd166fb1b64ac24ba6b53 100644 (file)
@@ -77,7 +77,7 @@
  */
 #define IsTransactionStmtPlan(plansource)  \
        ((plansource)->raw_parse_tree && \
-        IsA((plansource)->raw_parse_tree, TransactionStmt))
+        IsA((plansource)->raw_parse_tree->stmt, TransactionStmt))
 
 /*
  * This is the head of the backend's list of "saved" CachedPlanSources (i.e.,
@@ -95,6 +95,7 @@ static CachedPlan *BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 static bool choose_custom_plan(CachedPlanSource *plansource,
                                   ParamListInfo boundParams);
 static double cached_plan_cost(CachedPlan *plan, bool include_planner);
+static Query *QueryListGetPrimaryStmt(List *stmts);
 static void AcquireExecutorLocks(List *stmt_list, bool acquire);
 static void AcquirePlannerLocks(List *stmt_list, bool acquire);
 static void ScanQueryForLocks(Query *parsetree, bool acquire);
@@ -147,7 +148,7 @@ InitPlanCache(void)
  * commandTag: compile-time-constant tag for query, or NULL if empty query
  */
 CachedPlanSource *
-CreateCachedPlan(Node *raw_parse_tree,
+CreateCachedPlan(RawStmt *raw_parse_tree,
                                 const char *query_string,
                                 const char *commandTag)
 {
@@ -230,7 +231,7 @@ CreateCachedPlan(Node *raw_parse_tree,
  * commandTag: compile-time-constant tag for query, or NULL if empty query
  */
 CachedPlanSource *
-CreateOneShotCachedPlan(Node *raw_parse_tree,
+CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
                                                const char *query_string,
                                                const char *commandTag)
 {
@@ -555,7 +556,7 @@ static List *
 RevalidateCachedQuery(CachedPlanSource *plansource)
 {
        bool            snapshot_set;
-       Node       *rawtree;
+       RawStmt    *rawtree;
        List       *tlist;                      /* transient query-tree list */
        List       *qlist;                      /* permanent query-tree list */
        TupleDesc       resultDesc;
@@ -976,7 +977,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
        {
                PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
 
-               if (!IsA(plannedstmt, PlannedStmt))
+               if (plannedstmt->commandType == CMD_UTILITY)
                        continue;                       /* Ignore utility statements */
 
                if (plannedstmt->transientPlan)
@@ -1071,7 +1072,7 @@ cached_plan_cost(CachedPlan *plan, bool include_planner)
        {
                PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
 
-               if (!IsA(plannedstmt, PlannedStmt))
+               if (plannedstmt->commandType == CMD_UTILITY)
                        continue;                       /* Ignore utility statements */
 
                result += plannedstmt->planTree->total_cost;
@@ -1419,7 +1420,7 @@ CachedPlanIsValid(CachedPlanSource *plansource)
 List *
 CachedPlanGetTargetList(CachedPlanSource *plansource)
 {
-       Node       *pstmt;
+       Query      *pstmt;
 
        /* Assert caller is doing things in a sane order */
        Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
@@ -1436,9 +1437,34 @@ CachedPlanGetTargetList(CachedPlanSource *plansource)
        RevalidateCachedQuery(plansource);
 
        /* Get the primary statement and find out what it returns */
-       pstmt = PortalListGetPrimaryStmt(plansource->query_list);
+       pstmt = QueryListGetPrimaryStmt(plansource->query_list);
 
-       return FetchStatementTargetList(pstmt);
+       return FetchStatementTargetList((Node *) pstmt);
+}
+
+/*
+ * QueryListGetPrimaryStmt
+ *             Get the "primary" stmt within a list, ie, the one marked canSetTag.
+ *
+ * Returns NULL if no such stmt.  If multiple queries within the list are
+ * marked canSetTag, returns the first one.  Neither of these cases should
+ * occur in present usages of this function.
+ */
+static Query *
+QueryListGetPrimaryStmt(List *stmts)
+{
+       ListCell   *lc;
+
+       foreach(lc, stmts)
+       {
+               Query      *stmt = (Query *) lfirst(lc);
+
+               Assert(IsA(stmt, Query));
+
+               if (stmt->canSetTag)
+                       return stmt;
+       }
+       return NULL;
 }
 
 /*
@@ -1456,8 +1482,9 @@ AcquireExecutorLocks(List *stmt_list, bool acquire)
                int                     rt_index;
                ListCell   *lc2;
 
-               Assert(!IsA(plannedstmt, Query));
-               if (!IsA(plannedstmt, PlannedStmt))
+               Assert(IsA(plannedstmt, PlannedStmt));
+
+               if (plannedstmt->commandType == CMD_UTILITY)
                {
                        /*
                         * Ignore utility statements, except those (such as EXPLAIN) that
@@ -1466,7 +1493,7 @@ AcquireExecutorLocks(List *stmt_list, bool acquire)
                         * rule rewriting, because rewriting doesn't change the query
                         * representation.
                         */
-                       Query      *query = UtilityContainsQuery((Node *) plannedstmt);
+                       Query      *query = UtilityContainsQuery(plannedstmt->utilityStmt);
 
                        if (query)
                                ScanQueryForLocks(query, acquire);
@@ -1654,8 +1681,7 @@ PlanCacheComputeResultDesc(List *stmt_list)
                        return ExecCleanTypeFromTL(query->targetList, false);
 
                case PORTAL_ONE_RETURNING:
-                       query = (Query *) PortalListGetPrimaryStmt(stmt_list);
-                       Assert(IsA(query, Query));
+                       query = QueryListGetPrimaryStmt(stmt_list);
                        Assert(query->returningList);
                        return ExecCleanTypeFromTL(query->returningList, false);
 
@@ -1720,8 +1746,7 @@ PlanCacheRelCallback(Datum arg, Oid relid)
                        {
                                PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
 
-                               Assert(!IsA(plannedstmt, Query));
-                               if (!IsA(plannedstmt, PlannedStmt))
+                               if (plannedstmt->commandType == CMD_UTILITY)
                                        continue;       /* Ignore utility statements */
                                if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :
                                        list_member_oid(plannedstmt->relationOids, relid))
@@ -1795,8 +1820,7 @@ PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue)
                                PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
                                ListCell   *lc3;
 
-                               Assert(!IsA(plannedstmt, Query));
-                               if (!IsA(plannedstmt, PlannedStmt))
+                               if (plannedstmt->commandType == CMD_UTILITY)
                                        continue;       /* Ignore utility statements */
                                foreach(lc3, plannedstmt->invalItems)
                                {
index 5a15ed21d669eb6c326fcfa76d2500bd743dd34c..e8ebc4684caf32ff167d765c07c3c7de462cf46c 100644 (file)
@@ -139,45 +139,26 @@ GetPortalByName(const char *name)
 }
 
 /*
- * PortalListGetPrimaryStmt
+ * PortalGetPrimaryStmt
  *             Get the "primary" stmt within a portal, ie, the one marked canSetTag.
  *
  * 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 plancache.c, which needs both cases.
- *
- * Note: the reason this is just handed a List is so that plancache.c
- * can share the code.  For use with a portal, use PortalGetPrimaryStmt
- * rather than calling this directly.
  */
-Node *
-PortalListGetPrimaryStmt(List *stmts)
+PlannedStmt *
+PortalGetPrimaryStmt(Portal portal)
 {
        ListCell   *lc;
 
-       foreach(lc, stmts)
+       foreach(lc, portal->stmts)
        {
-               Node       *stmt = (Node *) lfirst(lc);
+               PlannedStmt *stmt = (PlannedStmt *) lfirst(lc);
 
-               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;
-               }
+               Assert(IsA(stmt, PlannedStmt));
+
+               if (stmt->canSetTag)
+                       return stmt;
        }
        return NULL;
 }
index 95cfd53eeca9bd8e12da12205e60f0363acf27c0..29604b6f4edeaad50dc46a23fc911268ab70a220 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201612231
+#define CATALOG_VERSION_NO     201701141
 
 #endif
index 4785631cef9cc2693443649d7fa54b7cf719b702..d63ca0f5e9966f4be93e8cbfe2acc707fea5a390 100644 (file)
@@ -22,7 +22,8 @@
 /* CopyStateData is private in commands/copy.c */
 typedef struct CopyStateData *CopyState;
 
-extern Oid DoCopy(ParseState *state, const CopyStmt *stmt,
+extern void DoCopy(ParseState *state, const CopyStmt *stmt,
+          int stmt_location, int stmt_len,
           uint64 *processed);
 
 extern void ProcessCopyOptions(ParseState *pstate, CopyState cstate, bool is_from, List *options);
index 1820dd66bb255e83a5fcf54f40683692d79f5c0a..9191e186c15456bdc200d14f2beee298811d6b1a 100644 (file)
@@ -49,6 +49,7 @@ typedef struct ExplainState
 
 /* Hook for plugins to get control in ExplainOneQuery() */
 typedef void (*ExplainOneQuery_hook_type) (Query *query,
+                                                                                                          int cursorOptions,
                                                                                                           IntoClause *into,
                                                                                                           ExplainState *es,
                                                                                                         const char *queryString,
index 0ae9b7213cdceaceaf191d21e291b9704c438a10..8f0e6c48f495aeaa58328e83ce06c436b586e8cd 100644 (file)
@@ -18,7 +18,7 @@
 #include "utils/portal.h"
 
 
-extern void PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
+extern void PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params,
                                  const char *queryString, bool isTopLevel);
 
 extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
index 0a3a8addca11d595f2e4c499e32a542c7af00c0d..d8d22edbbcd82545116be1a19722913780fc555d 100644 (file)
@@ -35,7 +35,8 @@ typedef struct
 
 
 /* Utility statements PREPARE, EXECUTE, DEALLOCATE, EXPLAIN EXECUTE */
-extern void PrepareQuery(PrepareStmt *stmt, const char *queryString);
+extern void PrepareQuery(PrepareStmt *stmt, const char *queryString,
+                        int stmt_location, int stmt_len);
 extern void ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
                         const char *queryString, ParamListInfo params,
                         DestReceiver *dest, char *completionTag);
index 087878ddc33abdceee85ced502dcf82f66786a07..f07a389c7f6981f8a24dabe016f60db63c8e05dd 100644 (file)
@@ -19,7 +19,8 @@
 #include "nodes/parsenodes.h"
 
 extern Oid CreateSchemaCommand(CreateSchemaStmt *parsetree,
-                                       const char *queryString);
+                                       const char *queryString,
+                                       int stmt_location, int stmt_len);
 
 extern void RemoveSchemaById(Oid schemaOid);
 
index 648665ebe5d8e451c37ca38a73eb4632fc399f73..39763913c80590df8526a3810c60196a9f636e55 100644 (file)
@@ -19,7 +19,8 @@
 
 extern void validateWithCheckOption(char *value);
 
-extern ObjectAddress DefineView(ViewStmt *stmt, const char *queryString);
+extern ObjectAddress DefineView(ViewStmt *stmt, const char *queryString,
+                  int stmt_location, int stmt_len);
 
 extern void StoreViewQuery(Oid viewOid, Query *viewParse, bool replace);
 
index 2c221255dcbf510578c99efa84640cdbea302383..c99ea818158c9dafb9d8b8ac9d1d5044851b54d1 100644 (file)
@@ -34,8 +34,7 @@ typedef struct QueryDesc
 {
        /* These fields are provided by CreateQueryDesc */
        CmdType         operation;              /* CMD_SELECT, CMD_UPDATE, etc. */
-       PlannedStmt *plannedstmt;       /* planner's output, or null if utility */
-       Node       *utilitystmt;        /* utility statement, or null */
+       PlannedStmt *plannedstmt;       /* planner's output (could be utility, too) */
        const char *sourceText;         /* source text of the query */
        Snapshot        snapshot;               /* snapshot to use for query */
        Snapshot        crosscheck_snapshot;    /* crosscheck for RI update/delete */
@@ -61,12 +60,6 @@ extern QueryDesc *CreateQueryDesc(PlannedStmt *plannedstmt,
                                ParamListInfo params,
                                int instrument_options);
 
-extern QueryDesc *CreateUtilityQueryDesc(Node *utilitystmt,
-                                          const char *sourceText,
-                                          Snapshot snapshot,
-                                          DestReceiver *dest,
-                                          ParamListInfo params);
-
 extern void FreeQueryDesc(QueryDesc *qdesc);
 
 #endif   /* EXECDESC_H  */
index a1bb0ac5cb89de61fd4e93b6ad1b63089eb3cf6d..4c4319bcabfb4ff26c4635d513a62155efaed4c8 100644 (file)
@@ -301,6 +301,7 @@ typedef enum NodeTag
        /*
         * TAGS FOR STATEMENT NODES (mostly in parsenodes.h)
         */
+       T_RawStmt,
        T_Query,
        T_PlannedStmt,
        T_InsertStmt,
index 7ceaa22c33b2265fce291b7658c683a3fb1ecb5b..edb5cd21521141993a4eb63e37e75aa88ebe950f 100644 (file)
@@ -7,7 +7,9 @@
  * This is a byte (not character) offset in the original source text, to be
  * used for positioning an error cursor when there is an error related to
  * the node.  Access to the original source text is needed to make use of
- * the location.
+ * the location.  At the topmost (statement) level, we also provide a
+ * statement length, likewise measured in bytes, for convenience in
+ * identifying statement boundaries in multi-statement source strings.
  *
  *
  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
@@ -89,9 +91,7 @@ typedef uint32 AclMode;                       /* a bitmask of privilege bits */
  *       for further processing by the rewriter and planner.
  *
  *       Utility statements (i.e. non-optimizable statements) have the
- *       utilityStmt field set, and the Query itself is mostly dummy.
- *       DECLARE CURSOR is a special case: it is represented like a SELECT,
- *       but the original DeclareCursorStmt is stored in utilityStmt.
+ *       utilityStmt field set, and the rest of the Query is mostly dummy.
  *
  *       Planning converts a Query tree into a Plan tree headed by a PlannedStmt
  *       node --- the Query structure is not used by the executor.
@@ -108,8 +108,7 @@ typedef struct Query
 
        bool            canSetTag;              /* do I set the command result tag? */
 
-       Node       *utilityStmt;        /* non-null if this is DECLARE CURSOR or a
-                                                                * non-optimizable statement */
+       Node       *utilityStmt;        /* non-null if commandType == CMD_UTILITY */
 
        int                     resultRelation; /* rtable index of target relation for
                                                                 * INSERT/UPDATE/DELETE; 0 for SELECT */
@@ -162,6 +161,15 @@ typedef struct Query
                                                                                 * are only added during rewrite and
                                                                                 * therefore are not written out as
                                                                                 * part of Query. */
+
+       /*
+        * The following two fields identify the portion of the source text string
+        * containing this query.  They are typically only populated in top-level
+        * Queries, not in sub-queries.  When not set, they might both be zero, or
+        * both be -1 meaning "unknown".
+        */
+       int                     stmt_location;  /* start location, or -1 if unknown */
+       int                     stmt_len;               /* length in bytes; 0 means "rest of string" */
 } Query;
 
 
@@ -1307,6 +1315,30 @@ typedef struct TriggerTransition
        bool            isTable;
 } TriggerTransition;
 
+/*****************************************************************************
+ *             Raw Grammar Output Statements
+ *****************************************************************************/
+
+/*
+ *             RawStmt --- container for any one statement's raw parse tree
+ *
+ * Parse analysis converts a raw parse tree headed by a RawStmt node into
+ * an analyzed statement headed by a Query node.  For optimizable statements,
+ * the conversion is complex.  For utility statements, the parser usually just
+ * transfers the raw parse tree (sans RawStmt) into the utilityStmt field of
+ * the Query node, and all the useful work happens at execution time.
+ *
+ * stmt_location/stmt_len identify the portion of the source text string
+ * containing this raw statement (useful for multi-statement strings).
+ */
+typedef struct RawStmt
+{
+       NodeTag         type;
+       Node       *stmt;                       /* raw parse tree */
+       int                     stmt_location;  /* start location, or -1 if unknown */
+       int                     stmt_len;               /* length in bytes; 0 means "rest of string" */
+} RawStmt;
+
 /*****************************************************************************
  *             Optimizable Statements
  *****************************************************************************/
@@ -1474,6 +1506,9 @@ typedef struct SetOperationStmt
  *             statements do need attention from parse analysis, and this is
  *             done by routines in parser/parse_utilcmd.c after ProcessUtility
  *             receives the command for execution.
+ *             DECLARE CURSOR, EXPLAIN, and CREATE TABLE AS are special cases:
+ *             they contain optimizable statements, which get processed normally
+ *             by parser/analyze.c.
  *****************************************************************************/
 
 /*
@@ -1794,7 +1829,7 @@ typedef struct CopyStmt
        NodeTag         type;
        RangeVar   *relation;           /* the relation to copy */
        Node       *query;                      /* the query (SELECT or DML statement with
-                                                                * RETURNING) to copy */
+                                                                * RETURNING) to copy, as a raw parse tree */
        List       *attlist;            /* List of column names (as Strings), or NIL
                                                                 * for all columns */
        bool            is_from;                /* TO or FROM */
@@ -2472,9 +2507,9 @@ typedef struct SecLabelStmt
 /* ----------------------
  *             Declare Cursor Statement
  *
- * Note: the "query" field of DeclareCursorStmt is only used in the raw grammar
- * output.  After parse analysis it's set to null, and the Query points to the
- * DeclareCursorStmt, not vice versa.
+ * The "query" field is initially a raw parse tree, and is converted to a
+ * Query node during parse analysis.  Note that rewriting and planning
+ * of the query are always postponed until execution.
  * ----------------------
  */
 #define CURSOR_OPT_BINARY              0x0001  /* BINARY */
@@ -2493,7 +2528,7 @@ typedef struct DeclareCursorStmt
        NodeTag         type;
        char       *portalname;         /* name of the portal (cursor) */
        int                     options;                /* bitmask of options (see above) */
-       Node       *query;                      /* the raw SELECT query */
+       Node       *query;                      /* the query (see comments above) */
 } DeclareCursorStmt;
 
 /* ----------------------
@@ -2841,7 +2876,7 @@ typedef struct ViewStmt
        NodeTag         type;
        RangeVar   *view;                       /* the view to be created */
        List       *aliases;            /* target column names */
-       Node       *query;                      /* the SELECT query */
+       Node       *query;                      /* the SELECT query (as a raw parse tree) */
        bool            replace;                /* replace an existing view? */
        List       *options;            /* options from WITH clause */
        ViewCheckOption withCheckOption;        /* WITH CHECK OPTION */
@@ -2950,9 +2985,9 @@ typedef struct VacuumStmt
 /* ----------------------
  *             Explain Statement
  *
- * The "query" field is either a raw parse tree (SelectStmt, InsertStmt, etc)
- * or a Query node if parse analysis has been done.  Note that rewriting and
- * planning of the query are always postponed until execution of EXPLAIN.
+ * The "query" field is initially a raw parse tree, and is converted to a
+ * Query node during parse analysis.  Note that rewriting and planning
+ * of the query are always postponed until execution.
  * ----------------------
  */
 typedef struct ExplainStmt
index 692a62698ed08316bb122a994bbe380b808bc63c..6810f8c0993dff974f91eb9647c773367ac12e7a 100644 (file)
  *
  * The output of the planner is a Plan tree headed by a PlannedStmt node.
  * PlannedStmt holds the "one time" information needed by the executor.
+ *
+ * For simplicity in APIs, we also wrap utility statements in PlannedStmt
+ * nodes; in such cases, commandType == CMD_UTILITY, the statement itself
+ * is in the utilityStmt field, and the rest of the struct is mostly dummy.
+ * (We do use canSetTag, stmt_location, stmt_len, and possibly queryId.)
  * ----------------
  */
 typedef struct PlannedStmt
 {
        NodeTag         type;
 
-       CmdType         commandType;    /* select|insert|update|delete */
+       CmdType         commandType;    /* select|insert|update|delete|utility */
 
        uint32          queryId;                /* query identifier (copied from Query) */
 
@@ -60,8 +65,6 @@ typedef struct PlannedStmt
        /* rtable indexes of target relations for INSERT/UPDATE/DELETE */
        List       *resultRelations;    /* integer list of RT indexes, or NIL */
 
-       Node       *utilityStmt;        /* non-null if this is DECLARE CURSOR */
-
        List       *subplans;           /* Plan trees for SubPlan expressions */
 
        Bitmapset  *rewindPlanIDs;      /* indices of subplans that require REWIND */
@@ -73,6 +76,12 @@ typedef struct PlannedStmt
        List       *invalItems;         /* other dependencies, as PlanInvalItems */
 
        int                     nParamExec;             /* number of PARAM_EXEC Params used */
+
+       Node       *utilityStmt;        /* non-null if this is utility stmt */
+
+       /* statement location in source string (copied from Query) */
+       int                     stmt_location;  /* start location, or -1 if unknown */
+       int                     stmt_len;               /* length in bytes; 0 means "rest of string" */
 } PlannedStmt;
 
 /* macro for fetching the Plan associated with a SubPlan node */
index 3bf4edd0bf2685376409a7b25b158289ffa08751..a7e5c55ab4f164f9ea4fe06df72fc621e71ca6a7 100644 (file)
@@ -22,19 +22,19 @@ typedef void (*post_parse_analyze_hook_type) (ParseState *pstate,
 extern PGDLLIMPORT post_parse_analyze_hook_type post_parse_analyze_hook;
 
 
-extern Query *parse_analyze(Node *parseTree, const char *sourceText,
+extern Query *parse_analyze(RawStmt *parseTree, const char *sourceText,
                          Oid *paramTypes, int numParams);
-extern Query *parse_analyze_varparams(Node *parseTree, const char *sourceText,
+extern Query *parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
                                                Oid **paramTypes, int *numParams);
 
 extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
                                  CommonTableExpr *parentCTE,
                                  bool locked_from_parent);
 
-extern Query *transformTopLevelStmt(ParseState *pstate, Node *parseTree);
+extern Query *transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree);
 extern Query *transformStmt(ParseState *pstate, Node *parseTree);
 
-extern bool analyze_requires_snapshot(Node *parseTree);
+extern bool analyze_requires_snapshot(RawStmt *parseTree);
 
 extern const char *LCS_asString(LockClauseStrength strength);
 extern void CheckSelectLocking(Query *qry, LockClauseStrength strength);
index 54da6b65efb1f79878df5940a2d025d48dc96acd..1958be85b72019e985c2de5b93fca32a907a744b 100644 (file)
@@ -47,9 +47,10 @@ typedef enum
 extern int     log_statement;
 
 extern List *pg_parse_query(const char *query_string);
-extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
+extern List *pg_analyze_and_rewrite(RawStmt *parsetree,
+                                          const char *query_string,
                                           Oid *paramTypes, int numParams);
-extern List *pg_analyze_and_rewrite_params(Node *parsetree,
+extern List *pg_analyze_and_rewrite_params(RawStmt *parsetree,
                                                          const char *query_string,
                                                          ParserSetupHook parserSetup,
                                                          void *parserSetupArg);
index 42c52a1c757dbf89d1d8cecd8594b321d30566cb..4f8d35390010b3c932288ccae480266bd61e4eb2 100644 (file)
@@ -24,16 +24,16 @@ typedef enum
 } ProcessUtilityContext;
 
 /* Hook for plugins to get control in ProcessUtility() */
-typedef void (*ProcessUtility_hook_type) (Node *parsetree,
+typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
                                          const char *queryString, ProcessUtilityContext context,
                                                                                                          ParamListInfo params,
                                                                        DestReceiver *dest, char *completionTag);
 extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
 
-extern void ProcessUtility(Node *parsetree, const char *queryString,
+extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                           ProcessUtilityContext context, ParamListInfo params,
                           DestReceiver *dest, char *completionTag);
-extern void standard_ProcessUtility(Node *parsetree, const char *queryString,
+extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                                                ProcessUtilityContext context, ParamListInfo params,
                                                DestReceiver *dest, char *completionTag);
 
@@ -47,6 +47,6 @@ extern const char *CreateCommandTag(Node *parsetree);
 
 extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
 
-extern bool CommandIsReadOnly(Node *parsetree);
+extern bool CommandIsReadOnly(PlannedStmt *pstmt);
 
 #endif   /* UTILITY_H */
index 9ee0bc6e9f11143ab9dc1c2734ce86c307a6e835..84952d56e70d8bb182f0e88b668acbd48049f40b 100644 (file)
@@ -18,6 +18,9 @@
 #include "access/tupdesc.h"
 #include "nodes/params.h"
 
+/* Forward declaration, to avoid including parsenodes.h here */
+struct RawStmt;
+
 #define CACHEDPLANSOURCE_MAGIC         195726186
 #define CACHEDPLAN_MAGIC                       953717834
 
@@ -76,7 +79,7 @@
 typedef struct CachedPlanSource
 {
        int                     magic;                  /* should equal CACHEDPLANSOURCE_MAGIC */
-       Node       *raw_parse_tree; /* output of raw_parser(), or NULL */
+       struct RawStmt *raw_parse_tree;         /* output of raw_parser(), or NULL */
        const char *query_string;       /* source text of query */
        const char *commandTag;         /* command tag (a constant!), or NULL */
        Oid                *param_types;        /* array of parameter type OIDs, or NULL */
@@ -126,8 +129,7 @@ typedef struct CachedPlanSource
 typedef struct CachedPlan
 {
        int                     magic;                  /* should equal CACHEDPLAN_MAGIC */
-       List       *stmt_list;          /* list of statement nodes (PlannedStmts and
-                                                                * bare utility statements) */
+       List       *stmt_list;          /* list of PlannedStmts */
        bool            is_oneshot;             /* is it a "oneshot" plan? */
        bool            is_saved;               /* is CachedPlan in a long-lived context? */
        bool            is_valid;               /* is the stmt_list currently valid? */
@@ -144,10 +146,10 @@ typedef struct CachedPlan
 extern void InitPlanCache(void);
 extern void ResetPlanCache(void);
 
-extern CachedPlanSource *CreateCachedPlan(Node *raw_parse_tree,
+extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
                                 const char *query_string,
                                 const char *commandTag);
-extern CachedPlanSource *CreateOneShotCachedPlan(Node *raw_parse_tree,
+extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
                                                const char *query_string,
                                                const char *commandTag);
 extern void CompleteCachedPlan(CachedPlanSource *plansource,
index 8359416b15ed5c72eb5b7c56736699f1d1d9a5df..dc76acd0a42d6f6b9e405e2024fae4ba1deae523 100644 (file)
@@ -133,7 +133,7 @@ typedef struct PortalData
        /* The query or queries the portal will execute */
        const char *sourceText;         /* text of query (as of 8.4, never NULL) */
        const char *commandTag;         /* command tag for original query */
-       List       *stmts;                      /* PlannedStmts and/or utility statements */
+       List       *stmts;                      /* list of PlannedStmts */
        CachedPlan *cplan;                      /* CachedPlan, if stmts are from one */
 
        ParamListInfo portalParams; /* params to pass to query */
@@ -201,7 +201,6 @@ typedef struct PortalData
  */
 #define PortalGetQueryDesc(portal)     ((portal)->queryDesc)
 #define PortalGetHeapMemory(portal) ((portal)->heap)
-#define PortalGetPrimaryStmt(portal) PortalListGetPrimaryStmt((portal)->stmts)
 
 
 /* Prototypes for functions in utils/mmgr/portalmem.c */
@@ -232,7 +231,7 @@ extern void PortalDefineQuery(Portal portal,
                                  const char *commandTag,
                                  List *stmts,
                                  CachedPlan *cplan);
-extern Node *PortalListGetPrimaryStmt(List *stmts);
+extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
 extern void PortalCreateHoldStore(Portal portal);
 extern void PortalHashTableDeleteAll(void);
 extern bool ThereAreNoReadyPortals(void);
index 192cbcf98323eb9ceaa7bea2b263e8c902006075..bc7b00199efb0832db288260a154179573cb23fe 100644 (file)
@@ -6827,12 +6827,11 @@ exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan)
        if (list_length(cplan->stmt_list) != 1)
                return;
        stmt = (PlannedStmt *) linitial(cplan->stmt_list);
+       Assert(IsA(stmt, PlannedStmt));
 
        /*
         * 2. It must be a RESULT plan --> no scan's required
         */
-       if (!IsA(stmt, PlannedStmt))
-               return;
        if (stmt->commandType != CMD_SELECT)
                return;
        plan = stmt->planTree;