1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-26 01:22:12 +03:00

Change representation of statement lists, and add statement location info.

This patch makes several changes that improve the consistency of
representation of lists of statements.  It's always been the case
that the output of parse analysis is a list of Query nodes, whatever
the types of the individual statements in the list.  This patch brings
similar consistency to the outputs of raw parsing and planning steps:

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

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

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

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

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

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

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

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

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

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

Discussion: https://postgr.es/m/alpine.DEB.2.20.1612200926310.29821@lancre
This commit is contained in:
Tom Lane
2017-01-14 16:02:35 -05:00
parent 75abb955df
commit ab1f0c8225
53 changed files with 788 additions and 547 deletions

View File

@ -292,7 +292,7 @@ static void pgss_ExecutorRun(QueryDesc *queryDesc,
uint64 count); uint64 count);
static void pgss_ExecutorFinish(QueryDesc *queryDesc); static void pgss_ExecutorFinish(QueryDesc *queryDesc);
static void pgss_ExecutorEnd(QueryDesc *queryDesc); static void pgss_ExecutorEnd(QueryDesc *queryDesc);
static void pgss_ProcessUtility(Node *parsetree, const char *queryString, static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params, ProcessUtilityContext context, ParamListInfo params,
DestReceiver *dest, char *completionTag); DestReceiver *dest, char *completionTag);
static uint32 pgss_hash_fn(const void *key, Size keysize); static uint32 pgss_hash_fn(const void *key, Size keysize);
@ -942,10 +942,12 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
* ProcessUtility hook * ProcessUtility hook
*/ */
static void static void
pgss_ProcessUtility(Node *parsetree, const char *queryString, pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params, ProcessUtilityContext context, ParamListInfo params,
DestReceiver *dest, char *completionTag) DestReceiver *dest, char *completionTag)
{ {
Node *parsetree = pstmt->utilityStmt;
/* /*
* If it's an EXECUTE statement, we don't track it and don't increment the * If it's an EXECUTE statement, we don't track it and don't increment the
* nesting level. This allows the cycles to be charged to the underlying * nesting level. This allows the cycles to be charged to the underlying
@ -979,11 +981,11 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
PG_TRY(); PG_TRY();
{ {
if (prev_ProcessUtility) if (prev_ProcessUtility)
prev_ProcessUtility(parsetree, queryString, prev_ProcessUtility(pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
standard_ProcessUtility(parsetree, queryString, standard_ProcessUtility(pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
nested_level--; nested_level--;
@ -1044,11 +1046,11 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
else else
{ {
if (prev_ProcessUtility) if (prev_ProcessUtility)
prev_ProcessUtility(parsetree, queryString, prev_ProcessUtility(pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
standard_ProcessUtility(parsetree, queryString, standard_ProcessUtility(pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
} }

View File

@ -297,13 +297,14 @@ sepgsql_exec_check_perms(List *rangeTabls, bool abort)
* break whole of the things if nefarious user would use. * break whole of the things if nefarious user would use.
*/ */
static void static void
sepgsql_utility_command(Node *parsetree, sepgsql_utility_command(PlannedStmt *pstmt,
const char *queryString, const char *queryString,
ProcessUtilityContext context, ProcessUtilityContext context,
ParamListInfo params, ParamListInfo params,
DestReceiver *dest, DestReceiver *dest,
char *completionTag) char *completionTag)
{ {
Node *parsetree = pstmt->utilityStmt;
sepgsql_context_info_t saved_context_info = sepgsql_context_info; sepgsql_context_info_t saved_context_info = sepgsql_context_info;
ListCell *cell; ListCell *cell;
@ -362,11 +363,11 @@ sepgsql_utility_command(Node *parsetree,
} }
if (next_ProcessUtility_hook) if (next_ProcessUtility_hook)
(*next_ProcessUtility_hook) (parsetree, queryString, (*next_ProcessUtility_hook) (pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
standard_ProcessUtility(parsetree, queryString, standard_ProcessUtility(pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
} }

View File

@ -934,7 +934,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
querytree_list = NIL; querytree_list = NIL;
foreach(lc, raw_parsetree_list) foreach(lc, raw_parsetree_list)
{ {
Node *parsetree = (Node *) lfirst(lc); RawStmt *parsetree = (RawStmt *) lfirst(lc);
List *querytree_sublist; List *querytree_sublist;
querytree_sublist = pg_analyze_and_rewrite_params(parsetree, querytree_sublist = pg_analyze_and_rewrite_params(parsetree,

View File

@ -287,13 +287,13 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
/* non-export function prototypes */ /* non-export function prototypes */
static CopyState BeginCopy(ParseState *pstate, bool is_from, Relation rel, Node *raw_query, static CopyState BeginCopy(ParseState *pstate, bool is_from, Relation rel,
const Oid queryRelId, List *attnamelist, RawStmt *raw_query, Oid queryRelId, List *attnamelist,
List *options); List *options);
static void EndCopy(CopyState cstate); static void EndCopy(CopyState cstate);
static void ClosePipeToProgram(CopyState cstate); static void ClosePipeToProgram(CopyState cstate);
static CopyState BeginCopyTo(ParseState *pstate, Relation rel, Node *query, static CopyState BeginCopyTo(ParseState *pstate, Relation rel, RawStmt *query,
const Oid queryRelId, const char *filename, bool is_program, Oid queryRelId, const char *filename, bool is_program,
List *attnamelist, List *options); List *attnamelist, List *options);
static void EndCopyTo(CopyState cstate); static void EndCopyTo(CopyState cstate);
static uint64 DoCopyTo(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 * Do not allow the copy if user doesn't have proper permission to access
* the table or the specifically requested columns. * the table or the specifically requested columns.
*/ */
Oid void
DoCopy(ParseState *pstate, const CopyStmt *stmt, uint64 *processed) DoCopy(ParseState *pstate, const CopyStmt *stmt,
int stmt_location, int stmt_len,
uint64 *processed)
{ {
CopyState cstate; CopyState cstate;
bool is_from = stmt->is_from; bool is_from = stmt->is_from;
bool pipe = (stmt->filename == NULL); bool pipe = (stmt->filename == NULL);
Relation rel; Relation rel;
Oid relid; Oid relid;
Node *query = NULL; RawStmt *query = NULL;
List *range_table = NIL; List *range_table = NIL;
/* Disallow COPY to/from file or program except to superusers. */ /* 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->targetList = targetList;
select->fromClause = list_make1(from); 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 * 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); 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; relid = InvalidOid;
rel = NULL; rel = NULL;
} }
@ -981,8 +990,6 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, uint64 *processed)
*/ */
if (rel != NULL) if (rel != NULL)
heap_close(rel, (is_from ? NoLock : AccessShareLock)); heap_close(rel, (is_from ? NoLock : AccessShareLock));
return relid;
} }
/* /*
@ -1364,8 +1371,8 @@ static CopyState
BeginCopy(ParseState *pstate, BeginCopy(ParseState *pstate,
bool is_from, bool is_from,
Relation rel, Relation rel,
Node *raw_query, RawStmt *raw_query,
const Oid queryRelId, Oid queryRelId,
List *attnamelist, List *attnamelist,
List *options) List *options)
{ {
@ -1456,7 +1463,7 @@ BeginCopy(ParseState *pstate,
* function and is executed repeatedly. (See also the same hack in * function and is executed repeatedly. (See also the same hack in
* DECLARE CURSOR and PREPARE.) XXX FIXME someday. * 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); pstate->p_sourcetext, NULL, 0);
/* check that we got back something we can work with */ /* check that we got back something we can work with */
@ -1747,8 +1754,8 @@ EndCopy(CopyState cstate)
static CopyState static CopyState
BeginCopyTo(ParseState *pstate, BeginCopyTo(ParseState *pstate,
Relation rel, Relation rel,
Node *query, RawStmt *query,
const Oid queryRelId, Oid queryRelId,
const char *filename, const char *filename,
bool is_program, bool is_program,
List *attnamelist, List *attnamelist,

View File

@ -326,7 +326,7 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
query = (Query *) linitial(rewritten); query = (Query *) linitial(rewritten);
Assert(query->commandType == CMD_SELECT); Assert(query->commandType == CMD_SELECT);
/* plan the query */ /* plan the query --- note we disallow parallelism */
plan = pg_plan_query(query, 0, params); plan = pg_plan_query(query, 0, params);
/* /*

View File

@ -53,7 +53,8 @@ explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
#define X_CLOSE_IMMEDIATE 2 #define X_CLOSE_IMMEDIATE 2
#define X_NOWHITESPACE 4 #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); const char *queryString, ParamListInfo params);
static void report_triggers(ResultRelInfo *rInfo, bool show_relname, static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
ExplainState *es); ExplainState *es);
@ -245,7 +246,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
/* Explain every plan */ /* Explain every plan */
foreach(l, rewritten) foreach(l, rewritten)
{ {
ExplainOneQuery((Query *) lfirst(l), NULL, es, ExplainOneQuery((Query *) lfirst(l),
CURSOR_OPT_PARALLEL_OK, NULL, es,
queryString, params); queryString, params);
/* Separate plans with an appropriate separator */ /* 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. * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
*/ */
static void static void
ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es, ExplainOneQuery(Query *query, int cursorOptions,
IntoClause *into, ExplainState *es,
const char *queryString, ParamListInfo params) const char *queryString, ParamListInfo params)
{ {
/* planner will not cope with utility statements */ /* 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 an advisor plugin is present, let it manage things */
if (ExplainOneQuery_hook) if (ExplainOneQuery_hook)
(*ExplainOneQuery_hook) (query, into, es, queryString, params); (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
queryString, params);
else else
{ {
PlannedStmt *plan; PlannedStmt *plan;
@ -351,7 +355,7 @@ ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
INSTR_TIME_SET_CURRENT(planstart); INSTR_TIME_SET_CURRENT(planstart);
/* plan the query */ /* 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_SET_CURRENT(planduration);
INSTR_TIME_SUBTRACT(planduration, planstart); 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 * We have to rewrite the contained SELECT and then pass it back to
* ExplainOneQuery. It's probably not really necessary to copy the * ExplainOneQuery. It's probably not really necessary to copy the
* contained parsetree another time, but let's be safe. * contained parsetree another time, but let's be safe.
*
* Like ExecCreateTableAs, disallow parallelism in the plan.
*/ */
CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt; CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
List *rewritten; List *rewritten;
@ -392,7 +398,28 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
Assert(IsA(ctas->query, Query)); Assert(IsA(ctas->query, Query));
rewritten = QueryRewrite((Query *) copyObject(ctas->query)); rewritten = QueryRewrite((Query *) copyObject(ctas->query));
Assert(list_length(rewritten) == 1); 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); queryString, params);
} }
else if (IsA(utilityStmt, ExecuteStmt)) 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, * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt,
* in which case executing the query should result in creating that table. * 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 * This is exported because it's called back from prepare.c in the
* EXPLAIN EXECUTE case, and because an index advisor plugin would need * EXPLAIN EXECUTE case, and because an index advisor plugin would need
* to call it. * to call it.
@ -444,6 +466,8 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
int eflags; int eflags;
int instrument_option = 0; int instrument_option = 0;
Assert(plannedstmt->commandType != CMD_UTILITY);
if (es->analyze && es->timing) if (es->analyze && es->timing)
instrument_option |= INSTRUMENT_TIMER; instrument_option |= INSTRUMENT_TIMER;
else if (es->analyze) else if (es->analyze)

View File

@ -712,7 +712,7 @@ execute_sql_string(const char *sql, const char *filename)
*/ */
foreach(lc1, raw_parsetree_list) foreach(lc1, raw_parsetree_list)
{ {
Node *parsetree = (Node *) lfirst(lc1); RawStmt *parsetree = (RawStmt *) lfirst(lc1);
List *stmt_list; List *stmt_list;
ListCell *lc2; ListCell *lc2;
@ -724,23 +724,17 @@ execute_sql_string(const char *sql, const char *filename)
foreach(lc2, stmt_list) foreach(lc2, stmt_list)
{ {
Node *stmt = (Node *) lfirst(lc2); PlannedStmt *stmt = (PlannedStmt *) lfirst(lc2);
if (IsA(stmt, TransactionStmt))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("transaction control statements are not allowed within an extension script")));
CommandCounterIncrement(); CommandCounterIncrement();
PushActiveSnapshot(GetTransactionSnapshot()); PushActiveSnapshot(GetTransactionSnapshot());
if (IsA(stmt, PlannedStmt) && if (stmt->utilityStmt == NULL)
((PlannedStmt *) stmt)->utilityStmt == NULL)
{ {
QueryDesc *qdesc; QueryDesc *qdesc;
qdesc = CreateQueryDesc((PlannedStmt *) stmt, qdesc = CreateQueryDesc(stmt,
sql, sql,
GetActiveSnapshot(), NULL, GetActiveSnapshot(), NULL,
dest, NULL, 0); dest, NULL, 0);
@ -754,6 +748,11 @@ execute_sql_string(const char *sql, const char *filename)
} }
else 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, ProcessUtility(stmt,
sql, sql,
PROCESS_UTILITY_QUERY, PROCESS_UTILITY_QUERY,
@ -1434,7 +1433,8 @@ CreateExtensionInternal(char *extensionName,
csstmt->authrole = NULL; /* will be created by current user */ csstmt->authrole = NULL; /* will be created by current user */
csstmt->schemaElts = NIL; csstmt->schemaElts = NIL;
csstmt->if_not_exists = false; csstmt->if_not_exists = false;
CreateSchemaCommand(csstmt, NULL); CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
-1, -1);
/* /*
* CreateSchemaCommand includes CommandCounterIncrement, so new * CreateSchemaCommand includes CommandCounterIncrement, so new

View File

@ -1572,7 +1572,9 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
*/ */
foreach(lc2, raw_parsetree_list) 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 * 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 */ /* Ensure creation schema is the one given in IMPORT statement */
cstmt->base.relation->schemaname = pstrdup(stmt->local_schema); 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 */ /* Execute statement */
ProcessUtility((Node *) cstmt, ProcessUtility(pstmt,
cmd, cmd,
PROCESS_UTILITY_SUBCOMMAND, NULL, PROCESS_UTILITY_SUBCOMMAND, NULL,
None_Receiver, NULL); None_Receiver, NULL);

View File

@ -27,7 +27,9 @@
#include "commands/portalcmds.h" #include "commands/portalcmds.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "executor/tstoreReceiver.h" #include "executor/tstoreReceiver.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h" #include "tcop/pquery.h"
#include "tcop/tcopprot.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/snapmgr.h" #include "utils/snapmgr.h"
@ -35,21 +37,18 @@
/* /*
* PerformCursorOpen * PerformCursorOpen
* Execute SQL DECLARE CURSOR command. * 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 void
PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params,
const char *queryString, bool isTopLevel) const char *queryString, bool isTopLevel)
{ {
DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt; Query *query = (Query *) cstmt->query;
List *rewritten;
PlannedStmt *plan;
Portal portal; Portal portal;
MemoryContext oldContext; MemoryContext oldContext;
if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt)) Assert(IsA(query, Query)); /* else parse analysis wasn't done */
elog(ERROR, "PerformCursorOpen called for non-cursor query");
/* /*
* Disallow empty-string cursor name (conflicts with protocol-level * Disallow empty-string cursor name (conflicts with protocol-level
@ -68,6 +67,32 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
if (!(cstmt->options & CURSOR_OPT_HOLD)) if (!(cstmt->options & CURSOR_OPT_HOLD))
RequireTransactionChain(isTopLevel, "DECLARE CURSOR"); 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. * Create a portal and copy the plan and queryString into its memory.
*/ */
@ -75,8 +100,7 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
stmt = copyObject(stmt); plan = copyObject(plan);
stmt->utilityStmt = NULL; /* make it look like plain SELECT */
queryString = pstrdup(queryString); queryString = pstrdup(queryString);
@ -84,7 +108,7 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
NULL, NULL,
queryString, queryString,
"SELECT", /* cursor's query is always a SELECT */ "SELECT", /* cursor's query is always a SELECT */
list_make1(stmt), list_make1(plan),
NULL); NULL);
/*---------- /*----------
@ -111,8 +135,8 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
portal->cursorOptions = cstmt->options; portal->cursorOptions = cstmt->options;
if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL))) if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
{ {
if (stmt->rowMarks == NIL && if (plan->rowMarks == NIL &&
ExecSupportsBackwardScan(stmt->planTree)) ExecSupportsBackwardScan(plan->planTree))
portal->cursorOptions |= CURSOR_OPT_SCROLL; portal->cursorOptions |= CURSOR_OPT_SCROLL;
else else
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;

View File

@ -52,8 +52,10 @@ static Datum build_regtype_array(Oid *param_types, int num_params);
* Implements the 'PREPARE' utility statement. * Implements the 'PREPARE' utility statement.
*/ */
void void
PrepareQuery(PrepareStmt *stmt, const char *queryString) PrepareQuery(PrepareStmt *stmt, const char *queryString,
int stmt_location, int stmt_len)
{ {
RawStmt *rawstmt;
CachedPlanSource *plansource; CachedPlanSource *plansource;
Oid *argtypes = NULL; Oid *argtypes = NULL;
int nargs; int nargs;
@ -70,11 +72,23 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
(errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION), (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
errmsg("invalid statement name: must not be empty"))); 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 * Create the CachedPlanSource before we do parse analysis, since it needs
* to see the unmodified raw parse tree. * to see the unmodified raw parse tree.
*/ */
plansource = CreateCachedPlan(stmt->query, queryString, plansource = CreateCachedPlan(rawstmt, queryString,
CreateCommandTag(stmt->query)); CreateCommandTag(stmt->query));
/* Transform list of TypeNames to array of type OIDs */ /* 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 * Analyze the statement using these parameter types (any parameters
* passed in from above us will not be visible to it), allowing * passed in from above us will not be visible to it), allowing
* information about unknown parameters to be deduced from context. * 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), query = parse_analyze_varparams(rawstmt, queryString,
queryString,
&argtypes, &nargs); &argtypes, &nargs);
/* /*
@ -256,9 +266,8 @@ ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("prepared statement is not a SELECT"))); errmsg("prepared statement is not a SELECT")));
pstmt = (PlannedStmt *) linitial(plan_list); pstmt = (PlannedStmt *) linitial(plan_list);
if (!IsA(pstmt, PlannedStmt) || Assert(IsA(pstmt, PlannedStmt));
pstmt->commandType != CMD_SELECT || if (pstmt->commandType != CMD_SELECT)
pstmt->utilityStmt != NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("prepared statement is not a SELECT"))); errmsg("prepared statement is not a SELECT")));
@ -664,10 +673,11 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
{ {
PlannedStmt *pstmt = (PlannedStmt *) lfirst(p); 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); ExplainOnePlan(pstmt, into, es, query_string, paramLI, NULL);
else 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 */ /* No need for CommandCounterIncrement, as ExplainOnePlan did it */

View File

@ -40,9 +40,16 @@ static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerI
/* /*
* CREATE SCHEMA * 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 Oid
CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
int stmt_location, int stmt_len)
{ {
const char *schemaName = stmt->schemaname; const char *schemaName = stmt->schemaname;
Oid namespaceId; Oid namespaceId;
@ -172,14 +179,24 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
foreach(parsetree_item, parsetree_list) foreach(parsetree_item, parsetree_list)
{ {
Node *stmt = (Node *) lfirst(parsetree_item); 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 */ /* do this step */
ProcessUtility(stmt, ProcessUtility(wrapper,
queryString, queryString,
PROCESS_UTILITY_SUBCOMMAND, PROCESS_UTILITY_SUBCOMMAND,
NULL, NULL,
None_Receiver, None_Receiver,
NULL); NULL);
/* make sure later steps can see the object created here */ /* make sure later steps can see the object created here */
CommandCounterIncrement(); CommandCounterIncrement();
} }

View File

@ -9285,7 +9285,8 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
querytree_list = NIL; querytree_list = NIL;
foreach(list_item, raw_parsetree_list) 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)) if (IsA(stmt, IndexStmt))
querytree_list = lappend(querytree_list, querytree_list = lappend(querytree_list,

View File

@ -1078,6 +1078,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
AlterTableStmt *atstmt = makeNode(AlterTableStmt); AlterTableStmt *atstmt = makeNode(AlterTableStmt);
AlterTableCmd *atcmd = makeNode(AlterTableCmd); AlterTableCmd *atcmd = makeNode(AlterTableCmd);
Constraint *fkcon = makeNode(Constraint); Constraint *fkcon = makeNode(Constraint);
PlannedStmt *wrapper = makeNode(PlannedStmt);
ereport(NOTICE, ereport(NOTICE,
(errmsg("converting trigger group into constraint \"%s\" %s", (errmsg("converting trigger group into constraint \"%s\" %s",
@ -1167,8 +1168,15 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->skip_validation = false; fkcon->skip_validation = false;
fkcon->initially_valid = true; 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 */ /* ... and execute it */
ProcessUtility((Node *) atstmt, ProcessUtility(wrapper,
"(generated ALTER TABLE ADD FOREIGN KEY command)", "(generated ALTER TABLE ADD FOREIGN KEY command)",
PROCESS_UTILITY_SUBCOMMAND, NULL, PROCESS_UTILITY_SUBCOMMAND, NULL,
None_Receiver, NULL); None_Receiver, NULL);

View File

@ -414,8 +414,10 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
* Execute a CREATE VIEW command. * Execute a CREATE VIEW command.
*/ */
ObjectAddress ObjectAddress
DefineView(ViewStmt *stmt, const char *queryString) DefineView(ViewStmt *stmt, const char *queryString,
int stmt_location, int stmt_len)
{ {
RawStmt *rawstmt;
Query *viewParse; Query *viewParse;
RangeVar *view; RangeVar *view;
ListCell *cell; ListCell *cell;
@ -429,8 +431,12 @@ DefineView(ViewStmt *stmt, const char *queryString)
* Since parse analysis scribbles on its input, copy the raw parse tree; * Since parse analysis scribbles on its input, copy the raw parse tree;
* this ensures we don't corrupt a prepared statement, for example. * this ensures we don't corrupt a prepared statement, for example.
*/ */
viewParse = parse_analyze((Node *) copyObject(stmt->query), rawstmt = makeNode(RawStmt);
queryString, NULL, 0); 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. * The grammar should ensure that the result is a single SELECT Query.
@ -443,8 +449,7 @@ DefineView(ViewStmt *stmt, const char *queryString)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("views must not contain SELECT INTO"))); errmsg("views must not contain SELECT INTO")));
if (viewParse->commandType != CMD_SELECT || if (viewParse->commandType != CMD_SELECT)
viewParse->utilityStmt != NULL)
elog(ERROR, "unexpected parse analysis result"); elog(ERROR, "unexpected parse analysis result");
/* /*

View File

@ -156,13 +156,15 @@ ExecSerializePlan(Plan *plan, EState *estate)
pstmt->planTree = plan; pstmt->planTree = plan;
pstmt->rtable = estate->es_range_table; pstmt->rtable = estate->es_range_table;
pstmt->resultRelations = NIL; pstmt->resultRelations = NIL;
pstmt->utilityStmt = NULL;
pstmt->subplans = NIL; pstmt->subplans = NIL;
pstmt->rewindPlanIDs = NULL; pstmt->rewindPlanIDs = NULL;
pstmt->rowMarks = NIL; pstmt->rowMarks = NIL;
pstmt->relationOids = NIL; pstmt->relationOids = NIL;
pstmt->invalItems = NIL; /* workers can't replan anyway... */ pstmt->invalItems = NIL; /* workers can't replan anyway... */
pstmt->nParamExec = estate->es_plannedstmt->nParamExec; 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 serialized copy of our dummy PlannedStmt. */
return nodeToString(pstmt); return nodeToString(pstmt);

View File

@ -66,7 +66,7 @@ typedef struct execution_state
ExecStatus status; ExecStatus status;
bool setsResult; /* true if this query produces func's result */ bool setsResult; /* true if this query produces func's result */
bool lazyEval; /* true if should fetch one row at a time */ 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 */ QueryDesc *qd; /* null unless status == RUN */
} execution_state; } execution_state;
@ -487,16 +487,24 @@ init_execution_state(List *queryTree_list,
foreach(lc2, qtlist) foreach(lc2, qtlist)
{ {
Query *queryTree = (Query *) lfirst(lc2); Query *queryTree = (Query *) lfirst(lc2);
Node *stmt; PlannedStmt *stmt;
execution_state *newes; execution_state *newes;
Assert(IsA(queryTree, Query)); Assert(IsA(queryTree, Query));
/* Plan the query if needed */ /* Plan the query if needed */
if (queryTree->commandType == CMD_UTILITY) 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 else
stmt = (Node *) pg_plan_query(queryTree, stmt = pg_plan_query(queryTree,
fcache->readonly_func ? CURSOR_OPT_PARALLEL_OK : 0, fcache->readonly_func ? CURSOR_OPT_PARALLEL_OK : 0,
NULL); NULL);
@ -504,28 +512,31 @@ init_execution_state(List *queryTree_list,
* Precheck all commands for validity in a function. This should * Precheck all commands for validity in a function. This should
* generally match the restrictions spi.c applies. * generally match the restrictions spi.c applies.
*/ */
if (IsA(stmt, CopyStmt) && if (stmt->commandType == CMD_UTILITY)
((CopyStmt *) stmt)->filename == NULL) {
if (IsA(stmt->utilityStmt, CopyStmt) &&
((CopyStmt *) stmt->utilityStmt)->filename == NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot COPY to/from client in a SQL function"))); errmsg("cannot COPY to/from client in a SQL function")));
if (IsA(stmt, TransactionStmt)) if (IsA(stmt->utilityStmt, TransactionStmt))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */ /* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a SQL function", errmsg("%s is not allowed in a SQL function",
CreateCommandTag(stmt)))); CreateCommandTag(stmt->utilityStmt))));
}
if (fcache->readonly_func && !CommandIsReadOnly(stmt)) if (fcache->readonly_func && !CommandIsReadOnly(stmt))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */ /* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function", errmsg("%s is not allowed in a non-volatile function",
CreateCommandTag(stmt)))); CreateCommandTag((Node *) stmt))));
if (IsInParallelMode() && !CommandIsReadOnly(stmt)) if (IsInParallelMode() && !CommandIsReadOnly(stmt))
PreventCommandIfParallelMode(CreateCommandTag(stmt)); PreventCommandIfParallelMode(CreateCommandTag((Node *) stmt));
/* OK, build the execution_state for this query */ /* OK, build the execution_state for this query */
newes = (execution_state *) palloc(sizeof(execution_state)); newes = (execution_state *) palloc(sizeof(execution_state));
@ -569,16 +580,10 @@ init_execution_state(List *queryTree_list,
{ {
lasttages->setsResult = true; lasttages->setsResult = true;
if (lazyEvalOK && if (lazyEvalOK &&
IsA(lasttages->stmt, PlannedStmt)) lasttages->stmt->commandType == CMD_SELECT &&
{ !lasttages->stmt->hasModifyingCTE)
PlannedStmt *ps = (PlannedStmt *) lasttages->stmt;
if (ps->commandType == CMD_SELECT &&
ps->utilityStmt == NULL &&
!ps->hasModifyingCTE)
fcache->lazyEval = lasttages->lazyEval = true; fcache->lazyEval = lasttages->lazyEval = true;
} }
}
return eslist; return eslist;
} }
@ -704,7 +709,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
flat_query_list = NIL; flat_query_list = NIL;
foreach(lc, raw_parsetree_list) foreach(lc, raw_parsetree_list)
{ {
Node *parsetree = (Node *) lfirst(lc); RawStmt *parsetree = (RawStmt *) lfirst(lc);
List *queryTree_sublist; List *queryTree_sublist;
queryTree_sublist = pg_analyze_and_rewrite_params(parsetree, queryTree_sublist = pg_analyze_and_rewrite_params(parsetree,
@ -801,22 +806,15 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
else else
dest = None_Receiver; dest = None_Receiver;
if (IsA(es->stmt, PlannedStmt)) es->qd = CreateQueryDesc(es->stmt,
es->qd = CreateQueryDesc((PlannedStmt *) es->stmt,
fcache->src, fcache->src,
GetActiveSnapshot(), GetActiveSnapshot(),
InvalidSnapshot, InvalidSnapshot,
dest, dest,
fcache->paramLI, 0); fcache->paramLI, 0);
else
es->qd = CreateUtilityQueryDesc(es->stmt,
fcache->src,
GetActiveSnapshot(),
dest,
fcache->paramLI);
/* Utility commands don't need Executor. */ /* 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 * 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; bool result;
if (es->qd->utilitystmt) if (es->qd->operation == CMD_UTILITY)
{ {
/* ProcessUtility needs the PlannedStmt for DECLARE CURSOR */ ProcessUtility(es->qd->plannedstmt,
ProcessUtility((es->qd->plannedstmt ?
(Node *) es->qd->plannedstmt :
es->qd->utilitystmt),
fcache->src, fcache->src,
PROCESS_UTILITY_QUERY, PROCESS_UTILITY_QUERY,
es->qd->params, es->qd->params,
@ -882,7 +877,7 @@ postquel_end(execution_state *es)
es->status = F_EXEC_DONE; es->status = F_EXEC_DONE;
/* Utility commands don't need Executor. */ /* Utility commands don't need Executor. */
if (es->qd->utilitystmt == NULL) if (es->qd->operation != CMD_UTILITY)
{ {
ExecutorFinish(es->qd); ExecutorFinish(es->qd);
ExecutorEnd(es->qd); ExecutorEnd(es->qd);
@ -1576,8 +1571,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
* entities. * entities.
*/ */
if (parse && if (parse &&
parse->commandType == CMD_SELECT && parse->commandType == CMD_SELECT)
parse->utilityStmt == NULL)
{ {
tlist_ptr = &parse->targetList; tlist_ptr = &parse->targetList;
tlist = parse->targetList; tlist = parse->targetList;

View File

@ -1232,7 +1232,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL))) if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
{ {
if (list_length(stmt_list) == 1 && 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 && ((PlannedStmt *) linitial(stmt_list))->rowMarks == NIL &&
ExecSupportsBackwardScan(((PlannedStmt *) linitial(stmt_list))->planTree)) ExecSupportsBackwardScan(((PlannedStmt *) linitial(stmt_list))->planTree))
portal->cursorOptions |= CURSOR_OPT_SCROLL; 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 (portal->cursorOptions & CURSOR_OPT_SCROLL)
{ {
if (list_length(stmt_list) == 1 && 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) ((PlannedStmt *) linitial(stmt_list))->rowMarks != NIL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@ -1270,7 +1270,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
foreach(lc, stmt_list) foreach(lc, stmt_list)
{ {
Node *pstmt = (Node *) lfirst(lc); PlannedStmt *pstmt = (PlannedStmt *) lfirst(lc);
if (!CommandIsReadOnly(pstmt)) if (!CommandIsReadOnly(pstmt))
{ {
@ -1279,9 +1279,9 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */ /* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function", errmsg("%s is not allowed in a non-volatile function",
CreateCommandTag(pstmt)))); CreateCommandTag((Node *) pstmt))));
else 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) foreach(list_item, raw_parsetree_list)
{ {
Node *parsetree = (Node *) lfirst(list_item); RawStmt *parsetree = (RawStmt *) lfirst(list_item);
List *stmt_list; List *stmt_list;
CachedPlanSource *plansource; CachedPlanSource *plansource;
@ -1767,7 +1767,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
*/ */
plansource = CreateCachedPlan(parsetree, plansource = CreateCachedPlan(parsetree,
src, src,
CreateCommandTag(parsetree)); CreateCommandTag(parsetree->stmt));
/* /*
* Parameter datatypes are driven by parserSetup hook if provided, * 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) foreach(list_item, raw_parsetree_list)
{ {
Node *parsetree = (Node *) lfirst(list_item); RawStmt *parsetree = (RawStmt *) lfirst(list_item);
CachedPlanSource *plansource; CachedPlanSource *plansource;
plansource = CreateOneShotCachedPlan(parsetree, plansource = CreateOneShotCachedPlan(parsetree,
src, src,
CreateCommandTag(parsetree)); CreateCommandTag(parsetree->stmt));
plancache_list = lappend(plancache_list, plansource); plancache_list = lappend(plancache_list, plansource);
} }
@ -1959,7 +1959,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
*/ */
if (plan->oneshot) if (plan->oneshot)
{ {
Node *parsetree = plansource->raw_parse_tree; RawStmt *parsetree = plansource->raw_parse_tree;
const char *src = plansource->query_string; const char *src = plansource->query_string;
List *stmt_list; List *stmt_list;
@ -2018,26 +2018,19 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
foreach(lc2, stmt_list) foreach(lc2, stmt_list)
{ {
Node *stmt = (Node *) lfirst(lc2); PlannedStmt *stmt = (PlannedStmt *) lfirst(lc2);
bool canSetTag; bool canSetTag = stmt->canSetTag;
DestReceiver *dest; DestReceiver *dest;
_SPI_current->processed = 0; _SPI_current->processed = 0;
_SPI_current->lastoid = InvalidOid; _SPI_current->lastoid = InvalidOid;
_SPI_current->tuptable = NULL; _SPI_current->tuptable = NULL;
if (IsA(stmt, PlannedStmt)) if (stmt->utilityStmt)
{ {
canSetTag = ((PlannedStmt *) stmt)->canSetTag; if (IsA(stmt->utilityStmt, CopyStmt))
}
else
{ {
/* utilities are canSetTag if only thing in list */ CopyStmt *cstmt = (CopyStmt *) stmt->utilityStmt;
canSetTag = (list_length(stmt_list) == 1);
if (IsA(stmt, CopyStmt))
{
CopyStmt *cstmt = (CopyStmt *) stmt;
if (cstmt->filename == NULL) if (cstmt->filename == NULL)
{ {
@ -2045,7 +2038,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
goto fail; goto fail;
} }
} }
else if (IsA(stmt, TransactionStmt)) else if (IsA(stmt->utilityStmt, TransactionStmt))
{ {
my_res = SPI_ERROR_TRANSACTION; my_res = SPI_ERROR_TRANSACTION;
goto fail; goto fail;
@ -2057,10 +2050,10 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */ /* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function", errmsg("%s is not allowed in a non-volatile function",
CreateCommandTag(stmt)))); CreateCommandTag((Node *) stmt))));
if (IsInParallelMode() && !CommandIsReadOnly(stmt)) if (IsInParallelMode() && !CommandIsReadOnly(stmt))
PreventCommandIfParallelMode(CreateCommandTag(stmt)); PreventCommandIfParallelMode(CreateCommandTag((Node *) stmt));
/* /*
* If not read-only mode, advance the command counter before each * 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); dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone);
if (IsA(stmt, PlannedStmt) && if (stmt->utilityStmt == NULL)
((PlannedStmt *) stmt)->utilityStmt == NULL)
{ {
QueryDesc *qdesc; QueryDesc *qdesc;
Snapshot snap; Snapshot snap;
@ -2085,7 +2077,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
else else
snap = InvalidSnapshot; snap = InvalidSnapshot;
qdesc = CreateQueryDesc((PlannedStmt *) stmt, qdesc = CreateQueryDesc(stmt,
plansource->query_string, plansource->query_string,
snap, crosscheck_snapshot, snap, crosscheck_snapshot,
dest, dest,
@ -2116,9 +2108,9 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
* Some utility statements return a row count, even though the * Some utility statements return a row count, even though the
* tuples are not returned to the caller. * 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) if (strncmp(completionTag, "SELECT ", 7) == 0)
_SPI_current->processed = _SPI_current->processed =
@ -2141,7 +2133,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
if (ctastmt->is_select_into) if (ctastmt->is_select_into)
res = SPI_OK_SELINTO; res = SPI_OK_SELINTO;
} }
else if (IsA(stmt, CopyStmt)) else if (IsA(stmt->utilityStmt, CopyStmt))
{ {
Assert(strncmp(completionTag, "COPY ", 5) == 0); Assert(strncmp(completionTag, "COPY ", 5) == 0);
_SPI_current->processed = pg_strtouint64(completionTag + 5, _SPI_current->processed = pg_strtouint64(completionTag + 5,
@ -2270,7 +2262,6 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
switch (operation) switch (operation)
{ {
case CMD_SELECT: case CMD_SELECT:
Assert(queryDesc->plannedstmt->utilityStmt == NULL);
if (queryDesc->dest->mydest != DestSPI) if (queryDesc->dest->mydest != DestSPI)
{ {
/* Don't return SPI_OK_SELECT if we're discarding result */ /* Don't return SPI_OK_SELECT if we're discarding result */

View File

@ -90,13 +90,15 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(planTree);
COPY_NODE_FIELD(rtable); COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(utilityStmt);
COPY_NODE_FIELD(subplans); COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs); COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks); COPY_NODE_FIELD(rowMarks);
COPY_NODE_FIELD(relationOids); COPY_NODE_FIELD(relationOids);
COPY_NODE_FIELD(invalItems); COPY_NODE_FIELD(invalItems);
COPY_SCALAR_FIELD(nParamExec); COPY_SCALAR_FIELD(nParamExec);
COPY_NODE_FIELD(utilityStmt);
COPY_LOCATION_FIELD(stmt_location);
COPY_LOCATION_FIELD(stmt_len);
return newnode; return newnode;
} }
@ -2767,6 +2769,20 @@ _copyQuery(const Query *from)
COPY_NODE_FIELD(setOperations); COPY_NODE_FIELD(setOperations);
COPY_NODE_FIELD(constraintDeps); COPY_NODE_FIELD(constraintDeps);
COPY_NODE_FIELD(withCheckOptions); 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; return newnode;
} }
@ -4728,6 +4744,9 @@ copyObject(const void *from)
case T_Query: case T_Query:
retval = _copyQuery(from); retval = _copyQuery(from);
break; break;
case T_RawStmt:
retval = _copyRawStmt(from);
break;
case T_InsertStmt: case T_InsertStmt:
retval = _copyInsertStmt(from); retval = _copyInsertStmt(from);
break; break;

View File

@ -946,6 +946,18 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_NODE_FIELD(setOperations); COMPARE_NODE_FIELD(setOperations);
COMPARE_NODE_FIELD(constraintDeps); COMPARE_NODE_FIELD(constraintDeps);
COMPARE_NODE_FIELD(withCheckOptions); 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; return true;
} }
@ -3015,6 +3027,9 @@ equal(const void *a, const void *b)
case T_Query: case T_Query:
retval = _equalQuery(a, b); retval = _equalQuery(a, b);
break; break;
case T_RawStmt:
retval = _equalRawStmt(a, b);
break;
case T_InsertStmt: case T_InsertStmt:
retval = _equalInsertStmt(a, b); retval = _equalInsertStmt(a, b);
break; break;

View File

@ -252,13 +252,15 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(planTree);
WRITE_NODE_FIELD(rtable); WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(utilityStmt);
WRITE_NODE_FIELD(subplans); WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks); WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(relationOids); WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems); WRITE_NODE_FIELD(invalItems);
WRITE_INT_FIELD(nParamExec); 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(rowMarks);
WRITE_NODE_FIELD(setOperations); WRITE_NODE_FIELD(setOperations);
WRITE_NODE_FIELD(constraintDeps); WRITE_NODE_FIELD(constraintDeps);
/* withCheckOptions intentionally omitted, see comment in parsenodes.h */
WRITE_LOCATION_FIELD(stmt_location);
WRITE_LOCATION_FIELD(stmt_len);
} }
static void static void

View File

@ -262,6 +262,9 @@ _readQuery(void)
READ_NODE_FIELD(rowMarks); READ_NODE_FIELD(rowMarks);
READ_NODE_FIELD(setOperations); READ_NODE_FIELD(setOperations);
READ_NODE_FIELD(constraintDeps); READ_NODE_FIELD(constraintDeps);
/* withCheckOptions intentionally omitted, see comment in parsenodes.h */
READ_LOCATION_FIELD(stmt_location);
READ_LOCATION_FIELD(stmt_len);
READ_DONE(); READ_DONE();
} }
@ -1415,13 +1418,15 @@ _readPlannedStmt(void)
READ_NODE_FIELD(planTree); READ_NODE_FIELD(planTree);
READ_NODE_FIELD(rtable); READ_NODE_FIELD(rtable);
READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(utilityStmt);
READ_NODE_FIELD(subplans); READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs); READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks); READ_NODE_FIELD(rowMarks);
READ_NODE_FIELD(relationOids); READ_NODE_FIELD(relationOids);
READ_NODE_FIELD(invalItems); READ_NODE_FIELD(invalItems);
READ_INT_FIELD(nParamExec); READ_INT_FIELD(nParamExec);
READ_NODE_FIELD(utilityStmt);
READ_LOCATION_FIELD(stmt_location);
READ_LOCATION_FIELD(stmt_len);
READ_DONE(); READ_DONE();
} }

View File

@ -193,11 +193,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
ListCell *lp, ListCell *lp,
*lr; *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 * 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, * 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 && IsUnderPostmaster &&
dynamic_shared_memory_type != DSM_IMPL_NONE && dynamic_shared_memory_type != DSM_IMPL_NONE &&
parse->commandType == CMD_SELECT && parse->commandType == CMD_SELECT &&
parse->utilityStmt == NULL &&
!parse->hasModifyingCTE && !parse->hasModifyingCTE &&
max_parallel_workers_per_gather > 0 && max_parallel_workers_per_gather > 0 &&
!IsParallelWorker() && !IsParallelWorker() &&
@ -421,13 +415,16 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->planTree = top_plan; result->planTree = top_plan;
result->rtable = glob->finalrtable; result->rtable = glob->finalrtable;
result->resultRelations = glob->resultRelations; result->resultRelations = glob->resultRelations;
result->utilityStmt = parse->utilityStmt;
result->subplans = glob->subplans; result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs; result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks; result->rowMarks = glob->finalrowmarks;
result->relationOids = glob->relationOids; result->relationOids = glob->relationOids;
result->invalItems = glob->invalItems; result->invalItems = glob->invalItems;
result->nParamExec = glob->nParamExec; 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; return result;
} }

View File

@ -1409,8 +1409,7 @@ is_simple_subquery(Query *subquery, RangeTblEntry *rte,
* Let's just make sure it's a valid subselect ... * Let's just make sure it's a valid subselect ...
*/ */
if (!IsA(subquery, Query) || if (!IsA(subquery, Query) ||
subquery->commandType != CMD_SELECT || subquery->commandType != CMD_SELECT)
subquery->utilityStmt != NULL)
elog(ERROR, "subquery is bogus"); 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 ... */ /* Let's just make sure it's a valid subselect ... */
if (!IsA(subquery, Query) || if (!IsA(subquery, Query) ||
subquery->commandType != CMD_SELECT || subquery->commandType != CMD_SELECT)
subquery->utilityStmt != NULL)
elog(ERROR, "subquery is bogus"); elog(ERROR, "subquery is bogus");
/* Is it a set-operation query at all? */ /* Is it a set-operation query at all? */

View File

@ -4479,7 +4479,6 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
*/ */
if (!IsA(querytree, Query) || if (!IsA(querytree, Query) ||
querytree->commandType != CMD_SELECT || querytree->commandType != CMD_SELECT ||
querytree->utilityStmt ||
querytree->hasAggs || querytree->hasAggs ||
querytree->hasWindowFuncs || querytree->hasWindowFuncs ||
querytree->hasTargetSRFs || querytree->hasTargetSRFs ||
@ -5006,8 +5005,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* The single command must be a plain SELECT. * The single command must be a plain SELECT.
*/ */
if (!IsA(querytree, Query) || if (!IsA(querytree, Query) ||
querytree->commandType != CMD_SELECT || querytree->commandType != CMD_SELECT)
querytree->utilityStmt)
goto fail; goto fail;
/* /*

View File

@ -48,6 +48,7 @@
/* Hook for plugins to get control at end of parse analysis */ /* Hook for plugins to get control at end of parse analysis */
post_parse_analyze_hook_type post_parse_analyze_hook = NULL; 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 *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
static List *transformInsertRow(ParseState *pstate, List *exprlist, 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. * a dummy CMD_UTILITY Query node.
*/ */
Query * Query *
parse_analyze(Node *parseTree, const char *sourceText, parse_analyze(RawStmt *parseTree, const char *sourceText,
Oid *paramTypes, int numParams) Oid *paramTypes, int numParams)
{ {
ParseState *pstate = make_parsestate(NULL); ParseState *pstate = make_parsestate(NULL);
@ -123,7 +124,7 @@ parse_analyze(Node *parseTree, const char *sourceText,
* be modified or enlarged (via repalloc). * be modified or enlarged (via repalloc).
*/ */
Query * Query *
parse_analyze_varparams(Node *parseTree, const char *sourceText, parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
Oid **paramTypes, int *numParams) Oid **paramTypes, int *numParams)
{ {
ParseState *pstate = make_parsestate(NULL); ParseState *pstate = make_parsestate(NULL);
@ -174,14 +175,35 @@ parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
* transformTopLevelStmt - * transformTopLevelStmt -
* transform a Parse tree into a Query tree. * 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 * 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 * convert SELECT ... INTO into CREATE TABLE AS. Since utility statements
* aren't allowed within larger statements, this is only allowed at the top * 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 * of the parse tree, and so we only try it before entering the recursive
* transformStmt() processing. * transformStmt() processing.
*/ */
Query * static Query *
transformTopLevelStmt(ParseState *pstate, Node *parseTree) transformOptionalSelectInto(ParseState *pstate, Node *parseTree)
{ {
if (IsA(parseTree, SelectStmt)) if (IsA(parseTree, SelectStmt))
{ {
@ -318,11 +340,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
* Classification here should match transformStmt(). * Classification here should match transformStmt().
*/ */
bool bool
analyze_requires_snapshot(Node *parseTree) analyze_requires_snapshot(RawStmt *parseTree)
{ {
bool result; bool result;
switch (nodeTag(parseTree)) switch (nodeTag(parseTree->stmt))
{ {
/* /*
* Optimizable statements * Optimizable statements
@ -338,10 +360,6 @@ analyze_requires_snapshot(Node *parseTree)
* Special cases * Special cases
*/ */
case T_DeclareCursorStmt: case T_DeclareCursorStmt:
/* yes, because it's analyzed just like SELECT */
result = true;
break;
case T_ExplainStmt: case T_ExplainStmt:
case T_CreateTableAsStmt: case T_CreateTableAsStmt:
/* yes, because we must analyze the contained statement */ /* yes, because we must analyze the contained statement */
@ -563,8 +581,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
/* The grammar should have produced a SELECT */ /* The grammar should have produced a SELECT */
if (!IsA(selectQuery, Query) || if (!IsA(selectQuery, Query) ||
selectQuery->commandType != CMD_SELECT || selectQuery->commandType != CMD_SELECT)
selectQuery->utilityStmt != NULL)
elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT"); elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT");
/* /*
@ -2344,17 +2361,17 @@ transformReturningList(ParseState *pstate, List *returningList)
* transformDeclareCursorStmt - * transformDeclareCursorStmt -
* transform a DECLARE CURSOR Statement * transform a DECLARE CURSOR Statement
* *
* DECLARE CURSOR is a hybrid case: it's an optimizable statement (in fact not * DECLARE CURSOR is like other utility statements in that we emit it as a
* significantly different from a SELECT) as far as parsing/rewriting/planning * CMD_UTILITY Query node; however, we must first transform the contained
* are concerned, but it's not passed to the executor and so in that sense is * query. We used to postpone that until execution, but it's really necessary
* a utility statement. We transform it into a Query exactly as if it were * to do it during the normal parse analysis phase to ensure that side effects
* a SELECT, then stick the original DeclareCursorStmt into the utilityStmt * of parser hooks happen at the expected time.
* field to carry the cursor name and options.
*/ */
static Query * static Query *
transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
{ {
Query *result; Query *result;
Query *query;
/* /*
* Don't allow both SCROLL and NO SCROLL to be specified * 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), (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
errmsg("cannot specify both SCROLL and NO SCROLL"))); 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 */ /* Grammar should not have allowed anything but SELECT */
if (!IsA(result, Query) || if (!IsA(query, Query) ||
result->commandType != CMD_SELECT || query->commandType != CMD_SELECT)
result->utilityStmt != NULL)
elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR"); 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 * allowed, but the semantics of when the updates occur might be
* surprising.) * surprising.)
*/ */
if (result->hasModifyingCTE) if (query->hasModifyingCTE)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DECLARE CURSOR must not contain data-modifying statements in WITH"))); errmsg("DECLARE CURSOR must not contain data-modifying statements in WITH")));
/* FOR UPDATE and WITH HOLD are not compatible */ /* 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, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/*------ /*------
translator: %s is a SQL row locking clause such as FOR UPDATE */ translator: %s is a SQL row locking clause such as FOR UPDATE */
errmsg("DECLARE CURSOR WITH HOLD ... %s is not supported", errmsg("DECLARE CURSOR WITH HOLD ... %s is not supported",
LCS_asString(((RowMarkClause *) LCS_asString(((RowMarkClause *)
linitial(result->rowMarks))->strength)), linitial(query->rowMarks))->strength)),
errdetail("Holdable cursors must be READ ONLY."))); errdetail("Holdable cursors must be READ ONLY.")));
/* FOR UPDATE and SCROLL are not compatible */ /* 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, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/*------ /*------
translator: %s is a SQL row locking clause such as FOR UPDATE */ translator: %s is a SQL row locking clause such as FOR UPDATE */
errmsg("DECLARE SCROLL CURSOR ... %s is not supported", errmsg("DECLARE SCROLL CURSOR ... %s is not supported",
LCS_asString(((RowMarkClause *) LCS_asString(((RowMarkClause *)
linitial(result->rowMarks))->strength)), linitial(query->rowMarks))->strength)),
errdetail("Scrollable cursors must be READ ONLY."))); errdetail("Scrollable cursors must be READ ONLY.")));
/* FOR UPDATE and INSENSITIVE are not compatible */ /* 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, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/*------ /*------
translator: %s is a SQL row locking clause such as FOR UPDATE */ translator: %s is a SQL row locking clause such as FOR UPDATE */
errmsg("DECLARE INSENSITIVE CURSOR ... %s is not supported", errmsg("DECLARE INSENSITIVE CURSOR ... %s is not supported",
LCS_asString(((RowMarkClause *) LCS_asString(((RowMarkClause *)
linitial(result->rowMarks))->strength)), linitial(query->rowMarks))->strength)),
errdetail("Insensitive cursors must be READ ONLY."))); errdetail("Insensitive cursors must be READ ONLY.")));
/* We won't need the raw querytree any more */ /* represent the command as a utility Query */
stmt->query = NULL; result = makeNode(Query);
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) stmt; result->utilityStmt = (Node *) stmt;
return result; return result;
@ -2441,7 +2459,7 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
Query *result; Query *result;
/* transform contained query, allowing SELECT INTO */ /* 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 */ /* represent the command as a utility Query */
result = makeNode(Query); result = makeNode(Query);
@ -2457,7 +2475,7 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
* transform a CREATE TABLE AS, SELECT ... INTO, or CREATE MATERIALIZED VIEW * transform a CREATE TABLE AS, SELECT ... INTO, or CREATE MATERIALIZED VIEW
* Statement * Statement
* *
* As with EXPLAIN, transform the contained statement now. * As with DECLARE CURSOR and EXPLAIN, transform the contained statement now.
*/ */
static Query * static Query *
transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
@ -2465,7 +2483,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
Query *result; Query *result;
Query *query; Query *query;
/* transform contained query */ /* transform contained query, not allowing SELECT INTO */
query = transformStmt(pstate, stmt->query); query = transformStmt(pstate, stmt->query);
stmt->query = (Node *) query; stmt->query = (Node *) query;

View File

@ -80,7 +80,8 @@
/* /*
* The above macro assigns -1 (unknown) as the parse location of any * 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 * for nonterminals defined like
* OptFooList: / * EMPTY * / { ... } | OptFooList Foo { ... } ; * OptFooList: / * EMPTY * / { ... } | OptFooList Foo { ... } ;
* because we'll set -1 as the location during the first reduction and then * 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 * (Although we have many nonterminals that follow this pattern, we only
* bother with fixing @$ like this when the nonterminal's parse location * bother with fixing @$ like this when the nonterminal's parse location
* is actually referenced in some rule.) * 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, static void base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner,
const char *msg); 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, static Node *makeColumnRef(char *colname, List *indirection,
int location, core_yyscan_t yyscanner); int location, core_yyscan_t yyscanner);
static Node *makeTypeCast(Node *arg, TypeName *typename, int location); 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 stmtmulti: stmtmulti ';' stmt
{ {
if ($1 != NIL)
{
/* update length of previous stmt */
updateRawStmtEnd((RawStmt *) llast($1), @2);
}
if ($3 != NULL) if ($3 != NULL)
$$ = lappend($1, $3); $$ = lappend($1, makeRawStmt($3, @2 + 1));
else else
$$ = $1; $$ = $1;
} }
| stmt | stmt
{ {
if ($1 != NULL) if ($1 != NULL)
$$ = list_make1($1); $$ = list_make1(makeRawStmt($1, 0));
else else
$$ = NIL; $$ = NIL;
} }
@ -14474,6 +14497,33 @@ base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner, const char *msg)
parser_yyerror(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 * static Node *
makeColumnRef(char *colname, List *indirection, makeColumnRef(char *colname, List *indirection,
int location, core_yyscan_t yyscanner) int location, core_yyscan_t yyscanner)

View File

@ -478,12 +478,11 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
pstate->p_expr_kind = EXPR_KIND_NONE; pstate->p_expr_kind = EXPR_KIND_NONE;
/* /*
* Check that we got something reasonable. Many of these conditions are * Check that we got a SELECT. Anything else should be impossible given
* impossible given restrictions of the grammar, but check 'em anyway. * restrictions of the grammar, but check anyway.
*/ */
if (!IsA(query, Query) || if (!IsA(query, Query) ||
query->commandType != CMD_SELECT || query->commandType != CMD_SELECT)
query->utilityStmt != NULL)
elog(ERROR, "unexpected non-SELECT command in subquery in FROM"); elog(ERROR, "unexpected non-SELECT command in subquery in FROM");
/* /*

View File

@ -1848,12 +1848,11 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false); qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false);
/* /*
* Check that we got something reasonable. Many of these conditions are * Check that we got a SELECT. Anything else should be impossible given
* impossible given restrictions of the grammar, but check 'em anyway. * restrictions of the grammar, but check anyway.
*/ */
if (!IsA(qtree, Query) || if (!IsA(qtree, Query) ||
qtree->commandType != CMD_SELECT || qtree->commandType != CMD_SELECT)
qtree->utilityStmt != NULL)
elog(ERROR, "unexpected non-SELECT command in SubLink"); elog(ERROR, "unexpected non-SELECT command in SubLink");
sublink->subselect = (Node *) qtree; sublink->subselect = (Node *) qtree;

View File

@ -720,7 +720,7 @@ typeStringToTypeName(const char *str)
*/ */
if (list_length(raw_parsetree_list) != 1) if (list_length(raw_parsetree_list) != 1)
goto fail; goto fail;
stmt = (SelectStmt *) linitial(raw_parsetree_list); stmt = (SelectStmt *) ((RawStmt *) linitial(raw_parsetree_list))->stmt;
if (stmt == NULL || if (stmt == NULL ||
!IsA(stmt, SelectStmt) || !IsA(stmt, SelectStmt) ||
stmt->distinctClause != NIL || stmt->distinctClause != NIL ||

View File

@ -29,7 +29,8 @@
* raw_parser * raw_parser
* Given a query in string form, do lexical and grammatical analysis. * 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 * List *
raw_parser(const char *str) raw_parser(const char *str)

View File

@ -330,8 +330,7 @@ DefineQueryRewrite(char *rulename,
*/ */
query = (Query *) linitial(action); query = (Query *) linitial(action);
if (!is_instead || if (!is_instead ||
query->commandType != CMD_SELECT || query->commandType != CMD_SELECT)
query->utilityStmt != NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("rules on SELECT must have action INSTEAD SELECT"))); errmsg("rules on SELECT must have action INSTEAD SELECT")));

View File

@ -183,8 +183,8 @@ static int errdetail_recovery_conflict(void);
static void start_xact_command(void); static void start_xact_command(void);
static void finish_xact_command(void); static void finish_xact_command(void);
static bool IsTransactionExitStmt(Node *parsetree); static bool IsTransactionExitStmt(Node *parsetree);
static bool IsTransactionExitStmtList(List *parseTrees); static bool IsTransactionExitStmtList(List *pstmts);
static bool IsTransactionStmtList(List *parseTrees); static bool IsTransactionStmtList(List *pstmts);
static void drop_unnamed_stmt(void); static void drop_unnamed_stmt(void);
static void SigHupHandler(SIGNAL_ARGS); static void SigHupHandler(SIGNAL_ARGS);
static void log_disconnections(int code, Datum arg); static void log_disconnections(int code, Datum arg);
@ -588,8 +588,8 @@ ProcessClientWriteInterrupt(bool blocked)
/* /*
* Do raw parsing (only). * Do raw parsing (only).
* *
* A list of parsetrees is returned, since there might be multiple * A list of parsetrees (RawStmt nodes) is returned, since there might be
* commands in the given string. * multiple commands in the given string.
* *
* NOTE: for interactive queries, it is important to keep this routine * NOTE: for interactive queries, it is important to keep this routine
* separate from the analysis & rewrite stages. Analysis and rewriting * 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. * NOTE: for reasons mentioned above, this must be separate from raw parsing.
*/ */
List * 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) Oid *paramTypes, int numParams)
{ {
Query *query; 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. * hooks instead of a fixed list of parameter datatypes.
*/ */
List * List *
pg_analyze_and_rewrite_params(Node *parsetree, pg_analyze_and_rewrite_params(RawStmt *parsetree,
const char *query_string, const char *query_string,
ParserSetupHook parserSetup, ParserSetupHook parserSetup,
void *parserSetupArg) void *parserSetupArg)
@ -833,8 +833,10 @@ pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
/* /*
* Generate plans for a list of already-rewritten queries. * Generate plans for a list of already-rewritten queries.
* *
* Normal optimizable statements generate PlannedStmt entries in the result * For normal optimizable statements, invoke the planner. For utility
* list. Utility statements are simply represented by their statement nodes. * statements, just make a wrapper PlannedStmt node.
*
* The result is a list of PlannedStmt nodes.
*/ */
List * List *
pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams) 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) foreach(query_list, querytrees)
{ {
Query *query = (Query *) lfirst(query_list); Query *query = (Query *) lfirst(query_list);
Node *stmt; PlannedStmt *stmt;
if (query->commandType == CMD_UTILITY) if (query->commandType == CMD_UTILITY)
{ {
/* Utility commands have no plans. */ /* Utility commands require no planning. */
stmt = query->utilityStmt; 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 else
{ {
stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams); stmt = pg_plan_query(query, cursorOptions, boundParams);
} }
stmt_list = lappend(stmt_list, stmt); stmt_list = lappend(stmt_list, stmt);
@ -955,7 +962,7 @@ exec_simple_query(const char *query_string)
*/ */
foreach(parsetree_item, parsetree_list) foreach(parsetree_item, parsetree_list)
{ {
Node *parsetree = (Node *) lfirst(parsetree_item); RawStmt *parsetree = (RawStmt *) lfirst(parsetree_item);
bool snapshot_set = false; bool snapshot_set = false;
const char *commandTag; const char *commandTag;
char completionTag[COMPLETION_TAG_BUFSIZE]; 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 * do any special start-of-SQL-command processing needed by the
* destination. * destination.
*/ */
commandTag = CreateCommandTag(parsetree); commandTag = CreateCommandTag(parsetree->stmt);
set_ps_display(commandTag, false); set_ps_display(commandTag, false);
@ -986,7 +993,7 @@ exec_simple_query(const char *query_string)
* state, but not many...) * state, but not many...)
*/ */
if (IsAbortedTransactionBlockState() && if (IsAbortedTransactionBlockState() &&
!IsTransactionExitStmt(parsetree)) !IsTransactionExitStmt(parsetree->stmt))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, " errmsg("current transaction is aborted, "
@ -1061,9 +1068,9 @@ exec_simple_query(const char *query_string)
* backward compatibility...) * backward compatibility...)
*/ */
format = 0; /* TEXT is default */ 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) if (!stmt->ismove)
{ {
@ -1102,7 +1109,7 @@ exec_simple_query(const char *query_string)
PortalDrop(portal, false); PortalDrop(portal, false);
if (IsA(parsetree, TransactionStmt)) if (IsA(parsetree->stmt, TransactionStmt))
{ {
/* /*
* If this was a transaction control statement, commit it. We will * 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 unnamed_stmt_context = NULL;
MemoryContext oldcontext; MemoryContext oldcontext;
List *parsetree_list; List *parsetree_list;
Node *raw_parse_tree; RawStmt *raw_parse_tree;
const char *commandTag; const char *commandTag;
List *querytree_list; List *querytree_list;
CachedPlanSource *psrc; CachedPlanSource *psrc;
@ -1279,12 +1286,12 @@ exec_parse_message(const char *query_string, /* string to execute */
bool snapshot_set = false; bool snapshot_set = false;
int i; 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. * 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 * 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...) * state, but not many...)
*/ */
if (IsAbortedTransactionBlockState() && if (IsAbortedTransactionBlockState() &&
!IsTransactionExitStmt(raw_parse_tree)) !IsTransactionExitStmt(raw_parse_tree->stmt))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, " errmsg("current transaction is aborted, "
@ -1552,7 +1559,7 @@ exec_bind_message(StringInfo input_message)
* functions. * functions.
*/ */
if (IsAbortedTransactionBlockState() && if (IsAbortedTransactionBlockState() &&
(!IsTransactionExitStmt(psrc->raw_parse_tree) || (!IsTransactionExitStmt(psrc->raw_parse_tree->stmt) ||
numParams != 0)) numParams != 0))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
@ -2140,11 +2147,11 @@ errdetail_execute(List *raw_parsetree_list)
foreach(parsetree_item, 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; PreparedStatement *pstmt;
pstmt = FetchPreparedStatement(stmt->name, false); pstmt = FetchPreparedStatement(stmt->name, false);
@ -2488,45 +2495,33 @@ IsTransactionExitStmt(Node *parsetree)
return false; return false;
} }
/* Test a list that might contain Query nodes or bare parsetrees */ /* Test a list that contains PlannedStmt nodes */
static bool static bool
IsTransactionExitStmtList(List *parseTrees) IsTransactionExitStmtList(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)) Assert(IsA(pstmt, PlannedStmt));
{ if (pstmt->commandType == CMD_UTILITY &&
Query *query = (Query *) stmt; IsTransactionExitStmt(pstmt->utilityStmt))
if (query->commandType == CMD_UTILITY &&
IsTransactionExitStmt(query->utilityStmt))
return true;
}
else if (IsTransactionExitStmt(stmt))
return true; return true;
} }
return false; return false;
} }
/* Test a list that might contain Query nodes or bare parsetrees */ /* Test a list that contains PlannedStmt nodes */
static bool 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)) Assert(IsA(pstmt, PlannedStmt));
{ if (pstmt->commandType == CMD_UTILITY &&
Query *query = (Query *) stmt; IsA(pstmt->utilityStmt, TransactionStmt))
if (query->commandType == CMD_UTILITY &&
IsA(query->utilityStmt, TransactionStmt))
return true;
}
else if (IsA(stmt, TransactionStmt))
return true; return true;
} }
return false; return false;

View File

@ -43,7 +43,7 @@ static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
DestReceiver *dest); DestReceiver *dest);
static uint64 PortalRunSelect(Portal portal, bool forward, long count, static uint64 PortalRunSelect(Portal portal, bool forward, long count,
DestReceiver *dest); DestReceiver *dest);
static void PortalRunUtility(Portal portal, Node *utilityStmt, static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot, bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, char *completionTag); DestReceiver *dest, char *completionTag);
static void PortalRunMulti(Portal portal, static void PortalRunMulti(Portal portal,
@ -73,7 +73,6 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
qd->operation = plannedstmt->commandType; /* operation */ qd->operation = plannedstmt->commandType; /* operation */
qd->plannedstmt = plannedstmt; /* plan */ qd->plannedstmt = plannedstmt; /* plan */
qd->utilitystmt = plannedstmt->utilityStmt; /* in case DECLARE CURSOR */
qd->sourceText = sourceText; /* query text */ qd->sourceText = sourceText; /* query text */
qd->snapshot = RegisterSnapshot(snapshot); /* snapshot */ qd->snapshot = RegisterSnapshot(snapshot); /* snapshot */
/* RI check snapshot */ /* RI check snapshot */
@ -92,37 +91,6 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
return qd; return qd;
} }
/*
* CreateUtilityQueryDesc
*/
QueryDesc *
CreateUtilityQueryDesc(Node *utilitystmt,
const char *sourceText,
Snapshot snapshot,
DestReceiver *dest,
ParamListInfo params)
{
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
qd->operation = CMD_UTILITY; /* operation */
qd->plannedstmt = NULL;
qd->utilitystmt = utilitystmt; /* utility command */
qd->sourceText = sourceText; /* query text */
qd->snapshot = RegisterSnapshot(snapshot); /* snapshot */
qd->crosscheck_snapshot = InvalidSnapshot; /* RI check snapshot */
qd->dest = dest; /* output dest */
qd->params = params; /* parameter values passed into query */
qd->instrument_options = false; /* uninteresting for utilities */
/* null these fields until set by ExecutorStart */
qd->tupDesc = NULL;
qd->estate = NULL;
qd->planstate = NULL;
qd->totaltime = NULL;
return qd;
}
/* /*
* FreeQueryDesc * FreeQueryDesc
*/ */
@ -236,7 +204,7 @@ ProcessQuery(PlannedStmt *plan,
* ChoosePortalStrategy * ChoosePortalStrategy
* Select portal execution strategy given the intended statement list. * 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. * That's more general than portals need, but plancache.c uses this too.
* *
* See the comments in portal.h. * See the comments in portal.h.
@ -263,16 +231,14 @@ ChoosePortalStrategy(List *stmts)
if (query->canSetTag) if (query->canSetTag)
{ {
if (query->commandType == CMD_SELECT && if (query->commandType == CMD_SELECT)
query->utilityStmt == NULL)
{ {
if (query->hasModifyingCTE) if (query->hasModifyingCTE)
return PORTAL_ONE_MOD_WITH; return PORTAL_ONE_MOD_WITH;
else else
return PORTAL_ONE_SELECT; return PORTAL_ONE_SELECT;
} }
if (query->commandType == CMD_UTILITY && if (query->commandType == CMD_UTILITY)
query->utilityStmt != NULL)
{ {
if (UtilityReturnsTuples(query->utilityStmt)) if (UtilityReturnsTuples(query->utilityStmt))
return PORTAL_UTIL_SELECT; return PORTAL_UTIL_SELECT;
@ -287,25 +253,25 @@ ChoosePortalStrategy(List *stmts)
if (pstmt->canSetTag) if (pstmt->canSetTag)
{ {
if (pstmt->commandType == CMD_SELECT && if (pstmt->commandType == CMD_SELECT)
pstmt->utilityStmt == NULL)
{ {
if (pstmt->hasModifyingCTE) if (pstmt->hasModifyingCTE)
return PORTAL_ONE_MOD_WITH; return PORTAL_ONE_MOD_WITH;
else else
return PORTAL_ONE_SELECT; return PORTAL_ONE_SELECT;
} }
} if (pstmt->commandType == CMD_UTILITY)
}
else
{ {
/* must be a utility command; assume it's canSetTag */ if (UtilityReturnsTuples(pstmt->utilityStmt))
if (UtilityReturnsTuples(stmt))
return PORTAL_UTIL_SELECT; return PORTAL_UTIL_SELECT;
/* it can't be ONE_RETURNING, so give up */ /* it can't be ONE_RETURNING, so give up */
return PORTAL_MULTI_QUERY; return PORTAL_MULTI_QUERY;
} }
} }
}
else
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
}
/* /*
* PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite. * PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite.
@ -325,7 +291,8 @@ ChoosePortalStrategy(List *stmts)
{ {
if (++nSetTag > 1) if (++nSetTag > 1)
return PORTAL_MULTI_QUERY; /* no need to look further */ 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 */ return PORTAL_MULTI_QUERY; /* no need to look further */
} }
} }
@ -337,11 +304,13 @@ ChoosePortalStrategy(List *stmts)
{ {
if (++nSetTag > 1) if (++nSetTag > 1)
return PORTAL_MULTI_QUERY; /* no need to look further */ 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 */ 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) if (nSetTag == 1)
return PORTAL_ONE_RETURNING; return PORTAL_ONE_RETURNING;
@ -364,7 +333,7 @@ FetchPortalTargetList(Portal portal)
if (portal->strategy == PORTAL_MULTI_QUERY) if (portal->strategy == PORTAL_MULTI_QUERY)
return NIL; return NIL;
/* get the primary statement and find out what it returns */ /* 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. * Given a statement that returns tuples, extract the query targetlist.
* Returns NIL if the statement doesn't have a determinable 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. * That's more general than portals need, but plancache.c uses this too.
* *
* Note: do not modify the result. * Note: do not modify the result.
@ -388,16 +357,14 @@ FetchStatementTargetList(Node *stmt)
{ {
Query *query = (Query *) stmt; Query *query = (Query *) stmt;
if (query->commandType == CMD_UTILITY && if (query->commandType == CMD_UTILITY)
query->utilityStmt != NULL)
{ {
/* transfer attention to utility statement */ /* transfer attention to utility statement */
stmt = query->utilityStmt; stmt = query->utilityStmt;
} }
else else
{ {
if (query->commandType == CMD_SELECT && if (query->commandType == CMD_SELECT)
query->utilityStmt == NULL)
return query->targetList; return query->targetList;
if (query->returningList) if (query->returningList)
return query->returningList; return query->returningList;
@ -408,13 +375,20 @@ FetchStatementTargetList(Node *stmt)
{ {
PlannedStmt *pstmt = (PlannedStmt *) stmt; PlannedStmt *pstmt = (PlannedStmt *) stmt;
if (pstmt->commandType == CMD_SELECT && if (pstmt->commandType == CMD_UTILITY)
pstmt->utilityStmt == NULL) {
/* transfer attention to utility statement */
stmt = pstmt->utilityStmt;
}
else
{
if (pstmt->commandType == CMD_SELECT)
return pstmt->planTree->targetlist; return pstmt->planTree->targetlist;
if (pstmt->hasReturning) if (pstmt->hasReturning)
return pstmt->planTree->targetlist; return pstmt->planTree->targetlist;
return NIL; return NIL;
} }
}
if (IsA(stmt, FetchStmt)) if (IsA(stmt, FetchStmt))
{ {
FetchStmt *fstmt = (FetchStmt *) stmt; FetchStmt *fstmt = (FetchStmt *) stmt;
@ -566,8 +540,7 @@ PortalStart(Portal portal, ParamListInfo params,
{ {
PlannedStmt *pstmt; PlannedStmt *pstmt;
pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal); pstmt = PortalGetPrimaryStmt(portal);
Assert(IsA(pstmt, PlannedStmt));
portal->tupDesc = portal->tupDesc =
ExecCleanTypeFromTL(pstmt->planTree->targetlist, ExecCleanTypeFromTL(pstmt->planTree->targetlist,
false); false);
@ -588,10 +561,10 @@ PortalStart(Portal portal, ParamListInfo params,
* take care of it if needed. * take care of it if needed.
*/ */
{ {
Node *ustmt = PortalGetPrimaryStmt(portal); PlannedStmt *pstmt = PortalGetPrimaryStmt(portal);
Assert(!IsA(ustmt, PlannedStmt)); Assert(pstmt->commandType == CMD_UTILITY);
portal->tupDesc = UtilityTupleDescriptor(ustmt); portal->tupDesc = UtilityTupleDescriptor(pstmt->utilityStmt);
} }
/* /*
@ -1047,7 +1020,7 @@ FillPortalStore(Portal portal, bool isTopLevel)
break; break;
case PORTAL_UTIL_SELECT: case PORTAL_UTIL_SELECT:
PortalRunUtility(portal, (Node *) linitial(portal->stmts), PortalRunUtility(portal, (PlannedStmt *) linitial(portal->stmts),
isTopLevel, true, treceiver, completionTag); isTopLevel, true, treceiver, completionTag);
break; break;
@ -1143,10 +1116,11 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count,
* Execute a utility statement inside a portal. * Execute a utility statement inside a portal.
*/ */
static void static void
PortalRunUtility(Portal portal, Node *utilityStmt, PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot, bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, char *completionTag) DestReceiver *dest, char *completionTag)
{ {
Node *utilityStmt = pstmt->utilityStmt;
Snapshot snapshot; Snapshot snapshot;
/* /*
@ -1186,7 +1160,7 @@ PortalRunUtility(Portal portal, Node *utilityStmt,
else else
snapshot = NULL; snapshot = NULL;
ProcessUtility(utilityStmt, ProcessUtility(pstmt,
portal->sourceText, portal->sourceText,
isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY, isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
portal->portalParams, portal->portalParams,
@ -1241,21 +1215,18 @@ PortalRunMulti(Portal portal,
*/ */
foreach(stmtlist_item, portal->stmts) 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 * If we got a cancel signal in prior command, quit
*/ */
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();
if (IsA(stmt, PlannedStmt) && if (pstmt->utilityStmt == NULL)
((PlannedStmt *) stmt)->utilityStmt == NULL)
{ {
/* /*
* process a plannable query. * process a plannable query.
*/ */
PlannedStmt *pstmt = (PlannedStmt *) stmt;
TRACE_POSTGRESQL_QUERY_EXECUTE_START(); TRACE_POSTGRESQL_QUERY_EXECUTE_START();
if (log_executor_stats) if (log_executor_stats)
@ -1320,9 +1291,6 @@ PortalRunMulti(Portal portal,
/* /*
* process utility functions (create, destroy, etc..) * 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 * We must not set a snapshot here for utility commands (if one is
* needed, PortalRunUtility will do it). If a utility command is * needed, PortalRunUtility will do it). If a utility command is
* alone in a portal then everything's fine. The only case where * 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 * whether it has a snapshot or not, so we just leave the current
* snapshot alone if we have one. * snapshot alone if we have one.
*/ */
if (list_length(portal->stmts) == 1) if (pstmt->canSetTag)
{ {
Assert(!active_snapshot_set); Assert(!active_snapshot_set);
/* statement can set tag string */ /* statement can set tag string */
PortalRunUtility(portal, stmt, isTopLevel, false, PortalRunUtility(portal, pstmt, isTopLevel, false,
dest, completionTag); dest, completionTag);
} }
else else
{ {
Assert(IsA(stmt, NotifyStmt)); Assert(IsA(pstmt->utilityStmt, NotifyStmt));
/* stmt added by rewrite cannot set tag */ /* stmt added by rewrite cannot set tag */
PortalRunUtility(portal, stmt, isTopLevel, false, PortalRunUtility(portal, pstmt, isTopLevel, false,
altdest, NULL); altdest, NULL);
} }
} }

View File

@ -72,7 +72,7 @@ ProcessUtility_hook_type ProcessUtility_hook = NULL;
/* local function declarations */ /* local function declarations */
static void ProcessUtilitySlow(ParseState *pstate, static void ProcessUtilitySlow(ParseState *pstate,
Node *parsetree, PlannedStmt *pstmt,
const char *queryString, const char *queryString,
ProcessUtilityContext context, ProcessUtilityContext context,
ParamListInfo params, ParamListInfo params,
@ -88,21 +88,18 @@ static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
* the query must be *in truth* read-only, because the caller wishes * the query must be *in truth* read-only, because the caller wishes
* not to do CommandCounterIncrement for it. * 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 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: case CMD_SELECT:
if (stmt->rowMarks != NIL) if (pstmt->rowMarks != NIL)
return false; /* SELECT FOR [KEY] UPDATE/SHARE */ return false; /* SELECT FOR [KEY] UPDATE/SHARE */
else if (stmt->hasModifyingCTE) else if (pstmt->hasModifyingCTE)
return false; /* data-modifying CTE */ return false; /* data-modifying CTE */
else else
return true; return true;
@ -110,13 +107,14 @@ CommandIsReadOnly(Node *parsetree)
case CMD_INSERT: case CMD_INSERT:
case CMD_DELETE: case CMD_DELETE:
return false; return false;
case CMD_UTILITY:
/* For now, treat all utility commands as read/write */
return false;
default: default:
elog(WARNING, "unrecognized commandType: %d", elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType); (int) pstmt->commandType);
break; break;
} }
}
/* For now, treat all utility commands as read/write */
return false; return false;
} }
@ -297,7 +295,7 @@ CheckRestrictedOperation(const char *cmdname)
* ProcessUtility * ProcessUtility
* general utility function invoker * 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 * queryString: original source text of command
* context: identifies source of statement (toplevel client command, * context: identifies source of statement (toplevel client command,
* non-toplevel client command, subcommand of a larger utility 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. * completionTag may be NULL if caller doesn't want a status string.
*/ */
void void
ProcessUtility(Node *parsetree, ProcessUtility(PlannedStmt *pstmt,
const char *queryString, const char *queryString,
ProcessUtilityContext context, ProcessUtilityContext context,
ParamListInfo params, ParamListInfo params,
DestReceiver *dest, DestReceiver *dest,
char *completionTag) char *completionTag)
{ {
Assert(IsA(pstmt, PlannedStmt));
Assert(pstmt->commandType == CMD_UTILITY);
Assert(queryString != NULL); /* required as of 8.4 */ Assert(queryString != NULL); /* required as of 8.4 */
/* /*
@ -330,11 +330,11 @@ ProcessUtility(Node *parsetree,
* call standard_ProcessUtility(). * call standard_ProcessUtility().
*/ */
if (ProcessUtility_hook) if (ProcessUtility_hook)
(*ProcessUtility_hook) (parsetree, queryString, (*ProcessUtility_hook) (pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
standard_ProcessUtility(parsetree, queryString, standard_ProcessUtility(pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
} }
@ -351,13 +351,14 @@ ProcessUtility(Node *parsetree,
* which requires being in a valid transaction. * which requires being in a valid transaction.
*/ */
void void
standard_ProcessUtility(Node *parsetree, standard_ProcessUtility(PlannedStmt *pstmt,
const char *queryString, const char *queryString,
ProcessUtilityContext context, ProcessUtilityContext context,
ParamListInfo params, ParamListInfo params,
DestReceiver *dest, DestReceiver *dest,
char *completionTag) char *completionTag)
{ {
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL); bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
ParseState *pstate; ParseState *pstate;
@ -486,20 +487,10 @@ standard_ProcessUtility(Node *parsetree,
/* /*
* Portal (cursor) manipulation * 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: case T_DeclareCursorStmt:
{ PerformCursorOpen((DeclareCursorStmt *) parsetree, params,
PlannedStmt *stmt = (PlannedStmt *) parsetree; queryString, isTopLevel);
if (stmt->utilityStmt == NULL ||
!IsA(stmt->utilityStmt, DeclareCursorStmt))
elog(ERROR, "non-DECLARE CURSOR PlannedStmt passed to ProcessUtility");
PerformCursorOpen(stmt, params, queryString, isTopLevel);
}
break; break;
case T_ClosePortalStmt: case T_ClosePortalStmt:
@ -545,7 +536,9 @@ standard_ProcessUtility(Node *parsetree,
{ {
uint64 processed; uint64 processed;
DoCopy(pstate, (CopyStmt *) parsetree, &processed); DoCopy(pstate, (CopyStmt *) parsetree,
pstmt->stmt_location, pstmt->stmt_len,
&processed);
if (completionTag) if (completionTag)
snprintf(completionTag, COMPLETION_TAG_BUFSIZE, snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
"COPY " UINT64_FORMAT, processed); "COPY " UINT64_FORMAT, processed);
@ -554,7 +547,8 @@ standard_ProcessUtility(Node *parsetree,
case T_PrepareStmt: case T_PrepareStmt:
CheckRestrictedOperation("PREPARE"); CheckRestrictedOperation("PREPARE");
PrepareQuery((PrepareStmt *) parsetree, queryString); PrepareQuery((PrepareStmt *) parsetree, queryString,
pstmt->stmt_location, pstmt->stmt_len);
break; break;
case T_ExecuteStmt: case T_ExecuteStmt:
@ -808,11 +802,11 @@ standard_ProcessUtility(Node *parsetree,
GrantStmt *stmt = (GrantStmt *) parsetree; GrantStmt *stmt = (GrantStmt *) parsetree;
if (EventTriggerSupportsGrantObjectType(stmt->objtype)) if (EventTriggerSupportsGrantObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, parsetree, queryString, ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
ExecuteGrantStmt((GrantStmt *) parsetree); ExecuteGrantStmt(stmt);
} }
break; break;
@ -821,7 +815,7 @@ standard_ProcessUtility(Node *parsetree,
DropStmt *stmt = (DropStmt *) parsetree; DropStmt *stmt = (DropStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->removeType)) if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, parsetree, queryString, ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
@ -834,7 +828,7 @@ standard_ProcessUtility(Node *parsetree,
RenameStmt *stmt = (RenameStmt *) parsetree; RenameStmt *stmt = (RenameStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->renameType)) if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, parsetree, queryString, ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
@ -847,7 +841,7 @@ standard_ProcessUtility(Node *parsetree,
AlterObjectDependsStmt *stmt = (AlterObjectDependsStmt *) parsetree; AlterObjectDependsStmt *stmt = (AlterObjectDependsStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->objectType)) if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, parsetree, queryString, ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
@ -860,7 +854,7 @@ standard_ProcessUtility(Node *parsetree,
AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree; AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->objectType)) if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, parsetree, queryString, ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
@ -873,7 +867,7 @@ standard_ProcessUtility(Node *parsetree,
AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree; AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->objectType)) if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, parsetree, queryString, ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
@ -886,11 +880,11 @@ standard_ProcessUtility(Node *parsetree,
CommentStmt *stmt = (CommentStmt *) parsetree; CommentStmt *stmt = (CommentStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->objtype)) if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, parsetree, queryString, ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
CommentObject((CommentStmt *) parsetree); CommentObject(stmt);
break; break;
} }
@ -899,7 +893,7 @@ standard_ProcessUtility(Node *parsetree,
SecLabelStmt *stmt = (SecLabelStmt *) parsetree; SecLabelStmt *stmt = (SecLabelStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->objtype)) if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, parsetree, queryString, ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
else else
@ -909,7 +903,7 @@ standard_ProcessUtility(Node *parsetree,
default: default:
/* All other statement types have event trigger support */ /* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, parsetree, queryString, ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, context, params,
dest, completionTag); dest, completionTag);
break; break;
@ -925,13 +919,14 @@ standard_ProcessUtility(Node *parsetree,
*/ */
static void static void
ProcessUtilitySlow(ParseState *pstate, ProcessUtilitySlow(ParseState *pstate,
Node *parsetree, PlannedStmt *pstmt,
const char *queryString, const char *queryString,
ProcessUtilityContext context, ProcessUtilityContext context,
ParamListInfo params, ParamListInfo params,
DestReceiver *dest, DestReceiver *dest,
char *completionTag) char *completionTag)
{ {
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL); bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
bool isCompleteQuery = (context <= PROCESS_UTILITY_QUERY); bool isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
bool needCleanup; bool needCleanup;
@ -955,7 +950,9 @@ ProcessUtilitySlow(ParseState *pstate,
*/ */
case T_CreateSchemaStmt: case T_CreateSchemaStmt:
CreateSchemaCommand((CreateSchemaStmt *) parsetree, CreateSchemaCommand((CreateSchemaStmt *) parsetree,
queryString); queryString,
pstmt->stmt_location,
pstmt->stmt_len);
/* /*
* EventTriggerCollectSimpleCommand called by * EventTriggerCollectSimpleCommand called by
@ -1036,7 +1033,16 @@ ProcessUtilitySlow(ParseState *pstate,
* call will stash the objects so created into our * call will stash the objects so created into our
* event trigger context. * 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, queryString,
PROCESS_UTILITY_SUBCOMMAND, PROCESS_UTILITY_SUBCOMMAND,
params, params,
@ -1105,8 +1111,16 @@ ProcessUtilitySlow(ParseState *pstate,
* queued commands is consistent with the way * queued commands is consistent with the way
* they are executed here. * they are executed here.
*/ */
PlannedStmt *wrapper;
EventTriggerAlterTableEnd(); 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, queryString,
PROCESS_UTILITY_SUBCOMMAND, PROCESS_UTILITY_SUBCOMMAND,
params, params,
@ -1376,7 +1390,8 @@ ProcessUtilitySlow(ParseState *pstate,
case T_ViewStmt: /* CREATE VIEW */ case T_ViewStmt: /* CREATE VIEW */
EventTriggerAlterTableStart(parsetree); EventTriggerAlterTableStart(parsetree);
address = DefineView((ViewStmt *) parsetree, queryString); address = DefineView((ViewStmt *) parsetree, queryString,
pstmt->stmt_location, pstmt->stmt_len);
EventTriggerCollectSimpleCommand(address, secondaryObject, EventTriggerCollectSimpleCommand(address, secondaryObject,
parsetree); parsetree);
/* stashed internally */ /* stashed internally */
@ -1480,6 +1495,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_AlterTSConfigurationStmt: case T_AlterTSConfigurationStmt:
AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree); AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
/* /*
* Commands are stashed in MakeConfigurationMapping and * Commands are stashed in MakeConfigurationMapping and
* DropConfigurationMapping, which are called from * DropConfigurationMapping, which are called from
@ -1736,10 +1752,8 @@ QueryReturnsTuples(Query *parsetree)
switch (parsetree->commandType) switch (parsetree->commandType)
{ {
case CMD_SELECT: case CMD_SELECT:
/* returns tuples ... unless it's DECLARE CURSOR */ /* returns tuples */
if (parsetree->utilityStmt == NULL)
return true; return true;
break;
case CMD_INSERT: case CMD_INSERT:
case CMD_UPDATE: case CMD_UPDATE:
case CMD_DELETE: case CMD_DELETE:
@ -1780,6 +1794,13 @@ UtilityContainsQuery(Node *parsetree)
switch (nodeTag(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: case T_ExplainStmt:
qry = (Query *) ((ExplainStmt *) parsetree)->query; qry = (Query *) ((ExplainStmt *) parsetree)->query;
Assert(IsA(qry, Query)); Assert(IsA(qry, Query));
@ -1931,7 +1952,8 @@ AlterObjectTypeCommandTag(ObjectType objtype)
/* /*
* CreateCommandTag * CreateCommandTag
* utility to get a string representation of the command operation, * 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 * This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here. * of 'em are utility commands, it seems sensible to keep it here.
@ -1946,6 +1968,11 @@ CreateCommandTag(Node *parsetree)
switch (nodeTag(parsetree)) switch (nodeTag(parsetree))
{ {
/* recurse if we're given a RawStmt */
case T_RawStmt:
tag = CreateCommandTag(((RawStmt *) parsetree)->stmt);
break;
/* raw plannable queries */ /* raw plannable queries */
case T_InsertStmt: case T_InsertStmt:
tag = "INSERT"; tag = "INSERT";
@ -2608,12 +2635,7 @@ CreateCommandTag(Node *parsetree)
* will be useful for complaints about read-only * will be useful for complaints about read-only
* statements * statements
*/ */
if (stmt->utilityStmt != NULL) if (stmt->rowMarks != NIL)
{
Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
tag = "DECLARE CURSOR";
}
else if (stmt->rowMarks != NIL)
{ {
/* not 100% but probably close enough */ /* not 100% but probably close enough */
switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength) switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
@ -2647,6 +2669,9 @@ CreateCommandTag(Node *parsetree)
case CMD_DELETE: case CMD_DELETE:
tag = "DELETE"; tag = "DELETE";
break; break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
default: default:
elog(WARNING, "unrecognized commandType: %d", elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType); (int) stmt->commandType);
@ -2670,12 +2695,7 @@ CreateCommandTag(Node *parsetree)
* will be useful for complaints about read-only * will be useful for complaints about read-only
* statements * statements
*/ */
if (stmt->utilityStmt != NULL) if (stmt->rowMarks != NIL)
{
Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
tag = "DECLARE CURSOR";
}
else if (stmt->rowMarks != NIL)
{ {
/* not 100% but probably close enough */ /* not 100% but probably close enough */
switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength) switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
@ -2735,7 +2755,8 @@ CreateCommandTag(Node *parsetree)
/* /*
* GetCommandLogLevel * GetCommandLogLevel
* utility to get the minimum log_statement level for a command, * 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 * This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here. * of 'em are utility commands, it seems sensible to keep it here.
@ -2747,6 +2768,11 @@ GetCommandLogLevel(Node *parsetree)
switch (nodeTag(parsetree)) switch (nodeTag(parsetree))
{ {
/* recurse if we're given a RawStmt */
case T_RawStmt:
lev = GetCommandLogLevel(((RawStmt *) parsetree)->stmt);
break;
/* raw plannable queries */ /* raw plannable queries */
case T_InsertStmt: case T_InsertStmt:
case T_DeleteStmt: case T_DeleteStmt:
@ -2850,7 +2876,7 @@ GetCommandLogLevel(Node *parsetree)
/* Look through an EXECUTE to the referenced stmt */ /* Look through an EXECUTE to the referenced stmt */
ps = FetchPreparedStatement(stmt->name, false); ps = FetchPreparedStatement(stmt->name, false);
if (ps && ps->plansource->raw_parse_tree) if (ps && ps->plansource->raw_parse_tree)
lev = GetCommandLogLevel(ps->plansource->raw_parse_tree); lev = GetCommandLogLevel(ps->plansource->raw_parse_tree->stmt);
else else
lev = LOGSTMT_ALL; lev = LOGSTMT_ALL;
} }
@ -3157,6 +3183,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_MOD; lev = LOGSTMT_MOD;
break; break;
case CMD_UTILITY:
lev = GetCommandLogLevel(stmt->utilityStmt);
break;
default: default:
elog(WARNING, "unrecognized commandType: %d", elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType); (int) stmt->commandType);

View File

@ -77,7 +77,7 @@
*/ */
#define IsTransactionStmtPlan(plansource) \ #define IsTransactionStmtPlan(plansource) \
((plansource)->raw_parse_tree && \ ((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., * 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, static bool choose_custom_plan(CachedPlanSource *plansource,
ParamListInfo boundParams); ParamListInfo boundParams);
static double cached_plan_cost(CachedPlan *plan, bool include_planner); 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 AcquireExecutorLocks(List *stmt_list, bool acquire);
static void AcquirePlannerLocks(List *stmt_list, bool acquire); static void AcquirePlannerLocks(List *stmt_list, bool acquire);
static void ScanQueryForLocks(Query *parsetree, 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 * commandTag: compile-time-constant tag for query, or NULL if empty query
*/ */
CachedPlanSource * CachedPlanSource *
CreateCachedPlan(Node *raw_parse_tree, CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string, const char *query_string,
const char *commandTag) const char *commandTag)
{ {
@ -230,7 +231,7 @@ CreateCachedPlan(Node *raw_parse_tree,
* commandTag: compile-time-constant tag for query, or NULL if empty query * commandTag: compile-time-constant tag for query, or NULL if empty query
*/ */
CachedPlanSource * CachedPlanSource *
CreateOneShotCachedPlan(Node *raw_parse_tree, CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
const char *query_string, const char *query_string,
const char *commandTag) const char *commandTag)
{ {
@ -555,7 +556,7 @@ static List *
RevalidateCachedQuery(CachedPlanSource *plansource) RevalidateCachedQuery(CachedPlanSource *plansource)
{ {
bool snapshot_set; bool snapshot_set;
Node *rawtree; RawStmt *rawtree;
List *tlist; /* transient query-tree list */ List *tlist; /* transient query-tree list */
List *qlist; /* permanent query-tree list */ List *qlist; /* permanent query-tree list */
TupleDesc resultDesc; TupleDesc resultDesc;
@ -976,7 +977,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
{ {
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc); PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
if (!IsA(plannedstmt, PlannedStmt)) if (plannedstmt->commandType == CMD_UTILITY)
continue; /* Ignore utility statements */ continue; /* Ignore utility statements */
if (plannedstmt->transientPlan) if (plannedstmt->transientPlan)
@ -1071,7 +1072,7 @@ cached_plan_cost(CachedPlan *plan, bool include_planner)
{ {
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc); PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
if (!IsA(plannedstmt, PlannedStmt)) if (plannedstmt->commandType == CMD_UTILITY)
continue; /* Ignore utility statements */ continue; /* Ignore utility statements */
result += plannedstmt->planTree->total_cost; result += plannedstmt->planTree->total_cost;
@ -1419,7 +1420,7 @@ CachedPlanIsValid(CachedPlanSource *plansource)
List * List *
CachedPlanGetTargetList(CachedPlanSource *plansource) CachedPlanGetTargetList(CachedPlanSource *plansource)
{ {
Node *pstmt; Query *pstmt;
/* Assert caller is doing things in a sane order */ /* Assert caller is doing things in a sane order */
Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
@ -1436,9 +1437,34 @@ CachedPlanGetTargetList(CachedPlanSource *plansource)
RevalidateCachedQuery(plansource); RevalidateCachedQuery(plansource);
/* Get the primary statement and find out what it returns */ /* 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; int rt_index;
ListCell *lc2; ListCell *lc2;
Assert(!IsA(plannedstmt, Query)); Assert(IsA(plannedstmt, PlannedStmt));
if (!IsA(plannedstmt, PlannedStmt))
if (plannedstmt->commandType == CMD_UTILITY)
{ {
/* /*
* Ignore utility statements, except those (such as EXPLAIN) that * 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 * rule rewriting, because rewriting doesn't change the query
* representation. * representation.
*/ */
Query *query = UtilityContainsQuery((Node *) plannedstmt); Query *query = UtilityContainsQuery(plannedstmt->utilityStmt);
if (query) if (query)
ScanQueryForLocks(query, acquire); ScanQueryForLocks(query, acquire);
@ -1654,8 +1681,7 @@ PlanCacheComputeResultDesc(List *stmt_list)
return ExecCleanTypeFromTL(query->targetList, false); return ExecCleanTypeFromTL(query->targetList, false);
case PORTAL_ONE_RETURNING: case PORTAL_ONE_RETURNING:
query = (Query *) PortalListGetPrimaryStmt(stmt_list); query = QueryListGetPrimaryStmt(stmt_list);
Assert(IsA(query, Query));
Assert(query->returningList); Assert(query->returningList);
return ExecCleanTypeFromTL(query->returningList, false); return ExecCleanTypeFromTL(query->returningList, false);
@ -1720,8 +1746,7 @@ PlanCacheRelCallback(Datum arg, Oid relid)
{ {
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc); PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
Assert(!IsA(plannedstmt, Query)); if (plannedstmt->commandType == CMD_UTILITY)
if (!IsA(plannedstmt, PlannedStmt))
continue; /* Ignore utility statements */ continue; /* Ignore utility statements */
if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL : if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :
list_member_oid(plannedstmt->relationOids, relid)) list_member_oid(plannedstmt->relationOids, relid))
@ -1795,8 +1820,7 @@ PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue)
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc); PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
ListCell *lc3; ListCell *lc3;
Assert(!IsA(plannedstmt, Query)); if (plannedstmt->commandType == CMD_UTILITY)
if (!IsA(plannedstmt, PlannedStmt))
continue; /* Ignore utility statements */ continue; /* Ignore utility statements */
foreach(lc3, plannedstmt->invalItems) foreach(lc3, plannedstmt->invalItems)
{ {

View File

@ -139,46 +139,27 @@ GetPortalByName(const char *name)
} }
/* /*
* PortalListGetPrimaryStmt * PortalGetPrimaryStmt
* Get the "primary" stmt within a portal, ie, the one marked canSetTag. * Get the "primary" stmt within a portal, ie, the one marked canSetTag.
* *
* Returns NULL if no such stmt. If multiple PlannedStmt structs within the * Returns NULL if no such stmt. If multiple PlannedStmt structs within the
* portal are marked canSetTag, returns the first one. Neither of these * portal are marked canSetTag, returns the first one. Neither of these
* cases should occur in present usages of this function. * 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 * PlannedStmt *
PortalListGetPrimaryStmt(List *stmts) PortalGetPrimaryStmt(Portal portal)
{ {
ListCell *lc; ListCell *lc;
foreach(lc, stmts) foreach(lc, portal->stmts)
{ {
Node *stmt = (Node *) lfirst(lc); PlannedStmt *stmt = (PlannedStmt *) lfirst(lc);
if (IsA(stmt, PlannedStmt)) Assert(IsA(stmt, PlannedStmt));
{
if (((PlannedStmt *) stmt)->canSetTag) if (stmt->canSetTag)
return stmt; return stmt;
} }
else if (IsA(stmt, Query))
{
if (((Query *) stmt)->canSetTag)
return stmt;
}
else
{
/* Utility stmts are assumed canSetTag if they're the only stmt */
if (list_length(stmts) == 1)
return stmt;
}
}
return NULL; return NULL;
} }

View File

@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201612231 #define CATALOG_VERSION_NO 201701141
#endif #endif

View File

@ -22,7 +22,8 @@
/* CopyStateData is private in commands/copy.c */ /* CopyStateData is private in commands/copy.c */
typedef struct CopyStateData *CopyState; 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); uint64 *processed);
extern void ProcessCopyOptions(ParseState *pstate, CopyState cstate, bool is_from, List *options); extern void ProcessCopyOptions(ParseState *pstate, CopyState cstate, bool is_from, List *options);

View File

@ -49,6 +49,7 @@ typedef struct ExplainState
/* Hook for plugins to get control in ExplainOneQuery() */ /* Hook for plugins to get control in ExplainOneQuery() */
typedef void (*ExplainOneQuery_hook_type) (Query *query, typedef void (*ExplainOneQuery_hook_type) (Query *query,
int cursorOptions,
IntoClause *into, IntoClause *into,
ExplainState *es, ExplainState *es,
const char *queryString, const char *queryString,

View File

@ -18,7 +18,7 @@
#include "utils/portal.h" #include "utils/portal.h"
extern void PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, extern void PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params,
const char *queryString, bool isTopLevel); const char *queryString, bool isTopLevel);
extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest, extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,

View File

@ -35,7 +35,8 @@ typedef struct
/* Utility statements PREPARE, EXECUTE, DEALLOCATE, EXPLAIN EXECUTE */ /* 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, extern void ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
const char *queryString, ParamListInfo params, const char *queryString, ParamListInfo params,
DestReceiver *dest, char *completionTag); DestReceiver *dest, char *completionTag);

View File

@ -19,7 +19,8 @@
#include "nodes/parsenodes.h" #include "nodes/parsenodes.h"
extern Oid CreateSchemaCommand(CreateSchemaStmt *parsetree, extern Oid CreateSchemaCommand(CreateSchemaStmt *parsetree,
const char *queryString); const char *queryString,
int stmt_location, int stmt_len);
extern void RemoveSchemaById(Oid schemaOid); extern void RemoveSchemaById(Oid schemaOid);

View File

@ -19,7 +19,8 @@
extern void validateWithCheckOption(char *value); 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); extern void StoreViewQuery(Oid viewOid, Query *viewParse, bool replace);

View File

@ -34,8 +34,7 @@ typedef struct QueryDesc
{ {
/* These fields are provided by CreateQueryDesc */ /* These fields are provided by CreateQueryDesc */
CmdType operation; /* CMD_SELECT, CMD_UPDATE, etc. */ CmdType operation; /* CMD_SELECT, CMD_UPDATE, etc. */
PlannedStmt *plannedstmt; /* planner's output, or null if utility */ PlannedStmt *plannedstmt; /* planner's output (could be utility, too) */
Node *utilitystmt; /* utility statement, or null */
const char *sourceText; /* source text of the query */ const char *sourceText; /* source text of the query */
Snapshot snapshot; /* snapshot to use for query */ Snapshot snapshot; /* snapshot to use for query */
Snapshot crosscheck_snapshot; /* crosscheck for RI update/delete */ Snapshot crosscheck_snapshot; /* crosscheck for RI update/delete */
@ -61,12 +60,6 @@ extern QueryDesc *CreateQueryDesc(PlannedStmt *plannedstmt,
ParamListInfo params, ParamListInfo params,
int instrument_options); int instrument_options);
extern QueryDesc *CreateUtilityQueryDesc(Node *utilitystmt,
const char *sourceText,
Snapshot snapshot,
DestReceiver *dest,
ParamListInfo params);
extern void FreeQueryDesc(QueryDesc *qdesc); extern void FreeQueryDesc(QueryDesc *qdesc);
#endif /* EXECDESC_H */ #endif /* EXECDESC_H */

View File

@ -301,6 +301,7 @@ typedef enum NodeTag
/* /*
* TAGS FOR STATEMENT NODES (mostly in parsenodes.h) * TAGS FOR STATEMENT NODES (mostly in parsenodes.h)
*/ */
T_RawStmt,
T_Query, T_Query,
T_PlannedStmt, T_PlannedStmt,
T_InsertStmt, T_InsertStmt,

View File

@ -7,7 +7,9 @@
* This is a byte (not character) offset in the original source text, to be * 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 * 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 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 * 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. * for further processing by the rewriter and planner.
* *
* Utility statements (i.e. non-optimizable statements) have the * Utility statements (i.e. non-optimizable statements) have the
* utilityStmt field set, and the Query itself is mostly dummy. * utilityStmt field set, and the rest of the Query is mostly dummy.
* DECLARE CURSOR is a special case: it is represented like a SELECT,
* but the original DeclareCursorStmt is stored in utilityStmt.
* *
* Planning converts a Query tree into a Plan tree headed by a PlannedStmt * Planning converts a Query tree into a Plan tree headed by a PlannedStmt
* node --- the Query structure is not used by the executor. * 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? */ bool canSetTag; /* do I set the command result tag? */
Node *utilityStmt; /* non-null if this is DECLARE CURSOR or a Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */
* non-optimizable statement */
int resultRelation; /* rtable index of target relation for int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */ * INSERT/UPDATE/DELETE; 0 for SELECT */
@ -162,6 +161,15 @@ typedef struct Query
* are only added during rewrite and * are only added during rewrite and
* therefore are not written out as * therefore are not written out as
* part of Query. */ * 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; } Query;
@ -1307,6 +1315,30 @@ typedef struct TriggerTransition
bool isTable; bool isTable;
} 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 * Optimizable Statements
*****************************************************************************/ *****************************************************************************/
@ -1474,6 +1506,9 @@ typedef struct SetOperationStmt
* statements do need attention from parse analysis, and this is * statements do need attention from parse analysis, and this is
* done by routines in parser/parse_utilcmd.c after ProcessUtility * done by routines in parser/parse_utilcmd.c after ProcessUtility
* receives the command for execution. * 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; NodeTag type;
RangeVar *relation; /* the relation to copy */ RangeVar *relation; /* the relation to copy */
Node *query; /* the query (SELECT or DML statement with 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 List *attlist; /* List of column names (as Strings), or NIL
* for all columns */ * for all columns */
bool is_from; /* TO or FROM */ bool is_from; /* TO or FROM */
@ -2472,9 +2507,9 @@ typedef struct SecLabelStmt
/* ---------------------- /* ----------------------
* Declare Cursor Statement * Declare Cursor Statement
* *
* Note: the "query" field of DeclareCursorStmt is only used in the raw grammar * The "query" field is initially a raw parse tree, and is converted to a
* output. After parse analysis it's set to null, and the Query points to the * Query node during parse analysis. Note that rewriting and planning
* DeclareCursorStmt, not vice versa. * of the query are always postponed until execution.
* ---------------------- * ----------------------
*/ */
#define CURSOR_OPT_BINARY 0x0001 /* BINARY */ #define CURSOR_OPT_BINARY 0x0001 /* BINARY */
@ -2493,7 +2528,7 @@ typedef struct DeclareCursorStmt
NodeTag type; NodeTag type;
char *portalname; /* name of the portal (cursor) */ char *portalname; /* name of the portal (cursor) */
int options; /* bitmask of options (see above) */ int options; /* bitmask of options (see above) */
Node *query; /* the raw SELECT query */ Node *query; /* the query (see comments above) */
} DeclareCursorStmt; } DeclareCursorStmt;
/* ---------------------- /* ----------------------
@ -2841,7 +2876,7 @@ typedef struct ViewStmt
NodeTag type; NodeTag type;
RangeVar *view; /* the view to be created */ RangeVar *view; /* the view to be created */
List *aliases; /* target column names */ 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? */ bool replace; /* replace an existing view? */
List *options; /* options from WITH clause */ List *options; /* options from WITH clause */
ViewCheckOption withCheckOption; /* WITH CHECK OPTION */ ViewCheckOption withCheckOption; /* WITH CHECK OPTION */
@ -2950,9 +2985,9 @@ typedef struct VacuumStmt
/* ---------------------- /* ----------------------
* Explain Statement * Explain Statement
* *
* The "query" field is either a raw parse tree (SelectStmt, InsertStmt, etc) * The "query" field is initially a raw parse tree, and is converted to a
* or a Query node if parse analysis has been done. Note that rewriting and * Query node during parse analysis. Note that rewriting and planning
* planning of the query are always postponed until execution of EXPLAIN. * of the query are always postponed until execution.
* ---------------------- * ----------------------
*/ */
typedef struct ExplainStmt typedef struct ExplainStmt

View File

@ -31,13 +31,18 @@
* *
* The output of the planner is a Plan tree headed by a PlannedStmt node. * The output of the planner is a Plan tree headed by a PlannedStmt node.
* PlannedStmt holds the "one time" information needed by the executor. * 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 typedef struct PlannedStmt
{ {
NodeTag type; NodeTag type;
CmdType commandType; /* select|insert|update|delete */ CmdType commandType; /* select|insert|update|delete|utility */
uint32 queryId; /* query identifier (copied from Query) */ uint32 queryId; /* query identifier (copied from Query) */
@ -60,8 +65,6 @@ typedef struct PlannedStmt
/* rtable indexes of target relations for INSERT/UPDATE/DELETE */ /* rtable indexes of target relations for INSERT/UPDATE/DELETE */
List *resultRelations; /* integer list of RT indexes, or NIL */ 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 */ List *subplans; /* Plan trees for SubPlan expressions */
Bitmapset *rewindPlanIDs; /* indices of subplans that require REWIND */ Bitmapset *rewindPlanIDs; /* indices of subplans that require REWIND */
@ -73,6 +76,12 @@ typedef struct PlannedStmt
List *invalItems; /* other dependencies, as PlanInvalItems */ List *invalItems; /* other dependencies, as PlanInvalItems */
int nParamExec; /* number of PARAM_EXEC Params used */ 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; } PlannedStmt;
/* macro for fetching the Plan associated with a SubPlan node */ /* macro for fetching the Plan associated with a SubPlan node */

View File

@ -22,19 +22,19 @@ typedef void (*post_parse_analyze_hook_type) (ParseState *pstate,
extern PGDLLIMPORT post_parse_analyze_hook_type post_parse_analyze_hook; extern 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); 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); Oid **paramTypes, int *numParams);
extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState, extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
CommonTableExpr *parentCTE, CommonTableExpr *parentCTE,
bool locked_from_parent); 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 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 const char *LCS_asString(LockClauseStrength strength);
extern void CheckSelectLocking(Query *qry, LockClauseStrength strength); extern void CheckSelectLocking(Query *qry, LockClauseStrength strength);

View File

@ -47,9 +47,10 @@ typedef enum
extern int log_statement; extern int log_statement;
extern List *pg_parse_query(const char *query_string); extern List *pg_parse_query(const char *query_string);
extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string, extern List *pg_analyze_and_rewrite(RawStmt *parsetree,
const char *query_string,
Oid *paramTypes, int numParams); 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, const char *query_string,
ParserSetupHook parserSetup, ParserSetupHook parserSetup,
void *parserSetupArg); void *parserSetupArg);

View File

@ -24,16 +24,16 @@ typedef enum
} ProcessUtilityContext; } ProcessUtilityContext;
/* Hook for plugins to get control in ProcessUtility() */ /* 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, const char *queryString, ProcessUtilityContext context,
ParamListInfo params, ParamListInfo params,
DestReceiver *dest, char *completionTag); DestReceiver *dest, char *completionTag);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook; 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, ProcessUtilityContext context, ParamListInfo params,
DestReceiver *dest, char *completionTag); 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, ProcessUtilityContext context, ParamListInfo params,
DestReceiver *dest, char *completionTag); DestReceiver *dest, char *completionTag);
@ -47,6 +47,6 @@ extern const char *CreateCommandTag(Node *parsetree);
extern LogStmtLevel GetCommandLogLevel(Node *parsetree); extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
extern bool CommandIsReadOnly(Node *parsetree); extern bool CommandIsReadOnly(PlannedStmt *pstmt);
#endif /* UTILITY_H */ #endif /* UTILITY_H */

View File

@ -18,6 +18,9 @@
#include "access/tupdesc.h" #include "access/tupdesc.h"
#include "nodes/params.h" #include "nodes/params.h"
/* Forward declaration, to avoid including parsenodes.h here */
struct RawStmt;
#define CACHEDPLANSOURCE_MAGIC 195726186 #define CACHEDPLANSOURCE_MAGIC 195726186
#define CACHEDPLAN_MAGIC 953717834 #define CACHEDPLAN_MAGIC 953717834
@ -76,7 +79,7 @@
typedef struct CachedPlanSource typedef struct CachedPlanSource
{ {
int magic; /* should equal CACHEDPLANSOURCE_MAGIC */ 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 *query_string; /* source text of query */
const char *commandTag; /* command tag (a constant!), or NULL */ const char *commandTag; /* command tag (a constant!), or NULL */
Oid *param_types; /* array of parameter type OIDs, or NULL */ Oid *param_types; /* array of parameter type OIDs, or NULL */
@ -126,8 +129,7 @@ typedef struct CachedPlanSource
typedef struct CachedPlan typedef struct CachedPlan
{ {
int magic; /* should equal CACHEDPLAN_MAGIC */ int magic; /* should equal CACHEDPLAN_MAGIC */
List *stmt_list; /* list of statement nodes (PlannedStmts and List *stmt_list; /* list of PlannedStmts */
* bare utility statements) */
bool is_oneshot; /* is it a "oneshot" plan? */ bool is_oneshot; /* is it a "oneshot" plan? */
bool is_saved; /* is CachedPlan in a long-lived context? */ bool is_saved; /* is CachedPlan in a long-lived context? */
bool is_valid; /* is the stmt_list currently valid? */ bool is_valid; /* is the stmt_list currently valid? */
@ -144,10 +146,10 @@ typedef struct CachedPlan
extern void InitPlanCache(void); extern void InitPlanCache(void);
extern void ResetPlanCache(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 *query_string,
const char *commandTag); const char *commandTag);
extern CachedPlanSource *CreateOneShotCachedPlan(Node *raw_parse_tree, extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string, const char *query_string,
const char *commandTag); const char *commandTag);
extern void CompleteCachedPlan(CachedPlanSource *plansource, extern void CompleteCachedPlan(CachedPlanSource *plansource,

View File

@ -133,7 +133,7 @@ typedef struct PortalData
/* The query or queries the portal will execute */ /* The query or queries the portal will execute */
const char *sourceText; /* text of query (as of 8.4, never NULL) */ const char *sourceText; /* text of query (as of 8.4, never NULL) */
const char *commandTag; /* command tag for original query */ 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 */ CachedPlan *cplan; /* CachedPlan, if stmts are from one */
ParamListInfo portalParams; /* params to pass to query */ ParamListInfo portalParams; /* params to pass to query */
@ -201,7 +201,6 @@ typedef struct PortalData
*/ */
#define PortalGetQueryDesc(portal) ((portal)->queryDesc) #define PortalGetQueryDesc(portal) ((portal)->queryDesc)
#define PortalGetHeapMemory(portal) ((portal)->heap) #define PortalGetHeapMemory(portal) ((portal)->heap)
#define PortalGetPrimaryStmt(portal) PortalListGetPrimaryStmt((portal)->stmts)
/* Prototypes for functions in utils/mmgr/portalmem.c */ /* Prototypes for functions in utils/mmgr/portalmem.c */
@ -232,7 +231,7 @@ extern void PortalDefineQuery(Portal portal,
const char *commandTag, const char *commandTag,
List *stmts, List *stmts,
CachedPlan *cplan); CachedPlan *cplan);
extern Node *PortalListGetPrimaryStmt(List *stmts); extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
extern void PortalCreateHoldStore(Portal portal); extern void PortalCreateHoldStore(Portal portal);
extern void PortalHashTableDeleteAll(void); extern void PortalHashTableDeleteAll(void);
extern bool ThereAreNoReadyPortals(void); extern bool ThereAreNoReadyPortals(void);

View File

@ -6827,12 +6827,11 @@ exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan)
if (list_length(cplan->stmt_list) != 1) if (list_length(cplan->stmt_list) != 1)
return; return;
stmt = (PlannedStmt *) linitial(cplan->stmt_list); stmt = (PlannedStmt *) linitial(cplan->stmt_list);
Assert(IsA(stmt, PlannedStmt));
/* /*
* 2. It must be a RESULT plan --> no scan's required * 2. It must be a RESULT plan --> no scan's required
*/ */
if (!IsA(stmt, PlannedStmt))
return;
if (stmt->commandType != CMD_SELECT) if (stmt->commandType != CMD_SELECT)
return; return;
plan = stmt->planTree; plan = stmt->planTree;