aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/pg_proc.c2
-rw-r--r--src/backend/commands/copy.c39
-rw-r--r--src/backend/commands/createas.c2
-rw-r--r--src/backend/commands/explain.c46
-rw-r--r--src/backend/commands/extension.c22
-rw-r--r--src/backend/commands/foreigncmds.c14
-rw-r--r--src/backend/commands/portalcmds.c50
-rw-r--r--src/backend/commands/prepare.c34
-rw-r--r--src/backend/commands/schemacmds.c21
-rw-r--r--src/backend/commands/tablecmds.c3
-rw-r--r--src/backend/commands/trigger.c10
-rw-r--r--src/backend/commands/view.c15
-rw-r--r--src/backend/executor/execParallel.c4
-rw-r--r--src/backend/executor/functions.c92
-rw-r--r--src/backend/executor/spi.c55
-rw-r--r--src/backend/nodes/copyfuncs.c21
-rw-r--r--src/backend/nodes/equalfuncs.c15
-rw-r--r--src/backend/nodes/outfuncs.c7
-rw-r--r--src/backend/nodes/readfuncs.c7
-rw-r--r--src/backend/optimizer/plan/planner.c11
-rw-r--r--src/backend/optimizer/prep/prepjointree.c6
-rw-r--r--src/backend/optimizer/util/clauses.c4
-rw-r--r--src/backend/parser/analyze.c88
-rw-r--r--src/backend/parser/gram.y58
-rw-r--r--src/backend/parser/parse_clause.c7
-rw-r--r--src/backend/parser/parse_expr.c7
-rw-r--r--src/backend/parser/parse_type.c2
-rw-r--r--src/backend/parser/parser.c3
-rw-r--r--src/backend/rewrite/rewriteDefine.c3
-rw-r--r--src/backend/tcop/postgres.c99
-rw-r--r--src/backend/tcop/pquery.c132
-rw-r--r--src/backend/tcop/utility.c190
-rw-r--r--src/backend/utils/cache/plancache.c60
-rw-r--r--src/backend/utils/mmgr/portalmem.c37
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/commands/copy.h3
-rw-r--r--src/include/commands/explain.h1
-rw-r--r--src/include/commands/portalcmds.h2
-rw-r--r--src/include/commands/prepare.h3
-rw-r--r--src/include/commands/schemacmds.h3
-rw-r--r--src/include/commands/view.h3
-rw-r--r--src/include/executor/execdesc.h9
-rw-r--r--src/include/nodes/nodes.h1
-rw-r--r--src/include/nodes/parsenodes.h65
-rw-r--r--src/include/nodes/plannodes.h15
-rw-r--r--src/include/parser/analyze.h8
-rw-r--r--src/include/tcop/tcopprot.h5
-rw-r--r--src/include/tcop/utility.h8
-rw-r--r--src/include/utils/plancache.h12
-rw-r--r--src/include/utils/portal.h5
-rw-r--r--src/pl/plpgsql/src/pl_exec.c3
51 files changed, 776 insertions, 538 deletions
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 6d8f17db2d1..85cec0cb1cb 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -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,
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index f56b2ac49b0..1fd21627948 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -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,
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 57ef9817653..cee3b4d50b5 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -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);
/*
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c762fb07d4d..ee7046c47b9 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -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)
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index be521484d08..967b52a133f 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -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
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 06b4bc3ba9a..476a023ec54 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -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);
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 71640b7748f..1d3e39299b9 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -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"
@@ -35,21 +37,18 @@
/*
* 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
@@ -69,14 +68,39 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
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.
*/
portal = CreatePortal(cstmt->portalname, false, false);
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;
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index d768cf8dda3..1ff41661a55 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -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;
@@ -71,10 +73,22 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
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 */
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index c475613b1c5..c3b37b26259 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -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();
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f913e87bc8b..e633a50dd2d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -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,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 61666ad1923..b404d1ea16f 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -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);
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e4be5c53320..1f008b07566 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -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");
/*
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 6cf62daab8a..e01fe6da964 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -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);
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 039defa7b85..e4a1da4dbbf 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -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;
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index ee7a7e2d78c..7bd37283b7f 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -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 */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 930f2f1d5f4..7107bbf1642 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index a27e5edf372..ec4bbfc770b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 806d0a93aac..cf0a6059e91 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index dc40d0181e0..e02dd94f055 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -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();
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 207290fd260..f936710171c 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -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;
}
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index be5d3d1104b..7cb1bc9a62c 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -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? */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 10abdc3aff1..59ccdf43d49 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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;
/*
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 5116cbb71a2..8278e742f8d 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9eef550d2ab..e61ba06efe5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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
@@ -91,6 +92,12 @@
* (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)
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index d788ffd7999..624ab41371f 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -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");
/*
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index f62e45f8ac8..73521b93fca 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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;
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index e2c884caae0..6660929dec7 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -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 ||
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index c870229817c..245b4cda3b9 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -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)
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 55900d4b0e1..864d45ff128 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -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")));
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 05b2e57b71d..bb89cce8cb9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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;
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 3b5da73051d..704be399cf7 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -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 */
@@ -93,37 +92,6 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
}
/*
- * 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
*/
void
@@ -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);
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 127dc862e8d..14921013360 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -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);
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index e48a878dc2a..c31c603fbf5 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -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)
{
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 5a15ed21d66..e8ebc4684ca 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -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;
}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 95cfd53eeca..29604b6f4ed 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201612231
+#define CATALOG_VERSION_NO 201701141
#endif
diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h
index 4785631cef9..d63ca0f5e99 100644
--- a/src/include/commands/copy.h
+++ b/src/include/commands/copy.h
@@ -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);
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index 1820dd66bb2..9191e186c15 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -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,
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index 0ae9b7213cd..8f0e6c48f49 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -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,
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index 0a3a8addca1..d8d22edbbcd 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -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);
diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h
index 087878ddc33..f07a389c7f6 100644
--- a/src/include/commands/schemacmds.h
+++ b/src/include/commands/schemacmds.h
@@ -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);
diff --git a/src/include/commands/view.h b/src/include/commands/view.h
index 648665ebe5d..39763913c80 100644
--- a/src/include/commands/view.h
+++ b/src/include/commands/view.h
@@ -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);
diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h
index 2c221255dcb..c99ea818158 100644
--- a/src/include/executor/execdesc.h
+++ b/src/include/executor/execdesc.h
@@ -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 */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a1bb0ac5cb8..4c4319bcabf 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -301,6 +301,7 @@ typedef enum NodeTag
/*
* TAGS FOR STATEMENT NODES (mostly in parsenodes.h)
*/
+ T_RawStmt,
T_Query,
T_PlannedStmt,
T_InsertStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 7ceaa22c33b..edb5cd21521 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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;
@@ -1308,6 +1316,30 @@ typedef struct TriggerTransition
} 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
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 692a62698ed..6810f8c0993 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -31,13 +31,18 @@
*
* 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 */
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index 3bf4edd0bf2..a7e5c55ab4f 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -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);
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 54da6b65efb..1958be85b72 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -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);
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index 42c52a1c757..4f8d3539001 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -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 */
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index 9ee0bc6e9f1..84952d56e70 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -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,
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 8359416b15e..dc76acd0a42 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -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);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 192cbcf9832..bc7b00199ef 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -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;